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