aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/vfs_syscalls.c
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2021-03-07 14:29:09 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2021-04-15 09:48:11 +0000
commit509124b62616f73dcdc42263ee109392dafafd99 (patch)
tree955762ad6dab0613574d56c408e93eb63de36ac3 /sys/kern/vfs_syscalls.c
parentd51b4b0aac43d9d25f7eb3f17b2d3034a5c851d8 (diff)
downloadsrc-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.c63
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);