1 /* Copyright (C) 1992-2001, 2003-2007, 2009-2013 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
76 static void
call_fclose(void * arg)77 call_fclose (void *arg)
78 {
79 if (arg != NULL)
80 fclose (arg);
81 }
82
83 char *
getpass(const char * prompt)84 getpass (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
188 char *
getpass(const char * prompt)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