1*0d9d0fd8Schristos /* $NetBSD: openpam_ttyconv.c,v 1.5 2023/06/30 21:46:21 christos Exp $ */
2201780c4Schristos
376e8c542Schristos /*-
476e8c542Schristos * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
54cb4af11Schristos * Copyright (c) 2004-2014 Dag-Erling Smørgrav
676e8c542Schristos * All rights reserved.
776e8c542Schristos *
876e8c542Schristos * This software was developed for the FreeBSD Project by ThinkSec AS and
976e8c542Schristos * Network Associates Laboratories, the Security Research Division of
1076e8c542Schristos * Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
1176e8c542Schristos * ("CBOSS"), as part of the DARPA CHATS research program.
1276e8c542Schristos *
1376e8c542Schristos * Redistribution and use in source and binary forms, with or without
1476e8c542Schristos * modification, are permitted provided that the following conditions
1576e8c542Schristos * are met:
1676e8c542Schristos * 1. Redistributions of source code must retain the above copyright
1776e8c542Schristos * notice, this list of conditions and the following disclaimer.
1876e8c542Schristos * 2. Redistributions in binary form must reproduce the above copyright
1976e8c542Schristos * notice, this list of conditions and the following disclaimer in the
2076e8c542Schristos * documentation and/or other materials provided with the distribution.
2176e8c542Schristos * 3. The name of the author may not be used to endorse or promote
2276e8c542Schristos * products derived from this software without specific prior written
2376e8c542Schristos * permission.
2476e8c542Schristos *
2576e8c542Schristos * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
2676e8c542Schristos * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2776e8c542Schristos * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2876e8c542Schristos * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
2976e8c542Schristos * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3076e8c542Schristos * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3176e8c542Schristos * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3276e8c542Schristos * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3376e8c542Schristos * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3476e8c542Schristos * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3576e8c542Schristos * SUCH DAMAGE.
3676e8c542Schristos */
3776e8c542Schristos
3876e8c542Schristos #ifdef HAVE_CONFIG_H
3976e8c542Schristos # include "config.h"
4076e8c542Schristos #endif
4176e8c542Schristos
42201780c4Schristos #include <sys/cdefs.h>
43*0d9d0fd8Schristos __RCSID("$NetBSD: openpam_ttyconv.c,v 1.5 2023/06/30 21:46:21 christos Exp $");
44201780c4Schristos
4576e8c542Schristos #include <sys/types.h>
4676e8c542Schristos #include <sys/poll.h>
4776e8c542Schristos #include <sys/time.h>
4876e8c542Schristos
4976e8c542Schristos #include <errno.h>
5076e8c542Schristos #include <fcntl.h>
5176e8c542Schristos #include <signal.h>
5276e8c542Schristos #include <stdio.h>
5376e8c542Schristos #include <stdlib.h>
5476e8c542Schristos #include <string.h>
5576e8c542Schristos #include <termios.h>
5676e8c542Schristos #include <unistd.h>
57201780c4Schristos #include <paths.h>
5876e8c542Schristos
5976e8c542Schristos #include <security/pam_appl.h>
6076e8c542Schristos
6176e8c542Schristos #include "openpam_impl.h"
6276e8c542Schristos #include "openpam_strlset.h"
6376e8c542Schristos
6476e8c542Schristos int openpam_ttyconv_timeout = 0;
6576e8c542Schristos
66201780c4Schristos #ifdef GETPASS_ECHO
67201780c4Schristos static int
prompt(const char * message,char * response,int echo)68201780c4Schristos prompt(const char *message, char *response, int echo)
69201780c4Schristos {
70201780c4Schristos char *rv;
7176e8c542Schristos
72201780c4Schristos rv = getpassfd(message, response, PAM_MAX_RESP_SIZE, NULL,
73201780c4Schristos GETPASS_NEED_TTY | GETPASS_FAIL_EOF | GETPASS_NO_SIGNAL |
74201780c4Schristos (echo ? GETPASS_ECHO : 0), openpam_ttyconv_timeout);
75201780c4Schristos if (rv == NULL) {
76201780c4Schristos fprintf(stderr, " %s\n", strerror(errno));
77201780c4Schristos return -1;
78201780c4Schristos } else if (echo == 0)
79201780c4Schristos fputs("\n", stderr);
80201780c4Schristos return 0;
81201780c4Schristos }
82201780c4Schristos
83201780c4Schristos #else
84201780c4Schristos
85201780c4Schristos static volatile sig_atomic_t caught_signal;
8676e8c542Schristos /*
8776e8c542Schristos * Handle incoming signals during tty conversation
8876e8c542Schristos */
8976e8c542Schristos static void
catch_signal(int signo)9076e8c542Schristos catch_signal(int signo)
9176e8c542Schristos {
9276e8c542Schristos
9376e8c542Schristos switch (signo) {
9476e8c542Schristos case SIGINT:
9576e8c542Schristos case SIGQUIT:
9676e8c542Schristos case SIGTERM:
9776e8c542Schristos caught_signal = signo;
9876e8c542Schristos break;
9976e8c542Schristos }
10076e8c542Schristos }
10176e8c542Schristos
10276e8c542Schristos /*
10376e8c542Schristos * Accept a response from the user on a tty
10476e8c542Schristos */
10576e8c542Schristos static int
prompt_tty(int ifd,int ofd,const char * message,char * response,int echo)10676e8c542Schristos prompt_tty(int ifd, int ofd, const char *message, char *response, int echo)
10776e8c542Schristos {
10876e8c542Schristos struct sigaction action;
10976e8c542Schristos struct sigaction saction_sigint, saction_sigquit, saction_sigterm;
11076e8c542Schristos struct termios tcattr;
11176e8c542Schristos struct timeval now, target, remaining;
11276e8c542Schristos int remaining_ms;
11376e8c542Schristos tcflag_t slflag;
11476e8c542Schristos struct pollfd pfd;
11576e8c542Schristos int serrno;
11676e8c542Schristos int pos, ret;
11776e8c542Schristos char ch;
11876e8c542Schristos
11976e8c542Schristos /* turn echo off if requested */
12076e8c542Schristos slflag = 0; /* prevent bogus uninitialized variable warning */
12176e8c542Schristos if (!echo) {
12276e8c542Schristos if (tcgetattr(ifd, &tcattr) != 0) {
12376e8c542Schristos openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
12476e8c542Schristos return (-1);
12576e8c542Schristos }
12676e8c542Schristos slflag = tcattr.c_lflag;
12776e8c542Schristos tcattr.c_lflag &= ~ECHO;
12876e8c542Schristos if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) {
12976e8c542Schristos openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
13076e8c542Schristos return (-1);
13176e8c542Schristos }
13276e8c542Schristos }
13376e8c542Schristos
134*0d9d0fd8Schristos /* write prompt */
135*0d9d0fd8Schristos if (write(ofd, message, strlen(message)) < 0) {
136*0d9d0fd8Schristos openpam_log(PAM_LOG_ERROR, "write(): %m");
137*0d9d0fd8Schristos return (-1);
138*0d9d0fd8Schristos }
139*0d9d0fd8Schristos
14076e8c542Schristos /* install signal handlers */
14176e8c542Schristos caught_signal = 0;
14276e8c542Schristos action.sa_handler = &catch_signal;
14376e8c542Schristos action.sa_flags = 0;
14476e8c542Schristos sigfillset(&action.sa_mask);
14576e8c542Schristos sigaction(SIGINT, &action, &saction_sigint);
14676e8c542Schristos sigaction(SIGQUIT, &action, &saction_sigquit);
14776e8c542Schristos sigaction(SIGTERM, &action, &saction_sigterm);
14876e8c542Schristos
14976e8c542Schristos /* compute timeout */
15076e8c542Schristos if (openpam_ttyconv_timeout > 0) {
15176e8c542Schristos (void)gettimeofday(&now, NULL);
15276e8c542Schristos remaining.tv_sec = openpam_ttyconv_timeout;
15376e8c542Schristos remaining.tv_usec = 0;
15476e8c542Schristos timeradd(&now, &remaining, &target);
15576e8c542Schristos } else {
15676e8c542Schristos /* prevent bogus uninitialized variable warning */
15776e8c542Schristos now.tv_sec = now.tv_usec = 0;
15876e8c542Schristos remaining.tv_sec = remaining.tv_usec = 0;
15976e8c542Schristos target.tv_sec = target.tv_usec = 0;
16076e8c542Schristos }
16176e8c542Schristos
16276e8c542Schristos /* input loop */
16376e8c542Schristos pos = 0;
16476e8c542Schristos ret = -1;
16576e8c542Schristos serrno = 0;
16676e8c542Schristos while (!caught_signal) {
16776e8c542Schristos pfd.fd = ifd;
16876e8c542Schristos pfd.events = POLLIN;
16976e8c542Schristos pfd.revents = 0;
17076e8c542Schristos if (openpam_ttyconv_timeout > 0) {
17176e8c542Schristos gettimeofday(&now, NULL);
17276e8c542Schristos if (timercmp(&now, &target, >))
17376e8c542Schristos break;
17476e8c542Schristos timersub(&target, &now, &remaining);
17576e8c542Schristos remaining_ms = remaining.tv_sec * 1000 +
17676e8c542Schristos remaining.tv_usec / 1000;
17776e8c542Schristos } else {
17876e8c542Schristos remaining_ms = -1;
17976e8c542Schristos }
18076e8c542Schristos if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
18176e8c542Schristos serrno = errno;
18276e8c542Schristos if (errno == EINTR)
18376e8c542Schristos continue;
18476e8c542Schristos openpam_log(PAM_LOG_ERROR, "poll(): %m");
18576e8c542Schristos break;
18676e8c542Schristos } else if (ret == 0) {
18776e8c542Schristos /* timeout */
18876e8c542Schristos write(ofd, " timed out", 10);
18976e8c542Schristos openpam_log(PAM_LOG_NOTICE, "timed out");
19076e8c542Schristos break;
19176e8c542Schristos }
19276e8c542Schristos if ((ret = read(ifd, &ch, 1)) < 0) {
19376e8c542Schristos serrno = errno;
19476e8c542Schristos openpam_log(PAM_LOG_ERROR, "read(): %m");
19576e8c542Schristos break;
19676e8c542Schristos } else if (ret == 0 || ch == '\n') {
19776e8c542Schristos response[pos] = '\0';
19876e8c542Schristos ret = pos;
19976e8c542Schristos break;
20076e8c542Schristos }
20176e8c542Schristos if (pos + 1 < PAM_MAX_RESP_SIZE)
20276e8c542Schristos response[pos++] = ch;
20376e8c542Schristos /* overflow is discarded */
20476e8c542Schristos }
20576e8c542Schristos
20676e8c542Schristos /* restore tty state */
20776e8c542Schristos if (!echo) {
20876e8c542Schristos tcattr.c_lflag = slflag;
20976e8c542Schristos if (tcsetattr(ifd, 0, &tcattr) != 0) {
21076e8c542Schristos /* treat as non-fatal, since we have our answer */
21176e8c542Schristos openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m");
21276e8c542Schristos }
21376e8c542Schristos }
21476e8c542Schristos
21576e8c542Schristos /* restore signal handlers and re-post caught signal*/
21676e8c542Schristos sigaction(SIGINT, &saction_sigint, NULL);
21776e8c542Schristos sigaction(SIGQUIT, &saction_sigquit, NULL);
21876e8c542Schristos sigaction(SIGTERM, &saction_sigterm, NULL);
21976e8c542Schristos if (caught_signal != 0) {
22076e8c542Schristos openpam_log(PAM_LOG_ERROR, "caught signal %d",
22176e8c542Schristos (int)caught_signal);
22276e8c542Schristos raise((int)caught_signal);
22376e8c542Schristos /* if raise() had no effect... */
22476e8c542Schristos serrno = EINTR;
22576e8c542Schristos ret = -1;
22676e8c542Schristos }
22776e8c542Schristos
22876e8c542Schristos /* done */
22976e8c542Schristos write(ofd, "\n", 1);
23076e8c542Schristos errno = serrno;
23176e8c542Schristos return (ret);
23276e8c542Schristos }
23376e8c542Schristos
23476e8c542Schristos /*
23576e8c542Schristos * Accept a response from the user on a non-tty stdin.
23676e8c542Schristos */
23776e8c542Schristos static int
prompt_notty(const char * message,char * response)23876e8c542Schristos prompt_notty(const char *message, char *response)
23976e8c542Schristos {
24076e8c542Schristos struct timeval now, target, remaining;
24176e8c542Schristos int remaining_ms;
24276e8c542Schristos struct pollfd pfd;
24376e8c542Schristos int ch, pos, ret;
24476e8c542Schristos
24576e8c542Schristos /* show prompt */
24676e8c542Schristos fputs(message, stdout);
24776e8c542Schristos fflush(stdout);
24876e8c542Schristos
24976e8c542Schristos /* compute timeout */
25076e8c542Schristos if (openpam_ttyconv_timeout > 0) {
25176e8c542Schristos (void)gettimeofday(&now, NULL);
25276e8c542Schristos remaining.tv_sec = openpam_ttyconv_timeout;
25376e8c542Schristos remaining.tv_usec = 0;
25476e8c542Schristos timeradd(&now, &remaining, &target);
25576e8c542Schristos } else {
25676e8c542Schristos /* prevent bogus uninitialized variable warning */
25776e8c542Schristos now.tv_sec = now.tv_usec = 0;
25876e8c542Schristos remaining.tv_sec = remaining.tv_usec = 0;
25976e8c542Schristos target.tv_sec = target.tv_usec = 0;
26076e8c542Schristos }
26176e8c542Schristos
26276e8c542Schristos /* input loop */
26376e8c542Schristos pos = 0;
26476e8c542Schristos for (;;) {
26576e8c542Schristos pfd.fd = STDIN_FILENO;
26676e8c542Schristos pfd.events = POLLIN;
26776e8c542Schristos pfd.revents = 0;
26876e8c542Schristos if (openpam_ttyconv_timeout > 0) {
26976e8c542Schristos gettimeofday(&now, NULL);
27076e8c542Schristos if (timercmp(&now, &target, >))
27176e8c542Schristos break;
27276e8c542Schristos timersub(&target, &now, &remaining);
27376e8c542Schristos remaining_ms = remaining.tv_sec * 1000 +
27476e8c542Schristos remaining.tv_usec / 1000;
27576e8c542Schristos } else {
27676e8c542Schristos remaining_ms = -1;
27776e8c542Schristos }
27876e8c542Schristos if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
27976e8c542Schristos /* interrupt is ok, everything else -> bail */
28076e8c542Schristos if (errno == EINTR)
28176e8c542Schristos continue;
28276e8c542Schristos perror("\nopenpam_ttyconv");
28376e8c542Schristos return (-1);
28476e8c542Schristos } else if (ret == 0) {
28576e8c542Schristos /* timeout */
28676e8c542Schristos break;
28776e8c542Schristos } else {
28876e8c542Schristos /* input */
28976e8c542Schristos if ((ch = getchar()) == EOF && ferror(stdin)) {
29076e8c542Schristos perror("\nopenpam_ttyconv");
29176e8c542Schristos return (-1);
29276e8c542Schristos }
29376e8c542Schristos if (ch == EOF || ch == '\n') {
29476e8c542Schristos response[pos] = '\0';
29576e8c542Schristos return (pos);
29676e8c542Schristos }
29776e8c542Schristos if (pos + 1 < PAM_MAX_RESP_SIZE)
29876e8c542Schristos response[pos++] = ch;
29976e8c542Schristos /* overflow is discarded */
30076e8c542Schristos }
30176e8c542Schristos }
30276e8c542Schristos fputs("\nopenpam_ttyconv: timeout\n", stderr);
30376e8c542Schristos return (-1);
30476e8c542Schristos }
30576e8c542Schristos
30676e8c542Schristos /*
30776e8c542Schristos * Determine whether stdin is a tty; if not, try to open the tty; in
30876e8c542Schristos * either case, call the appropriate method.
30976e8c542Schristos */
31076e8c542Schristos static int
prompt(const char * message,char * response,int echo)31176e8c542Schristos prompt(const char *message, char *response, int echo)
31276e8c542Schristos {
31376e8c542Schristos int ifd, ofd, ret;
31476e8c542Schristos
31576e8c542Schristos if (isatty(STDIN_FILENO)) {
31676e8c542Schristos fflush(stdout);
31776e8c542Schristos #ifdef HAVE_FPURGE
31876e8c542Schristos fpurge(stdin);
31976e8c542Schristos #endif
32076e8c542Schristos ifd = STDIN_FILENO;
32176e8c542Schristos ofd = STDOUT_FILENO;
32276e8c542Schristos } else {
32376e8c542Schristos if ((ifd = open("/dev/tty", O_RDWR)) < 0)
32476e8c542Schristos /* no way to prevent echo */
32576e8c542Schristos return (prompt_notty(message, response));
32676e8c542Schristos ofd = ifd;
32776e8c542Schristos }
32876e8c542Schristos ret = prompt_tty(ifd, ofd, message, response, echo);
32976e8c542Schristos if (ifd != STDIN_FILENO)
33076e8c542Schristos close(ifd);
33176e8c542Schristos return (ret);
33276e8c542Schristos }
333201780c4Schristos #endif
33476e8c542Schristos
33576e8c542Schristos /*
33676e8c542Schristos * OpenPAM extension
33776e8c542Schristos *
33876e8c542Schristos * Simple tty-based conversation function
33976e8c542Schristos */
34076e8c542Schristos
34176e8c542Schristos int
openpam_ttyconv(int n,const struct pam_message ** msg,struct pam_response ** resp,void * data)34276e8c542Schristos openpam_ttyconv(int n,
34376e8c542Schristos const struct pam_message **msg,
34476e8c542Schristos struct pam_response **resp,
34576e8c542Schristos void *data)
34676e8c542Schristos {
34776e8c542Schristos char respbuf[PAM_MAX_RESP_SIZE];
34876e8c542Schristos struct pam_response *aresp;
34976e8c542Schristos int i;
350201780c4Schristos FILE *infp, *outfp, *errfp;
35176e8c542Schristos
35276e8c542Schristos ENTER();
353201780c4Schristos /*LINTED unused*/
35476e8c542Schristos (void)data;
35576e8c542Schristos if (n <= 0 || n > PAM_MAX_NUM_MSG)
35676e8c542Schristos RETURNC(PAM_CONV_ERR);
357201780c4Schristos if ((aresp = calloc((size_t)n, sizeof *aresp)) == NULL)
35876e8c542Schristos RETURNC(PAM_BUF_ERR);
359201780c4Schristos
360201780c4Schristos /*
361201780c4Schristos * read and write to /dev/tty if possible; else read from
362201780c4Schristos * stdin and write to stderr.
363201780c4Schristos */
364201780c4Schristos if ((outfp = infp = errfp = fopen(_PATH_TTY, "w+")) == NULL) {
365201780c4Schristos errfp = stderr;
366201780c4Schristos outfp = stderr;
367201780c4Schristos infp = stdin;
368201780c4Schristos }
369201780c4Schristos
37076e8c542Schristos for (i = 0; i < n; ++i) {
37176e8c542Schristos aresp[i].resp_retcode = 0;
37276e8c542Schristos aresp[i].resp = NULL;
37372d8d6c3Schristos switch ((enum openpam_message_items)msg[i]->msg_style) {
37476e8c542Schristos case PAM_PROMPT_ECHO_OFF:
37576e8c542Schristos if (prompt(msg[i]->msg, respbuf, 0) < 0 ||
37676e8c542Schristos (aresp[i].resp = strdup(respbuf)) == NULL)
37776e8c542Schristos goto fail;
37876e8c542Schristos break;
37976e8c542Schristos case PAM_PROMPT_ECHO_ON:
38076e8c542Schristos if (prompt(msg[i]->msg, respbuf, 1) < 0 ||
38176e8c542Schristos (aresp[i].resp = strdup(respbuf)) == NULL)
38276e8c542Schristos goto fail;
38376e8c542Schristos break;
38476e8c542Schristos case PAM_ERROR_MSG:
385201780c4Schristos fputs(msg[i]->msg, errfp);
38676e8c542Schristos if (strlen(msg[i]->msg) > 0 &&
38776e8c542Schristos msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
388201780c4Schristos fputc('\n', errfp);
38976e8c542Schristos break;
39076e8c542Schristos case PAM_TEXT_INFO:
391201780c4Schristos fputs(msg[i]->msg, outfp);
39276e8c542Schristos if (strlen(msg[i]->msg) > 0 &&
39376e8c542Schristos msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
394201780c4Schristos fputc('\n', outfp);
39576e8c542Schristos break;
39676e8c542Schristos default:
39776e8c542Schristos goto fail;
39876e8c542Schristos }
39976e8c542Schristos }
400201780c4Schristos if (infp != stdin)
401201780c4Schristos (void)fclose(infp);
40276e8c542Schristos *resp = aresp;
40376e8c542Schristos memset(respbuf, 0, sizeof respbuf);
40476e8c542Schristos RETURNC(PAM_SUCCESS);
40576e8c542Schristos fail:
40676e8c542Schristos for (i = 0; i < n; ++i) {
40776e8c542Schristos if (aresp[i].resp != NULL) {
40876e8c542Schristos strlset(aresp[i].resp, 0, PAM_MAX_RESP_SIZE);
40976e8c542Schristos FREE(aresp[i].resp);
41076e8c542Schristos }
41176e8c542Schristos }
412201780c4Schristos if (infp != stdin)
413201780c4Schristos (void)fclose(infp);
414201780c4Schristos memset(aresp, 0, (size_t)n * sizeof *aresp);
41576e8c542Schristos FREE(aresp);
41676e8c542Schristos *resp = NULL;
41776e8c542Schristos memset(respbuf, 0, sizeof respbuf);
41876e8c542Schristos RETURNC(PAM_CONV_ERR);
41976e8c542Schristos }
42076e8c542Schristos
42176e8c542Schristos /*
42276e8c542Schristos * Error codes:
42376e8c542Schristos *
42476e8c542Schristos * PAM_SYSTEM_ERR
42576e8c542Schristos * PAM_BUF_ERR
42676e8c542Schristos * PAM_CONV_ERR
42776e8c542Schristos */
42876e8c542Schristos
42976e8c542Schristos /**
43076e8c542Schristos * The =openpam_ttyconv function is a standard conversation function
43176e8c542Schristos * suitable for use on TTY devices.
43276e8c542Schristos * It should be adequate for the needs of most text-based interactive
43376e8c542Schristos * programs.
43476e8c542Schristos *
435201780c4Schristos * The =openpam_ttyconv function displays a prompt to, and reads in a
436201780c4Schristos * password from /dev/tty. If this file is not accessible, =openpam_ttyconv
437201780c4Schristos * displays the prompt on the standard error output and reads from the
438201780c4Schristos * standard input.
439201780c4Schristos *
44076e8c542Schristos * The =openpam_ttyconv function allows the application to specify a
44176e8c542Schristos * timeout for user input by setting the global integer variable
44276e8c542Schristos * :openpam_ttyconv_timeout to the length of the timeout in seconds.
44376e8c542Schristos *
44476e8c542Schristos * >openpam_nullconv
44576e8c542Schristos * >pam_prompt
44676e8c542Schristos * >pam_vprompt
447201780c4Schristos * >getpass
44876e8c542Schristos */
449