xref: /netbsd-src/external/bsd/openpam/dist/lib/libpam/openpam_ttyconv.c (revision 0d9d0fd8a30be9a1924e715bbcf67a4a83efd262)
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