Solaris 开发者安全性指南

简单 PAM 消费方示例

以下将以 PAM 消费方应用程序作为示例。该示例是一个基本的终端锁定应用程序,用于验证尝试访问终端的用户。该示例执行以下步骤:

  1. 初始化 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 才能验证本地用户的口令。


  2. 验证用户。

    应用程序将调用 pam_authenticate(3PAM) 来验证当前用户。通常,系统会要求用户输入口令或其他验证令牌,具体取决于验证服务的类型。PAM 框架会调用 /etc/pam.conf 中为验证服务 auth 列出的模块。服务名 plock 用于确定要使用的 pam.conf 项。如果不存在与 plock 对应的项,则缺省情况下会使用 other 中的项。 如果应用程序配置文件中明确禁止使用 NULL 口令,则应该传递 PAM_DISALLOW_NULL_AUTHTOK 标志。Solaris 应用程序将检查 /etc/default/login 中的 PASSREQ=YES 设置。

  3. 检查帐户有效性。

    该示例使用 pam_acct_mgmt(3PAM) 函数检查已验证的用户帐户的有效性。在该示例中,pam_acct_mgmt() 用于检查口令的失效期。

    pam_acct_mgmt() 函数还会使用 PAM_DISALLOW_NULL_AUTHTOK 标志。如果 pam_acct_mgmt() 返回 PAM_NEW_AUTHTOK_REQD,则应调用 pam_chauthtok(3PAM) 以允许已验证的用户更改口令。

  4. 如果系统发现口令已过期,则会强制用户更改口令。

    该示例使用循环调用 pam_chauthtok(),直到返回成功信息为止。如果用户成功更改其验证信息(通常为口令),则 pam_chauthtok() 函数将返回成功信息。在该示例中,循环将继续直到返回成功信息为止。通常,应用程序会设置终止前应尝试的最多次数。

  5. 调用 pam_setcred(3PAM)

    pam_setcred(3PAM) 函数用于建立、修改或删除用户凭证。pam_setcred() 通常在验证用户之后进行调用。调用是在验证帐户后和打开会话前进行的。将 pam_setcred() 函数与 PAM_ESTABLISH_CRED 标志结合使用可建立新的用户会话。如果该会话是对现有会话(如对于 lockscreen)的更新,则应调用带有 PAM_REFRESH_CRED 标志的 pam_setcred()。如果会话要更改凭证(如使用 su 或承担角色),则应调用带有 PAM_REINITIALIZE_CRED 标志的 pam_setcred()

  6. 关闭 PAM 会话。

    PAM 会话通过调用 pam_end(3PAM) 函数进行关闭。pam_end() 还将释放所有的 PAM 资源。

以下示例给出了 PAM 消费方应用程序样例的源代码。


注 –

此示例的源代码也可以通过 Sun 下载中心获取。请访问 http://www.sun.com/download/products.xml?id=41912db5



示例 3–2 PAM 消费方应用程序样例

/*

 * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.

 * Use is subject to license terms.

 */



#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_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;

	}



if (pam_setcred(pamh, PAM_REFRESH_CRED) != pam_success){

    logout();

}



	(void) pam_end(pamh, 0);

	exit(0);

	/*NOTREACHED*/

}