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 */ } (责任编辑:IT) |