1 /* Copyright (C) 1992-2001, 2003-2007, 2009-2021 Free Software Foundation, Inc.
2
3 This file is part of the GNU C Library.
4
5 This file is free software: you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as
7 published by the Free Software Foundation; either version 2.1 of the
8 License, or (at your option) any later version.
9
10 This file 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 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>. */
17
18 #ifndef _LIBC
19 /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc
20 warns for the null checks on 'prompt' below. */
21 # define _GL_ARG_NONNULL(params)
22 # include <config.h>
23 #endif
24
25 #include "getpass.h"
26
27 #include <stdio.h>
28
29 #if !(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
79 static void
call_fclose(void * arg)80 call_fclose (void *arg)
81 {
82 if (arg != NULL)
83 fclose (arg);
84 }
85
86 char *
getpass(const char * prompt)87 getpass (const char *prompt)
88 {
89 FILE *tty;
90 FILE *in, *out;
91 # if HAVE_TCGETATTR
92 struct termios s, t;
93 # endif
94 bool tty_changed = false;
95 static char *buf;
96 static size_t bufsize;
97 ssize_t nread;
98
99 /* Try to write to and read from the terminal if we can.
100 If we can't open the terminal, use stderr and stdin. */
101
102 tty = fopen ("/dev/tty", "w+e");
103 if (tty == NULL)
104 {
105 in = stdin;
106 out = stderr;
107 }
108 else
109 {
110 /* We do the locking ourselves. */
111 __fsetlocking (tty, FSETLOCKING_BYCALLER);
112
113 out = in = tty;
114 }
115
116 flockfile (out);
117
118 /* Turn echoing off if it is on now. */
119 # if HAVE_TCGETATTR
120 if (tcgetattr (fileno (in), &t) == 0)
121 {
122 /* Save the old one. */
123 s = t;
124 /* Tricky, tricky. */
125 t.c_lflag &= ~(ECHO | ISIG);
126 tty_changed = (tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &t) == 0);
127 }
128 # endif
129
130 if (prompt)
131 {
132 /* Write the prompt. */
133 fputs_unlocked (prompt, out);
134 fflush_unlocked (out);
135 }
136
137 /* Read the password. */
138 nread = getline (&buf, &bufsize, in);
139
140 /* According to the C standard, input may not be followed by output
141 on the same stream without an intervening call to a file
142 positioning function. Suppose in == out; then without this fseek
143 call, on Solaris, HP-UX, AIX, OSF/1, the previous input gets
144 echoed, whereas on IRIX, the following newline is not output as
145 it should be. POSIX imposes similar restrictions if fileno (in)
146 == fileno (out). The POSIX restrictions are tricky and change
147 from POSIX version to POSIX version, so play it safe and invoke
148 fseek even if in != out. */
149 fseeko (out, 0, SEEK_CUR);
150
151 if (buf != NULL)
152 {
153 if (nread < 0)
154 buf[0] = '\0';
155 else if (buf[nread - 1] == '\n')
156 {
157 /* Remove the newline. */
158 buf[nread - 1] = '\0';
159 if (tty_changed)
160 {
161 /* Write the newline that was not echoed. */
162 putc_unlocked ('\n', out);
163 }
164 }
165 }
166
167 /* Restore the original setting. */
168 # if HAVE_TCSETATTR
169 if (tty_changed)
170 tcsetattr (fileno (in), TCSAFLUSH | TCSASOFT, &s);
171 # endif
172
173 funlockfile (out);
174
175 call_fclose (tty);
176
177 return buf;
178 }
179
180 #else /* W32 native */
181
182 /* Windows implementation by Martin Lambers <marlam@marlam.de>,
183 improved by Simon Josefsson. */
184
185 /* For PASS_MAX. */
186 # include <limits.h>
187 /* For _getch(). */
188 # include <conio.h>
189 /* For strdup(). */
190 # include <string.h>
191
192 # ifndef PASS_MAX
193 # define PASS_MAX 512
194 # endif
195
196 char *
getpass(const char * prompt)197 getpass (const char *prompt)
198 {
199 char getpassbuf[PASS_MAX + 1];
200 size_t i = 0;
201 int c;
202
203 if (prompt)
204 {
205 fputs (prompt, stderr);
206 fflush (stderr);
207 }
208
209 for (;;)
210 {
211 c = _getch ();
212 if (c == '\r')
213 {
214 getpassbuf[i] = '\0';
215 break;
216 }
217 else if (i < PASS_MAX)
218 {
219 getpassbuf[i++] = c;
220 }
221
222 if (i >= PASS_MAX)
223 {
224 getpassbuf[i] = '\0';
225 break;
226 }
227 }
228
229 if (prompt)
230 {
231 fputs ("\r\n", stderr);
232 fflush (stderr);
233 }
234
235 return strdup (getpassbuf);
236 }
237 #endif
238