Imported Upstream version 1.7.6p1
[debian/sudo] / tgetpass.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2010
3  *      Todd C. Miller <Todd.Miller@courtesan.com>
4  *
5  * Permission to use, copy, modify, and distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  * Sponsored in part by the Defense Advanced Research Projects
18  * Agency (DARPA) and Air Force Research Laboratory, Air Force
19  * Materiel Command, USAF, under agreement number F39502-99-1-0512.
20  */
21
22 #ifdef __TANDEM
23 # include <floss.h>
24 #endif
25
26 #include <config.h>
27
28 #include <sys/types.h>
29 #include <sys/param.h>
30 #include <stdio.h>
31 #ifdef STDC_HEADERS
32 # include <stdlib.h>
33 # include <stddef.h>
34 #else
35 # ifdef HAVE_STDLIB_H
36 #  include <stdlib.h>
37 # endif
38 #endif /* STDC_HEADERS */
39 #ifdef HAVE_STRING_H
40 # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
41 #  include <memory.h>
42 # endif
43 # include <string.h>
44 #endif /* HAVE_STRING_H */
45 #ifdef HAVE_STRINGS_H
46 # include <strings.h>
47 #endif /* HAVE_STRINGS_H */
48 #ifdef HAVE_UNISTD_H
49 # include <unistd.h>
50 #endif /* HAVE_UNISTD_H */
51 #include <pwd.h>
52 #include <errno.h>
53 #include <signal.h>
54 #include <fcntl.h>
55
56 #include "sudo.h"
57
58 static volatile sig_atomic_t signo[NSIG];
59
60 static RETSIGTYPE handler __P((int));
61 static char *getln __P((int, char *, size_t, int));
62 static char *sudo_askpass __P((const char *));
63
64 /*
65  * Like getpass(3) but with timeout and echo flags.
66  */
67 char *
68 tgetpass(prompt, timeout, flags)
69     const char *prompt;
70     int timeout;
71     int flags;
72 {
73     sigaction_t sa, savealrm, saveint, savehup, savequit, saveterm;
74     sigaction_t savetstp, savettin, savettou, savepipe;
75     char *pass;
76     static char buf[SUDO_PASS_MAX + 1];
77     int i, input, output, save_errno, neednl = 0, need_restart;
78
79     (void) fflush(stdout);
80
81     /* If using a helper program to get the password, run it instead. */
82     if (ISSET(flags, TGP_ASKPASS) && user_askpass)
83         return sudo_askpass(prompt);
84
85 restart:
86     for (i = 0; i < NSIG; i++)
87         signo[i] = 0;
88     pass = NULL;
89     save_errno = 0;
90     need_restart = 0;
91     /* Open /dev/tty for reading/writing if possible else use stdin/stderr. */
92     if (ISSET(flags, TGP_STDIN) ||
93         (input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) {
94         input = STDIN_FILENO;
95         output = STDERR_FILENO;
96     }
97
98     /*
99      * If we are using a tty but are not the foreground pgrp this will
100      * generate SIGTTOU, so do it *before* installing the signal handlers.
101      */
102     if (!ISSET(flags, TGP_ECHO)) {
103         if (def_pwfeedback)
104             neednl = term_cbreak(input);
105         else
106             neednl = term_noecho(input);
107     }
108
109     /*
110      * Catch signals that would otherwise cause the user to end
111      * up with echo turned off in the shell.
112      */
113     zero_bytes(&sa, sizeof(sa));
114     sigemptyset(&sa.sa_mask);
115     sa.sa_flags = SA_INTERRUPT; /* don't restart system calls */
116     sa.sa_handler = handler;
117     (void) sigaction(SIGALRM, &sa, &savealrm);
118     (void) sigaction(SIGINT, &sa, &saveint);
119     (void) sigaction(SIGHUP, &sa, &savehup);
120     (void) sigaction(SIGQUIT, &sa, &savequit);
121     (void) sigaction(SIGTERM, &sa, &saveterm);
122     (void) sigaction(SIGTSTP, &sa, &savetstp);
123     (void) sigaction(SIGTTIN, &sa, &savettin);
124     (void) sigaction(SIGTTOU, &sa, &savettou);
125
126     /* Ignore SIGPIPE in case stdin is a pipe and TGP_STDIN is set */
127     sa.sa_handler = SIG_IGN;
128     (void) sigaction(SIGPIPE, &sa, &savepipe);
129
130     if (prompt) {
131         if (write(output, prompt, strlen(prompt)) == -1)
132             goto restore;
133     }
134
135     if (timeout > 0)
136         alarm(timeout);
137     pass = getln(input, buf, sizeof(buf), def_pwfeedback);
138     alarm(0);
139     save_errno = errno;
140
141     if (neednl || pass == NULL) {
142         if (write(output, "\n", 1) == -1)
143             goto restore;
144     }
145
146 restore:
147     /* Restore old tty settings and signals. */
148     if (!ISSET(flags, TGP_ECHO))
149         term_restore(input, 1);
150     (void) sigaction(SIGALRM, &savealrm, NULL);
151     (void) sigaction(SIGINT, &saveint, NULL);
152     (void) sigaction(SIGHUP, &savehup, NULL);
153     (void) sigaction(SIGQUIT, &savequit, NULL);
154     (void) sigaction(SIGTERM, &saveterm, NULL);
155     (void) sigaction(SIGTSTP, &savetstp, NULL);
156     (void) sigaction(SIGTTIN, &savettin, NULL);
157     (void) sigaction(SIGTTOU, &savettou, NULL);
158     (void) sigaction(SIGTTOU, &savepipe, NULL);
159     if (input != STDIN_FILENO)
160         (void) close(input);
161
162     /*
163      * If we were interrupted by a signal, resend it to ourselves
164      * now that we have restored the signal handlers.
165      */
166     for (i = 0; i < NSIG; i++) {
167         if (signo[i]) {
168             kill(getpid(), i);
169             switch (i) {
170                 case SIGTSTP:
171                 case SIGTTIN:
172                 case SIGTTOU:
173                     need_restart = 1;
174                     break;
175             }
176         }
177     }
178     if (need_restart)
179         goto restart;
180
181     if (save_errno)
182         errno = save_errno;
183     return pass;
184 }
185
186 /*
187  * Fork a child and exec sudo-askpass to get the password from the user.
188  */
189 static char *
190 sudo_askpass(prompt)
191     const char *prompt;
192 {
193     static char buf[SUDO_PASS_MAX + 1], *pass;
194     sigaction_t sa, saved_sa_pipe;
195     int pfd[2];
196     pid_t pid;
197
198     if (pipe(pfd) == -1)
199         error(1, "unable to create pipe");
200
201     if ((pid = fork()) == -1)
202         error(1, "unable to fork");
203
204     if (pid == 0) {
205         /* child, point stdout to output side of the pipe and exec askpass */
206         if (dup2(pfd[1], STDOUT_FILENO) == -1) {
207             warning("dup2");
208             _exit(255);
209         }
210         (void) dup2(pfd[1], STDOUT_FILENO);
211         set_perms(PERM_FULL_USER);
212         closefrom(STDERR_FILENO + 1);
213         execl(user_askpass, user_askpass, prompt, (char *)NULL);
214         warning("unable to run %s", user_askpass);
215         _exit(255);
216     }
217
218     /* Ignore SIGPIPE in case child exits prematurely */
219     zero_bytes(&sa, sizeof(sa));
220     sigemptyset(&sa.sa_mask);
221     sa.sa_flags = SA_INTERRUPT;
222     sa.sa_handler = SIG_IGN;
223     (void) sigaction(SIGPIPE, &sa, &saved_sa_pipe);
224
225     /* Get response from child (askpass) and restore SIGPIPE handler */
226     (void) close(pfd[1]);
227     pass = getln(pfd[0], buf, sizeof(buf), 0);
228     (void) close(pfd[0]);
229     (void) sigaction(SIGPIPE, &saved_sa_pipe, NULL);
230
231     return pass;
232 }
233
234 extern int term_erase, term_kill;
235
236 static char *
237 getln(fd, buf, bufsiz, feedback)
238     int fd;
239     char *buf;
240     size_t bufsiz;
241     int feedback;
242 {
243     size_t left = bufsiz;
244     ssize_t nr = -1;
245     char *cp = buf;
246     char c = '\0';
247
248     if (left == 0) {
249         errno = EINVAL;
250         return NULL;                    /* sanity */
251     }
252
253     while (--left) {
254         nr = read(fd, &c, 1);
255         if (nr != 1 || c == '\n' || c == '\r')
256             break;
257         if (feedback) {
258             if (c == term_kill) {
259                 while (cp > buf) {
260                     if (write(fd, "\b \b", 3) == -1)
261                         break;
262                     --cp;
263                 }
264                 left = bufsiz;
265                 continue;
266             } else if (c == term_erase) {
267                 if (cp > buf) {
268                     if (write(fd, "\b \b", 3) == -1)
269                         break;
270                     --cp;
271                     left++;
272                 }
273                 continue;
274             }
275             if (write(fd, "*", 1) == -1)
276                 /* shut up glibc */;
277         }
278         *cp++ = c;
279     }
280     *cp = '\0';
281     if (feedback) {
282         /* erase stars */
283         while (cp > buf) {
284             if (write(fd, "\b \b", 3) == -1)
285                 break;
286             --cp;
287         }
288     }
289
290     return nr == 1 ? buf : NULL;
291 }
292
293 static RETSIGTYPE
294 handler(s)
295     int s;
296 {
297     if (s != SIGALRM)
298         signo[s] = 1;
299 }
300
301 int
302 tty_present()
303 {
304     int fd;
305
306     if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) != -1)
307         close(fd);
308     return fd != -1;
309 }