以下将以 PAM 使用者应用程序作为示例。该示例是一个基本的终端锁定应用程序,用于检验尝试访问终端的用户。
此示例执行以下步骤:
初始化 PAM 会话。
PAM 会话通过调用 pam_start(3PAM) 函数来启动。调用任何其他 PAM 函数之前,PAM 使用者应用程序必须首先建立 PAM 会话。
pam_start(3PAM) 函数采用以下参数:
plock-服务名,即应用程序的名称。PAM 框架使用服务名称来确定配置文件、/etc/pam.conf 或 /etc/pam.d 中的哪些规则可用。服务名通常用于日志记录和错误报告。
pw->pw_name-用户名,即 PAM 框架所作用的用户的名称。
&conv-对话函数 conv,用于提供 PAM 与用户或应用程序进行通信的通用方法。对话函数是必需的,因为 PAM 模块无法了解如何进行通信。通信可以采用 GUI、命令行、智能读卡器或其他设备等方式进行。有关更多信息,请参见Writing Conversation Functions。
&pamh-PAM 句柄 pamh,即 PAM 框架用于存储有关当前操作信息的不透明句柄。成功调用 pam_start() 后将返回此句柄。
验证用户。
应用程序将调用 pam_authenticate(3PAM) 来验证当前用户。通常,系统会要求用户输入口令或其他验证令牌,具体取决于验证服务的类型。
PAM 框架会调用 /etc/pam.conf 或 /etc/pam.d/plock(使用 Oracle Solaris 11.1 OS 时)中为服务名称 plock(对应于验证服务模块类型 (auth))配置的模块。如果在 /etc/pam.conf 或 /etc/pam.d/plock 中不存在与 plock 服务对应的 auth 条目,则会在 /etc/pam.conf 中搜索与 other 服务对应的 auth 条目,最后在 /etc/pam.d/other 文件中进行搜索。
检查帐户有效性。
该示例使用 pam_acct_mgmt(3PAM) 函数检查已验证的用户帐户的有效性。在该示例中,pam_acct_mgmt() 用于检查口令是否到期。
pam_acct_mgmt() 函数还会使用 PAM_DISALLOW_NULL_AUTHTOK 标志。如果 pam_acct_mgmt() 返回 PAM_NEW_AUTHTOK_REQD,则应调用 pam_chauthtok(3PAM) 以允许已验证的用户更改口令。
如果系统发现口令已到期,则会强制用户更改口令。
该示例使用循环调用 pam_chauthtok(),直到返回成功信息为止。如果用户成功更改其验证信息(通常为口令),则 pam_chauthtok() 函数将返回成功信息。在该示例中,循环将继续直到返回成功信息为止。通常,应用程序会设置终止前应尝试的最多次数。
pam_setcred(3PAM) 函数用于建立、修改或删除用户凭证。pam_setcred() 通常在验证用户之后进行调用。调用是在检验帐户后和打开会话前进行的。将 pam_setcred() 函数与 PAM_ESTABLISH_CRED 标志结合使用可建立新的用户会话。如果该会话是对现有会话(如对于 lockscreen)的更新,则应调用带有 PAM_REFRESH_CRED 标志的 pam_setcred()。如果会话要更改凭证(如使用 su 或承担角色),则应调用带有 PAM_REINITIALIZE_CRED 标志的 pam_setcred()。
关闭 PAM 会话。
PAM 会话通过调用 pam_end(3PAM) 函数进行关闭。pam_end() 还将释放所有的 PAM 资源。
以下示例给出了 PAM 使用者应用程序样例的源代码。
/*
* Copyright (c) 2005, 201
2, Oracle and/or its affiliates. All rights reserved.
*/
#include <sys/types.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>
#include <signal.h>
#include <pwd.h>
#include <errno.h>
#include <security/pam_appl.h>
extern int pam_tty_conv(int num_msg, struct pam_message **msg,
struct pam_response **response, void *appdata_ptr);
/* Disable keyboard interrupts (Ctrl-C, Ctrl-Z, Ctrl-\) */
static void
disable_kbd_signals(void)
{
(void) signal(SIGINT, SIG_IGN);
(void) signal(SIGTSTP, SIG_IGN);
(void) signal(SIGQUIT, SIG_IGN);
}
/* Terminate current user session, i.e., logout */
static void
logout()
{
pid_t pgroup = getpgrp();
(void) signal(SIGTERM, SIG_IGN);
(void) fprintf(stderr, "Sorry, your session can't be restored.\n");
(void) fprintf(stderr, "Press return to terminate this session.\n");
(void) getchar();
(void) kill(-pgroup, SIGTERM);
(void) sleep(2);
(void) kill(-pgroup, SIGKILL);
exit(-1);
}
int
/*ARGSUSED*/
main(int argc, char *argv)
{
struct pam_conv conv = { pam_tty_conv, NULL };
pam_handle_t *pamh;
struct passwd *pw;
int err;
disable_kbd_signals();
if ((pw = getpwuid(getuid())) == NULL) {
(void) fprintf(stderr, "plock: Can't get username: %s\n",
strerror(errno));
exit(1);
}
/* Initialize PAM framework */
err = pam_start("plock", pw->pw_name, &conv, &pamh);
if (err != PAM_SUCCESS) {
(void) fprintf(stderr, "plock: pam_start failed: %s\n",
pam_strerror(pamh, err));
exit(1);
}
/* Authenticate user in order to unlock screen */
do {
(void) fprintf(stderr, "Terminal locked for %s. ", pw->pw_name);
err = pam_authenticate(pamh, 0);
if (err == PAM_USER_UNKNOWN) {
logout();
} else if (err != PAM_SUCCESS) {
(void) fprintf(stderr, "Invalid password.\n");
}
} while (err != PAM_SUCCESS);
/* Make sure account and password are still valid */
switch (err = pam_acct_mgmt(pamh, 0)) {
case PAM_SUCCESS:
break;
case PAM_USER_UNKNOWN:
case PAM_ACCT_EXPIRED:
/* User not allowed in anymore */
logout();
break;
case PAM_NEW_AUTHTOK_REQD:
/* The user's password has expired. Get a new one */
do {
err = pam_chauthtok(pamh, 0);
} while (err == PAM_AUTHTOK_ERR);
if (err != PAM_SUCCESS)
logout();
break;
default:
logout();
}
if (pam_setcred(pamh, PAM_REFRESH_CRED) != PAM_SUCCESS){
logout();
}
(void) pam_end(pamh, 0);
return(0);
/*NOTREACHED*/
}