Imported Upstream version 1.6.6
[debian/sudo] / tgetpass.c
1 /*
2  * Copyright (c) 1996, 1998-2001 Todd C. Miller <Todd.Miller@courtesan.com>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * 3. The name of the author may not be used to endorse or promote products
17  *    derived from this software without specific prior written permission.
18  *
19  * 4. Products derived from this software may not be called "Sudo" nor
20  *    may "Sudo" appear in their names without specific prior written
21  *    permission from the author.
22  *
23  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
24  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
26  * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
29  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
30  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
31  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
32  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34
35 #include "config.h"
36
37 #include <sys/types.h>
38 #include <sys/param.h>
39 #ifdef HAVE_SYS_BSDTYPES_H
40 # include <sys/bsdtypes.h>
41 #endif /* HAVE_SYS_BSDTYPES_H */
42 #ifdef HAVE_SYS_SELECT_H
43 # include <sys/select.h>
44 #endif /* HAVE_SYS_SELECT_H */
45 #include <sys/time.h>
46 #include <stdio.h>
47 #ifdef STDC_HEADERS
48 # include <stdlib.h>
49 # include <stddef.h>
50 #else
51 # ifdef HAVE_STDLIB_H
52 #  include <stdlib.h>
53 # endif
54 #endif /* STDC_HEADERS */
55 #ifdef HAVE_STRING_H
56 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
57 #  include <memory.h>
58 # endif
59 # include <string.h>
60 #else
61 # ifdef HAVE_STRINGS_H
62 #  include <strings.h>
63 # endif
64 #endif /* HAVE_STRING_H */
65 #ifdef HAVE_UNISTD_H
66 # include <unistd.h>
67 #endif /* HAVE_UNISTD_H */
68 #include <pwd.h>
69 #include <errno.h>
70 #include <signal.h>
71 #include <fcntl.h>
72 #ifdef HAVE_TERMIOS_H
73 # include <termios.h>
74 #else
75 # ifdef HAVE_TERMIO_H
76 #  include <termio.h>
77 # else
78 #  include <sgtty.h>
79 #  include <sys/ioctl.h>
80 # endif /* HAVE_TERMIO_H */
81 #endif /* HAVE_TERMIOS_H */
82
83 #include "sudo.h"
84
85 #ifndef lint
86 static const char rcsid[] = "$Sudo: tgetpass.c,v 1.103 2001/12/17 23:56:47 millert Exp $";
87 #endif /* lint */
88
89 #ifndef TCSASOFT
90 # define TCSASOFT       0
91 #endif
92 #ifndef ECHONL
93 # define ECHONL 0
94 #endif
95
96 #ifndef _POSIX_VDISABLE
97 # ifdef VDISABLE
98 #  define _POSIX_VDISABLE       VDISABLE
99 # else
100 #  define _POSIX_VDISABLE       0
101 # endif
102 #endif
103
104 /*
105  * Abstract method of getting at the term flags.
106  */
107 #undef TERM
108 #undef tflags
109 #ifdef HAVE_TERMIOS_H
110 # define TERM                   termios
111 # define tflags                 c_lflag
112 # define term_getattr(f, t)     tcgetattr(f, t)
113 # define term_setattr(f, t)     tcsetattr(f, TCSAFLUSH|TCSASOFT, t)
114 #else
115 # ifdef HAVE_TERMIO_H
116 # define TERM                   termio
117 # define tflags                 c_lflag
118 # define term_getattr(f, t)     ioctl(f, TCGETA, t)
119 # define term_setattr(f, t)     ioctl(f, TCSETAF, t)
120 # else
121 #  define TERM                  sgttyb
122 #  define tflags                sg_flags
123 #  define term_getattr(f, t)    ioctl(f, TIOCGETP, t)
124 #  define term_setattr(f, t)    ioctl(f, TIOCSETP, t)
125 # endif /* HAVE_TERMIO_H */
126 #endif /* HAVE_TERMIOS_H */
127
128 static volatile sig_atomic_t signo;
129
130 static char *tgetline __P((int, char *, size_t, int));
131 static void handler __P((int));
132
133 /*
134  * Like getpass(3) but with timeout and echo flags.
135  */
136 char *
137 tgetpass(prompt, timeout, flags)
138     const char *prompt;
139     int timeout;
140     int flags;
141 {
142     sigaction_t sa, saveint, savehup, savequit, saveterm;
143     sigaction_t savetstp, savettin, savettou;
144     static char buf[SUDO_PASS_MAX + 1];
145     int input, output, save_errno;
146     struct TERM term, oterm;
147     char *pass;
148
149 restart:
150     /* Open /dev/tty for reading/writing if possible else use stdin/stderr. */
151     if ((flags & TGP_STDIN) ||
152         (input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) {
153         input = STDIN_FILENO;
154         output = STDERR_FILENO;
155     }
156
157     if (prompt)
158         (void) write(output, prompt, strlen(prompt));
159
160     /*
161      * Catch signals that would otherwise cause the user to end
162      * up with echo turned off in the shell.  Don't worry about
163      * things like SIGALRM and SIGPIPE for now.
164      */
165     sigemptyset(&sa.sa_mask);
166     sa.sa_flags = 0;            /* don't restart system calls */
167     sa.sa_handler = handler;
168     (void) sigaction(SIGINT, &sa, &saveint);
169     (void) sigaction(SIGHUP, &sa, &savehup);
170     (void) sigaction(SIGQUIT, &sa, &savequit);
171     (void) sigaction(SIGTERM, &sa, &saveterm);
172     (void) sigaction(SIGTSTP, &sa, &savetstp);
173     (void) sigaction(SIGTTIN, &sa, &savettin);
174     (void) sigaction(SIGTTOU, &sa, &savettou);
175
176     /* Turn echo off/on as specified by flags.  */
177     if (term_getattr(input, &oterm) == 0) {
178         (void) memcpy(&term, &oterm, sizeof(term));
179         if (!(flags & TGP_ECHO))
180             term.tflags &= ~(ECHO | ECHONL);
181 #ifdef VSTATUS
182         term.c_cc[VSTATUS] = _POSIX_VDISABLE;
183 #endif
184         (void) term_setattr(input, &term);
185     } else {
186         memset(&term, 0, sizeof(term));
187         memset(&oterm, 0, sizeof(oterm));
188     }
189
190     pass = tgetline(input, buf, sizeof(buf), timeout);
191     save_errno = errno;
192
193     if (!(term.tflags & ECHO))
194         (void) write(output, "\n", 1);
195
196     /* Restore old tty settings and signals. */
197     if (memcmp(&term, &oterm, sizeof(term)) != 0)
198         (void) term_setattr(input, &oterm);
199     (void) sigaction(SIGINT, &saveint, NULL);
200     (void) sigaction(SIGHUP, &savehup, NULL);
201     (void) sigaction(SIGQUIT, &savequit, NULL);
202     (void) sigaction(SIGTERM, &saveterm, NULL);
203     (void) sigaction(SIGTSTP, &savetstp, NULL);
204     (void) sigaction(SIGTTIN, &savettin, NULL);
205     (void) sigaction(SIGTTOU, &savettou, NULL);
206     if (input != STDIN_FILENO)
207         (void) close(input);
208
209     /*
210      * If we were interrupted by a signal, resend it to ourselves
211      * now that we have restored the signal handlers.
212      */
213     if (signo) {
214         kill(getpid(), signo); 
215         switch (signo) {
216             case SIGTSTP:
217             case SIGTTIN:
218             case SIGTTOU:
219                 signo = 0;
220                 goto restart;
221         }
222     }
223
224     errno = save_errno;
225     return(pass);
226 }
227
228 /*
229  * Get a line of input (optionally timing out) and place it in buf.
230  */
231 static char *
232 tgetline(fd, buf, bufsiz, timeout)
233     int fd;
234     char *buf;
235     size_t bufsiz;
236     int timeout;
237 {
238     fd_set *readfds = NULL;
239     struct timeval tv;
240     size_t left;
241     char *cp;
242     char c;
243     int n;
244
245     if (bufsiz == 0) {
246         errno = EINVAL;
247         return(NULL);                   /* sanity */
248     }
249
250     cp = buf;
251     left = bufsiz;
252
253     /*
254      * Timeout of <= 0 means no timeout.
255      */
256     if (timeout > 0) {
257         /* Setup for select(2) */
258         n = howmany(fd + 1, NFDBITS) * sizeof(fd_mask);
259         readfds = (fd_set *) emalloc(n);
260         (void) memset((VOID *)readfds, 0, n);
261
262         /* Set timeout for select */
263         tv.tv_sec = timeout;
264         tv.tv_usec = 0;
265
266         while (--left) {
267             FD_SET(fd, readfds);
268
269             /* Make sure there is something to read (or timeout) */
270             while ((n = select(fd + 1, readfds, 0, 0, &tv)) == -1 &&
271                 errno == EAGAIN)
272                 ;
273             if (n <= 0) {
274                 free(readfds);
275                 return(NULL);           /* timeout or interrupt */
276             }
277
278             /* Read a character, exit loop on error, EOF or EOL */
279             n = read(fd, &c, 1);
280             if (n != 1 || c == '\n' || c == '\r')
281                 break;
282             *cp++ = c;
283         }
284         free(readfds);
285     } else {
286         /* Keep reading until out of space, EOF, error, or newline */
287         n = -1;
288         while (--left && (n = read(fd, &c, 1)) == 1 && c != '\n' && c != '\r')
289             *cp++ = c;
290     }
291     *cp = '\0';
292
293     return(n == -1 ? NULL : buf);
294 }
295
296 static void handler(s)
297     int s;
298 {
299     signo = s;
300 }