接前一篇文章:PAM从入门到精通(十五)
本文参考:
《The Linux-PAM Application Developers' Guide》
PAM 的应用开发和内部实现源码分析
先再来重温一下PAM系统架构:
更加形象的形式:
六、整体流程示例
2. 更为完整的例程及解析
上一回讲解了《The Linux-PAM Application Developers' Guide》的“Chapter 8. An example application ”中给出的一个示例代码。官方文档中的例程太过简单了,只调用了4个应用接口函数,并且也没有更进一步的处理。下边再给出一个更为详细复杂的例程,其涵盖了大多数PAM应用接口函数(实际上就是前边各文章中“实例1. 一般性代码”的串联所组成的整体流程)。
代码如下:
/* 使用PAM所必需的两个头文件*/
#include <security/pam_appl.h>
#include <security/pam_misc.h>
static struct pam_conv conv = {
misc_conv,
NULL
}
void main(int argc, char *argv[], char **renvp)
{
pam_handle_t *pamh = NULL;
int status;
/* 初始化,并提供一个回调函数 */
if ((pam_start("login", user_name, &conv, &pamh)) != PAM_SUCCESS)
exit(1);
/* 设置一些关于认证用户信息的参数 */
pam_set_item(pamh, PAM_TTY, ttyn);
pam_set_item(pamh, PAM_RHOST, remote_host);
while (!authenticated && retry < MAX_RETRIES)
{
status = pam_authenticate(pamh, 0);/* 认证,检查用户输入的密码是否正确 */
}
/* 认证失败则应用程序退出*/
if (status != PAM_SUCCESS)
{
……
exit(1);
}
/* 通过了密码认证之后再调用帐号管理API,检查用户帐号是否已经过期 */
if ((status = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS)
{
if (status == PAM_AUTHTOK_EXPIRED)
{
status = pam_chauthtok(pamh, 0); /* 过期则要求用户更改密码 */
if (status != PAM_SUCCESS)
exit(1);
}
}
/* 通过帐户管理检查之后则打开会话 */
if (status = pam_open_session(pamh, 0) != PAM_SUCCESS)
exit(status);
……
/* 建立认证服务的用户证书*/
status = pam_setcred(pamh, PAM_ESTABLISH_CRED);
if (status != PAM_SUCCESS)
exit(status);
……
pam_end(pamh, PAM_SUCCESS); /* PAM事务的结束 */
……
}
再来详解一下这个更为全面的流程步骤:
(1)pam_start函数
代码片段:
/* 初始化,并提供一个回调函数 */
if ((pam_start("login", user_name, &pam_conv, &pamh)) != PAM_SUCCESS)
exit(1);
作用:
PAM事务初始化。pam_start函数创建PAM上下文并启动PAM事务。它是应用程序需要调用的第一个PAM函数。事务状态完全包含在此句柄标识的结构中,因此可以并行处理多个事务。但是不可能对不同的事务使用相同的句柄,每个新的上下文都需要一个新的句柄。
参数详解:
- const char *service_name
service_name参数指定要应用的服务的名称,并将作为PAM_SEVICE项存储在新上下文中。服务的策略将从文件/etc/pam.d/service_name中读取,如果该文件不存在,则从/etc/pam.conf中读取。如:passwd(/etc/pam.d/passwd)、useradd(/etc/pam.d/useradd)等。
此处传给service_name的实参为"login",服务的策略将从文件/etc/pam.d/login中读取。该文件内容如下:
$ ls /etc/pam.d/login
/etc/pam.d/login
$ cat /etc/pam.d/login
# Begin /etc/pam.d/login
# Set failure delay before next prompt to 3 seconds
auth optional pam_faildelay.so delay=3000000
# Check to make sure that the user is allowed to login
auth requisite pam_nologin.so
# Check to make sure that root is allowed to login
# Disabled by default. You will need to create /etc/securetty
# file for this module to function. See man 5 securetty.
#auth required pam_securetty.so
# Additional group memberships - disabled by default
#auth optional pam_group.so
# include system auth settings
auth include system-auth
# check access for the user
account required pam_access.so
# include system account settings
account include system-account
# Set default environment variables for the user
session required pam_env.so
# Set resource limits for the user
session required pam_limits.so
# Display date of last login - Disabled by default
#session optional pam_lastlog.so
# Display the message of the day - Disabled by default
#session optional pam_motd.so
# Check user's mail - Disabled by default
#session optional pam_mail.so standard quiet
# include system session and password settings
session include system-session
password include system-password
# End /etc/pam.d/login
- const char *user
user参数可以指定目标用户的名称,并将存储为PAM_USER项。如果参数为NULL,则模块必须在必要时询问此项。
此处传给user的实参为用户通过命令行或图形界面输入的用户名。
- const struct pam_conv *pam_conversation
pam_conversation参数指向描述要使用的会话函数的结构pam_conv。应用程序必须为加载的模块与应用程序之间的直接通信提供此功能。
此处传给pam_conversation的实参为&conv。
- pam_handle_t **path
在成功返回(PAM_SUCCESS)之后,pamh的内容是一个句柄,它包含对PAM函数的连续调用的PAM上下文。在错误情况下,pamh的内容未定义。
pam_handle_t是一个盲结构,应用程序不应试图直接探测其信息。而是应该通过PAM库提供的函数pam_set_item和pam_get_item。PAM句柄不能同时用于多个身份验证,只要以前没有对其调用pam_end函数。
此处传给path的实参为main函数中定义的pam_handle_t *pamh的地址&pamh。
(8)pam_start函数
代码片段:
pam_end(pamh, PAM_SUCCESS); /* PAM事务的结束 */
作用:
PAM事务终止。pam_end函数终止pam事务,是应用程序应在pam上下文中调用的最后一个函数。此函数为与pam_set_item和pam_get_item函数关联的项释放了所有内存。调用pam_end()后,与此类对象关联的指针不再有效。
参数详解:
- pam_handle_t *pamh
pamh参数是通过先前调用pam_start()获得的身份验证句柄。返回后,句柄pamh不再有效,并且与之相关的所有内存都将无效。
此处传给pamh的实参为上边调用pam_start()的时候得到的那个pamh。
- int pam_status
pam_status参数应设置为上次PAM库调用返回给应用程序的值。
pam_status获取的值用作模块特定回调函数cleanup的参数。通过这种方式,可以向模块发出拆除过程的通过/失败性质的通知,并在模块被取消链接之前执行适合模块的任何最后一分钟任务。此参数可以与PAM_DATA_SILENT进行逻辑“或”运算,以指示模块不应过于认真地对待调用。它通常用于指示库的当前关闭处于分支进程中,并且父进程将负责清理当前进程空间之外的内容(如文件等)。
此处传给pam_status的实参为PAM_SUCCESS。
更多讲解请看下回。