X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=check.c;h=afb6c2267dd9912cb127497e973cb790f4deff78;hb=432c6675f9328fcc59cf9b4ee37a44ca6e2be6ed;hp=4889ac7af96acbdcfb49f6d15f147540561b4d1b;hpb=9de1e8982cc932f7c8cac623135d9a1c8f3082e4;p=debian%2Fsudo diff --git a/check.c b/check.c index 4889ac7..afb6c22 100644 --- a/check.c +++ b/check.c @@ -1,5 +1,6 @@ /* - * Copyright (c) 1993-1996,1998-2005 Todd C. Miller + * Copyright (c) 1993-1996,1998-2005, 2007-2010 + * Todd C. Miller * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -22,7 +23,14 @@ #include #include +#include #include +#ifdef __linux__ +# include +#endif +#if defined(__sun) && defined(__SVR4) +# include +#endif #ifndef __TANDEM # include #endif @@ -37,35 +45,24 @@ #endif /* STDC_HEADERS */ #ifdef HAVE_STRING_H # include -#else -# ifdef HAVE_STRINGS_H -# include -# endif #endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include +#endif /* HAVE_STRINGS_H */ #ifdef HAVE_UNISTD_H # include #endif /* HAVE_UNISTD_H */ -#ifdef HAVE_ERR_H -# include -#else -# include "emul/err.h" -#endif /* HAVE_ERR_H */ +#if TIME_WITH_SYS_TIME +# include +#endif #include #include #include -#include #include #include -#ifndef HAVE_TIMESPEC -# include -#endif #include "sudo.h" -#ifndef lint -__unused static const char rcsid[] = "$Sudo: check.c,v 1.223.2.10 2008/01/05 23:59:42 millert Exp $"; -#endif /* lint */ - /* Status codes for timestamp_status() */ #define TS_CURRENT 0 #define TS_OLD 1 @@ -77,33 +74,83 @@ __unused static const char rcsid[] = "$Sudo: check.c,v 1.223.2.10 2008/01/05 23: #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 void build_timestamp __P((char **, char **)); static int timestamp_status __P((char *, char *, char *, int)); static char *expand_prompt __P((char *, char *, char *)); static void lecture __P((int)); static void update_timestamp __P((char *, char *)); +static int tty_is_devpts __P((const char *)); /* * This function only returns if the user can successfully * verify who he/she is. */ void -check_user(validated) +check_user(validated, mode) int validated; + int mode; { char *timestampdir = NULL; char *timestampfile = NULL; char *prompt; + struct stat sb; int status; - if (user_uid == 0 || user_uid == runas_pw->pw_uid || user_is_exempt()) - return; + /* 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); + } + + /* Always prompt for a password when -k was specified with the command. */ + if (ISSET(mode, MODE_INVALIDATE)) { + SET(validated, FLAG_CHECK_USER); + } else { + if (user_uid == 0 || user_uid == runas_pw->pw_uid || user_is_exempt()) + return; + } build_timestamp(×tampdir, ×tampfile); status = timestamp_status(timestampdir, timestampfile, user_name, TS_MAKE_DIRS); + if (status != TS_CURRENT || ISSET(validated, FLAG_CHECK_USER)) { - lecture(status); + /* Bail out if we are non-interactive and a password is required */ + if (ISSET(mode, MODE_NONINTERACTIVE)) + errorx(1, "sorry, a password is required to run %s", getprogname()); + + /* If user specified -A, make sure we have an askpass helper. */ + if (ISSET(tgetpass_flags, TGP_ASKPASS)) { + if (user_askpass == NULL) + log_error(NO_MAIL, + "no askpass program specified, try setting SUDO_ASKPASS"); + } else if (!ISSET(tgetpass_flags, TGP_STDIN)) { + /* If no tty but DISPLAY is set, use askpass if we have it. */ + if (!user_ttypath && !tty_present()) { + if (user_askpass && user_display && *user_display != '\0') { + SET(tgetpass_flags, TGP_ASKPASS); + } else if (!def_visiblepw) { + log_error(NO_MAIL, + "no tty present and no askpass program specified"); + } + } + } + + if (!ISSET(tgetpass_flags, TGP_ASKPASS)) + lecture(status); /* Expand any escapes in the prompt. */ prompt = expand_prompt(user_prompt ? user_prompt : def_passprompt, @@ -112,7 +159,7 @@ check_user(validated) verify_user(auth_pw, prompt); } /* Only update timestamp if user was validated. */ - if (status != TS_ERROR && ISSET(validated, VALIDATE_OK)) + if (ISSET(validated, VALIDATE_OK) && !ISSET(mode, MODE_INVALIDATE) && status != TS_ERROR) update_timestamp(timestampdir, timestampfile); efree(timestampdir); efree(timestampfile); @@ -120,7 +167,6 @@ check_user(validated) /* * Standard sudo lecture. - * TODO: allow the user to specify a file name instead. */ static void lecture(status) @@ -158,17 +204,26 @@ update_timestamp(timestampdir, timestampfile) 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 (touch(-1, timestampfile ? timestampfile : timestampdir, NULL) == -1) { - if (timestampfile) { - int fd = open(timestampfile, O_WRONLY|O_CREAT|O_TRUNC, 0600); - - if (fd == -1) - log_error(NO_EXIT|USE_ERRNO, "Can't open %s", timestampfile); - else - close(fd); - } else { + 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, "Can't open %s", timestampfile); + else { + lock_file(fd, SUDO_LOCK); + write(fd, &tty_info, sizeof(tty_info)); + close(fd); + } + } else { + if (touch(-1, timestampdir, NULL) == -1) { if (mkdir(timestampdir, 0700) == -1) log_error(NO_EXIT|USE_ERRNO, "Can't mkdir %s", timestampdir); } @@ -211,7 +266,7 @@ expand_prompt(old_prompt, user, host) if (def_rootpw) len += 2; else if (def_targetpw || def_runaspw) - len += strlen(*user_runas) - 2; + len += strlen(runas_pw->pw_name) - 2; else len += strlen(user_name) - 2; subst = 1; @@ -223,7 +278,7 @@ expand_prompt(old_prompt, user, host) break; case 'U': p++; - len += strlen(*user_runas) - 2; + len += strlen(runas_pw->pw_name) - 2; subst = 1; break; case '%': @@ -262,7 +317,7 @@ expand_prompt(old_prompt, user, host) if (def_rootpw) n = strlcpy(np, "root", np - endp); else if (def_targetpw || def_runaspw) - n = strlcpy(np, *user_runas, np - endp); + n = strlcpy(np, runas_pw->pw_name, np - endp); else n = strlcpy(np, user_name, np - endp); if (n >= np - endp) @@ -278,7 +333,7 @@ expand_prompt(old_prompt, user, host) continue; case 'U': p++; - n = strlcpy(np, *user_runas, np - endp); + n = strlcpy(np, runas_pw->pw_name, np - endp); if (n >= np - endp) goto oflow; np += n; @@ -304,7 +359,7 @@ expand_prompt(old_prompt, user, host) oflow: /* We pre-allocate enough space, so this should never happen. */ - errx(1, "internal error, expand_prompt() overflow"); + errorx(1, "internal error, expand_prompt() overflow"); } /* @@ -313,24 +368,9 @@ oflow: int user_is_exempt() { - struct group *grp; - char **gr_mem; - if (!def_exempt_group) return(FALSE); - - if (!(grp = getgrnam(def_exempt_group))) - return(FALSE); - - if (user_gid == grp->gr_gid) - return(TRUE); - - for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) { - if (strcmp(user_name, *gr_mem) == 0) - return(TRUE); - } - - return(FALSE); + return(user_in_group(sudo_user.pw, def_exempt_group)); } /* @@ -362,14 +402,14 @@ build_timestamp(timestampdir, timestampfile) p = user_tty; if (def_targetpw) len = easprintf(timestampfile, "%s/%s/%s:%s", dirparent, user_name, - p, *user_runas); + p, runas_pw->pw_name); else len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, p); if (len >= PATH_MAX) log_error(0, "timestamp path too long: %s", *timestampfile); } else if (def_targetpw) { len = easprintf(timestampfile, "%s/%s/%s", dirparent, user_name, - *user_runas); + runas_pw->pw_name); if (len >= PATH_MAX) log_error(0, "timestamp path too long: %s", *timestampfile); } else @@ -387,6 +427,7 @@ timestamp_status(timestampdir, timestampfile, user, flags) int flags; { struct stat sb; + struct timeval boottime, mtime; time_t now; char *dirparent = def_timestampdir; int status = TS_ERROR; /* assume the worst */ @@ -487,6 +528,8 @@ timestamp_status(timestampdir, timestampfile, user, flags) 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; @@ -510,7 +553,25 @@ timestamp_status(timestampdir, timestampfile, user, flags) if ((sb.st_mode & 0000777) != 0600) (void) chmod(timestampfile, 0600); - status = TS_OLD; /* actually check mtime below */ + /* + * 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) { @@ -523,33 +584,38 @@ timestamp_status(timestampdir, timestampfile, user, flags) * 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 && sb.st_mtime != 0) + if (def_timestamp_timeout < 0 && mtime.tv_sec != 0) status = TS_CURRENT; else { - /* XXX - should use timespec here */ now = time(NULL); if (def_timestamp_timeout && - now - sb.st_mtime < 60 * 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 (sb.st_mtime > now + 60 * def_timestamp_timeout * 2) { + 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(&sb.st_mtime)); + 4 + ctime(&tv_sec)); if (timestampfile) (void) unlink(timestampfile); else (void) rmdir(timestampdir); status = TS_MISSING; - } else + } else if (get_boottime(&boottime) && timevalcmp(&mtime, &boottime, <)) { + status = TS_OLD; + } else { status = TS_CURRENT; + } } } } +done: if (timestamp_uid != 0) set_perms(PERM_ROOT); return(status); @@ -562,7 +628,7 @@ void remove_timestamp(remove) int remove; { - struct timespec ts; + struct timeval tv; char *timestampdir, *timestampfile, *path; int status; @@ -582,12 +648,47 @@ remove_timestamp(remove) remove = FALSE; } } else { - timespecclear(&ts); - if (touch(-1, path, &ts) == -1) - err(1, "can't reset %s to Epoch", path); + timevalclear(&tv); + if (touch(-1, path, &tv) == -1 && errno != ENOENT) + error(1, "can't reset %s to 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(tty) + 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; +}