diff options
author | Konstantin Belousov <kib@FreeBSD.org> | 2021-03-07 14:29:09 +0000 |
---|---|---|
committer | Konstantin Belousov <kib@FreeBSD.org> | 2021-04-15 09:48:11 +0000 |
commit | 509124b62616f73dcdc42263ee109392dafafd99 (patch) | |
tree | 955762ad6dab0613574d56c408e93eb63de36ac3 /sys/kern/vfs_syscalls.c | |
parent | d51b4b0aac43d9d25f7eb3f17b2d3034a5c851d8 (diff) | |
download | src-509124b62616f73dcdc42263ee109392dafafd99.tar.gz src-509124b62616f73dcdc42263ee109392dafafd99.zip |
Add AT_EMPTY_PATH for several *at(2) syscalls
It is currently allowed to fchownat(2), fchmodat(2), fchflagsat(2),
utimensat(2), fstatat(2), and linkat(2).
For linkat(2), PRIV_VFS_FHOPEN privilege is required to exercise the flag.
It allows to link any open file.
Requested by: trasz
Tested by: pho, trasz
Reviewed by: markj
Sponsored by: The FreeBSD Foundation
MFC after: 2 weeks
Differential revision: https://reviews.freebsd.org/D29111
Diffstat (limited to 'sys/kern/vfs_syscalls.c')
-rw-r--r-- | sys/kern/vfs_syscalls.c | 63 |
1 files changed, 44 insertions, 19 deletions
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index 48df8a3e9051..45f155ebff3d 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -129,6 +129,8 @@ at2cnpflags(u_int at_flags, u_int mask) res |= (at_flags & AT_SYMLINK_NOFOLLOW) != 0 ? NOFOLLOW : FOLLOW; } + if ((mask & AT_EMPTY_PATH) != 0 && (at_flags & AT_EMPTY_PATH) != 0) + res |= EMPTYPATH; return (res); } @@ -1496,12 +1498,13 @@ sys_linkat(struct thread *td, struct linkat_args *uap) int flag; flag = uap->flag; - if ((flag & ~(AT_SYMLINK_FOLLOW | AT_RESOLVE_BENEATH)) != 0) + if ((flag & ~(AT_SYMLINK_FOLLOW | AT_RESOLVE_BENEATH | + AT_EMPTY_PATH)) != 0) return (EINVAL); return (kern_linkat(td, uap->fd1, uap->fd2, uap->path1, uap->path2, UIO_USERSPACE, at2cnpflags(flag, AT_SYMLINK_FOLLOW | - AT_RESOLVE_BENEATH))); + AT_RESOLVE_BENEATH | AT_EMPTY_PATH))); } int hardlink_check_uid = 0; @@ -1578,6 +1581,23 @@ kern_linkat_vp(struct thread *td, struct vnode *vp, int fd, const char *path, LOCKPARENT | SAVENAME | AUDITVNODE2 | NOCACHE, segflag, path, fd, &cap_linkat_target_rights, td); if ((error = namei(&nd)) == 0) { + if ((nd.ni_resflags & NIRES_EMPTYPATH) != 0) { + error = priv_check(td, PRIV_VFS_FHOPEN); + if (error != 0) { + NDFREE(&nd, NDF_ONLY_PNBUF); + if (nd.ni_vp != NULL) { + if (nd.ni_dvp == nd.ni_vp) + vrele(nd.ni_dvp); + else + vput(nd.ni_dvp); + vrele(nd.ni_vp); + } else { + vput(nd.ni_dvp); + } + vrele(vp); + return (error); + } + } if (nd.ni_vp != NULL) { NDFREE(&nd, NDF_ONLY_PNBUF); if (nd.ni_dvp == nd.ni_vp) @@ -2075,7 +2095,7 @@ kern_accessat(struct thread *td, int fd, const char *path, struct nameidata nd; int error; - if ((flag & ~(AT_EACCESS | AT_RESOLVE_BENEATH)) != 0) + if ((flag & ~(AT_EACCESS | AT_RESOLVE_BENEATH | AT_EMPTY_PATH)) != 0) return (EINVAL); if (amode != F_OK && (amode & ~(R_OK | W_OK | X_OK)) != 0) return (EINVAL); @@ -2096,8 +2116,8 @@ kern_accessat(struct thread *td, int fd, const char *path, usecred = cred; AUDIT_ARG_VALUE(amode); NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | LOCKSHARED | LOCKLEAF | - AUDITVNODE1 | at2cnpflags(flag, AT_RESOLVE_BENEATH), - pathseg, path, fd, &cap_fstat_rights, td); + AUDITVNODE1 | at2cnpflags(flag, AT_RESOLVE_BENEATH | + AT_EMPTY_PATH), pathseg, path, fd, &cap_fstat_rights, td); if ((error = namei(&nd)) != 0) goto out; vp = nd.ni_vp; @@ -2387,12 +2407,13 @@ kern_statat(struct thread *td, int flag, int fd, const char *path, struct nameidata nd; int error; - if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | + AT_EMPTY_PATH)) != 0) return (EINVAL); NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_RESOLVE_BENEATH | - AT_SYMLINK_NOFOLLOW) | LOCKSHARED | LOCKLEAF | AUDITVNODE1, - pathseg, path, fd, &cap_fstat_rights, td); + AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH) | LOCKSHARED | LOCKLEAF | + AUDITVNODE1, pathseg, path, fd, &cap_fstat_rights, td); if ((error = namei(&nd)) != 0) return (error); @@ -2710,7 +2731,8 @@ int sys_chflagsat(struct thread *td, struct chflagsat_args *uap) { - if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) + if ((uap->atflag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | + AT_EMPTY_PATH)) != 0) return (EINVAL); return (kern_chflagsat(td, uap->fd, uap->path, UIO_USERSPACE, @@ -2743,8 +2765,8 @@ kern_chflagsat(struct thread *td, int fd, const char *path, AUDIT_ARG_FFLAGS(flags); NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(atflag, AT_SYMLINK_NOFOLLOW | - AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd, - &cap_fchflags_rights, td); + AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path, + fd, &cap_fchflags_rights, td); if ((error = namei(&nd)) != 0) return (error); NDFREE_NOTHING(&nd); @@ -2838,7 +2860,8 @@ int sys_fchmodat(struct thread *td, struct fchmodat_args *uap) { - if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) + if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | + AT_EMPTY_PATH)) != 0) return (EINVAL); return (kern_fchmodat(td, uap->fd, uap->path, UIO_USERSPACE, @@ -2871,8 +2894,8 @@ kern_fchmodat(struct thread *td, int fd, const char *path, AUDIT_ARG_MODE(mode); NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | - AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd, - &cap_fchmod_rights, td); + AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path, + fd, &cap_fchmod_rights, td); if ((error = namei(&nd)) != 0) return (error); NDFREE_NOTHING(&nd); @@ -2966,7 +2989,8 @@ int sys_fchownat(struct thread *td, struct fchownat_args *uap) { - if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) + if ((uap->flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | + AT_EMPTY_PATH)) != 0) return (EINVAL); return (kern_fchownat(td, uap->fd, uap->path, UIO_USERSPACE, uap->uid, @@ -2982,8 +3006,8 @@ kern_fchownat(struct thread *td, int fd, const char *path, AUDIT_ARG_OWNER(uid, gid); NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | - AT_RESOLVE_BENEATH) | AUDITVNODE1, pathseg, path, fd, - &cap_fchown_rights, td); + AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path, + fd, &cap_fchown_rights, td); if ((error = namei(&nd)) != 0) return (error); @@ -3334,13 +3358,14 @@ kern_utimensat(struct thread *td, int fd, const char *path, struct timespec ts[2]; int error, flags; - if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH)) != 0) + if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_RESOLVE_BENEATH | + AT_EMPTY_PATH)) != 0) return (EINVAL); if ((error = getutimens(tptr, tptrseg, ts, &flags)) != 0) return (error); NDINIT_ATRIGHTS(&nd, LOOKUP, at2cnpflags(flag, AT_SYMLINK_NOFOLLOW | - AT_RESOLVE_BENEATH) | AUDITVNODE1, + AT_RESOLVE_BENEATH | AT_EMPTY_PATH) | AUDITVNODE1, pathseg, path, fd, &cap_futimes_rights, td); if ((error = namei(&nd)) != 0) return (error); |