From 09942654d30c71718c5230d4423ad0b1ab6ebadb Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 20 Jul 2018 11:55:18 +0200 Subject: [PATCH 1/3] fileio: add additional safety checks Let's protect against attempts to create temporary files above the root dir, as that makes little sense. Let's better be safe than sorry. --- src/basic/fileio.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 6b0bad5b71..9ff9118031 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1225,9 +1225,13 @@ int tempfn_xxxxxx(const char *p, const char *extra, char **ret) { const char *fn; char *t; - assert(p); assert(ret); + if (isempty(p)) + return -EINVAL; + if (path_equal(p, "/")) + return -EINVAL; + /* * Turns this: * /foo/bar/waldo @@ -1258,9 +1262,13 @@ int tempfn_random(const char *p, const char *extra, char **ret) { uint64_t u; unsigned i; - assert(p); assert(ret); + if (isempty(p)) + return -EINVAL; + if (path_equal(p, "/")) + return -EINVAL; + /* * Turns this: * /foo/bar/waldo @@ -1311,7 +1319,8 @@ int tempfn_random_child(const char *p, const char *extra, char **ret) { r = tmp_dir(&p); if (r < 0) return r; - } + } else if (isempty(p)) + return -EINVAL; extra = strempty(extra); @@ -1404,7 +1413,8 @@ int open_tmpfile_unlinkable(const char *directory, int flags) { r = tmp_dir(&directory); if (r < 0) return r; - } + } else if (isempty(directory)) + return -EINVAL; /* Returns an unlinked temporary file that cannot be linked into the file system anymore */ From ef8becfac5f1d8e0cfce69afd3aadffb330efb70 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 20 Jul 2018 11:57:24 +0200 Subject: [PATCH 2/3] fs-util: introduce open_parent() helper We often open the parent directory of a path. Let's add a common helper for that, that shortens our code a bit and adds some extra safety checks, for example it will fail if used on the root directory (which doesn't really have a parent). The helper is actually generalized from a function in btrfs-util.[ch] which already existed for this purpose. --- src/basic/btrfs-util.c | 24 ++++-------------------- src/basic/fs-util.c | 28 ++++++++++++++++++++++++++++ src/basic/fs-util.h | 2 ++ 3 files changed, 34 insertions(+), 20 deletions(-) diff --git a/src/basic/btrfs-util.c b/src/basic/btrfs-util.c index 6d2490f3d7..efac0b9420 100644 --- a/src/basic/btrfs-util.c +++ b/src/basic/btrfs-util.c @@ -28,6 +28,7 @@ #include "device-nodes.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "io-util.h" #include "macro.h" #include "missing.h" @@ -59,23 +60,6 @@ static int validate_subvolume_name(const char *name) { return 0; } -static int open_parent(const char *path, int flags) { - _cleanup_free_ char *parent = NULL; - int fd; - - assert(path); - - parent = dirname_malloc(path); - if (!parent) - return -ENOMEM; - - fd = open(parent, flags); - if (fd < 0) - return -errno; - - return fd; -} - static int extract_subvolume_name(const char *path, const char **subvolume) { const char *fn; int r; @@ -144,7 +128,7 @@ int btrfs_subvol_make(const char *path) { if (r < 0) return r; - fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + fd = open_parent(path, O_CLOEXEC, 0); if (fd < 0) return fd; @@ -1283,7 +1267,7 @@ int btrfs_subvol_remove(const char *path, BtrfsRemoveFlags flags) { if (r < 0) return r; - fd = open_parent(path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + fd = open_parent(path, O_CLOEXEC, 0); if (fd < 0) return fd; @@ -1723,7 +1707,7 @@ int btrfs_subvol_snapshot_fd(int old_fd, const char *new_path, BtrfsSnapshotFlag if (r < 0) return r; - new_fd = open_parent(new_path, O_RDONLY|O_NOCTTY|O_CLOEXEC|O_DIRECTORY); + new_fd = open_parent(new_path, O_CLOEXEC, 0); if (new_fd < 0) return new_fd; diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 3a8b32d881..567f4af1ca 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -1195,3 +1195,31 @@ int fsync_directory_of_file(int fd) { return 0; } + +int open_parent(const char *path, int flags, mode_t mode) { + _cleanup_free_ char *parent = NULL; + int fd; + + if (isempty(path)) + return -EINVAL; + if (path_equal(path, "/")) /* requesting the parent of the root dir is fishy, let's prohibit that */ + return -EINVAL; + + parent = dirname_malloc(path); + if (!parent) + return -ENOMEM; + + /* Let's insist on O_DIRECTORY since the parent of a file or directory is a directory. Except if we open an + * O_TMPFILE file, because in that case we are actually create a regular file below the parent directory. */ + + if ((flags & O_PATH) == O_PATH) + flags |= O_DIRECTORY; + else if ((flags & O_TMPFILE) != O_TMPFILE) + flags |= O_DIRECTORY|O_RDONLY; + + fd = open(parent, flags, mode); + if (fd < 0) + return -errno; + + return fd; +} diff --git a/src/basic/fs-util.h b/src/basic/fs-util.h index 28566773c6..754163defd 100644 --- a/src/basic/fs-util.h +++ b/src/basic/fs-util.h @@ -103,3 +103,5 @@ void unlink_tempfilep(char (*p)[]); int unlinkat_deallocate(int fd, const char *name, int flags); int fsync_directory_of_file(int fd); + +int open_parent(const char *path, int flags, mode_t mode); From 0c462ea4ef6029b7974a052e44b6d1359a79953f Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Fri, 20 Jul 2018 12:02:14 +0200 Subject: [PATCH 3/3] tree-wide: port various bits over to open_parent() --- src/basic/fileio.c | 20 ++++++-------------- src/basic/fs-util.c | 10 +++------- src/basic/mount-util.c | 8 ++------ src/import/export-raw.c | 7 ++----- src/machine/machine-dbus.c | 8 +++----- 5 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/basic/fileio.c b/src/basic/fileio.c index 9ff9118031..20d3f567c9 100644 --- a/src/basic/fileio.c +++ b/src/basic/fileio.c @@ -1449,22 +1449,14 @@ int open_tmpfile_linkable(const char *target, int flags, char **ret_path) { * which case "ret_path" will be returned as NULL. If not possible a the tempoary path name used is returned in * "ret_path". Use link_tmpfile() below to rename the result after writing the file in full. */ - { - _cleanup_free_ char *dn = NULL; - - dn = dirname_malloc(target); - if (!dn) - return -ENOMEM; - - fd = open(dn, O_TMPFILE|flags, 0640); - if (fd >= 0) { - *ret_path = NULL; - return fd; - } - - log_debug_errno(errno, "Failed to use O_TMPFILE on %s: %m", dn); + fd = open_parent(target, O_TMPFILE|flags, 0640); + if (fd >= 0) { + *ret_path = NULL; + return fd; } + log_debug_errno(fd, "Failed to use O_TMPFILE for %s: %m", target); + r = tempfn_random(target, NULL, &tmp); if (r < 0) return r; diff --git a/src/basic/fs-util.c b/src/basic/fs-util.c index 567f4af1ca..aca9921de7 100644 --- a/src/basic/fs-util.c +++ b/src/basic/fs-util.c @@ -1156,7 +1156,7 @@ int unlinkat_deallocate(int fd, const char *name, int flags) { } int fsync_directory_of_file(int fd) { - _cleanup_free_ char *path = NULL, *dn = NULL; + _cleanup_free_ char *path = NULL; _cleanup_close_ int dfd = -1; int r; @@ -1182,13 +1182,9 @@ int fsync_directory_of_file(int fd) { if (!path_is_absolute(path)) return -EINVAL; - dn = dirname_malloc(path); - if (!dn) - return -ENOMEM; - - dfd = open(dn, O_RDONLY|O_CLOEXEC|O_DIRECTORY); + dfd = open_parent(path, O_CLOEXEC, 0); if (dfd < 0) - return -errno; + return dfd; if (fsync(dfd) < 0) return -errno; diff --git a/src/basic/mount-util.c b/src/basic/mount-util.c index ebe41a4c6c..54d911b095 100644 --- a/src/basic/mount-util.c +++ b/src/basic/mount-util.c @@ -261,7 +261,7 @@ fallback_fstat: /* flags can be AT_SYMLINK_FOLLOW or 0 */ int path_is_mount_point(const char *t, const char *root, int flags) { - _cleanup_free_ char *canonical = NULL, *parent = NULL; + _cleanup_free_ char *canonical = NULL; _cleanup_close_ int fd = -1; int r; @@ -283,11 +283,7 @@ int path_is_mount_point(const char *t, const char *root, int flags) { t = canonical; } - parent = dirname_malloc(t); - if (!parent) - return -ENOMEM; - - fd = openat(AT_FDCWD, parent, O_DIRECTORY|O_CLOEXEC|O_PATH); + fd = open_parent(t, O_PATH|O_CLOEXEC, 0); if (fd < 0) return -errno; diff --git a/src/import/export-raw.c b/src/import/export-raw.c index f97ae461c5..f3a34d40c5 100644 --- a/src/import/export-raw.c +++ b/src/import/export-raw.c @@ -16,6 +16,7 @@ #include "export-raw.h" #include "fd-util.h" #include "fileio.h" +#include "fs-util.h" #include "import-common.h" #include "missing.h" #include "ratelimit.h" @@ -244,13 +245,9 @@ static int raw_export_on_defer(sd_event_source *s, void *userdata) { } static int reflink_snapshot(int fd, const char *path) { - char *p, *d; int new_fd, r; - p = strdupa(path); - d = dirname(p); - - new_fd = open(d, O_TMPFILE|O_CLOEXEC|O_NOCTTY|O_RDWR, 0600); + new_fd = open_parent(path, O_TMPFILE|O_CLOEXEC|O_RDWR, 0600); if (new_fd < 0) { _cleanup_free_ char *t = NULL; diff --git a/src/machine/machine-dbus.c b/src/machine/machine-dbus.c index 7f41465ccd..77e8b161b0 100644 --- a/src/machine/machine-dbus.c +++ b/src/machine/machine-dbus.c @@ -1058,7 +1058,7 @@ finish: } int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_error *error) { - const char *src, *dest, *host_path, *container_path, *host_basename, *host_dirname, *container_basename, *container_dirname; + const char *src, *dest, *host_path, *container_path, *host_basename, *container_basename, *container_dirname; _cleanup_close_pair_ int errno_pipe_fd[2] = { -1, -1 }; CopyFlags copy_flags = COPY_REFLINK|COPY_MERGE; _cleanup_close_ int hostfd = -1; @@ -1119,16 +1119,14 @@ int bus_machine_method_copy(sd_bus_message *message, void *userdata, sd_bus_erro } host_basename = basename(host_path); - t = strdupa(host_path); - host_dirname = dirname(t); container_basename = basename(container_path); t = strdupa(container_path); container_dirname = dirname(t); - hostfd = open(host_dirname, O_CLOEXEC|O_RDONLY|O_NOCTTY|O_DIRECTORY); + hostfd = open_parent(host_path, O_CLOEXEC, 0); if (hostfd < 0) - return sd_bus_error_set_errnof(error, errno, "Failed to open host directory %s: %m", host_dirname); + return sd_bus_error_set_errnof(error, hostfd, "Failed to open host directory %s: %m", host_path); if (pipe2(errno_pipe_fd, O_CLOEXEC|O_NONBLOCK) < 0) return sd_bus_error_set_errnof(error, errno, "Failed to create pipe: %m");