]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/tmpfiles/tmpfiles.c
libpasswdqc: add missing `#include "dlfcn-util.h"`
[thirdparty/systemd.git] / src / tmpfiles / tmpfiles.c
index 73880245568917f7dd2a91876771c94d0c6da995..807925f199d4eb631d8415825437fe57b7172c51 100644 (file)
@@ -49,7 +49,6 @@
 #include "mkdir-label.h"
 #include "mount-util.h"
 #include "mountpoint-util.h"
-#include "nulstr-util.h"
 #include "offline-passwd.h"
 #include "pager.h"
 #include "parse-argument.h"
@@ -68,6 +67,7 @@
 #include "string-table.h"
 #include "string-util.h"
 #include "strv.h"
+#include "sysctl-util.h"
 #include "terminal-util.h"
 #include "umask-util.h"
 #include "user-util.h"
@@ -370,7 +370,7 @@ static int user_config_paths(char*** ret) {
         if (r < 0 && !ERRNO_IS_NOINFO(r))
                 return r;
 
-        r = strv_extend_strv_concat(&res, config_dirs, "/user-tmpfiles.d");
+        r = strv_extend_strv_concat(&res, (const char* const*) config_dirs, "/user-tmpfiles.d");
         if (r < 0)
                 return r;
 
@@ -382,7 +382,7 @@ static int user_config_paths(char*** ret) {
         if (r < 0)
                 return r;
 
-        r = strv_extend_strv_concat(&res, data_dirs, "/user-tmpfiles.d");
+        r = strv_extend_strv_concat(&res, (const char* const*) data_dirs, "/user-tmpfiles.d");
         if (r < 0)
                 return r;
 
@@ -892,9 +892,9 @@ static int dir_cleanup(
                                 continue;
 
                         if (!arg_dry_run) {
-                                fd = xopenat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME|O_NONBLOCK);
+                                fd = xopenat(dirfd(d), de->d_name, O_RDONLY|O_CLOEXEC|O_NOFOLLOW|O_NOATIME|O_NONBLOCK|O_NOCTTY);
                                 if (fd < 0 && !IN_SET(fd, -ENOENT, -ELOOP))
-                                        log_warning_errno(fd, "Opening file \"%s\" failed, ignoring: %m", sub_path);
+                                        log_warning_errno(fd, "Opening file \"%s\" failed, proceeding without lock: %m", sub_path);
                                 if (fd >= 0 && flock(fd, LOCK_EX|LOCK_NB) < 0 && errno == EAGAIN) {
                                         log_debug_errno(errno, "Couldn't acquire shared BSD lock on file \"%s\", skipping: %m", sub_path);
                                         continue;
@@ -933,37 +933,35 @@ finish:
         return r;
 }
 
-static bool dangerous_hardlinks(void) {
-        _cleanup_free_ char *value = NULL;
+static bool hardlinks_protected(void) {
         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. */
+         * as that's what the kernel default is.
+         * Note that we ship 50-default.conf where it is enabled, but better be safe than sorry. */
 
         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;
-        }
+        _cleanup_free_ char *value = NULL;
 
-        r = parse_boolean(value);
+        r = sysctl_read("fs/protected_hardlinks", &value);
         if (r < 0) {
-                log_debug_errno(r, "Failed to parse fs.protected_hardlinks sysctl: %m");
-                return true;
+                log_debug_errno(r, "Failed to read fs.protected_hardlinks sysctl, assuming disabled: %m");
+                return false;
         }
 
-        cached = r == 0;
-        return cached;
+        cached = parse_boolean(value);
+        if (cached < 0)
+                log_debug_errno(cached, "Failed to parse fs.protected_hardlinks sysctl, assuming disabled: %m");
+        return cached > 0;
 }
 
 static bool hardlink_vulnerable(const struct stat *st) {
         assert(st);
 
-        return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && dangerous_hardlinks();
+        return !S_ISDIR(st->st_mode) && st->st_nlink > 1 && !hardlinks_protected();
 }
 
 static mode_t process_mask_perms(mode_t mode, mode_t current) {
@@ -1258,60 +1256,47 @@ static int parse_acls_from_arg(Item *item) {
 #if HAVE_ACL
 static int parse_acl_cond_exec(
                 const char *path,
-                acl_t access, /* could be empty (NULL) */
-                acl_t cond_exec,
                 const struct stat *st,
+                acl_t cond_exec,
+                acl_t access, /* could be empty (NULL) */
                 bool append,
                 acl_t *ret) {
 
-        _cleanup_(acl_freep) acl_t parsed = NULL;
         acl_entry_t entry;
         acl_permset_t permset;
         bool has_exec;
         int r;
 
         assert(path);
-        assert(ret);
         assert(st);
+        assert(cond_exec);
+        assert(ret);
 
-        parsed = access ? acl_dup(access) : acl_init(0);
-        if (!parsed)
-                return -errno;
-
-        /* Since we substitute 'X' with 'x' in parse_acl(), we just need to copy the entries over
-         * for directories */
-        if (S_ISDIR(st->st_mode)) {
-                for (r = acl_get_entry(cond_exec, ACL_FIRST_ENTRY, &entry);
-                     r > 0;
-                     r = acl_get_entry(cond_exec, ACL_NEXT_ENTRY, &entry)) {
-
-                        acl_entry_t parsed_entry;
-
-                        if (acl_create_entry(&parsed, &parsed_entry) < 0)
-                                return -errno;
-
-                        if (acl_copy_entry(parsed_entry, entry) < 0)
-                                return -errno;
-                }
-                if (r < 0)
-                        return -errno;
-
-                goto finish;
-        }
-
-        has_exec = st->st_mode & S_IXUSR;
-
-        if (!has_exec && append) {
+        if (!S_ISDIR(st->st_mode)) {
                 _cleanup_(acl_freep) acl_t old = NULL;
 
                 old = acl_get_file(path, ACL_TYPE_ACCESS);
                 if (!old)
                         return -errno;
 
+                has_exec = false;
+
                 for (r = acl_get_entry(old, ACL_FIRST_ENTRY, &entry);
                      r > 0;
                      r = acl_get_entry(old, ACL_NEXT_ENTRY, &entry)) {
 
+                        acl_tag_t tag;
+
+                        if (acl_get_tag_type(entry, &tag) < 0)
+                                return -errno;
+
+                        if (tag == ACL_MASK)
+                                continue;
+
+                        /* If not appending, skip ACL definitions */
+                        if (!append && IN_SET(tag, ACL_USER, ACL_GROUP))
+                                continue;
+
                         if (acl_get_permset(entry, &permset) < 0)
                                 return -errno;
 
@@ -1325,28 +1310,33 @@ static int parse_acl_cond_exec(
                 }
                 if (r < 0)
                         return -errno;
-        }
 
-        /* Check if we're about to set the execute bit in acl_access */
-        if (!has_exec && access) {
-                for (r = acl_get_entry(access, ACL_FIRST_ENTRY, &entry);
-                     r > 0;
-                     r = acl_get_entry(access, ACL_NEXT_ENTRY, &entry)) {
+                /* Check if we're about to set the execute bit in acl_access */
+                if (!has_exec && access) {
+                        for (r = acl_get_entry(access, ACL_FIRST_ENTRY, &entry);
+                             r > 0;
+                             r = acl_get_entry(access, ACL_NEXT_ENTRY, &entry)) {
 
-                        if (acl_get_permset(entry, &permset) < 0)
-                                return -errno;
+                                if (acl_get_permset(entry, &permset) < 0)
+                                        return -errno;
 
-                        r = acl_get_perm(permset, ACL_EXECUTE);
+                                r = acl_get_perm(permset, ACL_EXECUTE);
+                                if (r < 0)
+                                        return -errno;
+                                if (r > 0) {
+                                        has_exec = true;
+                                        break;
+                                }
+                        }
                         if (r < 0)
                                 return -errno;
-                        if (r > 0) {
-                                has_exec = true;
-                                break;
-                        }
                 }
-                if (r < 0)
-                        return -errno;
-        }
+        } else
+                has_exec = true;
+
+        _cleanup_(acl_freep) acl_t parsed = access ? acl_dup(access) : acl_init(0);
+        if (!parsed)
+                return -errno;
 
         for (r = acl_get_entry(cond_exec, ACL_FIRST_ENTRY, &entry);
              r > 0;
@@ -1360,6 +1350,7 @@ static int parse_acl_cond_exec(
                 if (acl_copy_entry(parsed_entry, entry) < 0)
                         return -errno;
 
+                /* We substituted 'X' with 'x' in parse_acl(), so drop execute bit here if not applicable. */
                 if (!has_exec) {
                         if (acl_get_permset(parsed_entry, &permset) < 0)
                                 return -errno;
@@ -1371,7 +1362,6 @@ static int parse_acl_cond_exec(
         if (r < 0)
                 return -errno;
 
-finish:
         if (!append) { /* want_mask = true */
                 r = calc_acl_mask_if_needed(&parsed);
                 if (r < 0)
@@ -1476,10 +1466,9 @@ static int fd_set_acls(
         }
 
         if (item->acl_access_exec) {
-                r = parse_acl_cond_exec(FORMAT_PROC_FD_PATH(fd),
-                                        item->acl_access,
+                r = parse_acl_cond_exec(FORMAT_PROC_FD_PATH(fd), st,
                                         item->acl_access_exec,
-                                        st,
+                                        item->acl_access,
                                         item->append_or_force,
                                         &access_with_exec_parsed);
                 if (r < 0)
@@ -2522,12 +2511,13 @@ static int item_do(
                 fdaction_t action) {
 
         struct stat st;
-        int r = 0, q;
+        int r;
 
         assert(c);
         assert(i);
-        assert(path);
         assert(fd >= 0);
+        assert(path);
+        assert(action);
 
         if (fstat(fd, &st) < 0) {
                 r = log_error_errno(errno, "fstat() on file failed: %m");
@@ -2544,36 +2534,35 @@ static int item_do(
                  * reading the directory content. */
                 d = opendir(FORMAT_PROC_FD_PATH(fd));
                 if (!d) {
-                        log_error_errno(errno, "Failed to opendir() '%s': %m", FORMAT_PROC_FD_PATH(fd));
-                        if (r == 0)
-                                r = -errno;
+                        RET_GATHER(r, log_error_errno(errno, "Failed to opendir() '%s': %m", FORMAT_PROC_FD_PATH(fd)));
                         goto finish;
                 }
 
-                FOREACH_DIRENT_ALL(de, d, q = -errno; goto finish) {
-                        int de_fd;
+                FOREACH_DIRENT_ALL(de, d, RET_GATHER(r, -errno); goto finish) {
+                        _cleanup_close_ int de_fd = -EBADF;
+                        _cleanup_free_ char *de_path = NULL;
 
                         if (dot_or_dot_dot(de->d_name))
                                 continue;
 
                         de_fd = openat(fd, de->d_name, O_NOFOLLOW|O_CLOEXEC|O_PATH);
-                        if (de_fd < 0)
-                                q = log_error_errno(errno, "Failed to open() file '%s': %m", de->d_name);
-                        else {
-                                _cleanup_free_ char *de_path = NULL;
-
-                                de_path = path_join(path, de->d_name);
-                                if (!de_path)
-                                        q = log_oom();
-                                else
-                                        /* Pass ownership of dirent fd over */
-                                        q = item_do(c, i, de_fd, de_path, CREATION_EXISTING, action);
+                        if (de_fd < 0) {
+                                if (errno != ENOENT)
+                                        RET_GATHER(r, log_error_errno(errno, "Failed to open file '%s': %m", de->d_name));
+                                continue;
                         }
 
-                        if (q < 0 && r == 0)
-                                r = q;
+                        de_path = path_join(path, de->d_name);
+                        if (!de_path) {
+                                r = log_oom();
+                                goto finish;
+                        }
+
+                        /* Pass ownership of dirent fd over */
+                        RET_GATHER(r, item_do(c, i, TAKE_FD(de_fd), de_path, CREATION_EXISTING, action));
                 }
         }
+
 finish:
         safe_close(fd);
         return r;
@@ -2583,21 +2572,22 @@ static int glob_item(Context *c, Item *i, action_t action) {
         _cleanup_globfree_ glob_t g = {
                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
         };
-        int r = 0, k;
+        int r;
 
         assert(c);
         assert(i);
+        assert(action);
 
-        k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
-        if (k < 0 && k != -ENOENT)
-                return log_error_errno(k, "glob(%s) failed: %m", i->path);
+        r = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to glob '%s': %m", i->path);
 
-        STRV_FOREACH(fn, g.gl_pathv) {
+        r = 0;
+        STRV_FOREACH(fn, g.gl_pathv)
                 /* We pass CREATION_EXISTING here, since if we are globbing for it, it always has to exist */
-                k = action(c, i, *fn, CREATION_EXISTING);
-                if (k < 0 && r == 0)
-                        r = k;
-        }
+                RET_GATHER(r, action(c, i, *fn, CREATION_EXISTING));
 
         return r;
 }
@@ -2610,34 +2600,33 @@ static int glob_item_recursively(
         _cleanup_globfree_ glob_t g = {
                 .gl_opendir = (void *(*)(const char *)) opendir_nomod,
         };
-        int r = 0, k;
+        int r;
+
+        assert(c);
+        assert(i);
+        assert(action);
 
-        k = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
-        if (k < 0 && k != -ENOENT)
-                return log_error_errno(k, "glob(%s) failed: %m", i->path);
+        r = safe_glob(i->path, GLOB_NOSORT|GLOB_BRACE, &g);
+        if (r == -ENOENT)
+                return 0;
+        if (r < 0)
+                return log_error_errno(r, "Failed to glob '%s': %m", i->path);
 
+        r = 0;
         STRV_FOREACH(fn, g.gl_pathv) {
                 _cleanup_close_ int fd = -EBADF;
 
-                /* Make sure we won't trigger/follow file object (such as
-                 * device nodes, automounts, ...) pointed out by 'fn' with
-                 * O_PATH. Note, when O_PATH is used, flags other than
+                /* Make sure we won't trigger/follow file object (such as device nodes, automounts, ...)
+                 * pointed out by 'fn' with O_PATH. Note, when O_PATH is used, flags other than
                  * O_CLOEXEC, O_DIRECTORY, and O_NOFOLLOW are ignored. */
 
                 fd = open(*fn, O_CLOEXEC|O_NOFOLLOW|O_PATH);
                 if (fd < 0) {
-                        log_error_errno(errno, "Opening '%s' failed: %m", *fn);
-                        if (r == 0)
-                                r = -errno;
+                        RET_GATHER(r, log_error_errno(errno, "Failed to open '%s': %m", *fn));
                         continue;
                 }
 
-                k = item_do(c, i, fd, *fn, CREATION_EXISTING, action);
-                if (k < 0 && r == 0)
-                        r = k;
-
-                /* we passed fd ownership to the previous call */
-                fd = -EBADF;
+                RET_GATHER(r, item_do(c, i, TAKE_FD(fd), *fn, CREATION_EXISTING, action));
         }
 
         return r;
@@ -2668,7 +2657,7 @@ static int rm_if_wrong_type_safe(
         r = fstatat_harder(parent_fd, name, &st, flags, REMOVE_CHMOD | REMOVE_CHMOD_RESTORE);
         if (r < 0) {
                 (void) fd_get_path(parent_fd, &parent_name);
-                return log_full_errno(r == -ENOENT? LOG_DEBUG : LOG_ERR, r,
+                return log_full_errno(r == -ENOENT ? LOG_DEBUG : LOG_ERR, r,
                                       "Failed to stat \"%s/%s\": %m", parent_name ?: "...", name);
         }
 
@@ -3649,8 +3638,7 @@ static int parse_line(
                         &mode,
                         &user,
                         &group,
-                        &age,
-                        NULL);
+                        &age);
         if (r < 0) {
                 if (IN_SET(r, -EINVAL, -EBADSLT))
                         /* invalid quoting and such or an unknown specifier */
@@ -4580,7 +4568,7 @@ static int run(int argc, char *argv[]) {
                 break;
 
         case RUNTIME_SCOPE_SYSTEM:
-                config_dirs = strv_split_nulstr(CONF_PATHS_NULSTR("tmpfiles.d"));
+                config_dirs = strv_new(CONF_PATHS("tmpfiles.d"));
                 if (!config_dirs)
                         return log_oom();
                 break;