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>