xref: /freebsd/contrib/openpam/lib/libpam/openpam_ttyconv.c (revision d64f40448833cad15c4e0be0d1675aeb4c12e22a)
1f7e6344dSDag-Erling Smørgrav /*-
2f7e6344dSDag-Erling Smørgrav  * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
3f7e6344dSDag-Erling Smørgrav  * Copyright (c) 2004-2011 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  *
35*d64f4044SDag-Erling Smørgrav  * $Id: openpam_ttyconv.c 807 2014-09-09 09:41:32Z des $
36f7e6344dSDag-Erling Smørgrav  */
37f7e6344dSDag-Erling Smørgrav 
38f7e6344dSDag-Erling Smørgrav #ifdef HAVE_CONFIG_H
39f7e6344dSDag-Erling Smørgrav # include "config.h"
40f7e6344dSDag-Erling Smørgrav #endif
41f7e6344dSDag-Erling Smørgrav 
42f7e6344dSDag-Erling Smørgrav #include <sys/types.h>
43ce77a8d6SDag-Erling Smørgrav #include <sys/poll.h>
44ce77a8d6SDag-Erling Smørgrav #include <sys/time.h>
45f7e6344dSDag-Erling Smørgrav 
46f7e6344dSDag-Erling Smørgrav #include <errno.h>
47ce77a8d6SDag-Erling Smørgrav #include <fcntl.h>
48f7e6344dSDag-Erling Smørgrav #include <signal.h>
49f7e6344dSDag-Erling Smørgrav #include <stdio.h>
50f7e6344dSDag-Erling Smørgrav #include <stdlib.h>
51f7e6344dSDag-Erling Smørgrav #include <string.h>
52f7e6344dSDag-Erling Smørgrav #include <termios.h>
53f7e6344dSDag-Erling Smørgrav #include <unistd.h>
54f7e6344dSDag-Erling Smørgrav 
55f7e6344dSDag-Erling Smørgrav #include <security/pam_appl.h>
56f7e6344dSDag-Erling Smørgrav 
57f7e6344dSDag-Erling Smørgrav #include "openpam_impl.h"
58*d64f4044SDag-Erling Smørgrav #include "openpam_strlset.h"
59f7e6344dSDag-Erling Smørgrav 
60f7e6344dSDag-Erling Smørgrav int openpam_ttyconv_timeout = 0;
61f7e6344dSDag-Erling Smørgrav 
62ce77a8d6SDag-Erling Smørgrav static volatile sig_atomic_t caught_signal;
63ce77a8d6SDag-Erling Smørgrav 
64ce77a8d6SDag-Erling Smørgrav /*
65ce77a8d6SDag-Erling Smørgrav  * Handle incoming signals during tty conversation
66ce77a8d6SDag-Erling Smørgrav  */
67f7e6344dSDag-Erling Smørgrav static void
68ce77a8d6SDag-Erling Smørgrav catch_signal(int signo)
69f7e6344dSDag-Erling Smørgrav {
70f7e6344dSDag-Erling Smørgrav 
71ce77a8d6SDag-Erling Smørgrav 	switch (signo) {
72ce77a8d6SDag-Erling Smørgrav 	case SIGINT:
73ce77a8d6SDag-Erling Smørgrav 	case SIGQUIT:
74ce77a8d6SDag-Erling Smørgrav 	case SIGTERM:
75ce77a8d6SDag-Erling Smørgrav 		caught_signal = signo;
76ce77a8d6SDag-Erling Smørgrav 		break;
77ce77a8d6SDag-Erling Smørgrav 	}
78f7e6344dSDag-Erling Smørgrav }
79f7e6344dSDag-Erling Smørgrav 
80ce77a8d6SDag-Erling Smørgrav /*
81ce77a8d6SDag-Erling Smørgrav  * Accept a response from the user on a tty
82ce77a8d6SDag-Erling Smørgrav  */
83ce77a8d6SDag-Erling Smørgrav static int
84ce77a8d6SDag-Erling Smørgrav prompt_tty(int ifd, int ofd, const char *message, char *response, int echo)
85f7e6344dSDag-Erling Smørgrav {
86ce77a8d6SDag-Erling Smørgrav 	struct sigaction action;
87ce77a8d6SDag-Erling Smørgrav 	struct sigaction saction_sigint, saction_sigquit, saction_sigterm;
88ce77a8d6SDag-Erling Smørgrav 	struct termios tcattr;
89ce77a8d6SDag-Erling Smørgrav 	struct timeval now, target, remaining;
90ce77a8d6SDag-Erling Smørgrav 	int remaining_ms;
91ce77a8d6SDag-Erling Smørgrav 	tcflag_t slflag;
92ce77a8d6SDag-Erling Smørgrav 	struct pollfd pfd;
93ce77a8d6SDag-Erling Smørgrav 	int serrno;
94ce77a8d6SDag-Erling Smørgrav 	int pos, ret;
95f7e6344dSDag-Erling Smørgrav 	char ch;
96f7e6344dSDag-Erling Smørgrav 
97ce77a8d6SDag-Erling Smørgrav 	/* write prompt */
98ce77a8d6SDag-Erling Smørgrav 	if (write(ofd, message, strlen(message)) < 0) {
99ce77a8d6SDag-Erling Smørgrav 		openpam_log(PAM_LOG_ERROR, "write(): %m");
100ce77a8d6SDag-Erling Smørgrav 		return (-1);
101ce77a8d6SDag-Erling Smørgrav 	}
102ce77a8d6SDag-Erling Smørgrav 
103ce77a8d6SDag-Erling Smørgrav 	/* turn echo off if requested */
104ce77a8d6SDag-Erling Smørgrav 	slflag = 0; /* prevent bogus uninitialized variable warning */
105ce77a8d6SDag-Erling Smørgrav 	if (!echo) {
106ce77a8d6SDag-Erling Smørgrav 		if (tcgetattr(ifd, &tcattr) != 0) {
107ce77a8d6SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
108ce77a8d6SDag-Erling Smørgrav 			return (-1);
109ce77a8d6SDag-Erling Smørgrav 		}
110ce77a8d6SDag-Erling Smørgrav 		slflag = tcattr.c_lflag;
111ce77a8d6SDag-Erling Smørgrav 		tcattr.c_lflag &= ~ECHO;
112ce77a8d6SDag-Erling Smørgrav 		if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) {
113ce77a8d6SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
114ce77a8d6SDag-Erling Smørgrav 			return (-1);
115ce77a8d6SDag-Erling Smørgrav 		}
116ce77a8d6SDag-Erling Smørgrav 	}
117ce77a8d6SDag-Erling Smørgrav 
118ce77a8d6SDag-Erling Smørgrav 	/* install signal handlers */
119ce77a8d6SDag-Erling Smørgrav 	caught_signal = 0;
120ce77a8d6SDag-Erling Smørgrav 	action.sa_handler = &catch_signal;
121f7e6344dSDag-Erling Smørgrav 	action.sa_flags = 0;
122ce77a8d6SDag-Erling Smørgrav 	sigfillset(&action.sa_mask);
123ce77a8d6SDag-Erling Smørgrav 	sigaction(SIGINT, &action, &saction_sigint);
124ce77a8d6SDag-Erling Smørgrav 	sigaction(SIGQUIT, &action, &saction_sigquit);
125ce77a8d6SDag-Erling Smørgrav 	sigaction(SIGTERM, &action, &saction_sigterm);
126ce77a8d6SDag-Erling Smørgrav 
127ce77a8d6SDag-Erling Smørgrav 	/* compute timeout */
128ce77a8d6SDag-Erling Smørgrav 	if (openpam_ttyconv_timeout > 0) {
129ce77a8d6SDag-Erling Smørgrav 		(void)gettimeofday(&now, NULL);
130ce77a8d6SDag-Erling Smørgrav 		remaining.tv_sec = openpam_ttyconv_timeout;
131ce77a8d6SDag-Erling Smørgrav 		remaining.tv_usec = 0;
132ce77a8d6SDag-Erling Smørgrav 		timeradd(&now, &remaining, &target);
133ce77a8d6SDag-Erling Smørgrav 	} else {
134ce77a8d6SDag-Erling Smørgrav 		/* prevent bogus uninitialized variable warning */
135ce77a8d6SDag-Erling Smørgrav 		now.tv_sec = now.tv_usec = 0;
136ce77a8d6SDag-Erling Smørgrav 		remaining.tv_sec = remaining.tv_usec = 0;
137ce77a8d6SDag-Erling Smørgrav 		target.tv_sec = target.tv_usec = 0;
138ce77a8d6SDag-Erling Smørgrav 	}
139ce77a8d6SDag-Erling Smørgrav 
140ce77a8d6SDag-Erling Smørgrav 	/* input loop */
141ce77a8d6SDag-Erling Smørgrav 	pos = 0;
142ce77a8d6SDag-Erling Smørgrav 	ret = -1;
143ce77a8d6SDag-Erling Smørgrav 	serrno = 0;
144ce77a8d6SDag-Erling Smørgrav 	while (!caught_signal) {
145ce77a8d6SDag-Erling Smørgrav 		pfd.fd = ifd;
146ce77a8d6SDag-Erling Smørgrav 		pfd.events = POLLIN;
147ce77a8d6SDag-Erling Smørgrav 		pfd.revents = 0;
148ce77a8d6SDag-Erling Smørgrav 		if (openpam_ttyconv_timeout > 0) {
149ce77a8d6SDag-Erling Smørgrav 			gettimeofday(&now, NULL);
150ce77a8d6SDag-Erling Smørgrav 			if (timercmp(&now, &target, >))
151ce77a8d6SDag-Erling Smørgrav 				break;
152ce77a8d6SDag-Erling Smørgrav 			timersub(&target, &now, &remaining);
153ce77a8d6SDag-Erling Smørgrav 			remaining_ms = remaining.tv_sec * 1000 +
154ce77a8d6SDag-Erling Smørgrav 			    remaining.tv_usec / 1000;
155ce77a8d6SDag-Erling Smørgrav 		} else {
156ce77a8d6SDag-Erling Smørgrav 			remaining_ms = -1;
157ce77a8d6SDag-Erling Smørgrav 		}
158ce77a8d6SDag-Erling Smørgrav 		if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
159ce77a8d6SDag-Erling Smørgrav 			serrno = errno;
160ce77a8d6SDag-Erling Smørgrav 			if (errno == EINTR)
161ce77a8d6SDag-Erling Smørgrav 				continue;
162ce77a8d6SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "poll(): %m");
163ce77a8d6SDag-Erling Smørgrav 			break;
164ce77a8d6SDag-Erling Smørgrav 		} else if (ret == 0) {
165ce77a8d6SDag-Erling Smørgrav 			/* timeout */
166ce77a8d6SDag-Erling Smørgrav 			write(ofd, " timed out", 10);
167ce77a8d6SDag-Erling Smørgrav 			openpam_log(PAM_LOG_NOTICE, "timed out");
168ce77a8d6SDag-Erling Smørgrav 			break;
169ce77a8d6SDag-Erling Smørgrav 		}
170ce77a8d6SDag-Erling Smørgrav 		if ((ret = read(ifd, &ch, 1)) < 0) {
171ce77a8d6SDag-Erling Smørgrav 			serrno = errno;
172ce77a8d6SDag-Erling Smørgrav 			openpam_log(PAM_LOG_ERROR, "read(): %m");
173ce77a8d6SDag-Erling Smørgrav 			break;
174ce77a8d6SDag-Erling Smørgrav 		} else if (ret == 0 || ch == '\n') {
175ce77a8d6SDag-Erling Smørgrav 			response[pos] = '\0';
176ce77a8d6SDag-Erling Smørgrav 			ret = pos;
177ce77a8d6SDag-Erling Smørgrav 			break;
178ce77a8d6SDag-Erling Smørgrav 		}
179ce77a8d6SDag-Erling Smørgrav 		if (pos + 1 < PAM_MAX_RESP_SIZE)
180ce77a8d6SDag-Erling Smørgrav 			response[pos++] = ch;
181ce77a8d6SDag-Erling Smørgrav 		/* overflow is discarded */
182ce77a8d6SDag-Erling Smørgrav 	}
183ce77a8d6SDag-Erling Smørgrav 
184ce77a8d6SDag-Erling Smørgrav 	/* restore tty state */
185ce77a8d6SDag-Erling Smørgrav 	if (!echo) {
186ce77a8d6SDag-Erling Smørgrav 		tcattr.c_lflag = slflag;
187ce77a8d6SDag-Erling Smørgrav 		if (tcsetattr(ifd, 0, &tcattr) != 0) {
188ce77a8d6SDag-Erling Smørgrav 			/* treat as non-fatal, since we have our answer */
189ce77a8d6SDag-Erling Smørgrav 			openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m");
190ce77a8d6SDag-Erling Smørgrav 		}
191ce77a8d6SDag-Erling Smørgrav 	}
192ce77a8d6SDag-Erling Smørgrav 
193ce77a8d6SDag-Erling Smørgrav 	/* restore signal handlers and re-post caught signal*/
194ce77a8d6SDag-Erling Smørgrav 	sigaction(SIGINT, &saction_sigint, NULL);
195ce77a8d6SDag-Erling Smørgrav 	sigaction(SIGQUIT, &saction_sigquit, NULL);
196ce77a8d6SDag-Erling Smørgrav 	sigaction(SIGTERM, &saction_sigterm, NULL);
197ce77a8d6SDag-Erling Smørgrav 	if (caught_signal != 0) {
198ce77a8d6SDag-Erling Smørgrav 		openpam_log(PAM_LOG_ERROR, "caught signal %d",
199ce77a8d6SDag-Erling Smørgrav 		    (int)caught_signal);
200ce77a8d6SDag-Erling Smørgrav 		raise((int)caught_signal);
201ce77a8d6SDag-Erling Smørgrav 		/* if raise() had no effect... */
202ce77a8d6SDag-Erling Smørgrav 		serrno = EINTR;
203ce77a8d6SDag-Erling Smørgrav 		ret = -1;
204ce77a8d6SDag-Erling Smørgrav 	}
205ce77a8d6SDag-Erling Smørgrav 
206ce77a8d6SDag-Erling Smørgrav 	/* done */
207ce77a8d6SDag-Erling Smørgrav 	write(ofd, "\n", 1);
208ce77a8d6SDag-Erling Smørgrav 	errno = serrno;
209ce77a8d6SDag-Erling Smørgrav 	return (ret);
210ce77a8d6SDag-Erling Smørgrav }
211ce77a8d6SDag-Erling Smørgrav 
212ce77a8d6SDag-Erling Smørgrav /*
213ce77a8d6SDag-Erling Smørgrav  * Accept a response from the user on a non-tty stdin.
214ce77a8d6SDag-Erling Smørgrav  */
215ce77a8d6SDag-Erling Smørgrav static int
216ce77a8d6SDag-Erling Smørgrav prompt_notty(const char *message, char *response)
217ce77a8d6SDag-Erling Smørgrav {
218ce77a8d6SDag-Erling Smørgrav 	struct timeval now, target, remaining;
219ce77a8d6SDag-Erling Smørgrav 	int remaining_ms;
220ce77a8d6SDag-Erling Smørgrav 	struct pollfd pfd;
221ce77a8d6SDag-Erling Smørgrav 	int ch, pos, ret;
222ce77a8d6SDag-Erling Smørgrav 
223ce77a8d6SDag-Erling Smørgrav 	/* show prompt */
224ce77a8d6SDag-Erling Smørgrav 	fputs(message, stdout);
225ce77a8d6SDag-Erling Smørgrav 	fflush(stdout);
226ce77a8d6SDag-Erling Smørgrav 
227ce77a8d6SDag-Erling Smørgrav 	/* compute timeout */
228ce77a8d6SDag-Erling Smørgrav 	if (openpam_ttyconv_timeout > 0) {
229ce77a8d6SDag-Erling Smørgrav 		(void)gettimeofday(&now, NULL);
230ce77a8d6SDag-Erling Smørgrav 		remaining.tv_sec = openpam_ttyconv_timeout;
231ce77a8d6SDag-Erling Smørgrav 		remaining.tv_usec = 0;
232ce77a8d6SDag-Erling Smørgrav 		timeradd(&now, &remaining, &target);
233ce77a8d6SDag-Erling Smørgrav 	} else {
234ce77a8d6SDag-Erling Smørgrav 		/* prevent bogus uninitialized variable warning */
235ce77a8d6SDag-Erling Smørgrav 		now.tv_sec = now.tv_usec = 0;
236ce77a8d6SDag-Erling Smørgrav 		remaining.tv_sec = remaining.tv_usec = 0;
237ce77a8d6SDag-Erling Smørgrav 		target.tv_sec = target.tv_usec = 0;
238ce77a8d6SDag-Erling Smørgrav 	}
239ce77a8d6SDag-Erling Smørgrav 
240ce77a8d6SDag-Erling Smørgrav 	/* input loop */
241ce77a8d6SDag-Erling Smørgrav 	pos = 0;
242ce77a8d6SDag-Erling Smørgrav 	for (;;) {
243ce77a8d6SDag-Erling Smørgrav 		pfd.fd = STDIN_FILENO;
244ce77a8d6SDag-Erling Smørgrav 		pfd.events = POLLIN;
245ce77a8d6SDag-Erling Smørgrav 		pfd.revents = 0;
246ce77a8d6SDag-Erling Smørgrav 		if (openpam_ttyconv_timeout > 0) {
247ce77a8d6SDag-Erling Smørgrav 			gettimeofday(&now, NULL);
248ce77a8d6SDag-Erling Smørgrav 			if (timercmp(&now, &target, >))
249ce77a8d6SDag-Erling Smørgrav 				break;
250ce77a8d6SDag-Erling Smørgrav 			timersub(&target, &now, &remaining);
251ce77a8d6SDag-Erling Smørgrav 			remaining_ms = remaining.tv_sec * 1000 +
252ce77a8d6SDag-Erling Smørgrav 			    remaining.tv_usec / 1000;
253ce77a8d6SDag-Erling Smørgrav 		} else {
254ce77a8d6SDag-Erling Smørgrav 			remaining_ms = -1;
255ce77a8d6SDag-Erling Smørgrav 		}
256ce77a8d6SDag-Erling Smørgrav 		if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
257ce77a8d6SDag-Erling Smørgrav 			/* interrupt is ok, everything else -> bail */
258ce77a8d6SDag-Erling Smørgrav 			if (errno == EINTR)
259ce77a8d6SDag-Erling Smørgrav 				continue;
260ce77a8d6SDag-Erling Smørgrav 			perror("\nopenpam_ttyconv");
261ce77a8d6SDag-Erling Smørgrav 			return (-1);
262ce77a8d6SDag-Erling Smørgrav 		} else if (ret == 0) {
263ce77a8d6SDag-Erling Smørgrav 			/* timeout */
264ce77a8d6SDag-Erling Smørgrav 			break;
265ce77a8d6SDag-Erling Smørgrav 		} else {
266ce77a8d6SDag-Erling Smørgrav 			/* input */
267ce77a8d6SDag-Erling Smørgrav 			if ((ch = getchar()) == EOF && ferror(stdin)) {
268ce77a8d6SDag-Erling Smørgrav 				perror("\nopenpam_ttyconv");
269ce77a8d6SDag-Erling Smørgrav 				return (-1);
270ce77a8d6SDag-Erling Smørgrav 			}
271ce77a8d6SDag-Erling Smørgrav 			if (ch == EOF || ch == '\n') {
272ce77a8d6SDag-Erling Smørgrav 				response[pos] = '\0';
273ce77a8d6SDag-Erling Smørgrav 				return (pos);
274ce77a8d6SDag-Erling Smørgrav 			}
275ce77a8d6SDag-Erling Smørgrav 			if (pos + 1 < PAM_MAX_RESP_SIZE)
276ce77a8d6SDag-Erling Smørgrav 				response[pos++] = ch;
277ce77a8d6SDag-Erling Smørgrav 			/* overflow is discarded */
278ce77a8d6SDag-Erling Smørgrav 		}
279ce77a8d6SDag-Erling Smørgrav 	}
280ce77a8d6SDag-Erling Smørgrav 	fputs("\nopenpam_ttyconv: timeout\n", stderr);
281ce77a8d6SDag-Erling Smørgrav 	return (-1);
282ce77a8d6SDag-Erling Smørgrav }
283ce77a8d6SDag-Erling Smørgrav 
284ce77a8d6SDag-Erling Smørgrav /*
285ce77a8d6SDag-Erling Smørgrav  * Determine whether stdin is a tty; if not, try to open the tty; in
286ce77a8d6SDag-Erling Smørgrav  * either case, call the appropriate method.
287ce77a8d6SDag-Erling Smørgrav  */
288ce77a8d6SDag-Erling Smørgrav static int
289ce77a8d6SDag-Erling Smørgrav prompt(const char *message, char *response, int echo)
290ce77a8d6SDag-Erling Smørgrav {
291ce77a8d6SDag-Erling Smørgrav 	int ifd, ofd, ret;
292ce77a8d6SDag-Erling Smørgrav 
293ce77a8d6SDag-Erling Smørgrav 	if (isatty(STDIN_FILENO)) {
294f7e6344dSDag-Erling Smørgrav 		fflush(stdout);
295f7e6344dSDag-Erling Smørgrav #ifdef HAVE_FPURGE
296f7e6344dSDag-Erling Smørgrav 		fpurge(stdin);
297f7e6344dSDag-Erling Smørgrav #endif
298ce77a8d6SDag-Erling Smørgrav 		ifd = STDIN_FILENO;
299ce77a8d6SDag-Erling Smørgrav 		ofd = STDOUT_FILENO;
300ce77a8d6SDag-Erling Smørgrav 	} else {
301ce77a8d6SDag-Erling Smørgrav 		if ((ifd = open("/dev/tty", O_RDWR)) < 0)
302ce77a8d6SDag-Erling Smørgrav 			/* no way to prevent echo */
303ce77a8d6SDag-Erling Smørgrav 			return (prompt_notty(message, response));
304ce77a8d6SDag-Erling Smørgrav 		ofd = ifd;
305f7e6344dSDag-Erling Smørgrav 	}
306ce77a8d6SDag-Erling Smørgrav 	ret = prompt_tty(ifd, ofd, message, response, echo);
307ce77a8d6SDag-Erling Smørgrav 	if (ifd != STDIN_FILENO)
308ce77a8d6SDag-Erling Smørgrav 		close(ifd);
309f7e6344dSDag-Erling Smørgrav 	return (ret);
310f7e6344dSDag-Erling Smørgrav }
311f7e6344dSDag-Erling Smørgrav 
312f7e6344dSDag-Erling Smørgrav /*
313f7e6344dSDag-Erling Smørgrav  * OpenPAM extension
314f7e6344dSDag-Erling Smørgrav  *
315f7e6344dSDag-Erling Smørgrav  * Simple tty-based conversation function
316f7e6344dSDag-Erling Smørgrav  */
317f7e6344dSDag-Erling Smørgrav 
318f7e6344dSDag-Erling Smørgrav int
319f7e6344dSDag-Erling Smørgrav openpam_ttyconv(int n,
320f7e6344dSDag-Erling Smørgrav 	 const struct pam_message **msg,
321f7e6344dSDag-Erling Smørgrav 	 struct pam_response **resp,
322f7e6344dSDag-Erling Smørgrav 	 void *data)
323f7e6344dSDag-Erling Smørgrav {
324ce77a8d6SDag-Erling Smørgrav 	char respbuf[PAM_MAX_RESP_SIZE];
325f7e6344dSDag-Erling Smørgrav 	struct pam_response *aresp;
326f7e6344dSDag-Erling Smørgrav 	int i;
327f7e6344dSDag-Erling Smørgrav 
328f7e6344dSDag-Erling Smørgrav 	ENTER();
329f7e6344dSDag-Erling Smørgrav 	(void)data;
330f7e6344dSDag-Erling Smørgrav 	if (n <= 0 || n > PAM_MAX_NUM_MSG)
331f7e6344dSDag-Erling Smørgrav 		RETURNC(PAM_CONV_ERR);
332f7e6344dSDag-Erling Smørgrav 	if ((aresp = calloc(n, sizeof *aresp)) == NULL)
333f7e6344dSDag-Erling Smørgrav 		RETURNC(PAM_BUF_ERR);
334f7e6344dSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
335f7e6344dSDag-Erling Smørgrav 		aresp[i].resp_retcode = 0;
336f7e6344dSDag-Erling Smørgrav 		aresp[i].resp = NULL;
337f7e6344dSDag-Erling Smørgrav 		switch (msg[i]->msg_style) {
338f7e6344dSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_OFF:
339ce77a8d6SDag-Erling Smørgrav 			if (prompt(msg[i]->msg, respbuf, 0) < 0 ||
340ce77a8d6SDag-Erling Smørgrav 			    (aresp[i].resp = strdup(respbuf)) == NULL)
341f7e6344dSDag-Erling Smørgrav 				goto fail;
342f7e6344dSDag-Erling Smørgrav 			break;
343f7e6344dSDag-Erling Smørgrav 		case PAM_PROMPT_ECHO_ON:
344ce77a8d6SDag-Erling Smørgrav 			if (prompt(msg[i]->msg, respbuf, 1) < 0 ||
345ce77a8d6SDag-Erling Smørgrav 			    (aresp[i].resp = strdup(respbuf)) == NULL)
346f7e6344dSDag-Erling Smørgrav 				goto fail;
347f7e6344dSDag-Erling Smørgrav 			break;
348f7e6344dSDag-Erling Smørgrav 		case PAM_ERROR_MSG:
349f7e6344dSDag-Erling Smørgrav 			fputs(msg[i]->msg, stderr);
350f7e6344dSDag-Erling Smørgrav 			if (strlen(msg[i]->msg) > 0 &&
351f7e6344dSDag-Erling Smørgrav 			    msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
352f7e6344dSDag-Erling Smørgrav 				fputc('\n', stderr);
353f7e6344dSDag-Erling Smørgrav 			break;
354f7e6344dSDag-Erling Smørgrav 		case PAM_TEXT_INFO:
355f7e6344dSDag-Erling Smørgrav 			fputs(msg[i]->msg, stdout);
356f7e6344dSDag-Erling Smørgrav 			if (strlen(msg[i]->msg) > 0 &&
357f7e6344dSDag-Erling Smørgrav 			    msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
358f7e6344dSDag-Erling Smørgrav 				fputc('\n', stdout);
359f7e6344dSDag-Erling Smørgrav 			break;
360f7e6344dSDag-Erling Smørgrav 		default:
361f7e6344dSDag-Erling Smørgrav 			goto fail;
362f7e6344dSDag-Erling Smørgrav 		}
363f7e6344dSDag-Erling Smørgrav 	}
364f7e6344dSDag-Erling Smørgrav 	*resp = aresp;
365ce77a8d6SDag-Erling Smørgrav 	memset(respbuf, 0, sizeof respbuf);
366f7e6344dSDag-Erling Smørgrav 	RETURNC(PAM_SUCCESS);
367f7e6344dSDag-Erling Smørgrav fail:
368f7e6344dSDag-Erling Smørgrav 	for (i = 0; i < n; ++i) {
369f7e6344dSDag-Erling Smørgrav 		if (aresp[i].resp != NULL) {
370*d64f4044SDag-Erling Smørgrav 			strlset(aresp[i].resp, 0, PAM_MAX_RESP_SIZE);
371f7e6344dSDag-Erling Smørgrav 			FREE(aresp[i].resp);
372f7e6344dSDag-Erling Smørgrav 		}
373f7e6344dSDag-Erling Smørgrav 	}
374f7e6344dSDag-Erling Smørgrav 	memset(aresp, 0, n * sizeof *aresp);
375f7e6344dSDag-Erling Smørgrav 	FREE(aresp);
376f7e6344dSDag-Erling Smørgrav 	*resp = NULL;
377ce77a8d6SDag-Erling Smørgrav 	memset(respbuf, 0, sizeof respbuf);
378f7e6344dSDag-Erling Smørgrav 	RETURNC(PAM_CONV_ERR);
379f7e6344dSDag-Erling Smørgrav }
380f7e6344dSDag-Erling Smørgrav 
381f7e6344dSDag-Erling Smørgrav /*
382f7e6344dSDag-Erling Smørgrav  * Error codes:
383f7e6344dSDag-Erling Smørgrav  *
384f7e6344dSDag-Erling Smørgrav  *	PAM_SYSTEM_ERR
385f7e6344dSDag-Erling Smørgrav  *	PAM_BUF_ERR
386f7e6344dSDag-Erling Smørgrav  *	PAM_CONV_ERR
387f7e6344dSDag-Erling Smørgrav  */
388f7e6344dSDag-Erling Smørgrav 
389f7e6344dSDag-Erling Smørgrav /**
390f7e6344dSDag-Erling Smørgrav  * The =openpam_ttyconv function is a standard conversation function
391f7e6344dSDag-Erling Smørgrav  * suitable for use on TTY devices.
392f7e6344dSDag-Erling Smørgrav  * It should be adequate for the needs of most text-based interactive
393f7e6344dSDag-Erling Smørgrav  * programs.
394f7e6344dSDag-Erling Smørgrav  *
395f7e6344dSDag-Erling Smørgrav  * The =openpam_ttyconv function allows the application to specify a
396f7e6344dSDag-Erling Smørgrav  * timeout for user input by setting the global integer variable
397f7e6344dSDag-Erling Smørgrav  * :openpam_ttyconv_timeout to the length of the timeout in seconds.
398f7e6344dSDag-Erling Smørgrav  *
399f7e6344dSDag-Erling Smørgrav  * >openpam_nullconv
400f7e6344dSDag-Erling Smørgrav  * >pam_prompt
401f7e6344dSDag-Erling Smørgrav  * >pam_vprompt
402f7e6344dSDag-Erling Smørgrav  */
403