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

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: /*
                     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;
                     74:     static char buf[SUDO_PASS_MAX + 1];
                     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')
                     99:            errorx(1, _("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:      */
                    131:     zero_bytes(&sa, sizeof(sa));
                    132:     sigemptyset(&sa.sa_mask);
                    133:     sa.sa_flags = SA_INTERRUPT;        /* don't restart system calls */
                    134:     sa.sa_handler = handler;
                    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: {
                    211:     static char buf[SUDO_PASS_MAX + 1], *pass;
                    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)
                    218:        error(1, _("unable to create pipe"));
                    219: 
                    220:     if ((pid = fork()) == -1)
                    221:        error(1, _("unable to fork"));
                    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:        }
                    229:        (void) setuid(ROOT_UID);
                    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
                    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;
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>