]> granicus.if.org Git - shadow/blob - lib/getpass.c
[svn-upgrade] Integrating new upstream version, shadow (4.0.16)
[shadow] / lib / getpass.c
1 /*
2  * Copyright 1990 - 1995, Julianne Frances Haugh
3  * Copyright 1998, Pavel Machek <pavel@ucw.cz>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. Neither the name of Julianne F. Haugh nor the names of its contributors
15  *    may be used to endorse or promote products derived from this software
16  *    without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND
19  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
24  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
26  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
27  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28  * SUCH DAMAGE.
29  */
30
31 #include <config.h>
32
33 #ifdef  SKEY
34
35 #ident "$Id: getpass.c,v 1.15 2006/05/12 22:54:22 kloczek Exp $"
36
37 #include "defines.h"
38 #include <signal.h>
39 #include <stdio.h>
40 #include "getdef.h"
41 /* new code, #undef if there are any problems...  */
42 #define USE_SETJMP 1
43 #ifdef USE_SETJMP
44 #include <setjmp.h>
45 static sigjmp_buf intr;         /* where to jump on SIGINT */
46 #endif
47
48 static int sig_caught;
49
50 #ifdef HAVE_SIGACTION
51 static struct sigaction sigact;
52 #endif
53
54  /*ARGSUSED*/ static RETSIGTYPE sig_catch (int sig)
55 {
56         sig_caught = 1;
57 #ifdef USE_SETJMP
58         siglongjmp (intr, 1);
59 #endif
60 }
61
62 #define MAXLEN 127
63
64
65 static char *readpass (FILE * ifp, FILE * ofp, int with_echo, int max_asterisks)
66 {
67         static char input[MAXLEN + 1], asterix[MAXLEN + 1];
68         static char once;
69         char *cp, *ap, c;
70         int i;
71
72         if (max_asterisks < 0) {
73                 /* traditional code using fgets() */
74                 if (fgets (input, sizeof input, ifp) != input)
75                         return NULL;
76                 cp = strrchr (input, '\n');
77                 if (cp)
78                         *cp = '\0';
79                 else
80                         input[sizeof input - 1] = '\0';
81                 return input;
82         }
83         if (!once) {
84                 srandom (time (0) * getpid ());
85                 once = 1;
86         }
87         cp = input;
88         ap = asterix;
89         while (read (fileno (ifp), &c, 1)) {
90                 switch (c) {
91                 case '\n':
92                 case '\r':
93                         goto endwhile;
94                 case '\b':
95                 case 127:
96                         if (cp > input) {
97                                 cp--;
98                                 ap--;
99                                 for (i = *ap; i > 0; i--)
100                                         fputs ("\b \b", ofp);
101                                 *cp = '\0';
102                                 *ap = 0;
103                         } else {
104                                 putc ('\a', ofp);       /* BEL */
105                         }
106                         break;
107                 case '\025':    /* Ctrl-U = erase everything typed so far */
108                         if (cp == input) {
109                                 putc ('\a', ofp);       /* BEL */
110                         } else
111                                 while (cp > input) {
112                                         cp--;
113                                         ap--;
114                                         for (i = *ap; i > 0; i--)
115                                                 fputs ("\b \b", ofp);
116                                         *cp = '\0';
117                                         *ap = 0;
118                                 }
119                         break;
120                 default:
121                         *cp++ = c;
122                         if (with_echo) {
123                                 *ap = 1;
124                                 putc (c, ofp);
125                         } else if (max_asterisks > 0) {
126                                 *ap = (random () % max_asterisks) + 1;
127                                 for (i = *ap; i > 0; i--)
128                                         putc ('*', ofp);
129                         } else {
130                                 *ap = 0;
131                         }
132                         ap++;
133                         break;
134                 }
135                 fflush (ofp);
136                 if (cp >= input + MAXLEN) {
137                         putc ('\a', ofp);       /* BEL */
138                         break;
139                 }
140         }
141       endwhile:
142         *cp = '\0';
143         putc ('\n', ofp);
144         return input;
145 }
146
147 static char *prompt_password (const char *prompt, int with_echo)
148 {
149         static char nostring[1] = "";
150         static char *return_value;
151         volatile int tty_opened;
152         static FILE *ifp, *ofp;
153         volatile int is_tty;
154
155 #ifdef HAVE_SIGACTION
156         struct sigaction old_sigact;
157 #else
158         RETSIGTYPE (*old_signal) ();
159 #endif
160         TERMIO old_modes;
161         int max_asterisks = getdef_num ("GETPASS_ASTERISKS", -1);
162
163         /*
164          * set a flag so the SIGINT signal can be re-sent if it
165          * is caught
166          */
167
168         sig_caught = 0;
169         return_value = NULL;
170         tty_opened = 0;
171
172         /*
173          * if /dev/tty can't be opened, getpass() needs to read
174          * from stdin and write to stderr instead.
175          */
176
177         if (!(ifp = fopen ("/dev/tty", "r+"))) {
178                 ifp = stdin;
179                 ofp = stderr;
180         } else {
181                 ofp = ifp;
182                 tty_opened = 1;
183         }
184         setbuf (ifp, (char *) 0);
185
186         /*
187          * the current tty modes must be saved so they can be
188          * restored later on.  echo will be turned off, except
189          * for the newline character
190          */
191
192         is_tty = 1;
193         if (GTTY (fileno (ifp), &old_modes)) {
194                 is_tty = 0;
195         }
196 #ifdef USE_SETJMP
197         /*
198          * If we get a SIGINT, sig_catch() will jump here -
199          * no need to press Enter after Ctrl-C.
200          */
201         if (sigsetjmp (intr, 1))
202                 goto out;
203 #endif
204
205 #ifdef HAVE_SIGACTION
206         sigact.sa_handler = sig_catch;
207         sigemptyset (&sigact.sa_mask);
208         sigact.sa_flags = 0;
209         sigaction (SIGINT, &sigact, &old_sigact);
210 #else
211         old_signal = signal (SIGINT, sig_catch);
212 #endif
213
214         if (is_tty) {
215                 TERMIO new_modes = old_modes;
216
217                 if (max_asterisks < 0)
218                         new_modes.c_lflag |= ICANON;
219                 else
220                         new_modes.c_lflag &= ~(ICANON);
221
222                 if (with_echo)
223                         new_modes.c_lflag |= (ECHO | ECHOE | ECHOK);
224                 else
225                         new_modes.c_lflag &= ~(ECHO | ECHOE | ECHOK);
226
227                 new_modes.c_lflag |= ECHONL;
228
229                 if (STTY (fileno (ifp), &new_modes))
230                         goto out;
231         }
232
233         /*
234          * the prompt is output, and the response read without
235          * echoing.  the trailing newline must be removed.  if
236          * the fgets() returns an error, a NULL pointer is
237          * returned.
238          */
239
240         if ((fputs (prompt, ofp) != EOF) && (fflush (ofp) != EOF))
241                 return_value = readpass (ifp, ofp, with_echo, max_asterisks);
242       out:
243         /*
244          * the old SIGINT handler is restored after the tty
245          * modes.  then /dev/tty is closed if it was opened in
246          * the beginning.  finally, if a signal was caught it
247          * is sent to this process for normal processing.
248          */
249
250         if (is_tty) {
251                 if (STTY (fileno (ifp), &old_modes))
252                         return_value = NULL;
253         }
254 #ifdef HAVE_SIGACTION
255         (void) sigaction (SIGINT, &old_sigact, NULL);
256 #else
257         (void) signal (SIGINT, old_signal);
258 #endif
259         if (tty_opened)
260                 (void) fclose (ifp);
261
262         if (sig_caught) {
263                 kill (getpid (), SIGINT);
264                 return_value = NULL;
265         }
266         if (!return_value) {
267                 nostring[0] = '\0';
268                 return_value = nostring;
269         }
270         return return_value;
271 }
272
273 char *libshadow_getpass (const char *prompt)
274 {
275         return prompt_password (prompt, 0);
276 }
277
278 char *getpass_with_echo (const char *prompt)
279 {
280         return prompt_password (prompt, 1);
281 }
282 #endif                          /* SKEY */