230a1d
From db1f27c0350e9e437c93780ffe88648ae1984467 Mon Sep 17 00:00:00 2001
230a1d
From: "Todd C. Miller" <Todd.Miller@sudo.ws>
230a1d
Date: Wed, 6 Jan 2021 10:16:00 -0700
230a1d
Subject: [PATCH] Fix potential directory existing info leak in sudoedit. When
230a1d
 creating a new file, sudoedit checks to make sure the parent directory exists
230a1d
 so it can provide the user with a sensible error message.  However, this
230a1d
 could be used to test for the existence of directories not normally
230a1d
 accessible to the user by pointing to them with a symbolic link when the
230a1d
 parent directory is controlled by the user.  Problem reported by Matthias
230a1d
 Gerstner of SUSE.
230a1d
230a1d
---
230a1d
 src/sudo_edit.c | 29 ++++++++++++++++++++++++-----
230a1d
 1 file changed, 24 insertions(+), 5 deletions(-)
230a1d
230a1d
diff --git a/src/sudo_edit.c b/src/sudo_edit.c
230a1d
index 82e04a71b..5502b7bd9 100644
230a1d
--- a/src/sudo_edit.c
230a1d
+++ b/src/sudo_edit.c
230a1d
@@ -541,14 +541,33 @@ sudo_edit_create_tfiles(struct command_details *command_details,
230a1d
 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
230a1d
 	if (ofd != -1 || errno == ENOENT) {
230a1d
 	    if (ofd == -1) {
230a1d
-		/* New file, verify parent dir exists unless in cwd. */
230a1d
+		/*
230a1d
+		 * New file, verify parent dir exists unless in cwd.
230a1d
+		 * This fails early so the user knows ahead of time if the
230a1d
+		 * edit won't succeed.  Additional checks are performed
230a1d
+		 * when copying the temporary file back to the origin.
230a1d
+		 */
230a1d
 		char *slash = strrchr(files[i], '/');
230a1d
 		if (slash != NULL && slash != files[i]) {
230a1d
-		    int serrno = errno;
230a1d
+		    const int sflags = command_details->flags;
230a1d
+		    const int serrno = errno;
230a1d
+		    int dfd;
230a1d
+
230a1d
+		    /*
230a1d
+		     * The parent directory is allowed to be a symbolic
230a1d
+		     * link as long as *its* parent is not writable.
230a1d
+		     */
230a1d
 		    *slash = '\0';
230a1d
-		    if (stat(files[i], &sb) == 0 && S_ISDIR(sb.st_mode)) {
230a1d
-			memset(&sb, 0, sizeof(sb));
230a1d
-			rc = 0;
230a1d
+		    SET(command_details->flags, CD_SUDOEDIT_FOLLOW);
230a1d
+		    dfd = sudo_edit_open(files[i], DIR_OPEN_FLAGS,
230a1d
+			S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
230a1d
+		    command_details->flags = sflags;
230a1d
+		    if (dfd != -1) {
230a1d
+			if (fstat(dfd, &sb) == 0 && S_ISDIR(sb.st_mode)) {
230a1d
+			    memset(&sb, 0, sizeof(sb));
230a1d
+			    rc = 0;
230a1d
+			}
230a1d
+			close(dfd);
230a1d
 		    }
230a1d
 		    *slash = '/';
230a1d
 		    errno = serrno;
230a1d
-- 
230a1d
2.26.2
230a1d