Merge pull request #8605 from poettering/drop-in-name-fix
Fix validation of unit file drop-in naming in install.c.
This commit is contained in:
commit
c0f9017c11
|
@ -427,7 +427,6 @@ int move_fd(int from, int to, int cloexec) {
|
|||
|
||||
int acquire_data_fd(const void *data, size_t size, unsigned flags) {
|
||||
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_pair_ int pipefds[2] = { -1, -1 };
|
||||
char pattern[] = "/dev/shm/data-fd-XXXXXX";
|
||||
_cleanup_close_ int fd = -1;
|
||||
|
@ -537,12 +536,7 @@ try_dev_shm:
|
|||
return -EIO;
|
||||
|
||||
/* Let's reopen the thing, in order to get an O_RDONLY fd for the original O_RDWR one */
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
r = open(procfs_path, O_RDONLY|O_CLOEXEC);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
return fd_reopen(fd, O_RDONLY|O_CLOEXEC);
|
||||
}
|
||||
|
||||
try_dev_shm_without_o_tmpfile:
|
||||
|
@ -725,3 +719,22 @@ finish:
|
|||
|
||||
return r;
|
||||
}
|
||||
|
||||
int fd_reopen(int fd, int flags) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
int new_fd;
|
||||
|
||||
/* Reopens the specified fd with new flags. This is useful for convert an O_PATH fd into a regular one, or to
|
||||
* turn O_RDWR fds into O_RDONLY fds.
|
||||
*
|
||||
* This doesn't work on sockets (since they cannot be open()ed, ever).
|
||||
*
|
||||
* This implicitly resets the file read index to 0. */
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
new_fd = open(procfs_path, flags);
|
||||
if (new_fd < 0)
|
||||
return -errno;
|
||||
|
||||
return new_fd;
|
||||
}
|
||||
|
|
|
@ -113,3 +113,5 @@ static inline int make_null_stdio(void) {
|
|||
(fd) = -1; \
|
||||
_fd_; \
|
||||
})
|
||||
|
||||
int fd_reopen(int fd, int flags);
|
||||
|
|
|
@ -577,6 +577,10 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
|
|||
return r;
|
||||
}
|
||||
|
||||
static bool noop_root(const char *root) {
|
||||
return isempty(root) || path_equal(root, "/");
|
||||
}
|
||||
|
||||
static bool safe_transition(const struct stat *a, const struct stat *b) {
|
||||
/* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
|
||||
* privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
|
||||
|
@ -627,9 +631,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
* specified path. */
|
||||
|
||||
/* A root directory of "/" or "" is identical to none */
|
||||
if (isempty(original_root) || path_equal(original_root, "/"))
|
||||
if (noop_root(original_root))
|
||||
original_root = NULL;
|
||||
|
||||
if (!original_root && !ret && (flags & (CHASE_NONEXISTENT|CHASE_NO_AUTOFS|CHASE_SAFE|CHASE_OPEN)) == CHASE_OPEN) {
|
||||
/* Shortcut the CHASE_OPEN case if the caller isn't interested in the actual path and has no root set
|
||||
* and doesn't care about any of the other special features we provide either. */
|
||||
r = open(path, O_PATH|O_CLOEXEC);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
if (original_root) {
|
||||
r = path_make_absolute_cwd(original_root, &root);
|
||||
if (r < 0)
|
||||
|
@ -874,6 +888,86 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
|
|||
return exists;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_open(
|
||||
const char *path,
|
||||
const char *root,
|
||||
unsigned chase_flags,
|
||||
int open_flags,
|
||||
char **ret_path) {
|
||||
|
||||
_cleanup_close_ int path_fd = -1;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
int r;
|
||||
|
||||
if (chase_flags & CHASE_NONEXISTENT)
|
||||
return -EINVAL;
|
||||
|
||||
if (noop_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
|
||||
/* Shortcut this call if none of the special features of this call are requested */
|
||||
r = open(path, open_flags);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
|
||||
if (path_fd < 0)
|
||||
return path_fd;
|
||||
|
||||
r = fd_reopen(path_fd, open_flags);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
int chase_symlinks_and_opendir(
|
||||
const char *path,
|
||||
const char *root,
|
||||
unsigned chase_flags,
|
||||
char **ret_path,
|
||||
DIR **ret_dir) {
|
||||
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_ int path_fd = -1;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
DIR *d;
|
||||
|
||||
if (!ret_dir)
|
||||
return -EINVAL;
|
||||
if (chase_flags & CHASE_NONEXISTENT)
|
||||
return -EINVAL;
|
||||
|
||||
if (noop_root(root) && !ret_path && (chase_flags & (CHASE_NO_AUTOFS|CHASE_SAFE)) == 0) {
|
||||
/* Shortcut this call if none of the special features of this call are requested */
|
||||
d = opendir(path);
|
||||
if (!d)
|
||||
return -errno;
|
||||
|
||||
*ret_dir = d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
path_fd = chase_symlinks(path, root, chase_flags|CHASE_OPEN, ret_path ? &p : NULL);
|
||||
if (path_fd < 0)
|
||||
return path_fd;
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", path_fd);
|
||||
d = opendir(procfs_path);
|
||||
if (!d)
|
||||
return -errno;
|
||||
|
||||
if (ret_path)
|
||||
*ret_path = TAKE_PTR(p);
|
||||
|
||||
*ret_dir = d;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int access_fd(int fd, int mode) {
|
||||
char p[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(fd) + 1];
|
||||
int r;
|
||||
|
@ -881,10 +975,9 @@ int access_fd(int fd, int mode) {
|
|||
/* Like access() but operates on an already open fd */
|
||||
|
||||
xsprintf(p, "/proc/self/fd/%i", fd);
|
||||
|
||||
r = access(p, mode);
|
||||
if (r < 0)
|
||||
r = -errno;
|
||||
return -errno;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
along with systemd; If not, see <http://www.gnu.org/licenses/>.
|
||||
***/
|
||||
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
|
@ -91,6 +92,9 @@ enum {
|
|||
|
||||
int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
|
||||
|
||||
int chase_symlinks_and_open(const char *path, const char *root, unsigned chase_flags, int open_flags, char **ret_path);
|
||||
int chase_symlinks_and_opendir(const char *path, const char *root, unsigned chase_flags, char **ret_path, DIR **ret_dir);
|
||||
|
||||
/* Useful for usage with _cleanup_(), removes a directory and frees the pointer */
|
||||
static inline void rmdir_and_free(char *p) {
|
||||
PROTECT_ERRNO;
|
||||
|
|
|
@ -460,7 +460,6 @@ static int bus_append_cgroup_property(sd_bus_message *m, const char *field, cons
|
|||
return bus_append_safe_atou64(m, field, eq);
|
||||
|
||||
return bus_append_parse_size(m, field, eq, 1024);
|
||||
|
||||
}
|
||||
|
||||
if (streq(field, "CPUQuota")) {
|
||||
|
|
|
@ -1299,18 +1299,14 @@ int dissected_image_acquire_metadata(DissectedImage *m) {
|
|||
fds[2*k] = safe_close(fds[2*k]);
|
||||
|
||||
NULSTR_FOREACH(p, paths[k]) {
|
||||
_cleanup_free_ char *q = NULL;
|
||||
|
||||
r = chase_symlinks(p, t, CHASE_PREFIX_ROOT|CHASE_TRAIL_SLASH, &q);
|
||||
if (r < 0)
|
||||
continue;
|
||||
|
||||
fd = open(q, O_RDONLY|O_CLOEXEC|O_NOCTTY);
|
||||
fd = chase_symlinks_and_open(p, t, CHASE_PREFIX_ROOT, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
|
||||
if (fd >= 0)
|
||||
break;
|
||||
}
|
||||
if (fd < 0)
|
||||
if (fd < 0) {
|
||||
log_debug_errno(fd, "Failed to read %s file of image, ignoring: %m", paths[k]);
|
||||
continue;
|
||||
}
|
||||
|
||||
r = copy_bytes(fd, fds[2*k+1], (uint64_t) -1, 0);
|
||||
if (r < 0)
|
||||
|
|
|
@ -59,8 +59,9 @@
|
|||
#define UNIT_FILE_FOLLOW_SYMLINK_MAX 64
|
||||
|
||||
typedef enum SearchFlags {
|
||||
SEARCH_LOAD = 1,
|
||||
SEARCH_FOLLOW_CONFIG_SYMLINKS = 2,
|
||||
SEARCH_LOAD = 1U << 0,
|
||||
SEARCH_FOLLOW_CONFIG_SYMLINKS = 1U << 1,
|
||||
SEARCH_DROPIN = 1U << 2,
|
||||
} SearchFlags;
|
||||
|
||||
typedef struct {
|
||||
|
@ -1228,6 +1229,7 @@ static int unit_file_load(
|
|||
InstallContext *c,
|
||||
UnitFileInstallInfo *info,
|
||||
const char *path,
|
||||
const char *root_dir,
|
||||
SearchFlags flags) {
|
||||
|
||||
const ConfigTableItem items[] = {
|
||||
|
@ -1248,40 +1250,57 @@ static int unit_file_load(
|
|||
assert(info);
|
||||
assert(path);
|
||||
|
||||
type = unit_name_to_type(info->name);
|
||||
if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) &&
|
||||
!unit_type_may_template(type))
|
||||
return log_error_errno(EINVAL, "Unit type %s cannot be templated.", unit_type_to_string(type));
|
||||
if (!(flags & SEARCH_DROPIN)) {
|
||||
/* Loading or checking for the main unit file… */
|
||||
|
||||
if (!(flags & SEARCH_LOAD)) {
|
||||
r = lstat(path, &st);
|
||||
if (r < 0)
|
||||
type = unit_name_to_type(info->name);
|
||||
if (type < 0)
|
||||
return -EINVAL;
|
||||
if (unit_name_is_valid(info->name, UNIT_NAME_TEMPLATE|UNIT_NAME_INSTANCE) && !unit_type_may_template(type)) {
|
||||
log_error("Unit type %s cannot be templated.", unit_type_to_string(type));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (!(flags & SEARCH_LOAD)) {
|
||||
r = lstat(path, &st);
|
||||
if (r < 0)
|
||||
return -errno;
|
||||
|
||||
if (null_or_empty(&st))
|
||||
info->type = UNIT_FILE_TYPE_MASKED;
|
||||
else if (S_ISREG(st.st_mode))
|
||||
info->type = UNIT_FILE_TYPE_REGULAR;
|
||||
else if (S_ISLNK(st.st_mode))
|
||||
return -ELOOP;
|
||||
else if (S_ISDIR(st.st_mode))
|
||||
return -EISDIR;
|
||||
else
|
||||
return -ENOTTY;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
} else {
|
||||
/* Operating on a drop-in file. If we aren't supposed to load the unit file drop-ins don't matter, let's hence shortcut this. */
|
||||
|
||||
if (null_or_empty(&st))
|
||||
info->type = UNIT_FILE_TYPE_MASKED;
|
||||
else if (S_ISREG(st.st_mode))
|
||||
info->type = UNIT_FILE_TYPE_REGULAR;
|
||||
else if (S_ISLNK(st.st_mode))
|
||||
return -ELOOP;
|
||||
else if (S_ISDIR(st.st_mode))
|
||||
return -EISDIR;
|
||||
else
|
||||
return -ENOTTY;
|
||||
if (!(flags & SEARCH_LOAD))
|
||||
return 0;
|
||||
|
||||
return 0;
|
||||
fd = chase_symlinks_and_open(path, root_dir, 0, O_RDONLY|O_CLOEXEC|O_NOCTTY, NULL);
|
||||
if (fd < 0)
|
||||
return fd;
|
||||
}
|
||||
|
||||
/* c is only needed if we actually load the file */
|
||||
assert(c);
|
||||
|
||||
fd = open(path, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW);
|
||||
if (fd < 0)
|
||||
return -errno;
|
||||
if (fstat(fd, &st) < 0)
|
||||
return -errno;
|
||||
|
||||
if (null_or_empty(&st)) {
|
||||
info->type = UNIT_FILE_TYPE_MASKED;
|
||||
if ((flags & SEARCH_DROPIN) == 0)
|
||||
info->type = UNIT_FILE_TYPE_MASKED;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -1294,6 +1313,9 @@ static int unit_file_load(
|
|||
return -errno;
|
||||
fd = -1;
|
||||
|
||||
/* c is only needed if we actually load the file (it's referenced from items[] btw, in case you wonder.) */
|
||||
assert(c);
|
||||
|
||||
r = config_parse(info->name, path, f,
|
||||
NULL,
|
||||
config_item_table_lookup, items,
|
||||
|
@ -1301,7 +1323,8 @@ static int unit_file_load(
|
|||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to parse %s: %m", info->name);
|
||||
|
||||
info->type = UNIT_FILE_TYPE_REGULAR;
|
||||
if ((flags & SEARCH_DROPIN) == 0)
|
||||
info->type = UNIT_FILE_TYPE_REGULAR;
|
||||
|
||||
return
|
||||
(int) strv_length(info->aliases) +
|
||||
|
@ -1319,8 +1342,8 @@ static int unit_file_load_or_readlink(
|
|||
_cleanup_free_ char *target = NULL;
|
||||
int r;
|
||||
|
||||
r = unit_file_load(c, info, path, flags);
|
||||
if (r != -ELOOP)
|
||||
r = unit_file_load(c, info, path, root_dir, flags);
|
||||
if (r != -ELOOP || (flags & SEARCH_DROPIN))
|
||||
return r;
|
||||
|
||||
/* This is a symlink, let's read it. */
|
||||
|
@ -1383,16 +1406,12 @@ static int unit_file_search(
|
|||
const LookupPaths *paths,
|
||||
SearchFlags flags) {
|
||||
|
||||
const char *dropin_dir_name = NULL, *dropin_template_dir_name = NULL;
|
||||
_cleanup_strv_free_ char **dirs = NULL, **files = NULL;
|
||||
_cleanup_free_ char *template = NULL;
|
||||
_cleanup_strv_free_ char **dirs = NULL;
|
||||
_cleanup_strv_free_ char **files = NULL;
|
||||
const char *dropin_dir_name = NULL;
|
||||
const char *dropin_template_dir_name = NULL;
|
||||
|
||||
char **p;
|
||||
int r;
|
||||
int result;
|
||||
bool found_unit = false;
|
||||
int r, result;
|
||||
char **p;
|
||||
|
||||
assert(info);
|
||||
assert(paths);
|
||||
|
@ -1420,7 +1439,6 @@ static int unit_file_search(
|
|||
return -ENOMEM;
|
||||
|
||||
r = unit_file_load_or_readlink(c, info, path, paths->root_dir, flags);
|
||||
|
||||
if (r >= 0) {
|
||||
info->path = TAKE_PTR(path);
|
||||
result = r;
|
||||
|
@ -1499,7 +1517,7 @@ static int unit_file_search(
|
|||
return log_debug_errno(r, "Failed to get list of conf files: %m");
|
||||
|
||||
STRV_FOREACH(p, files) {
|
||||
r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags);
|
||||
r = unit_file_load_or_readlink(c, info, *p, paths->root_dir, flags | SEARCH_DROPIN);
|
||||
if (r < 0)
|
||||
return log_debug_errno(r, "Failed to load conf file %s: %m", *p);
|
||||
}
|
||||
|
|
|
@ -270,17 +270,13 @@ static void test_chase_symlinks(void) {
|
|||
|
||||
pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
|
||||
if (pfd != -ENOENT) {
|
||||
char procfs[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(pfd) + 1];
|
||||
_cleanup_close_ int fd = -1;
|
||||
sd_id128_t a, b;
|
||||
|
||||
assert_se(pfd >= 0);
|
||||
|
||||
xsprintf(procfs, "/proc/self/fd/%i", pfd);
|
||||
|
||||
fd = open(procfs, O_RDONLY|O_CLOEXEC);
|
||||
fd = fd_reopen(pfd, O_RDONLY|O_CLOEXEC);
|
||||
assert_se(fd >= 0);
|
||||
|
||||
safe_close(pfd);
|
||||
|
||||
assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
|
||||
|
|
|
@ -1186,7 +1186,6 @@ static int parse_attribute_from_arg(Item *item) {
|
|||
}
|
||||
|
||||
static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
|
||||
char procfs_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int)];
|
||||
_cleanup_close_ int procfs_fd = -1;
|
||||
_cleanup_free_ char *path = NULL;
|
||||
unsigned f;
|
||||
|
@ -1213,9 +1212,7 @@ static int fd_set_attribute(Item *item, int fd, const struct stat *st) {
|
|||
if (!S_ISDIR(st->st_mode))
|
||||
f &= ~FS_DIRSYNC_FL;
|
||||
|
||||
xsprintf(procfs_path, "/proc/self/fd/%i", fd);
|
||||
|
||||
procfs_fd = open(procfs_path, O_RDONLY|O_CLOEXEC|O_NOATIME);
|
||||
procfs_fd = fd_reopen(fd, O_RDONLY|O_CLOEXEC|O_NOATIME);
|
||||
if (procfs_fd < 0)
|
||||
return -errno;
|
||||
|
||||
|
|
Loading…
Reference in a new issue