summaryrefslogtreecommitdiff
blob: ea92d7e63f8d00ef5a3cff1a20397ad56bbfcfcc (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
diff -Naur postfix-2.5.3.orig/src/util/safe_open.c postfix-2.5.3/src/util/safe_open.c
--- postfix-2.5.3.orig/src/util/safe_open.c	2006-06-05 01:04:49.000000000 +0200
+++ postfix-2.5.3/src/util/safe_open.c	2008-08-03 16:42:10.882440950 +0200
@@ -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 @@
      * 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