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