File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers / timestamp.c
Revision 1.1.1.3 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 16:12:54 2014 UTC (10 years, 3 months ago) by misho
Branches: sudo, MAIN
CVS tags: v1_8_10p3_0, v1_8_10p3, HEAD
sudo v 1.8.10p3

    1: /*
    2:  * Copyright (c) 2014 Todd C. Miller <Todd.Miller@courtesan.com>
    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 */
   40: #ifdef TIME_WITH_SYS_TIME
   41: # include <time.h>
   42: #endif
   43: #ifndef HAVE_STRUCT_TIMESPEC
   44: # include "compat/timespec.h"
   45: #endif
   46: #include <errno.h>
   47: #include <fcntl.h>
   48: #include <pwd.h>
   49: #include <grp.h>
   50: 
   51: #include "sudoers.h"
   52: #include "secure_path.h"
   53: #include "check.h"
   54: 
   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;
   67: 
   68: /*
   69:  * Returns true if entry matches key, else false.
   70:  */
   71: static bool
   72: ts_match_record(struct timestamp_entry *key, struct timestamp_entry *entry)
   73: {
   74:     debug_decl(ts_match_record, SUDO_DEBUG_AUTH)
   75: 
   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)
  115: 
  116:     /*
  117:      * Look for a matching record.
  118:      * We don't match on the sid or actual time stamp.
  119:      */
  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: }
  138: 
  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: 	    }
  161: 	}
  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;
  233: 	} else {
  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);
  288: 	}
  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);
  315:     }
  316: 
  317:     debug_return_int(len);
  318: }
  319: 
  320: /*
  321:  * Update the time on the timestamp file/dir or create it if necessary.
  322:  * Returns true on success or false on failure.
  323:  */
  324: bool
  325: update_timestamp(struct passwd *pw)
  326: {
  327:     struct timestamp_entry entry;
  328:     bool rval = false;
  329:     int fd;
  330:     debug_decl(update_timestamp, SUDO_DEBUG_AUTH)
  331: 
  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. */
  345:     if (timestamp_uid != 0)
  346: 	set_perms(PERM_TIMESTAMP);
  347:     fd = open(timestamp_file, O_RDWR|O_CREAT, 0600);
  348:     if (timestamp_uid != 0)
  349: 	restore_perms();
  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);
  364: }
  365: 
  366: /*
  367:  * Check the timestamp file and directory and return their status.
  368:  * Returns one of TS_CURRENT, TS_OLD, TS_MISSING, TS_NOFILE, TS_ERROR.
  369:  */
  370: int
  371: timestamp_status(struct passwd *pw)
  372: {
  373:     struct timestamp_entry entry;
  374:     struct timespec diff, timeout;
  375:     int status = TS_ERROR;		/* assume the worst */
  376:     struct stat sb;
  377:     int fd = -1;
  378:     debug_decl(timestamp_status, SUDO_DEBUG_AUTH)
  379: 
  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:     }
  397: 
  398:     /*
  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".
  401:      */
  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;
  408:     } else {
  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();
  421: 	}
  422:     }
  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)
  427: 	goto done;
  428: 
  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) {
  436: 	status = TS_MISSING;
  437: 	goto done;
  438:     }
  439:     lock_file(fd, SUDO_LOCK);
  440: 
  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;
  450: 	}
  451:     }
  452: 
  453:     /* Read existing record, if any. */
  454:     if (!ts_find_record(fd, &timestamp_key, &entry)) {
  455: 	status = TS_MISSING;
  456: 	goto done;
  457:     }
  458: 
  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);
  507: 	}
  508: #endif /* CLOCK_MONOTONIC */
  509:     } else {
  510: 	status = TS_OLD;
  511:     }
  512: 
  513: done:
  514:     if (fd != -1)
  515: 	close(fd);
  516:     debug_return_int(status);
  517: }
  518: 
  519: /*
  520:  * Remove the timestamp entry or file if unlink_it is set.
  521:  */
  522: void
  523: remove_timestamp(bool unlink_it)
  524: {
  525:     struct timestamp_entry entry;
  526:     int fd = -1;
  527:     debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)
  528: 
  529:     if (build_timestamp(NULL) == -1)
  530: 	debug_return;
  531: 
  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();
  556: 	}
  557:     }
  558: 
  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:
  584:     debug_return;
  585: }
  586: 
  587: /*
  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.
  613:  */
  614: bool
  615: set_lectured(void)
  616: {
  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);
  643: }

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