Solaris 开发者安全性指南

编写对话函数

PAM 模块或应用程序可以采用多种方式与用户进行通信:命令行、对话框等。因此,与用户通信的 PAM 消费方的设计者需要编写对话函数。对话函数用于在用户与模块之间传递消息,而与通信方式无关。对话函数可从对话函数回调 pam_message 参数中的 msg_style 参数派生消息类型。请参见 pam_start(3PAM)

开发者不应对 PAM 如何与用户进行通信做出假设。而应用程序应该与用户交换消息,直到操作完成为止。应用程序会显示与对话函数对应的消息字符串,但不进行解释或修改。一条单独的消息中可以包含多行、控制字符或额外的空格。请注意,服务模块负责本地化发送给对话函数的任何字符串。

下面提供了对话函数样例 pam_tty_conv()pam_tty_conv() 采用以下参数:

函数样例从 stdin 获取用户输入。例程需要为响应缓冲区分配内存。可以设置最大值 PAM_MAX_NUM_MSG 以限制消息的数量。如果对话函数返回错误,则对话函数会负责清除并释放为响应分配的任何内存。此外,对话函数必须将响应指针设置为 NULL。请注意,应使用零填充方法来完成内存清除。对话函数的调用方负责释放返回给它的所有响应。要进行对话,该函数将循环处理来自用户应用程序的消息。有效的消息将写入 stdout,所有错误将写入 stderr


注 –

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



示例 3–3 PAM 对话函数

/* 

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

 * Use is subject to license terms. 

 */

 

#pragma ident	"@(#)pam_tty_conv.c	1.4	05/02/12 SMI"  



#define	__EXTENSIONS__	/* to expose flockfile and friends in stdio.h */ 

#include <errno.h>

#include <libgen.h>

#include <malloc.h>

#include <signal.h>

#include <stdio.h>

#include <stdlib.h>

#include <strings.h>

#include <stropts.h>

#include <unistd.h>

#include <termio.h>

#include <security/pam_appl.h>



static int ctl_c;	/* was the conversation interrupted? */



/* ARGSUSED 1 */

static void

interrupt(int x)

{

	ctl_c = 1;

}



/* getinput -- read user input from stdin abort on ^C

 *	Entry	noecho == TRUE, don't echo input.

 *	Exit	User's input.

 *		If interrupted, send SIGINT to caller for processing.

 */

static char *

getinput(int noecho)

{

	struct termio tty;

	unsigned short tty_flags;

	char input[PAM_MAX_RESP_SIZE];

	int c;

	int i = 0;

	void (*sig)(int);



	ctl_c = 0;

	sig = signal(SIGINT, interrupt);

	if (noecho) {

		(void) ioctl(fileno(stdin), TCGETA, &tty);

		tty_flags = tty.c_lflag;

		tty.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);

		(void) ioctl(fileno(stdin), TCSETAF, &tty);

	}



	/* go to end, but don't overflow PAM_MAX_RESP_SIZE */

	flockfile(stdin);

	while (ctl_c == 0 &&

	    (c = getchar_unlocked()) != '\n' &&

	    c != '\r' &&

	    c != EOF) {

		if (i < PAM_MAX_RESP_SIZE) {

			input[i++] = (char)c;

		}

	}

	funlockfile(stdin);

	input[i] = '\0';

	if (noecho) {

		tty.c_lflag = tty_flags;

		(void) ioctl(fileno(stdin), TCSETAW, &tty);

		(void) fputc('\n', stdout);

	}

	(void) signal(SIGINT, sig);

	if (ctl_c == 1)

		(void) kill(getpid(), SIGINT);



	return (strdup(input));

}



/* Service modules do not clean up responses if an error is returned.

 * Free responses here.

 */

static void

free_resp(int num_msg, struct pam_response *pr)

{

	int i;

	struct pam_response *r = pr;



	if (pr == NULL)

		return;



	for (i = 0; i < num_msg; i++, r++) {



		if (r->resp) {

			/* clear before freeing -- may be a password */

			bzero(r->resp, strlen(r->resp));

			free(r->resp);

			r->resp = NULL;

		}

	}

	free(pr);

}



/* ARGSUSED */

int

pam_tty_conv(int num_msg, struct pam_message **mess,

    struct pam_response **resp, void *my_data)

{

	struct pam_message *m = *mess;

	struct pam_response *r;

	int i;



	if (num_msg <= 0 || num_msg >= PAM_MAX_NUM_MSG) {

		(void) fprintf(stderr, "bad number of messages %d "

		    "<= 0 || >= %d\n",

		    num_msg, PAM_MAX_NUM_MSG);

		*resp = NULL;

		return (PAM_CONV_ERR);

	}

	if ((*resp = r = calloc(num_msg,

	    sizeof (struct pam_response))) == NULL)

		return (PAM_BUF_ERR);



	/* Loop through messages */

	for (i = 0; i < num_msg; i++) {

		int echo_off;



		/* bad message from service module */

		if (m->msg == NULL) {

			(void) fprintf(stderr, "message[%d]: %d/NULL\n",

			    i, m->msg_style);

			goto err;

		}



		/*

		 * fix up final newline:

		 * 	removed for prompts

		 * 	added back for messages

		 */

		if (m->msg[strlen(m->msg)] == '\n')

			m->msg[strlen(m->msg)] = '\0';



		r->resp = NULL;

		r->resp_retcode = 0;

		echo_off = 0;

		switch (m->msg_style) {



		case PAM_PROMPT_ECHO_OFF:

			echo_off = 1;

			/*FALLTHROUGH*/



		case PAM_PROMPT_ECHO_ON:

			(void) fputs(m->msg, stdout);



			r->resp = getinput(echo_off);

			break;



		case PAM_ERROR_MSG:

			(void) fputs(m->msg, stderr);

			(void) fputc('\n', stderr);

			break;



		case PAM_TEXT_INFO:

			(void) fputs(m->msg, stdout);

			(void) fputc('\n', stdout);

			break;



		default:

			(void) fprintf(stderr, "message[%d]: unknown type "

			    "%d/val=\"%s\"\n",

			    i, m->msg_style, m->msg);

			/* error, service module won't clean up */

			goto err;

		}

		if (errno == EINTR)

			goto err;



		/* next message/response */

		m++;

		r++;

	}

	return (PAM_SUCCESS);



err:

	free_resp(i, r);

	*resp = NULL;

	return (PAM_CONV_ERR);

}