2 * Copyright (c) 2002-2003 Networks Associates Technology, Inc.
3 * Copyright (c) 2004-2011 Dag-Erling Smørgrav
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.
11 * Redistribution and use in source and binary forms, with or without
12 * modification, are permitted provided that the following conditions
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
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
35 * $Id: openpam_ttyconv.c 688 2013-07-11 16:40:08Z des $
42 #include <sys/types.h>
55 #include <security/pam_appl.h>
57 #include "openpam_impl.h"
59 int openpam_ttyconv_timeout = 0;
61 static volatile sig_atomic_t caught_signal;
64 * Handle incoming signals during tty conversation
67 catch_signal(int signo)
74 caught_signal = signo;
80 * Accept a response from the user on a tty
83 prompt_tty(int ifd, int ofd, const char *message, char *response, int echo)
85 struct sigaction action;
86 struct sigaction saction_sigint, saction_sigquit, saction_sigterm;
87 struct termios tcattr;
88 struct timeval now, target, remaining;
97 if (write(ofd, message, strlen(message)) < 0) {
98 openpam_log(PAM_LOG_ERROR, "write(): %m");
102 /* turn echo off if requested */
103 slflag = 0; /* prevent bogus uninitialized variable warning */
105 if (tcgetattr(ifd, &tcattr) != 0) {
106 openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m");
109 slflag = tcattr.c_lflag;
110 tcattr.c_lflag &= ~ECHO;
111 if (tcsetattr(ifd, TCSAFLUSH, &tcattr) != 0) {
112 openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m");
117 /* install signal handlers */
119 action.sa_handler = &catch_signal;
121 sigfillset(&action.sa_mask);
122 sigaction(SIGINT, &action, &saction_sigint);
123 sigaction(SIGQUIT, &action, &saction_sigquit);
124 sigaction(SIGTERM, &action, &saction_sigterm);
126 /* compute timeout */
127 if (openpam_ttyconv_timeout > 0) {
128 (void)gettimeofday(&now, NULL);
129 remaining.tv_sec = openpam_ttyconv_timeout;
130 remaining.tv_usec = 0;
131 timeradd(&now, &remaining, &target);
133 /* prevent bogus uninitialized variable warning */
134 now.tv_sec = now.tv_usec = 0;
135 remaining.tv_sec = remaining.tv_usec = 0;
136 target.tv_sec = target.tv_usec = 0;
143 while (!caught_signal) {
147 if (openpam_ttyconv_timeout > 0) {
148 gettimeofday(&now, NULL);
149 if (timercmp(&now, &target, >))
151 timersub(&target, &now, &remaining);
152 remaining_ms = remaining.tv_sec * 1000 +
153 remaining.tv_usec / 1000;
157 if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
161 openpam_log(PAM_LOG_ERROR, "poll(): %m");
163 } else if (ret == 0) {
165 write(ofd, " timed out", 10);
166 openpam_log(PAM_LOG_NOTICE, "timed out");
169 if ((ret = read(ifd, &ch, 1)) < 0) {
171 openpam_log(PAM_LOG_ERROR, "read(): %m");
173 } else if (ret == 0 || ch == '\n') {
174 response[pos] = '\0';
178 if (pos + 1 < PAM_MAX_RESP_SIZE)
179 response[pos++] = ch;
180 /* overflow is discarded */
183 /* restore tty state */
185 tcattr.c_lflag = slflag;
186 if (tcsetattr(ifd, 0, &tcattr) != 0) {
187 /* treat as non-fatal, since we have our answer */
188 openpam_log(PAM_LOG_NOTICE, "tcsetattr(): %m");
192 /* restore signal handlers and re-post caught signal*/
193 sigaction(SIGINT, &saction_sigint, NULL);
194 sigaction(SIGQUIT, &saction_sigquit, NULL);
195 sigaction(SIGTERM, &saction_sigterm, NULL);
196 if (caught_signal != 0) {
197 openpam_log(PAM_LOG_ERROR, "caught signal %d",
199 raise((int)caught_signal);
200 /* if raise() had no effect... */
212 * Accept a response from the user on a non-tty stdin.
215 prompt_notty(const char *message, char *response)
217 struct timeval now, target, remaining;
223 fputs(message, stdout);
226 /* compute timeout */
227 if (openpam_ttyconv_timeout > 0) {
228 (void)gettimeofday(&now, NULL);
229 remaining.tv_sec = openpam_ttyconv_timeout;
230 remaining.tv_usec = 0;
231 timeradd(&now, &remaining, &target);
233 /* prevent bogus uninitialized variable warning */
234 now.tv_sec = now.tv_usec = 0;
235 remaining.tv_sec = remaining.tv_usec = 0;
236 target.tv_sec = target.tv_usec = 0;
242 pfd.fd = STDIN_FILENO;
245 if (openpam_ttyconv_timeout > 0) {
246 gettimeofday(&now, NULL);
247 if (timercmp(&now, &target, >))
249 timersub(&target, &now, &remaining);
250 remaining_ms = remaining.tv_sec * 1000 +
251 remaining.tv_usec / 1000;
255 if ((ret = poll(&pfd, 1, remaining_ms)) < 0) {
256 /* interrupt is ok, everything else -> bail */
259 perror("\nopenpam_ttyconv");
261 } else if (ret == 0) {
266 if ((ch = getchar()) == EOF && ferror(stdin)) {
267 perror("\nopenpam_ttyconv");
270 if (ch == EOF || ch == '\n') {
271 response[pos] = '\0';
274 if (pos + 1 < PAM_MAX_RESP_SIZE)
275 response[pos++] = ch;
276 /* overflow is discarded */
279 fputs("\nopenpam_ttyconv: timeout\n", stderr);
284 * Determine whether stdin is a tty; if not, try to open the tty; in
285 * either case, call the appropriate method.
288 prompt(const char *message, char *response, int echo)
292 if (isatty(STDIN_FILENO)) {
300 if ((ifd = open("/dev/tty", O_RDWR)) < 0)
301 /* no way to prevent echo */
302 return (prompt_notty(message, response));
305 ret = prompt_tty(ifd, ofd, message, response, echo);
306 if (ifd != STDIN_FILENO)
314 * Simple tty-based conversation function
318 openpam_ttyconv(int n,
319 const struct pam_message **msg,
320 struct pam_response **resp,
323 char respbuf[PAM_MAX_RESP_SIZE];
324 struct pam_response *aresp;
329 if (n <= 0 || n > PAM_MAX_NUM_MSG)
330 RETURNC(PAM_CONV_ERR);
331 if ((aresp = calloc(n, sizeof *aresp)) == NULL)
332 RETURNC(PAM_BUF_ERR);
333 for (i = 0; i < n; ++i) {
334 aresp[i].resp_retcode = 0;
335 aresp[i].resp = NULL;
336 switch (msg[i]->msg_style) {
337 case PAM_PROMPT_ECHO_OFF:
338 if (prompt(msg[i]->msg, respbuf, 0) < 0 ||
339 (aresp[i].resp = strdup(respbuf)) == NULL)
342 case PAM_PROMPT_ECHO_ON:
343 if (prompt(msg[i]->msg, respbuf, 1) < 0 ||
344 (aresp[i].resp = strdup(respbuf)) == NULL)
348 fputs(msg[i]->msg, stderr);
349 if (strlen(msg[i]->msg) > 0 &&
350 msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
354 fputs(msg[i]->msg, stdout);
355 if (strlen(msg[i]->msg) > 0 &&
356 msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
364 memset(respbuf, 0, sizeof respbuf);
365 RETURNC(PAM_SUCCESS);
367 for (i = 0; i < n; ++i) {
368 if (aresp[i].resp != NULL) {
369 memset(aresp[i].resp, 0, strlen(aresp[i].resp));
373 memset(aresp, 0, n * sizeof *aresp);
376 memset(respbuf, 0, sizeof respbuf);
377 RETURNC(PAM_CONV_ERR);
389 * The =openpam_ttyconv function is a standard conversation function
390 * suitable for use on TTY devices.
391 * It should be adequate for the needs of most text-based interactive
394 * The =openpam_ttyconv function allows the application to specify a
395 * timeout for user input by setting the global integer variable
396 * :openpam_ttyconv_timeout to the length of the timeout in seconds.