File revert-suse-disable-dir-canonicalization.patch of Package sudo.34945

commit ff64b1940a82a5e2a5d176b191105716d792cba6
Author: Simon Lees <sflees@suse.de>
Date:   Wed Jun 5 12:37:05 2024 +0930

    Revert "Match using canonicalized directories where possible."
    
    This reverts commit b52631e8777ae9334e403259d211fb980aebc848.

Index: sudo-1.9.15p5/plugins/sudoers/match_command.c
===================================================================
--- sudo-1.9.15p5.orig/plugins/sudoers/match_command.c
+++ sudo-1.9.15p5/plugins/sudoers/match_command.c
@@ -240,51 +240,66 @@ static int
 command_matches_dir(struct sudoers_context *ctx, const char *sudoers_dir,
     size_t dlen, int real_root, const struct command_digest_list *digests)
 {
+    char buf[PATH_MAX];
     struct stat sudoers_stat;
-    char path[PATH_MAX];
-    int len, fd = -1;
+    struct dirent *dent;
+    int fd = -1;
+    DIR *dirp;
     int ret = DENY;
     debug_decl(command_matches_dir, SUDOERS_DEBUG_MATCH);
 
-    /* Compare the canonicalized directories, if possible. */
-    if (ctx->user.cmnd_dir != NULL) {
-	char *resolved = canon_path(sudoers_dir);
-	if (resolved != NULL) {
-	    if (strcmp(resolved, ctx->user.cmnd_dir) != 0) {
-		canon_path_free(resolved);
-		goto done;
+    /*
+     * Grot through directory entries, looking for user_base.
+     */
+    dirp = opendir(sudoers_dir);
+    if (dirp == NULL)
+	debug_return_int(DENY);
+
+    if (strlcpy(buf, sudoers_dir, sizeof(buf)) >= sizeof(buf)) {
+	closedir(dirp);
+	debug_return_int(DENY);
 	    }
-	    canon_path_free(resolved);
-	}
+    while ((dent = readdir(dirp)) != NULL) {
+	if (fd != -1) {
+	    close(fd);
+	    fd = -1;
     }
 
-    /* Check for command in sudoers_dir. */
-    len = snprintf(path, sizeof(path), "%s/%s", sudoers_dir, ctx->user.cmnd_base);
-    if (len < 0 || len >= ssizeof(path))
-	goto done;
+	/* ignore paths > PATH_MAX (XXX - log) */
+	buf[dlen] = '\0';
+	if (strlcat(buf, dent->d_name, sizeof(buf)) >= sizeof(buf))
+	    continue;
+
+	/* only stat if basenames are the same */
+	if (strcmp(ctx->user.cmnd_base, dent->d_name) != 0)
+	    continue;
 
     /* Open the file for fdexec or for digest matching. */
-    if (!open_cmnd(path, digests, &fd))
-	goto done;
-    if (!do_stat(fd, path, &sudoers_stat))
-	goto done;
-
-    if (ctx->user.cmnd_stat == NULL ||
-	(ctx->user.cmnd_stat->st_dev == sudoers_stat.st_dev &&
-	ctx->user.cmnd_stat->st_ino == sudoers_stat.st_ino)) {
-	if (digest_matches(fd, path, digests) != ALLOW)
-	    goto done;
-	free(ctx->runas.cmnd);
-	if ((ctx->runas.cmnd = strdup(path)) == NULL) {
-	    sudo_warnx(U_("%s: %s"), __func__,
-		U_("unable to allocate memory"));
-	}
-	ret = ALLOW;
-	goto done;
+	if (!open_cmnd(buf, digests, &fd))
+	    continue;
+	if (!do_stat(fd, buf, &sudoers_stat))
+	    continue;
+
+	if (ctx->user.cmnd_stat == NULL ||
+	    (ctx->user.cmnd_stat->st_dev == sudoers_stat.st_dev &&
+	    ctx->user.cmnd_stat->st_ino == sudoers_stat.st_ino)) {
+	    if (digest_matches(fd, buf, digests) != ALLOW)
+		    continue;
+	    free(ctx->runas.cmnd);
+	    if ((ctx->runas.cmnd = strdup(buf)) == NULL) {
+	        sudo_warnx(U_("%s: %s"), __func__,
+		    U_("unable to allocate memory"));
+		    dent = NULL;
+	    }
+	    break;
+    }
     }
-    ret = DENY;
+    closedir(dirp);
 
-done:
+    if (dent != NULL) {
+	set_cmnd_fd(ctx, fd, real_root);
+	debug_return_int(ALLOW);
+    }
     if (fd != -1)
 	close(fd);
     debug_return_int(ret);
@@ -333,7 +348,7 @@ command_matches_all(struct sudoers_conte
     int fd = -1;
     debug_decl(command_matches_all, SUDOERS_DEBUG_MATCH);
 
-    if (strchr(ctx->user.cmnd, '/') != NULL) {
+    if (ctx->user.cmnd[0] == '/') {
 #ifndef SUDOERS_NAME_MATCH
 	/* Open the file for fdexec or for digest matching. */
 	bool open_error = !open_cmnd(ctx->user.cmnd, digests, &fd);
@@ -369,25 +384,12 @@ command_matches_fnmatch(struct sudoers_c
     const char *sudoers_args, int real_root,
     const struct command_digest_list *digests)
 {
-    const char *cmnd = ctx->user.cmnd;
-    char buf[PATH_MAX];
-    int len, fd = -1;
 #ifndef SUDOERS_NAME_MATCH
     struct stat sb;
 #endif
+    int fd = -1;
     debug_decl(command_matches_fnmatch, SUDOERS_DEBUG_MATCH);
 
-    /* A relative ctx->user.cmnd will not match, try canonicalized version. */
-    if (ctx->user.cmnd[0] != '/') {
-	if (ctx->user.cmnd_dir == NULL)
-	    debug_return_int(DENY);
-	len = snprintf(buf, sizeof(buf), "%s/%s", ctx->user.cmnd_dir,
-	    ctx->user.cmnd_base);
-	if (len < 0 || len >= ssizeof(buf))
-	    debug_return_int(DENY);
-	cmnd = buf;
-    }
-
     /*
      * Return ALLOW if fnmatch(3) succeeds AND
      *  a) there are no args in sudoers OR
@@ -395,19 +397,19 @@ command_matches_fnmatch(struct sudoers_c
      *  c) there are args in sudoers and on command line and they match
      *     else return DENY.
      */
-    if (fnmatch(sudoers_cmnd, cmnd, FNM_PATHNAME) != 0)
+    if (fnmatch(sudoers_cmnd, ctx->user.cmnd, FNM_PATHNAME) != 0)
 	debug_return_int(DENY);
 
     if (command_args_match(ctx, sudoers_cmnd, sudoers_args) == ALLOW) {
 	/* Open the file for fdexec or for digest matching. */
-	if (!open_cmnd(cmnd, digests, &fd))
+	if (!open_cmnd(ctx->user.cmnd, digests, &fd))
 	    goto bad;
 #ifndef SUDOERS_NAME_MATCH
-	if (!do_stat(fd, cmnd, &sb))
+	if (!do_stat(fd, ctx->user.cmnd, &sb))
 	    goto bad;
 #endif
-	/* Check digest of cmnd since sudoers_cmnd is a pattern. */
-	if (digest_matches(fd, cmnd, digests) != ALLOW)
+	/* Check digest of user_ctx.cmnd since sudoers_cmnd is a pattern. */
+	if (!digest_matches(fd, ctx->user.cmnd, digests))
 	    goto bad;
 	set_cmnd_fd(ctx, fd, real_root);
 
@@ -425,45 +427,32 @@ command_matches_regex(struct sudoers_con
     const char *sudoers_args, int real_root,
     const struct command_digest_list *digests)
 {
-    const char *cmnd = ctx->user.cmnd;
-    char buf[PATH_MAX];
-    int len, fd = -1;
 #ifndef SUDOERS_NAME_MATCH
     struct stat sb;
 #endif
+    int fd = -1;
     debug_decl(command_matches_regex, SUDOERS_DEBUG_MATCH);
 
-    /* A relative ctx->user.cmnd will not match, try canonicalized version. */
-    if (ctx->user.cmnd[0] != '/') {
-	if (ctx->user.cmnd_dir == NULL)
-	    debug_return_int(DENY);
-	len = snprintf(buf, sizeof(buf), "%s/%s", ctx->user.cmnd_dir,
-	    ctx->user.cmnd_base);
-	if (len < 0 || len >= ssizeof(buf))
-	    debug_return_int(DENY);
-	cmnd = buf;
-    }
-
     /*
-     * Return ALLOW if sudoers_cmnd regex matches cmnd AND
+     * Return true if sudoers_cmnd regex matches user_cmnd 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 DENY.
      */
-    if (regex_matches(sudoers_cmnd, cmnd) != ALLOW)
+    if (regex_matches(sudoers_cmnd, ctx->user.cmnd) != ALLOW)
 	debug_return_int(DENY);
 
     if (command_args_match(ctx, sudoers_cmnd, sudoers_args) == ALLOW) {
 	/* Open the file for fdexec or for digest matching. */
-	if (!open_cmnd(cmnd, digests, &fd))
+	if (!open_cmnd(ctx->user.cmnd, digests, &fd))
 	    goto bad;
 #ifndef SUDOERS_NAME_MATCH
-	if (!do_stat(fd, cmnd, &sb))
+	if (!do_stat(fd, ctx->user.cmnd, &sb))
 	    goto bad;
 #endif
-	/* Check digest of cmnd since sudoers_cmnd is a pattern. */
-	if (digest_matches(fd, cmnd, digests) != ALLOW)
+	/* Check digest of user_cmnd since sudoers_cmnd is a pattern. */
+	if (!digest_matches(fd, ctx->user.cmnd, digests))
 	    goto bad;
 	set_cmnd_fd(ctx, fd, real_root);
 
@@ -550,7 +539,7 @@ command_matches_glob(struct sudoers_cont
 	    goto done;
 	}
     }
-    /* No exact match, compare basename, cmnd_dir, st_dev and st_ino. */
+    /* No exact match, compare basename, st_dev and st_ino. */
     if (!bad_digest) {
 	for (ap = gl.gl_pathv; (cp = *ap) != NULL; ap++) {
 	    if (fd != -1) {
@@ -573,24 +562,6 @@ command_matches_glob(struct sudoers_cont
 	    if (strcmp(ctx->user.cmnd_base, base) != 0)
 		continue;
 
-	    /* Compare the canonicalized parent directories, if possible. */
-	    if (ctx->user.cmnd_dir != NULL) {
-		char *slash = strrchr(cp, '/');
-		if (slash != NULL) {
-		    char *resolved;
-		    *slash = '\0';
-		    resolved = canon_path(cp);
-		    *slash = '/';
-		    if (resolved != NULL) {
-			/* Canonicalized directories must match. */
-			int result = strcmp(resolved, ctx->user.cmnd_dir);
-			canon_path_free(resolved);
-			if (result != 0)
-			    continue;
-		    }
-		}
-	    }
-
 	    /* Open the file for fdexec or for digest matching. */
 	    if (!open_cmnd(cp, digests, &fd))
 		continue;
@@ -600,7 +571,7 @@ command_matches_glob(struct sudoers_cont
 		(ctx->user.cmnd_stat->st_dev == sudoers_stat.st_dev &&
 		ctx->user.cmnd_stat->st_ino == sudoers_stat.st_ino)) {
 		if (digest_matches(fd, cp, digests) != ALLOW)
-		    continue;
+		continue;
 		free(ctx->runas.cmnd);
 		if ((ctx->runas.cmnd = strdup(cp)) == NULL) {
 		    sudo_warnx(U_("%s: %s"), __func__,
@@ -648,28 +619,6 @@ command_matches_normal(struct sudoers_co
     if (strcmp(ctx->user.cmnd_base, base) != 0)
 	debug_return_int(DENY);
 
-    /* Compare the canonicalized parent directories, if possible. */
-    if (ctx->user.cmnd_dir != NULL) {
-	const char *slash = strrchr(sudoers_cmnd, '/');
-	if (slash != NULL) {
-	    char sudoers_cmnd_dir[PATH_MAX], *resolved;
-	    const size_t len = (size_t)(slash - sudoers_cmnd);
-	    if (len >= sizeof(sudoers_cmnd_dir))
-		goto bad;
-	    if (len != 0)
-		memcpy(sudoers_cmnd_dir, sudoers_cmnd, len);
-	    sudoers_cmnd_dir[len] = '\0';
-	    resolved = canon_path(sudoers_cmnd_dir);
-	    if (resolved != NULL) {
-		if (strcmp(resolved, ctx->user.cmnd_dir) != 0) {
-		    canon_path_free(resolved);
-		    goto bad;
-		}
-		canon_path_free(resolved);
-	    }
-	}
-    }
-
     /* Open the file for fdexec or for digest matching. */
     if (!open_cmnd(sudoers_cmnd, digests, &fd))
 	goto bad;
openSUSE Build Service is sponsored by