Annotation of embedaddon/sudo/src/tgetpass.c, revision 1.1.1.5

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>