1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include <linux/magic.h>
5 #include "alloc-util.h"
6 #include "chase-symlinks.h"
10 #include "glyph-util.h"
12 #include "path-util.h"
13 #include "string-util.h"
14 #include "user-util.h"
16 bool unsafe_transition(const struct stat
*a
, const struct stat
*b
) {
17 /* Returns true if the transition from a to b is safe, i.e. that we never transition from unprivileged to
18 * privileged files or directories. Why bother? So that unprivileged code can't symlink to privileged files
19 * making us believe we read something safe even though it isn't safe in the specific context we open it in. */
21 if (a
->st_uid
== 0) /* Transitioning from privileged to unprivileged is always fine */
24 return a
->st_uid
!= b
->st_uid
; /* Otherwise we need to stay within the same UID */
27 static int log_unsafe_transition(int a
, int b
, const char *path
, ChaseSymlinksFlags flags
) {
28 _cleanup_free_
char *n1
= NULL
, *n2
= NULL
, *user_a
= NULL
, *user_b
= NULL
;
31 if (!FLAGS_SET(flags
, CHASE_WARN
))
34 (void) fd_get_path(a
, &n1
);
35 (void) fd_get_path(b
, &n2
);
37 if (fstat(a
, &st
) == 0)
38 user_a
= uid_to_name(st
.st_uid
);
39 if (fstat(b
, &st
) == 0)
40 user_b
= uid_to_name(st
.st_uid
);
42 return log_warning_errno(SYNTHETIC_ERRNO(ENOLINK
),
43 "Detected unsafe path transition %s (owned by %s) %s %s (owned by %s) during canonicalization of %s.",
44 strna(n1
), strna(user_a
), special_glyph(SPECIAL_GLYPH_ARROW_RIGHT
), strna(n2
), strna(user_b
), path
);
47 static int log_autofs_mount_point(int fd
, const char *path
, ChaseSymlinksFlags flags
) {
48 _cleanup_free_
char *n1
= NULL
;
50 if (!FLAGS_SET(flags
, CHASE_WARN
))
53 (void) fd_get_path(fd
, &n1
);
55 return log_warning_errno(SYNTHETIC_ERRNO(EREMOTE
),
56 "Detected autofs mount point %s during canonicalization of %s.",
60 static int log_prohibited_symlink(int fd
, ChaseSymlinksFlags flags
) {
61 _cleanup_free_
char *n1
= NULL
;
65 if (!FLAGS_SET(flags
, CHASE_WARN
))
68 (void) fd_get_path(fd
, &n1
);
70 return log_warning_errno(SYNTHETIC_ERRNO(EREMCHG
),
71 "Detected symlink where not symlink is allowed at %s, refusing.",
75 int chase_symlinks_at(
78 ChaseSymlinksFlags flags
,
82 _cleanup_free_
char *buffer
= NULL
, *done
= NULL
;
83 _cleanup_close_
int fd
= -EBADF
, root_fd
= -EBADF
;
84 unsigned max_follow
= CHASE_SYMLINKS_MAX
; /* how many symlinks to follow before giving up and returning ELOOP */
85 bool exists
= true, append_trail_slash
= false;
86 struct stat previous_stat
;
91 assert(!FLAGS_SET(flags
, CHASE_PREFIX_ROOT
));
92 assert(dir_fd
>= 0 || dir_fd
== AT_FDCWD
);
94 /* Either the file may be missing, or we return an fd to the final object, but both make no sense */
95 if ((flags
& CHASE_NONEXISTENT
) && ret_fd
)
98 if ((flags
& CHASE_STEP
) && ret_fd
)
104 /* This function resolves symlinks of the path relative to the given directory file descriptor. If
105 * CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified, symlinks are resolved relative to the given directory
106 * file descriptor. Otherwise, they are resolved relative to the root directory of the host.
108 * Note that when CHASE_SYMLINKS_RESOLVE_IN_ROOT is specified and we find an absolute symlink, it is
109 * resolved relative to given directory file descriptor and not the root of the host. Also, when
110 * following relative symlinks, this functions ensure they cannot be used to "escape" the given
111 * directory file descriptor. The "path" parameter is always interpreted relative to the given
112 * directory file descriptor. If the given directory file descriptor is AT_FDCWD and "path" is
113 * absolute, it is interpreted relative to the root directory of the host.
115 * If "dir_fd" is a valid directory fd, "path" is an absolute path and "ret_path" is not NULL, this
116 * functions returns a relative path in "ret_path" because openat() like functions generally ignore
117 * the directory fd if they are provided with an absolute path. On the other hand, if "dir_fd" is
118 * AT_FDCWD and "path" is an absolute path, we need to return an absolute path in "ret_path" because
119 * otherwise, if the caller passes the returned relative path to another openat() like function, it
120 * would be resolved relative to the current working directory instead of to "/".
122 * Algorithmically this operates on two path buffers: "done" are the components of the path we
123 * already processed and resolved symlinks, "." and ".." of. "todo" are the components of the path we
124 * still need to process. On each iteration, we move one component from "todo" to "done", processing
125 * it's special meaning each time. We always keep an O_PATH fd to the component we are currently
126 * processing, thus keeping lookup races to a minimum.
128 * Suggested usage: whenever you want to canonicalize a path, use this function. Pass the absolute
129 * path you got as-is: fully qualified and relative to your host's root. Optionally, specify the
130 * "dir_fd" parameter to tell this function what to do when encountering a symlink with an absolute
131 * path as directory: resolve it relative to the given directory file descriptor.
133 * There are five ways to invoke this function:
135 * 1. Without CHASE_STEP or ret_fd: in this case the path is resolved and the normalized path is
136 * returned in `ret_path`. The return value is < 0 on error. If CHASE_NONEXISTENT is also set, 0
137 * is returned if the file doesn't exist, > 0 otherwise. If CHASE_NONEXISTENT is not set, >= 0 is
138 * returned if the destination was found, -ENOENT if it wasn't.
140 * 2. With ret_fd: in this case the destination is opened after chasing it as O_PATH and this file
141 * descriptor is returned as return value. This is useful to open files relative to some root
142 * directory. Note that the returned O_PATH file descriptors must be converted into a regular one
143 * (using fd_reopen() or such) before it can be used for reading/writing. ret_fd may not be
144 * combined with CHASE_NONEXISTENT.
146 * 3. With CHASE_STEP: in this case only a single step of the normalization is executed, i.e. only
147 * the first symlink or ".." component of the path is resolved, and the resulting path is
148 * returned. This is useful if a caller wants to trace the path through the file system verbosely.
149 * Returns < 0 on error, > 0 if the path is fully normalized, and == 0 for each normalization
150 * step. This may be combined with CHASE_NONEXISTENT, in which case 1 is returned when a component
153 * 4. With CHASE_SAFE: in this case the path must not contain unsafe transitions, i.e. transitions
154 * from unprivileged to privileged files or directories. In such cases the return value is
155 * -ENOLINK. If CHASE_WARN is also set, a warning describing the unsafe transition is emitted.
156 * CHASE_WARN cannot be used in PID 1.
158 * 5. With CHASE_NO_AUTOFS: in this case if an autofs mount point is encountered, path normalization
159 * is aborted and -EREMOTE is returned. If CHASE_WARN is also set, a warning showing the path of
160 * the mount point is emitted. CHASE_WARN cannot be used in PID 1.
163 if (!(flags
& (CHASE_AT_RESOLVE_IN_ROOT
|CHASE_NONEXISTENT
|CHASE_NO_AUTOFS
|CHASE_SAFE
|CHASE_STEP
)) &&
164 !ret_path
&& ret_fd
) {
166 /* Shortcut the ret_fd case if the caller isn't interested in the actual path and has no root
167 * set and doesn't care about any of the other special features we provide either. */
168 r
= openat(dir_fd
, path
, O_PATH
|O_CLOEXEC
|((flags
& CHASE_NOFOLLOW
) ? O_NOFOLLOW
: 0));
176 buffer
= strdup(path
);
180 bool need_absolute
= !FLAGS_SET(flags
, CHASE_AT_RESOLVE_IN_ROOT
) && path_is_absolute(path
);
187 if (FLAGS_SET(flags
, CHASE_AT_RESOLVE_IN_ROOT
))
188 root_fd
= openat(dir_fd
, ".", O_CLOEXEC
|O_DIRECTORY
|O_PATH
);
190 root_fd
= open("/", O_CLOEXEC
|O_DIRECTORY
|O_PATH
);
194 if (FLAGS_SET(flags
, CHASE_AT_RESOLVE_IN_ROOT
) || !path_is_absolute(path
))
195 fd
= openat(dir_fd
, ".", O_CLOEXEC
|O_DIRECTORY
|O_PATH
);
197 fd
= open("/", O_CLOEXEC
|O_DIRECTORY
|O_PATH
);
201 if (fstat(fd
, &previous_stat
) < 0)
204 if (flags
& CHASE_TRAIL_SLASH
)
205 append_trail_slash
= endswith(buffer
, "/") || endswith(buffer
, "/.");
207 for (todo
= buffer
;;) {
208 _cleanup_free_
char *first
= NULL
;
209 _cleanup_close_
int child
= -1;
213 r
= path_find_first_component(&todo
, /* accept_dot_dot= */ true, &e
);
216 if (r
== 0) { /* We reached the end. */
217 if (append_trail_slash
)
218 if (!strextend(&done
, "/"))
223 first
= strndup(e
, r
);
227 /* Two dots? Then chop off the last bit of what we already found out. */
228 if (path_equal(first
, "..")) {
229 _cleanup_free_
char *parent
= NULL
;
230 _cleanup_close_
int fd_parent
= -EBADF
;
232 /* If we already are at the top, then going up will not change anything. This is
233 * in-line with how the kernel handles this. */
234 if (empty_or_root(done
) && FLAGS_SET(flags
, CHASE_AT_RESOLVE_IN_ROOT
))
237 fd_parent
= openat(fd
, "..", O_CLOEXEC
|O_NOFOLLOW
|O_PATH
|O_DIRECTORY
);
241 if (fstat(fd_parent
, &st
) < 0)
244 /* If we opened the same directory, that means we're at the host root directory, so
245 * going up won't change anything. */
246 if (st
.st_dev
== previous_stat
.st_dev
&& st
.st_ino
== previous_stat
.st_ino
)
249 r
= path_extract_directory(done
, &parent
);
250 if (r
>= 0 || r
== -EDESTADDRREQ
)
251 free_and_replace(done
, parent
);
252 else if (IN_SET(r
, -EINVAL
, -EADDRNOTAVAIL
)) {
253 /* If we're at the top of "dir_fd", start appending ".." to "done". */
254 if (!path_extend(&done
, ".."))
259 if (flags
& CHASE_STEP
)
262 if (flags
& CHASE_SAFE
) {
263 if (unsafe_transition(&previous_stat
, &st
))
264 return log_unsafe_transition(fd
, fd_parent
, path
, flags
);
269 close_and_replace(fd
, fd_parent
);
274 /* Otherwise let's see what this is. */
275 child
= openat(fd
, first
, O_CLOEXEC
|O_NOFOLLOW
|O_PATH
);
277 if (errno
== ENOENT
&&
278 (flags
& CHASE_NONEXISTENT
) &&
279 (isempty(todo
) || path_is_safe(todo
))) {
280 /* If CHASE_NONEXISTENT is set, and the path does not exist, then
281 * that's OK, return what we got so far. But don't allow this if the
282 * remaining path contains "../" or something else weird. */
284 if (!path_extend(&done
, first
, todo
))
294 if (fstat(child
, &st
) < 0)
296 if ((flags
& CHASE_SAFE
) &&
297 unsafe_transition(&previous_stat
, &st
))
298 return log_unsafe_transition(fd
, child
, path
, flags
);
302 if ((flags
& CHASE_NO_AUTOFS
) &&
303 fd_is_fs_type(child
, AUTOFS_SUPER_MAGIC
) > 0)
304 return log_autofs_mount_point(child
, path
, flags
);
306 if (S_ISLNK(st
.st_mode
) && !((flags
& CHASE_NOFOLLOW
) && isempty(todo
))) {
307 _cleanup_free_
char *destination
= NULL
;
309 if (flags
& CHASE_PROHIBIT_SYMLINKS
)
310 return log_prohibited_symlink(child
, flags
);
312 /* This is a symlink, in this case read the destination. But let's make sure we
313 * don't follow symlinks without bounds. */
314 if (--max_follow
<= 0)
317 r
= readlinkat_malloc(fd
, first
, &destination
);
320 if (isempty(destination
))
323 if (path_is_absolute(destination
)) {
325 /* An absolute destination. Start the loop from the beginning, but use the
326 * root file descriptor as base. */
329 fd
= fd_reopen(root_fd
, O_CLOEXEC
|O_PATH
|O_DIRECTORY
);
333 if (flags
& CHASE_SAFE
) {
334 if (fstat(fd
, &st
) < 0)
337 if (unsafe_transition(&previous_stat
, &st
))
338 return log_unsafe_transition(child
, fd
, path
, flags
);
343 r
= free_and_strdup(&done
, need_absolute
? "/" : NULL
);
348 /* Prefix what's left to do with what we just read, and start the loop again, but
349 * remain in the current directory. */
350 if (!path_extend(&destination
, todo
))
353 free_and_replace(buffer
, destination
);
356 if (flags
& CHASE_STEP
)
362 /* If this is not a symlink, then let's just add the name we read to what we already verified. */
363 if (!path_extend(&done
, first
))
366 /* And iterate again, but go one directory further down. */
367 close_and_replace(fd
, child
);
371 *ret_path
= TAKE_PTR(done
);
374 /* Return the O_PATH fd we currently are looking to the caller. It can translate it to a
375 * proper fd by opening /proc/self/fd/xyz. */
378 *ret_fd
= TAKE_FD(fd
);
381 if (flags
& CHASE_STEP
)
390 /* todo may contain slashes at the beginning. */
391 r
= path_find_first_component(&todo
, /* accept_dot_dot= */ true, &e
);
395 *ret_path
= TAKE_PTR(done
);
399 c
= path_join(done
, e
);
412 const char *original_root
,
413 ChaseSymlinksFlags flags
,
417 _cleanup_free_
char *root
= NULL
, *absolute
= NULL
, *p
= NULL
;
418 _cleanup_close_
int fd
= -EBADF
, pfd
= -EBADF
;
426 /* A root directory of "/" or "" is identical to none */
427 if (empty_or_root(original_root
))
428 original_root
= NULL
;
431 r
= path_make_absolute_cwd(original_root
, &root
);
435 /* Simplify the root directory, so that it has no duplicate slashes and nothing at the
436 * end. While we won't resolve the root path we still simplify it. Note that dropping the
437 * trailing slash should not change behaviour, since when opening it we specify O_DIRECTORY
438 * anyway. Moreover at the end of this function after processing everything we'll always turn
439 * the empty string back to "/". */
440 delete_trailing_chars(root
, "/");
443 if (flags
& CHASE_PREFIX_ROOT
) {
444 absolute
= path_join(root
, path
);
451 r
= path_make_absolute_cwd(path
, &absolute
);
457 path
= path_startswith(absolute
, root
);
459 return log_full_errno(flags
& CHASE_WARN
? LOG_WARNING
: LOG_DEBUG
,
460 SYNTHETIC_ERRNO(ECHRNG
),
461 "Specified path '%s' is outside of specified root directory '%s', refusing to resolve.",
464 fd
= open(root
, O_CLOEXEC
|O_DIRECTORY
|O_PATH
);
468 flags
|= CHASE_AT_RESOLVE_IN_ROOT
;
474 r
= chase_symlinks_at(fd
, path
, flags
& ~CHASE_PREFIX_ROOT
, ret_path
? &p
: NULL
, ret_fd
? &pfd
: NULL
);
479 char *q
= path_join(root
, p
);
483 *ret_path
= TAKE_PTR(q
);
487 *ret_fd
= TAKE_FD(pfd
);
492 int chase_symlinks_and_open(
495 ChaseSymlinksFlags chase_flags
,
499 _cleanup_close_
int path_fd
= -EBADF
;
500 _cleanup_free_
char *p
= NULL
;
503 if (chase_flags
& (CHASE_NONEXISTENT
|CHASE_STEP
))
506 if (empty_or_root(root
) && !ret_path
&& (chase_flags
& (CHASE_NO_AUTOFS
|CHASE_SAFE
)) == 0) {
507 /* Shortcut this call if none of the special features of this call are requested */
508 r
= open(path
, open_flags
| (FLAGS_SET(chase_flags
, CHASE_NOFOLLOW
) ? O_NOFOLLOW
: 0));
515 r
= chase_symlinks(path
, root
, chase_flags
, ret_path
? &p
: NULL
, &path_fd
);
518 assert(path_fd
>= 0);
520 r
= fd_reopen(path_fd
, open_flags
);
525 *ret_path
= TAKE_PTR(p
);
530 int chase_symlinks_and_opendir(
533 ChaseSymlinksFlags chase_flags
,
537 _cleanup_close_
int path_fd
= -EBADF
;
538 _cleanup_free_
char *p
= NULL
;
544 if (chase_flags
& (CHASE_NONEXISTENT
|CHASE_STEP
))
547 if (empty_or_root(root
) && !ret_path
&& (chase_flags
& (CHASE_NO_AUTOFS
|CHASE_SAFE
)) == 0) {
548 /* Shortcut this call if none of the special features of this call are requested */
557 r
= chase_symlinks(path
, root
, chase_flags
, ret_path
? &p
: NULL
, &path_fd
);
560 assert(path_fd
>= 0);
562 d
= xopendirat(path_fd
, ".", O_NOFOLLOW
);
567 *ret_path
= TAKE_PTR(p
);
573 int chase_symlinks_and_stat(
576 ChaseSymlinksFlags chase_flags
,
578 struct stat
*ret_stat
,
581 _cleanup_close_
int path_fd
= -EBADF
;
582 _cleanup_free_
char *p
= NULL
;
588 if (chase_flags
& (CHASE_NONEXISTENT
|CHASE_STEP
))
591 if (empty_or_root(root
) && !ret_path
&& (chase_flags
& (CHASE_NO_AUTOFS
|CHASE_SAFE
)) == 0 && !ret_fd
) {
592 /* Shortcut this call if none of the special features of this call are requested */
594 if (fstatat(AT_FDCWD
, path
, ret_stat
, FLAGS_SET(chase_flags
, CHASE_NOFOLLOW
) ? AT_SYMLINK_NOFOLLOW
: 0) < 0)
600 r
= chase_symlinks(path
, root
, chase_flags
, ret_path
? &p
: NULL
, &path_fd
);
603 assert(path_fd
>= 0);
605 if (fstat(path_fd
, ret_stat
) < 0)
609 *ret_path
= TAKE_PTR(p
);
611 *ret_fd
= TAKE_FD(path_fd
);
616 int chase_symlinks_and_access(
619 ChaseSymlinksFlags chase_flags
,
624 _cleanup_close_
int path_fd
= -EBADF
;
625 _cleanup_free_
char *p
= NULL
;
630 if (chase_flags
& (CHASE_NONEXISTENT
|CHASE_STEP
))
633 if (empty_or_root(root
) && !ret_path
&& (chase_flags
& (CHASE_NO_AUTOFS
|CHASE_SAFE
)) == 0 && !ret_fd
) {
634 /* Shortcut this call if none of the special features of this call are requested */
636 if (faccessat(AT_FDCWD
, path
, access_mode
, FLAGS_SET(chase_flags
, CHASE_NOFOLLOW
) ? AT_SYMLINK_NOFOLLOW
: 0) < 0)
642 r
= chase_symlinks(path
, root
, chase_flags
, ret_path
? &p
: NULL
, &path_fd
);
645 assert(path_fd
>= 0);
647 r
= access_fd(path_fd
, access_mode
);
652 *ret_path
= TAKE_PTR(p
);
654 *ret_fd
= TAKE_FD(path_fd
);
659 int chase_symlinks_and_fopen_unlocked(
662 ChaseSymlinksFlags chase_flags
,
663 const char *open_flags
,
667 _cleanup_free_
char *final_path
= NULL
;
668 _cleanup_close_
int fd
= -EBADF
;
675 mode_flags
= fopen_mode_to_flags(open_flags
);
679 fd
= chase_symlinks_and_open(path
, root
, chase_flags
, mode_flags
, ret_path
? &final_path
: NULL
);
683 r
= take_fdopen_unlocked(&fd
, open_flags
, ret_file
);
688 *ret_path
= TAKE_PTR(final_path
);