Annotation of embedaddon/sudo/src/tgetpass.c, revision 1.1.1.4
1.1 misho 1: /*
1.1.1.4 ! misho 2: * Copyright (c) 1996, 1998-2005, 2007-2013
1.1 misho 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 <stdio.h>
30: #ifdef STDC_HEADERS
31: # include <stdlib.h>
32: # include <stddef.h>
33: #else
34: # ifdef HAVE_STDLIB_H
35: # include <stdlib.h>
36: # endif
37: #endif /* STDC_HEADERS */
38: #ifdef HAVE_STRING_H
39: # if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS)
40: # include <memory.h>
41: # endif
42: # include <string.h>
43: #endif /* HAVE_STRING_H */
44: #ifdef HAVE_STRINGS_H
45: # include <strings.h>
46: #endif /* HAVE_STRINGS_H */
47: #ifdef HAVE_UNISTD_H
48: # include <unistd.h>
49: #endif /* HAVE_UNISTD_H */
50: #include <pwd.h>
51: #include <errno.h>
52: #include <signal.h>
53: #include <fcntl.h>
54:
55: #include "sudo.h"
56:
57: static volatile sig_atomic_t signo[NSIG];
58:
1.1.1.4 ! misho 59: static void tgetpass_handler(int);
1.1 misho 60: static char *getln(int, char *, size_t, int);
61: static char *sudo_askpass(const char *, const char *);
62:
63: /*
64: * Like getpass(3) but with timeout and echo flags.
65: */
66: char *
67: tgetpass(const char *prompt, int timeout, int flags)
68: {
69: sigaction_t sa, savealrm, saveint, savehup, savequit, saveterm;
70: sigaction_t savetstp, savettin, savettou, savepipe;
71: char *pass;
72: static const char *askpass;
73: static char buf[SUDO_PASS_MAX + 1];
74: int i, input, output, save_errno, neednl = 0, need_restart;
1.1.1.2 misho 75: debug_decl(tgetpass, SUDO_DEBUG_CONV)
1.1 misho 76:
77: (void) fflush(stdout);
78:
79: if (askpass == NULL) {
1.1.1.3 misho 80: askpass = getenv_unhooked("SUDO_ASKPASS");
1.1 misho 81: if (askpass == NULL || *askpass == '\0')
1.1.1.2 misho 82: askpass = sudo_conf_askpass_path();
1.1 misho 83: }
84:
85: /* If no tty present and we need to disable echo, try askpass. */
86: if (!ISSET(flags, TGP_STDIN|TGP_ECHO|TGP_ASKPASS|TGP_NOECHO_TRY) &&
87: !tty_present()) {
1.1.1.3 misho 88: if (askpass == NULL || getenv_unhooked("DISPLAY") == NULL) {
1.1 misho 89: warningx(_("no tty present and no askpass program specified"));
1.1.1.2 misho 90: debug_return_str(NULL);
1.1 misho 91: }
92: SET(flags, TGP_ASKPASS);
93: }
94:
95: /* If using a helper program to get the password, run it instead. */
96: if (ISSET(flags, TGP_ASKPASS)) {
97: if (askpass == NULL || *askpass == '\0')
1.1.1.4 ! misho 98: fatalx(_("no askpass program specified, try setting SUDO_ASKPASS"));
1.1.1.2 misho 99: debug_return_str_masked(sudo_askpass(askpass, prompt));
1.1 misho 100: }
101:
102: restart:
103: for (i = 0; i < NSIG; i++)
104: signo[i] = 0;
105: pass = NULL;
106: save_errno = 0;
107: need_restart = 0;
108: /* Open /dev/tty for reading/writing if possible else use stdin/stderr. */
109: if (ISSET(flags, TGP_STDIN) ||
110: (input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) {
111: input = STDIN_FILENO;
112: output = STDERR_FILENO;
113: }
114:
115: /*
116: * If we are using a tty but are not the foreground pgrp this will
117: * generate SIGTTOU, so do it *before* installing the signal handlers.
118: */
119: if (!ISSET(flags, TGP_ECHO)) {
120: if (ISSET(flags, TGP_MASK))
121: neednl = term_cbreak(input);
122: else
123: neednl = term_noecho(input);
124: }
125:
126: /*
127: * Catch signals that would otherwise cause the user to end
128: * up with echo turned off in the shell.
129: */
130: zero_bytes(&sa, sizeof(sa));
131: sigemptyset(&sa.sa_mask);
132: sa.sa_flags = SA_INTERRUPT; /* don't restart system calls */
1.1.1.4 ! misho 133: sa.sa_handler = tgetpass_handler;
1.1 misho 134: (void) sigaction(SIGALRM, &sa, &savealrm);
135: (void) sigaction(SIGINT, &sa, &saveint);
136: (void) sigaction(SIGHUP, &sa, &savehup);
137: (void) sigaction(SIGQUIT, &sa, &savequit);
138: (void) sigaction(SIGTERM, &sa, &saveterm);
139: (void) sigaction(SIGTSTP, &sa, &savetstp);
140: (void) sigaction(SIGTTIN, &sa, &savettin);
141: (void) sigaction(SIGTTOU, &sa, &savettou);
142:
143: /* Ignore SIGPIPE in case stdin is a pipe and TGP_STDIN is set */
144: sa.sa_handler = SIG_IGN;
145: (void) sigaction(SIGPIPE, &sa, &savepipe);
146:
147: if (prompt) {
148: if (write(output, prompt, strlen(prompt)) == -1)
149: goto restore;
150: }
151:
152: if (timeout > 0)
153: alarm(timeout);
154: pass = getln(input, buf, sizeof(buf), ISSET(flags, TGP_MASK));
155: alarm(0);
156: save_errno = errno;
157:
158: if (neednl || pass == NULL) {
159: if (write(output, "\n", 1) == -1)
160: goto restore;
161: }
162:
163: restore:
164: /* Restore old tty settings and signals. */
165: if (!ISSET(flags, TGP_ECHO))
166: term_restore(input, 1);
167: (void) sigaction(SIGALRM, &savealrm, NULL);
168: (void) sigaction(SIGINT, &saveint, NULL);
169: (void) sigaction(SIGHUP, &savehup, NULL);
170: (void) sigaction(SIGQUIT, &savequit, NULL);
171: (void) sigaction(SIGTERM, &saveterm, NULL);
172: (void) sigaction(SIGTSTP, &savetstp, NULL);
173: (void) sigaction(SIGTTIN, &savettin, NULL);
174: (void) sigaction(SIGTTOU, &savettou, NULL);
175: (void) sigaction(SIGTTOU, &savepipe, NULL);
176: if (input != STDIN_FILENO)
177: (void) close(input);
178:
179: /*
180: * If we were interrupted by a signal, resend it to ourselves
181: * now that we have restored the signal handlers.
182: */
183: for (i = 0; i < NSIG; i++) {
184: if (signo[i]) {
185: kill(getpid(), i);
186: switch (i) {
187: case SIGTSTP:
188: case SIGTTIN:
189: case SIGTTOU:
190: need_restart = 1;
191: break;
192: }
193: }
194: }
195: if (need_restart)
196: goto restart;
197:
198: if (save_errno)
199: errno = save_errno;
1.1.1.2 misho 200:
201: debug_return_str_masked(pass);
1.1 misho 202: }
203:
204: /*
205: * Fork a child and exec sudo-askpass to get the password from the user.
206: */
207: static char *
208: sudo_askpass(const char *askpass, const char *prompt)
209: {
210: static char buf[SUDO_PASS_MAX + 1], *pass;
211: sigaction_t sa, saved_sa_pipe;
212: int pfd[2];
213: pid_t pid;
1.1.1.2 misho 214: debug_decl(sudo_askpass, SUDO_DEBUG_CONV)
1.1 misho 215:
216: if (pipe(pfd) == -1)
1.1.1.4 ! misho 217: fatal(_("unable to create pipe"));
1.1 misho 218:
219: if ((pid = fork()) == -1)
1.1.1.4 ! misho 220: fatal(_("unable to fork"));
1.1 misho 221:
222: if (pid == 0) {
223: /* child, point stdout to output side of the pipe and exec askpass */
224: if (dup2(pfd[1], STDOUT_FILENO) == -1) {
225: warning("dup2");
226: _exit(255);
227: }
1.1.1.4 ! misho 228: if (setuid(ROOT_UID) == -1)
! 229: warning("setuid(%d)", ROOT_UID);
1.1 misho 230: if (setgid(user_details.gid)) {
231: warning(_("unable to set gid to %u"), (unsigned int)user_details.gid);
232: _exit(255);
233: }
234: if (setuid(user_details.uid)) {
235: warning(_("unable to set uid to %u"), (unsigned int)user_details.uid);
236: _exit(255);
237: }
238: closefrom(STDERR_FILENO + 1);
239: execl(askpass, askpass, prompt, (char *)NULL);
240: warning(_("unable to run %s"), askpass);
241: _exit(255);
242: }
243:
244: /* Ignore SIGPIPE in case child exits prematurely */
245: zero_bytes(&sa, sizeof(sa));
246: sigemptyset(&sa.sa_mask);
247: sa.sa_flags = SA_INTERRUPT;
248: sa.sa_handler = SIG_IGN;
249: (void) sigaction(SIGPIPE, &sa, &saved_sa_pipe);
250:
251: /* Get response from child (askpass) and restore SIGPIPE handler */
252: (void) close(pfd[1]);
253: pass = getln(pfd[0], buf, sizeof(buf), 0);
254: (void) close(pfd[0]);
255: (void) sigaction(SIGPIPE, &saved_sa_pipe, NULL);
256:
1.1.1.2 misho 257: if (pass == NULL)
258: errno = EINTR; /* make cancel button simulate ^C */
259:
260: debug_return_str_masked(pass);
1.1 misho 261: }
262:
263: extern int term_erase, term_kill;
264:
265: static char *
266: getln(int fd, char *buf, size_t bufsiz, int feedback)
267: {
268: size_t left = bufsiz;
269: ssize_t nr = -1;
270: char *cp = buf;
271: char c = '\0';
1.1.1.2 misho 272: debug_decl(getln, SUDO_DEBUG_CONV)
1.1 misho 273:
274: if (left == 0) {
275: errno = EINVAL;
1.1.1.2 misho 276: debug_return_str(NULL); /* sanity */
1.1 misho 277: }
278:
279: while (--left) {
280: nr = read(fd, &c, 1);
281: if (nr != 1 || c == '\n' || c == '\r')
282: break;
283: if (feedback) {
284: if (c == term_kill) {
285: while (cp > buf) {
286: if (write(fd, "\b \b", 3) == -1)
287: break;
288: --cp;
289: }
290: left = bufsiz;
291: continue;
292: } else if (c == term_erase) {
293: if (cp > buf) {
294: if (write(fd, "\b \b", 3) == -1)
295: break;
296: --cp;
297: left++;
298: }
299: continue;
300: }
1.1.1.2 misho 301: ignore_result(write(fd, "*", 1));
1.1 misho 302: }
303: *cp++ = c;
304: }
305: *cp = '\0';
306: if (feedback) {
307: /* erase stars */
308: while (cp > buf) {
309: if (write(fd, "\b \b", 3) == -1)
310: break;
311: --cp;
312: }
313: }
314:
1.1.1.2 misho 315: debug_return_str_masked(nr == 1 ? buf : NULL);
1.1 misho 316: }
317:
318: static void
1.1.1.4 ! misho 319: tgetpass_handler(int s)
1.1 misho 320: {
321: if (s != SIGALRM)
322: signo[s] = 1;
323: }
324:
325: int
326: tty_present(void)
327: {
328: int fd;
1.1.1.2 misho 329: debug_decl(tty_present, SUDO_DEBUG_UTIL)
1.1 misho 330:
331: if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) != -1)
332: close(fd);
1.1.1.2 misho 333: debug_return_bool(fd != -1);
1.1 misho 334: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>