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