1/*- 2 * Copyright (c) 2002-2003 Networks Associates Technology, Inc. 3 * Copyright (c) 2004-2007 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 408 2007-12-21 11:36:24Z des $ 36 */ 37 38#include <sys/types.h> 39 40#include <ctype.h> 41#include <errno.h> 42#include <setjmp.h> 43#include <signal.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <termios.h> 48#include <unistd.h> 49 50#include <security/pam_appl.h> 51 52#include "openpam_impl.h" 53 54int openpam_ttyconv_timeout = 0; 55 56static void 57timeout(int sig) 58{ 59 60 (void)sig; 61} 62 63static char * 64prompt(const char *msg) 65{ 66 char buf[PAM_MAX_RESP_SIZE]; 67 struct sigaction action, saved_action; 68 sigset_t saved_sigset, sigset; 69 unsigned int saved_alarm; 70 int eof, error, fd; 71 size_t len; 72 char *retval; 73 char ch; 74 75 sigemptyset(&sigset); 76 sigaddset(&sigset, SIGINT); 77 sigaddset(&sigset, SIGTSTP); 78 sigprocmask(SIG_SETMASK, &sigset, &saved_sigset); 79 action.sa_handler = &timeout; 80 action.sa_flags = 0; 81 sigemptyset(&action.sa_mask); 82 sigaction(SIGALRM, &action, &saved_action); 83 fputs(msg, stdout); 84 fflush(stdout); 85#ifdef HAVE_FPURGE 86 fpurge(stdin); 87#endif 88 fd = fileno(stdin); 89 buf[0] = '\0'; 90 eof = error = 0; 91 saved_alarm = 0; 92 if (openpam_ttyconv_timeout >= 0) 93 saved_alarm = alarm(openpam_ttyconv_timeout); 94 ch = '\0'; 95 for (len = 0; ch != '\n' && !eof && !error; ++len) { 96 switch (read(fd, &ch, 1)) { 97 case 1: 98 if (len < PAM_MAX_RESP_SIZE - 1) { 99 buf[len + 1] = '\0'; 100 buf[len] = ch; 101 } 102 break; 103 case 0: 104 eof = 1; 105 break; 106 default: 107 error = errno; 108 break; 109 } 110 } 111 if (openpam_ttyconv_timeout >= 0) 112 alarm(0); 113 sigaction(SIGALRM, &saved_action, NULL); 114 sigprocmask(SIG_SETMASK, &saved_sigset, NULL); 115 if (saved_alarm > 0) 116 alarm(saved_alarm); 117 if (error == EINTR) 118 fputs(" timeout!", stderr); 119 if (error || eof) { 120 fputs("\n", stderr); 121 memset(buf, 0, sizeof(buf)); 122 return (NULL); 123 } 124 /* trim trailing whitespace */ 125 for (len = strlen(buf); len > 0; --len) 126 if (buf[len - 1] != '\r' && buf[len - 1] != '\n') 127 break; 128 buf[len] = '\0'; 129 retval = strdup(buf); 130 memset(buf, 0, sizeof(buf)); 131 return (retval); 132} 133 134static char * 135prompt_echo_off(const char *msg) 136{ 137 struct termios tattr; 138 tcflag_t lflag; 139 char *ret; 140 int fd; 141 142 fd = fileno(stdin); 143 if (tcgetattr(fd, &tattr) != 0) { 144 openpam_log(PAM_LOG_ERROR, "tcgetattr(): %m"); 145 return (NULL); 146 } 147 lflag = tattr.c_lflag; 148 tattr.c_lflag &= ~ECHO; 149 if (tcsetattr(fd, TCSAFLUSH, &tattr) != 0) { 150 openpam_log(PAM_LOG_ERROR, "tcsetattr(): %m"); 151 return (NULL); 152 } 153 ret = prompt(msg); 154 tattr.c_lflag = lflag; 155 (void)tcsetattr(fd, TCSANOW, &tattr); 156 if (ret != NULL) 157 fputs("\n", stdout); 158 return (ret); 159} 160 161/* 162 * OpenPAM extension 163 * 164 * Simple tty-based conversation function 165 */ 166 167int 168openpam_ttyconv(int n, 169 const struct pam_message **msg, 170 struct pam_response **resp, 171 void *data) 172{ 173 struct pam_response *aresp; 174 int i; 175 176 ENTER(); 177 (void)data; 178 if (n <= 0 || n > PAM_MAX_NUM_MSG) 179 RETURNC(PAM_CONV_ERR); 180 if ((aresp = calloc(n, sizeof *aresp)) == NULL) 181 RETURNC(PAM_BUF_ERR); 182 for (i = 0; i < n; ++i) { 183 aresp[i].resp_retcode = 0; 184 aresp[i].resp = NULL; 185 switch (msg[i]->msg_style) { 186 case PAM_PROMPT_ECHO_OFF: 187 aresp[i].resp = prompt_echo_off(msg[i]->msg); 188 if (aresp[i].resp == NULL) 189 goto fail; 190 break; 191 case PAM_PROMPT_ECHO_ON: 192 aresp[i].resp = prompt(msg[i]->msg); 193 if (aresp[i].resp == NULL) 194 goto fail; 195 break; 196 case PAM_ERROR_MSG: 197 fputs(msg[i]->msg, stderr); 198 if (strlen(msg[i]->msg) > 0 && 199 msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n') 200 fputc('\n', stderr); 201 break; 202 case PAM_TEXT_INFO: 203 fputs(msg[i]->msg, stdout); 204 if (strlen(msg[i]->msg) > 0 && 205 msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n') 206 fputc('\n', stdout); 207 break; 208 default: 209 goto fail; 210 } 211 } 212 *resp = aresp; 213 RETURNC(PAM_SUCCESS); 214 fail: 215 for (i = 0; i < n; ++i) { 216 if (aresp[i].resp != NULL) { 217 memset(aresp[i].resp, 0, strlen(aresp[i].resp)); 218 FREE(aresp[i].resp); 219 } 220 } 221 memset(aresp, 0, n * sizeof *aresp); 222 FREE(aresp); 223 *resp = NULL; 224 RETURNC(PAM_CONV_ERR); 225} 226 227/* 228 * XXX OpenPAM migration hack 229 * 230 * misc_conv 231 */ 232 233int misc_conv(int num_msg, 234 const struct pam_message **msgm, 235 struct pam_response **response, 236 void *appdata_ptr) 237{ 238 return openpam_ttyconv(num_msg, msgm, response, appdata_ptr); 239} 240 241 242/* 243 * Error codes: 244 * 245 * PAM_SYSTEM_ERR 246 * PAM_BUF_ERR 247 * PAM_CONV_ERR 248 */ 249 250/** 251 * The =openpam_ttyconv function is a standard conversation function 252 * suitable for use on TTY devices. 253 * It should be adequate for the needs of most text-based interactive 254 * programs. 255 * 256 * The =openpam_ttyconv function allows the application to specify a 257 * timeout for user input by setting the global integer variable 258 * :openpam_ttyconv_timeout to the length of the timeout in seconds. 259 * 260 * >openpam_nullconv 261 * >pam_prompt 262 * >pam_vprompt 263 */ 264