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,
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)
* 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);
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);
}