From de9d57cf384b5a6c821d06102c467a23ca8a0691 Mon Sep 17 00:00:00 2001 From: kib Date: Thu, 25 Oct 2018 22:16:34 +0000 Subject: [PATCH] Implement O_BENEATH and AT_BENEATH. Flags prevent open(2) and *at(2) vfs syscalls name lookup from escaping the starting directory. Supposedly the interface is similar to the same proposed Linux flags. Reviewed by: jilles (code, previous version of manpages), 0mp (manpages) Discussed with: allanjude, emaste, jonathan Sponsored by: The FreeBSD Foundation Differential revision: https://reviews.freebsd.org/D17547 --- lib/libc/sys/access.2 | 13 +++- lib/libc/sys/chflags.2 | 14 ++++- lib/libc/sys/chmod.2 | 14 ++++- lib/libc/sys/chown.2 | 14 ++++- lib/libc/sys/link.2 | 15 ++++- lib/libc/sys/open.2 | 28 ++++++++- lib/libc/sys/stat.2 | 18 +++++- lib/libc/sys/unlink.2 | 14 ++++- lib/libc/sys/utimensat.2 | 14 ++++- sys/cddl/compat/opensolaris/sys/vnode.h | 2 +- sys/compat/cloudabi/cloudabi_file.c | 4 +- sys/compat/linux/linux_file.c | 8 +-- sys/kern/vfs_lookup.c | 9 ++- sys/kern/vfs_mountroot.c | 2 +- sys/kern/vfs_syscalls.c | 83 +++++++++++++------------ sys/kern/vfs_vnops.c | 4 ++ sys/sys/fcntl.h | 11 ++-- sys/sys/namei.h | 1 + sys/sys/syscallsubr.h | 4 +- sys/ufs/ffs/ffs_alloc.c | 2 +- 20 files changed, 205 insertions(+), 69 deletions(-) diff --git a/lib/libc/sys/access.2 b/lib/libc/sys/access.2 index f21eb0388419..b1b049925c7d 100644 --- a/lib/libc/sys/access.2 +++ b/lib/libc/sys/access.2 @@ -28,7 +28,7 @@ .\" @(#)access.2 8.2 (Berkeley) 4/1/94 .\" $FreeBSD$ .\" -.Dd September 15, 2014 +.Dd October 20, 2018 .Dt ACCESS 2 .Os .Sh NAME @@ -120,6 +120,8 @@ list, defined in The checks for accessibility are performed using the effective user and group IDs instead of the real user and group ID as required in a call to .Fn access . +.It Dv AT_BENEATH +Only operate on files and directories below the starting directory. .El .Pp Even if a process's real or effective user has appropriate privileges @@ -195,6 +197,15 @@ argument is not an absolute path and is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa path +is not strictly relative to the starting directory. +For example, +.Fa path +is absolute or includes a ".." component that escapes the starting directory. .El .Sh SEE ALSO .Xr chmod 2 , diff --git a/lib/libc/sys/chflags.2 b/lib/libc/sys/chflags.2 index 6fbec4129e53..c9caea862009 100644 --- a/lib/libc/sys/chflags.2 +++ b/lib/libc/sys/chflags.2 @@ -28,7 +28,7 @@ .\" @(#)chflags.2 8.3 (Berkeley) 5/2/95 .\" $FreeBSD$ .\" -.Dd March 22, 2013 +.Dd October 20, 2018 .Dt CHFLAGS 2 .Os .Sh NAME @@ -94,6 +94,9 @@ defined in If .Fa path names a symbolic link, then the flags of the symbolic link are changed. +.It Dv AT_BENEATH +Only allow to change flags for a file which is beneath of +the starting directory. .El .Pp If @@ -302,6 +305,15 @@ error occurred while reading from or writing to the file system. The underlying file system does not support file flags, or does not support all of the flags set in .Fa flags . +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa path +is not strictly relative to the starting directory. +For example, +.Fa path +is absolute or includes a ".." component that escapes the starting directory. .El .Sh SEE ALSO .Xr chflags 1 , diff --git a/lib/libc/sys/chmod.2 b/lib/libc/sys/chmod.2 index 2c9ef9dbcb62..65a704729329 100644 --- a/lib/libc/sys/chmod.2 +++ b/lib/libc/sys/chmod.2 @@ -28,7 +28,7 @@ .\" @(#)chmod.2 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd December 1, 2017 +.Dd October 20, 2018 .Dt CHMOD 2 .Os .Sh NAME @@ -101,6 +101,9 @@ in If .Fa path names a symbolic link, then the mode of the symbolic link is changed. +.It Dv AT_BENEATH +Only allow to change permissions of a file which is beneath of +the starting directory. .El .Pp If @@ -285,6 +288,15 @@ argument is not an absolute path and is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa path +is not strictly relative to the starting directory. +For example, +.Fa path +is absolute or includes a ".." component that escapes the starting directory. .El .Sh SEE ALSO .Xr chmod 1 , diff --git a/lib/libc/sys/chown.2 b/lib/libc/sys/chown.2 index 97cf8a389059..8ba2b9240b3d 100644 --- a/lib/libc/sys/chown.2 +++ b/lib/libc/sys/chown.2 @@ -28,7 +28,7 @@ .\" @(#)chown.2 8.4 (Berkeley) 4/19/94 .\" $FreeBSD$ .\" -.Dd December 1, 2017 +.Dd Octover 20, 2018 .Dt CHOWN 2 .Os .Sh NAME @@ -118,6 +118,9 @@ list, defined in If .Fa path names a symbolic link, ownership of the symbolic link is changed. +.It Dv AT_BENEATH +Only allow to change ownership of a file which is beneath of +the starting directory. .El .Pp If @@ -227,6 +230,15 @@ argument is not an absolute path and is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa path +is not strictly relative to the starting directory. +For example, +.Fa path +is absolute or includes a ".." component that escapes the starting directory. .El .Sh SEE ALSO .Xr chgrp 1 , diff --git a/lib/libc/sys/link.2 b/lib/libc/sys/link.2 index bb238447e5db..057f2aeccd52 100644 --- a/lib/libc/sys/link.2 +++ b/lib/libc/sys/link.2 @@ -28,7 +28,7 @@ .\" @(#)link.2 8.3 (Berkeley) 1/12/94 .\" $FreeBSD$ .\" -.Dd December 1, 2017 +.Dd October 20, 2018 .Dt LINK 2 .Os .Sh NAME @@ -115,6 +115,8 @@ If .Fa name1 names a symbolic link, a new link for the target of the symbolic link is created. +.It Dv AT_BENEATH +Only allow to link to a file which is beneath of the starting directory. .El .Pp If @@ -257,6 +259,17 @@ or respectively, is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa name1 +is not strictly relative to the starting directory. +For example, +.Fa name1 +is absolute or includes a ".." component that escapes the starting directory. +.Dv AT_BENEATH +flag was specified. .El .Sh SEE ALSO .Xr chflags 2 , diff --git a/lib/libc/sys/open.2 b/lib/libc/sys/open.2 index 7e226e444728..d98d5b62a227 100644 --- a/lib/libc/sys/open.2 +++ b/lib/libc/sys/open.2 @@ -28,7 +28,7 @@ .\" @(#)open.2 8.2 (Berkeley) 11/16/93 .\" $FreeBSD$ .\" -.Dd December 1, 2017 +.Dd October 20, 2018 .Dt OPEN 2 .Os .Sh NAME @@ -142,6 +142,7 @@ O_TTY_INIT ignored O_DIRECTORY error if file is not a directory O_CLOEXEC set FD_CLOEXEC upon open O_VERIFY verify the contents of the file +O_BENEATH require path to be strictly relative to starting directory .Ed .Pp Opening a file with @@ -269,6 +270,23 @@ means is implementation specific. The run-time linker (rtld) uses this flag to ensure shared objects have been verified before operating on them. .Pp +.Dv O_BENEATH +returns +.Er ENOTCAPABLE +if the specified path, after resolving all symlinks and ".." references +in it, does not reside in the directory hierarchy of children beneath +the starting directory, or is an absolute path. +Starting directory is the process current directory if relative +.Fa path +is used for +.Fn open , +and the directory referenced by the +.Fa fd +argument when specifying relative +.Fa path +for +.Fn openat . +.Pp If successful, .Fn open returns a non-negative integer, termed a file descriptor. @@ -487,9 +505,13 @@ is specified and the process is in capability mode. was called and the process is in capability mode. .It Bq Er ENOTCAPABLE .Fa path -is an absolute path or contained a ".." component leading to a +is an absolute path, +or contained a ".." component leading to a directory outside of the directory hierarchy specified by -.Fa fd . +.Fa fd , +and the process is in capability mode or the +.Dv O_BENEATH +flag was provided. .El .Sh SEE ALSO .Xr chmod 2 , diff --git a/lib/libc/sys/stat.2 b/lib/libc/sys/stat.2 index 356dcfeafa5b..37b04aedcd80 100644 --- a/lib/libc/sys/stat.2 +++ b/lib/libc/sys/stat.2 @@ -28,7 +28,7 @@ .\" @(#)stat.2 8.4 (Berkeley) 5/1/95 .\" $FreeBSD$ .\" -.Dd December 1, 2017 +.Dd October 20, 2018 .Dt STAT 2 .Os .Sh NAME @@ -100,6 +100,13 @@ defined in If .Fa path names a symbolic link, the status of the symbolic link is returned. +.It Dv AT_BENEATH +Only stat files and directories below the starting directory. +See the description of the +.Dv O_BENEATH +flag in the +.Xr open 2 +manual page. .El .Pp If @@ -397,6 +404,15 @@ argument is not an absolute path and is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa path +is not strictly relative to the starting directory. +For example, +.Fa path +is absolute or includes a ".." component that escapes the starting directory. .El .Sh SEE ALSO .Xr access 2 , diff --git a/lib/libc/sys/unlink.2 b/lib/libc/sys/unlink.2 index 39e3095429a6..2e0ed54b0a29 100644 --- a/lib/libc/sys/unlink.2 +++ b/lib/libc/sys/unlink.2 @@ -28,7 +28,7 @@ .\" @(#)unlink.2 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd December 1, 2017 +.Dd October 20, 2018 .Dt UNLINK 2 .Os .Sh NAME @@ -89,6 +89,9 @@ Remove the directory entry specified by and .Fa path as a directory, not a normal file. +.It Dv AT_BENEATH +Only unlink files and directories which are beneath of the starting +directory. .El .Pp If @@ -200,6 +203,15 @@ argument is not an absolute path and is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa path +is not strictly relative to the starting directory. +For example, +.Fa path +is absolute or includes a ".." component that escapes the starting directory. .El .Sh SEE ALSO .Xr chflags 2 , diff --git a/lib/libc/sys/utimensat.2 b/lib/libc/sys/utimensat.2 index 3f5434230caa..7fcb3c669697 100644 --- a/lib/libc/sys/utimensat.2 +++ b/lib/libc/sys/utimensat.2 @@ -31,7 +31,7 @@ .\" @(#)utimes.2 8.1 (Berkeley) 6/4/93 .\" $FreeBSD$ .\" -.Dd June 7, 2017 +.Dd October 20, 2018 .Dt UTIMENSAT 2 .Os .Sh NAME @@ -146,6 +146,9 @@ names a symbolic link, the symbolic link's times are changed. By default, .Fn utimensat changes the times of the file referenced by the symbolic link. +.It Dv AT_BENEATH +Only allow to change the times of a file which is beneath of +the starting directory. .El .Sh RETURN VALUES .Rv -std @@ -267,6 +270,15 @@ argument is not an absolute path and is neither .Dv AT_FDCWD nor a file descriptor associated with a directory. +.It Bq Er ENOTCAPABLE +The +.Dv AT_BENEATH +flag was specified but +.Fa path +is not strictly relative to the starting directory. +For example, +.Fa path +is absolute or includes a ".." component that escapes the starting directory. .El .Sh SEE ALSO .Xr chflags 2 , diff --git a/sys/cddl/compat/opensolaris/sys/vnode.h b/sys/cddl/compat/opensolaris/sys/vnode.h index d15cd88c193a..54e78f2ffe9f 100644 --- a/sys/cddl/compat/opensolaris/sys/vnode.h +++ b/sys/cddl/compat/opensolaris/sys/vnode.h @@ -278,7 +278,7 @@ vn_remove(char *fnamep, enum uio_seg seg, enum rm dirflag) ASSERT(seg == UIO_SYSSPACE); ASSERT(dirflag == RMFILE); - return (kern_unlinkat(curthread, AT_FDCWD, fnamep, seg, 0)); + return (kern_unlinkat(curthread, AT_FDCWD, fnamep, seg, 0, 0)); } #endif /* _KERNEL */ diff --git a/sys/compat/cloudabi/cloudabi_file.c b/sys/compat/cloudabi/cloudabi_file.c index 19f13570b77d..602ae5042962 100644 --- a/sys/compat/cloudabi/cloudabi_file.c +++ b/sys/compat/cloudabi/cloudabi_file.c @@ -752,9 +752,9 @@ cloudabi_sys_file_unlink(struct thread *td, return (error); if (uap->flags & CLOUDABI_UNLINK_REMOVEDIR) - error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE); + error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE, 0); else - error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0); + error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0, 0); cloudabi_freestr(path); return (error); } diff --git a/sys/compat/linux/linux_file.c b/sys/compat/linux/linux_file.c index 017e947cb23a..ecc95370d4c1 100644 --- a/sys/compat/linux/linux_file.c +++ b/sys/compat/linux/linux_file.c @@ -590,7 +590,7 @@ linux_unlink(struct thread *td, struct linux_unlink_args *args) printf(ARGS(unlink, "%s"), path); #endif - error = kern_unlinkat(td, AT_FDCWD, path, UIO_SYSSPACE, 0); + error = kern_unlinkat(td, AT_FDCWD, path, UIO_SYSSPACE, 0, 0); if (error == EPERM) { /* Introduce POSIX noncompliant behaviour of Linux */ if (kern_statat(td, 0, AT_FDCWD, path, UIO_SYSSPACE, &st, @@ -623,9 +623,9 @@ linux_unlinkat(struct thread *td, struct linux_unlinkat_args *args) #endif if (args->flag & LINUX_AT_REMOVEDIR) - error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE); + error = kern_rmdirat(td, dfd, path, UIO_SYSSPACE, 0); else - error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE, 0); + error = kern_unlinkat(td, dfd, path, UIO_SYSSPACE, 0, 0); if (error == EPERM && !(args->flag & LINUX_AT_REMOVEDIR)) { /* Introduce POSIX noncompliant behaviour of Linux */ if (kern_statat(td, AT_SYMLINK_NOFOLLOW, dfd, path, @@ -741,7 +741,7 @@ linux_rmdir(struct thread *td, struct linux_rmdir_args *args) if (ldebug(rmdir)) printf(ARGS(rmdir, "%s"), path); #endif - error = kern_rmdirat(td, AT_FDCWD, path, UIO_SYSSPACE); + error = kern_rmdirat(td, AT_FDCWD, path, UIO_SYSSPACE, 0); LFREEPATH(path); return (error); } diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 675c0aa02ac3..496a4e6f5a68 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -242,7 +242,8 @@ namei_handle_root(struct nameidata *ndp, struct vnode **dpp) struct componentname *cnp; cnp = &ndp->ni_cnd; - if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0) { + if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0 || + (cnp->cn_flags & BENEATH) != 0) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_CAPFAIL)) ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); @@ -434,8 +435,10 @@ namei(struct nameidata *ndp) vrele(dp); goto out; } - if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0 && - lookup_cap_dotdot != 0) + if (((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0 && + lookup_cap_dotdot != 0) || + ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0 && + (cnp->cn_flags & BENEATH) != 0)) ndp->ni_lcf |= NI_LCF_CAP_DOTDOT; SDT_PROBE3(vfs, namei, lookup, entry, dp, cnp->cn_pnbuf, cnp->cn_flags); diff --git a/sys/kern/vfs_mountroot.c b/sys/kern/vfs_mountroot.c index 0c46d68e6409..babcf5a77732 100644 --- a/sys/kern/vfs_mountroot.c +++ b/sys/kern/vfs_mountroot.c @@ -389,7 +389,7 @@ vfs_mountroot_shuffle(struct thread *td, struct mount *mpdevfs) vfs_unbusy(mpdevfs); /* Unlink the no longer needed /dev/dev -> / symlink */ error = kern_unlinkat(td, AT_FDCWD, "/dev/dev", - UIO_SYSSPACE, 0); + UIO_SYSSPACE, 0, 0); if (error) printf("mountroot: unable to unlink /dev/dev " "(error %d)\n", error); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index a0ddefaa15df..a54e8bc1b2cc 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -1443,11 +1443,12 @@ sys_linkat(struct thread *td, struct linkat_args *uap) int flag; flag = uap->flag; - if (flag & ~AT_SYMLINK_FOLLOW) + if ((flag & ~(AT_SYMLINK_FOLLOW | AT_BENEATH)) != 0) return (EINVAL); return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, - UIO_USERSPACE, (flag & AT_SYMLINK_FOLLOW) ? FOLLOW : NOFOLLOW)); + UIO_USERSPACE, ((flag & AT_SYMLINK_FOLLOW) != 0 ? FOLLOW : + NOFOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0))); } int hardlink_check_uid = 0; @@ -1731,7 +1732,7 @@ int sys_unlink(struct thread *td, struct unlink_args *uap) { - return (kern_unlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0)); + return (kern_unlinkat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0, 0)); } #ifndef _SYS_SYSPROTO_H_ @@ -1744,22 +1745,25 @@ struct unlinkat_args { int sys_unlinkat(struct thread *td, struct unlinkat_args *uap) { - int flag = uap->flag; - int fd = uap->fd; - char *path = uap->path; + int fd, flag; + char *path; - if (flag & ~AT_REMOVEDIR) + flag = uap->flag; + fd = uap->fd; + path = uap->path; + + if ((flag & ~(AT_REMOVEDIR | AT_BENEATH)) != 0) return (EINVAL); - if (flag & AT_REMOVEDIR) - return (kern_rmdirat(td, fd, path, UIO_USERSPACE)); + if ((uap->flag & AT_REMOVEDIR) != 0) + return (kern_rmdirat(td, fd, path, UIO_USERSPACE, flag)); else - return (kern_unlinkat(td, fd, path, UIO_USERSPACE, 0)); + return (kern_unlinkat(td, fd, path, UIO_USERSPACE, flag, 0)); } int kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, - ino_t oldinum) + int flag, ino_t oldinum) { struct mount *mp; struct vnode *vp; @@ -1769,7 +1773,8 @@ kern_unlinkat(struct thread *td, int fd, char *path, enum uio_seg pathseg, restart: bwillwrite(); - NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1, + NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | + ((flag & AT_BENEATH) != 0 ? BENEATH : 0), pathseg, path, fd, &cap_unlinkat_rights, td); if ((error = namei(&nd)) != 0) return (error == EINVAL ? EPERM : error); @@ -1960,7 +1965,7 @@ kern_accessat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct nameidata nd; int error; - if (flag & ~AT_EACCESS) + if ((flag & ~(AT_EACCESS | AT_BENEATH)) != 0) return (EINVAL); if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0) return (EINVAL); @@ -1981,8 +1986,8 @@ kern_accessat(struct thread *td, int fd, char *path, enum uio_seg pathseg, usecred = cred; AUDIT_ARG_VALUE(amode); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | - AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, - td); + AUDITVNODE1 | ((flag & AT_BENEATH) != 0 ? BENEATH : 0), + pathseg, path, fd, &cap_fstat_rights, td); if ((error = namei(&nd)) != 0) goto out; vp = nd.ni_vp; @@ -2273,11 +2278,12 @@ kern_statat(struct thread *td, int flag, int fd, char *path, struct stat sb; int error; - if (flag & ~AT_SYMLINK_NOFOLLOW) + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); - NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : - FOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1, pathseg, path, fd, + 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); if ((error = namei(&nd)) != 0) @@ -2588,15 +2594,12 @@ struct chflagsat_args { int sys_chflagsat(struct thread *td, struct chflagsat_args *uap) { - int fd = uap->fd; - const char *path = uap->path; - u_long flags = uap->flags; - int atflag = uap->atflag; - if (atflag & ~AT_SYMLINK_NOFOLLOW) + if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); - return (kern_chflagsat(td, fd, path, UIO_USERSPACE, flags, atflag)); + return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE, + uap->flags, uap->atflag)); } /* @@ -2625,6 +2628,7 @@ kern_chflagsat(struct thread *td, int fd, const char *path, 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, &cap_fchflags_rights, td); if ((error = namei(&nd)) != 0) @@ -2719,15 +2723,12 @@ struct fchmodat_args { int sys_fchmodat(struct thread *td, struct fchmodat_args *uap) { - int flag = uap->flag; - int fd = uap->fd; - char *path = uap->path; - mode_t mode = uap->mode; - if (flag & ~AT_SYMLINK_NOFOLLOW) + if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); - return (kern_fchmodat(td, fd, path, UIO_USERSPACE, mode, flag)); + return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE, + uap->mode, uap->flag)); } /* @@ -2755,7 +2756,8 @@ kern_fchmodat(struct thread *td, int fd, char *path, enum uio_seg pathseg, int error, follow; AUDIT_ARG_MODE(mode); - follow = (flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : FOLLOW; + follow = (flag & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : FOLLOW; + follow |= (flag & AT_BENEATH) != 0 ? BENEATH : 0; NDINIT_ATRIGHTS(&nd, LOOKUP, follow | AUDITVNODE1, pathseg, path, fd, &cap_fchmod_rights, td); if ((error = namei(&nd)) != 0) @@ -2850,10 +2852,8 @@ struct fchownat_args { int sys_fchownat(struct thread *td, struct fchownat_args *uap) { - int flag; - flag = uap->flag; - if (flag & ~AT_SYMLINK_NOFOLLOW) + if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, @@ -2869,6 +2869,7 @@ kern_fchownat(struct thread *td, int fd, char *path, enum uio_seg pathseg, 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, &cap_fchown_rights, td); @@ -3220,14 +3221,14 @@ kern_utimensat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct timespec ts[2]; int error, flags; - if (flag & ~AT_SYMLINK_NOFOLLOW) + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_BENEATH)) != 0) return (EINVAL); if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) return (error); NDINIT_ATRIGHTS(&nd, LOOKUP, ((flag & AT_SYMLINK_NOFOLLOW) ? NOFOLLOW : - FOLLOW) | AUDITVNODE1, pathseg, path, fd, - &cap_futimes_rights, td); + FOLLOW) | ((flag & AT_BENEATH) != 0 ? BENEATH : 0) | AUDITVNODE1, + pathseg, path, fd, &cap_futimes_rights, td); if ((error = namei(&nd)) != 0) return (error); /* @@ -3671,11 +3672,12 @@ int sys_rmdir(struct thread *td, struct rmdir_args *uap) { - return (kern_rmdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE)); + return (kern_rmdirat(td, AT_FDCWD, uap->path, UIO_USERSPACE, 0)); } int -kern_rmdirat(struct thread *td, int fd, char *path, enum uio_seg pathseg) +kern_rmdirat(struct thread *td, int fd, char *path, enum uio_seg pathseg, + int flag) { struct mount *mp; struct vnode *vp; @@ -3684,7 +3686,8 @@ kern_rmdirat(struct thread *td, int fd, char *path, enum uio_seg pathseg) restart: bwillwrite(); - NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1, + NDINIT_ATRIGHTS(&nd, DELETE, LOCKPARENT | LOCKLEAF | AUDITVNODE1 | + ((flag & AT_BENEATH) != 0 ? BENEATH : 0), pathseg, path, fd, &cap_unlinkat_rights, td); if ((error = namei(&nd)) != 0) return (error); diff --git a/sys/kern/vfs_vnops.c b/sys/kern/vfs_vnops.c index 00392ee3a072..9c7280125deb 100644 --- a/sys/kern/vfs_vnops.c +++ b/sys/kern/vfs_vnops.c @@ -212,6 +212,8 @@ restart: ndp->ni_cnd.cn_flags = ISOPEN | LOCKPARENT | LOCKLEAF | NOCACHE; if ((fmode & O_EXCL) == 0 && (fmode & O_NOFOLLOW) == 0) ndp->ni_cnd.cn_flags |= FOLLOW; + if ((fmode & O_BENEATH) != 0) + ndp->ni_cnd.cn_flags |= BENEATH; if (!(vn_open_flags & VN_OPEN_NOAUDIT)) ndp->ni_cnd.cn_flags |= AUDITVNODE1; if (vn_open_flags & VN_OPEN_NOCAPCHECK) @@ -269,6 +271,8 @@ restart: ((fmode & O_NOFOLLOW) ? NOFOLLOW : FOLLOW) | LOCKLEAF; if (!(fmode & FWRITE)) ndp->ni_cnd.cn_flags |= LOCKSHARED; + if ((fmode & O_BENEATH) != 0) + ndp->ni_cnd.cn_flags |= BENEATH; if (!(vn_open_flags & VN_OPEN_NOAUDIT)) ndp->ni_cnd.cn_flags |= AUDITVNODE1; if (vn_open_flags & VN_OPEN_NOCAPCHECK) diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h index cce169b4e4a1..04eab5e27ed1 100644 --- a/sys/sys/fcntl.h +++ b/sys/sys/fcntl.h @@ -133,6 +133,7 @@ typedef __pid_t pid_t; #if __BSD_VISIBLE #define O_VERIFY 0x00200000 /* open only after verification */ +#define O_BENEATH 0x00400000 /* Fail if not under cwd */ #endif /* @@ -206,10 +207,12 @@ typedef __pid_t pid_t; /* * Miscellaneous flags for the *at() syscalls. */ -#define AT_EACCESS 0x100 /* Check access using effective user and group ID */ -#define AT_SYMLINK_NOFOLLOW 0x200 /* Do not follow symbolic links */ -#define AT_SYMLINK_FOLLOW 0x400 /* Follow symbolic link */ -#define AT_REMOVEDIR 0x800 /* Remove directory instead of file */ +#define AT_EACCESS 0x0100 /* Check access using effective user + and group ID */ +#define AT_SYMLINK_NOFOLLOW 0x0200 /* Do not follow symbolic links */ +#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 */ #endif /* diff --git a/sys/sys/namei.h b/sys/sys/namei.h index f2c2f7543741..3d2a0bc2c002 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -119,6 +119,7 @@ struct nameidata { #define WANTPARENT 0x0010 /* want parent vnode returned unlocked */ #define NOCACHE 0x0020 /* name must not be left in cache */ #define FOLLOW 0x0040 /* follow symbolic links */ +#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 */ diff --git a/sys/sys/syscallsubr.h b/sys/sys/syscallsubr.h index 5ec4fa33f534..20e4cb39770e 100644 --- a/sys/sys/syscallsubr.h +++ b/sys/sys/syscallsubr.h @@ -219,7 +219,7 @@ int kern_recvit(struct thread *td, int s, struct msghdr *mp, int kern_renameat(struct thread *td, int oldfd, char *old, int newfd, char *new, enum uio_seg pathseg); int kern_rmdirat(struct thread *td, int fd, char *path, - enum uio_seg pathseg); + enum uio_seg pathseg, int flag); int kern_sched_getparam(struct thread *td, struct thread *targettd, struct sched_param *param); int kern_sched_getscheduler(struct thread *td, struct thread *targettd, @@ -286,7 +286,7 @@ int kern_thr_suspend(struct thread *td, struct timespec *tsp); int kern_truncate(struct thread *td, char *path, enum uio_seg pathseg, off_t length); int kern_unlinkat(struct thread *td, int fd, char *path, - enum uio_seg pathseg, ino_t oldinum); + enum uio_seg pathseg, int flag, ino_t oldinum); int kern_utimesat(struct thread *td, int fd, char *path, enum uio_seg pathseg, struct timeval *tptr, enum uio_seg tptrseg); int kern_utimensat(struct thread *td, int fd, char *path, diff --git a/sys/ufs/ffs/ffs_alloc.c b/sys/ufs/ffs/ffs_alloc.c index 636e8e67257e..e82605af09e4 100644 --- a/sys/ufs/ffs/ffs_alloc.c +++ b/sys/ufs/ffs/ffs_alloc.c @@ -3399,7 +3399,7 @@ sysctl_ffs_fsck(SYSCTL_HANDLER_ARGS) vn_finished_write(mp); mp = NULL; error = kern_unlinkat(td, AT_FDCWD, (char *)(intptr_t)cmd.value, - UIO_USERSPACE, (ino_t)cmd.size); + UIO_USERSPACE, 0, (ino_t)cmd.size); break; case FFS_SET_INODE: