summaryrefslogtreecommitdiff
blob: f8d6ecb5f3f77f2e8f316e3c52abe5c28b54d7e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
Index: postfix-2.4.7/src/util/safe_open.c
===================================================================
--- postfix-2.4.7.orig/src/util/safe_open.c
+++ postfix-2.4.7/src/util/safe_open.c
@@ -83,6 +83,7 @@
 #include <msg.h>
 #include <vstream.h>
 #include <vstring.h>
+#include <stringops.h>
 #include <safe_open.h>
 
 /* safe_open_exist - open existing file */
@@ -138,13 +139,30 @@ static VSTREAM *safe_open_exist(const ch
      * for symlinks owned by root. NEVER, NEVER, make exceptions for symlinks
      * owned by a non-root user. This would open a security hole when
      * delivering mail to a world-writable mailbox directory.
+     *
+     * The semantics of link(symlink, target) has changed over time.
+     * Traditionally, UNIX systems hardlink the target of the symlink.
+     * However, some systems hardlink the symlink itself. The latter behavior
+     * was introduced with Solaris 2.0, and with Linux kernel 2.0. Sebastian
+     * Krahmer of SuSE found that hardlinks to symlinks could be used to
+     * append mail for root to a sensitive file. For this reason, we not
+     * only require that a symlink is owned by root, but we now also require
+     * that its parent directory is writable only by root.
      */
     else if (lstat(path, &lstat_st) < 0) {
 	vstring_sprintf(why, "file status changed unexpectedly: %m");
 	errno = EPERM;
     } else if (S_ISLNK(lstat_st.st_mode)) {
-	if (lstat_st.st_uid == 0)
-	    return (fp);
+	if (lstat_st.st_uid == 0) {
+	    struct stat parent_st;
+	    const char *parent;
+
+	    parent = sane_dirname((VSTRING *) 0, path);
+	    if (stat(parent, &parent_st) == 0	/* real parent */
+		&& parent_st.st_uid == 0
+		&& (parent_st.st_mode & (S_IWGRP | S_IWOTH)) == 0)
+		return (fp);
+	}
 	vstring_sprintf(why, "file is a symbolic link");
 	errno = EPERM;
     } else if (fstat_st->st_dev != lstat_st.st_dev