Blame SOURCES/0724-fs-util-add-new-chase_symlinks-flag-CHASE_OPEN.patch

17b0f1
From c211b650ee5cb9934067dbba40718a4a33063e06 Mon Sep 17 00:00:00 2001
17b0f1
From: David Tardon <dtardon@redhat.com>
17b0f1
Date: Thu, 3 Jan 2019 14:44:36 +0100
17b0f1
Subject: [PATCH] fs-util: add new chase_symlinks() flag CHASE_OPEN
17b0f1
17b0f1
The new flag returns the O_PATH fd of the final component, which may be
17b0f1
converted into a proper fd by open()ing it again through the
17b0f1
/proc/self/fd/xyz path.
17b0f1
17b0f1
Together with O_SAFE this provides us with a somewhat safe way to open()
17b0f1
files in directories potentially owned by unprivileged code, where we
17b0f1
want to refuse operation if any symlink tricks are played pointing to
17b0f1
privileged files.
17b0f1
17b0f1
(cherry picked from commit 1ed34d75d4f21d2335c5625261954c848d176ae6)
17b0f1
17b0f1
Related: #1663143
17b0f1
---
17b0f1
 Makefile.am          |  1 +
17b0f1
 src/shared/util.c    | 17 +++++++++++++
17b0f1
 src/shared/util.h    |  1 +
17b0f1
 src/test/test-util.c | 59 +++++++++++++++++++++++++++++++++++++++++++-
17b0f1
 4 files changed, 77 insertions(+), 1 deletion(-)
17b0f1
17b0f1
diff --git a/Makefile.am b/Makefile.am
17b0f1
index 995c421b8b..648f54b957 100644
17b0f1
--- a/Makefile.am
17b0f1
+++ b/Makefile.am
17b0f1
@@ -1675,6 +1675,7 @@ test_util_SOURCES = \
17b0f1
 	src/test/test-util.c
17b0f1
 
17b0f1
 test_util_LDADD = \
17b0f1
+	libsystemd-internal.la \
17b0f1
 	libsystemd-shared.la
17b0f1
 
17b0f1
 test_path_lookup_SOURCES = \
17b0f1
diff --git a/src/shared/util.c b/src/shared/util.c
17b0f1
index fc4887920f..354d15ff18 100644
17b0f1
--- a/src/shared/util.c
17b0f1
+++ b/src/shared/util.c
17b0f1
@@ -9225,6 +9225,10 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
17b0f1
 
17b0f1
         assert(path);
17b0f1
 
17b0f1
+        /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
17b0f1
+        if ((flags & (CHASE_NONEXISTENT|CHASE_OPEN)) == (CHASE_NONEXISTENT|CHASE_OPEN))
17b0f1
+                return -EINVAL;
17b0f1
+
17b0f1
         /* This is a lot like canonicalize_file_name(), but takes an additional "root" parameter, that allows following
17b0f1
          * symlinks relative to a root directory, instead of the root of the host.
17b0f1
          *
17b0f1
@@ -9476,5 +9480,18 @@ int chase_symlinks(const char *path, const char *original_root, unsigned flags,
17b0f1
                 done = NULL;
17b0f1
         }
17b0f1
 
17b0f1
+        if (flags & CHASE_OPEN) {
17b0f1
+                int q;
17b0f1
+
17b0f1
+                /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a proper fd by
17b0f1
+                 * opening /proc/self/fd/xyz. */
17b0f1
+
17b0f1
+                assert(fd >= 0);
17b0f1
+                q = fd;
17b0f1
+                fd = -1;
17b0f1
+
17b0f1
+                return q;
17b0f1
+        }
17b0f1
+
17b0f1
         return exists;
17b0f1
 }
17b0f1
diff --git a/src/shared/util.h b/src/shared/util.h
17b0f1
index fa3e2e3009..d89f0d34a1 100644
17b0f1
--- a/src/shared/util.h
17b0f1
+++ b/src/shared/util.h
17b0f1
@@ -1161,6 +1161,7 @@ enum {
17b0f1
         CHASE_NONEXISTENT = 1U << 1,   /* If set, it's OK if the path doesn't actually exist. */
17b0f1
         CHASE_NO_AUTOFS   = 1U << 2,   /* If set, return -EREMOTE if autofs mount point found */
17b0f1
         CHASE_SAFE        = 1U << 3,   /* If set, return EPERM if we ever traverse from unprivileged to privileged files or directories */
17b0f1
+        CHASE_OPEN        = 1U << 4,   /* If set, return an O_PATH object to the final component */
17b0f1
 };
17b0f1
 
17b0f1
 int chase_symlinks(const char *path_with_prefix, const char *root, unsigned flags, char **ret);
17b0f1
diff --git a/src/test/test-util.c b/src/test/test-util.c
17b0f1
index e5a646ec20..8ef3850e10 100644
17b0f1
--- a/src/test/test-util.c
17b0f1
+++ b/src/test/test-util.c
17b0f1
@@ -1910,11 +1910,45 @@ static void test_acquire_data_fd(void) {
17b0f1
         test_acquire_data_fd_one(ACQUIRE_NO_DEV_NULL|ACQUIRE_NO_MEMFD|ACQUIRE_NO_PIPE|ACQUIRE_NO_TMPFILE);
17b0f1
 }
17b0f1
 
17b0f1
+static int id128_read_fd(int fd, sd_id128_t *ret) {
17b0f1
+        char buf[33];
17b0f1
+        ssize_t k;
17b0f1
+        unsigned j;
17b0f1
+        sd_id128_t t;
17b0f1
+
17b0f1
+        assert_return(fd >= 0, -EINVAL);
17b0f1
+
17b0f1
+        k = loop_read(fd, buf, 33, false);
17b0f1
+        if (k < 0)
17b0f1
+                return (int) k;
17b0f1
+
17b0f1
+        if (k != 33)
17b0f1
+                return -EIO;
17b0f1
+
17b0f1
+        if (buf[32] !='\n')
17b0f1
+                return -EIO;
17b0f1
+
17b0f1
+        for (j = 0; j < 16; j++) {
17b0f1
+                int a, b;
17b0f1
+
17b0f1
+                a = unhexchar(buf[j*2]);
17b0f1
+                b = unhexchar(buf[j*2+1]);
17b0f1
+
17b0f1
+                if (a < 0 || b < 0)
17b0f1
+                        return -EIO;
17b0f1
+
17b0f1
+                t.bytes[j] = a << 4 | b;
17b0f1
+        }
17b0f1
+
17b0f1
+        *ret = t;
17b0f1
+        return 0;
17b0f1
+}
17b0f1
+
17b0f1
 static void test_chase_symlinks(void) {
17b0f1
         _cleanup_free_ char *result = NULL;
17b0f1
         char temp[] = "/tmp/test-chase.XXXXXX";
17b0f1
         const char *top, *p, *pslash, *q, *qslash;
17b0f1
-        int r;
17b0f1
+        int r, pfd;
17b0f1
 
17b0f1
         assert_se(mkdtemp(temp));
17b0f1
 
17b0f1
@@ -2139,6 +2173,29 @@ static void test_chase_symlinks(void) {
17b0f1
                 assert_se(chase_symlinks(q, NULL, CHASE_SAFE, NULL) >= 0);
17b0f1
         }
17b0f1
 
17b0f1
+        p = strjoina(temp, "/machine-id-test");
17b0f1
+        assert_se(symlink("/usr/../etc/./machine-id", p) >= 0);
17b0f1
+
17b0f1
+        pfd = chase_symlinks(p, NULL, CHASE_OPEN, NULL);
17b0f1
+        if (pfd != -ENOENT) {
17b0f1
+                char procfs[sizeof("/proc/self/fd/") - 1 + DECIMAL_STR_MAX(pfd) + 1];
17b0f1
+                _cleanup_close_ int fd = -1;
17b0f1
+                sd_id128_t a, b;
17b0f1
+
17b0f1
+                assert_se(pfd >= 0);
17b0f1
+
17b0f1
+                xsprintf(procfs, "/proc/self/fd/%i", pfd);
17b0f1
+
17b0f1
+                fd = open(procfs, O_RDONLY|O_CLOEXEC);
17b0f1
+                assert_se(fd >= 0);
17b0f1
+
17b0f1
+                safe_close(pfd);
17b0f1
+
17b0f1
+                assert_se(id128_read_fd(fd, &a) >= 0);
17b0f1
+                assert_se(sd_id128_get_machine(&b) >= 0);
17b0f1
+                assert_se(sd_id128_equal(a, b));
17b0f1
+        }
17b0f1
+
17b0f1
         assert_se(rm_rf_dangerous(temp, false, true, false) >= 0);
17b0f1
 }
17b0f1