Diff for /embedaddon/sudo/plugins/sudoers/check.c between versions 1.1.1.1 and 1.1.1.4

version 1.1.1.1, 2012/02/21 16:23:02 version 1.1.1.4, 2013/07/22 10:46:12
Line 1 Line 1
 /*  /*
 * Copyright (c) 1993-1996,1998-2005, 2007-2011 * Copyright (c) 1993-1996,1998-2005, 2007-2013
  *      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
Line 22 Line 22
 #include <config.h>  #include <config.h>
   
 #include <sys/types.h>  #include <sys/types.h>
 #include <sys/param.h>  
 #include <sys/time.h>  #include <sys/time.h>
 #include <sys/stat.h>  
 #ifdef __linux__  
 # include <sys/vfs.h>  
 #endif  
 #if defined(__sun) && defined(__SVR4)  
 # include <sys/statvfs.h>  
 #endif  
 #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 57 Line 46
 #endif  #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 "check.h"
   
/* Status codes for timestamp_status() */static bool display_lecture(int);
#define TS_CURRENT              0 
#define TS_OLD                  1 
#define TS_MISSING              2 
#define TS_NOFILE               3 
#define TS_ERROR                4 
 
/* Flags for timestamp_status() */ 
#define TS_MAKE_DIRS            1 
#define TS_REMOVE               2 
 
/* 
 * Info stored in tty ticket from stat(2) to help with tty matching. 
 */ 
static struct tty_info { 
    dev_t dev;                  /* ID of device tty resides on */ 
    dev_t rdev;                 /* tty device ID */ 
    ino_t ino;                  /* tty inode number */ 
    struct timeval ctime;       /* tty inode change time */ 
} tty_info; 
 
static int   build_timestamp(char **, char **); 
static int   timestamp_status(char *, char *, char *, int); 
static char *expand_prompt(char *, char *, char *); 
static void  lecture(int); 
static void  update_timestamp(char *, char *); 
static int   tty_is_devpts(const char *); 
 static struct passwd *get_authpw(void);  static struct passwd *get_authpw(void);
   
 /*  /*
 * Returns TRUE if the user successfully authenticates, else FALSE. * Returns true if the user successfully authenticates, false if not
  * or -1 on error.
  */   */
intstatic int
check_user(int validated, int mode)check_user_interactive(int validated, int mode, struct passwd *auth_pw)
 {  {
    struct passwd *auth_pw;    int status, rval = true;
    char *timestampdir = NULL;    debug_decl(check_user_interactive, SUDO_DEBUG_AUTH)
    char *timestampfile = NULL; 
    char *prompt; 
    struct stat sb; 
    int status, rval = TRUE; 
    int need_pass = def_authenticate; 
   
    /*    /* Always need a password when -k was specified with the command. */
     * Init authentication system regardless of whether we need a password.    if (ISSET(mode, MODE_IGNORE_TICKET))
     * Required for proper PAM session support.        SET(validated, FLAG_CHECK_USER);
     */ 
    auth_pw = get_authpw(); 
    if (sudo_auth_init(auth_pw) == -1) { 
        rval = -1; 
        goto done; 
    } 
   
    if (need_pass) {    if (build_timestamp(auth_pw) == -1) {
        /* Always need a password when -k was specified with the command. */ 
        if (ISSET(mode, MODE_IGNORE_TICKET)) { 
            SET(validated, FLAG_CHECK_USER); 
        } else { 
            /* 
             * Don't prompt for the root passwd or if the user is exempt. 
             * If the user is not changing uid/gid, no need for a password. 
             */ 
            if (user_uid == 0 || (user_uid == runas_pw->pw_uid && 
                (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name))) 
                || user_is_exempt()) 
                need_pass = FALSE; 
        } 
    } 
    if (!need_pass) 
        goto done; 
 
    /* Stash the tty's ctime for tty ticket comparison. */ 
    if (def_tty_tickets && user_ttypath && stat(user_ttypath, &sb) == 0) { 
        tty_info.dev = sb.st_dev; 
        tty_info.ino = sb.st_ino; 
        tty_info.rdev = sb.st_rdev; 
        if (tty_is_devpts(user_ttypath)) 
            ctim_get(&sb, &tty_info.ctime); 
    } 
 
    if (build_timestamp(&timestampdir, &timestampfile) == -1) { 
         rval = -1;          rval = -1;
         goto done;          goto done;
     }      }
   
    status = timestamp_status(timestampdir, timestampfile, user_name,    status = timestamp_status(auth_pw);
        TS_MAKE_DIRS); 
   
     if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {      if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) {
           char *prompt;
           bool lectured;
   
         /* Bail out if we are non-interactive and a password is required */          /* Bail out if we are non-interactive and a password is required */
         if (ISSET(mode, MODE_NONINTERACTIVE)) {          if (ISSET(mode, MODE_NONINTERACTIVE)) {
            warningx(_("sorry, a password is required to run %s"), getprogname());            validated |= FLAG_NON_INTERACTIVE;
             log_auth_failure(validated, 0);
             rval = -1;              rval = -1;
             goto done;              goto done;
         }          }
   
         /* XXX - should not lecture if askpass helper is being used. */          /* XXX - should not lecture if askpass helper is being used. */
        lecture(status);        lectured = display_lecture(status);
   
         /* Expand any escapes in the prompt. */          /* Expand any escapes in the prompt. */
         prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,          prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt,
             user_name, user_shost);              user_name, user_shost);
   
        rval = verify_user(auth_pw, prompt);        rval = verify_user(auth_pw, prompt, validated);
         if (rval == true && lectured)
             set_lectured();
         efree(prompt);
     }      }
     /* Only update timestamp if user was validated. */      /* Only update timestamp if user was validated. */
    if (rval == TRUE && ISSET(validated, VALIDATE_OK) &&    if (rval == true && ISSET(validated, VALIDATE_OK) &&
         !ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR)          !ISSET(mode, MODE_IGNORE_TICKET) && status != TS_ERROR)
        update_timestamp(timestampdir, timestampfile);        update_timestamp(auth_pw);
    efree(timestampdir);done:
    efree(timestampfile);    debug_return_bool(rval);
 }
   
   /*
    * Returns true if the user successfully authenticates, false if not
    * or -1 on error.
    */
   int
   check_user(int validated, int mode)
   {
       struct passwd *auth_pw;
       int rval = true;
       debug_decl(check_user, SUDO_DEBUG_AUTH)
   
       /*
        * Init authentication system regardless of whether we need a password.
        * Required for proper PAM session support.
        */
       auth_pw = get_authpw();
       if (sudo_auth_init(auth_pw) == -1) {
           rval = -1;
           goto done;
       }
   
       /*
        * Don't prompt for the root passwd or if the user is exempt.
        * If the user is not changing uid/gid, no need for a password.
        */
       if (!def_authenticate || user_uid == 0 || user_is_exempt())
           goto done;
       if (user_uid == runas_pw->pw_uid &&
           (!runas_gr || user_in_group(sudo_user.pw, runas_gr->gr_name))) {
   #ifdef HAVE_SELINUX
           if (user_role == NULL && user_type == NULL)
   #endif
   #ifdef HAVE_PRIV_SET
           if (runas_privs == NULL && runas_limitprivs == NULL)
   #endif
               goto done;
       }
   
       rval = check_user_interactive(validated, mode, auth_pw);
   
 done:  done:
     sudo_auth_cleanup(auth_pw);      sudo_auth_cleanup(auth_pw);
    pw_delref(auth_pw);    sudo_pw_delref(auth_pw);
   
    return rval;    debug_return_bool(rval);
 }  }
   
 #define DEFAULT_LECTURE "\n" \  
     "We trust you have received the usual lecture from the local System\n" \  
     "Administrator. It usually boils down to these three things:\n\n" \  
     "    #1) Respect the privacy of others.\n" \  
     "    #2) Think before you type.\n" \  
     "    #3) With great power comes great responsibility.\n\n"  
   
 /*  /*
 * Standard sudo lecture. * Display sudo lecture (standard or custom).
  * Returns true if the user was lectured, else false.
  */   */
static voidstatic bool
lecture(int status)display_lecture(int status)
 {  {
     FILE *fp;      FILE *fp;
     char buf[BUFSIZ];      char buf[BUFSIZ];
     ssize_t nread;      ssize_t nread;
     struct sudo_conv_message msg;      struct sudo_conv_message msg;
     struct sudo_conv_reply repl;      struct sudo_conv_reply repl;
       debug_decl(lecture, SUDO_DEBUG_AUTH)
   
     if (def_lecture == never ||      if (def_lecture == never ||
        (def_lecture == once && status != TS_MISSING && status != TS_ERROR))        (def_lecture == once && already_lectured(status)))
        return;        debug_return_bool(false);
   
     memset(&msg, 0, sizeof(msg));      memset(&msg, 0, sizeof(msg));
     memset(&repl, 0, sizeof(repl));      memset(&repl, 0, sizeof(repl));
   
     if (def_lecture_file && (fp = fopen(def_lecture_file, "r")) != NULL) {      if (def_lecture_file && (fp = fopen(def_lecture_file, "r")) != NULL) {
         while ((nread = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) != 0) {          while ((nread = fread(buf, sizeof(char), sizeof(buf) - 1, fp)) != 0) {
            buf[sizeof(buf) - 1] = '\0';            buf[nread] = '\0';
             msg.msg_type = SUDO_CONV_ERROR_MSG;              msg.msg_type = SUDO_CONV_ERROR_MSG;
             msg.msg = buf;              msg.msg = buf;
             sudo_conv(1, &msg, &repl);              sudo_conv(1, &msg, &repl);
Line 218  lecture(int status) Line 186  lecture(int status)
         fclose(fp);          fclose(fp);
     } else {      } else {
         msg.msg_type = SUDO_CONV_ERROR_MSG;          msg.msg_type = SUDO_CONV_ERROR_MSG;
        msg.msg = _(DEFAULT_LECTURE);        msg.msg = _("\n"
             "We trust you have received the usual lecture from the local System\n"
             "Administrator. It usually boils down to these three things:\n\n"
             "    #1) Respect the privacy of others.\n"
             "    #2) Think before you type.\n"
             "    #3) With great power comes great responsibility.\n\n");
         sudo_conv(1, &msg, &repl);          sudo_conv(1, &msg, &repl);
     }      }
       debug_return_bool(true);
 }  }
   
 /*  /*
  * Update the time on the timestamp file/dir or create it if necessary.  
  */  
 static void  
 update_timestamp(char *timestampdir, char *timestampfile)  
 {  
     /* If using tty timestamps but we have no tty there is nothing to do. */  
     if (def_tty_tickets && !user_ttypath)  
         return;  
   
     if (timestamp_uid != 0)  
         set_perms(PERM_TIMESTAMP);  
     if (timestampfile) {  
         /*  
          * Store tty info in timestamp file  
          */  
         int fd = open(timestampfile, O_WRONLY|O_CREAT, 0600);  
         if (fd == -1)  
             log_error(NO_EXIT|USE_ERRNO, _("unable to open %s"), timestampfile);  
         else {  
             lock_file(fd, SUDO_LOCK);  
             if (write(fd, &tty_info, sizeof(tty_info)) != sizeof(tty_info)) {  
                 log_error(NO_EXIT|USE_ERRNO, _("unable to write to %s"),  
                     timestampfile);  
             }  
             close(fd);  
         }  
     } else {  
         if (touch(-1, timestampdir, NULL) == -1) {  
             if (mkdir(timestampdir, 0700) == -1) {  
                 log_error(NO_EXIT|USE_ERRNO, _("unable to mkdir %s"),  
                     timestampdir);  
             }  
         }  
     }  
     if (timestamp_uid != 0)  
         restore_perms();  
 }  
   
 /*  
  * Expand %h and %u escapes in the prompt and pass back the dynamically  
  * allocated result.  Returns the same string if there are no escapes.  
  */  
 static char *  
 expand_prompt(char *old_prompt, char *user, char *host)  
 {  
     size_t len, n;  
     int subst;  
     char *p, *np, *new_prompt, *endp;  
   
     /* How much space do we need to malloc for the prompt? */  
     subst = 0;  
     for (p = old_prompt, len = strlen(old_prompt); *p; p++) {  
         if (p[0] =='%') {  
             switch (p[1]) {  
                 case 'h':  
                     p++;  
                     len += strlen(user_shost) - 2;  
                     subst = 1;  
                     break;  
                 case 'H':  
                     p++;  
                     len += strlen(user_host) - 2;  
                     subst = 1;  
                     break;  
                 case 'p':  
                     p++;  
                     if (def_rootpw)  
                             len += 2;  
                     else if (def_targetpw || def_runaspw)  
                             len += strlen(runas_pw->pw_name) - 2;  
                     else  
                             len += strlen(user_name) - 2;  
                     subst = 1;  
                     break;  
                 case 'u':  
                     p++;  
                     len += strlen(user_name) - 2;  
                     subst = 1;  
                     break;  
                 case 'U':  
                     p++;  
                     len += strlen(runas_pw->pw_name) - 2;  
                     subst = 1;  
                     break;  
                 case '%':  
                     p++;  
                     len--;  
                     subst = 1;  
                     break;  
                 default:  
                     break;  
             }  
         }  
     }  
   
     if (subst) {  
         new_prompt = emalloc(++len);  
         endp = new_prompt + len;  
         for (p = old_prompt, np = new_prompt; *p; p++) {  
             if (p[0] =='%') {  
                 switch (p[1]) {  
                     case 'h':  
                         p++;  
                         n = strlcpy(np, user_shost, np - endp);  
                         if (n >= np - endp)  
                             goto oflow;  
                         np += n;  
                         continue;  
                     case 'H':  
                         p++;  
                         n = strlcpy(np, user_host, np - endp);  
                         if (n >= np - endp)  
                             goto oflow;  
                         np += n;  
                         continue;  
                     case 'p':  
                         p++;  
                         if (def_rootpw)  
                                 n = strlcpy(np, "root", np - endp);  
                         else if (def_targetpw || def_runaspw)  
                                 n = strlcpy(np, runas_pw->pw_name, np - endp);  
                         else  
                                 n = strlcpy(np, user_name, np - endp);  
                         if (n >= np - endp)  
                                 goto oflow;  
                         np += n;  
                         continue;  
                     case 'u':  
                         p++;  
                         n = strlcpy(np, user_name, np - endp);  
                         if (n >= np - endp)  
                             goto oflow;  
                         np += n;  
                         continue;  
                     case 'U':  
                         p++;  
                         n = strlcpy(np,  runas_pw->pw_name, np - endp);  
                         if (n >= np - endp)  
                             goto oflow;  
                         np += n;  
                         continue;  
                     case '%':  
                         /* convert %% -> % */  
                         p++;  
                         break;  
                     default:  
                         /* no conversion */  
                         break;  
                 }  
             }  
             *np++ = *p;  
             if (np >= endp)  
                 goto oflow;  
         }  
         *np = '\0';  
     } else  
         new_prompt = old_prompt;  
   
     return new_prompt;  
   
 oflow:  
     /* We pre-allocate enough space, so this should never happen. */  
     errorx(1, _("internal error, expand_prompt() overflow"));  
 }  
   
 /*  
  * Checks if the user is exempt from supplying a password.   * Checks if the user is exempt from supplying a password.
  */   */
intbool
 user_is_exempt(void)  user_is_exempt(void)
 {  {
    if (!def_exempt_group)    bool rval = false;
        return FALSE;    debug_decl(user_is_exempt, SUDO_DEBUG_AUTH)
    return user_in_group(sudo_user.pw, def_exempt_group); 
} 
   
/*    if (def_exempt_group)
 * Fills in timestampdir as well as timestampfile if using tty tickets.        rval = user_in_group(sudo_user.pw, def_exempt_group);
 */    debug_return_bool(rval);
static int 
build_timestamp(char **timestampdir, char **timestampfile) 
{ 
    char *dirparent; 
    int len; 
 
    dirparent = def_timestampdir; 
    len = easprintf(timestampdir, "%s/%s", dirparent, user_name); 
    if (len >= PATH_MAX) 
        goto bad; 
 
    /* 
     * Timestamp file may be a file in the directory or NUL to use 
     * the directory as the timestamp. 
     */ 
    if (def_tty_tickets) { 
        char *p; 
 
        if ((p = strrchr(user_tty, '/'))) 
            p++; 
        else 
            p = user_tty; 
        if (def_targetpw) 
            len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name, 
                p, runas_pw->pw_name); 
        else 
            len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p); 
        if (len >= PATH_MAX) 
            goto bad; 
    } else if (def_targetpw) { 
        len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, 
            runas_pw->pw_name); 
        if (len >= PATH_MAX) 
            goto bad; 
    } else 
        *timestampfile = NULL; 
 
    return len; 
bad: 
    log_error(0, _("timestamp path too long: %s"), *timestampfile); 
    return -1; 
 }  }
   
 /*  /*
  * Check the timestamp file and directory and return their status.  
  */  
 static int  
 timestamp_status(char *timestampdir, char *timestampfile, char *user, int flags)  
 {  
     struct stat sb;  
     struct timeval boottime, mtime;  
     time_t now;  
     char *dirparent = def_timestampdir;  
     int status = TS_ERROR;              /* assume the worst */  
   
     if (timestamp_uid != 0)  
         set_perms(PERM_TIMESTAMP);  
   
     /*  
      * Sanity check dirparent and make it if it doesn't already exist.  
      * We start out assuming the worst (that the dir is not sane) and  
      * 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) {  
         if (!S_ISDIR(sb.st_mode))  
             log_error(NO_EXIT, _("%s exists but is not a directory (0%o)"),  
                 dirparent, (unsigned int) sb.st_mode);  
         else if (sb.st_uid != timestamp_uid)  
             log_error(NO_EXIT, _("%s owned by uid %u, should be uid %u"),  
                 dirparent, (unsigned int) sb.st_uid,  
                 (unsigned int) timestamp_uid);  
         else if ((sb.st_mode & 0000022))  
             log_error(NO_EXIT,  
                 _("%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_error(NO_EXIT|USE_ERRNO, _("unable to stat %s"), dirparent);  
     } else {  
         /* No dirparent, try to make one. */  
         if (ISSET(flags, TS_MAKE_DIRS)) {  
             if (mkdir(dirparent, S_IRWXU))  
                 log_error(NO_EXIT|USE_ERRNO, _("unable to mkdir %s"),  
                     dirparent);  
             else  
                 status = TS_MISSING;  
         }  
     }  
     if (status == TS_ERROR)  
         goto done;  
   
     /*  
      * Sanity check the user's ticket dir.  We start by downgrading  
      * the status to TS_ERROR.  If the ticket dir exists and is sane  
      * this will be upgraded to TS_OLD.  If the dir does not exist,  
      * it will be upgraded to TS_MISSING.  
      */  
     status = TS_ERROR;                  /* downgrade status again */  
     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_error(NO_EXIT, _("%s exists but is not a directory (0%o)"),  
                     timestampdir, (unsigned int) sb.st_mode);  
         } else if (sb.st_uid != timestamp_uid)  
             log_error(NO_EXIT, _("%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_error(NO_EXIT,  
                 _("%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_error(NO_EXIT|USE_ERRNO, _("unable to stat %s"), timestampdir);  
     } else  
         status = TS_MISSING;  
   
     /*  
      * If there is no user ticket dir, AND we are in tty ticket mode,  
      * AND the TS_MAKE_DIRS flag is set, create the user ticket dir.  
      */  
     if (status == TS_MISSING && timestampfile && ISSET(flags, TS_MAKE_DIRS)) {  
         if (mkdir(timestampdir, S_IRWXU) == -1) {  
             status = TS_ERROR;  
             log_error(NO_EXIT|USE_ERRNO, _("unable to mkdir %s"), timestampdir);  
         }  
     }  
   
     /*  
      * Sanity check the tty ticket file if it exists.  
      */  
     if (timestampfile && status != TS_ERROR) {  
         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_error(NO_EXIT, _("%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_error(NO_EXIT,  
                         _("%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_error(NO_EXIT,  
                         _("%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);  
   
                     /*  
                      * Check for stored tty info.  If the file is zero-sized  
                      * it is an old-style timestamp with no tty info in it.  
                      * If removing, we don't care about the contents.  
                      * The actual mtime check is done later.  
                      */  
                     if (ISSET(flags, TS_REMOVE)) {  
                         status = TS_OLD;  
                     } else if (sb.st_size != 0) {  
                         struct 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_error(NO_EXIT|USE_ERRNO, _("unable to stat %s"), timestampfile);  
             status = TS_ERROR;  
         }  
     }  
   
     /*  
      * If the file/dir exists and we are not removing it, check its mtime.  
      */  
     if (status == TS_OLD && !ISSET(flags, TS_REMOVE)) {  
         mtim_get(&sb, &mtime);  
         /* Negative timeouts only expire manually (sudo -k). */  
         if (def_timestamp_timeout < 0 && mtime.tv_sec != 0)  
             status = TS_CURRENT;  
         else {  
             now = time(NULL);  
             if (def_timestamp_timeout &&  
                 now - mtime.tv_sec < 60 * def_timestamp_timeout) {  
                 /*  
                  * Check for bogus time on the stampfile.  The clock may  
                  * have been set back or someone could be trying to spoof us.  
                  */  
                 if (mtime.tv_sec > now + 60 * def_timestamp_timeout * 2) {  
                     time_t tv_sec = (time_t)mtime.tv_sec;  
                     log_error(NO_EXIT,  
                         _("timestamp too far in the future: %20.20s"),  
                         4 + ctime(&tv_sec));  
                     if (timestampfile)  
                         (void) unlink(timestampfile);  
                     else  
                         (void) rmdir(timestampdir);  
                     status = TS_MISSING;  
                 } else if (get_boottime(&boottime) && timevalcmp(&mtime, &boottime, <)) {  
                     status = TS_OLD;  
                 } else {  
                     status = TS_CURRENT;  
                 }  
             }  
         }  
     }  
   
 done:  
     if (timestamp_uid != 0)  
         restore_perms();  
     return status;  
 }  
   
 /*  
  * Remove the timestamp ticket file/dir.  
  */  
 void  
 remove_timestamp(int remove)  
 {  
     struct timeval tv;  
     char *timestampdir, *timestampfile, *path;  
     int status;  
   
     if (build_timestamp(&timestampdir, &timestampfile) == -1)  
         return;  
   
     status = timestamp_status(timestampdir, timestampfile, user_name,  
         TS_REMOVE);  
     if (status != TS_MISSING && status != TS_ERROR) {  
         path = timestampfile ? timestampfile : timestampdir;  
         if (remove) {  
             if (timestampfile)  
                 status = unlink(timestampfile);  
             else  
                 status = rmdir(timestampdir);  
             if (status == -1 && errno != ENOENT) {  
                 log_error(NO_EXIT,  
                     _("unable to remove %s (%s), will reset to the epoch"),  
                     path, strerror(errno));  
                 remove = FALSE;  
             }  
         }  
         if (!remove) {  
             timevalclear(&tv);  
             if (touch(-1, path, &tv) == -1 && errno != ENOENT)  
                 error(1, _("unable to reset %s to the epoch"), path);  
         }  
     }  
   
     efree(timestampdir);  
     efree(timestampfile);  
 }  
   
 /*  
  * Returns TRUE if tty lives on a devpts or /devices filesystem, else FALSE.  
  * Unlike most filesystems, the ctime of devpts nodes is not updated when  
  * the device node is written to, only when the inode's status changes,  
  * typically via the chmod, chown, link, rename, or utimes system calls.  
  * Since the ctime is "stable" in this case, we can stash it the tty ticket  
  * file and use it to determine whether the tty ticket file is stale.  
  */  
 static int  
 tty_is_devpts(const char *tty)  
 {  
     int retval = FALSE;  
 #ifdef __linux__  
     struct statfs sfs;  
   
 #ifndef DEVPTS_SUPER_MAGIC  
 # define DEVPTS_SUPER_MAGIC 0x1cd1  
 #endif  
   
     if (statfs(tty, &sfs) == 0) {  
         if (sfs.f_type == DEVPTS_SUPER_MAGIC)  
             retval = TRUE;  
     }  
 #elif defined(__sun) && defined(__SVR4)  
     struct statvfs sfs;  
   
     if (statvfs(tty, &sfs) == 0) {  
         if (strcmp(sfs.f_fstr, "devices") == 0)  
             retval = TRUE;  
     }  
 #endif /* __linux__ */  
     return retval;  
 }  
   
 /*  
  * Get passwd entry for the user we are going to authenticate as.   * Get passwd entry for the user we are going to authenticate as.
  * By default, this is the user invoking sudo.  In the most common   * By default, this is the user invoking sudo.  In the most common
  * case, this matches sudo_user.pw or runas_pw.   * case, this matches sudo_user.pw or runas_pw.
Line 727  static struct passwd * Line 220  static struct passwd *
 get_authpw(void)  get_authpw(void)
 {  {
     struct passwd *pw;      struct passwd *pw;
       debug_decl(get_authpw, SUDO_DEBUG_AUTH)
   
     if (def_rootpw) {      if (def_rootpw) {
         if ((pw = sudo_getpwuid(ROOT_UID)) == NULL)          if ((pw = sudo_getpwuid(ROOT_UID)) == NULL)
            log_error(0, _("unknown uid: %u"), ROOT_UID);            log_fatal(0, N_("unknown uid: %u"), ROOT_UID);
     } else if (def_runaspw) {      } else if (def_runaspw) {
         if ((pw = sudo_getpwnam(def_runas_default)) == NULL)          if ((pw = sudo_getpwnam(def_runas_default)) == NULL)
            log_error(0, _("unknown user: %s"), def_runas_default);            log_fatal(0, N_("unknown user: %s"), def_runas_default);
     } else if (def_targetpw) {      } else if (def_targetpw) {
         if (runas_pw->pw_name == NULL)          if (runas_pw->pw_name == NULL)
            log_error(NO_MAIL|MSG_ONLY, _("unknown uid: %u"),            log_fatal(NO_MAIL|MSG_ONLY, N_("unknown uid: %u"),
                 (unsigned int) runas_pw->pw_uid);                  (unsigned int) runas_pw->pw_uid);
        pw_addref(runas_pw);        sudo_pw_addref(runas_pw);
         pw = runas_pw;          pw = runas_pw;
     } else {      } else {
        pw_addref(sudo_user.pw);        sudo_pw_addref(sudo_user.pw);
         pw = sudo_user.pw;          pw = sudo_user.pw;
     }      }
   
    return pw;    debug_return_ptr(pw);
 }  }

Removed from v.1.1.1.1  
changed lines
  Added in v.1.1.1.4


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