]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
tmpfiles: refuse to chown()/chmod() files which are hardlinked, unless protected_hard...
authorLennart Poettering <lennart@poettering.net>
Tue, 23 Jan 2018 13:03:34 +0000 (14:03 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 23 Jan 2018 20:19:00 +0000 (21:19 +0100)
Let's add some extra safety.

Fixes: #7736
src/tmpfiles/tmpfiles.c

index d733768f277bdf9d4d9c1c048372521cfc9466d7..5b56e7dcdd2b9c0e86282688a5a08735492fa5af 100644 (file)
@@ -753,6 +753,39 @@ finish:
         return r;
 }
 
+static bool dangerous_hardlinks(void) {
+        _cleanup_free_ char *value = NULL;
+        static int cached = -1;
+        int r;
+
+        /* Check whether the fs.protected_hardlinks sysctl is on. If we can't determine it we assume its off, as that's
+         * what the upstream default is. */
+
+        if (cached >= 0)
+                return cached;
+
+        r = read_one_line_file("/proc/sys/fs/protected_hardlinks", &value);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to read fs.protected_hardlinks sysctl: %m");
+                return true;
+        }
+
+        r = parse_boolean(value);
+        if (r < 0) {
+                log_debug_errno(r, "Failed to parse fs.protected_hardlinks sysctl: %m");
+                return true;
+        }
+
+        cached = r == 0;
+        return cached;
+}
+
+static bool hardlink_vulnerable(struct stat *st) {
+        assert(st);
+
+        return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
+}
+
 static int path_set_perms(Item *i, const char *path) {
         char fn[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
         _cleanup_close_ int fd = -1;
@@ -787,6 +820,11 @@ static int path_set_perms(Item *i, const char *path) {
         if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
                 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
 
+        if (hardlink_vulnerable(&st)) {
+                log_error("Refusing to set permissions on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+                return -EPERM;
+        }
+
         xsprintf(fn, "/proc/self/fd/%i", fd);
 
         if (i->mode_set) {
@@ -971,6 +1009,11 @@ static int path_set_acls(Item *item, const char *path) {
         if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
                 return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
 
+        if (hardlink_vulnerable(&st)) {
+                log_error("Refusing to set ACLs on hardlinked file %s while the fs.protected_hardlinks sysctl is turned off.", path);
+                return -EPERM;
+        }
+
         if (S_ISLNK(st.st_mode)) {
                 log_debug("Skipping ACL fix for symlink %s.", path);
                 return 0;