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

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

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