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