]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
fs-util: add new chase_symlinks() flag CHASE_OPEN
authorLennart Poettering <lennart@poettering.net>
Thu, 4 Jan 2018 19:00:28 +0000 (20:00 +0100)
committerLennart Poettering <lennart@poettering.net>
Thu, 11 Jan 2018 14:12:16 +0000 (15:12 +0100)
The new flag returns the O_PATH fd of the final component, which may be
converted into a proper fd by open()ing it again through the
/proc/self/fd/xyz path.

Together with O_SAFE this provides us with a somewhat safe way to open()
files in directories potentially owned by unprivileged code, where we
want to refuse operation if any symlink tricks are played pointing to
privileged files.

src/basic/fs-util.c
src/basic/fs-util.h
src/test/test-fs-util.c

index a7ee785202c99b7514af7f608383dc14c6874a58..7b76f33683cdf00984941054f8ac8a543a1ea109 100644 (file)
@@ -632,6 +632,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
 
         assert(path);
 
+        /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
+        if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
+                return -EINVAL;
+
         /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
          * symlinks relative to a root directory, instead of the root of the host.
          *
@@ -881,6 +885,19 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
                 done = NULL;
         }
 
+        if (flags & CHASE_OPEN) {
+                int q;
+
+                /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
+                 * opening /proc/self/fd/xyz. */
+
+                assert(fd >= 0);
+                q = fd;
+                fd = -1;
+
+                return q;
+        }
+
         return exists;
 }
 
index 182dc63547949866b53e922a3b6f9420098e2306..4dba1ea56adb511731a7d603a6339cfceeb86cca 100644 (file)
@@ -85,6 +85,7 @@ enum {
         CHASE_NONEXISTENT = 1U << 1,   /* If set, it's OK if the path doesn't actually exist. */
         CHASE_NO_AUTOFS   = 1U << 2,   /* If set, return -EREMOTE if autofs mount point found */
         CHASE_SAFE        = 1U << 3,   /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
+        CHASE_OPEN        = 1U << 4,   /* If set, return an O_PATH object to the final component */
 };
 
 int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
index b5ec086d374d128ebc8d88baf0205c18e35dde31..cda6d9cc72e45ba00af4c9c7cc773242740657fa 100644 (file)
 
 #include "alloc-util.h"
 #include "fd-util.h"
+#include "fd-util.h"
 #include "fileio.h"
 #include "fs-util.h"
+#include "id128-util.h"
 #include "macro.h"
 #include "mkdir.h"
 #include "path-util.h"
 #include "rm-rf.h"
+#include "stdio-util.h"
 #include "string-util.h"
 #include "strv.h"
 #include "user-util.h"
@@ -37,7 +40,7 @@ static void test_chase_symlinks(void) {
         _cleanup_free_ char *result = NULL;
         char temp[] = "/tmp/test-chase.XXXXXX";
         const char *top, *p, *pslash, *q, *qslash;
-        int r;
+        int r, pfd;
 
         assert_se(mkdtemp(temp));
 
@@ -262,6 +265,29 @@ static void test_chase_symlinks(void) {
                 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
         }
 
+        p = strjoina(temp, "/machine-id-test");
+        assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
+
+        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);
+                assert_se(fd >= 0);
+
+                safe_close(pfd);
+
+                assert_se(id128_read_fd(fd, ID128_PLAIN, &a) >= 0);
+                assert_se(sd_id128_get_machine(&b) >= 0);
+                assert_se(sd_id128_equal(a, b));
+        }
+
         assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
 }