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.
This commit is contained in:
Todd C. Miller
2004-08-24 18:01:14 +00:00
parent badd06688f
commit f30ab72c44
7 changed files with 62 additions and 59 deletions

10
TODO
View File

@@ -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 line and that have a constant record length (sparse files) for
easy seeking. easy seeking.
46) Move cmnd_base setting and stashing of stat info from parse.c to sudo.c 46) Investigate using glob(3) instead of fnmatch(3) for path matching. That
47) Investigate using glob(3) instead of fnmatch(3) for path matching. That
way we can stat each potential match like we normally would. Patterns way we can stat each potential match like we normally would. Patterns
ending in '/*' can be replaced with '/basename' as an optimization. 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). 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. 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. the edit; e.g., try opening with O_APPEND.

View File

@@ -62,9 +62,10 @@ static const char rcsid[] = "$Sudo$";
* but it is in '.' and IGNORE_DOT is set. * but it is in '.' and IGNORE_DOT is set.
*/ */
int int
find_path(infile, outfile, path) find_path(infile, outfile, sbp, path)
char *infile; /* file to find */ char *infile; /* file to find */
char **outfile; /* result parameter */ char **outfile; /* result parameter */
struct stat *sbp; /* stat result parameter */
char *path; /* path to search */ char *path; /* path to search */
{ {
static char command[PATH_MAX]; /* qualified filename */ static char command[PATH_MAX]; /* qualified filename */
@@ -83,7 +84,7 @@ find_path(infile, outfile, path)
*/ */
if (strchr(infile, '/')) { if (strchr(infile, '/')) {
strlcpy(command, infile, sizeof(command)); /* paranoia */ strlcpy(command, infile, sizeof(command)); /* paranoia */
if (sudo_goodpath(command)) { if (sudo_goodpath(command, sbp)) {
*outfile = command; *outfile = command;
return(FOUND); return(FOUND);
} else } else
@@ -120,7 +121,7 @@ find_path(infile, outfile, path)
len = snprintf(command, sizeof(command), "%s/%s", path, infile); len = snprintf(command, sizeof(command), "%s/%s", path, infile);
if (len <= 0 || len >= sizeof(command)) if (len <= 0 || len >= sizeof(command))
errx(1, "%s: File name too long", infile); errx(1, "%s: File name too long", infile);
if ((result = sudo_goodpath(command))) if ((result = sudo_goodpath(command, sbp)))
break; break;
path = n + 1; path = n + 1;
@@ -132,7 +133,7 @@ find_path(infile, outfile, path)
* Check current dir if dot was in the PATH * Check current dir if dot was in the PATH
*/ */
if (!result && checkdot) { if (!result && checkdot) {
result = sudo_goodpath(infile); result = sudo_goodpath(infile, sbp);
if (result && def_ignore_dot) if (result && def_ignore_dot)
return(NOT_FOUND_DOT); return(NOT_FOUND_DOT);
} }

View File

@@ -47,8 +47,9 @@ static const char rcsid[] = "$Sudo$";
* Verify that path is a normal file and executable by root. * Verify that path is a normal file and executable by root.
*/ */
char * char *
sudo_goodpath(path) sudo_goodpath(path, sbp)
const char *path; const char *path;
struct stat *sbp;
{ {
struct stat sb; struct stat sb;
@@ -65,5 +66,7 @@ sudo_goodpath(path)
return(NULL); return(NULL);
} }
if (sbp != NULL)
(void) memcpy(sbp, &sb, sizeof(struct stat));
return((char *)path); return((char *)path);
} }

74
parse.c
View File

@@ -230,27 +230,25 @@ sudoers_lookup(pwflag)
* otherwise, return TRUE if user_cmnd names one of the inodes in path. * otherwise, return TRUE if user_cmnd names one of the inodes in path.
*/ */
int int
command_matches(path, sudoers_args) command_matches(sudoers_cmnd, sudoers_args)
char *path; char *sudoers_cmnd;
char *sudoers_args; char *sudoers_args;
{ {
int plen; struct stat sudoers_stat;
static struct stat cst;
struct stat pst;
DIR *dirp;
struct dirent *dent; struct dirent *dent;
char buf[PATH_MAX]; char buf[PATH_MAX];
static char *cmnd_base; DIR *dirp;
/* Check for pseudo-commands */ /* Check for pseudo-commands */
if (strchr(user_cmnd, '/') == NULL) { 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 * a) there are no args in sudoers OR
* b) there are no args on command line and none req by 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 * 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); return(FALSE);
if (!sudoers_args || if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", 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)) { fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
if (safe_cmnd) if (safe_cmnd)
free(safe_cmnd); free(safe_cmnd);
safe_cmnd = estrdup(path); safe_cmnd = estrdup(sudoers_cmnd);
return(TRUE); return(TRUE);
} else } else
return(FALSE); 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) * If sudoers_cmnd has meta characters in it, use fnmatch(3)
* to do the matching * to do the matching.
*/ */
if (has_meta(path)) { if (has_meta(sudoers_cmnd)) {
/* /*
* Return true if fnmatch(3) succeeds AND * Return true if fnmatch(3) succeeds AND
* a) there are no args in sudoers OR * 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 * c) there are args in sudoers and on command line and they match
* else return false. * else return false.
*/ */
if (fnmatch(path, user_cmnd, FNM_PATHNAME) != 0) if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0)
return(FALSE); return(FALSE);
if (!sudoers_args || if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) ||
@@ -301,19 +287,22 @@ command_matches(path, sudoers_args)
} else } else
return(FALSE); return(FALSE);
} else { } else {
size_t dlen = strlen(sudoers_cmnd);
/* /*
* No meta characters * No meta characters
* Check to make sure this is not a directory spec (doesn't end in '/') * Check to make sure this is not a directory spec (doesn't end in '/')
*/ */
if (path[plen - 1] != '/') { if (sudoers_cmnd[dlen - 1] != '/') {
char *p; char *base;
/* Only proceed if cmnd_base and basename(path) are the same */ /* Only proceed if user_base and basename(sudoers_cmnd) match */
if ((p = strrchr(path, '/')) == NULL) if ((base = strrchr(sudoers_cmnd, '/')) == NULL)
p = path; base = sudoers_cmnd;
else else
p++; base++;
if (strcmp(cmnd_base, p) != 0 || stat(path, &pst) == -1) if (strcmp(user_base, base) != 0 ||
stat(sudoers_cmnd, &sudoers_stat) == -1)
return(FALSE); 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 * 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 * 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); return(FALSE);
if (!sudoers_args || if (!sudoers_args ||
(!user_args && sudoers_args && !strcmp("\"\"", 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)) { fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) {
if (safe_cmnd) if (safe_cmnd)
free(safe_cmnd); free(safe_cmnd);
safe_cmnd = estrdup(path); safe_cmnd = estrdup(sudoers_cmnd);
return(TRUE); return(TRUE);
} else } else
return(FALSE); 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) if (dirp == NULL)
return(FALSE); return(FALSE);
if (strlcpy(buf, path, sizeof(buf)) >= sizeof(buf)) if (strlcpy(buf, sudoers_cmnd, sizeof(buf)) >= sizeof(buf))
return(FALSE); return(FALSE);
while ((dent = readdir(dirp)) != NULL) { while ((dent = readdir(dirp)) != NULL) {
/* ignore paths > PATH_MAX (XXX - log) */ /* ignore paths > PATH_MAX (XXX - log) */
buf[plen] = '\0'; buf[dlen] = '\0';
if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf)) if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
continue; continue;
/* only stat if basenames are the same */ /* 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; 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) if (safe_cmnd)
free(safe_cmnd); free(safe_cmnd);
safe_cmnd = estrdup(buf); safe_cmnd = estrdup(buf);

9
sudo.c
View File

@@ -622,16 +622,17 @@ init_vars(sudo_mode)
/* Resolve the path and return. */ /* Resolve the path and return. */
rval = FOUND; rval = FOUND;
user_stat = emalloc(sizeof(struct stat));
if (sudo_mode & (MODE_RUN | MODE_EDIT)) { if (sudo_mode & (MODE_RUN | MODE_EDIT)) {
if (ISSET(sudo_mode, MODE_RUN)) { if (ISSET(sudo_mode, MODE_RUN)) {
/* XXX - default_runas may be modified during parsing of sudoers */ /* XXX - default_runas may be modified during parsing of sudoers */
set_perms(PERM_RUNAS); 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); set_perms(PERM_ROOT);
if (rval != FOUND) { if (rval != FOUND) {
/* Failed as root, try as invoking user. */ /* Failed as root, try as invoking user. */
set_perms(PERM_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); set_perms(PERM_ROOT);
} }
} }
@@ -662,6 +663,10 @@ init_vars(sudo_mode)
*--to = '\0'; *--to = '\0';
} }
} }
if ((user_base = strrchr(user_cmnd, '/')) != NULL)
user_base++;
else
user_base = user_cmnd;
return(rval); return(rval);
} }

10
sudo.h
View File

@@ -35,6 +35,7 @@
struct sudo_user { struct sudo_user {
struct passwd *pw; struct passwd *pw;
struct passwd *_runas_pw; struct passwd *_runas_pw;
struct stat *cmnd_stat;
char *path; char *path;
char *shell; char *shell;
char *tty; char *tty;
@@ -43,9 +44,10 @@ struct sudo_user {
char *shost; char *shost;
char **runas; char **runas;
char *prompt; char *prompt;
char *cmnd_safe;
char *cmnd; char *cmnd;
char *cmnd_args; char *cmnd_args;
char *cmnd_base;
char *cmnd_safe;
char *class_name; char *class_name;
}; };
@@ -128,6 +130,8 @@ struct sudo_user {
#define user_runas (sudo_user.runas) #define user_runas (sudo_user.runas)
#define user_cmnd (sudo_user.cmnd) #define user_cmnd (sudo_user.cmnd)
#define user_args (sudo_user.cmnd_args) #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_path (sudo_user.path)
#define user_prompt (sudo_user.prompt) #define user_prompt (sudo_user.prompt)
#define user_host (sudo_user.host) #define user_host (sudo_user.host)
@@ -189,9 +193,9 @@ size_t strlcat __P((char *, const char *, size_t));
#ifndef HAVE_STRLCPY #ifndef HAVE_STRLCPY
size_t strlcpy __P((char *, const char *, size_t)); size_t strlcpy __P((char *, const char *, size_t));
#endif #endif
char *sudo_goodpath __P((const char *)); char *sudo_goodpath __P((const char *, struct stat *));
char *tgetpass __P((const char *, int, int)); 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 check_user __P((int));
void verify_user __P((struct passwd *, char *)); void verify_user __P((struct passwd *, char *));
int sudoers_lookup __P((int)); int sudoers_lookup __P((int));

View File

@@ -248,7 +248,7 @@ main(argc, argv)
if (UserEditor && *UserEditor == '\0') if (UserEditor && *UserEditor == '\0')
UserEditor = NULL; UserEditor = NULL;
else if (UserEditor) { else if (UserEditor) {
if (find_path(UserEditor, &Editor, getenv("PATH")) == FOUND) { if (find_path(UserEditor, &Editor, NULL, getenv("PATH")) == FOUND) {
UserEditor = Editor; UserEditor = Editor;
} else { } else {
if (def_env_editor) { if (def_env_editor) {
@@ -318,7 +318,7 @@ main(argc, argv)
EditorPath = estrdup(def_editor); EditorPath = estrdup(def_editor);
Editor = strtok(EditorPath, ":"); Editor = strtok(EditorPath, ":");
do { do {
if (sudo_goodpath(Editor)) if (sudo_goodpath(Editor, NULL))
break; break;
} while ((Editor = strtok(NULL, ":"))); } while ((Editor = strtok(NULL, ":")));