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