Index: sys/kern/vfs_lookup.c =================================================================== --- sys/kern/vfs_lookup.c +++ sys/kern/vfs_lookup.c @@ -413,6 +413,15 @@ if (error == 0) ndp->ni_lcf |= NI_LCF_LATCH; } + if ((cnp->cn_flags & RBENEATH) != 0) { + if ((ndp->ni_lcf & NI_LCF_BENEATH_ABS) != 0) { + error = EINVAL; + } else if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0) { + ndp->ni_lcf |= NI_LCF_STRICTRELATIVE | + NI_LCF_CAP_DOTDOT; + } + } + /* * If we are auditing the kernel pathname, save the user pathname. */ @@ -1364,9 +1373,9 @@ } void -NDINIT_ALL(struct nameidata *ndp, u_long op, u_long flags, enum uio_seg segflg, - const char *namep, int dirfd, struct vnode *startdir, cap_rights_t *rightsp, - struct thread *td) +NDINIT_ALL(struct nameidata *ndp, u_long op, u_int64_t flags, + enum uio_seg segflg, const char *namep, int dirfd, struct vnode *startdir, + cap_rights_t *rightsp, struct thread *td) { ndp->ni_cnd.cn_nameiop = op; Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -114,6 +114,26 @@ static int kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path, enum uio_seg segflag); +static u_int64_t +at2cnpf(u_int at_flags, u_int mask) +{ + u_int64_t res; + + res = 0; + at_flags &= mask; + if ((at_flags & AT_BENEATH) != 0) + res |= BENEATH; + if ((at_flags & AT_RBENEATH) != 0) + res |= RBENEATH; + /* FOLLOW is pseudo flag */ + if ((at_flags & AT_SYMLINK_NOFOLLOW) != 0) + res |= NOFOLLOW; + if ((mask & AT_SYMLINK_FOLLOW) != 0 && + (at_flags & AT_SYMLINK_FOLLOW) == 0) + res |= NOFOLLOW; + return (res); +} + int kern_sync(struct thread *td) { @@ -1486,12 +1506,12 @@ int flag; flag = uap->flag; - if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH)) != 0) + if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH | AT_RBENEATH)) != 0) return (EINVAL); return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, - UIO_USERSPACE, ((flag & AT_SYMLINK_FOLLOW) != 0 ? FOLLOW : - NOFOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0))); + UIO_USERSPACE, at2cnpf(flag, AT_SYMLINK_FOLLOW | AT_BENEATH | + AT_RBENEATH))); } int hardlink_check_uid = 0; @@ -1856,7 +1876,7 @@ restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | - ((flag & AT_BENEATH) != 0 ? BENEATH : 0), + at2cnpf(flag, AT_BENEATH | AT_RBENEATH), pathseg, path, dfd, &cap_unlinkat_rights, td); if ((error = namei(&nd)) != 0) { if (error == EINVAL) @@ -2059,7 +2079,7 @@ struct nameidata nd; int error; - if ((flag & ~(AT_EACCESS | AT_BENEATH)) != 0) + if ((flag & ~(AT_EACCESS | AT_BENEATH | AT_RBENEATH)) != 0) return (EINVAL); if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0) return (EINVAL); @@ -2080,7 +2100,7 @@ usecred = cred; AUDIT_ARG_VALUE(amode); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | - AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), + AUDITVNODE1 | at2cnpf(flag, AT_BENEATH | AT_RBENEATH), pathseg, path, fd, &cap_fstat_rights, td); if ((error = namei(&nd)) != 0) goto out; @@ -2371,13 +2391,12 @@ struct nameidata nd; int error; - if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH | AT_RBENEATH)) != 0) return (EINVAL); - NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) != 0 ? - NOFOLLOW : FOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0) | - LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, - &cap_fstat_rights, td); + NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpf(flag, AT_BENEATH | AT_RBENEATH | + AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1, + pathseg, path, fd, &cap_fstat_rights, td); if ((error = namei(&nd)) != 0) return (error); @@ -2698,7 +2717,8 @@ sys_chflagsat(struct thread *td, struct chflagsat_args *uap) { - if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) + if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH | + AT_RBENEATH)) != 0) return (EINVAL); return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE, @@ -2727,12 +2747,11 @@ enum uio_seg pathseg, u_long flags, int atflag) { struct nameidata nd; - int error, follow; + int error; AUDIT_ARG_FFLAGS(flags); - follow = (atflag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; - follow |= (atflag & AT_BENEATH) != 0 ? BENEATH : 0; - NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, + NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpf(atflag, AT_SYMLINK_NOFOLLOW | + AT_BENEATH | AT_RBENEATH) | AUDITVNODE1, pathseg, path, fd, &cap_fchflags_rights, td); if ((error = namei(&nd)) != 0) return (error); @@ -2827,7 +2846,8 @@ sys_fchmodat(struct thread *td, struct fchmodat_args *uap) { - if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) + if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH | + AT_RBENEATH)) != 0) return (EINVAL); return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE, @@ -2856,12 +2876,11 @@ enum uio_seg pathseg, mode_t mode, int flag) { struct nameidata nd; - int error, follow; + int error; AUDIT_ARG_MODE(mode); - follow = (flag & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : FOLLOW; - follow |= (flag & AT_BENEATH) != 0 ? BENEATH : 0; - NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, + NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpf(flag, AT_SYMLINK_NOFOLLOW | + AT_BENEATH | AT_RBENEATH) | AUDITVNODE1, pathseg, path, fd, &cap_fchmod_rights, td); if ((error = namei(&nd)) != 0) return (error); @@ -2956,7 +2975,8 @@ sys_fchownat(struct thread *td, struct fchownat_args *uap) { - if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) + if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH | + AT_RBENEATH)) != 0) return (EINVAL); return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, @@ -2968,12 +2988,11 @@ enum uio_seg pathseg, int uid, int gid, int flag) { struct nameidata nd; - int error, follow; + int error; AUDIT_ARG_OWNER(uid, gid); - follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; - follow |= (flag & AT_BENEATH) != 0 ? BENEATH : 0; - NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, + NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpf(flag, AT_SYMLINK_NOFOLLOW | + AT_BENEATH | AT_RBENEATH) | AUDITVNODE1, pathseg, path, fd, &cap_fchown_rights, td); if ((error = namei(&nd)) != 0) @@ -3325,13 +3344,13 @@ struct timespec ts[2]; int error, flags; - if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH | AT_RBENEATH)) != 0) return (EINVAL); if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) return (error); - NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : - FOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0) | AUDITVNODE1, + NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpf(flag, AT_SYMLINK_NOFOLLOW | + AT_BENEATH | AT_RBENEATH) | AUDITVNODE1, pathseg, path, fd, &cap_futimes_rights, td); if ((error = namei(&nd)) != 0) return (error); @@ -3803,7 +3822,7 @@ restart: bwillwrite(); NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | - ((flag & AT_BENEATH) != 0 ? BENEATH : 0), + at2cnpf(flag, AT_BENEATH | AT_RBENEATH), pathseg, path, dfd, &cap_unlinkat_rights, td); if ((error = namei(&nd)) != 0) goto fdout; @@ -4289,7 +4308,8 @@ sys_getfhat(struct thread *td, struct getfhat_args *uap) { - if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) + if ((uap->flags & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH | + AT_RBENEATH)) != 0) return (EINVAL); return (kern_getfhat(td, uap->flags, uap->fd, uap->path, UIO_USERSPACE, uap->fhp)); @@ -4307,9 +4327,9 @@ error = priv_check(td, PRIV_VFS_GETFH); if (error != 0) return (error); - NDINIT_AT(&nd, LOOKUP, ((flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : - FOLLOW) | ((flags & AT_BENEATH) != 0 ? BENEATH : 0) | LOCKLEAF | - AUDITVNODE1, pathseg, path, fd, td); + NDINIT_AT(&nd, LOOKUP, at2cnpf(flags, AT_SYMLINK_NOFOLLOW | + AT_BENEATH | AT_RBENEATH) | LOCKLEAF | AUDITVNODE1, + pathseg, path, fd, td); error = namei(&nd); if (error != 0) return (error); Index: sys/kern/vfs_vnops.c =================================================================== --- sys/kern/vfs_vnops.c +++ sys/kern/vfs_vnops.c @@ -223,6 +223,8 @@ ndp->ni_cnd.cn_flags |= FOLLOW; if ((fmode & O_BENEATH) != 0) ndp->ni_cnd.cn_flags |= BENEATH; + if ((fmode & O_RBENEATH) != 0) + ndp->ni_cnd.cn_flags |= RBENEATH; if (!(vn_open_flags & VN_OPEN_NOAUDIT)) ndp->ni_cnd.cn_flags |= AUDITVNODE1; if (vn_open_flags & VN_OPEN_NOCAPCHECK) @@ -287,6 +289,8 @@ ndp->ni_cnd.cn_flags |= LOCKSHARED; if ((fmode & O_BENEATH) != 0) ndp->ni_cnd.cn_flags |= BENEATH; + if ((fmode & O_RBENEATH) != 0) + ndp->ni_cnd.cn_flags |= RBENEATH; if (!(vn_open_flags & VN_OPEN_NOAUDIT)) ndp->ni_cnd.cn_flags |= AUDITVNODE1; if (vn_open_flags & VN_OPEN_NOCAPCHECK) Index: sys/sys/fcntl.h =================================================================== --- sys/sys/fcntl.h +++ sys/sys/fcntl.h @@ -136,6 +136,7 @@ #if __BSD_VISIBLE #define O_VERIFY 0x00200000 /* open only after verification */ #define O_BENEATH 0x00400000 /* Fail if not under cwd */ +#define O_RBENEATH 0x00800000 /* XXX */ #endif /* @@ -215,6 +216,7 @@ #define AT_SYMLINK_FOLLOW 0x0400 /* Follow symbolic link */ #define AT_REMOVEDIR 0x0800 /* Remove directory instead of file */ #define AT_BENEATH 0x1000 /* Fail if not under dirfd */ +#define AT_RBENEATH 0x1000 /* XXX */ #endif /* Index: sys/sys/namei.h =================================================================== --- sys/sys/namei.h +++ sys/sys/namei.h @@ -133,7 +133,8 @@ #define BENEATH 0x0080 /* No escape from the start dir */ #define LOCKSHARED 0x0100 /* Shared lock leaf */ #define NOFOLLOW 0x0000 /* do not follow symbolic links (pseudo) */ -#define MODMASK 0x01fc /* mask of operational modifiers */ +#define RBENEATH 0x100000000ULL /* XXX */ +#define MODMASK 0xf000001fcULL /* mask of operational modifiers */ /* * Namei parameter descriptors. * @@ -196,7 +197,7 @@ #define NDINIT_ATVP(ndp, op, flags, segflg, namep, vp, td) \ NDINIT_ALL(ndp, op, flags, segflg, namep, AT_FDCWD, vp, 0, td) -void NDINIT_ALL(struct nameidata *ndp, u_long op, u_long flags, +void NDINIT_ALL(struct nameidata *ndp, u_long op, u_int64_t flags, enum uio_seg segflg, const char *namep, int dirfd, struct vnode *startdir, cap_rights_t *rightsp, struct thread *td);