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
58static volatile sig_atomic_t signo[NSIG];
59
60static RETSIGTYPE handler __P((int));
61static char *getln __P((int, char *, size_t, int));
62static char *sudo_askpass __P((const char *));
63
64/*
65 * Like getpass(3) but with timeout and echo flags.
66 */
67char *
68tgetpass(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
85restart:
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
146restore:
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 */
189static char *
190sudo_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    if (pass == NULL)
232	errno = EINTR;	/* make cancel button simulate ^C */
233
234    return pass;
235}
236
237extern int term_erase, term_kill;
238
239static char *
240getln(fd, buf, bufsiz, feedback)
241    int fd;
242    char *buf;
243    size_t bufsiz;
244    int feedback;
245{
246    size_t left = bufsiz;
247    ssize_t nr = -1;
248    char *cp = buf;
249    char c = '\0';
250
251    if (left == 0) {
252	errno = EINVAL;
253	return NULL;			/* sanity */
254    }
255
256    while (--left) {
257	nr = read(fd, &c, 1);
258	if (nr != 1 || c == '\n' || c == '\r')
259	    break;
260	if (feedback) {
261	    if (c == term_kill) {
262		while (cp > buf) {
263		    if (write(fd, "\b \b", 3) == -1)
264			break;
265		    --cp;
266		}
267		left = bufsiz;
268		continue;
269	    } else if (c == term_erase) {
270		if (cp > buf) {
271		    if (write(fd, "\b \b", 3) == -1)
272			break;
273		    --cp;
274		    left++;
275		}
276		continue;
277	    }
278	    ignore_result(write(fd, "*", 1));
279	}
280	*cp++ = c;
281    }
282    *cp = '\0';
283    if (feedback) {
284	/* erase stars */
285	while (cp > buf) {
286	    if (write(fd, "\b \b", 3) == -1)
287		break;
288	    --cp;
289	}
290    }
291
292    return nr == 1 ? buf : NULL;
293}
294
295static RETSIGTYPE
296handler(s)
297    int s;
298{
299    if (s != SIGALRM)
300	signo[s] = 1;
301}
302
303int
304tty_present()
305{
306    int fd;
307
308    if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) != -1)
309	close(fd);
310    return fd != -1;
311}
312