| 跳过导航链接 | |
| 退出打印视图 | |
 
			 | 
			Oracle Solaris 开发者安全性指南 Oracle Solaris 10 8/11 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 下载中心获取。请参见 https://cds.sun.com/is-bin/INTERSHOP.enfinity/WFS/CDS-CDS_SMI-Site/en_US/-/USD/ViewProductDetail-Start?ProductRef=Security_code-Dev1.1-G-F@CDS-CDS_SMI。
示例 3-2 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);
}