]> git.ipfire.org Git - thirdparty/systemd.git/blobdiff - src/basic/fs-util.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / basic / fs-util.c
index e45ad06491aaf375890f41ddc1c9a2bd9f39cf9a..f25bf2cbbcb6c2400f061ae79d0d6bd45d5ef958 100644 (file)
@@ -13,7 +13,6 @@
 #include "alloc-util.h"
 #include "dirent-util.h"
 #include "fd-util.h"
-#include "fileio.h"
 #include "fs-util.h"
 #include "locale-util.h"
 #include "log.h"
@@ -28,6 +27,7 @@
 #include "string-util.h"
 #include "strv.h"
 #include "time-util.h"
+#include "tmpfile-util.h"
 #include "user-util.h"
 #include "util.h"
 
@@ -212,31 +212,62 @@ int readlink_and_make_absolute(const char *p, char **r) {
 }
 
 int chmod_and_chown(const char *path, mode_t mode, uid_t uid, gid_t gid) {
+        char fd_path[STRLEN("/proc/self/fd/") + DECIMAL_STR_MAX(int) + 1];
+        _cleanup_close_ int fd = -1;
         assert(path);
 
-        /* Under the assumption that we are running privileged we
-         * first change the access mode and only then hand out
+        /* Under the assumption that we are running privileged we first change the access mode and only then hand out
          * ownership to avoid a window where access is too open. */
 
-        if (mode != MODE_INVALID)
-                if (chmod(path, mode) < 0)
+        fd = open(path, O_PATH|O_CLOEXEC|O_NOFOLLOW); /* Let's acquire an O_PATH fd, as precaution to change mode/owner
+                                                       * on the same file */
+        if (fd < 0)
+                return -errno;
+
+        xsprintf(fd_path, "/proc/self/fd/%i", fd);
+
+        if (mode != MODE_INVALID) {
+
+                if ((mode & S_IFMT) != 0) {
+                        struct stat st;
+
+                        if (stat(fd_path, &st) < 0)
+                                return -errno;
+
+                        if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
+                                return -EINVAL;
+                }
+
+                if (chmod(fd_path, mode & 07777) < 0)
                         return -errno;
+        }
 
         if (uid != UID_INVALID || gid != GID_INVALID)
-                if (chown(path, uid, gid) < 0)
+                if (chown(fd_path, uid, gid) < 0)
                         return -errno;
 
         return 0;
 }
 
 int fchmod_and_chown(int fd, mode_t mode, uid_t uid, gid_t gid) {
-        /* Under the assumption that we are running privileged we
-         * first change the access mode and only then hand out
+        /* Under the assumption that we are running privileged we first change the access mode and only then hand out
          * ownership to avoid a window where access is too open. */
 
-        if (mode != MODE_INVALID)
-                if (fchmod(fd, mode) < 0)
+        if (mode != MODE_INVALID) {
+
+                if ((mode & S_IFMT) != 0) {
+                        struct stat st;
+
+                        if (fstat(fd, &st) < 0)
+                                return -errno;
+
+                        if ((mode & S_IFMT) != (st.st_mode & S_IFMT))
+                                return -EINVAL;
+                }
+
+                if (fchmod(fd, mode & 0777) < 0)
                         return -errno;
+        }
 
         if (uid != UID_INVALID || gid != GID_INVALID)
                 if (fchown(fd, uid, gid) < 0)
@@ -264,7 +295,6 @@ int fchmod_opath(int fd, mode_t m) {
          * fchownat() does. */
 
         xsprintf(procfs_path, "/proc/self/fd/%i", fd);
-
         if (chmod(procfs_path, m) < 0)
                 return -errno;
 
@@ -634,29 +664,42 @@ int inotify_add_watch_fd(int fd, int what, uint32_t mask) {
         return r;
 }
 
-static bool safe_transition(const struct stat *a, const struct stat *b) {
+static bool unsafe_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
          * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
 
         if (a->st_uid == 0) /* Transitioning from privileged to unprivileged is always fine */
-                return true;
+                return false;
 
-        return a->st_uid == b->st_uid; /* Otherwise we need to stay within the same UID */
+        return a->st_uid != b->st_uid; /* Otherwise we need to stay within the same UID */
 }
 
 static int log_unsafe_transition(int a, int b, const char *path, unsigned flags) {
         _cleanup_free_ char *n1 = NULL, *n2 = NULL;
 
         if (!FLAGS_SET(flags, CHASE_WARN))
-                return -EPERM;
+                return -ENOLINK;
 
         (void) fd_get_path(a, &n1);
         (void) fd_get_path(b, &n2);
 
-        return log_warning_errno(SYNTHETIC_ERRNO(EPERM),
+        return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK),
                                  "Detected unsafe path transition %s %s %s during canonicalization of %s.",
-                                 n1, special_glyph(ARROW), n2, path);
+                                 n1, special_glyph(SPECIAL_GLYPH_ARROW), n2, path);
+}
+
+static int log_autofs_mount_point(int fd, const char *path, unsigned flags) {
+        _cleanup_free_ char *n1 = NULL;
+
+        if (!FLAGS_SET(flags, CHASE_WARN))
+                return -EREMOTE;
+
+        (void) fd_get_path(fd, &n1);
+
+        return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE),
+                                 "Detected autofs mount point %s during canonicalization of %s.",
+                                 n1, path);
 }
 
 int chase_symlinks(const char *path, const char *original_root, unsigned flags, char **ret) {
@@ -719,6 +762,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
          *    path is fully normalized, and == 0 for each normalization step. This may be combined with
          *    CHASE_NONEXISTENT, in which case 1 is returned when a component is not found.
          *
+         * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions from
+         *    unprivileged to privileged files or directories. In such cases the return value is -ENOLINK. If
+         *    CHASE_WARN is also set a warning describing the unsafe transition is emitted.
+         *
+         * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, the path normalization is
+         *    aborted and -EREMOTE is returned. If CHASE_WARN is also set a warning showing the path of the mount point
+         *    is emitted.
+         *
          * */
 
         /* A root directory of "/" or "" is identical to none */
@@ -833,7 +884,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                 if (fstat(fd_parent, &st) < 0)
                                         return -errno;
 
-                                if (!safe_transition(&previous_stat, &st))
+                                if (unsafe_transition(&previous_stat, &st))
                                         return log_unsafe_transition(fd, fd_parent, path, flags);
 
                                 previous_stat = st;
@@ -874,14 +925,14 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                 if (fstat(child, &st) < 0)
                         return -errno;
                 if ((flags & CHASE_SAFE) &&
-                    !safe_transition(&previous_stat, &st))
+                    unsafe_transition(&previous_stat, &st))
                         return log_unsafe_transition(fd, child, path, flags);
 
                 previous_stat = st;
 
                 if ((flags & CHASE_NO_AUTOFS) &&
                     fd_is_fs_type(child, AUTOFS_SUPER_MAGIC) > 0)
-                        return -EREMOTE;
+                        return log_autofs_mount_point(child, path, flags);
 
                 if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
                         char *joined;
@@ -913,7 +964,7 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                                         if (fstat(fd, &st) < 0)
                                                 return -errno;
 
-                                        if (!safe_transition(&previous_stat, &st))
+                                        if (unsafe_transition(&previous_stat, &st))
                                                 return log_unsafe_transition(child, fd, path, flags);
 
                                         previous_stat = st;