PAM从入门到精通(十六)

接前一篇文章:PAM从入门到精通(十五)

本文参考:

《The Linux-PAM Application Developers' Guide》

PAM 的应用开发和内部实现源码分析

先再来重温一下PAM系统架构:

更加形象的形式:

六、整体流程示例

2. 更为完整的例程及解析

上一回讲解了《The Linux-PAM Application Developers' Guide》的"Chapter 8. An example application "中给出的一个示例代码。官方文档中的例程太过简单了,只调用了4个应用接口函数,并且也没有更进一步的处理。下边再给出一个更为详细复杂的例程,其涵盖了大多数PAM应用接口函数(实际上就是前边各文章中"实例1. 一般性代码"的串联所组成的整体流程)。

代码如下:

cpp 复制代码
/* 使用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函数

代码片段:

cpp 复制代码
    /* 初始化,并提供一个回调函数 */
    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中读取。该文件内容如下:

bash 复制代码
$ 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函数

代码片段:

cpp 复制代码
    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。

更多讲解请看下回。

相关推荐
ManageEngine卓豪9 个月前
如何监控特权帐户,保护敏感数据
pam·敏感数据保护·特权账户监控
ManageEngine卓豪1 年前
适用于 Linux 和 Unix 的特权访问管理
linux·unix·pam·特权访问管理
岬淢箫声1 年前
如何通过PAM禁止部分用户登录
linux·ubuntu·centos·ssh·pam·sssd
ManageEngine卓豪1 年前
零信任特权访问管理
零信任·pam·特权访问·特权访问管理
蓝天居士1 年前
PAM从入门到精通(十九)
pam·linux安全
蓝天居士1 年前
PAM从入门到精通(十五)
pam·linux安全
蓝天居士1 年前
PAM从入门到精通(十二)
pam·linux安全
蓝天居士1 年前
PAM从入门到精通(十)
pam·linux安全
蓝天居士1 年前
PAM从入门到精通(七)
pam·linux安全