From: Todd C. Miller Date: Tue, 10 Feb 2009 13:09:14 +0000 (+0000) Subject: Add simple_glob option to use fnmatch() instead of glob(). This is X-Git-Tag: SUDO_1_7_1~62 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=7207b4cd609025d8d9e5e209fa64b7214ad31d5b;p=sudo Add simple_glob option to use fnmatch() instead of glob(). This is useful when you need to specify patterns that reference network file systems. --- diff --git a/def_data.c b/def_data.c index 6910a4361..ccb993ec8 100644 --- a/def_data.c +++ b/def_data.c @@ -306,6 +306,10 @@ struct sudo_defs_types sudo_defs_table[] = { "pwstars", T_FLAG, "Print a stars at the password prompt when there is user input", NULL, + }, { + "simple_glob", T_FLAG, + "Use simpler globbing that is less accurate but does not access the filesystem", + NULL, }, { NULL, 0, NULL } diff --git a/def_data.h b/def_data.h index 0f661c2f1..964f99d6f 100644 --- a/def_data.h +++ b/def_data.h @@ -140,6 +140,8 @@ #define I_VISIBLEPW 69 #define def_pwstars (sudo_defs_table[70].sd_un.flag) #define I_PWSTARS 70 +#define def_simple_glob (sudo_defs_table[71].sd_un.flag) +#define I_SIMPLE_GLOB 71 enum def_tupple { never, diff --git a/def_data.in b/def_data.in index 8419933c5..91ef2d0ec 100644 --- a/def_data.in +++ b/def_data.in @@ -226,3 +226,6 @@ visiblepw pwstars T_FLAG "Print a stars at the password prompt when there is user input" +simple_glob + T_FLAG + "Use simpler globbing that is less accurate but does not access the filesystem" diff --git a/match.c b/match.c index 914f8f3b2..92542ef18 100644 --- a/match.c +++ b/match.c @@ -97,6 +97,9 @@ __unused static const char rcsid[] = "$Sudo$"; static struct member_list empty; static int command_matches_dir __P((char *, size_t)); +static int command_matches_glob __P((char *, char *)); +static int command_matches_fnmatch __P((char *, char *)); +static int command_matches_normal __P((char *, char *)); /* * Returns TRUE if string 's' contains meta characters. @@ -370,11 +373,6 @@ command_matches(sudoers_cmnd, sudoers_args) char *sudoers_cmnd; char *sudoers_args; { - struct stat sudoers_stat; - char **ap, *base, *cp; - glob_t gl; - size_t dlen; - /* Check for pseudo-commands */ if (strchr(user_cmnd, '/') == NULL) { /* @@ -396,110 +394,163 @@ command_matches(sudoers_cmnd, sudoers_args) } else return(FALSE); } - dlen = strlen(sudoers_cmnd); - /* - * If sudoers_cmnd has meta characters in it, we may need to - * use glob(3) and fnmatch(3) to do the matching. - */ if (has_meta(sudoers_cmnd)) { /* - * First check to see if we can avoid the call to glob(3). - * Short circuit if there are no meta chars in the command itself - * and user_base and basename(sudoers_cmnd) don't match. - */ - if (sudoers_cmnd[dlen - 1] != '/') { - if ((base = strrchr(sudoers_cmnd, '/')) != NULL) { - base++; - if (!has_meta(base) && strcmp(user_base, base) != 0) - return(FALSE); - } - } - /* - * Return true if we find a match in the glob(3) results AND - * a) there are no args in sudoers OR - * b) there are no args on command line and none required by sudoers OR - * c) there are args in sudoers and on command line and they match - * else return false. + * If sudoers_cmnd has meta characters in it, we need to + * use glob(3) and/or fnmatch(3) to do the matching. */ -#define GLOB_FLAGS (GLOB_NOSORT | GLOB_MARK | GLOB_BRACE | GLOB_TILDE) - if (glob(sudoers_cmnd, GLOB_FLAGS, NULL, &gl) != 0) { - globfree(&gl); - return(FALSE); - } - /* For each glob match, compare basename, st_dev and st_ino. */ - for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) { - /* If it ends in '/' it is a directory spec. */ - dlen = strlen(cp); - if (cp[dlen - 1] == '/') { - if (command_matches_dir(cp, dlen)) - return(TRUE); - continue; - } + if (def_simple_glob) + return(command_matches_fnmatch(sudoers_cmnd, sudoers_args)); + return(command_matches_glob(sudoers_cmnd, sudoers_args)); + } + return(command_matches_normal(sudoers_cmnd, sudoers_args)); +} - /* Only proceed if user_base and basename(cp) match */ - if ((base = strrchr(cp, '/')) != NULL) - base++; - else - base = cp; - if (strcmp(user_base, base) != 0 || - stat(cp, &sudoers_stat) == -1) - continue; - if (user_stat == NULL || - (user_stat->st_dev == sudoers_stat.st_dev && - user_stat->st_ino == sudoers_stat.st_ino)) { - efree(safe_cmnd); - safe_cmnd = estrdup(cp); - break; - } - } - globfree(&gl); - if (cp == NULL) - return(FALSE); +static int +command_matches_fnmatch(sudoers_cmnd, sudoers_args) + char *sudoers_cmnd; + char *sudoers_args; +{ + /* + * Return true if fnmatch(3) succeeds AND + * a) there are no args in sudoers OR + * b) there are no args on command line and none required by sudoers OR + * c) there are args in sudoers and on command line and they match + * else return false. + */ + if (fnmatch(sudoers_cmnd, user_cmnd, FNM_PATHNAME) != 0) + return(FALSE); + if (!sudoers_args || + (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || + (sudoers_args && + fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { + if (safe_cmnd) + free(safe_cmnd); + safe_cmnd = estrdup(user_cmnd); + return(TRUE); + } else + return(FALSE); +} - if (!sudoers_args || - (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || - (sudoers_args && - fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { - efree(safe_cmnd); - safe_cmnd = estrdup(user_cmnd); - return(TRUE); +static int +command_matches_glob(sudoers_cmnd, sudoers_args) + char *sudoers_cmnd; + char *sudoers_args; +{ + struct stat sudoers_stat; + size_t dlen; + char **ap, *base, *cp; + glob_t gl; + + /* + * First check to see if we can avoid the call to glob(3). + * Short circuit if there are no meta chars in the command itself + * and user_base and basename(sudoers_cmnd) don't match. + */ + dlen = strlen(sudoers_cmnd); + if (sudoers_cmnd[dlen - 1] != '/') { + if ((base = strrchr(sudoers_cmnd, '/')) != NULL) { + base++; + if (!has_meta(base) && strcmp(user_base, base) != 0) + return(FALSE); } + } + /* + * Return true if we find a match in the glob(3) results AND + * a) there are no args in sudoers OR + * b) there are no args on command line and none required by sudoers OR + * c) there are args in sudoers and on command line and they match + * else return false. + */ +#define GLOB_FLAGS (GLOB_NOSORT | GLOB_MARK | GLOB_BRACE | GLOB_TILDE) + if (glob(sudoers_cmnd, GLOB_FLAGS, NULL, &gl) != 0) { + globfree(&gl); return(FALSE); - } else { + } + /* For each glob match, compare basename, st_dev and st_ino. */ + for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) { /* If it ends in '/' it is a directory spec. */ - if (sudoers_cmnd[dlen - 1] == '/') - return(command_matches_dir(sudoers_cmnd, dlen)); + dlen = strlen(cp); + if (cp[dlen - 1] == '/') { + if (command_matches_dir(cp, dlen)) + return(TRUE); + continue; + } - /* Only proceed if user_base and basename(sudoers_cmnd) match */ - if ((base = strrchr(sudoers_cmnd, '/')) == NULL) - base = sudoers_cmnd; - else + /* Only proceed if user_base and basename(cp) match */ + if ((base = strrchr(cp, '/')) != NULL) base++; + else + base = cp; if (strcmp(user_base, base) != 0 || - stat(sudoers_cmnd, &sudoers_stat) == -1) - return(FALSE); - - /* - * Return true if inode/device matches 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 (user_stat != NULL && - (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)) || - (sudoers_args && - fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { + stat(cp, &sudoers_stat) == -1) + continue; + if (user_stat == NULL || + (user_stat->st_dev == sudoers_stat.st_dev && + user_stat->st_ino == sudoers_stat.st_ino)) { efree(safe_cmnd); - safe_cmnd = estrdup(sudoers_cmnd); - return(TRUE); + safe_cmnd = estrdup(cp); + break; } + } + globfree(&gl); + if (cp == NULL) return(FALSE); + + if (!sudoers_args || + (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) || + (sudoers_args && + fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { + efree(safe_cmnd); + safe_cmnd = estrdup(user_cmnd); + return(TRUE); } + return(FALSE); +} + +static int +command_matches_normal(sudoers_cmnd, sudoers_args) + char *sudoers_cmnd; + char *sudoers_args; +{ + struct stat sudoers_stat; + char *base, *cp; + size_t dlen; + + /* If it ends in '/' it is a directory spec. */ + dlen = strlen(sudoers_cmnd); + if (sudoers_cmnd[dlen - 1] == '/') + return(command_matches_dir(sudoers_cmnd, dlen)); + + /* Only proceed if user_base and basename(sudoers_cmnd) match */ + if ((base = strrchr(sudoers_cmnd, '/')) == NULL) + base = sudoers_cmnd; + else + base++; + if (strcmp(user_base, base) != 0 || + stat(sudoers_cmnd, &sudoers_stat) == -1) + return(FALSE); + + /* + * Return true if inode/device matches 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 (user_stat != NULL && + (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)) || + (sudoers_args && + fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)) { + efree(safe_cmnd); + safe_cmnd = estrdup(sudoers_cmnd); + return(TRUE); + } + return(FALSE); } /* diff --git a/sudoers.pod b/sudoers.pod index 4a56a33e7..54ec932d9 100644 --- a/sudoers.pod +++ b/sudoers.pod @@ -735,6 +735,19 @@ shell is determined by the C environment variable if it is set, falling back on the shell listed in the invoking user's /etc/passwd entry if not). This flag is I by default. +=item simple_glob + +Normally, B uses the L function to do shell-style +globbing when matching pathnames. However, since it accesses the +file system, L can take a long time to complete for some +patterns, especially when the pattern references a network file +system that is mounted on demand (automounted). The I +option causes B to use the L function, which does +simpler matching that does not access the file system. The +disadvantage of I is that it is unable to match relative +pathnames such as F<./ls> or F<../bin/ls>. This flag is I by +default. + =item stay_setuid Normally, when B executes a command the real and effective