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, ×tamp_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(×tamp_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, ×tamp_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, ×tamp_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(×tamp_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, ×tamp_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(×tamp_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, ×tamp_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>