Diff for /embedaddon/sudo/plugins/sudoers/timestamp.c between versions 1.1 and 1.1.1.3

version 1.1, 2013/07/22 00:51:38 version 1.1.1.3, 2014/06/15 16:12:54
Line 1 Line 1
 /*  /*
 * Copyright (c) 1993-1996,1998-2005, 2007-2013 * Copyright (c) 2014 Todd C. Miller <Todd.Miller@courtesan.com>
 *        Todd C. Miller <Todd.Miller@courtesan.com> 
  *   *
  * Permission to use, copy, modify, and distribute this software for any   * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above   * purpose with or without fee is hereby granted, provided that the above
Line 13 Line 12
  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN   * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF   * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.   * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  *  
  * Sponsored in part by the Defense Advanced Research Projects  
  * Agency (DARPA) and Air Force Research Laboratory, Air Force  
  * Materiel Command, USAF, under agreement number F39502-99-1-0512.  
  */   */
   
 #include <config.h>  #include <config.h>
Line 24 Line 19
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/time.h>  #include <sys/time.h>
 #include <sys/stat.h>  #include <sys/stat.h>
 #ifndef __TANDEM  
 # include <sys/file.h>  
 #endif  
 #include <stdio.h>  #include <stdio.h>
 #ifdef STDC_HEADERS  #ifdef STDC_HEADERS
 # include <stdlib.h>  # include <stdlib.h>
Line 45 Line 37
 #ifdef HAVE_UNISTD_H  #ifdef HAVE_UNISTD_H
 # include <unistd.h>  # include <unistd.h>
 #endif /* HAVE_UNISTD_H */  #endif /* HAVE_UNISTD_H */
#if TIME_WITH_SYS_TIME#ifdef TIME_WITH_SYS_TIME
 # include <time.h>  # include <time.h>
 #endif  #endif
   #ifndef HAVE_STRUCT_TIMESPEC
   # include "compat/timespec.h"
   #endif
 #include <errno.h>  #include <errno.h>
 #include <fcntl.h>  #include <fcntl.h>
 #include <signal.h>  
 #include <pwd.h>  #include <pwd.h>
 #include <grp.h>  #include <grp.h>
   
 #include "sudoers.h"  #include "sudoers.h"
   #include "secure_path.h"
 #include "check.h"  #include "check.h"
   
static struct sudo_tty_info tty_info;/* On Linux, CLOCK_MONOTONIC does not run while suspended. */
static char timestampdir[PATH_MAX];#if defined(CLOCK_BOOTTIME)
static char timestampfile[PATH_MAX];# define SUDO_CLOCK_MONOTONIC   CLOCK_BOOTTIME
 #elif defined(CLOCK_MONOTONIC)
 # define SUDO_CLOCK_MONOTONIC   CLOCK_MONOTONIC
 #else
 # define SUDO_CLOCK_MONOTONIC   CLOCK_REALTIME
 #endif
   
   static char timestamp_file[PATH_MAX];
   static off_t timestamp_hint = (off_t)-1;
   static struct timestamp_entry timestamp_key;
   
 /*  /*
 * Fills in timestampdir as well as timestampfile if using tty tickets. * Returns true if entry matches key, else false.
  */   */
intstatic bool
build_timestamp(struct passwd *pw)ts_match_record(struct timestamp_entry *key, struct timestamp_entry *entry)
 {  {
    char *dirparent;    debug_decl(ts_match_record, SUDO_DEBUG_AUTH)
    struct stat sb; 
    int len; 
    debug_decl(build_timestamp, SUDO_DEBUG_AUTH) 
   
    /* Stash the tty's device, session ID and ctime for ticket comparison. */    if (entry->version != key->version)
    if (def_tty_tickets && user_ttypath && stat(user_ttypath, &sb) == 0) {        debug_return_bool(false);
        tty_info.dev = sb.st_dev;    if (!ISSET(key->flags, TS_ANYUID) && entry->auth_uid != key->auth_uid)
        tty_info.ino = sb.st_ino;        debug_return_bool(false);
        tty_info.rdev = sb.st_rdev;    if (entry->type != key->type)
        tty_info.uid = sb.st_uid;        debug_return_bool(false);
        tty_info.gid = sb.st_gid;    switch (entry->type) {
        tty_info.sid = user_sid;    case TS_GLOBAL:
         /* no ppid or tty to match */
         break;
     case TS_PPID:
         /* verify parent pid */
         if (entry->u.ppid != key->u.ppid)
             debug_return_bool(false);
         break;
     case TS_TTY:
         if (entry->u.ttydev != key->u.ttydev)
             debug_return_bool(false);
         break;
     default:
         /* unknown record type, ignore it */
         debug_return_bool(false);
     }      }
       debug_return_bool(true);
   }
   
    dirparent = def_timestampdir;/*
    timestampfile[0] = '\0'; * Searches the time stamp file descriptor for a record that matches key.
    len = snprintf(timestampdir, sizeof(timestampdir), "%s/%s", dirparent, * On success, fills in entry with the matching record and returns true.
        user_name); * On failure, returns false.
    if (len <= 0 || len >= sizeof(timestampdir)) *
        goto bad; * Note that records are searched starting at the current file offset,
  * which may not be the beginning of the file.
  */
 static bool
 ts_find_record(int fd, struct timestamp_entry *key, struct timestamp_entry *entry)
 {
     struct timestamp_entry cur;
     debug_decl(ts_find_record, SUDO_DEBUG_AUTH)
   
     /*      /*
     * Timestamp file may be a file in the directory or NUL to use     * Look for a matching record.
     * the directory as the timestamp.     * We don't match on the sid or actual time stamp.
      */       */
    if (def_tty_tickets) {    while (read(fd, &cur, sizeof(cur)) == sizeof(cur)) {
        char *p;        if (cur.size != sizeof(cur)) {
             /* wrong size, seek to start of next record */
             sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO,
                 "wrong sized record, got %hu, expected %zu",
                 cur.size, sizeof(cur));
             lseek(fd, (off_t)cur.size - (off_t)sizeof(cur), SEEK_CUR);
             if (cur.size == 0)
                 break;                  /* size must be non-zero */
             continue;
         }
         if (ts_match_record(key, &cur)) {
             memcpy(entry, &cur, sizeof(struct timestamp_entry));
             debug_return_bool(true);
         }
     }
     debug_return_bool(false);
 }
   
        if ((p = strrchr(user_tty, '/')))/*
            p++; * Find matching record to update or append a new one.
        else * Returns true if the entry was written successfully, else false.
            p = user_tty; */
        if (def_targetpw)static bool
            len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s:%s",ts_update_record(int fd, struct timestamp_entry *entry, off_t timestamp_hint)
                dirparent, user_name, p, runas_pw->pw_name);{
        else    struct timestamp_entry cur;
            len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s",    ssize_t nwritten;
                dirparent, user_name, p);    off_t old_eof = (off_t)-1;
        if (len <= 0 || len >= sizeof(timestampfile))    debug_decl(ts_update_record, SUDO_DEBUG_AUTH)
            goto bad;
    } else if (def_targetpw) {    /* First try the hint if one is given. */
        len = snprintf(timestampfile, sizeof(timestampfile), "%s/%s/%s",    if (timestamp_hint != (off_t)-1) {
            dirparent, user_name, runas_pw->pw_name);        if (lseek(fd, timestamp_hint, SEEK_SET) != -1) {
        if (len <= 0 || len >= sizeof(timestampfile))            if (read(fd, &cur, sizeof(cur)) == sizeof(cur)) {
            goto bad;                if (ts_match_record(entry, &cur)) {
                     sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                         "found existing time stamp record using hint");
                     goto found_it;
                 }
             }
         }
     }      }
     sudo_debug_printf(SUDO_DEBUG_INFO, "using timestamp file %s", timestampfile);  
   
       /* Search for matching record. */
       sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
           "searching for time stamp record");
       lseek(fd, (off_t)0, SEEK_SET);
       if (ts_find_record(fd, entry, &cur)) {
           sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
               "found existing time stamp record");
   found_it:
           /* back up over old record */
           lseek(fd, (off_t)0 - (off_t)cur.size, SEEK_CUR);
       } else {
           sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
               "appending new time stamp record");
           old_eof = lseek(fd, (off_t)0, SEEK_CUR);
       }
   
       /* Overwrite existing record or append to end. */
       nwritten = write(fd, entry, sizeof(struct timestamp_entry));
       if ((size_t)nwritten == sizeof(struct timestamp_entry))
           debug_return_bool(true);
   
       log_warning(nwritten == -1 ? USE_ERRNO : 0,
           N_("unable to write to %s"), timestamp_file);
   
       /* Truncate on partial write to be safe. */
       if (nwritten > 0 && old_eof != (off_t)-1) {
           sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
               "short write, truncating partial time stamp record");
           if (ftruncate(fd, old_eof) != 0) {
               warning(U_("unable to truncate time stamp file to %lld bytes"),
                   (long long)old_eof);
           }
       }
   
       debug_return_bool(false);
   }
   
   /*
    * Create a directory and any missing parent directories with the
    * specified mode.
    * Returns true on success.
    * Returns false on failure and displays a warning to stderr.
    */
   static bool
   ts_mkdirs(char *path, uid_t owner, mode_t mode, mode_t parent_mode, bool quiet)
   {
       struct stat sb;
       gid_t parent_gid = 0;
       char *slash = path;
       bool rval = false;
       debug_decl(ts_mkdirs, SUDO_DEBUG_AUTH)
   
       while ((slash = strchr(slash + 1, '/')) != NULL) {
           *slash = '\0';
           if (stat(path, &sb) != 0) {
               sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
                   "mkdir %s, mode 0%o", path, parent_mode);
               if (mkdir(path, parent_mode) != 0) {
                   if (!quiet)
                       warning(U_("unable to mkdir %s"), path);
                   goto done;
               }
               ignore_result(chown(path, (uid_t)-1, parent_gid));
           } else if (!S_ISDIR(sb.st_mode)) {
               if (!quiet) {
                   warningx(U_("%s exists but is not a directory (0%o)"),
                       path, (unsigned int) sb.st_mode);
               }
               goto done;
           } else {
               /* Inherit gid of parent dir for ownership. */
               parent_gid = sb.st_gid;
           }
           *slash = '/';
       }
       /* Create final path component. */
       sudo_debug_printf(SUDO_DEBUG_DEBUG|SUDO_DEBUG_LINENO,
           "mkdir %s, mode 0%o", path, mode);
       if (mkdir(path, mode) != 0 && errno != EEXIST) {
           if (!quiet)
               warning(U_("unable to mkdir %s"), path);
           goto done;
       }
       ignore_result(chown(path, owner, parent_gid));
       rval = true;
   done:
       debug_return_bool(rval);
   }
   
   /*
    * Check that path is owned by timestamp_uid and not writable by
    * group or other.  If path is missing and make_it is true, create
    * the directory and its parent dirs.
    * Returns true on success or false on failure, setting errno.
    */
   static bool
   ts_secure_dir(char *path, bool make_it, bool quiet)
   {
       struct stat sb;
       bool rval = false;
       debug_decl(ts_secure_dir, SUDO_DEBUG_AUTH)
   
       sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, "checking %s", path);
       switch (sudo_secure_dir(path, timestamp_uid, -1, &sb)) {
       case SUDO_PATH_SECURE:
           rval = true;
           break;
       case SUDO_PATH_MISSING:
           if (make_it && ts_mkdirs(path, timestamp_uid, 0700, 0711, quiet)) {
               rval = true;
               break;
           }
           errno = ENOENT;
           break;
       case SUDO_PATH_BAD_TYPE:
           errno = ENOTDIR;
           if (!quiet)
               warning("%s", path);
           break;
       case SUDO_PATH_WRONG_OWNER:
           if (!quiet) {
               warningx(U_("%s is owned by uid %u, should be %u"),
                   path, (unsigned int) sb.st_uid,
                   (unsigned int) timestamp_uid);
           }
           errno = EACCES;
           break;
       case SUDO_PATH_GROUP_WRITABLE:
           if (!quiet)
               warningx(U_("%s is group writable"), path);
           errno = EACCES;
           break;
       }
       debug_return_bool(rval);
   }
   
   /*
    * Fills in the timestamp_file[] global variable.
    * Returns the length of timestamp_file.
    */
   int
   build_timestamp(struct passwd *pw)
   {
       int len;
       debug_decl(build_timestamp, SUDO_DEBUG_AUTH)
   
       len = snprintf(timestamp_file, sizeof(timestamp_file), "%s/%s",
           def_timestampdir, user_name);
       if (len <= 0 || (size_t)len >= sizeof(timestamp_file)) {
           log_fatal(0, N_("timestamp path too long: %s/%s"),
               def_timestampdir, user_name);
       }
   
     debug_return_int(len);      debug_return_int(len);
 bad:  
     log_fatal(0, N_("timestamp path too long: %s"),  
         *timestampfile ? timestampfile : timestampdir);  
     /* NOTREACHED */  
     debug_return_int(-1);  
 }  }
   
 /*  /*
  * Update the time on the timestamp file/dir or create it if necessary.   * Update the time on the timestamp file/dir or create it if necessary.
    * Returns true on success or false on failure.
  */   */
 bool  bool
 update_timestamp(struct passwd *pw)  update_timestamp(struct passwd *pw)
 {  {
       struct timestamp_entry entry;
       bool rval = false;
       int fd;
     debug_decl(update_timestamp, SUDO_DEBUG_AUTH)      debug_decl(update_timestamp, SUDO_DEBUG_AUTH)
   
    /* If using tty timestamps but we have no tty there is nothing to do. */    /* Zero timeout means don't update the time stamp file. */
    if (def_tty_tickets && !user_ttypath)    if (def_timestamp_timeout == 0)
        debug_return_bool(false);        goto done;
   
       /* Check/create parent directories as needed. */
       if (!ts_secure_dir(def_timestampdir, true, false))
           goto done;
   
       /* Fill in time stamp. */
       memcpy(&entry, &timestamp_key, sizeof(struct timestamp_entry));
       clock_gettime(SUDO_CLOCK_MONOTONIC, &entry.ts);
   
       /* Open time stamp file and lock it for exclusive access. */
     if (timestamp_uid != 0)      if (timestamp_uid != 0)
         set_perms(PERM_TIMESTAMP);          set_perms(PERM_TIMESTAMP);
    if (*timestampfile) {    fd = open(timestamp_file, O_RDWR|O_CREAT, 0600);
        /* 
         * Store tty info in timestamp file 
         */ 
        int fd = open(timestampfile, O_WRONLY|O_CREAT, 0600); 
        if (fd == -1) 
            log_warning(USE_ERRNO, N_("unable to open %s"), timestampfile); 
        else { 
            lock_file(fd, SUDO_LOCK); 
            if (write(fd, &tty_info, sizeof(tty_info)) != sizeof(tty_info)) 
                log_warning(USE_ERRNO, N_("unable to write to %s"), timestampfile); 
            close(fd); 
        } 
    } else { 
        if (touch(-1, timestampdir, NULL) == -1) { 
            if (mkdir(timestampdir, 0700) == -1) { 
                log_warning(USE_ERRNO, N_("unable to mkdir %s"), 
                    timestampdir); 
            } 
        } 
    } 
     if (timestamp_uid != 0)      if (timestamp_uid != 0)
         restore_perms();          restore_perms();
    debug_return_bool(true);    if (fd == -1) {
         log_warning(USE_ERRNO, N_("unable to open %s"), timestamp_file);
         goto done;
     }
 
     /* Update record or append a new one. */
     lock_file(fd, SUDO_LOCK);
     ts_update_record(fd, &entry, timestamp_hint);
     close(fd);
 
     rval = true;
 
 done:
     debug_return_bool(rval);
 }  }
   
 /*  /*
  * Check the timestamp file and directory and return their status.   * Check the timestamp file and directory and return their status.
    * Returns one of TS_CURRENT, TS_OLD, TS_MISSING, TS_NOFILE, TS_ERROR.
  */   */
static intint
timestamp_status_internal(bool removing)timestamp_status(struct passwd *pw)
 {  {
    struct stat sb;    struct timestamp_entry entry;
    struct timeval boottime, mtime;    struct timespec diff, timeout;
    time_t now; 
    char *dirparent = def_timestampdir; 
     int status = TS_ERROR;              /* assume the worst */      int status = TS_ERROR;              /* assume the worst */
    debug_decl(timestamp_status_internal, SUDO_DEBUG_AUTH)    struct stat sb;
     int fd = -1;
     debug_decl(timestamp_status, SUDO_DEBUG_AUTH)
   
    if (timestamp_uid != 0)    /* Reset time stamp offset hint. */
        set_perms(PERM_TIMESTAMP);    timestamp_hint = (off_t)-1;
   
       /* Zero timeout means ignore time stamp files. */
       if (def_timestamp_timeout == 0) {
           status = TS_OLD;        /* XXX - could also be TS_MISSING */
           goto done;
       }
   
       /* Ignore time stamp files in an insecure directory. */
       if (!ts_secure_dir(def_timestampdir, false, false)) {
           if (errno != ENOENT) {
               status = TS_ERROR;
               goto done;
           }
           status = TS_MISSING;    /* not insecure, just missing */
       }
   
     /*      /*
     * Sanity check dirparent and make it if it doesn't already exist.     * Create a key used for matching entries in the time stamp file.
     * We start out assuming the worst (that the dir is not sane) and     * The actual time stamp in the key is used below as the time "now".
     * if it is ok upgrade the status to ``no timestamp file''. 
     * Note that we don't check the parent(s) of dirparent for 
     * sanity since the sudo dir is often just located in /tmp. 
      */       */
    if (lstat(dirparent, &sb) == 0) {    memset(&timestamp_key, 0, sizeof(timestamp_key));
        if (!S_ISDIR(sb.st_mode))    timestamp_key.version = TS_VERSION;
            log_warning(0, N_("%s exists but is not a directory (0%o)"),    timestamp_key.size = sizeof(timestamp_key);
                dirparent, (unsigned int) sb.st_mode);    timestamp_key.type = TS_GLOBAL;     /* may be overriden below */
        else if (sb.st_uid != timestamp_uid)    if (pw != NULL) {
            log_warning(0, N_("%s owned by uid %u, should be uid %u"),        timestamp_key.auth_uid = pw->pw_uid;
                dirparent, (unsigned int) sb.st_uid, 
                (unsigned int) timestamp_uid); 
        else if ((sb.st_mode & 0000022)) 
            log_warning(0, 
                N_("%s writable by non-owner (0%o), should be mode 0700"), 
                dirparent, (unsigned int) sb.st_mode); 
        else { 
            if ((sb.st_mode & 0000777) != 0700) 
                (void) chmod(dirparent, 0700); 
            status = TS_MISSING; 
        } 
    } else if (errno != ENOENT) { 
        log_warning(USE_ERRNO, N_("unable to stat %s"), dirparent); 
     } else {      } else {
        /* No dirparent, try to make one. */        timestamp_key.flags = TS_ANYUID;
        if (!removing) {    }
            if (mkdir(dirparent, S_IRWXU))    timestamp_key.sid = user_sid;
                log_warning(USE_ERRNO, N_("unable to mkdir %s"),    if (def_tty_tickets) {
                    dirparent);        if (user_ttypath != NULL && stat(user_ttypath, &sb) == 0) {
            else            /* tty-based time stamp */
                status = TS_MISSING;            timestamp_key.type = TS_TTY;
             timestamp_key.u.ttydev = sb.st_rdev;
         } else {
             /* ppid-based time stamp */
             timestamp_key.type = TS_PPID;
             timestamp_key.u.ppid = getppid();
         }          }
     }      }
    if (status == TS_ERROR)    clock_gettime(SUDO_CLOCK_MONOTONIC, &timestamp_key.ts);
 
     /* If the time stamp dir is missing there is nothing to do. */
     if (status == TS_MISSING)
         goto done;          goto done;
   
    /*    /* Open time stamp file and lock it for exclusive access. */
     * Sanity check the user's ticket dir.  We start by downgrading    if (timestamp_uid != 0)
     * the status to TS_ERROR.  If the ticket dir exists and is sane        set_perms(PERM_TIMESTAMP);
     * this will be upgraded to TS_OLD.  If the dir does not exist,    fd = open(timestamp_file, O_RDWR);
     * it will be upgraded to TS_MISSING.    if (timestamp_uid != 0)
     */        restore_perms();
    status = TS_ERROR;                  /* downgrade status again */    if (fd == -1) {
    if (lstat(timestampdir, &sb) == 0) { 
        if (!S_ISDIR(sb.st_mode)) { 
            if (S_ISREG(sb.st_mode)) { 
                /* convert from old style */ 
                if (unlink(timestampdir) == 0) 
                    status = TS_MISSING; 
            } else 
                log_warning(0, N_("%s exists but is not a directory (0%o)"), 
                    timestampdir, (unsigned int) sb.st_mode); 
        } else if (sb.st_uid != timestamp_uid) 
            log_warning(0, N_("%s owned by uid %u, should be uid %u"), 
                timestampdir, (unsigned int) sb.st_uid, 
                (unsigned int) timestamp_uid); 
        else if ((sb.st_mode & 0000022)) 
            log_warning(0, 
                N_("%s writable by non-owner (0%o), should be mode 0700"), 
                timestampdir, (unsigned int) sb.st_mode); 
        else { 
            if ((sb.st_mode & 0000777) != 0700) 
                (void) chmod(timestampdir, 0700); 
            status = TS_OLD;            /* do date check later */ 
        } 
    } else if (errno != ENOENT) { 
        log_warning(USE_ERRNO, N_("unable to stat %s"), timestampdir); 
    } else 
         status = TS_MISSING;          status = TS_MISSING;
           goto done;
       }
       lock_file(fd, SUDO_LOCK);
   
    /*    /* Ignore and clear time stamp file if mtime predates boot time. */
     * If there is no user ticket dir, AND we are in tty ticket mode,    if (fstat(fd, &sb) == 0) {
     * AND we are not just going to remove it, create the user ticket dir.        struct timeval boottime, mtime;
     */
    if (status == TS_MISSING && *timestampfile && !removing) {        mtim_get(&sb, &mtime);
        if (mkdir(timestampdir, S_IRWXU) == -1) {        if (get_boottime(&boottime) && sudo_timevalcmp(&mtime, &boottime, <)) {
            status = TS_ERROR;            ignore_result(ftruncate(fd, (off_t)0));
            log_warning(USE_ERRNO, N_("unable to mkdir %s"), timestampdir);            status = TS_MISSING;
             goto done;
         }          }
     }      }
   
    /*    /* Read existing record, if any. */
     * Sanity check the tty ticket file if it exists.    if (!ts_find_record(fd, &timestamp_key, &entry)) {
     */        status = TS_MISSING;
    if (*timestampfile && status != TS_ERROR) {        goto done;
        if (status != TS_MISSING)    }
            status = TS_NOFILE;                 /* dir there, file missing */ 
        if (def_tty_tickets && !user_ttypath) 
            goto done;                          /* no tty, always prompt */ 
        if (lstat(timestampfile, &sb) == 0) { 
            if (!S_ISREG(sb.st_mode)) { 
                status = TS_ERROR; 
                log_warning(0, N_("%s exists but is not a regular file (0%o)"), 
                    timestampfile, (unsigned int) sb.st_mode); 
            } else { 
                /* If bad uid or file mode, complain and kill the bogus file. */ 
                if (sb.st_uid != timestamp_uid) { 
                    log_warning(0, 
                        N_("%s owned by uid %u, should be uid %u"), 
                        timestampfile, (unsigned int) sb.st_uid, 
                        (unsigned int) timestamp_uid); 
                    (void) unlink(timestampfile); 
                } else if ((sb.st_mode & 0000022)) { 
                    log_warning(0, 
                        N_("%s writable by non-owner (0%o), should be mode 0600"), 
                        timestampfile, (unsigned int) sb.st_mode); 
                    (void) unlink(timestampfile); 
                } else { 
                    /* If not mode 0600, fix it. */ 
                    if ((sb.st_mode & 0000777) != 0600) 
                        (void) chmod(timestampfile, 0600); 
   
                    /*    /* Set record position hint for use by update_timestamp() */
                     * Check for stored tty info.  If the file is zero-sized    timestamp_hint = lseek(fd, (off_t)0, SEEK_CUR);
                     * it is an old-style timestamp with no tty info in it.    if (timestamp_hint != (off_t)-1)
                     * If removing, we don't care about the contents.        timestamp_hint -= entry.size;
                     * The actual mtime check is done later.
                     */    if (ISSET(entry.flags, TS_DISABLED)) {
                    if (removing) {        status = TS_OLD;        /* disabled via sudo -k */
                        status = TS_OLD;        goto done;
                    } else if (sb.st_size != 0) { 
                        struct sudo_tty_info info; 
                        int fd = open(timestampfile, O_RDONLY, 0644); 
                        if (fd != -1) { 
                            if (read(fd, &info, sizeof(info)) == sizeof(info) && 
                                memcmp(&info, &tty_info, sizeof(info)) == 0) { 
                                status = TS_OLD; 
                            } 
                            close(fd); 
                        } 
                    } 
                } 
            } 
        } else if (errno != ENOENT) { 
            log_warning(USE_ERRNO, N_("unable to stat %s"), timestampfile); 
            status = TS_ERROR; 
        } 
     }      }
   
    /*    if (entry.type != TS_GLOBAL && entry.sid != timestamp_key.sid) {
     * If the file/dir exists and we are not removing it, check its mtime.        status = TS_OLD;       /* belongs to different session */
     */        goto done;
    if (status == TS_OLD && !removing) {    }
        mtim_get(&sb, &mtime);
        if (timevalisset(&mtime)) {    /* Negative timeouts only expire manually (sudo -k).  */
            /* Negative timeouts only expire manually (sudo -k). */    if (def_timestamp_timeout < 0) {
            if (def_timestamp_timeout < 0) {        status = TS_CURRENT;
                status = TS_CURRENT;        goto done;
            } else {    }
                time(&now);
                if (def_timestamp_timeout &&    /* Compare stored time stamp with current time. */
                    now - mtime.tv_sec < 60 * def_timestamp_timeout) {    sudo_timespecsub(&timestamp_key.ts, &entry.ts, &diff);
                    /*    timeout.tv_sec = 60 * def_timestamp_timeout;
                     * Check for bogus time on the stampfile.  The clock may    timeout.tv_nsec = ((60.0 * def_timestamp_timeout) - (double)timeout.tv_sec)
                     * have been set back or user could be trying to spoof us.        * 1000000000.0;
                     */    if (sudo_timespeccmp(&diff, &timeout, <)) {
                    if (mtime.tv_sec > now + 60 * def_timestamp_timeout * 2) {        status = TS_CURRENT;
                        time_t tv_sec = (time_t)mtime.tv_sec;#ifdef CLOCK_MONOTONIC
                        log_warning(0,        /* A monotonic clock should never run backwards. */
                            N_("timestamp too far in the future: %20.20s"),        if (diff.tv_sec < 0) {
                            4 + ctime(&tv_sec));            log_warning(0, N_("ignoring time stamp from the future"));
                        if (*timestampfile)            status = TS_OLD;
                            (void) unlink(timestampfile);            SET(entry.flags, TS_DISABLED);
                        else            ts_update_record(fd, &entry, timestamp_hint);
                            (void) rmdir(timestampdir); 
                        status = TS_MISSING; 
                    } else if (get_boottime(&boottime) && 
                        timevalcmp(&mtime, &boottime, <)) { 
                        status = TS_OLD; 
                    } else { 
                        status = TS_CURRENT; 
                    } 
                } 
            } 
         }          }
   #else
           /* Check for bogus (future) time in the stampfile. */
           sudo_timespecsub(&entry.ts, &timestamp_key.ts, &diff);
           timeout.tv_sec *= 2;
           if (sudo_timespeccmp(&diff, &timeout, >)) {
               time_t tv_sec = (time_t)entry.ts.tv_sec;
               log_warning(0,
                   N_("time stamp too far in the future: %20.20s"),
                   4 + ctime(&tv_sec));
               status = TS_OLD;
               SET(entry.flags, TS_DISABLED);
               ts_update_record(fd, &entry, timestamp_hint);
           }
   #endif /* CLOCK_MONOTONIC */
       } else {
           status = TS_OLD;
     }      }
   
 done:  done:
    if (timestamp_uid != 0)    if (fd != -1)
        restore_perms();        close(fd);
     debug_return_int(status);      debug_return_int(status);
 }  }
   
 int  
 timestamp_status(struct passwd *pw)  
 {  
     return timestamp_status_internal(false);  
 }  
   
 /*  /*
 * Remove the timestamp ticket file/dir. * Remove the timestamp entry or file if unlink_it is set.
  */   */
 void  void
remove_timestamp(bool remove)remove_timestamp(bool unlink_it)
 {  {
    struct timeval tv;    struct timestamp_entry entry;
    char *path;    int fd = -1;
    int status; 
     debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)      debug_decl(remove_timestamp, SUDO_DEBUG_AUTH)
   
     if (build_timestamp(NULL) == -1)      if (build_timestamp(NULL) == -1)
         debug_return;          debug_return;
   
    status = timestamp_status_internal(true);    /* For "sudo -K" simply unlink the time stamp file. */
    if (status != TS_MISSING && status != TS_ERROR) {    if (unlink_it) {
        path = *timestampfile ? timestampfile : timestampdir;        (void) unlink(timestamp_file);
        if (remove) {        debug_return;
            if (*timestampfile)    }
                status = unlink(timestampfile);
            else    /*
                status = rmdir(timestampdir);     * Create a key used for matching entries in the time stamp file.
            if (status == -1 && errno != ENOENT) {     */
                log_warning(0,    memset(&timestamp_key, 0, sizeof(timestamp_key));
                    N_("unable to remove %s, will reset to the epoch"), path);    timestamp_key.version = TS_VERSION;
                remove = false;    timestamp_key.size = sizeof(timestamp_key);
            }    timestamp_key.type = TS_GLOBAL;     /* may be overriden below */
     timestamp_key.flags = TS_ANYUID;
     if (def_tty_tickets) {
         struct stat sb;
         if (user_ttypath != NULL && stat(user_ttypath, &sb) == 0) {
             /* tty-based time stamp */
             timestamp_key.type = TS_TTY;
             timestamp_key.u.ttydev = sb.st_rdev;
         } else {
             /* ppid-based time stamp */
             timestamp_key.type = TS_PPID;
             timestamp_key.u.ppid = getppid();
         }          }
         if (!remove) {  
             timevalclear(&tv);  
             if (touch(-1, path, &tv) == -1 && errno != ENOENT)  
                 fatal(_("unable to reset %s to the epoch"), path);  
         }  
     }      }
   
       /* Open time stamp file and lock it for exclusive access. */
       if (timestamp_uid != 0)
           set_perms(PERM_TIMESTAMP);
       fd = open(timestamp_file, O_RDWR);
       if (timestamp_uid != 0)
           restore_perms();
       if (fd == -1)
           goto done;
       lock_file(fd, SUDO_LOCK);
   
       /*
        * Find matching entries and invalidate them.
        */
       while (ts_find_record(fd, &timestamp_key, &entry)) {
           /* Set record position hint for use by update_timestamp() */
           timestamp_hint = lseek(fd, (off_t)0, SEEK_CUR);
           if (timestamp_hint != (off_t)-1)
               timestamp_hint -= (off_t)entry.size;
           /* Disable the entry. */
           SET(entry.flags, TS_DISABLED);
           ts_update_record(fd, &entry, timestamp_hint);
       }
       close(fd);
   
   done:
     debug_return;      debug_return;
 }  }
   
 /*  /*
 * Lecture status is currently implied by the timestamp status but * Returns true if the user has already been lectured.
 * may be stored separately in a future release. 
  */   */
 bool  bool
   already_lectured(int unused)
   {
       char status_file[PATH_MAX];
       struct stat sb;
       int len;
       debug_decl(already_lectured, SUDO_DEBUG_AUTH)
   
       if (ts_secure_dir(def_lecture_status_dir, false, true)) {
           len = snprintf(status_file, sizeof(status_file), "%s/%s",
               def_lecture_status_dir, user_name);
           if (len <= 0 || (size_t)len >= sizeof(status_file)) {
               log_fatal(0, N_("lecture status path too long: %s/%s"),
                   def_lecture_status_dir, user_name);
           }
           debug_return_bool(stat(status_file, &sb) == 0);
       }
       debug_return_bool(false);
   }
   
   /*
    * Create the lecture status file.
    * Returns true on success or false on failure.
    */
   bool
 set_lectured(void)  set_lectured(void)
 {  {
    return true;    char lecture_status[PATH_MAX];
     int len, fd = -1;
     debug_decl(set_lectured, SUDO_DEBUG_AUTH)
 
     len = snprintf(lecture_status, sizeof(lecture_status), "%s/%s",
         def_lecture_status_dir, user_name);
     if (len <= 0 || (size_t)len >= sizeof(lecture_status)) {
         log_fatal(0, N_("lecture status path too long: %s/%s"),
             def_lecture_status_dir, user_name);
     }
 
     /* Sanity check lecture dir and create if missing. */
     if (!ts_secure_dir(def_lecture_status_dir, true, false))
         goto done;
 
     /* Create lecture file. */
     if (timestamp_uid != 0)
         set_perms(PERM_TIMESTAMP);
     fd = open(lecture_status, O_WRONLY|O_CREAT|O_TRUNC, 0600);
     if (timestamp_uid != 0)
         restore_perms();
     if (fd != -1)
         close(fd);
 
 done:
     debug_return_bool(fd != -1 ? true : false);
 }  }

Removed from v.1.1  
changed lines
  Added in v.1.1.1.3


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