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