PAM是 PLUGGABLE AUTHENTICATION MODULES
的缩写.可插入的认证模块(并不是Linux指的模块)用于实现应用程序的认证机制,
是程序员或管理员不需要重新编写或编译程序就可以改变认证机制.在linux它已经被广泛的应用了, 例如:
/etc/securitty /etc/onlogin
/etc/ftpusers
实际上都是给它用的.你在登陆的时候的输入密码和你修改密码时也都用的是它./etc/pam.conf和/etc/pam/*
都是它的配置文件.
它最大的优点是它的弹性和可扩充性. 你可以随意修改认证机制,
按你的实际需要来定制系统.你了解后就会非常清楚了.
DESIGN GOALS(设计目标)
(a) 管理员可以选择认证方式, 从简单的密码到智能卡系统.
(b) 可以为不同的程序配置不同的认证机制.如 使telnet使用 S/Key认证.
而本机的 login
缺使用一般的 UNIX password.
(c) 支持程序的显示方式的需求. 如login 需要基于终端的显示, 而dtlogin
需要 X 显示,
而`ftp' 和 `telnet'需要透过网络来认证.
(d) 支持为一个程序配置同时使用多种认证机制.
(e) 可是用户在使用多种认证机制时,不必为同一个密码敲入多次.
(f) 可是用户在认真时需要输入多个密码.
(g) 当底层的认证机制改变时上层软件不需要修改.
(h) 结构为system authentication提供一个 _pluggable_ model.
(i) 必须能满足现有的服务需要.
4. OVERVIEW OF THE PAM FRAMEWORK (纵览PAM的框架)
其核心实际上是一些库函数. 你写的应用程序要调用它们.PAM为你提供了一套入口(the front end).
而这套函数互调用 特定认证机制所定义的模块(the back end).简单的说是这样的:
你调用一个函数仅仅告诉它你要认正,这就足够了.至于用那一种机制来认证是由配置文件规定的.
你只需要看一看返回值就知道认证是
否成功了. 对于开发应用程序的人来说只需要记住几个函数.
ftp telnet login (Applications)
| | |
| |
|
+--------+--------+
|
+-----+-----+
| PAM API |
<-- pam.conf
file
+-----+-----+
|
+--------+--------+
UNIX
Kerberos Smart Cards (Mechanisms)
Figure 1: 基本的 PAM 结构
PAM的功能被分为四个部分: (1) authentication(认证), (2)account
(账号管理), (3) session (对话管理), 和 (4) password (密码管理).
这四个东东都是什么呢?
(a) Authentication management: 包括 `pam_authenticate()'
来认证用户, `pam_setcred()' 来设置 刷新和销毁用户的 credentials.
(b) Account management: 包括 `pam_acct_mgmt()'
来检查用的账号是否还有效.可以被用来检查用户是否超时或账号是否过期.
(c) Session management: 包括 `pam_open_session()' 和
`pam_close_session()' 用于对话过程的管理. 例如: 可以用来纪录用户的连接时间.
一次telnet过程实际上也是一个session.
(d) Password management: `pam_chauthtok()' 用来修该密码.
程序通过调用 `pam_start()'和 `pam_end()' 来开始或结束一次PAM 事务.
`pam_get_item()' 和 `pam_set_item()' 读写有关事务的信息.
可以用`pam_strerror()'来取得错误信息.
如何配置你的系统呢?
pam.conf的一个例子:
#服务名 模块类型 控制标志 模块的名字 选项
#-------
----------- ------------ ----------- -------
login auth
required pam_unix_auth.so nowarn
login session required
pam_unix_session.so
login account required
pam_unix_account.so
ftp auth required pam_skey_auth.so
debug
ftp session required pam_unix_session.so
telnet
session required pam_unix_session.so
login password
required pam_unix_passwd.so
passwd password required
pam_unix_passwd.so
OTHER auth required
pam_unix_auth.so
OTHER session required
pam_unix_session.so
OTHER account required
pam_unix_account.so
`OTHER'
被用来为没有它数指定的服务用的.
选项是随着模块的参数.为一个服务是可以指定多个auth模块的.它们一次被调用来验证用户的身份.这叫做
Stacked
Modules.下面的例子为LOGIN指定了三个模块.
login
|
+--------+--------+
|
| |
session auth account
| | |
+--+--+ +--+--+
+--+--+
| PAM | | PAM | | PAM |
+--+--+ +--+--+
+--+--+
| | |
UNIX UNIX UNIX
session auth
account
|
Kerberos
auth
|
RSA
auth
Figure 2: Stacking With the PAM Architecture
pam.conf中的 `控制标志' 实际上是指明这些 Stacked module
的工作方式.
它可取的值如下:
(a) `required':
该模块的让正必须通过,失败者立即返回错误信息.
(b) `optional':
表示可以忽略它的错误而继续下面一个模块.
(c) `sufficient':
表明该模块的让正已经是足够了,下面的模块就不用调用了.立即
返回成功的消息.
Password-Mapping 密码的映射
多层的模块认证可能会需要多个密码.
则会使用户感到厌烦.一个简单的方法是是用户使用同一个密码而透过mapping机制来加强安全性.
其实是通过一个密码来产生另一个密码,使一个密码可被多个模块使用. 当然要保证第一个密码是强壮的.
auth的模块多有下列 `选项':
(a) `use_first_pass':
使用第一个模块要求输入的密码.不再向用户提示要密码.
(b) `try_first_pass':
先试着使用第一个模块要求输入的密码, 不对再要求用户输入.
(c) `use_mapped_pass':
使用 password-mapping 来得到密码,
不再向用户提示要密码.
(d) `try_mapped_pass':
先试着使用 password-mapping 来得到密码,
不对再向用户提示要密码.
下面的例子可以看懂了吧:
pam.conf
login auth required pam_unix.so
debug
login auth required pam_kerb.so
use_mapped_pass
login auth optional pam_rsa.so
use_first_pass
这种设计使你没有办法来知道执行的具体情况.
当然它也是与应用程序无关的.Notes:在linux下,
也可以用/etc/pam.d中的文件来配置.这些文件的格式与pam.conf类似,只是没有服务名.
其服务名就是它的文件名.入上述的login也可以配置如下:/etc/pam.d/login
auth
required pam_unix.so debug
auth required pam_kerb.so
use_mapped_pass
auth optional pam_rsa.so use_first_pass
APPENDIX A. PAM API'S
A.1. Framework Layer API's
int
pam_start(
char *service_name, // pam.conf中的服务的名字
char *user, // 用户名
struct pam_conv
*pam_conversation,
// 一个用于互交的函数
pam_handle_t **pamh //
一个句柄
);
`pam_start()' 用于初始化一次pam事务
PAM modules 透过struct pam_conv
*pam_conversation中定义的函数来与应用程序
通信.
int
pam_end(
pam_handle_t *pamh,
int pam_status
//上一个pam函数的返回值
);
`pam_end()' 结束一次PAM 事务, 释放相关的内存.
int
pam_set_item(
pam_handle_t *pamh,
int
item_type,
void *item
);
int
pam_get_item(
pam_handle_t *pamh,
int
item_type,
void **item
);
`pam_get_item()' 和 `pam_set_item()' 用于一些特殊处理.读一些信息.
Table 5: Possible Values for Item_type
Item Name Description
---------
-----------
PAM_SERVICE The service name
PAM_USER The
user name
PAM_TTY The tty name
PAM_RHOST The remote host
name
PAM_CONV The pam_conv structure
PAM_AUTHTOK The
authentication token (password)
PAM_OLDAUTHTOK The old
authentication token
PAM_RUSER The remote user name
char *
pam_strerror(
int errnum
);
int
pam_set_data(
pam_handle_t *pamh,
char
*module_data_name,
char *data,
(*cleanup)(pam_handle_t
*pamh, char *data,
int error_status)
);
int
pam_get_data(
pam_handle_t *pamh,
char
*module_data_name,
void **datap
);
用于读取与模块相关的数据.
A.2. Authentication API's
int
pam_authenticate(
pam_handle_t *pamh,
int
flags
);
int
pam_setcred(
pam_handle_t *pamh,
int
flags
);
A.3. Account Management API
int
pam_acct_mgmt(
pam_handle_t *pamh,
int
flags
);
A.4. Session Management API's
int
pam_open_session(
pam_handle_t *pamh,
int
flags
);
int
pam_close_session(
pam_handle_t *pamh,
int
flags
);
A.5. Password Management API's
int
pam_chauthtok(
pam_handle_t *pamh,
int
flags
);
APPENDIX B. SAMPLE PAM APPLICATION
/*
/etc/pam.conf
check_user auth required
/lib/security/pam_unix_auth.so
check_user account required
/lib/security/pam_unix_acct.so
注意要和你的系统一致redhat是这样
编译时要这样:
gcc
check.c -ldl -lpam -lpam_misc -o check_user
*/
#include
#include
#include
static struct pam_conv conv = {
misc_conv,
//定义在pam_misc.h中, 方便你编程
NULL
};
int main(int argc, char *argv[])
{
pam_handle_t
*pamh=NULL;
int retval;
const char *user="nobody";
if(argc == 2) {
user = argv[1];
}
if(argc > 2) {
fprintf(stderr, "Usage: check_user
[username]\n");
exit(1);
}
retval = pam_start("check_user", user, &conv,
&pamh);
/* 开始 */
if (retval ==
PAM_SUCCESS)
retval = pam_authenticate(pamh, 0);
/*
认证是不是该用户? 提示你输入一个密码 */
if (retval == PAM_SUCCESS)
retval = pam_acct_mgmt(pamh,
0);
/* 账号是否有效? */
if (retval == PAM_SUCCESS) {
fprintf(stdout,
"Authenticated\n");
} else {
fprintf(stdout, "Not
Authenticated\n");
}
if (pam_end(pamh,retval) != PAM_SUCCESS) {
/* 结束
*/
pamh = NULL;
fprintf(stderr, "check_user: failed to
release authenticator\n");
exit(1);
}
return ( retval == PAM_SUCCESS ? 0:1 ); /* indicate success
*/
}
- Bricks Team