#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"
#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"
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;
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;
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;
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) {
#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;
}
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;
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;
if (r < 0)
return -errno;
-finish:
if (!append) { /* want_mask = true */
r = calc_acl_mask_if_needed(&parsed);
if (r < 0)
}
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)
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");
* 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;
_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;
}
_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;
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);
}
&mode,
&user,
&group,
- &age,
- NULL);
+ &age);
if (r < 0) {
if (IN_SET(r, -EINVAL, -EBADSLT))
/* invalid quoting and such or an unknown specifier */
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;