1 /* Copyright (C) 1992-2001, 2003, 2004, 2005 Free Software Foundation, Inc. 2 This file is part of the GNU C Library. 3 4 This program is free software; you can redistribute it and/or modify 5 it under the terms of the GNU General Public License as published by 6 the Free Software Foundation; either version 2, or (at your option) 7 any later version. 8 9 This program is distributed in the hope that it will be useful, 10 but WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 GNU General Public License for more details. 13 14 You should have received a copy of the GNU General Public License along 15 with this program; if not, write to the Free Software Foundation, 16 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ 17 #include <sys/cdefs.h> 18 __RCSID("$NetBSD: getpass.c,v 1.2 2016/05/17 14:00:09 christos Exp $"); 19 20 21 #ifdef HAVE_CONFIG_H 22 # include <config.h> 23 #endif 24 25 #include "getpass.h" 26 27 #include <stdio.h> 28 29 #if !defined _WIN32 30 31 #include <stdbool.h> 32 33 #if HAVE_STDIO_EXT_H 34 # include <stdio_ext.h> 35 #endif 36 #if !HAVE___FSETLOCKING 37 # define __fsetlocking(stream, type) /* empty */ 38 #endif 39 40 #if HAVE_TERMIOS_H 41 # include <termios.h> 42 #endif 43 44 #include "getline.h" 45 46 #if USE_UNLOCKED_IO 47 # include "unlocked-io.h" 48 #else 49 # if !HAVE_DECL_FFLUSH_UNLOCKED 50 # undef fflush_unlocked 51 # define fflush_unlocked(x) fflush (x) 52 # endif 53 # if !HAVE_DECL_FLOCKFILE 54 # undef flockfile 55 # define flockfile(x) ((void) 0) 56 # endif 57 # if !HAVE_DECL_FUNLOCKFILE 58 # undef funlockfile 59 # define funlockfile(x) ((void) 0) 60 # endif 61 # if !HAVE_DECL_FPUTS_UNLOCKED 62 # undef fputs_unlocked 63 # define fputs_unlocked(str,stream) fputs (str, stream) 64 # endif 65 # if !HAVE_DECL_PUTC_UNLOCKED 66 # undef putc_unlocked 67 # define putc_unlocked(c,stream) putc (c, stream) 68 # endif 69 #endif 70 71 /* It is desirable to use this bit on systems that have it. 72 The only bit of terminal state we want to twiddle is echoing, which is 73 done in software; there is no need to change the state of the terminal 74 hardware. */ 75 76 #ifndef TCSASOFT 77 # define TCSASOFT 0 78 #endif 79 80 static void 81 call_fclose (void *arg) 82 { 83 if (arg != NULL) 84 fclose (arg); 85 } 86 87 char * 88 getpass (const char *prompt) 89 { 90 FILE *tty; 91 FILE *in, *out; 92 struct termios s, t; 93 bool tty_changed = false; 94 static char *buf; 95 static size_t bufsize; 96 ssize_t nread; 97 98 /* Try to write to and read from the terminal if we can. 99 If we can't open the terminal, use stderr and stdin. */ 100 101 tty = fopen ("/dev/tty", "w+"); 102 if (tty == NULL) 103 { 104 in = stdin; 105 out = stderr; 106 } 107 else 108 { 109 /* We do the locking ourselves. */ 110 __fsetlocking (tty, FSETLOCKING_BYCALLER); 111 112 out = in = tty; 113 } 114 115 flockfile (out); 116 117 /* Turn echoing off if it is on now. */ 118 #if HAVE_TCGETATTR 119 if (tcgetattr (fileno (in), &t) == 0) 120 { 121 /* Save the old one. */ 122 s = t; 123 /* Tricky, tricky. */ 124 t.c_lflag &= ~(ECHO | ISIG); 125 tty_changed = (tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &t) == 0); 126 } 127 #endif 128 129 /* Write the prompt. */ 130 fputs_unlocked (prompt, out); 131 fflush_unlocked (out); 132 133 /* Read the password. */ 134 nread = getline (&buf, &bufsize, in); 135 136 /* According to the C standard, input may not be followed by output 137 on the same stream without an intervening call to a file 138 positioning function. Suppose in == out; then without this fseek 139 call, on Solaris, HP-UX, AIX, OSF/1, the previous input gets 140 echoed, whereas on IRIX, the following newline is not output as 141 it should be. POSIX imposes similar restrictions if fileno (in) 142 == fileno (out). The POSIX restrictions are tricky and change 143 from POSIX version to POSIX version, so play it safe and invoke 144 fseek even if in != out. */ 145 fseek (out, 0, SEEK_CUR); 146 147 if (buf != NULL) 148 { 149 if (nread < 0) 150 buf[0] = '\0'; 151 else if (buf[nread - 1] == '\n') 152 { 153 /* Remove the newline. */ 154 buf[nread - 1] = '\0'; 155 if (tty_changed) 156 { 157 /* Write the newline that was not echoed. */ 158 putc_unlocked ('\n', out); 159 } 160 } 161 } 162 163 /* Restore the original setting. */ 164 #if HAVE_TCSETATTR 165 if (tty_changed) 166 tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &s); 167 #endif 168 169 funlockfile (out); 170 171 call_fclose (tty); 172 173 return buf; 174 } 175 176 #else /* WIN32 */ 177 178 /* Windows implementation by Martin Lambers <marlam@marlam.de>, 179 improved by Simon Josefsson. */ 180 181 /* For PASS_MAX. */ 182 #include <limits.h> 183 184 #ifndef PASS_MAX 185 # define PASS_MAX 512 186 #endif 187 188 char * 189 getpass (const char *prompt) 190 { 191 char getpassbuf[PASS_MAX + 1]; 192 size_t i = 0; 193 int c; 194 195 if (prompt) 196 { 197 fputs (prompt, stderr); 198 fflush (stderr); 199 } 200 201 for (;;) 202 { 203 c = _getch (); 204 if (c == '\r') 205 { 206 getpassbuf[i] = '\0'; 207 break; 208 } 209 else if (i < PASS_MAX) 210 { 211 getpassbuf[i++] = c; 212 } 213 214 if (i >= PASS_MAX) 215 { 216 getpassbuf[i] = '\0'; 217 break; 218 } 219 } 220 221 if (prompt) 222 { 223 fputs ("\r\n", stderr); 224 fflush (stderr); 225 } 226 227 return strdup (getpassbuf); 228 } 229 #endif 230