summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <Todd.Miller@sudo.ws>2021-01-06 10:16:00 -0700
committerTodd C. Miller <Todd.Miller@sudo.ws>2021-01-06 10:16:00 -0700
commitbf495920acd628a6a7feb46b3fdb5a64d708afdf (patch)
tree08d5d8bc2dfb0d063baf6ba446ebe78bcb88b437
parent8c9f04a74824f1999e5c6afa16b3c872667fcb25 (diff)
downloadsudo-bf495920acd628a6a7feb46b3fdb5a64d708afdf.tar.gz
Fix potential directory existing info leak in sudoedit.
When creating a new file, sudoedit checks to make sure the parent directory exists so it can provide the user with a sensible error message. However, this could be used to test for the existence of directories not normally accessible to the user by pointing to them with a symbolic link when the parent directory is controlled by the user. Problem reported by Matthias Gerstner of SUSE.
-rw-r--r--src/sudo_edit.c29
1 files changed, 24 insertions, 5 deletions
diff --git a/src/sudo_edit.c b/src/sudo_edit.c
index 15f0aff76..7dda881d8 100644
--- a/src/sudo_edit.c
+++ b/src/sudo_edit.c
@@ -578,14 +578,33 @@ sudo_edit_create_tfiles(struct command_details *command_details,
S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
if (ofd != -1 || errno == ENOENT) {
if (ofd == -1) {
- /* New file, verify parent dir exists unless in cwd. */
+ /*
+ * New file, verify parent dir exists unless in cwd.
+ * This fails early so the user knows ahead of time if the
+ * edit won't succeed. Additional checks are performed
+ * when copying the temporary file back to the origin.
+ */
char *slash = strrchr(files[i], '/');
if (slash != NULL && slash != files[i]) {
- int serrno = errno;
+ const int sflags = command_details->flags;
+ const int serrno = errno;
+ int dfd;
+
+ /*
+ * The parent directory is allowed to be a symbolic
+ * link as long as *its* parent is not writable.
+ */
*slash = '\0';
- if (stat(files[i], &sb) == 0 && S_ISDIR(sb.st_mode)) {
- memset(&sb, 0, sizeof(sb));
- rc = 0;
+ SET(command_details->flags, CD_SUDOEDIT_FOLLOW);
+ dfd = sudo_edit_open(files[i], DIR_OPEN_FLAGS,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, command_details);
+ command_details->flags = sflags;
+ if (dfd != -1) {
+ if (fstat(dfd, &sb) == 0 && S_ISDIR(sb.st_mode)) {
+ memset(&sb, 0, sizeof(sb));
+ rc = 0;
+ }
+ close(dfd);
}
*slash = '/';
errno = serrno;