From 5579f85663d10269e7ac7464be6548c99cea4ada Mon Sep 17 00:00:00 2001
From: Lennart Poettering <lennart@poettering.net>
Date: Tue, 23 Jan 2018 14:03:34 +0100
Subject: [PATCH] tmpfiles: refuse to chown()/chmod() files which are
hardlinked, unless protected_hardlinks sysctl is on
Let's add some extra safety.
Fixes: #7736
---
src/tmpfiles/tmpfiles.c | 43 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 43 insertions(+)
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
index d733768f277..5b56e7dcdd2 100644
--- a/src/tmpfiles/tmpfiles.c
+++ b/src/tmpfiles/tmpfiles.c
@@ -604,6 +604,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) {
_cleanup_close_ int fd = -1;
struct stat st;
@@ -623,6 +623,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;
+ }
+
if (S_ISLNK(st.st_mode))
log_debug("Skipping mode an owner fix for symlink %s.", path);
else {
@@ -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;