跳过导航链接 | |
退出打印视图 | |
![]() |
Oracle Solaris 11 开发者安全性指南 Oracle Solaris 11.1 Information Library (简体中文) |
PAM 模块或应用程序可以采用多种方式与用户进行通信:命令行、对话框等。因此,与用户通信的 PAM 使用者的设计者需要编写对话函数。对话函数用于在用户与模块之间传递消息,而与通信方式无关。对话函数可从对话函数回调 pam_message 参数中的 msg_style 参数派生消息类型。请参见 pam_start(3PAM)。
开发者不应对 PAM 如何与用户进行通信做出假设。而应用程序应该与用户交换消息,直到操作完成为止。应用程序会显示与对话函数对应的消息字符串,但不进行解释或修改。一条单独的消息中可以包含多行、控制字符或额外的空格。请注意,服务模块负责本地化发送给对话函数的任何字符串。
下面提供了对话函数样例 pam_tty_conv()。pam_tty_conv() 采用以下参数:
num_msg-传递给函数的消息数。
**mess-指向缓冲区的指针,该缓冲区包含来自用户的消息。
**resp-指向缓冲区的指针,该缓冲区包含对用户所做的响应。
*my_data-指向应用程序数据的指针。
函数样例从 stdin 获取用户输入。例程需要为响应缓冲区分配内存。可以设置最大值 PAM_MAX_NUM_MSG 以限制消息的数量。如果对话函数返回错误,则对话函数会负责清除并释放为响应分配的任何内存。此外,对话函数必须将响应指针设置为 NULL。请注意,应使用零填充方法来完成内存清除。对话函数的调用者负责释放返回给它的所有响应。要进行对话,该函数将循环处理来自用户应用程序的消息。有效的消息将写入 stdout,所有错误将写入 stderr。
注 - 此示例的源代码也可以通过 Oracle 下载中心获取。请参见 http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html。
示例 3-2 PAM 对话函数
/* * Copyright (c) 2005, 2012, Oracle and/or its affiliates. * All rights reserved.
#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); errno = 0; /* don't propogate possible EINTR */ /* 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); }