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