openpam_ttyconv.c revision 228690
1/*-
2 * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2011 Dag-Erling Sm��rgrav
4 * All rights reserved.
5 *
6 * This software was developed for the FreeBSD Project by ThinkSec AS and
7 * Network Associates Laboratories, the Security Research Division of
8 * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
9 * ("CBOSS"), as part of the DARPA CHATS research program.
10 *
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
13 * are met:
14 * 1. Redistributions of source code must retain the above copyright
15 *    notice, this list of conditions and the following disclaimer.
16 * 2. Redistributions in binary form must reproduce the above copyright
17 *    notice, this list of conditions and the following disclaimer in the
18 *    documentation and/or other materials provided with the distribution.
19 * 3. The name of the author may not be used to endorse or promote
20 *    products derived from this software without specific prior written
21 *    permission.
22 *
23 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
33 * SUCH DAMAGE.
34 *
35 * $Id: openpam_ttyconv.c 437 2011-09-13 12:00:13Z des $
36 */
37
38#ifdef HAVE_CONFIG_H
39# include "config.h"
40#endif
41
42#include <sys/types.h>
43
44#include <ctype.h>
45#include <errno.h>
46#include <setjmp.h>
47#include <signal.h>
48#include <stdio.h>
49#include <stdlib.h>
50#include <string.h>
51#include <termios.h>
52#include <unistd.h>
53
54#include <security/pam_appl.h>
55
56#include "openpam_impl.h"
57
58int openpam_ttyconv_timeout = 0;
59
60static void
61timeout(int sig)
62{
63
64	(void)sig;
65}
66
67static char *
68prompt(const char *msg)
69{
70	char buf[PAM_MAX_RESP_SIZE];
71	struct sigaction action, saved_action;
72	sigset_t saved_sigset, sigset;
73	unsigned int saved_alarm;
74	int eof, error, fd;
75	size_t len;
76	char *retval;
77	char ch;
78
79	sigemptyset(&sigset);
80	sigaddset(&sigset, SIGINT);
81	sigaddset(&sigset, SIGTSTP);
82	sigprocmask(SIG_SETMASK, &sigset, &saved_sigset);
83	action.sa_handler = &timeout;
84	action.sa_flags = 0;
85	sigemptyset(&action.sa_mask);
86	sigaction(SIGALRM, &action, &saved_action);
87	fputs(msg, stdout);
88	fflush(stdout);
89#ifdef HAVE_FPURGE
90	fpurge(stdin);
91#endif
92	fd = fileno(stdin);
93	buf[0] = '\0';
94	eof = error = 0;
95	saved_alarm = 0;
96	if (openpam_ttyconv_timeout >= 0)
97		saved_alarm = alarm(openpam_ttyconv_timeout);
98	ch = '\0';
99	for (len = 0; ch != '\n' && !eof && !error; ++len) {
100		switch (read(fd, &ch, 1)) {
101		case 1:
102			if (len < PAM_MAX_RESP_SIZE - 1) {
103				buf[len + 1] = '\0';
104				buf[len] = ch;
105			}
106			break;
107		case 0:
108			eof = 1;
109			break;
110		default:
111			error = errno;
112			break;
113		}
114	}
115	if (openpam_ttyconv_timeout >= 0)
116		alarm(0);
117	sigaction(SIGALRM, &saved_action, NULL);
118	sigprocmask(SIG_SETMASK, &saved_sigset, NULL);
119	if (saved_alarm > 0)
120		alarm(saved_alarm);
121	if (error == EINTR)
122		fputs(" timeout!", stderr);
123	if (error || eof) {
124		fputs("\n", stderr);
125		memset(buf, 0, sizeof(buf));
126		return (NULL);
127	}
128	/* trim trailing whitespace */
129	for (len = strlen(buf); len > 0; --len)
130		if (buf[len - 1] != '\r' && buf[len - 1] != '\n')
131			break;
132	buf[len] = '\0';
133	retval = strdup(buf);
134	memset(buf, 0, sizeof(buf));
135	return (retval);
136}
137
138static char *
139prompt_echo_off(const char *msg)
140{
141	struct termios tattr;
142	tcflag_t lflag;
143	char *ret;
144	int fd;
145
146	fd = fileno(stdin);
147	if (tcgetattr(fd, &tattr) != 0) {
148		openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
149		return (NULL);
150	}
151	lflag = tattr.c_lflag;
152	tattr.c_lflag &= ~ECHO;
153	if (tcsetattr(fd, TCSAFLUSH, &tattr) != 0) {
154		openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
155		return (NULL);
156	}
157	ret = prompt(msg);
158	tattr.c_lflag = lflag;
159	(void)tcsetattr(fd, TCSANOW, &tattr);
160	if (ret != NULL)
161		fputs("\n", stdout);
162	return (ret);
163}
164
165/*
166 * OpenPAM extension
167 *
168 * Simple tty-based conversation function
169 */
170
171int
172openpam_ttyconv(int n,
173	 const struct pam_message **msg,
174	 struct pam_response **resp,
175	 void *data)
176{
177	struct pam_response *aresp;
178	int i;
179
180	ENTER();
181	(void)data;
182	if (n <= 0 || n > PAM_MAX_NUM_MSG)
183		RETURNC(PAM_CONV_ERR);
184	if ((aresp = calloc(n, sizeof *aresp)) == NULL)
185		RETURNC(PAM_BUF_ERR);
186	for (i = 0; i < n; ++i) {
187		aresp[i].resp_retcode = 0;
188		aresp[i].resp = NULL;
189		switch (msg[i]->msg_style) {
190		case PAM_PROMPT_ECHO_OFF:
191			aresp[i].resp = prompt_echo_off(msg[i]->msg);
192			if (aresp[i].resp == NULL)
193				goto fail;
194			break;
195		case PAM_PROMPT_ECHO_ON:
196			aresp[i].resp = prompt(msg[i]->msg);
197			if (aresp[i].resp == NULL)
198				goto fail;
199			break;
200		case PAM_ERROR_MSG:
201			fputs(msg[i]->msg, stderr);
202			if (strlen(msg[i]->msg) > 0 &&
203			    msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
204				fputc('\n', stderr);
205			break;
206		case PAM_TEXT_INFO:
207			fputs(msg[i]->msg, stdout);
208			if (strlen(msg[i]->msg) > 0 &&
209			    msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
210				fputc('\n', stdout);
211			break;
212		default:
213			goto fail;
214		}
215	}
216	*resp = aresp;
217	RETURNC(PAM_SUCCESS);
218fail:
219	for (i = 0; i < n; ++i) {
220		if (aresp[i].resp != NULL) {
221			memset(aresp[i].resp, 0, strlen(aresp[i].resp));
222			FREE(aresp[i].resp);
223		}
224	}
225	memset(aresp, 0, n * sizeof *aresp);
226	FREE(aresp);
227	*resp = NULL;
228	RETURNC(PAM_CONV_ERR);
229}
230
231/*
232 * Error codes:
233 *
234 *	PAM_SYSTEM_ERR
235 *	PAM_BUF_ERR
236 *	PAM_CONV_ERR
237 */
238
239/**
240 * The =openpam_ttyconv function is a standard conversation function
241 * suitable for use on TTY devices.
242 * It should be adequate for the needs of most text-based interactive
243 * programs.
244 *
245 * The =openpam_ttyconv function allows the application to specify a
246 * timeout for user input by setting the global integer variable
247 * :openpam_ttyconv_timeout to the length of the timeout in seconds.
248 *
249 * >openpam_nullconv
250 * >pam_prompt
251 * >pam_vprompt
252 */
253