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

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

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