介绍PAM
BY XUNDI 2000-05-08
本人水平有限,错误在所难免,尽请指教xundi@xfocus.org
原文BY - P H R A C K M A G A Z I N E -56-13.html
----|介绍
此文主要介绍了PAM模块的知识,最后我还加上了一个PAM的简单使用例子,希望有用。
PAM的英文全称是Pluggable Authentication Module系统,即此程序是有关执行
用户鉴别和帐号维护的服务。鉴别部分通常通过一(合法性)质询-回应的交互
来完成的。使用PAM,管理员可以通过不重编辑鉴定程序来定制一些使用方法。
PAM有四部分组成,第一部分是libpam,是实现PAM API的库,第二部分是PAM配置
文件,/etc/pam.conf,第三部分有一套动态可装载两进位对象组成,常常用来调
用一些处理实际鉴别(authentication)工作的服务模块。最后模块是使用PAM API
的系统命令组成,如login,us,ftp,telnet,etc...
----| LIBPAM
PAM API的认证(authentication)常规程序有三个主要函数组成:
pam_start( const char *service_name, const char *username,
const struct pam_conv *conv, pam_handle_t **pamh_p );
pam_end( pam_handle_t *pamh, int exit_status );
pam_authenticate( pam_handle_t *pamh, int flags );
pam_start()和pam_end()函数是开始和结束一个PAM会话,传递给pam_start()
函数的参数如下所示:
+ service_name: 一定义在pam.conf中的特殊服务(请看下面)
+ username: 需要鉴权的用户登录名。
+ conv: 一指向pam_conf结构的指针
+ pamh_p: 一双精度指向pam_handle_t结构的指针。PAM构架会分配或不
分配内存给这个结构,并且一应用程序不能直接访问它。它基本上是用来通过PAM
构架(framework)来处理多个并发的PAM会话。
pam_conv结构如下所示:
struct pam_conv {
int (*conv)(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr);
void *appdata_ptr;
}
*conv是指向PAM对话函数的指针,它将会在下面详细讨论,appdata_ptr指针指
向特殊应用程序数据,它并不常用。
pam_end()函数的参数由在pam_start()函数中填充的pam_handle_t*组成,并且返回
的是exit状态。exit状态正常情况下是PAM_SUCCESS。pam_edn()会收回与
pam_handle_t*相关联的内存,并且任何企图重用这个句柄将返回一个seg fault
错误。
pam_authenticate()函数也再一次由通过pam_start()填充的pam_handle_t*组成,
并且可选的标志(flags)可以传递给结构(framework)
另外一些可以应用于应用程序的PAM API函数如下所示(可以参看你的系统文档):
+ pam_set_item() - 为PAM会话写状态信息
+ pam_get_item() - 为PAM获得状态信息
+ pam_acct_mgmt() - 检查当前用户帐号是否合法
+ pam_open_session() - 开始一个新的会话
+ pam_close_session() - 关闭当前会话进程
+ pam_setcred() -管理用户信任资格(credentials)
+ pam_chauthtok() - 改变用户的鉴权(authentication)token牌
+ pam_strerror() - 返回错误字符串,类似与perror()函数
----| PAM.CONF
PAM配置文件通常位于/etc/pam.conf,它可以分为四个部分:鉴权(authentication),
帐号管理,会话管理和密码管理。一个标准的一行配置如下所示:
loginauthrequired/usr/lib/security/pam_unix.so.1try_first_pass
第一栏是服务名,这是参照在pam_start()函数中的第一个参数。如果通过pam_start()
的服务请求不列在pam.conf,则将使用默认的"other"服务。"other"服务名字可以
是"su"和"rlogin",如果服务名不止一次说明,模块将会提示"stacked",并且
framework将会通过第三栏的值来决定。
第二栏指示这特定的服务将执行何种类型的行为,合法的值是:
"auth"为鉴权行为;
"account"为帐号管理;
"session"为会话管理;
"password"为密码管理;
不是所有的应用程序需要使用每一个行为,如,su仅仅需要使用"auth"鉴权行为,
"passwd"只需要来使用"password"管理行为。
第三栏作为一个控制类型的一栏。如果用户在鉴定行为中失败,它指示PAM 架构
(framework)行为。此栏正确的值为:"requisite","required", "sufficient", "optional":
+ "requisite"意思指如果用户在鉴定(quthentication)时失败,PAM framework
会立即返回一失败信息,其中没有其他模块调用。
+ "required"指示如果一个用户鉴定(quthentication)时失败,PAM framework
只在调用其他所有模块后在返回失败信息。这样做的话用户会不知道哪个模块
鉴权被拒绝,如果一个用户成功被鉴别,所有"required"模块必须返回成功。
+ "optional"意思是用户将被允许访问即使鉴权失败,失败的结果是下一个在
堆栈中的模块将被处理。
+ "sufficient"指的是如果用户传递一这特定模块,PAM framework会立即返回
成功,即使随后的模块有"requisite"或者"required"控制值,类似于"optional"
"sufficient"回允许访问即使鉴全步骤失败。
Note that if any module returns success, the user will succeed authentication
with the only exception being if the user previously failed to authenticate
with a "required" module.
在pam.conf第四栏中是认证模块的路径,各个系统路径不同,如在Linux-PAM系统
中PAM模块在/usr/lib目录下,而Solaris在/usr/lib/security中维护模块。
第五栏是一个空格分开的module-dependent选项列表,是传递给认证模块调用。
---| 模块 MODULES
每一个PAM模块本质上是一个必须输出特定函数的库,这些函数可以被PAM framework
调用,通过库输出的函数有如下列表:
+ pam_sm_authenticate()
+ pam_sm_setcred()
+ pam_sm_acct_mgmt()
+ pam_sm_open_session()
+ pam_sm_close_session()
+ pam_sm_chauthtok()
如果实现者不决定在一模块内支持特定的操作,模块会为此操作返回PAM_SUCCESS,
例如:如果一个模块设计成为不支持帐号管理(account management),
pam_sm_acct_mgmt()函数会简单的返回PAM_SUCCESS。
pam_sm_authenticate()是按下面的方式声明的:
extern int pam_sm_authenticate( pam_handle_t *pamh, int flags,
int argc, char **argv);
上面的指针是指想一个PAM句柄--已经通过framework填充了,flags是应用程序
调用pam_authenticate()传递给framework的一组标志,argc和argv是在pam.conf
中此服务的选项参数的数字和值。
一个简单的pam_unix 模块中的pam_sm_authenticate()函数应该如下所示:
#include
#include <...>
extern int
pam_sm_authenticate( pam_handle_t *pamh, int flgs, int c, char **v )
{
char *user;
char *passwd;
struct passwd *pwd;
int ret;
/* ignore flags and optional arguments */
if ( (ret = pam_get_user( ..., &user )) != PAM_SUCCESS )
return ret;
if ( (ret = pam_get_pass( ..., &passwd )) != PAM_SUCCESS )
return ret;
if ( (pwd = getpwnam(user)) != NULL ) {
if ( !strcmp(pwd->pw_passwd, crypt(passwd)) )
return PAM_SUCCESS;
else
return PAM_AUTH_ERR;
}
return PAM_AUTH_ERR;
}
当然,这个函数非常单纯化,但它演示了pam_sm_authenticate()函数的基本功能。
它从Framework中获得用户的LOGIN名字和密码,在获得用户加密的密码,最后调用
crypt()函数并把结果和加密了的系统密码进行比较。pam_get_*()函数调用Framework,
----|应用程序 APPLICATION
一个应用程序处理PAM部分必须由pam_start()和pam_end()组成和一PAM对话函数。
比较幸运的是,user-space PAM API定义的比较成熟和稳定所以对话函数是比较
模板型的代码(至少对命令行应用程序)。一个简单的su PAM实现所下所示:
#include
#include <...>
int su_conv(int, const struct pam_message **,
struct pam_response **, void *);
static struct pam_conv pam_conv = { su_conv, NULL };
int
main( int argc, char **argv )
{
pam_handle_t *pamh;
int ret;
struct passwd *pwd;
/* assume arguments are correct and argv[1] is the username */
ret = pam_start("su", argv[1], &pam_conv, &pamh);
if ( ret == PAM_SUCCESS )
ret = pam_authenticate(pamh, 0);
if ( ret == PAM_SUCCESS )
ret = pam_acct_mgmt(pamh,
推荐文章 |
