From: Todd C. Miller Date: Tue, 24 Aug 2004 18:01:14 +0000 (+0000) Subject: Add cmnd_base to struct sudo_user and set it in init_vars(). X-Git-Tag: SUDO_1_6_8p1~52 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f30ab72c445dbaf08f1b5aa1a5b49a2fdbed483a;p=sudo Add cmnd_base to struct sudo_user and set it in init_vars(). Add cmnd_stat to struct sudo_user and set it in sudo_goodpath(). No longer use gross statics in command_matches(). Also rename some variables for improved clarity. --- diff --git a/TODO b/TODO index 6ff07c711..89d98846f 100644 --- a/TODO +++ b/TODO @@ -127,17 +127,15 @@ TODO list (most will be addressed in sudo 2.0) line and that have a constant record length (sparse files) for easy seeking. -46) Move cmnd_base setting and stashing of stat info from parse.c to sudo.c - -47) Investigate using glob(3) instead of fnmatch(3) for path matching. That +46) Investigate using glob(3) instead of fnmatch(3) for path matching. That way we can stat each potential match like we normally would. Patterns ending in '/*' can be replaced with '/basename' as an optimization. -48) Some way of using a new pty for the program run via sudo would prevent +47) Some way of using a new pty for the program run via sudo would prevent access to the caller's /dev/tty (but probably makes job control tricky). -49) Maybe have a database of checksums that commands are verified against. +48) Maybe have a database of checksums that commands are verified against. Basically replace the st_ino/st_dev check with a checksum lookup. -50) Look into testing writability of a file via sudoedit *before* doing +49) Look into testing writability of a file via sudoedit *before* doing the edit; e.g., try opening with O_APPEND. diff --git a/find_path.c b/find_path.c index d362de77d..c141e4ef5 100644 --- a/find_path.c +++ b/find_path.c @@ -62,9 +62,10 @@ static const char rcsid[] = "$Sudo$"; * but it is in '.' and IGNORE_DOT is set. */ int -find_path(infile, outfile, path) +find_path(infile, outfile, sbp, path) char *infile; /* file to find */ char **outfile; /* result parameter */ + struct stat *sbp; /* stat result parameter */ char *path; /* path to search */ { static char command[PATH_MAX]; /* qualified filename */ @@ -83,7 +84,7 @@ find_path(infile, outfile, path) */ if (strchr(infile, '/')) { strlcpy(command, infile, sizeof(command)); /* paranoia */ - if (sudo_goodpath(command)) { + if (sudo_goodpath(command, sbp)) { *outfile = command; return(FOUND); } else @@ -120,7 +121,7 @@ find_path(infile, outfile, path) len = snprintf(command, sizeof(command), "%s/%s", path, infile); if (len <= 0 || len >= sizeof(command)) errx(1, "%s: File name too long", infile); - if ((result = sudo_goodpath(command))) + if ((result = sudo_goodpath(command, sbp))) break; path = n + 1; @@ -132,7 +133,7 @@ find_path(infile, outfile, path) * Check current dir if dot was in the PATH */ if (!result && checkdot) { - result = sudo_goodpath(infile); + result = sudo_goodpath(infile, sbp); if (result && def_ignore_dot) return(NOT_FOUND_DOT); } diff --git a/goodpath.c b/goodpath.c index 337995e66..fdfb50151 100644 --- a/goodpath.c +++ b/goodpath.c @@ -47,8 +47,9 @@ static const char rcsid[] = "$Sudo$"; * Verify that path is a normal file and executable by root. */ char * -sudo_goodpath(path) +sudo_goodpath(path, sbp) const char *path; + struct stat *sbp; { struct stat sb; @@ -65,5 +66,7 @@ sudo_goodpath(path) return(NULL); } + if (sbp != NULL) + (void) memcpy(sbp, &sb, sizeof(struct stat)); return((char *)path); } diff --git a/parse.c b/parse.c index 39e8484a5..d7eb8dbdb 100644 --- a/parse.c +++ b/parse.c @@ -230,27 +230,25 @@ sudoers_lookup(pwflag) * otherwise, return TRUE if user_cmnd names one of the inodes in path. */ int -command_matches(path, sudoers_args) - char *path; +command_matches(sudoers_cmnd, sudoers_args) + char *sudoers_cmnd; char *sudoers_args; { - int plen; - static struct stat cst; - struct stat pst; - DIR *dirp; + struct stat sudoers_stat; struct dirent *dent; char buf[PATH_MAX]; - static char *cmnd_base; + DIR *dirp; /* Check for pseudo-commands */ if (strchr(user_cmnd, '/') == NULL) { /* - * Return true if both path and user_cmnd are "sudoedit" AND + * Return true if both sudoers_cmnd and user_cmnd are "sudoedit" AND * a) there are no args in sudoers OR * b) there are no args on command line and none req by sudoers OR * c) there are args in sudoers and on command line and they match */ - if (strcmp(path, "sudoedit") != 0 || strcmp(user_cmnd, "sudoedit") != 0) + if (strcmp(sudoers_cmnd, "sudoedit") != 0 || + strcmp(user_cmnd, "sudoedit") != 0) return(FALSE); if (!sudoers_args || (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || @@ -258,29 +256,17 @@ command_matches(path, sudoers_args) fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { if (safe_cmnd) free(safe_cmnd); - safe_cmnd = estrdup(path); + safe_cmnd = estrdup(sudoers_cmnd); return(TRUE); } else return(FALSE); } - plen = strlen(path); - - /* Only need to stat user_cmnd and set base once since it never changes */ - if (cmnd_base == NULL) { - if (stat(user_cmnd, &cst) == -1) - return(FALSE); - if ((cmnd_base = strrchr(user_cmnd, '/')) == NULL) - cmnd_base = user_cmnd; - else - cmnd_base++; - } - /* - * If the pathname has meta characters in it use fnmatch(3) - * to do the matching + * If sudoers_cmnd has meta characters in it, use fnmatch(3) + * to do the matching. */ - if (has_meta(path)) { + if (has_meta(sudoers_cmnd)) { /* * Return true if fnmatch(3) succeeds AND * a) there are no args in sudoers OR @@ -288,7 +274,7 @@ command_matches(path, sudoers_args) * c) there are args in sudoers and on command line and they match * else return false. */ - if (fnmatch(path, user_cmnd, FNM_PATHNAME) != 0) + if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0) return(FALSE); if (!sudoers_args || (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || @@ -301,19 +287,22 @@ command_matches(path, sudoers_args) } else return(FALSE); } else { + size_t dlen = strlen(sudoers_cmnd); + /* * No meta characters * Check to make sure this is not a directory spec (doesn't end in '/') */ - if (path[plen - 1] != '/') { - char *p; + if (sudoers_cmnd[dlen - 1] != '/') { + char *base; - /* Only proceed if cmnd_base and basename(path) are the same */ - if ((p = strrchr(path, '/')) == NULL) - p = path; + /* Only proceed if user_base and basename(sudoers_cmnd) match */ + if ((base = strrchr(sudoers_cmnd, '/')) == NULL) + base = sudoers_cmnd; else - p++; - if (strcmp(cmnd_base, p) != 0 || stat(path, &pst) == -1) + base++; + if (strcmp(user_base, base) != 0 || + stat(sudoers_cmnd, &sudoers_stat) == -1) return(FALSE); /* @@ -322,7 +311,8 @@ command_matches(path, sudoers_args) * b) there are no args on command line and none req by sudoers OR * c) there are args in sudoers and on command line and they match */ - if (cst.st_dev != pst.st_dev || cst.st_ino != pst.st_ino) + if (user_stat->st_dev != sudoers_stat.st_dev || + user_stat->st_ino != sudoers_stat.st_ino) return(FALSE); if (!sudoers_args || (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || @@ -330,31 +320,33 @@ command_matches(path, sudoers_args) fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { if (safe_cmnd) free(safe_cmnd); - safe_cmnd = estrdup(path); + safe_cmnd = estrdup(sudoers_cmnd); return(TRUE); } else return(FALSE); } /* - * Grot through path's directory entries, looking for cmnd_base. + * Grot through sudoers_cmnd's directory entries, looking for user_base. */ - dirp = opendir(path); + dirp = opendir(sudoers_cmnd); if (dirp == NULL) return(FALSE); - if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) + if (strlcpy(buf, sudoers_cmnd, sizeof(buf)) >= sizeof(buf)) return(FALSE); while ((dent = readdir(dirp)) != NULL) { /* ignore paths > PATH_MAX (XXX - log) */ - buf[plen] = '\0'; + buf[dlen] = '\0'; if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf)) continue; /* only stat if basenames are the same */ - if (strcmp(cmnd_base, dent->d_name) != 0 || stat(buf, &pst) == -1) + if (strcmp(user_base, dent->d_name) != 0 || + stat(buf, &sudoers_stat) == -1) continue; - if (cst.st_dev == pst.st_dev && cst.st_ino == pst.st_ino) { + if (user_stat->st_dev == sudoers_stat.st_dev && + user_stat->st_ino == sudoers_stat.st_ino) { if (safe_cmnd) free(safe_cmnd); safe_cmnd = estrdup(buf); diff --git a/sudo.c b/sudo.c index 90ddfd31a..daa77d5f8 100644 --- a/sudo.c +++ b/sudo.c @@ -622,16 +622,17 @@ init_vars(sudo_mode) /* Resolve the path and return. */ rval = FOUND; + user_stat = emalloc(sizeof(struct stat)); if (sudo_mode & (MODE_RUN | MODE_EDIT)) { if (ISSET(sudo_mode, MODE_RUN)) { /* XXX - default_runas may be modified during parsing of sudoers */ set_perms(PERM_RUNAS); - rval = find_path(NewArgv[0], &user_cmnd, user_path); + rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path); set_perms(PERM_ROOT); if (rval != FOUND) { /* Failed as root, try as invoking user. */ set_perms(PERM_USER); - rval = find_path(NewArgv[0], &user_cmnd, user_path); + rval = find_path(NewArgv[0], &user_cmnd, user_stat, user_path); set_perms(PERM_ROOT); } } @@ -662,6 +663,10 @@ init_vars(sudo_mode) *--to = '\0'; } } + if ((user_base = strrchr(user_cmnd, '/')) != NULL) + user_base++; + else + user_base = user_cmnd; return(rval); } diff --git a/sudo.h b/sudo.h index 2d21bccd2..2f7e68c3e 100644 --- a/sudo.h +++ b/sudo.h @@ -35,6 +35,7 @@ struct sudo_user { struct passwd *pw; struct passwd *_runas_pw; + struct stat *cmnd_stat; char *path; char *shell; char *tty; @@ -43,9 +44,10 @@ struct sudo_user { char *shost; char **runas; char *prompt; - char *cmnd_safe; char *cmnd; char *cmnd_args; + char *cmnd_base; + char *cmnd_safe; char *class_name; }; @@ -128,6 +130,8 @@ struct sudo_user { #define user_runas (sudo_user.runas) #define user_cmnd (sudo_user.cmnd) #define user_args (sudo_user.cmnd_args) +#define user_base (sudo_user.cmnd_base) +#define user_stat (sudo_user.cmnd_stat) #define user_path (sudo_user.path) #define user_prompt (sudo_user.prompt) #define user_host (sudo_user.host) @@ -189,9 +193,9 @@ size_t strlcat __P((char *, const char *, size_t)); #ifndef HAVE_STRLCPY size_t strlcpy __P((char *, const char *, size_t)); #endif -char *sudo_goodpath __P((const char *)); +char *sudo_goodpath __P((const char *, struct stat *)); char *tgetpass __P((const char *, int, int)); -int find_path __P((char *, char **, char *)); +int find_path __P((char *, char **, struct stat *, char *)); void check_user __P((int)); void verify_user __P((struct passwd *, char *)); int sudoers_lookup __P((int)); diff --git a/visudo.c b/visudo.c index 65a10b6c3..25efc5a77 100644 --- a/visudo.c +++ b/visudo.c @@ -248,7 +248,7 @@ main(argc, argv) if (UserEditor && *UserEditor == '\0') UserEditor = NULL; else if (UserEditor) { - if (find_path(UserEditor, &Editor, getenv("PATH")) == FOUND) { + if (find_path(UserEditor, &Editor, NULL, getenv("PATH")) == FOUND) { UserEditor = Editor; } else { if (def_env_editor) { @@ -318,7 +318,7 @@ main(argc, argv) EditorPath = estrdup(def_editor); Editor = strtok(EditorPath, ":"); do { - if (sudo_goodpath(Editor)) + if (sudo_goodpath(Editor, NULL)) break; } while ((Editor = strtok(NULL, ":")));