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.
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.
* 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 */
*/
if (strchr(infile, '/')) {
strlcpy(command, infile, sizeof(command)); /* paranoia */
- if (sudo_goodpath(command)) {
+ if (sudo_goodpath(command, sbp)) {
*outfile = command;
return(FOUND);
} else
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;
* 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);
}
* 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;
return(NULL);
}
+ if (sbp != NULL)
+ (void) memcpy(sbp, &sb, sizeof(struct stat));
return((char *)path);
}
* 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)) ||
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
* 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)) ||
} 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);
/*
* 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)) ||
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);
/* 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);
}
}
*--to = '\0';
}
}
+ if ((user_base = strrchr(user_cmnd, '/')) != NULL)
+ user_base++;
+ else
+ user_base = user_cmnd;
return(rval);
}
struct sudo_user {
struct passwd *pw;
struct passwd *_runas_pw;
+ struct stat *cmnd_stat;
char *path;
char *shell;
char *tty;
char *shost;
char **runas;
char *prompt;
- char *cmnd_safe;
char *cmnd;
char *cmnd_args;
+ char *cmnd_base;
+ char *cmnd_safe;
char *class_name;
};
#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)
#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));
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) {
EditorPath = estrdup(def_editor);
Editor = strtok(EditorPath, ":");
do {
- if (sudo_goodpath(Editor))
+ if (sudo_goodpath(Editor, NULL))
break;
} while ((Editor = strtok(NULL, ":")));