xref: /dflybsd-src/contrib/openpam/lib/libpam/openpam_ttyconv.c (revision 7031abe4d1ef8c309d4113438494530b74f3f3fe)
1*10b5fe87SSascha Wildner /*-
2*10b5fe87SSascha Wildner  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
3*10b5fe87SSascha Wildner  * Copyright (c) 2004-2014 Dag-Erling Smørgrav
4*10b5fe87SSascha Wildner  * All rights reserved.
5*10b5fe87SSascha Wildner  *
6*10b5fe87SSascha Wildner  * This software was developed for the FreeBSD Project by ThinkSec AS and
7*10b5fe87SSascha Wildner  * Network Associates Laboratories, the Security Research Division of
8*10b5fe87SSascha Wildner  * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
9*10b5fe87SSascha Wildner  * ("CBOSS"), as part of the DARPA CHATS research program.
10*10b5fe87SSascha Wildner  *
11*10b5fe87SSascha Wildner  * Redistribution and use in source and binary forms, with or without
12*10b5fe87SSascha Wildner  * modification, are permitted provided that the following conditions
13*10b5fe87SSascha Wildner  * are met:
14*10b5fe87SSascha Wildner  * 1. Redistributions of source code must retain the above copyright
15*10b5fe87SSascha Wildner  *    notice, this list of conditions and the following disclaimer.
16*10b5fe87SSascha Wildner  * 2. Redistributions in binary form must reproduce the above copyright
17*10b5fe87SSascha Wildner  *    notice, this list of conditions and the following disclaimer in the
18*10b5fe87SSascha Wildner  *    documentation and/or other materials provided with the distribution.
19*10b5fe87SSascha Wildner  * 3. The name of the author may not be used to endorse or promote
20*10b5fe87SSascha Wildner  *    products derived from this software without specific prior written
21*10b5fe87SSascha Wildner  *    permission.
22*10b5fe87SSascha Wildner  *
23*10b5fe87SSascha Wildner  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24*10b5fe87SSascha Wildner  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25*10b5fe87SSascha Wildner  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26*10b5fe87SSascha Wildner  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27*10b5fe87SSascha Wildner  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28*10b5fe87SSascha Wildner  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29*10b5fe87SSascha Wildner  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30*10b5fe87SSascha Wildner  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31*10b5fe87SSascha Wildner  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32*10b5fe87SSascha Wildner  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33*10b5fe87SSascha Wildner  * SUCH DAMAGE.
34*10b5fe87SSascha Wildner  *
35*10b5fe87SSascha Wildner  * $OpenPAM: openpam_ttyconv.c 938 2017-04-30 21:34:42Z des $
36*10b5fe87SSascha Wildner  */
37*10b5fe87SSascha Wildner 
38*10b5fe87SSascha Wildner #ifdef HAVE_CONFIG_H
39*10b5fe87SSascha Wildner # include "config.h"
40*10b5fe87SSascha Wildner #endif
41*10b5fe87SSascha Wildner 
42*10b5fe87SSascha Wildner #include <sys/types.h>
43*10b5fe87SSascha Wildner #include <sys/poll.h>
44*10b5fe87SSascha Wildner #include <sys/time.h>
45*10b5fe87SSascha Wildner 
46*10b5fe87SSascha Wildner #include <errno.h>
47*10b5fe87SSascha Wildner #include <fcntl.h>
48*10b5fe87SSascha Wildner #include <signal.h>
49*10b5fe87SSascha Wildner #include <stdio.h>
50*10b5fe87SSascha Wildner #include <stdlib.h>
51*10b5fe87SSascha Wildner #include <string.h>
52*10b5fe87SSascha Wildner #include <termios.h>
53*10b5fe87SSascha Wildner #include <unistd.h>
54*10b5fe87SSascha Wildner 
55*10b5fe87SSascha Wildner #include <security/pam_appl.h>
56*10b5fe87SSascha Wildner 
57*10b5fe87SSascha Wildner #include "openpam_impl.h"
58*10b5fe87SSascha Wildner #include "openpam_strlset.h"
59*10b5fe87SSascha Wildner 
60*10b5fe87SSascha Wildner int openpam_ttyconv_timeout = 0;
61*10b5fe87SSascha Wildner 
62*10b5fe87SSascha Wildner static volatile sig_atomic_t caught_signal;
63*10b5fe87SSascha Wildner 
64*10b5fe87SSascha Wildner /*
65*10b5fe87SSascha Wildner  * Handle incoming signals during tty conversation
66*10b5fe87SSascha Wildner  */
67*10b5fe87SSascha Wildner static void
catch_signal(int signo)68*10b5fe87SSascha Wildner catch_signal(int signo)
69*10b5fe87SSascha Wildner {
70*10b5fe87SSascha Wildner 
71*10b5fe87SSascha Wildner 	switch (signo) {
72*10b5fe87SSascha Wildner 	case SIGINT:
73*10b5fe87SSascha Wildner 	case SIGQUIT:
74*10b5fe87SSascha Wildner 	case SIGTERM:
75*10b5fe87SSascha Wildner 		caught_signal = signo;
76*10b5fe87SSascha Wildner 		break;
77*10b5fe87SSascha Wildner 	}
78*10b5fe87SSascha Wildner }
79*10b5fe87SSascha Wildner 
80*10b5fe87SSascha Wildner /*
81*10b5fe87SSascha Wildner  * Accept a response from the user on a tty
82*10b5fe87SSascha Wildner  */
83*10b5fe87SSascha Wildner static int
prompt_tty(int ifd,int ofd,const char * message,char * response,int echo)84*10b5fe87SSascha Wildner prompt_tty(int ifd, int ofd, const char *message, char *response, int echo)
85*10b5fe87SSascha Wildner {
86*10b5fe87SSascha Wildner 	struct sigaction action;
87*10b5fe87SSascha Wildner 	struct sigaction saction_sigint, saction_sigquit, saction_sigterm;
88*10b5fe87SSascha Wildner 	struct termios tcattr;
89*10b5fe87SSascha Wildner 	struct timeval now, target, remaining;
90*10b5fe87SSascha Wildner 	int remaining_ms;
91*10b5fe87SSascha Wildner 	tcflag_t slflag;
92*10b5fe87SSascha Wildner 	struct pollfd pfd;
93*10b5fe87SSascha Wildner 	int serrno;
94*10b5fe87SSascha Wildner 	int pos, ret;
95*10b5fe87SSascha Wildner 	char ch;
96*10b5fe87SSascha Wildner 
97*10b5fe87SSascha Wildner 	/* write prompt */
98*10b5fe87SSascha Wildner 	if (write(ofd, message, strlen(message)) < 0) {
99*10b5fe87SSascha Wildner 		openpam_log(PAM_LOG_ERROR, "write(): %m");
100*10b5fe87SSascha Wildner 		return (-1);
101*10b5fe87SSascha Wildner 	}
102*10b5fe87SSascha Wildner 
103*10b5fe87SSascha Wildner 	/* turn echo off if requested */
104*10b5fe87SSascha Wildner 	slflag = 0; /* prevent bogus uninitialized variable warning */
105*10b5fe87SSascha Wildner 	if (!echo) {
106*10b5fe87SSascha Wildner 		if (tcgetattr(ifd, &tcattr) != 0) {
107*10b5fe87SSascha Wildner 			openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
108*10b5fe87SSascha Wildner 			return (-1);
109*10b5fe87SSascha Wildner 		}
110*10b5fe87SSascha Wildner 		slflag = tcattr.c_lflag;
111*10b5fe87SSascha Wildner 		tcattr.c_lflag &= ~ECHO;
112*10b5fe87SSascha Wildner 		if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) {
113*10b5fe87SSascha Wildner 			openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
114*10b5fe87SSascha Wildner 			return (-1);
115*10b5fe87SSascha Wildner 		}
116*10b5fe87SSascha Wildner 	}
117*10b5fe87SSascha Wildner 
118*10b5fe87SSascha Wildner 	/* install signal handlers */
119*10b5fe87SSascha Wildner 	caught_signal = 0;
120*10b5fe87SSascha Wildner 	action.sa_handler = &catch_signal;
121*10b5fe87SSascha Wildner 	action.sa_flags = 0;
122*10b5fe87SSascha Wildner 	sigfillset(&action.sa_mask);
123*10b5fe87SSascha Wildner 	sigaction(SIGINT, &action, &saction_sigint);
124*10b5fe87SSascha Wildner 	sigaction(SIGQUIT, &action, &saction_sigquit);
125*10b5fe87SSascha Wildner 	sigaction(SIGTERM, &action, &saction_sigterm);
126*10b5fe87SSascha Wildner 
127*10b5fe87SSascha Wildner 	/* compute timeout */
128*10b5fe87SSascha Wildner 	if (openpam_ttyconv_timeout > 0) {
129*10b5fe87SSascha Wildner 		(void)gettimeofday(&now, NULL);
130*10b5fe87SSascha Wildner 		remaining.tv_sec = openpam_ttyconv_timeout;
131*10b5fe87SSascha Wildner 		remaining.tv_usec = 0;
132*10b5fe87SSascha Wildner 		timeradd(&now, &remaining, &target);
133*10b5fe87SSascha Wildner 	} else {
134*10b5fe87SSascha Wildner 		/* prevent bogus uninitialized variable warning */
135*10b5fe87SSascha Wildner 		now.tv_sec = now.tv_usec = 0;
136*10b5fe87SSascha Wildner 		remaining.tv_sec = remaining.tv_usec = 0;
137*10b5fe87SSascha Wildner 		target.tv_sec = target.tv_usec = 0;
138*10b5fe87SSascha Wildner 	}
139*10b5fe87SSascha Wildner 
140*10b5fe87SSascha Wildner 	/* input loop */
141*10b5fe87SSascha Wildner 	pos = 0;
142*10b5fe87SSascha Wildner 	ret = -1;
143*10b5fe87SSascha Wildner 	serrno = 0;
144*10b5fe87SSascha Wildner 	while (!caught_signal) {
145*10b5fe87SSascha Wildner 		pfd.fd = ifd;
146*10b5fe87SSascha Wildner 		pfd.events = POLLIN;
147*10b5fe87SSascha Wildner 		pfd.revents = 0;
148*10b5fe87SSascha Wildner 		if (openpam_ttyconv_timeout > 0) {
149*10b5fe87SSascha Wildner 			gettimeofday(&now, NULL);
150*10b5fe87SSascha Wildner 			if (timercmp(&now, &target, >))
151*10b5fe87SSascha Wildner 				break;
152*10b5fe87SSascha Wildner 			timersub(&target, &now, &remaining);
153*10b5fe87SSascha Wildner 			remaining_ms = remaining.tv_sec * 1000 +
154*10b5fe87SSascha Wildner 			    remaining.tv_usec / 1000;
155*10b5fe87SSascha Wildner 		} else {
156*10b5fe87SSascha Wildner 			remaining_ms = -1;
157*10b5fe87SSascha Wildner 		}
158*10b5fe87SSascha Wildner 		if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
159*10b5fe87SSascha Wildner 			serrno = errno;
160*10b5fe87SSascha Wildner 			if (errno == EINTR)
161*10b5fe87SSascha Wildner 				continue;
162*10b5fe87SSascha Wildner 			openpam_log(PAM_LOG_ERROR, "poll(): %m");
163*10b5fe87SSascha Wildner 			break;
164*10b5fe87SSascha Wildner 		} else if (ret == 0) {
165*10b5fe87SSascha Wildner 			/* timeout */
166*10b5fe87SSascha Wildner 			write(ofd, " timed out", 10);
167*10b5fe87SSascha Wildner 			openpam_log(PAM_LOG_NOTICE, "timed out");
168*10b5fe87SSascha Wildner 			break;
169*10b5fe87SSascha Wildner 		}
170*10b5fe87SSascha Wildner 		if ((ret = read(ifd, &ch, 1)) < 0) {
171*10b5fe87SSascha Wildner 			serrno = errno;
172*10b5fe87SSascha Wildner 			openpam_log(PAM_LOG_ERROR, "read(): %m");
173*10b5fe87SSascha Wildner 			break;
174*10b5fe87SSascha Wildner 		} else if (ret == 0 || ch == '\n') {
175*10b5fe87SSascha Wildner 			response[pos] = '\0';
176*10b5fe87SSascha Wildner 			ret = pos;
177*10b5fe87SSascha Wildner 			break;
178*10b5fe87SSascha Wildner 		}
179*10b5fe87SSascha Wildner 		if (pos + 1 < PAM_MAX_RESP_SIZE)
180*10b5fe87SSascha Wildner 			response[pos++] = ch;
181*10b5fe87SSascha Wildner 		/* overflow is discarded */
182*10b5fe87SSascha Wildner 	}
183*10b5fe87SSascha Wildner 
184*10b5fe87SSascha Wildner 	/* restore tty state */
185*10b5fe87SSascha Wildner 	if (!echo) {
186*10b5fe87SSascha Wildner 		tcattr.c_lflag = slflag;
187*10b5fe87SSascha Wildner 		if (tcsetattr(ifd, 0, &tcattr) != 0) {
188*10b5fe87SSascha Wildner 			/* treat as non-fatal, since we have our answer */
189*10b5fe87SSascha Wildner 			openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m");
190*10b5fe87SSascha Wildner 		}
191*10b5fe87SSascha Wildner 	}
192*10b5fe87SSascha Wildner 
193*10b5fe87SSascha Wildner 	/* restore signal handlers and re-post caught signal*/
194*10b5fe87SSascha Wildner 	sigaction(SIGINT, &saction_sigint, NULL);
195*10b5fe87SSascha Wildner 	sigaction(SIGQUIT, &saction_sigquit, NULL);
196*10b5fe87SSascha Wildner 	sigaction(SIGTERM, &saction_sigterm, NULL);
197*10b5fe87SSascha Wildner 	if (caught_signal != 0) {
198*10b5fe87SSascha Wildner 		openpam_log(PAM_LOG_ERROR, "caught signal %d",
199*10b5fe87SSascha Wildner 		    (int)caught_signal);
200*10b5fe87SSascha Wildner 		raise((int)caught_signal);
201*10b5fe87SSascha Wildner 		/* if raise() had no effect... */
202*10b5fe87SSascha Wildner 		serrno = EINTR;
203*10b5fe87SSascha Wildner 		ret = -1;
204*10b5fe87SSascha Wildner 	}
205*10b5fe87SSascha Wildner 
206*10b5fe87SSascha Wildner 	/* done */
207*10b5fe87SSascha Wildner 	write(ofd, "\n", 1);
208*10b5fe87SSascha Wildner 	errno = serrno;
209*10b5fe87SSascha Wildner 	return (ret);
210*10b5fe87SSascha Wildner }
211*10b5fe87SSascha Wildner 
212*10b5fe87SSascha Wildner /*
213*10b5fe87SSascha Wildner  * Accept a response from the user on a non-tty stdin.
214*10b5fe87SSascha Wildner  */
215*10b5fe87SSascha Wildner static int
prompt_notty(const char * message,char * response)216*10b5fe87SSascha Wildner prompt_notty(const char *message, char *response)
217*10b5fe87SSascha Wildner {
218*10b5fe87SSascha Wildner 	struct timeval now, target, remaining;
219*10b5fe87SSascha Wildner 	int remaining_ms;
220*10b5fe87SSascha Wildner 	struct pollfd pfd;
221*10b5fe87SSascha Wildner 	int ch, pos, ret;
222*10b5fe87SSascha Wildner 
223*10b5fe87SSascha Wildner 	/* show prompt */
224*10b5fe87SSascha Wildner 	fputs(message, stdout);
225*10b5fe87SSascha Wildner 	fflush(stdout);
226*10b5fe87SSascha Wildner 
227*10b5fe87SSascha Wildner 	/* compute timeout */
228*10b5fe87SSascha Wildner 	if (openpam_ttyconv_timeout > 0) {
229*10b5fe87SSascha Wildner 		(void)gettimeofday(&now, NULL);
230*10b5fe87SSascha Wildner 		remaining.tv_sec = openpam_ttyconv_timeout;
231*10b5fe87SSascha Wildner 		remaining.tv_usec = 0;
232*10b5fe87SSascha Wildner 		timeradd(&now, &remaining, &target);
233*10b5fe87SSascha Wildner 	} else {
234*10b5fe87SSascha Wildner 		/* prevent bogus uninitialized variable warning */
235*10b5fe87SSascha Wildner 		now.tv_sec = now.tv_usec = 0;
236*10b5fe87SSascha Wildner 		remaining.tv_sec = remaining.tv_usec = 0;
237*10b5fe87SSascha Wildner 		target.tv_sec = target.tv_usec = 0;
238*10b5fe87SSascha Wildner 	}
239*10b5fe87SSascha Wildner 
240*10b5fe87SSascha Wildner 	/* input loop */
241*10b5fe87SSascha Wildner 	pos = 0;
242*10b5fe87SSascha Wildner 	for (;;) {
243*10b5fe87SSascha Wildner 		pfd.fd = STDIN_FILENO;
244*10b5fe87SSascha Wildner 		pfd.events = POLLIN;
245*10b5fe87SSascha Wildner 		pfd.revents = 0;
246*10b5fe87SSascha Wildner 		if (openpam_ttyconv_timeout > 0) {
247*10b5fe87SSascha Wildner 			gettimeofday(&now, NULL);
248*10b5fe87SSascha Wildner 			if (timercmp(&now, &target, >))
249*10b5fe87SSascha Wildner 				break;
250*10b5fe87SSascha Wildner 			timersub(&target, &now, &remaining);
251*10b5fe87SSascha Wildner 			remaining_ms = remaining.tv_sec * 1000 +
252*10b5fe87SSascha Wildner 			    remaining.tv_usec / 1000;
253*10b5fe87SSascha Wildner 		} else {
254*10b5fe87SSascha Wildner 			remaining_ms = -1;
255*10b5fe87SSascha Wildner 		}
256*10b5fe87SSascha Wildner 		if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
257*10b5fe87SSascha Wildner 			/* interrupt is ok, everything else -> bail */
258*10b5fe87SSascha Wildner 			if (errno == EINTR)
259*10b5fe87SSascha Wildner 				continue;
260*10b5fe87SSascha Wildner 			perror("\nopenpam_ttyconv");
261*10b5fe87SSascha Wildner 			return (-1);
262*10b5fe87SSascha Wildner 		} else if (ret == 0) {
263*10b5fe87SSascha Wildner 			/* timeout */
264*10b5fe87SSascha Wildner 			break;
265*10b5fe87SSascha Wildner 		} else {
266*10b5fe87SSascha Wildner 			/* input */
267*10b5fe87SSascha Wildner 			if ((ch = getchar()) == EOF && ferror(stdin)) {
268*10b5fe87SSascha Wildner 				perror("\nopenpam_ttyconv");
269*10b5fe87SSascha Wildner 				return (-1);
270*10b5fe87SSascha Wildner 			}
271*10b5fe87SSascha Wildner 			if (ch == EOF || ch == '\n') {
272*10b5fe87SSascha Wildner 				response[pos] = '\0';
273*10b5fe87SSascha Wildner 				return (pos);
274*10b5fe87SSascha Wildner 			}
275*10b5fe87SSascha Wildner 			if (pos + 1 < PAM_MAX_RESP_SIZE)
276*10b5fe87SSascha Wildner 				response[pos++] = ch;
277*10b5fe87SSascha Wildner 			/* overflow is discarded */
278*10b5fe87SSascha Wildner 		}
279*10b5fe87SSascha Wildner 	}
280*10b5fe87SSascha Wildner 	fputs("\nopenpam_ttyconv: timeout\n", stderr);
281*10b5fe87SSascha Wildner 	return (-1);
282*10b5fe87SSascha Wildner }
283*10b5fe87SSascha Wildner 
284*10b5fe87SSascha Wildner /*
285*10b5fe87SSascha Wildner  * Determine whether stdin is a tty; if not, try to open the tty; in
286*10b5fe87SSascha Wildner  * either case, call the appropriate method.
287*10b5fe87SSascha Wildner  */
288*10b5fe87SSascha Wildner static int
prompt(const char * message,char * response,int echo)289*10b5fe87SSascha Wildner prompt(const char *message, char *response, int echo)
290*10b5fe87SSascha Wildner {
291*10b5fe87SSascha Wildner 	int ifd, ofd, ret;
292*10b5fe87SSascha Wildner 
293*10b5fe87SSascha Wildner 	if (isatty(STDIN_FILENO)) {
294*10b5fe87SSascha Wildner 		fflush(stdout);
295*10b5fe87SSascha Wildner #ifdef HAVE_FPURGE
296*10b5fe87SSascha Wildner 		fpurge(stdin);
297*10b5fe87SSascha Wildner #endif
298*10b5fe87SSascha Wildner 		ifd = STDIN_FILENO;
299*10b5fe87SSascha Wildner 		ofd = STDOUT_FILENO;
300*10b5fe87SSascha Wildner 	} else {
301*10b5fe87SSascha Wildner 		if ((ifd = open("/dev/tty", O_RDWR)) < 0)
302*10b5fe87SSascha Wildner 			/* no way to prevent echo */
303*10b5fe87SSascha Wildner 			return (prompt_notty(message, response));
304*10b5fe87SSascha Wildner 		ofd = ifd;
305*10b5fe87SSascha Wildner 	}
306*10b5fe87SSascha Wildner 	ret = prompt_tty(ifd, ofd, message, response, echo);
307*10b5fe87SSascha Wildner 	if (ifd != STDIN_FILENO)
308*10b5fe87SSascha Wildner 		close(ifd);
309*10b5fe87SSascha Wildner 	return (ret);
310*10b5fe87SSascha Wildner }
311*10b5fe87SSascha Wildner 
312*10b5fe87SSascha Wildner /*
313*10b5fe87SSascha Wildner  * OpenPAM extension
314*10b5fe87SSascha Wildner  *
315*10b5fe87SSascha Wildner  * Simple tty-based conversation function
316*10b5fe87SSascha Wildner  */
317*10b5fe87SSascha Wildner 
318*10b5fe87SSascha Wildner int
openpam_ttyconv(int n,const struct pam_message ** msg,struct pam_response ** resp,void * data)319*10b5fe87SSascha Wildner openpam_ttyconv(int n,
320*10b5fe87SSascha Wildner 	 const struct pam_message **msg,
321*10b5fe87SSascha Wildner 	 struct pam_response **resp,
322*10b5fe87SSascha Wildner 	 void *data)
323*10b5fe87SSascha Wildner {
324*10b5fe87SSascha Wildner 	char respbuf[PAM_MAX_RESP_SIZE];
325*10b5fe87SSascha Wildner 	struct pam_response *aresp;
326*10b5fe87SSascha Wildner 	int i;
327*10b5fe87SSascha Wildner 
328*10b5fe87SSascha Wildner 	ENTER();
329*10b5fe87SSascha Wildner 	(void)data;
330*10b5fe87SSascha Wildner 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
331*10b5fe87SSascha Wildner 		RETURNC(PAM_CONV_ERR);
332*10b5fe87SSascha Wildner 	if ((aresp = calloc(n, sizeof *aresp)) == NULL)
333*10b5fe87SSascha Wildner 		RETURNC(PAM_BUF_ERR);
334*10b5fe87SSascha Wildner 	for (i = 0; i < n; ++i) {
335*10b5fe87SSascha Wildner 		aresp[i].resp_retcode = 0;
336*10b5fe87SSascha Wildner 		aresp[i].resp = NULL;
337*10b5fe87SSascha Wildner 		switch (msg[i]->msg_style) {
338*10b5fe87SSascha Wildner 		case PAM_PROMPT_ECHO_OFF:
339*10b5fe87SSascha Wildner 			if (prompt(msg[i]->msg, respbuf, 0) < 0 ||
340*10b5fe87SSascha Wildner 			    (aresp[i].resp = strdup(respbuf)) == NULL)
341*10b5fe87SSascha Wildner 				goto fail;
342*10b5fe87SSascha Wildner 			break;
343*10b5fe87SSascha Wildner 		case PAM_PROMPT_ECHO_ON:
344*10b5fe87SSascha Wildner 			if (prompt(msg[i]->msg, respbuf, 1) < 0 ||
345*10b5fe87SSascha Wildner 			    (aresp[i].resp = strdup(respbuf)) == NULL)
346*10b5fe87SSascha Wildner 				goto fail;
347*10b5fe87SSascha Wildner 			break;
348*10b5fe87SSascha Wildner 		case PAM_ERROR_MSG:
349*10b5fe87SSascha Wildner 			fputs(msg[i]->msg, stderr);
350*10b5fe87SSascha Wildner 			if (strlen(msg[i]->msg) > 0 &&
351*10b5fe87SSascha Wildner 			    msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
352*10b5fe87SSascha Wildner 				fputc('\n', stderr);
353*10b5fe87SSascha Wildner 			break;
354*10b5fe87SSascha Wildner 		case PAM_TEXT_INFO:
355*10b5fe87SSascha Wildner 			fputs(msg[i]->msg, stdout);
356*10b5fe87SSascha Wildner 			if (strlen(msg[i]->msg) > 0 &&
357*10b5fe87SSascha Wildner 			    msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
358*10b5fe87SSascha Wildner 				fputc('\n', stdout);
359*10b5fe87SSascha Wildner 			break;
360*10b5fe87SSascha Wildner 		default:
361*10b5fe87SSascha Wildner 			goto fail;
362*10b5fe87SSascha Wildner 		}
363*10b5fe87SSascha Wildner 	}
364*10b5fe87SSascha Wildner 	*resp = aresp;
365*10b5fe87SSascha Wildner 	memset(respbuf, 0, sizeof respbuf);
366*10b5fe87SSascha Wildner 	RETURNC(PAM_SUCCESS);
367*10b5fe87SSascha Wildner fail:
368*10b5fe87SSascha Wildner 	for (i = 0; i < n; ++i) {
369*10b5fe87SSascha Wildner 		if (aresp[i].resp != NULL) {
370*10b5fe87SSascha Wildner 			strlset(aresp[i].resp, 0, PAM_MAX_RESP_SIZE);
371*10b5fe87SSascha Wildner 			FREE(aresp[i].resp);
372*10b5fe87SSascha Wildner 		}
373*10b5fe87SSascha Wildner 	}
374*10b5fe87SSascha Wildner 	memset(aresp, 0, n * sizeof *aresp);
375*10b5fe87SSascha Wildner 	FREE(aresp);
376*10b5fe87SSascha Wildner 	*resp = NULL;
377*10b5fe87SSascha Wildner 	memset(respbuf, 0, sizeof respbuf);
378*10b5fe87SSascha Wildner 	RETURNC(PAM_CONV_ERR);
379*10b5fe87SSascha Wildner }
380*10b5fe87SSascha Wildner 
381*10b5fe87SSascha Wildner /*
382*10b5fe87SSascha Wildner  * Error codes:
383*10b5fe87SSascha Wildner  *
384*10b5fe87SSascha Wildner  *	PAM_SYSTEM_ERR
385*10b5fe87SSascha Wildner  *	PAM_BUF_ERR
386*10b5fe87SSascha Wildner  *	PAM_CONV_ERR
387*10b5fe87SSascha Wildner  */
388*10b5fe87SSascha Wildner 
389*10b5fe87SSascha Wildner /**
390*10b5fe87SSascha Wildner  * The =openpam_ttyconv function is a standard conversation function
391*10b5fe87SSascha Wildner  * suitable for use on TTY devices.
392*10b5fe87SSascha Wildner  * It should be adequate for the needs of most text-based interactive
393*10b5fe87SSascha Wildner  * programs.
394*10b5fe87SSascha Wildner  *
395*10b5fe87SSascha Wildner  * The =openpam_ttyconv function allows the application to specify a
396*10b5fe87SSascha Wildner  * timeout for user input by setting the global integer variable
397*10b5fe87SSascha Wildner  * :openpam_ttyconv_timeout to the length of the timeout in seconds.
398*10b5fe87SSascha Wildner  *
399*10b5fe87SSascha Wildner  * >openpam_nullconv
400*10b5fe87SSascha Wildner  * >pam_prompt
401*10b5fe87SSascha Wildner  * >pam_vprompt
402*10b5fe87SSascha Wildner  */
403