1/* Copyright (C) 1992-2001, 2003-2007, 2009-2014 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 <http://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 __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 76static void 77call_fclose (void *arg) 78{ 79 if (arg != NULL) 80 fclose (arg); 81} 82 83char * 84getpass (const char *prompt) 85{ 86 FILE *tty; 87 FILE *in, *out; 88 struct termios s, t; 89 bool tty_changed = false; 90 static char *buf; 91 static size_t bufsize; 92 ssize_t nread; 93 94 /* Try to write to and read from the terminal if we can. 95 If we can't open the terminal, use stderr and stdin. */ 96 97 tty = fopen ("/dev/tty", "w+"); 98 if (tty == NULL) 99 { 100 in = stdin; 101 out = stderr; 102 } 103 else 104 { 105 /* We do the locking ourselves. */ 106 __fsetlocking (tty, FSETLOCKING_BYCALLER); 107 108 out = in = tty; 109 } 110 111 flockfile (out); 112 113 /* Turn echoing off if it is on now. */ 114# if HAVE_TCGETATTR 115 if (tcgetattr (fileno (in), &t) == 0) 116 { 117 /* Save the old one. */ 118 s = t; 119 /* Tricky, tricky. */ 120 t.c_lflag &= ~(ECHO | ISIG); 121 tty_changed = (tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &t) == 0); 122 } 123# endif 124 125 /* Write the prompt. */ 126 fputs_unlocked (prompt, out); 127 fflush_unlocked (out); 128 129 /* Read the password. */ 130 nread = getline (&buf, &bufsize, in); 131 132 /* According to the C standard, input may not be followed by output 133 on the same stream without an intervening call to a file 134 positioning function. Suppose in == out; then without this fseek 135 call, on Solaris, HP-UX, AIX, OSF/1, the previous input gets 136 echoed, whereas on IRIX, the following newline is not output as 137 it should be. POSIX imposes similar restrictions if fileno (in) 138 == fileno (out). The POSIX restrictions are tricky and change 139 from POSIX version to POSIX version, so play it safe and invoke 140 fseek even if in != out. */ 141 fseeko (out, 0, SEEK_CUR); 142 143 if (buf != NULL) 144 { 145 if (nread < 0) 146 buf[0] = '\0'; 147 else if (buf[nread - 1] == '\n') 148 { 149 /* Remove the newline. */ 150 buf[nread - 1] = '\0'; 151 if (tty_changed) 152 { 153 /* Write the newline that was not echoed. */ 154 putc_unlocked ('\n', out); 155 } 156 } 157 } 158 159 /* Restore the original setting. */ 160# if HAVE_TCSETATTR 161 if (tty_changed) 162 tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &s); 163# endif 164 165 funlockfile (out); 166 167 call_fclose (tty); 168 169 return buf; 170} 171 172#else /* W32 native */ 173 174/* Windows implementation by Martin Lambers <marlam@marlam.de>, 175 improved by Simon Josefsson. */ 176 177/* For PASS_MAX. */ 178# include <limits.h> 179/* For _getch(). */ 180# include <conio.h> 181/* For strdup(). */ 182# include <string.h> 183 184# ifndef PASS_MAX 185# define PASS_MAX 512 186# endif 187 188char * 189getpass (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