跳过导航链接 | |
退出打印视图 | |
Oracle Solaris 11 开发者安全性指南 Oracle Solaris 11 Information Library (简体中文) |
本节提供了使用多个 PAM 函数的应用程序样例。
以下将以 PAM 使用者应用程序作为示例。 该示例是一个基本的终端锁定应用程序,用于检验尝试访问终端的用户。
此示例执行以下步骤:
初始化 PAM 会话。
PAM 会话通过调用 pam_start(3PAM) 函数来启动。 调用任何其他 PAM 函数之前,PAM 使用者应用程序必须首先建立 PAM 会话。
pam_start(3PAM) 函数采用以下参数:
plock-服务名,即应用程序的名称。 PAM 框架使用服务名来确定配置文件 /etc/pam.conf 中适用的规则。 服务名通常用于日志记录和错误报告。
pw->pw_name-用户名,即 PAM 框架所作用的用户的名称。
&conv-对话函数 conv,用于提供 PAM 与用户或应用程序进行通信的通用方法。 对话函数是必需的,因为 PAM 模块无法了解如何进行通信。 通信可以采用 GUI、命令行、智能读卡器或其他设备等方式进行。 有关更多信息,请参见编写对话函数。
&pamh-PAM 句柄 pamh,即 PAM 框架用于存储有关当前操作信息的不透明句柄。 成功调用 pam_start() 后将返回此句柄。
注 - 调用 PAM 接口的应用程序必须具有足够的特权才能执行任何所需的操作,如验证、口令更改、进程凭证处理或审计状态初始化。 在该示例中,应用程序必须可以读取 /etc/shadow 才能验证本地用户的口令。
验证用户。
应用程序将调用 pam_authenticate(3PAM) 来验证当前用户。 通常,系统会要求用户输入口令或其他验证令牌,具体取决于验证服务的类型。 PAM 框架会调用 /etc/pam.conf 中为验证服务 auth 列出的模块。 服务名 plock 用于确定要使用的 pam.conf 项。 如果不存在与 plock 对应的项,则缺省情况下会使用 other 中的项。 如果应用程序配置文件中明确禁止使用 NULL 口令,则应该传递 PAM_DISALLOW_NULL_AUTHTOK 标志。 Oracle Solaris 应用程序将检查 /etc/default/login 中的 PASSREQ=YES 设置。
检查帐户有效性。
该示例使用 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 使用者应用程序样例的源代码。
注 - 此示例的源代码也可以通过 Oracle 下载中心获取。 请参见 http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html。
示例 3-1 PAM 使用者应用程序样例
/* * Copyright (c) 2005, 2011, 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*/ }
前面的示例 3-1 是一个简单的应用程序,它仅说明了几个主要的 PAM 函数。 本节将介绍一些其他有用的 PAM 函数。
成功验证用户后,将会调用 pam_open_session(3PAM) 函数打开新会话。
调用 pam_getenvlist(3PAM) 函数可以建立新环境。pam_getenvlist() 将返回要与现有环境合并的新环境。