From: Lennart Poettering Date: Fri, 11 Nov 2022 16:31:34 +0000 (+0100) Subject: chase-symlinks: add new flag for prohibiting any following of symlinks X-Git-Tag: v253-rc1~540^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=d43e78b643535da398345d5ae680a96d7b65940e;p=thirdparty%2Fsystemd.git chase-symlinks: add new flag for prohibiting any following of symlinks 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. --- diff --git a/src/basic/chase-symlinks.c b/src/basic/chase-symlinks.c index a0d29427d33..35fa97773b7 100644 --- a/src/basic/chase-symlinks.c +++ b/src/basic/chase-symlinks.c @@ -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) diff --git a/src/basic/chase-symlinks.h b/src/basic/chase-symlinks.h index 6ea0b178759..af0fcf155a2 100644 --- a/src/basic/chase-symlinks.h +++ b/src/basic/chase-symlinks.h @@ -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); diff --git a/src/test/test-fs-util.c b/src/test/test-fs-util.c index d0259843b67..4bf0a5daf87 100644 --- a/src/test/test-fs-util.c +++ b/src/test/test-fs-util.c @@ -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); }