Annotation of embedaddon/sudo/plugins/sudoers/timestamp.c, revision 1.1
1.1 ! misho 1: /*
! 2: * Copyright (c) 1993-1996,1998-2005, 2007-2013
! 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/time.h>
! 26: #include <sys/stat.h>
! 27: #ifndef __TANDEM
! 28: # include <sys/file.h>
! 29: #endif
! 30: #include <stdio.h>
! 31: #ifdef STDC_HEADERS
! 32: # include <stdlib.h>
! 33: # include <stddef.h>
! 34: #else
! 35: # ifdef HAVE_STDLIB_H
! 36: # include <stdlib.h>
! 37: # endif
! 38: #endif /* STDC_HEADERS */
! 39: #ifdef HAVE_STRING_H
! 40: # include <string.h>
! 41: #endif /* HAVE_STRING_H */
! 42: #ifdef HAVE_STRINGS_H
! 43: # include <strings.h>
! 44: #endif /* HAVE_STRINGS_H */
! 45: #ifdef HAVE_UNISTD_H
! 46: # include <unistd.h>
! 47: #endif /* HAVE_UNISTD_H */
! 48: #if TIME_WITH_SYS_TIME
! 49: # include <time.h>
! 50: #endif
! 51: #include <errno.h>
! 52: #include <fcntl.h>
! 53: #include <signal.h>
! 54: #include <pwd.h>
! 55: #include <grp.h>
! 56:
! 57: #include "sudoers.h"
! 58: #include "check.h"
! 59:
! 60: static struct sudo_tty_info tty_info;
! 61: static char timestampdir[PATH_MAX];
! 62: static char timestampfile[PATH_MAX];
! 63:
! 64: /*
! 65: * Fills in timestampdir as well as timestampfile if using tty tickets.
! 66: */
! 67: int
! 68: build_timestamp(struct passwd *pw)
! 69: {
! 70: char *dirparent;
! 71: struct stat sb;
! 72: int len;
! 73: debug_decl(build_timestamp, SUDO_DEBUG_AUTH)
! 74:
! 75: /* Stash the tty's device, session ID and ctime for ticket comparison. */
! 76: if (def_tty_tickets && user_ttypath && stat(user_ttypath, &sb) == 0) {
! 77: tty_info.dev = sb.st_dev;
! 78: tty_info.ino = sb.st_ino;
! 79: tty_info.rdev = sb.st_rdev;
! 80: tty_info.uid = sb.st_uid;
! 81: tty_info.gid = sb.st_gid;
! 82: tty_info.sid = user_sid;
! 83: }
! 84:
! 85: dirparent = def_timestampdir;
! 86: timestampfile[0] = '\0';
! 87: len = snprintf(timestampdir, sizeof(timestampdir), "%s/%s", dirparent,
! 88: user_name);
! 89: if (len <= 0 || len >= sizeof(timestampdir))
! 90: goto bad;
! 91:
! 92: /*
! 93: * Timestamp file may be a file in the directory or NUL to use
! 94: * the directory as the timestamp.
! 95: */
! 96: if (def_tty_tickets) {
! 97: char *p;
! 98:
! 99: if ((p = strrchr(user_tty, '/')))
! 100: p++;
! 101: else
! 102: p = user_tty;
! 103: if (def_targetpw)
! 104: len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s:%s",
! 105: dirparent, user_name, p, runas_pw->pw_name);
! 106: else
! 107: len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s",
! 108: dirparent, user_name, p);
! 109: if (len <= 0 || len >= sizeof(timestampfile))
! 110: goto bad;
! 111: } else if (def_targetpw) {
! 112: len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s",
! 113: dirparent, user_name, runas_pw->pw_name);
! 114: if (len <= 0 || len >= sizeof(timestampfile))
! 115: goto bad;
! 116: }
! 117: sudo_debug_printf(SUDO_DEBUG_INFO, "using timestamp file %s", timestampfile);
! 118:
! 119: debug_return_int(len);
! 120: bad:
! 121: log_fatal(0, N_("timestamp path too long: %s"),
! 122: *timestampfile ? timestampfile : timestampdir);
! 123: /* NOTREACHED */
! 124: debug_return_int(-1);
! 125: }
! 126:
! 127: /*
! 128: * Update the time on the timestamp file/dir or create it if necessary.
! 129: */
! 130: bool
! 131: update_timestamp(struct passwd *pw)
! 132: {
! 133: debug_decl(update_timestamp, SUDO_DEBUG_AUTH)
! 134:
! 135: /* If using tty timestamps but we have no tty there is nothing to do. */
! 136: if (def_tty_tickets && !user_ttypath)
! 137: debug_return_bool(false);
! 138:
! 139: if (timestamp_uid != 0)
! 140: set_perms(PERM_TIMESTAMP);
! 141: if (*timestampfile) {
! 142: /*
! 143: * Store tty info in timestamp file
! 144: */
! 145: int fd = open(timestampfile, O_WRONLY|O_CREAT, 0600);
! 146: if (fd == -1)
! 147: log_warning(USE_ERRNO, N_("unable to open %s"), timestampfile);
! 148: else {
! 149: lock_file(fd, SUDO_LOCK);
! 150: if (write(fd, &tty_info, sizeof(tty_info)) != sizeof(tty_info))
! 151: log_warning(USE_ERRNO, N_("unable to write to %s"), timestampfile);
! 152: close(fd);
! 153: }
! 154: } else {
! 155: if (touch(-1, timestampdir, NULL) == -1) {
! 156: if (mkdir(timestampdir, 0700) == -1) {
! 157: log_warning(USE_ERRNO, N_("unable to mkdir %s"),
! 158: timestampdir);
! 159: }
! 160: }
! 161: }
! 162: if (timestamp_uid != 0)
! 163: restore_perms();
! 164: debug_return_bool(true);
! 165: }
! 166:
! 167: /*
! 168: * Check the timestamp file and directory and return their status.
! 169: */
! 170: static int
! 171: timestamp_status_internal(bool removing)
! 172: {
! 173: struct stat sb;
! 174: struct timeval boottime, mtime;
! 175: time_t now;
! 176: char *dirparent = def_timestampdir;
! 177: int status = TS_ERROR; /* assume the worst */
! 178: debug_decl(timestamp_status_internal, SUDO_DEBUG_AUTH)
! 179:
! 180: if (timestamp_uid != 0)
! 181: set_perms(PERM_TIMESTAMP);
! 182:
! 183: /*
! 184: * Sanity check dirparent and make it if it doesn't already exist.
! 185: * We start out assuming the worst (that the dir is not sane) and
! 186: * if it is ok upgrade the status to ``no timestamp file''.
! 187: * Note that we don't check the parent(s) of dirparent for
! 188: * sanity since the sudo dir is often just located in /tmp.
! 189: */
! 190: if (lstat(dirparent, &sb) == 0) {
! 191: if (!S_ISDIR(sb.st_mode))
! 192: log_warning(0, N_("%s exists but is not a directory (0%o)"),
! 193: dirparent, (unsigned int) sb.st_mode);
! 194: else if (sb.st_uid != timestamp_uid)
! 195: log_warning(0, N_("%s owned by uid %u, should be uid %u"),
! 196: dirparent, (unsigned int) sb.st_uid,
! 197: (unsigned int) timestamp_uid);
! 198: else if ((sb.st_mode & 0000022))
! 199: log_warning(0,
! 200: N_("%s writable by non-owner (0%o), should be mode 0700"),
! 201: dirparent, (unsigned int) sb.st_mode);
! 202: else {
! 203: if ((sb.st_mode & 0000777) != 0700)
! 204: (void) chmod(dirparent, 0700);
! 205: status = TS_MISSING;
! 206: }
! 207: } else if (errno != ENOENT) {
! 208: log_warning(USE_ERRNO, N_("unable to stat %s"), dirparent);
! 209: } else {
! 210: /* No dirparent, try to make one. */
! 211: if (!removing) {
! 212: if (mkdir(dirparent, S_IRWXU))
! 213: log_warning(USE_ERRNO, N_("unable to mkdir %s"),
! 214: dirparent);
! 215: else
! 216: status = TS_MISSING;
! 217: }
! 218: }
! 219: if (status == TS_ERROR)
! 220: goto done;
! 221:
! 222: /*
! 223: * Sanity check the user's ticket dir. We start by downgrading
! 224: * the status to TS_ERROR. If the ticket dir exists and is sane
! 225: * this will be upgraded to TS_OLD. If the dir does not exist,
! 226: * it will be upgraded to TS_MISSING.
! 227: */
! 228: status = TS_ERROR; /* downgrade status again */
! 229: if (lstat(timestampdir, &sb) == 0) {
! 230: if (!S_ISDIR(sb.st_mode)) {
! 231: if (S_ISREG(sb.st_mode)) {
! 232: /* convert from old style */
! 233: if (unlink(timestampdir) == 0)
! 234: status = TS_MISSING;
! 235: } else
! 236: log_warning(0, N_("%s exists but is not a directory (0%o)"),
! 237: timestampdir, (unsigned int) sb.st_mode);
! 238: } else if (sb.st_uid != timestamp_uid)
! 239: log_warning(0, N_("%s owned by uid %u, should be uid %u"),
! 240: timestampdir, (unsigned int) sb.st_uid,
! 241: (unsigned int) timestamp_uid);
! 242: else if ((sb.st_mode & 0000022))
! 243: log_warning(0,
! 244: N_("%s writable by non-owner (0%o), should be mode 0700"),
! 245: timestampdir, (unsigned int) sb.st_mode);
! 246: else {
! 247: if ((sb.st_mode & 0000777) != 0700)
! 248: (void) chmod(timestampdir, 0700);
! 249: status = TS_OLD; /* do date check later */
! 250: }
! 251: } else if (errno != ENOENT) {
! 252: log_warning(USE_ERRNO, N_("unable to stat %s"), timestampdir);
! 253: } else
! 254: status = TS_MISSING;
! 255:
! 256: /*
! 257: * If there is no user ticket dir, AND we are in tty ticket mode,
! 258: * AND we are not just going to remove it, create the user ticket dir.
! 259: */
! 260: if (status == TS_MISSING && *timestampfile && !removing) {
! 261: if (mkdir(timestampdir, S_IRWXU) == -1) {
! 262: status = TS_ERROR;
! 263: log_warning(USE_ERRNO, N_("unable to mkdir %s"), timestampdir);
! 264: }
! 265: }
! 266:
! 267: /*
! 268: * Sanity check the tty ticket file if it exists.
! 269: */
! 270: if (*timestampfile && status != TS_ERROR) {
! 271: if (status != TS_MISSING)
! 272: status = TS_NOFILE; /* dir there, file missing */
! 273: if (def_tty_tickets && !user_ttypath)
! 274: goto done; /* no tty, always prompt */
! 275: if (lstat(timestampfile, &sb) == 0) {
! 276: if (!S_ISREG(sb.st_mode)) {
! 277: status = TS_ERROR;
! 278: log_warning(0, N_("%s exists but is not a regular file (0%o)"),
! 279: timestampfile, (unsigned int) sb.st_mode);
! 280: } else {
! 281: /* If bad uid or file mode, complain and kill the bogus file. */
! 282: if (sb.st_uid != timestamp_uid) {
! 283: log_warning(0,
! 284: N_("%s owned by uid %u, should be uid %u"),
! 285: timestampfile, (unsigned int) sb.st_uid,
! 286: (unsigned int) timestamp_uid);
! 287: (void) unlink(timestampfile);
! 288: } else if ((sb.st_mode & 0000022)) {
! 289: log_warning(0,
! 290: N_("%s writable by non-owner (0%o), should be mode 0600"),
! 291: timestampfile, (unsigned int) sb.st_mode);
! 292: (void) unlink(timestampfile);
! 293: } else {
! 294: /* If not mode 0600, fix it. */
! 295: if ((sb.st_mode & 0000777) != 0600)
! 296: (void) chmod(timestampfile, 0600);
! 297:
! 298: /*
! 299: * Check for stored tty info. If the file is zero-sized
! 300: * it is an old-style timestamp with no tty info in it.
! 301: * If removing, we don't care about the contents.
! 302: * The actual mtime check is done later.
! 303: */
! 304: if (removing) {
! 305: status = TS_OLD;
! 306: } else if (sb.st_size != 0) {
! 307: struct sudo_tty_info info;
! 308: int fd = open(timestampfile, O_RDONLY, 0644);
! 309: if (fd != -1) {
! 310: if (read(fd, &info, sizeof(info)) == sizeof(info) &&
! 311: memcmp(&info, &tty_info, sizeof(info)) == 0) {
! 312: status = TS_OLD;
! 313: }
! 314: close(fd);
! 315: }
! 316: }
! 317: }
! 318: }
! 319: } else if (errno != ENOENT) {
! 320: log_warning(USE_ERRNO, N_("unable to stat %s"), timestampfile);
! 321: status = TS_ERROR;
! 322: }
! 323: }
! 324:
! 325: /*
! 326: * If the file/dir exists and we are not removing it, check its mtime.
! 327: */
! 328: if (status == TS_OLD && !removing) {
! 329: mtim_get(&sb, &mtime);
! 330: if (timevalisset(&mtime)) {
! 331: /* Negative timeouts only expire manually (sudo -k). */
! 332: if (def_timestamp_timeout < 0) {
! 333: status = TS_CURRENT;
! 334: } else {
! 335: time(&now);
! 336: if (def_timestamp_timeout &&
! 337: now - mtime.tv_sec < 60 * def_timestamp_timeout) {
! 338: /*
! 339: * Check for bogus time on the stampfile. The clock may
! 340: * have been set back or user could be trying to spoof us.
! 341: */
! 342: if (mtime.tv_sec > now + 60 * def_timestamp_timeout * 2) {
! 343: time_t tv_sec = (time_t)mtime.tv_sec;
! 344: log_warning(0,
! 345: N_("timestamp too far in the future: %20.20s"),
! 346: 4 + ctime(&tv_sec));
! 347: if (*timestampfile)
! 348: (void) unlink(timestampfile);
! 349: else
! 350: (void) rmdir(timestampdir);
! 351: status = TS_MISSING;
! 352: } else if (get_boottime(&boottime) &&
! 353: timevalcmp(&mtime, &boottime, <)) {
! 354: status = TS_OLD;
! 355: } else {
! 356: status = TS_CURRENT;
! 357: }
! 358: }
! 359: }
! 360: }
! 361: }
! 362:
! 363: done:
! 364: if (timestamp_uid != 0)
! 365: restore_perms();
! 366: debug_return_int(status);
! 367: }
! 368:
! 369: int
! 370: timestamp_status(struct passwd *pw)
! 371: {
! 372: return timestamp_status_internal(false);
! 373: }
! 374:
! 375: /*
! 376: * Remove the timestamp ticket file/dir.
! 377: */
! 378: void
! 379: remove_timestamp(bool remove)
! 380: {
! 381: struct timeval tv;
! 382: char *path;
! 383: int status;
! 384: debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)
! 385:
! 386: if (build_timestamp(NULL) == -1)
! 387: debug_return;
! 388:
! 389: status = timestamp_status_internal(true);
! 390: if (status != TS_MISSING && status != TS_ERROR) {
! 391: path = *timestampfile ? timestampfile : timestampdir;
! 392: if (remove) {
! 393: if (*timestampfile)
! 394: status = unlink(timestampfile);
! 395: else
! 396: status = rmdir(timestampdir);
! 397: if (status == -1 && errno != ENOENT) {
! 398: log_warning(0,
! 399: N_("unable to remove %s, will reset to the epoch"), path);
! 400: remove = false;
! 401: }
! 402: }
! 403: if (!remove) {
! 404: timevalclear(&tv);
! 405: if (touch(-1, path, &tv) == -1 && errno != ENOENT)
! 406: fatal(_("unable to reset %s to the epoch"), path);
! 407: }
! 408: }
! 409:
! 410: debug_return;
! 411: }
! 412:
! 413: /*
! 414: * Lecture status is currently implied by the timestamp status but
! 415: * may be stored separately in a future release.
! 416: */
! 417: bool
! 418: set_lectured(void)
! 419: {
! 420: return true;
! 421: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>