Skip to content

Commit 2781214

Browse files
cypharAl Viro
authored andcommitted
namei: LOOKUP_NO_SYMLINKS: block symlink resolution
/* Background. */ Userspace cannot easily resolve a path without resolving symlinks, and would have to manually resolve each path component with O_PATH and O_NOFOLLOW. This is clearly inefficient, and can be fairly easy to screw up (resulting in possible security bugs). Linus has mentioned that Git has a particular need for this kind of flag[1]. It also resolves a fairly long-standing perceived deficiency in O_NOFOLLOw -- that it only blocks the opening of trailing symlinks. This is part of a refresh of Al's AT_NO_JUMPS patchset[2] (which was a variation on David Drysdale's O_BENEATH patchset[3], which in turn was based on the Capsicum project[4]). /* Userspace API. */ LOOKUP_NO_SYMLINKS will be exposed to userspace through openat2(2). /* Semantics. */ Unlike most other LOOKUP flags (most notably LOOKUP_FOLLOW), LOOKUP_NO_SYMLINKS applies to all components of the path. With LOOKUP_NO_SYMLINKS, any symlink path component encountered during path resolution will yield -ELOOP. If the trailing component is a symlink (and no other components were symlinks), then O_PATH|O_NOFOLLOW will not error out and will instead provide a handle to the trailing symlink -- without resolving it. /* Testing. */ LOOKUP_NO_SYMLINKS is tested as part of the openat2(2) selftests. [1]: https://lore.kernel.org/lkml/CA+55aFyOKM7DW7+0sdDFKdZFXgptb5r1id9=Wvhd8AgSP7qjwQ@mail.gmail.com/ [2]: https://lore.kernel.org/lkml/[email protected]/ [3]: https://lore.kernel.org/lkml/[email protected]/ [4]: https://lore.kernel.org/lkml/[email protected]/ Cc: Christian Brauner <[email protected]> Suggested-by: Al Viro <[email protected]> Suggested-by: Linus Torvalds <[email protected]> Signed-off-by: Aleksa Sarai <[email protected]> Signed-off-by: Al Viro <[email protected]>
1 parent 740a167 commit 2781214

File tree

2 files changed

+6
-0
lines changed

2 files changed

+6
-0
lines changed

fs/namei.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1056,6 +1056,9 @@ const char *get_link(struct nameidata *nd)
10561056
int error;
10571057
const char *res;
10581058

1059+
if (unlikely(nd->flags & LOOKUP_NO_SYMLINKS))
1060+
return ERR_PTR(-ELOOP);
1061+
10591062
if (!(nd->flags & LOOKUP_RCU)) {
10601063
touch_atime(&last->link);
10611064
cond_resched();

include/linux/namei.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,9 @@ enum {LAST_NORM, LAST_ROOT, LAST_DOT, LAST_DOTDOT, LAST_BIND};
3939
#define LOOKUP_ROOT 0x2000
4040
#define LOOKUP_ROOT_GRABBED 0x0008
4141

42+
/* Scoping flags for lookup. */
43+
#define LOOKUP_NO_SYMLINKS 0x010000 /* No symlink crossing. */
44+
4245
extern int path_pts(struct path *path);
4346

4447
extern int user_path_at_empty(int, const char __user *, unsigned, struct path *, int *empty);

0 commit comments

Comments
 (0)