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

1.1       misho       1: /*
1.1.1.3 ! misho       2:  * Copyright (c) 2014 Todd C. Miller <Todd.Miller@courtesan.com>
1.1       misho       3:  *
                      4:  * Permission to use, copy, modify, and distribute this software for any
                      5:  * purpose with or without fee is hereby granted, provided that the above
                      6:  * copyright notice and this permission notice appear in all copies.
                      7:  *
                      8:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                      9:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     10:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     11:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     12:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     13:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     14:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     15:  */
                     16: 
                     17: #include <config.h>
                     18: 
                     19: #include <sys/types.h>
                     20: #include <sys/time.h>
                     21: #include <sys/stat.h>
                     22: #include <stdio.h>
                     23: #ifdef STDC_HEADERS
                     24: # include <stdlib.h>
                     25: # include <stddef.h>
                     26: #else
                     27: # ifdef HAVE_STDLIB_H
                     28: #  include <stdlib.h>
                     29: # endif
                     30: #endif /* STDC_HEADERS */
                     31: #ifdef HAVE_STRING_H
                     32: # include <string.h>
                     33: #endif /* HAVE_STRING_H */
                     34: #ifdef HAVE_STRINGS_H
                     35: # include <strings.h>
                     36: #endif /* HAVE_STRINGS_H */
                     37: #ifdef HAVE_UNISTD_H
                     38: # include <unistd.h>
                     39: #endif /* HAVE_UNISTD_H */
1.1.1.3 ! misho      40: #ifdef TIME_WITH_SYS_TIME
1.1       misho      41: # include <time.h>
                     42: #endif
1.1.1.3 ! misho      43: #ifndef HAVE_STRUCT_TIMESPEC
        !            44: # include "compat/timespec.h"
        !            45: #endif
1.1       misho      46: #include <errno.h>
                     47: #include <fcntl.h>
                     48: #include <pwd.h>
                     49: #include <grp.h>
                     50: 
                     51: #include "sudoers.h"
1.1.1.3 ! misho      52: #include "secure_path.h"
1.1       misho      53: #include "check.h"
                     54: 
1.1.1.3 ! misho      55: /* On Linux, CLOCK_MONOTONIC does not run while suspended. */
        !            56: #if defined(CLOCK_BOOTTIME)
        !            57: # define SUDO_CLOCK_MONOTONIC  CLOCK_BOOTTIME
        !            58: #elif defined(CLOCK_MONOTONIC)
        !            59: # define SUDO_CLOCK_MONOTONIC  CLOCK_MONOTONIC
        !            60: #else
        !            61: # define SUDO_CLOCK_MONOTONIC  CLOCK_REALTIME
        !            62: #endif
        !            63: 
        !            64: static char timestamp_file[PATH_MAX];
        !            65: static off_t timestamp_hint = (off_t)-1;
        !            66: static struct timestamp_entry timestamp_key;
1.1       misho      67: 
                     68: /*
1.1.1.3 ! misho      69:  * Returns true if entry matches key, else false.
1.1       misho      70:  */
1.1.1.3 ! misho      71: static bool
        !            72: ts_match_record(struct timestamp_entry *key, struct timestamp_entry *entry)
1.1       misho      73: {
1.1.1.3 ! misho      74:     debug_decl(ts_match_record, SUDO_DEBUG_AUTH)
1.1       misho      75: 
1.1.1.3 ! misho      76:     if (entry->version != key->version)
        !            77:        debug_return_bool(false);
        !            78:     if (!ISSET(key->flags, TS_ANYUID) && entry->auth_uid != key->auth_uid)
        !            79:        debug_return_bool(false);
        !            80:     if (entry->type != key->type)
        !            81:        debug_return_bool(false);
        !            82:     switch (entry->type) {
        !            83:     case TS_GLOBAL:
        !            84:        /* no ppid or tty to match */
        !            85:        break;
        !            86:     case TS_PPID:
        !            87:        /* verify parent pid */
        !            88:        if (entry->u.ppid != key->u.ppid)
        !            89:            debug_return_bool(false);
        !            90:        break;
        !            91:     case TS_TTY:
        !            92:        if (entry->u.ttydev != key->u.ttydev)
        !            93:            debug_return_bool(false);
        !            94:        break;
        !            95:     default:
        !            96:        /* unknown record type, ignore it */
        !            97:        debug_return_bool(false);
        !            98:     }
        !            99:     debug_return_bool(true);
        !           100: }
        !           101: 
        !           102: /*
        !           103:  * Searches the time stamp file descriptor for a record that matches key.
        !           104:  * On success, fills in entry with the matching record and returns true.
        !           105:  * On failure, returns false.
        !           106:  *
        !           107:  * Note that records are searched starting at the current file offset,
        !           108:  * which may not be the beginning of the file.
        !           109:  */
        !           110: static bool
        !           111: ts_find_record(int fd, struct timestamp_entry *key, struct timestamp_entry *entry)
        !           112: {
        !           113:     struct timestamp_entry cur;
        !           114:     debug_decl(ts_find_record, SUDO_DEBUG_AUTH)
1.1       misho     115: 
                    116:     /*
1.1.1.3 ! misho     117:      * Look for a matching record.
        !           118:      * We don't match on the sid or actual time stamp.
1.1       misho     119:      */
1.1.1.3 ! misho     120:     while (read(fd, &cur, sizeof(cur)) == sizeof(cur)) {
        !           121:        if (cur.size != sizeof(cur)) {
        !           122:            /* wrong size, seek to start of next record */
        !           123:            sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
        !           124:                "wrong sized record, got %hu, expected %zu",
        !           125:                cur.size, sizeof(cur));
        !           126:            lseek(fd, (off_t)cur.size - (off_t)sizeof(cur), SEEK_CUR);
        !           127:            if (cur.size == 0)
        !           128:                break;                  /* size must be non-zero */
        !           129:            continue;
        !           130:        }
        !           131:        if (ts_match_record(key, &cur)) {
        !           132:            memcpy(entry, &cur, sizeof(struct timestamp_entry));
        !           133:            debug_return_bool(true);
        !           134:        }
        !           135:     }
        !           136:     debug_return_bool(false);
        !           137: }
1.1       misho     138: 
1.1.1.3 ! misho     139: /*
        !           140:  * Find matching record to update or append a new one.
        !           141:  * Returns true if the entry was written successfully, else false.
        !           142:  */
        !           143: static bool
        !           144: ts_update_record(int fd, struct timestamp_entry *entry, off_t timestamp_hint)
        !           145: {
        !           146:     struct timestamp_entry cur;
        !           147:     ssize_t nwritten;
        !           148:     off_t old_eof = (off_t)-1;
        !           149:     debug_decl(ts_update_record, SUDO_DEBUG_AUTH)
        !           150: 
        !           151:     /* First try the hint if one is given. */
        !           152:     if (timestamp_hint != (off_t)-1) {
        !           153:        if (lseek(fd, timestamp_hint, SEEK_SET) != -1) {
        !           154:            if (read(fd, &cur, sizeof(cur)) == sizeof(cur)) {
        !           155:                if (ts_match_record(entry, &cur)) {
        !           156:                    sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
        !           157:                        "found existing time stamp record using hint");
        !           158:                    goto found_it;
        !           159:                }
        !           160:            }
1.1.1.2   misho     161:        }
1.1.1.3 ! misho     162:     }
        !           163: 
        !           164:     /* Search for matching record. */
        !           165:     sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
        !           166:        "searching for time stamp record");
        !           167:     lseek(fd, (off_t)0, SEEK_SET);
        !           168:     if (ts_find_record(fd, entry, &cur)) {
        !           169:        sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
        !           170:            "found existing time stamp record");
        !           171: found_it:
        !           172:        /* back up over old record */
        !           173:        lseek(fd, (off_t)0 - (off_t)cur.size, SEEK_CUR);
        !           174:     } else {
        !           175:        sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
        !           176:            "appending new time stamp record");
        !           177:        old_eof = lseek(fd, (off_t)0, SEEK_CUR);
        !           178:     }
        !           179: 
        !           180:     /* Overwrite existing record or append to end. */
        !           181:     nwritten = write(fd, entry, sizeof(struct timestamp_entry));
        !           182:     if ((size_t)nwritten == sizeof(struct timestamp_entry))
        !           183:        debug_return_bool(true);
        !           184: 
        !           185:     log_warning(nwritten == -1 ? USE_ERRNO : 0,
        !           186:        N_("unable to write to %s"), timestamp_file);
        !           187: 
        !           188:     /* Truncate on partial write to be safe. */
        !           189:     if (nwritten > 0 && old_eof != (off_t)-1) {
        !           190:        sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
        !           191:            "short write, truncating partial time stamp record");
        !           192:        if (ftruncate(fd, old_eof) != 0) {
        !           193:            warning(U_("unable to truncate time stamp file to %lld bytes"),
        !           194:                (long long)old_eof);
        !           195:        }
        !           196:     }
        !           197: 
        !           198:     debug_return_bool(false);
        !           199: }
        !           200: 
        !           201: /*
        !           202:  * Create a directory and any missing parent directories with the
        !           203:  * specified mode.
        !           204:  * Returns true on success.
        !           205:  * Returns false on failure and displays a warning to stderr.
        !           206:  */
        !           207: static bool
        !           208: ts_mkdirs(char *path, uid_t owner, mode_t mode, mode_t parent_mode, bool quiet)
        !           209: {
        !           210:     struct stat sb;
        !           211:     gid_t parent_gid = 0;
        !           212:     char *slash = path;
        !           213:     bool rval = false;
        !           214:     debug_decl(ts_mkdirs, SUDO_DEBUG_AUTH)
        !           215: 
        !           216:     while ((slash = strchr(slash + 1, '/')) != NULL) {
        !           217:        *slash = '\0';
        !           218:        if (stat(path, &sb) != 0) {
        !           219:            sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
        !           220:                "mkdir %s, mode 0%o", path, parent_mode);
        !           221:            if (mkdir(path, parent_mode) != 0) {
        !           222:                if (!quiet)
        !           223:                    warning(U_("unable to mkdir %s"), path);
        !           224:                goto done;
        !           225:            }
        !           226:            ignore_result(chown(path, (uid_t)-1, parent_gid));
        !           227:        } else if (!S_ISDIR(sb.st_mode)) {
        !           228:            if (!quiet) {
        !           229:                warningx(U_("%s exists but is not a directory (0%o)"),
        !           230:                    path, (unsigned int) sb.st_mode);
        !           231:            }
        !           232:            goto done;
1.1.1.2   misho     233:        } else {
1.1.1.3 ! misho     234:            /* Inherit gid of parent dir for ownership. */
        !           235:            parent_gid = sb.st_gid;
        !           236:        }
        !           237:        *slash = '/';
        !           238:     }
        !           239:     /* Create final path component. */
        !           240:     sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
        !           241:        "mkdir %s, mode 0%o", path, mode);
        !           242:     if (mkdir(path, mode) != 0 && errno != EEXIST) {
        !           243:        if (!quiet)
        !           244:            warning(U_("unable to mkdir %s"), path);
        !           245:        goto done;
        !           246:     }
        !           247:     ignore_result(chown(path, owner, parent_gid));
        !           248:     rval = true;
        !           249: done:
        !           250:     debug_return_bool(rval);
        !           251: }
        !           252: 
        !           253: /*
        !           254:  * Check that path is owned by timestamp_uid and not writable by
        !           255:  * group or other.  If path is missing and make_it is true, create
        !           256:  * the directory and its parent dirs.
        !           257:  * Returns true on success or false on failure, setting errno.
        !           258:  */
        !           259: static bool
        !           260: ts_secure_dir(char *path, bool make_it, bool quiet)
        !           261: {
        !           262:     struct stat sb;
        !           263:     bool rval = false;
        !           264:     debug_decl(ts_secure_dir, SUDO_DEBUG_AUTH)
        !           265: 
        !           266:     sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "checking %s", path);
        !           267:     switch (sudo_secure_dir(path, timestamp_uid, -1, &sb)) {
        !           268:     case SUDO_PATH_SECURE:
        !           269:        rval = true;
        !           270:        break;
        !           271:     case SUDO_PATH_MISSING:
        !           272:        if (make_it && ts_mkdirs(path, timestamp_uid, 0700, 0711, quiet)) {
        !           273:            rval = true;
        !           274:            break;
        !           275:        }
        !           276:        errno = ENOENT;
        !           277:        break;
        !           278:     case SUDO_PATH_BAD_TYPE:
        !           279:        errno = ENOTDIR;
        !           280:        if (!quiet)
        !           281:            warning("%s", path);
        !           282:        break;
        !           283:     case SUDO_PATH_WRONG_OWNER:
        !           284:        if (!quiet) {
        !           285:            warningx(U_("%s is owned by uid %u, should be %u"),
        !           286:                path, (unsigned int) sb.st_uid,
        !           287:                (unsigned int) timestamp_uid);
1.1.1.2   misho     288:        }
1.1.1.3 ! misho     289:        errno = EACCES;
        !           290:        break;
        !           291:     case SUDO_PATH_GROUP_WRITABLE:
        !           292:        if (!quiet)
        !           293:            warningx(U_("%s is group writable"), path);
        !           294:        errno = EACCES;
        !           295:        break;
        !           296:     }
        !           297:     debug_return_bool(rval);
        !           298: }
        !           299: 
        !           300: /*
        !           301:  * Fills in the timestamp_file[] global variable.
        !           302:  * Returns the length of timestamp_file.
        !           303:  */
        !           304: int
        !           305: build_timestamp(struct passwd *pw)
        !           306: {
        !           307:     int len;
        !           308:     debug_decl(build_timestamp, SUDO_DEBUG_AUTH)
        !           309: 
        !           310:     len = snprintf(timestamp_file, sizeof(timestamp_file), "%s/%s",
        !           311:        def_timestampdir, user_name);
        !           312:     if (len <= 0 || (size_t)len >= sizeof(timestamp_file)) {
        !           313:        log_fatal(0, N_("timestamp path too long: %s/%s"),
        !           314:            def_timestampdir, user_name);
1.1       misho     315:     }
                    316: 
                    317:     debug_return_int(len);
                    318: }
                    319: 
                    320: /*
                    321:  * Update the time on the timestamp file/dir or create it if necessary.
1.1.1.3 ! misho     322:  * Returns true on success or false on failure.
1.1       misho     323:  */
                    324: bool
                    325: update_timestamp(struct passwd *pw)
                    326: {
1.1.1.3 ! misho     327:     struct timestamp_entry entry;
        !           328:     bool rval = false;
        !           329:     int fd;
1.1       misho     330:     debug_decl(update_timestamp, SUDO_DEBUG_AUTH)
                    331: 
1.1.1.3 ! misho     332:     /* Zero timeout means don't update the time stamp file. */
        !           333:     if (def_timestamp_timeout == 0)
        !           334:        goto done;
        !           335: 
        !           336:     /* Check/create parent directories as needed. */
        !           337:     if (!ts_secure_dir(def_timestampdir, true, false))
        !           338:        goto done;
        !           339: 
        !           340:     /* Fill in time stamp. */
        !           341:     memcpy(&entry, &timestamp_key, sizeof(struct timestamp_entry));
        !           342:     clock_gettime(SUDO_CLOCK_MONOTONIC, &entry.ts);
        !           343: 
        !           344:     /* Open time stamp file and lock it for exclusive access. */
1.1       misho     345:     if (timestamp_uid != 0)
                    346:        set_perms(PERM_TIMESTAMP);
1.1.1.3 ! misho     347:     fd = open(timestamp_file, O_RDWR|O_CREAT, 0600);
1.1       misho     348:     if (timestamp_uid != 0)
                    349:        restore_perms();
1.1.1.3 ! misho     350:     if (fd == -1) {
        !           351:        log_warning(USE_ERRNO, N_("unable to open %s"), timestamp_file);
        !           352:        goto done;
        !           353:     }
        !           354: 
        !           355:     /* Update record or append a new one. */
        !           356:     lock_file(fd, SUDO_LOCK);
        !           357:     ts_update_record(fd, &entry, timestamp_hint);
        !           358:     close(fd);
        !           359: 
        !           360:     rval = true;
        !           361: 
        !           362: done:
        !           363:     debug_return_bool(rval);
1.1       misho     364: }
                    365: 
                    366: /*
                    367:  * Check the timestamp file and directory and return their status.
1.1.1.3 ! misho     368:  * Returns one of TS_CURRENT, TS_OLD, TS_MISSING, TS_NOFILE, TS_ERROR.
1.1       misho     369:  */
1.1.1.3 ! misho     370: int
        !           371: timestamp_status(struct passwd *pw)
1.1       misho     372: {
1.1.1.3 ! misho     373:     struct timestamp_entry entry;
        !           374:     struct timespec diff, timeout;
1.1       misho     375:     int status = TS_ERROR;             /* assume the worst */
1.1.1.3 ! misho     376:     struct stat sb;
        !           377:     int fd = -1;
        !           378:     debug_decl(timestamp_status, SUDO_DEBUG_AUTH)
1.1       misho     379: 
1.1.1.3 ! misho     380:     /* Reset time stamp offset hint. */
        !           381:     timestamp_hint = (off_t)-1;
        !           382: 
        !           383:     /* Zero timeout means ignore time stamp files. */
        !           384:     if (def_timestamp_timeout == 0) {
        !           385:        status = TS_OLD;        /* XXX - could also be TS_MISSING */
        !           386:        goto done;
        !           387:     }
        !           388: 
        !           389:     /* Ignore time stamp files in an insecure directory. */
        !           390:     if (!ts_secure_dir(def_timestampdir, false, false)) {
        !           391:        if (errno != ENOENT) {
        !           392:            status = TS_ERROR;
        !           393:            goto done;
        !           394:        }
        !           395:        status = TS_MISSING;    /* not insecure, just missing */
        !           396:     }
1.1       misho     397: 
                    398:     /*
1.1.1.3 ! misho     399:      * Create a key used for matching entries in the time stamp file.
        !           400:      * The actual time stamp in the key is used below as the time "now".
1.1       misho     401:      */
1.1.1.3 ! misho     402:     memset(&timestamp_key, 0, sizeof(timestamp_key));
        !           403:     timestamp_key.version = TS_VERSION;
        !           404:     timestamp_key.size = sizeof(timestamp_key);
        !           405:     timestamp_key.type = TS_GLOBAL;    /* may be overriden below */
        !           406:     if (pw != NULL) {
        !           407:        timestamp_key.auth_uid = pw->pw_uid;
1.1       misho     408:     } else {
1.1.1.3 ! misho     409:        timestamp_key.flags = TS_ANYUID;
        !           410:     }
        !           411:     timestamp_key.sid = user_sid;
        !           412:     if (def_tty_tickets) {
        !           413:        if (user_ttypath != NULL && stat(user_ttypath, &sb) == 0) {
        !           414:            /* tty-based time stamp */
        !           415:            timestamp_key.type = TS_TTY;
        !           416:            timestamp_key.u.ttydev = sb.st_rdev;
        !           417:        } else {
        !           418:            /* ppid-based time stamp */
        !           419:            timestamp_key.type = TS_PPID;
        !           420:            timestamp_key.u.ppid = getppid();
1.1       misho     421:        }
                    422:     }
1.1.1.3 ! misho     423:     clock_gettime(SUDO_CLOCK_MONOTONIC, &timestamp_key.ts);
        !           424: 
        !           425:     /* If the time stamp dir is missing there is nothing to do. */
        !           426:     if (status == TS_MISSING)
1.1       misho     427:        goto done;
                    428: 
1.1.1.3 ! misho     429:     /* Open time stamp file and lock it for exclusive access. */
        !           430:     if (timestamp_uid != 0)
        !           431:        set_perms(PERM_TIMESTAMP);
        !           432:     fd = open(timestamp_file, O_RDWR);
        !           433:     if (timestamp_uid != 0)
        !           434:        restore_perms();
        !           435:     if (fd == -1) {
1.1       misho     436:        status = TS_MISSING;
1.1.1.3 ! misho     437:        goto done;
        !           438:     }
        !           439:     lock_file(fd, SUDO_LOCK);
1.1       misho     440: 
1.1.1.3 ! misho     441:     /* Ignore and clear time stamp file if mtime predates boot time. */
        !           442:     if (fstat(fd, &sb) == 0) {
        !           443:        struct timeval boottime, mtime;
        !           444: 
        !           445:        mtim_get(&sb, &mtime);
        !           446:        if (get_boottime(&boottime) && sudo_timevalcmp(&mtime, &boottime, <)) {
        !           447:            ignore_result(ftruncate(fd, (off_t)0));
        !           448:            status = TS_MISSING;
        !           449:            goto done;
1.1       misho     450:        }
                    451:     }
                    452: 
1.1.1.3 ! misho     453:     /* Read existing record, if any. */
        !           454:     if (!ts_find_record(fd, &timestamp_key, &entry)) {
        !           455:        status = TS_MISSING;
        !           456:        goto done;
1.1       misho     457:     }
                    458: 
1.1.1.3 ! misho     459:     /* Set record position hint for use by update_timestamp() */
        !           460:     timestamp_hint = lseek(fd, (off_t)0, SEEK_CUR);
        !           461:     if (timestamp_hint != (off_t)-1)
        !           462:        timestamp_hint -= entry.size;
        !           463: 
        !           464:     if (ISSET(entry.flags, TS_DISABLED)) {
        !           465:        status = TS_OLD;        /* disabled via sudo -k */
        !           466:        goto done;
        !           467:     }
        !           468: 
        !           469:     if (entry.type != TS_GLOBAL && entry.sid != timestamp_key.sid) {
        !           470:        status = TS_OLD;        /* belongs to different session */
        !           471:        goto done;
        !           472:     }
        !           473: 
        !           474:     /* Negative timeouts only expire manually (sudo -k).  */
        !           475:     if (def_timestamp_timeout < 0) {
        !           476:        status = TS_CURRENT;
        !           477:        goto done;
        !           478:     }
        !           479: 
        !           480:     /* Compare stored time stamp with current time. */
        !           481:     sudo_timespecsub(&timestamp_key.ts, &entry.ts, &diff);
        !           482:     timeout.tv_sec = 60 * def_timestamp_timeout;
        !           483:     timeout.tv_nsec = ((60.0 * def_timestamp_timeout) - (double)timeout.tv_sec)
        !           484:        * 1000000000.0;
        !           485:     if (sudo_timespeccmp(&diff, &timeout, <)) {
        !           486:        status = TS_CURRENT;
        !           487: #ifdef CLOCK_MONOTONIC
        !           488:        /* A monotonic clock should never run backwards. */
        !           489:        if (diff.tv_sec < 0) {
        !           490:            log_warning(0, N_("ignoring time stamp from the future"));
        !           491:            status = TS_OLD;
        !           492:            SET(entry.flags, TS_DISABLED);
        !           493:            ts_update_record(fd, &entry, timestamp_hint);
        !           494:        }
        !           495: #else
        !           496:        /* Check for bogus (future) time in the stampfile. */
        !           497:        sudo_timespecsub(&entry.ts, &timestamp_key.ts, &diff);
        !           498:        timeout.tv_sec *= 2;
        !           499:        if (sudo_timespeccmp(&diff, &timeout, >)) {
        !           500:            time_t tv_sec = (time_t)entry.ts.tv_sec;
        !           501:            log_warning(0,
        !           502:                N_("time stamp too far in the future: %20.20s"),
        !           503:                4 + ctime(&tv_sec));
        !           504:            status = TS_OLD;
        !           505:            SET(entry.flags, TS_DISABLED);
        !           506:            ts_update_record(fd, &entry, timestamp_hint);
1.1       misho     507:        }
1.1.1.3 ! misho     508: #endif /* CLOCK_MONOTONIC */
        !           509:     } else {
        !           510:        status = TS_OLD;
1.1       misho     511:     }
                    512: 
                    513: done:
1.1.1.3 ! misho     514:     if (fd != -1)
        !           515:        close(fd);
1.1       misho     516:     debug_return_int(status);
                    517: }
                    518: 
                    519: /*
1.1.1.3 ! misho     520:  * Remove the timestamp entry or file if unlink_it is set.
1.1       misho     521:  */
                    522: void
1.1.1.3 ! misho     523: remove_timestamp(bool unlink_it)
1.1       misho     524: {
1.1.1.3 ! misho     525:     struct timestamp_entry entry;
        !           526:     int fd = -1;
1.1       misho     527:     debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)
                    528: 
                    529:     if (build_timestamp(NULL) == -1)
                    530:        debug_return;
                    531: 
1.1.1.3 ! misho     532:     /* For "sudo -K" simply unlink the time stamp file. */
        !           533:     if (unlink_it) {
        !           534:        (void) unlink(timestamp_file);
        !           535:        debug_return;
        !           536:     }
        !           537: 
        !           538:     /*
        !           539:      * Create a key used for matching entries in the time stamp file.
        !           540:      */
        !           541:     memset(&timestamp_key, 0, sizeof(timestamp_key));
        !           542:     timestamp_key.version = TS_VERSION;
        !           543:     timestamp_key.size = sizeof(timestamp_key);
        !           544:     timestamp_key.type = TS_GLOBAL;    /* may be overriden below */
        !           545:     timestamp_key.flags = TS_ANYUID;
        !           546:     if (def_tty_tickets) {
        !           547:        struct stat sb;
        !           548:        if (user_ttypath != NULL && stat(user_ttypath, &sb) == 0) {
        !           549:            /* tty-based time stamp */
        !           550:            timestamp_key.type = TS_TTY;
        !           551:            timestamp_key.u.ttydev = sb.st_rdev;
        !           552:        } else {
        !           553:            /* ppid-based time stamp */
        !           554:            timestamp_key.type = TS_PPID;
        !           555:            timestamp_key.u.ppid = getppid();
1.1       misho     556:        }
                    557:     }
                    558: 
1.1.1.3 ! misho     559:     /* Open time stamp file and lock it for exclusive access. */
        !           560:     if (timestamp_uid != 0)
        !           561:        set_perms(PERM_TIMESTAMP);
        !           562:     fd = open(timestamp_file, O_RDWR);
        !           563:     if (timestamp_uid != 0)
        !           564:        restore_perms();
        !           565:     if (fd == -1)
        !           566:        goto done;
        !           567:     lock_file(fd, SUDO_LOCK);
        !           568: 
        !           569:     /*
        !           570:      * Find matching entries and invalidate them.
        !           571:      */
        !           572:     while (ts_find_record(fd, &timestamp_key, &entry)) {
        !           573:        /* Set record position hint for use by update_timestamp() */
        !           574:        timestamp_hint = lseek(fd, (off_t)0, SEEK_CUR);
        !           575:        if (timestamp_hint != (off_t)-1)
        !           576:            timestamp_hint -= (off_t)entry.size;
        !           577:        /* Disable the entry. */
        !           578:        SET(entry.flags, TS_DISABLED);
        !           579:        ts_update_record(fd, &entry, timestamp_hint);
        !           580:     }
        !           581:     close(fd);
        !           582: 
        !           583: done:
1.1       misho     584:     debug_return;
                    585: }
                    586: 
                    587: /*
1.1.1.3 ! misho     588:  * Returns true if the user has already been lectured.
        !           589:  */
        !           590: bool
        !           591: already_lectured(int unused)
        !           592: {
        !           593:     char status_file[PATH_MAX];
        !           594:     struct stat sb;
        !           595:     int len;
        !           596:     debug_decl(already_lectured, SUDO_DEBUG_AUTH)
        !           597: 
        !           598:     if (ts_secure_dir(def_lecture_status_dir, false, true)) {
        !           599:        len = snprintf(status_file, sizeof(status_file), "%s/%s",
        !           600:            def_lecture_status_dir, user_name);
        !           601:        if (len <= 0 || (size_t)len >= sizeof(status_file)) {
        !           602:            log_fatal(0, N_("lecture status path too long: %s/%s"),
        !           603:                def_lecture_status_dir, user_name);
        !           604:        }
        !           605:        debug_return_bool(stat(status_file, &sb) == 0);
        !           606:     }
        !           607:     debug_return_bool(false);
        !           608: }
        !           609: 
        !           610: /*
        !           611:  * Create the lecture status file.
        !           612:  * Returns true on success or false on failure.
1.1       misho     613:  */
                    614: bool
                    615: set_lectured(void)
                    616: {
1.1.1.3 ! misho     617:     char lecture_status[PATH_MAX];
        !           618:     int len, fd = -1;
        !           619:     debug_decl(set_lectured, SUDO_DEBUG_AUTH)
        !           620: 
        !           621:     len = snprintf(lecture_status, sizeof(lecture_status), "%s/%s",
        !           622:        def_lecture_status_dir, user_name);
        !           623:     if (len <= 0 || (size_t)len >= sizeof(lecture_status)) {
        !           624:        log_fatal(0, N_("lecture status path too long: %s/%s"),
        !           625:            def_lecture_status_dir, user_name);
        !           626:     }
        !           627: 
        !           628:     /* Sanity check lecture dir and create if missing. */
        !           629:     if (!ts_secure_dir(def_lecture_status_dir, true, false))
        !           630:        goto done;
        !           631: 
        !           632:     /* Create lecture file. */
        !           633:     if (timestamp_uid != 0)
        !           634:        set_perms(PERM_TIMESTAMP);
        !           635:     fd = open(lecture_status, O_WRONLY|O_CREAT|O_TRUNC, 0600);
        !           636:     if (timestamp_uid != 0)
        !           637:        restore_perms();
        !           638:     if (fd != -1)
        !           639:        close(fd);
        !           640: 
        !           641: done:
        !           642:     debug_return_bool(fd != -1 ? true : false);
1.1       misho     643: }

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