]> granicus.if.org Git - sudo/commitdiff
Add cmnd_base to struct sudo_user and set it in init_vars().
authorTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 24 Aug 2004 18:01:14 +0000 (18:01 +0000)
committerTodd C. Miller <Todd.Miller@courtesan.com>
Tue, 24 Aug 2004 18:01:14 +0000 (18:01 +0000)
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.

TODO
find_path.c
goodpath.c
parse.c
sudo.c
sudo.h
visudo.c

diff --git a/TODO b/TODO
index 6ff07c711d62dc2d0a258f8379701be42b069da3..89d98846fb51985aaf20f7d1cc9ba3b09f453091 100644 (file)
--- a/TODO
+++ b/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
     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.
index d362de77d58277ce029b3437c4eaec89d422aebf..c141e4ef5046ce99fcdb1a7e3340042f01241a6f 100644 (file)
@@ -62,9 +62,10 @@ static const char rcsid[] = "$Sudo$";
  * 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 */
@@ -83,7 +84,7 @@ find_path(infile, outfile, path)
      */
     if (strchr(infile, '/')) {
        strlcpy(command, infile, sizeof(command));      /* paranoia */
-       if (sudo_goodpath(command)) {
+       if (sudo_goodpath(command, sbp)) {
            *outfile = command;
            return(FOUND);
        } else
@@ -120,7 +121,7 @@ find_path(infile, outfile, path)
        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;
@@ -132,7 +133,7 @@ find_path(infile, outfile, path)
      * 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);
     }
index 337995e6647d155a3b16fbcfbf5daaa605b55a84..fdfb50151bce005a6f6c8e7597e5d26a5f4fd060 100644 (file)
@@ -47,8 +47,9 @@ static const char rcsid[] = "$Sudo$";
  * 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;
 
@@ -65,5 +66,7 @@ sudo_goodpath(path)
        return(NULL);
     }
 
+    if (sbp != NULL)
+       (void) memcpy(sbp, &sb, sizeof(struct stat));
     return((char *)path);
 }
diff --git a/parse.c b/parse.c
index 39e8484a5dab968a0abd7088f1bbabdee0694567..d7eb8dbdb0b44a9a36912898c5b9ae9d4c7cd8f1 100644 (file)
--- a/parse.c
+++ b/parse.c
@@ -230,27 +230,25 @@ sudoers_lookup(pwflag)
  * 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)) ||
@@ -258,29 +256,17 @@ command_matches(path, 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
@@ -288,7 +274,7 @@ command_matches(path, sudoers_args)
         *  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)) ||
@@ -301,19 +287,22 @@ command_matches(path, 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);
 
            /*
@@ -322,7 +311,8 @@ command_matches(path, sudoers_args)
             *  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)) ||
@@ -330,31 +320,33 @@ command_matches(path, 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);
diff --git a/sudo.c b/sudo.c
index 90ddfd31acc254b7e2374d68853d243953ea8ba3..daa77d5f84614e986125cf7a1ebcd3a00574963b 100644 (file)
--- a/sudo.c
+++ b/sudo.c
@@ -622,16 +622,17 @@ init_vars(sudo_mode)
 
     /* 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);
            }
        }
@@ -662,6 +663,10 @@ init_vars(sudo_mode)
            *--to = '\0';
        }
     }
+    if ((user_base = strrchr(user_cmnd, '/')) != NULL)
+       user_base++;
+    else
+       user_base = user_cmnd;
 
     return(rval);
 }
diff --git a/sudo.h b/sudo.h
index 2d21bccd2b54754b3b07ffe3dfd93ffec565fac7..2f7e68c3ef0cfca58e10762d6d6866ca177afdda 100644 (file)
--- a/sudo.h
+++ b/sudo.h
@@ -35,6 +35,7 @@
 struct sudo_user {
     struct passwd *pw;
     struct passwd *_runas_pw;
+    struct stat *cmnd_stat;
     char *path;
     char *shell;
     char *tty;
@@ -43,9 +44,10 @@ struct sudo_user {
     char *shost;
     char **runas;
     char *prompt;
-    char *cmnd_safe;
     char *cmnd;
     char *cmnd_args;
+    char *cmnd_base;
+    char *cmnd_safe;
     char *class_name;
 };
 
@@ -128,6 +130,8 @@ struct sudo_user {
 #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)
@@ -189,9 +193,9 @@ size_t strlcat              __P((char *, const char *, size_t));
 #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));
index 65a10b6c321cd553e04ca80747bd6a4b2c7a295b..25efc5a772d33d5b977816cb50680233b7501ac1 100644 (file)
--- a/visudo.c
+++ b/visudo.c
@@ -248,7 +248,7 @@ main(argc, argv)
     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) {
@@ -318,7 +318,7 @@ main(argc, argv)
        EditorPath = estrdup(def_editor);
        Editor = strtok(EditorPath, ":");
        do {
-           if (sudo_goodpath(Editor))
+           if (sudo_goodpath(Editor, NULL))
                break;
        } while ((Editor = strtok(NULL, ":")));