Return to timestamp.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / sudo / plugins / sudoers |
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: }