面向开发者的 Oracle® Solaris 11 安全性指南

退出打印视图

更新时间: 2014 年 7 月
 
 

简单 PAM 使用者示例

以下将以 PAM 使用者应用程序作为示例。该示例是一个基本的终端锁定应用程序,用于检验尝试访问终端的用户。

    此示例执行以下步骤:

  1. 初始化 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 接口的应用程序必须具有足够的特权才能执行任何所需的操作,如验证、口令更改、进程凭证处理或审计状态初始化。在该示例中,应用程序必须可以读取 /etc/shadow 才能验证本地用户的口令。
  2. 验证用户。

    应用程序将调用 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 文件中进行搜索。

  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 使用者应用程序样例的源代码。


注 - 此示例的源代码也可以通过 Oracle 下载中心获取。请参见 http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html
示例 3-1  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*/
}