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