Annotation of embedaddon/sudo/plugins/sudoers/check.c, revision 1.1.1.2

1.1       misho       1: /*
                      2:  * Copyright (c) 1993-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: #include <config.h>
                     23: 
                     24: #include <sys/types.h>
                     25: #include <sys/param.h>
                     26: #include <sys/time.h>
                     27: #include <sys/stat.h>
                     28: #ifdef __linux__
                     29: # include <sys/vfs.h>
                     30: #endif
                     31: #if defined(__sun) && defined(__SVR4)
                     32: # include <sys/statvfs.h>
                     33: #endif
                     34: #ifndef __TANDEM
                     35: # include <sys/file.h>
                     36: #endif
                     37: #include <stdio.h>
                     38: #ifdef STDC_HEADERS
                     39: # include <stdlib.h>
                     40: # include <stddef.h>
                     41: #else
                     42: # ifdef HAVE_STDLIB_H
                     43: #  include <stdlib.h>
                     44: # endif
                     45: #endif /* STDC_HEADERS */
                     46: #ifdef HAVE_STRING_H
                     47: # include <string.h>
                     48: #endif /* HAVE_STRING_H */
                     49: #ifdef HAVE_STRINGS_H
                     50: # include <strings.h>
                     51: #endif /* HAVE_STRINGS_H */
                     52: #ifdef HAVE_UNISTD_H
                     53: # include <unistd.h>
                     54: #endif /* HAVE_UNISTD_H */
                     55: #if TIME_WITH_SYS_TIME
                     56: # include <time.h>
                     57: #endif
                     58: #include <errno.h>
                     59: #include <fcntl.h>
                     60: #include <signal.h>
                     61: #include <pwd.h>
                     62: #include <grp.h>
                     63: 
                     64: #include "sudoers.h"
                     65: 
                     66: /* Status codes for timestamp_status() */
                     67: #define TS_CURRENT             0
                     68: #define TS_OLD                 1
                     69: #define TS_MISSING             2
                     70: #define TS_NOFILE              3
                     71: #define TS_ERROR               4
                     72: 
                     73: /* Flags for timestamp_status() */
                     74: #define TS_MAKE_DIRS           1
                     75: #define TS_REMOVE              2
                     76: 
                     77: /*
                     78:  * Info stored in tty ticket from stat(2) to help with tty matching.
                     79:  */
                     80: static struct tty_info {
                     81:     dev_t dev;                 /* ID of device tty resides on */
                     82:     dev_t rdev;                        /* tty device ID */
                     83:     ino_t ino;                 /* tty inode number */
                     84:     struct timeval ctime;      /* tty inode change time */
                     85: } tty_info;
                     86: 
                     87: static int   build_timestamp(char **, char **);
                     88: static int   timestamp_status(char *, char *, char *, int);
                     89: static char *expand_prompt(char *, char *, char *);
                     90: static void  lecture(int);
                     91: static void  update_timestamp(char *, char *);
1.1.1.2 ! misho      92: static bool  tty_is_devpts(const char *);
1.1       misho      93: static struct passwd *get_authpw(void);
                     94: 
                     95: /*
1.1.1.2 ! misho      96:  * Returns true if the user successfully authenticates, else false.
1.1       misho      97:  */
                     98: int
                     99: check_user(int validated, int mode)
                    100: {
                    101:     struct passwd *auth_pw;
                    102:     char *timestampdir = NULL;
                    103:     char *timestampfile = NULL;
                    104:     char *prompt;
                    105:     struct stat sb;
1.1.1.2 ! misho     106:     int status, rval = true;
        !           107:     bool need_pass = def_authenticate;
        !           108:     debug_decl(check_user, SUDO_DEBUG_AUTH)
1.1       misho     109: 
                    110:     /*
                    111:      * Init authentication system regardless of whether we need a password.
                    112:      * Required for proper PAM session support.
                    113:      */
                    114:     auth_pw = get_authpw();
                    115:     if (sudo_auth_init(auth_pw) == -1) {
                    116:        rval = -1;
                    117:        goto done;
                    118:     }
                    119: 
                    120:     if (need_pass) {
                    121:        /* Always need a password when -k was specified with the command. */
                    122:        if (ISSET(mode, MODE_IGNORE_TICKET)) {
                    123:            SET(validated, FLAG_CHECK_USER);
                    124:        } else {
                    125:            /*
                    126:             * Don't prompt for the root passwd or if the user is exempt.
                    127:             * If the user is not changing uid/gid, no need for a password.
                    128:             */
                    129:            if (user_uid == 0 || (user_uid == runas_pw->pw_uid &&
                    130:                (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name)))
                    131:                || user_is_exempt())
1.1.1.2 ! misho     132:                need_pass = false;
1.1       misho     133:        }
                    134:     }
                    135:     if (!need_pass)
                    136:        goto done;
                    137: 
                    138:     /* Stash the tty's ctime for tty ticket comparison. */
                    139:     if (def_tty_tickets && user_ttypath && stat(user_ttypath, &sb) == 0) {
                    140:        tty_info.dev = sb.st_dev;
                    141:        tty_info.ino = sb.st_ino;
                    142:        tty_info.rdev = sb.st_rdev;
                    143:        if (tty_is_devpts(user_ttypath))
                    144:            ctim_get(&sb, &tty_info.ctime);
                    145:     }
                    146: 
                    147:     if (build_timestamp(&timestampdir, &timestampfile) == -1) {
                    148:        rval = -1;
                    149:        goto done;
                    150:     }
                    151: 
                    152:     status = timestamp_status(timestampdir, timestampfile, user_name,
                    153:        TS_MAKE_DIRS);
                    154: 
                    155:     if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {
                    156:        /* Bail out if we are non-interactive and a password is required */
                    157:        if (ISSET(mode, MODE_NONINTERACTIVE)) {
                    158:            warningx(_("sorry, a password is required to run %s"), getprogname());
                    159:            rval = -1;
                    160:            goto done;
                    161:        }
                    162: 
                    163:        /* XXX - should not lecture if askpass helper is being used. */
                    164:        lecture(status);
                    165: 
                    166:        /* Expand any escapes in the prompt. */
                    167:        prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
                    168:            user_name, user_shost);
                    169: 
                    170:        rval = verify_user(auth_pw, prompt);
                    171:     }
                    172:     /* Only update timestamp if user was validated. */
1.1.1.2 ! misho     173:     if (rval == true && ISSET(validated, VALIDATE_OK) &&
1.1       misho     174:        !ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR)
                    175:        update_timestamp(timestampdir, timestampfile);
                    176:     efree(timestampdir);
                    177:     efree(timestampfile);
                    178: 
                    179: done:
                    180:     sudo_auth_cleanup(auth_pw);
                    181:     pw_delref(auth_pw);
                    182: 
1.1.1.2 ! misho     183:     debug_return_bool(rval);
1.1       misho     184: }
                    185: 
                    186: #define DEFAULT_LECTURE "\n" \
                    187:     "We trust you have received the usual lecture from the local System\n" \
                    188:     "Administrator. It usually boils down to these three things:\n\n" \
                    189:     "    #1) Respect the privacy of others.\n" \
                    190:     "    #2) Think before you type.\n" \
                    191:     "    #3) With great power comes great responsibility.\n\n"
                    192: 
                    193: /*
                    194:  * Standard sudo lecture.
                    195:  */
                    196: static void
                    197: lecture(int status)
                    198: {
                    199:     FILE *fp;
                    200:     char buf[BUFSIZ];
                    201:     ssize_t nread;
                    202:     struct sudo_conv_message msg;
                    203:     struct sudo_conv_reply repl;
1.1.1.2 ! misho     204:     debug_decl(lecture, SUDO_DEBUG_AUTH)
1.1       misho     205: 
                    206:     if (def_lecture == never ||
                    207:        (def_lecture == once && status != TS_MISSING && status != TS_ERROR))
1.1.1.2 ! misho     208:        debug_return;
1.1       misho     209: 
                    210:     memset(&msg, 0, sizeof(msg));
                    211:     memset(&repl, 0, sizeof(repl));
                    212: 
                    213:     if (def_lecture_file && (fp = fopen(def_lecture_file, "r")) != NULL) {
                    214:        while ((nread = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) != 0) {
1.1.1.2 ! misho     215:            buf[nread] = '\0';
1.1       misho     216:            msg.msg_type = SUDO_CONV_ERROR_MSG;
                    217:            msg.msg = buf;
                    218:            sudo_conv(1, &msg, &repl);
                    219:        }
                    220:        fclose(fp);
                    221:     } else {
                    222:        msg.msg_type = SUDO_CONV_ERROR_MSG;
                    223:        msg.msg = _(DEFAULT_LECTURE);
                    224:        sudo_conv(1, &msg, &repl);
                    225:     }
1.1.1.2 ! misho     226:     debug_return;
1.1       misho     227: }
                    228: 
                    229: /*
                    230:  * Update the time on the timestamp file/dir or create it if necessary.
                    231:  */
                    232: static void
                    233: update_timestamp(char *timestampdir, char *timestampfile)
                    234: {
1.1.1.2 ! misho     235:     debug_decl(update_timestamp, SUDO_DEBUG_AUTH)
        !           236: 
1.1       misho     237:     /* If using tty timestamps but we have no tty there is nothing to do. */
                    238:     if (def_tty_tickets && !user_ttypath)
1.1.1.2 ! misho     239:        debug_return;
1.1       misho     240: 
                    241:     if (timestamp_uid != 0)
                    242:        set_perms(PERM_TIMESTAMP);
                    243:     if (timestampfile) {
                    244:        /*
                    245:         * Store tty info in timestamp file
                    246:         */
                    247:        int fd = open(timestampfile, O_WRONLY|O_CREAT, 0600);
                    248:        if (fd == -1)
1.1.1.2 ! misho     249:            log_error(USE_ERRNO, _("unable to open %s"), timestampfile);
1.1       misho     250:        else {
                    251:            lock_file(fd, SUDO_LOCK);
                    252:            if (write(fd, &tty_info, sizeof(tty_info)) != sizeof(tty_info)) {
1.1.1.2 ! misho     253:                log_error(USE_ERRNO, _("unable to write to %s"),
1.1       misho     254:                    timestampfile);
                    255:            }
                    256:            close(fd);
                    257:        }
                    258:     } else {
                    259:        if (touch(-1, timestampdir, NULL) == -1) {
                    260:            if (mkdir(timestampdir, 0700) == -1) {
1.1.1.2 ! misho     261:                log_error(USE_ERRNO, _("unable to mkdir %s"),
1.1       misho     262:                    timestampdir);
                    263:            }
                    264:        }
                    265:     }
                    266:     if (timestamp_uid != 0)
                    267:        restore_perms();
1.1.1.2 ! misho     268:     debug_return;
1.1       misho     269: }
                    270: 
                    271: /*
                    272:  * Expand %h and %u escapes in the prompt and pass back the dynamically
                    273:  * allocated result.  Returns the same string if there are no escapes.
                    274:  */
                    275: static char *
                    276: expand_prompt(char *old_prompt, char *user, char *host)
                    277: {
                    278:     size_t len, n;
                    279:     int subst;
                    280:     char *p, *np, *new_prompt, *endp;
1.1.1.2 ! misho     281:     debug_decl(expand_prompt, SUDO_DEBUG_AUTH)
1.1       misho     282: 
                    283:     /* How much space do we need to malloc for the prompt? */
                    284:     subst = 0;
                    285:     for (p = old_prompt, len = strlen(old_prompt); *p; p++) {
                    286:        if (p[0] =='%') {
                    287:            switch (p[1]) {
                    288:                case 'h':
                    289:                    p++;
                    290:                    len += strlen(user_shost) - 2;
                    291:                    subst = 1;
                    292:                    break;
                    293:                case 'H':
                    294:                    p++;
                    295:                    len += strlen(user_host) - 2;
                    296:                    subst = 1;
                    297:                    break;
                    298:                case 'p':
                    299:                    p++;
                    300:                    if (def_rootpw)
                    301:                            len += 2;
                    302:                    else if (def_targetpw || def_runaspw)
                    303:                            len += strlen(runas_pw->pw_name) - 2;
                    304:                    else
                    305:                            len += strlen(user_name) - 2;
                    306:                    subst = 1;
                    307:                    break;
                    308:                case 'u':
                    309:                    p++;
                    310:                    len += strlen(user_name) - 2;
                    311:                    subst = 1;
                    312:                    break;
                    313:                case 'U':
                    314:                    p++;
                    315:                    len += strlen(runas_pw->pw_name) - 2;
                    316:                    subst = 1;
                    317:                    break;
                    318:                case '%':
                    319:                    p++;
                    320:                    len--;
                    321:                    subst = 1;
                    322:                    break;
                    323:                default:
                    324:                    break;
                    325:            }
                    326:        }
                    327:     }
                    328: 
                    329:     if (subst) {
                    330:        new_prompt = emalloc(++len);
                    331:        endp = new_prompt + len;
                    332:        for (p = old_prompt, np = new_prompt; *p; p++) {
                    333:            if (p[0] =='%') {
                    334:                switch (p[1]) {
                    335:                    case 'h':
                    336:                        p++;
                    337:                        n = strlcpy(np, user_shost, np - endp);
                    338:                        if (n >= np - endp)
                    339:                            goto oflow;
                    340:                        np += n;
                    341:                        continue;
                    342:                    case 'H':
                    343:                        p++;
                    344:                        n = strlcpy(np, user_host, np - endp);
                    345:                        if (n >= np - endp)
                    346:                            goto oflow;
                    347:                        np += n;
                    348:                        continue;
                    349:                    case 'p':
                    350:                        p++;
                    351:                        if (def_rootpw)
                    352:                                n = strlcpy(np, "root", np - endp);
                    353:                        else if (def_targetpw || def_runaspw)
                    354:                                n = strlcpy(np, runas_pw->pw_name, np - endp);
                    355:                        else
                    356:                                n = strlcpy(np, user_name, np - endp);
                    357:                        if (n >= np - endp)
                    358:                                goto oflow;
                    359:                        np += n;
                    360:                        continue;
                    361:                    case 'u':
                    362:                        p++;
                    363:                        n = strlcpy(np, user_name, np - endp);
                    364:                        if (n >= np - endp)
                    365:                            goto oflow;
                    366:                        np += n;
                    367:                        continue;
                    368:                    case 'U':
                    369:                        p++;
                    370:                        n = strlcpy(np,  runas_pw->pw_name, np - endp);
                    371:                        if (n >= np - endp)
                    372:                            goto oflow;
                    373:                        np += n;
                    374:                        continue;
                    375:                    case '%':
                    376:                        /* convert %% -> % */
                    377:                        p++;
                    378:                        break;
                    379:                    default:
                    380:                        /* no conversion */
                    381:                        break;
                    382:                }
                    383:            }
                    384:            *np++ = *p;
                    385:            if (np >= endp)
                    386:                goto oflow;
                    387:        }
                    388:        *np = '\0';
                    389:     } else
                    390:        new_prompt = old_prompt;
                    391: 
1.1.1.2 ! misho     392:     debug_return_str(new_prompt);
1.1       misho     393: 
                    394: oflow:
                    395:     /* We pre-allocate enough space, so this should never happen. */
                    396:     errorx(1, _("internal error, expand_prompt() overflow"));
                    397: }
                    398: 
                    399: /*
                    400:  * Checks if the user is exempt from supplying a password.
                    401:  */
1.1.1.2 ! misho     402: bool
1.1       misho     403: user_is_exempt(void)
                    404: {
1.1.1.2 ! misho     405:     bool rval = false;
        !           406:     debug_decl(user_is_exempt, SUDO_DEBUG_AUTH)
        !           407: 
        !           408:     if (def_exempt_group)
        !           409:        rval = user_in_group(sudo_user.pw, def_exempt_group);
        !           410:     debug_return_bool(rval);
1.1       misho     411: }
                    412: 
                    413: /*
                    414:  * Fills in timestampdir as well as timestampfile if using tty tickets.
                    415:  */
                    416: static int
                    417: build_timestamp(char **timestampdir, char **timestampfile)
                    418: {
                    419:     char *dirparent;
                    420:     int len;
1.1.1.2 ! misho     421:     debug_decl(build_timestamp, SUDO_DEBUG_AUTH)
1.1       misho     422: 
                    423:     dirparent = def_timestampdir;
                    424:     len = easprintf(timestampdir, "%s/%s", dirparent, user_name);
                    425:     if (len >= PATH_MAX)
                    426:        goto bad;
                    427: 
                    428:     /*
                    429:      * Timestamp file may be a file in the directory or NUL to use
                    430:      * the directory as the timestamp.
                    431:      */
                    432:     if (def_tty_tickets) {
                    433:        char *p;
                    434: 
                    435:        if ((p = strrchr(user_tty, '/')))
                    436:            p++;
                    437:        else
                    438:            p = user_tty;
                    439:        if (def_targetpw)
                    440:            len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name,
                    441:                p, runas_pw->pw_name);
                    442:        else
                    443:            len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p);
                    444:        if (len >= PATH_MAX)
                    445:            goto bad;
                    446:     } else if (def_targetpw) {
                    447:        len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name,
                    448:            runas_pw->pw_name);
                    449:        if (len >= PATH_MAX)
                    450:            goto bad;
                    451:     } else
                    452:        *timestampfile = NULL;
                    453: 
1.1.1.2 ! misho     454:     debug_return_int(len);
1.1       misho     455: bad:
1.1.1.2 ! misho     456:     log_fatal(0, _("timestamp path too long: %s"), *timestampfile);
        !           457:     debug_return_int(-1);
1.1       misho     458: }
                    459: 
                    460: /*
                    461:  * Check the timestamp file and directory and return their status.
                    462:  */
                    463: static int
                    464: timestamp_status(char *timestampdir, char *timestampfile, char *user, int flags)
                    465: {
                    466:     struct stat sb;
                    467:     struct timeval boottime, mtime;
                    468:     time_t now;
                    469:     char *dirparent = def_timestampdir;
                    470:     int status = TS_ERROR;             /* assume the worst */
1.1.1.2 ! misho     471:     debug_decl(timestamp_status, SUDO_DEBUG_AUTH)
1.1       misho     472: 
                    473:     if (timestamp_uid != 0)
                    474:        set_perms(PERM_TIMESTAMP);
                    475: 
                    476:     /*
                    477:      * Sanity check dirparent and make it if it doesn't already exist.
                    478:      * We start out assuming the worst (that the dir is not sane) and
                    479:      * if it is ok upgrade the status to ``no timestamp file''.
                    480:      * Note that we don't check the parent(s) of dirparent for
                    481:      * sanity since the sudo dir is often just located in /tmp.
                    482:      */
                    483:     if (lstat(dirparent, &sb) == 0) {
                    484:        if (!S_ISDIR(sb.st_mode))
1.1.1.2 ! misho     485:            log_error(0, _("%s exists but is not a directory (0%o)"),
1.1       misho     486:                dirparent, (unsigned int) sb.st_mode);
                    487:        else if (sb.st_uid != timestamp_uid)
1.1.1.2 ! misho     488:            log_error(0, _("%s owned by uid %u, should be uid %u"),
1.1       misho     489:                dirparent, (unsigned int) sb.st_uid,
                    490:                (unsigned int) timestamp_uid);
                    491:        else if ((sb.st_mode & 0000022))
1.1.1.2 ! misho     492:            log_error(0,
1.1       misho     493:                _("%s writable by non-owner (0%o), should be mode 0700"),
                    494:                dirparent, (unsigned int) sb.st_mode);
                    495:        else {
                    496:            if ((sb.st_mode & 0000777) != 0700)
                    497:                (void) chmod(dirparent, 0700);
                    498:            status = TS_MISSING;
                    499:        }
                    500:     } else if (errno != ENOENT) {
1.1.1.2 ! misho     501:        log_error(USE_ERRNO, _("unable to stat %s"), dirparent);
1.1       misho     502:     } else {
                    503:        /* No dirparent, try to make one. */
                    504:        if (ISSET(flags, TS_MAKE_DIRS)) {
                    505:            if (mkdir(dirparent, S_IRWXU))
1.1.1.2 ! misho     506:                log_error(USE_ERRNO, _("unable to mkdir %s"),
1.1       misho     507:                    dirparent);
                    508:            else
                    509:                status = TS_MISSING;
                    510:        }
                    511:     }
                    512:     if (status == TS_ERROR)
                    513:        goto done;
                    514: 
                    515:     /*
                    516:      * Sanity check the user's ticket dir.  We start by downgrading
                    517:      * the status to TS_ERROR.  If the ticket dir exists and is sane
                    518:      * this will be upgraded to TS_OLD.  If the dir does not exist,
                    519:      * it will be upgraded to TS_MISSING.
                    520:      */
                    521:     status = TS_ERROR;                 /* downgrade status again */
                    522:     if (lstat(timestampdir, &sb) == 0) {
                    523:        if (!S_ISDIR(sb.st_mode)) {
                    524:            if (S_ISREG(sb.st_mode)) {
                    525:                /* convert from old style */
                    526:                if (unlink(timestampdir) == 0)
                    527:                    status = TS_MISSING;
                    528:            } else
1.1.1.2 ! misho     529:                log_error(0, _("%s exists but is not a directory (0%o)"),
1.1       misho     530:                    timestampdir, (unsigned int) sb.st_mode);
                    531:        } else if (sb.st_uid != timestamp_uid)
1.1.1.2 ! misho     532:            log_error(0, _("%s owned by uid %u, should be uid %u"),
1.1       misho     533:                timestampdir, (unsigned int) sb.st_uid,
                    534:                (unsigned int) timestamp_uid);
                    535:        else if ((sb.st_mode & 0000022))
1.1.1.2 ! misho     536:            log_error(0,
1.1       misho     537:                _("%s writable by non-owner (0%o), should be mode 0700"),
                    538:                timestampdir, (unsigned int) sb.st_mode);
                    539:        else {
                    540:            if ((sb.st_mode & 0000777) != 0700)
                    541:                (void) chmod(timestampdir, 0700);
                    542:            status = TS_OLD;            /* do date check later */
                    543:        }
                    544:     } else if (errno != ENOENT) {
1.1.1.2 ! misho     545:        log_error(USE_ERRNO, _("unable to stat %s"), timestampdir);
1.1       misho     546:     } else
                    547:        status = TS_MISSING;
                    548: 
                    549:     /*
                    550:      * If there is no user ticket dir, AND we are in tty ticket mode,
                    551:      * AND the TS_MAKE_DIRS flag is set, create the user ticket dir.
                    552:      */
                    553:     if (status == TS_MISSING && timestampfile && ISSET(flags, TS_MAKE_DIRS)) {
                    554:        if (mkdir(timestampdir, S_IRWXU) == -1) {
                    555:            status = TS_ERROR;
1.1.1.2 ! misho     556:            log_error(USE_ERRNO, _("unable to mkdir %s"), timestampdir);
1.1       misho     557:        }
                    558:     }
                    559: 
                    560:     /*
                    561:      * Sanity check the tty ticket file if it exists.
                    562:      */
                    563:     if (timestampfile && status != TS_ERROR) {
                    564:        if (status != TS_MISSING)
                    565:            status = TS_NOFILE;                 /* dir there, file missing */
                    566:        if (def_tty_tickets && !user_ttypath)
                    567:            goto done;                          /* no tty, always prompt */
                    568:        if (lstat(timestampfile, &sb) == 0) {
                    569:            if (!S_ISREG(sb.st_mode)) {
                    570:                status = TS_ERROR;
1.1.1.2 ! misho     571:                log_error(0, _("%s exists but is not a regular file (0%o)"),
1.1       misho     572:                    timestampfile, (unsigned int) sb.st_mode);
                    573:            } else {
                    574:                /* If bad uid or file mode, complain and kill the bogus file. */
                    575:                if (sb.st_uid != timestamp_uid) {
1.1.1.2 ! misho     576:                    log_error(0,
1.1       misho     577:                        _("%s owned by uid %u, should be uid %u"),
                    578:                        timestampfile, (unsigned int) sb.st_uid,
                    579:                        (unsigned int) timestamp_uid);
                    580:                    (void) unlink(timestampfile);
                    581:                } else if ((sb.st_mode & 0000022)) {
1.1.1.2 ! misho     582:                    log_error(0,
1.1       misho     583:                        _("%s writable by non-owner (0%o), should be mode 0600"),
                    584:                        timestampfile, (unsigned int) sb.st_mode);
                    585:                    (void) unlink(timestampfile);
                    586:                } else {
                    587:                    /* If not mode 0600, fix it. */
                    588:                    if ((sb.st_mode & 0000777) != 0600)
                    589:                        (void) chmod(timestampfile, 0600);
                    590: 
                    591:                    /*
                    592:                     * Check for stored tty info.  If the file is zero-sized
                    593:                     * it is an old-style timestamp with no tty info in it.
                    594:                     * If removing, we don't care about the contents.
                    595:                     * The actual mtime check is done later.
                    596:                     */
                    597:                    if (ISSET(flags, TS_REMOVE)) {
                    598:                        status = TS_OLD;
                    599:                    } else if (sb.st_size != 0) {
                    600:                        struct tty_info info;
                    601:                        int fd = open(timestampfile, O_RDONLY, 0644);
                    602:                        if (fd != -1) {
                    603:                            if (read(fd, &info, sizeof(info)) == sizeof(info) &&
                    604:                                memcmp(&info, &tty_info, sizeof(info)) == 0) {
                    605:                                status = TS_OLD;
                    606:                            }
                    607:                            close(fd);
                    608:                        }
                    609:                    }
                    610:                }
                    611:            }
                    612:        } else if (errno != ENOENT) {
1.1.1.2 ! misho     613:            log_error(USE_ERRNO, _("unable to stat %s"), timestampfile);
1.1       misho     614:            status = TS_ERROR;
                    615:        }
                    616:     }
                    617: 
                    618:     /*
                    619:      * If the file/dir exists and we are not removing it, check its mtime.
                    620:      */
                    621:     if (status == TS_OLD && !ISSET(flags, TS_REMOVE)) {
                    622:        mtim_get(&sb, &mtime);
                    623:        /* Negative timeouts only expire manually (sudo -k). */
                    624:        if (def_timestamp_timeout < 0 && mtime.tv_sec != 0)
                    625:            status = TS_CURRENT;
                    626:        else {
                    627:            now = time(NULL);
                    628:            if (def_timestamp_timeout &&
                    629:                now - mtime.tv_sec < 60 * def_timestamp_timeout) {
                    630:                /*
                    631:                 * Check for bogus time on the stampfile.  The clock may
                    632:                 * have been set back or someone could be trying to spoof us.
                    633:                 */
                    634:                if (mtime.tv_sec > now + 60 * def_timestamp_timeout * 2) {
                    635:                    time_t tv_sec = (time_t)mtime.tv_sec;
1.1.1.2 ! misho     636:                    log_error(0,
1.1       misho     637:                        _("timestamp too far in the future: %20.20s"),
                    638:                        4 + ctime(&tv_sec));
                    639:                    if (timestampfile)
                    640:                        (void) unlink(timestampfile);
                    641:                    else
                    642:                        (void) rmdir(timestampdir);
                    643:                    status = TS_MISSING;
                    644:                } else if (get_boottime(&boottime) && timevalcmp(&mtime, &boottime, <)) {
                    645:                    status = TS_OLD;
                    646:                } else {
                    647:                    status = TS_CURRENT;
                    648:                }
                    649:            }
                    650:        }
                    651:     }
                    652: 
                    653: done:
                    654:     if (timestamp_uid != 0)
                    655:        restore_perms();
1.1.1.2 ! misho     656:     debug_return_int(status);
1.1       misho     657: }
                    658: 
                    659: /*
                    660:  * Remove the timestamp ticket file/dir.
                    661:  */
                    662: void
1.1.1.2 ! misho     663: remove_timestamp(bool remove)
1.1       misho     664: {
                    665:     struct timeval tv;
                    666:     char *timestampdir, *timestampfile, *path;
                    667:     int status;
1.1.1.2 ! misho     668:     debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)
1.1       misho     669: 
                    670:     if (build_timestamp(&timestampdir, &timestampfile) == -1)
1.1.1.2 ! misho     671:        debug_return;
1.1       misho     672: 
                    673:     status = timestamp_status(timestampdir, timestampfile, user_name,
                    674:        TS_REMOVE);
                    675:     if (status != TS_MISSING && status != TS_ERROR) {
                    676:        path = timestampfile ? timestampfile : timestampdir;
                    677:        if (remove) {
                    678:            if (timestampfile)
                    679:                status = unlink(timestampfile);
                    680:            else
                    681:                status = rmdir(timestampdir);
                    682:            if (status == -1 && errno != ENOENT) {
1.1.1.2 ! misho     683:                log_error(0,
1.1       misho     684:                    _("unable to remove %s (%s), will reset to the epoch"),
                    685:                    path, strerror(errno));
1.1.1.2 ! misho     686:                remove = false;
1.1       misho     687:            }
                    688:        }
                    689:        if (!remove) {
                    690:            timevalclear(&tv);
                    691:            if (touch(-1, path, &tv) == -1 && errno != ENOENT)
                    692:                error(1, _("unable to reset %s to the epoch"), path);
                    693:        }
                    694:     }
                    695:     efree(timestampdir);
                    696:     efree(timestampfile);
1.1.1.2 ! misho     697: 
        !           698:     debug_return;
1.1       misho     699: }
                    700: 
                    701: /*
1.1.1.2 ! misho     702:  * Returns true if tty lives on a devpts, /dev or /devices filesystem, else
        !           703:  * false.  Unlike most filesystems, the ctime of devpts nodes is not updated
        !           704:  * when the device node is written to, only when the inode's status changes,
1.1       misho     705:  * typically via the chmod, chown, link, rename, or utimes system calls.
                    706:  * Since the ctime is "stable" in this case, we can stash it the tty ticket
                    707:  * file and use it to determine whether the tty ticket file is stale.
                    708:  */
1.1.1.2 ! misho     709: static bool
1.1       misho     710: tty_is_devpts(const char *tty)
                    711: {
1.1.1.2 ! misho     712:     bool retval = false;
1.1       misho     713: #ifdef __linux__
                    714:     struct statfs sfs;
1.1.1.2 ! misho     715:     debug_decl(tty_is_devpts, SUDO_DEBUG_PTY)
1.1       misho     716: 
                    717: #ifndef DEVPTS_SUPER_MAGIC
                    718: # define DEVPTS_SUPER_MAGIC 0x1cd1
                    719: #endif
                    720: 
                    721:     if (statfs(tty, &sfs) == 0) {
                    722:        if (sfs.f_type == DEVPTS_SUPER_MAGIC)
1.1.1.2 ! misho     723:            retval = true;
1.1       misho     724:     }
                    725: #elif defined(__sun) && defined(__SVR4)
                    726:     struct statvfs sfs;
1.1.1.2 ! misho     727:     debug_decl(tty_is_devpts, SUDO_DEBUG_PTY)
1.1       misho     728: 
                    729:     if (statvfs(tty, &sfs) == 0) {
1.1.1.2 ! misho     730:        if (strcmp(sfs.f_fstr, "dev") == 0 || strcmp(sfs.f_fstr, "devices") == 0)
        !           731:            retval = true;
1.1       misho     732:     }
1.1.1.2 ! misho     733: #else
        !           734:     debug_decl(tty_is_devpts, SUDO_DEBUG_PTY)
1.1       misho     735: #endif /* __linux__ */
1.1.1.2 ! misho     736:     debug_return_bool(retval);
1.1       misho     737: }
                    738: 
                    739: /*
                    740:  * Get passwd entry for the user we are going to authenticate as.
                    741:  * By default, this is the user invoking sudo.  In the most common
                    742:  * case, this matches sudo_user.pw or runas_pw.
                    743:  */
                    744: static struct passwd *
                    745: get_authpw(void)
                    746: {
                    747:     struct passwd *pw;
1.1.1.2 ! misho     748:     debug_decl(get_authpw, SUDO_DEBUG_AUTH)
1.1       misho     749: 
                    750:     if (def_rootpw) {
                    751:        if ((pw = sudo_getpwuid(ROOT_UID)) == NULL)
1.1.1.2 ! misho     752:            log_fatal(0, _("unknown uid: %u"), ROOT_UID);
1.1       misho     753:     } else if (def_runaspw) {
                    754:        if ((pw = sudo_getpwnam(def_runas_default)) == NULL)
1.1.1.2 ! misho     755:            log_fatal(0, _("unknown user: %s"), def_runas_default);
1.1       misho     756:     } else if (def_targetpw) {
                    757:        if (runas_pw->pw_name == NULL)
1.1.1.2 ! misho     758:            log_fatal(NO_MAIL|MSG_ONLY, _("unknown uid: %u"),
1.1       misho     759:                (unsigned int) runas_pw->pw_uid);
                    760:        pw_addref(runas_pw);
                    761:        pw = runas_pw;
                    762:     } else {
                    763:        pw_addref(sudo_user.pw);
                    764:        pw = sudo_user.pw;
                    765:     }
                    766: 
1.1.1.2 ! misho     767:     debug_return_ptr(pw);
1.1       misho     768: }

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