X-Git-Url: https://git.gag.com/?a=blobdiff_plain;ds=sidebyside;f=src%2Ftgetpass.c;fp=src%2Ftgetpass.c;h=b23db4cc5fe87c9b3b9a1e684e9cb1fb47c668f4;hb=6ad45aa23af5f5f3b54468937d6a13089201b891;hp=0000000000000000000000000000000000000000;hpb=97bd3ae46779c69fcdab82d0c64bdf05be009ec3;p=debian%2Fsudo diff --git a/src/tgetpass.c b/src/tgetpass.c new file mode 100644 index 0000000..b23db4c --- /dev/null +++ b/src/tgetpass.c @@ -0,0 +1,334 @@ +/* + * Copyright (c) 1996, 1998-2005, 2007-2011 + * Todd C. Miller + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Sponsored in part by the Defense Advanced Research Projects + * Agency (DARPA) and Air Force Research Laboratory, Air Force + * Materiel Command, USAF, under agreement number F39502-99-1-0512. + */ + +#ifdef __TANDEM +# include +#endif + +#include + +#include +#include +#include +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif /* STDC_HEADERS */ +#ifdef HAVE_STRING_H +# if defined(HAVE_MEMORY_H) && !defined(STDC_HEADERS) +# include +# endif +# include +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ +#ifdef HAVE_UNISTD_H +# include +#endif /* HAVE_UNISTD_H */ +#include +#include +#include +#include + +#include "sudo.h" + +static volatile sig_atomic_t signo[NSIG]; + +static void handler(int); +static char *getln(int, char *, size_t, int); +static char *sudo_askpass(const char *, const char *); + +/* + * Like getpass(3) but with timeout and echo flags. + */ +char * +tgetpass(const char *prompt, int timeout, int flags) +{ + sigaction_t sa, savealrm, saveint, savehup, savequit, saveterm; + sigaction_t savetstp, savettin, savettou, savepipe; + char *pass; + static const char *askpass; + static char buf[SUDO_PASS_MAX + 1]; + int i, input, output, save_errno, neednl = 0, need_restart; + debug_decl(tgetpass, SUDO_DEBUG_CONV) + + (void) fflush(stdout); + + if (askpass == NULL) { + askpass = getenv_unhooked("SUDO_ASKPASS"); + if (askpass == NULL || *askpass == '\0') + askpass = sudo_conf_askpass_path(); + } + + /* If no tty present and we need to disable echo, try askpass. */ + if (!ISSET(flags, TGP_STDIN|TGP_ECHO|TGP_ASKPASS|TGP_NOECHO_TRY) && + !tty_present()) { + if (askpass == NULL || getenv_unhooked("DISPLAY") == NULL) { + warningx(_("no tty present and no askpass program specified")); + debug_return_str(NULL); + } + SET(flags, TGP_ASKPASS); + } + + /* If using a helper program to get the password, run it instead. */ + if (ISSET(flags, TGP_ASKPASS)) { + if (askpass == NULL || *askpass == '\0') + errorx(1, _("no askpass program specified, try setting SUDO_ASKPASS")); + debug_return_str_masked(sudo_askpass(askpass, prompt)); + } + +restart: + for (i = 0; i < NSIG; i++) + signo[i] = 0; + pass = NULL; + save_errno = 0; + need_restart = 0; + /* Open /dev/tty for reading/writing if possible else use stdin/stderr. */ + if (ISSET(flags, TGP_STDIN) || + (input = output = open(_PATH_TTY, O_RDWR|O_NOCTTY)) == -1) { + input = STDIN_FILENO; + output = STDERR_FILENO; + } + + /* + * If we are using a tty but are not the foreground pgrp this will + * generate SIGTTOU, so do it *before* installing the signal handlers. + */ + if (!ISSET(flags, TGP_ECHO)) { + if (ISSET(flags, TGP_MASK)) + neednl = term_cbreak(input); + else + neednl = term_noecho(input); + } + + /* + * Catch signals that would otherwise cause the user to end + * up with echo turned off in the shell. + */ + zero_bytes(&sa, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_INTERRUPT; /* don't restart system calls */ + sa.sa_handler = handler; + (void) sigaction(SIGALRM, &sa, &savealrm); + (void) sigaction(SIGINT, &sa, &saveint); + (void) sigaction(SIGHUP, &sa, &savehup); + (void) sigaction(SIGQUIT, &sa, &savequit); + (void) sigaction(SIGTERM, &sa, &saveterm); + (void) sigaction(SIGTSTP, &sa, &savetstp); + (void) sigaction(SIGTTIN, &sa, &savettin); + (void) sigaction(SIGTTOU, &sa, &savettou); + + /* Ignore SIGPIPE in case stdin is a pipe and TGP_STDIN is set */ + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGPIPE, &sa, &savepipe); + + if (prompt) { + if (write(output, prompt, strlen(prompt)) == -1) + goto restore; + } + + if (timeout > 0) + alarm(timeout); + pass = getln(input, buf, sizeof(buf), ISSET(flags, TGP_MASK)); + alarm(0); + save_errno = errno; + + if (neednl || pass == NULL) { + if (write(output, "\n", 1) == -1) + goto restore; + } + +restore: + /* Restore old tty settings and signals. */ + if (!ISSET(flags, TGP_ECHO)) + term_restore(input, 1); + (void) sigaction(SIGALRM, &savealrm, NULL); + (void) sigaction(SIGINT, &saveint, NULL); + (void) sigaction(SIGHUP, &savehup, NULL); + (void) sigaction(SIGQUIT, &savequit, NULL); + (void) sigaction(SIGTERM, &saveterm, NULL); + (void) sigaction(SIGTSTP, &savetstp, NULL); + (void) sigaction(SIGTTIN, &savettin, NULL); + (void) sigaction(SIGTTOU, &savettou, NULL); + (void) sigaction(SIGTTOU, &savepipe, NULL); + if (input != STDIN_FILENO) + (void) close(input); + + /* + * If we were interrupted by a signal, resend it to ourselves + * now that we have restored the signal handlers. + */ + for (i = 0; i < NSIG; i++) { + if (signo[i]) { + kill(getpid(), i); + switch (i) { + case SIGTSTP: + case SIGTTIN: + case SIGTTOU: + need_restart = 1; + break; + } + } + } + if (need_restart) + goto restart; + + if (save_errno) + errno = save_errno; + + debug_return_str_masked(pass); +} + +/* + * Fork a child and exec sudo-askpass to get the password from the user. + */ +static char * +sudo_askpass(const char *askpass, const char *prompt) +{ + static char buf[SUDO_PASS_MAX + 1], *pass; + sigaction_t sa, saved_sa_pipe; + int pfd[2]; + pid_t pid; + debug_decl(sudo_askpass, SUDO_DEBUG_CONV) + + if (pipe(pfd) == -1) + error(1, _("unable to create pipe")); + + if ((pid = fork()) == -1) + error(1, _("unable to fork")); + + if (pid == 0) { + /* child, point stdout to output side of the pipe and exec askpass */ + if (dup2(pfd[1], STDOUT_FILENO) == -1) { + warning("dup2"); + _exit(255); + } + (void) setuid(ROOT_UID); + if (setgid(user_details.gid)) { + warning(_("unable to set gid to %u"), (unsigned int)user_details.gid); + _exit(255); + } + if (setuid(user_details.uid)) { + warning(_("unable to set uid to %u"), (unsigned int)user_details.uid); + _exit(255); + } + closefrom(STDERR_FILENO + 1); + execl(askpass, askpass, prompt, (char *)NULL); + warning(_("unable to run %s"), askpass); + _exit(255); + } + + /* Ignore SIGPIPE in case child exits prematurely */ + zero_bytes(&sa, sizeof(sa)); + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_INTERRUPT; + sa.sa_handler = SIG_IGN; + (void) sigaction(SIGPIPE, &sa, &saved_sa_pipe); + + /* Get response from child (askpass) and restore SIGPIPE handler */ + (void) close(pfd[1]); + pass = getln(pfd[0], buf, sizeof(buf), 0); + (void) close(pfd[0]); + (void) sigaction(SIGPIPE, &saved_sa_pipe, NULL); + + if (pass == NULL) + errno = EINTR; /* make cancel button simulate ^C */ + + debug_return_str_masked(pass); +} + +extern int term_erase, term_kill; + +static char * +getln(int fd, char *buf, size_t bufsiz, int feedback) +{ + size_t left = bufsiz; + ssize_t nr = -1; + char *cp = buf; + char c = '\0'; + debug_decl(getln, SUDO_DEBUG_CONV) + + if (left == 0) { + errno = EINVAL; + debug_return_str(NULL); /* sanity */ + } + + while (--left) { + nr = read(fd, &c, 1); + if (nr != 1 || c == '\n' || c == '\r') + break; + if (feedback) { + if (c == term_kill) { + while (cp > buf) { + if (write(fd, "\b \b", 3) == -1) + break; + --cp; + } + left = bufsiz; + continue; + } else if (c == term_erase) { + if (cp > buf) { + if (write(fd, "\b \b", 3) == -1) + break; + --cp; + left++; + } + continue; + } + ignore_result(write(fd, "*", 1)); + } + *cp++ = c; + } + *cp = '\0'; + if (feedback) { + /* erase stars */ + while (cp > buf) { + if (write(fd, "\b \b", 3) == -1) + break; + --cp; + } + } + + debug_return_str_masked(nr == 1 ? buf : NULL); +} + +static void +handler(int s) +{ + if (s != SIGALRM) + signo[s] = 1; +} + +int +tty_present(void) +{ + int fd; + debug_decl(tty_present, SUDO_DEBUG_UTIL) + + if ((fd = open(_PATH_TTY, O_RDWR|O_NOCTTY)) != -1) + close(fd); + debug_return_bool(fd != -1); +}