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:
10
TODO
10
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
|
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.
|
||||||
|
@@ -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);
|
||||||
}
|
}
|
||||||
|
@@ -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
74
parse.c
@@ -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
9
sudo.c
@@ -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
10
sudo.h
@@ -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));
|
||||||
|
4
visudo.c
4
visudo.c
@@ -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, ":")));
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user