add doc about interaction with RAMRUN to README.Debian in response to #581393
[debian/sudo] / tgetpass.c
1 /*
2  * Copyright (c) 1996, 1998-2005, 2007-2009
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 #else
45 # ifdef HAVE_STRINGS_H
46 #  include <strings.h>
47 # endif
48 #endif /* HAVE_STRING_H */
49 #ifdef HAVE_UNISTD_H
50 # include <unistd.h>
51 #endif /* HAVE_UNISTD_H */
52 #include <pwd.h>
53 #include <errno.h>
54 #include <signal.h>
55 #include <fcntl.h>
56
57 #include "sudo.h"
58
59 static volatile sig_atomic_t signo;
60
61 static void handler __P((int));
62 static char *getln __P((int, char *, size_t, int));
63 static char *sudo_askpass __P((const char *));
64
65 extern int term_restore __P((int));
66 extern int term_noecho __P((int));
67 extern int term_raw __P((int));
68
69 /*
70  * Like getpass(3) but with timeout and echo flags.
71  */
72 char *
73 tgetpass(prompt, timeout, flags)
74     const char *prompt;
75     int timeout;
76     int flags;
77 {
78     sigaction_t sa, savealrm, saveint, savehup, savequit, saveterm;
79     sigaction_t savetstp, savettin, savettou;
80     char *pass;
81     static char buf[SUDO_PASS_MAX + 1];
82     int input, output, save_errno, neednl;;
83
84     (void) fflush(stdout);
85
86     /* If using a helper program to get the password, run it instead. */
87     if (ISSET(flags, TGP_ASKPASS) && user_askpass)
88         return(sudo_askpass(prompt));
89
90 restart:
91     signo = 0;
92     pass = NULL;
93     save_errno = 0;
94     /* Open /dev/tty for reading/writing if possible else use stdin/stderr. */
95     if (ISSET(flags, TGP_STDIN) ||
96         (input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) {
97         input = STDIN_FILENO;
98         output = STDERR_FILENO;
99     }
100
101     /*
102      * Catch signals that would otherwise cause the user to end
103      * up with echo turned off in the shell.
104      */
105     zero_bytes(&sa, sizeof(sa));
106     sigemptyset(&sa.sa_mask);
107     sa.sa_flags = SA_INTERRUPT; /* don't restart system calls */
108     sa.sa_handler = handler;
109     (void) sigaction(SIGALRM, &sa, &savealrm);
110     (void) sigaction(SIGINT, &sa, &saveint);
111     (void) sigaction(SIGHUP, &sa, &savehup);
112     (void) sigaction(SIGQUIT, &sa, &savequit);
113     (void) sigaction(SIGTERM, &sa, &saveterm);
114     (void) sigaction(SIGTSTP, &sa, &savetstp);
115     (void) sigaction(SIGTTIN, &sa, &savettin);
116     (void) sigaction(SIGTTOU, &sa, &savettou);
117
118     if (def_pwfeedback)
119         neednl = term_raw(input);
120     else
121         neednl = term_noecho(input);
122
123     /* No output if we are already backgrounded. */
124     if (signo != SIGTTOU && signo != SIGTTIN) {
125         if (prompt)
126             (void) write(output, prompt, strlen(prompt));
127
128         if (timeout > 0)
129             alarm(timeout);
130         pass = getln(input, buf, sizeof(buf), def_pwfeedback);
131         alarm(0);
132         save_errno = errno;
133
134         if (neednl)
135             (void) write(output, "\n", 1);
136     }
137
138     /* Restore old tty settings and signals. */
139     term_restore(input);
140     (void) sigaction(SIGALRM, &savealrm, NULL);
141     (void) sigaction(SIGINT, &saveint, NULL);
142     (void) sigaction(SIGHUP, &savehup, NULL);
143     (void) sigaction(SIGQUIT, &savequit, NULL);
144     (void) sigaction(SIGTERM, &saveterm, NULL);
145     (void) sigaction(SIGTSTP, &savetstp, NULL);
146     (void) sigaction(SIGTTIN, &savettin, NULL);
147     (void) sigaction(SIGTTOU, &savettou, NULL);
148     if (input != STDIN_FILENO)
149         (void) close(input);
150
151     /*
152      * If we were interrupted by a signal, resend it to ourselves
153      * now that we have restored the signal handlers.
154      */
155     if (signo) {
156         kill(getpid(), signo);
157         switch (signo) {
158             case SIGTSTP:
159             case SIGTTIN:
160             case SIGTTOU:
161                 goto restart;
162         }
163     }
164
165     if (save_errno)
166         errno = save_errno;
167     return(pass);
168 }
169
170 /*
171  * Fork a child and exec sudo-askpass to get the password from the user.
172  */
173 static char *
174 sudo_askpass(prompt)
175     const char *prompt;
176 {
177     static char buf[SUDO_PASS_MAX + 1], *pass;
178     sigaction_t sa, saved_sa_pipe;
179     int pfd[2];
180     pid_t pid;
181
182     if (pipe(pfd) == -1)
183         error(1, "unable to create pipe");
184
185     if ((pid = fork()) == -1)
186         error(1, "unable to fork");
187
188     if (pid == 0) {
189         /* child, point stdout to output side of the pipe and exec askpass */
190         (void) dup2(pfd[1], STDOUT_FILENO);
191         set_perms(PERM_FULL_USER);
192         closefrom(STDERR_FILENO + 1);
193         execl(user_askpass, user_askpass, prompt, (char *)NULL);
194         warning("unable to run %s", user_askpass);
195         _exit(255);
196     }
197
198     /* Ignore SIGPIPE in case child exits prematurely */
199     zero_bytes(&sa, sizeof(sa));
200     sigemptyset(&sa.sa_mask);
201     sa.sa_flags = 0;
202     sa.sa_handler = SIG_IGN;
203     (void) sigaction(SIGPIPE, &sa, &saved_sa_pipe);
204
205     /* Get response from child (askpass) and restore SIGPIPE handler */
206     (void) close(pfd[1]);
207     pass = getln(pfd[0], buf, sizeof(buf), 0);
208     (void) close(pfd[0]);
209     (void) sigaction(SIGPIPE, &saved_sa_pipe, NULL);
210
211     return(pass);
212 }
213
214 extern int term_erase, term_kill;
215
216 static char *
217 getln(fd, buf, bufsiz, feedback)
218     int fd;
219     char *buf;
220     size_t bufsiz;
221     int feedback;
222 {
223     size_t left = bufsiz;
224     ssize_t nr = -1;
225     char *cp = buf;
226     char c = '\0';
227
228     if (left == 0) {
229         errno = EINVAL;
230         return(NULL);                   /* sanity */
231     }
232
233     while (--left) {
234         nr = read(fd, &c, 1);
235         if (nr != 1 || c == '\n' || c == '\r')
236             break;
237         if (feedback) {
238             if (c == term_kill) {
239                 while (cp > buf) {
240                     (void) write(fd, "\b \b", 3);
241                     --cp;
242                 }
243                 left = bufsiz;
244                 continue;
245             } else if (c == term_erase) {
246                 if (cp > buf) {
247                     (void) write(fd, "\b \b", 3);
248                     --cp;
249                     left++;
250                 }
251                 continue;
252             }
253             (void) write(fd, "*", 1);
254         }
255         *cp++ = c;
256     }
257     *cp = '\0';
258     if (feedback) {
259         /* erase stars */
260         while (cp > buf) {
261             (void) write(fd, "\b \b", 3);
262             --cp;
263         }
264     }
265
266     return(nr == 1 ? buf : NULL);
267 }
268
269 static void
270 handler(s)
271     int s;
272 {
273     if (s != SIGALRM)
274         signo = s;
275 }
276
277 int
278 tty_present()
279 {
280     int fd;
281
282     if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) != -1)
283         close(fd);
284     return(fd != -1);
285 }