]> granicus.if.org Git - sudo/commitdiff
Add simple_glob option to use fnmatch() instead of glob(). This is
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 10 Feb 2009 13:09:14 +0000 (13:09 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 10 Feb 2009 13:09:14 +0000 (13:09 +0000)
useful when you need to specify patterns that reference network file
systems.

def_data.c
def_data.h
def_data.in
match.c
sudoers.pod

index 6910a436180175d2cd4b94e4cb34717b1d94a9f5..ccb993ec80dc43f3ec07e336dc48eb42061bb52c 100644 (file)
@@ -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
     }
index 0f661c2f132d1188d8544c1c9c0e8be1b26360f7..964f99d6fe994799c88bd1ea240e3115d1d98593 100644 (file)
 #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,
index 8419933c5a9418386e834267c3badb46765a6e65..91ef2d0ec53d35eb9881c95f35762650b0dcd51c 100644 (file)
@@ -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 914f8f3b2162d769640915ef9eca9e6b68d3f1b8..92542ef18411f92fa5fe2df5b3d5d8b16211dcaf 100644 (file)
--- 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);
 }
 
 /*
index 4a56a33e7fc305dba219a7c2bedfd17efb05ed88..54ec932d951f5b7bdc6f6eeb333aaea28fe1d2e6 100644 (file)
@@ -735,6 +735,19 @@ shell is determined by the C<SHELL> 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<off> by default.
 
+=item simple_glob
+
+Normally, B<sudo> uses the L<glob(3)> function to do shell-style
+globbing when matching pathnames.  However, since it accesses the
+file system, L<glob(3)> 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<simple_glob>
+option causes B<sudo> to use the L<fnmatch(3)> function, which does
+simpler matching that does not access the file system.  The
+disadvantage of I<simple_glob> is that it is unable to match relative
+pathnames such as F<./ls> or F<../bin/ls>.  This flag is I<off> by
+default.
+
 =item stay_setuid
 
 Normally, when B<sudo> executes a command the real and effective