|
|
1abbee |
From 595a93d716680715a751737ec2f87b06ea582763 Mon Sep 17 00:00:00 2001
|
|
|
1abbee |
From: Lennart Poettering <lennart@poettering.net>
|
|
|
1abbee |
Date: Mon, 13 Apr 2015 15:16:54 +0200
|
|
|
1abbee |
Subject: [PATCH] tmpfiles: don't follow symlinks when adjusting ACLs, fille
|
|
|
1abbee |
attributes, access modes or ownership
|
|
|
1abbee |
|
|
|
1abbee |
Cherry-picked from: 48b8aaa82724bc2d8440470f414fb0d2416f29c
|
|
|
1abbee |
Resolves: #1296288
|
|
|
1abbee |
---
|
|
|
1abbee |
src/tmpfiles/tmpfiles.c | 112 ++++++++++++++++++++++++++++++++----------------
|
|
|
1abbee |
1 file changed, 74 insertions(+), 38 deletions(-)
|
|
|
1abbee |
|
|
|
1abbee |
diff --git a/src/tmpfiles/tmpfiles.c b/src/tmpfiles/tmpfiles.c
|
|
|
181b3f |
index d0e6567d8..64c733aaa 100644
|
|
|
1abbee |
--- a/src/tmpfiles/tmpfiles.c
|
|
|
1abbee |
+++ b/src/tmpfiles/tmpfiles.c
|
|
|
1abbee |
@@ -570,51 +570,69 @@ finish:
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
static int path_set_perms(Item *i, const char *path) {
|
|
|
1abbee |
+ _cleanup_close_ int fd = -1;
|
|
|
1abbee |
struct stat st;
|
|
|
1abbee |
- bool st_valid;
|
|
|
1abbee |
|
|
|
1abbee |
assert(i);
|
|
|
1abbee |
assert(path);
|
|
|
1abbee |
|
|
|
1abbee |
- st_valid = stat(path, &st) == 0;
|
|
|
1abbee |
+ /* We open the file with O_PATH here, to make the operation
|
|
|
1abbee |
+ * somewhat atomic. Also there's unfortunately no fchmodat()
|
|
|
1abbee |
+ * with AT_SYMLINK_NOFOLLOW, hence we emulate it here via
|
|
|
1abbee |
+ * O_PATH. */
|
|
|
1abbee |
+
|
|
|
1abbee |
+ fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
|
|
|
1abbee |
+ if (fd < 0)
|
|
|
1abbee |
+ return log_error_errno(errno, "Adjusting owner and mode for %s failed: %m", path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
|
|
|
1abbee |
+ return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (S_ISLNK(st.st_mode))
|
|
|
1abbee |
+ log_debug("Skipping mode an owner fix for symlink %s.", path);
|
|
|
1abbee |
+ else {
|
|
|
1abbee |
+ char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
|
|
1abbee |
+ xsprintf(fn, "/proc/self/fd/%i", fd);
|
|
|
1abbee |
|
|
|
1abbee |
- /* not using i->path directly because it may be a glob */
|
|
|
1abbee |
- if (i->mode_set) {
|
|
|
1abbee |
- mode_t m = i->mode;
|
|
|
1abbee |
+ /* not using i->path directly because it may be a glob */
|
|
|
1abbee |
+ if (i->mode_set) {
|
|
|
1abbee |
+ mode_t m = i->mode;
|
|
|
1abbee |
|
|
|
1abbee |
- if (i->mask_perms && st_valid) {
|
|
|
1abbee |
- if (!(st.st_mode & 0111))
|
|
|
1abbee |
- m &= ~0111;
|
|
|
1abbee |
- if (!(st.st_mode & 0222))
|
|
|
1abbee |
+ if (i->mask_perms) {
|
|
|
1abbee |
+ if (!(st.st_mode & 0111))
|
|
|
1abbee |
+ m &= ~0111;
|
|
|
1abbee |
+ if (!(st.st_mode & 0222))
|
|
|
1abbee |
m &= ~0222;
|
|
|
1abbee |
- if (!(st.st_mode & 0444))
|
|
|
1abbee |
- m &= ~0444;
|
|
|
1abbee |
- if (!S_ISDIR(st.st_mode))
|
|
|
1abbee |
- m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
|
|
|
1abbee |
- }
|
|
|
1abbee |
+ if (!(st.st_mode & 0444))
|
|
|
1abbee |
+ m &= ~0444;
|
|
|
1abbee |
+ if (!S_ISDIR(st.st_mode))
|
|
|
1abbee |
+ m &= ~07000; /* remove sticky/sgid/suid bit, unless directory */
|
|
|
1abbee |
+ }
|
|
|
1abbee |
|
|
|
1abbee |
- if (st_valid && m == (st.st_mode & 07777))
|
|
|
1abbee |
- log_debug("\"%s\" has right mode %o", path, st.st_mode);
|
|
|
1abbee |
- else {
|
|
|
1abbee |
- log_debug("chmod \"%s\" to mode %o", path, m);
|
|
|
1abbee |
- if (chmod(path, m) < 0)
|
|
|
1abbee |
- return log_error_errno(errno, "chmod(%s) failed: %m", path);
|
|
|
1abbee |
+ if (m == (st.st_mode & 07777))
|
|
|
1abbee |
+ log_debug("\"%s\" has right mode %o", path, st.st_mode);
|
|
|
1abbee |
+ else {
|
|
|
1abbee |
+ log_debug("chmod \"%s\" to mode %o", path, m);
|
|
|
1abbee |
+ if (chmod(fn, m) < 0)
|
|
|
1abbee |
+ return log_error_errno(errno, "chmod(%s) failed: %m", path);
|
|
|
1abbee |
+ }
|
|
|
1abbee |
}
|
|
|
1abbee |
- }
|
|
|
1abbee |
-
|
|
|
1abbee |
- if ((!st_valid || i->uid != st.st_uid || i->gid != st.st_gid) &&
|
|
|
1abbee |
- (i->uid_set || i->gid_set)) {
|
|
|
1abbee |
- log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
|
|
|
1abbee |
- path,
|
|
|
1abbee |
- i->uid_set ? i->uid : UID_INVALID,
|
|
|
1abbee |
- i->gid_set ? i->gid : GID_INVALID);
|
|
|
1abbee |
- if (chown(path,
|
|
|
1abbee |
- i->uid_set ? i->uid : UID_INVALID,
|
|
|
1abbee |
- i->gid_set ? i->gid : GID_INVALID) < 0)
|
|
|
1abbee |
|
|
|
1abbee |
+ if ((i->uid != st.st_uid || i->gid != st.st_gid) &&
|
|
|
1abbee |
+ (i->uid_set || i->gid_set)) {
|
|
|
1abbee |
+ log_debug("chown \"%s\" to "UID_FMT"."GID_FMT,
|
|
|
1abbee |
+ path,
|
|
|
1abbee |
+ i->uid_set ? i->uid : UID_INVALID,
|
|
|
1abbee |
+ i->gid_set ? i->gid : GID_INVALID);
|
|
|
1abbee |
+ if (chown(fn,
|
|
|
1abbee |
+ i->uid_set ? i->uid : UID_INVALID,
|
|
|
1abbee |
+ i->gid_set ? i->gid : GID_INVALID) < 0)
|
|
|
1abbee |
return log_error_errno(errno, "chown(%s) failed: %m", path);
|
|
|
1abbee |
+ }
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
+ fd = safe_close(fd);
|
|
|
1abbee |
+
|
|
|
1abbee |
return label_fix(path, false, false);
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
@@ -699,10 +717,10 @@ static int get_acls_from_arg(Item *item) {
|
|
|
1abbee |
}
|
|
|
1abbee |
|
|
|
1abbee |
#ifdef HAVE_ACL
|
|
|
1abbee |
-static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modify) {
|
|
|
1abbee |
+static int path_set_acl(const char *path, const char *pretty, acl_type_t type, acl_t acl, bool modify) {
|
|
|
1abbee |
+ _cleanup_(acl_free_charpp) char *t = NULL;
|
|
|
1abbee |
_cleanup_(acl_freep) acl_t dup = NULL;
|
|
|
1abbee |
int r;
|
|
|
1abbee |
- _cleanup_(acl_free_charpp) char *t = NULL;
|
|
|
1abbee |
|
|
|
1abbee |
/* Returns 0 for success, positive error if already warned,
|
|
|
1abbee |
* negative error otherwise. */
|
|
|
1abbee |
@@ -728,16 +746,16 @@ static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modif
|
|
|
1abbee |
return r;
|
|
|
1abbee |
|
|
|
1abbee |
t = acl_to_any_text(dup, NULL, ',', TEXT_ABBREVIATE);
|
|
|
1abbee |
- log_debug("\"%s\": setting %s ACL \"%s\"", path,
|
|
|
1abbee |
+ log_debug("Setting %s ACL %s on %s.",
|
|
|
1abbee |
type == ACL_TYPE_ACCESS ? "access" : "default",
|
|
|
1abbee |
- strna(t));
|
|
|
1abbee |
+ strna(t), pretty);
|
|
|
1abbee |
|
|
|
1abbee |
r = acl_set_file(path, type, dup);
|
|
|
1abbee |
if (r < 0)
|
|
|
1abbee |
return -log_error_errno(errno,
|
|
|
1abbee |
"Setting %s ACL \"%s\" on %s failed: %m",
|
|
|
1abbee |
type == ACL_TYPE_ACCESS ? "access" : "default",
|
|
|
1abbee |
- strna(t), path);
|
|
|
1abbee |
+ strna(t), pretty);
|
|
|
1abbee |
|
|
|
1abbee |
return 0;
|
|
|
1abbee |
}
|
|
|
1abbee |
@@ -746,14 +764,32 @@ static int path_set_acl(const char *path, acl_type_t type, acl_t acl, bool modif
|
|
|
1abbee |
static int path_set_acls(Item *item, const char *path) {
|
|
|
1abbee |
int r = 0;
|
|
|
1abbee |
#ifdef HAVE_ACL
|
|
|
1abbee |
+ char fn[strlen("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
|
|
1abbee |
+ _cleanup_close_ int fd = -1;
|
|
|
1abbee |
+ struct stat st;
|
|
|
1abbee |
+
|
|
|
1abbee |
assert(item);
|
|
|
1abbee |
assert(path);
|
|
|
1abbee |
|
|
|
1abbee |
+ fd = open(path, O_RDONLY|O_NOFOLLOW|O_CLOEXEC|O_PATH|O_NOATIME);
|
|
|
1abbee |
+ if (fd < 0)
|
|
|
1abbee |
+ return log_error_errno(errno, "Adjusting ACL of %s failed: %m", path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (fstatat(fd, "", &st, AT_EMPTY_PATH) < 0)
|
|
|
1abbee |
+ return log_error_errno(errno, "Failed to fstat() file %s: %m", path);
|
|
|
1abbee |
+
|
|
|
1abbee |
+ if (S_ISLNK(st.st_mode)) {
|
|
|
1abbee |
+ log_debug("Skipping ACL fix for symlink %s.", path);
|
|
|
1abbee |
+ return 0;
|
|
|
1abbee |
+ }
|
|
|
1abbee |
+
|
|
|
1abbee |
+ xsprintf(fn, "/proc/self/fd/%i", fd);
|
|
|
1abbee |
+
|
|
|
1abbee |
if (item->acl_access)
|
|
|
1abbee |
- r = path_set_acl(path, ACL_TYPE_ACCESS, item->acl_access, item->force);
|
|
|
1abbee |
+ r = path_set_acl(fn, path, ACL_TYPE_ACCESS, item->acl_access, item->force);
|
|
|
1abbee |
|
|
|
1abbee |
if (r == 0 && item->acl_default)
|
|
|
1abbee |
- r = path_set_acl(path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
|
|
|
1abbee |
+ r = path_set_acl(fn, path, ACL_TYPE_DEFAULT, item->acl_default, item->force);
|
|
|
1abbee |
|
|
|
1abbee |
if (r > 0)
|
|
|
1abbee |
return -r; /* already warned */
|