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