]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
chase-symlinks: add new flag for prohibiting any following of symlinks
authorLennart Poettering <lennart@poettering.net>
Fri, 11 Nov 2022 16:31:34 +0000 (17:31 +0100)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Sun, 13 Nov 2022 08:46:30 +0000 (17:46 +0900)
This is useful when operating in the ESP, which is untrusted territory,
and where under no circumstances we should be tricked by symlinks into
doing anything we don't want to.

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

index a0d29427d33a8333c450664cb683c3a008793bad..35fa97773b75f45e4d8b9006e9c2a1880775bbae 100644 (file)
@@ -57,6 +57,21 @@ static int log_autofs_mount_point(int fd, const char *path, ChaseSymlinksFlags f
                                  strna(n1), path);
 }
 
+static int log_prohibited_symlink(int fd, ChaseSymlinksFlags flags) {
+        _cleanup_free_ char *n1 = NULL;
+
+        assert(fd >= 0);
+
+        if (!FLAGS_SET(flags, CHASE_WARN))
+                return -EREMCHG;
+
+        (void) fd_get_path(fd, &n1);
+
+        return log_warning_errno(SYNTHETIC_ERRNO(EREMCHG),
+                                 "Detected symlink where not symlink is allowed at %s, refusing.",
+                                 strna(n1));
+}
+
 int chase_symlinks_at(
                 int dir_fd,
                 const char *path,
@@ -291,6 +306,9 @@ int chase_symlinks_at(
                 if (S_ISLNK(st.st_mode) && !((flags & CHASE_NOFOLLOW) && isempty(todo))) {
                         _cleanup_free_ char *destination = NULL;
 
+                        if (flags & CHASE_PROHIBIT_SYMLINKS)
+                                return log_prohibited_symlink(child, flags);
+
                         /* This is a symlink, in this case read the destination. But let's make sure we
                          * don't follow symlinks without bounds. */
                         if (--max_follow <= 0)
index 6ea0b178759f30b0a436fe1669f3463b75ee434a..af0fcf155a26973c3adedc59ce4faee975cb1f83 100644 (file)
@@ -19,6 +19,7 @@ typedef enum ChaseSymlinksFlags {
                                             * Note: this may do an NSS lookup, hence this flag cannot be used in PID 1. */
         CHASE_AT_RESOLVE_IN_ROOT = 1 << 8, /* Same as openat2()'s RESOLVE_IN_ROOT flag, symlinks are resolved
                                             * relative to the given directory fd instead of root. */
+        CHASE_PROHIBIT_SYMLINKS  = 1 << 9, /* Refuse all symlinks */
 } ChaseSymlinksFlags;
 
 bool unsafe_transition(const struct stat *a, const struct stat *b);
index d0259843b6723a43d57e4380be5299855b83c028..4bf0a5daf8765c85d4fcf8b9389addf166cf8a8a 100644 (file)
@@ -386,6 +386,15 @@ TEST(chase_symlinks) {
         assert_se(path_equal(path_startswith(result, p), "usr"));
         result = mfree(result);
 
+        /* Test CHASE_PROHIBIT_SYMLINKS */
+
+        assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dotdot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS, NULL, NULL) == -EREMCHG);
+        assert_se(chase_symlinks("top/dot/dot", temp, CHASE_PREFIX_ROOT|CHASE_PROHIBIT_SYMLINKS|CHASE_WARN, NULL, NULL) == -EREMCHG);
+
  cleanup:
         assert_se(rm_rf(temp, REMOVE_ROOT|REMOVE_PHYSICAL) >= 0);
 }