Page MenuHomeFreeBSD

D50371.id156498.diff
No OneTemporary

D50371.id156498.diff

diff --git a/sys/fs/fdescfs/fdesc_vnops.c b/sys/fs/fdescfs/fdesc_vnops.c
--- a/sys/fs/fdescfs/fdesc_vnops.c
+++ b/sys/fs/fdescfs/fdesc_vnops.c
@@ -502,7 +502,7 @@
cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
} else {
error = getvnode_path(td, fd,
- cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
+ cap_rights_init_one(&rights, CAP_EXTATTR_SET), NULL, &fp);
}
if (error) {
/*
@@ -639,7 +639,7 @@
VOP_UNLOCK(vn);
td = curthread;
- error = fget_cap(td, fd_fd, &cap_no_rights, &fp, NULL);
+ error = fget_cap(td, fd_fd, &cap_no_rights, NULL, &fp, NULL);
if (error != 0)
goto out;
diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c
--- a/sys/kern/kern_descrip.c
+++ b/sys/kern/kern_descrip.c
@@ -109,8 +109,8 @@
static void fdunused(struct filedesc *fdp, int fd);
static void fdused(struct filedesc *fdp, int fd);
static int fget_unlocked_seq(struct thread *td, int fd,
- const cap_rights_t *needrightsp, struct file **fpp,
- seqc_t *seqp);
+ const cap_rights_t *needrightsp, uint8_t *flagsp,
+ struct file **fpp, seqc_t *seqp);
static int getmaxfd(struct thread *td);
static u_long *filecaps_copy_prep(const struct filecaps *src);
static void filecaps_copy_finish(const struct filecaps *src,
@@ -527,7 +527,8 @@
fde = fdeget_noref(fdp, fd);
if (fde != NULL) {
td->td_retval[0] =
- (fde->fde_flags & UF_EXCLOSE) ? FD_CLOEXEC : 0;
+ ((fde->fde_flags & UF_EXCLOSE) ? FD_CLOEXEC : 0) |
+ ((fde->fde_flags & UF_BENEATH) ? FD_BENEATH : 0);
error = 0;
}
FILEDESC_SUNLOCK(fdp);
@@ -538,8 +539,10 @@
FILEDESC_XLOCK(fdp);
fde = fdeget_noref(fdp, fd);
if (fde != NULL) {
+ /* UF_BENEATH is sticky and cannot be cleared. */
fde->fde_flags = (fde->fde_flags & ~UF_EXCLOSE) |
- (arg & FD_CLOEXEC ? UF_EXCLOSE : 0);
+ (arg & FD_CLOEXEC ? UF_EXCLOSE : 0) |
+ (arg & FD_BENEATH ? UF_BENEATH : 0);
error = 0;
}
FILEDESC_XUNLOCK(fdp);
@@ -2164,7 +2167,8 @@
seqc_write_begin(&fde->fde_seqc);
#endif
fde->fde_file = fp;
- fde->fde_flags = (flags & O_CLOEXEC) != 0 ? UF_EXCLOSE : 0;
+ fde->fde_flags = ((flags & O_CLOEXEC) != 0 ? UF_EXCLOSE : 0) |
+ ((flags & O_RESOLVE_BENEATH) != 0 ? UF_BENEATH : 0);
if (fcaps != NULL)
filecaps_move(fcaps, &fde->fde_caps);
else
@@ -2912,7 +2916,7 @@
#ifdef CAPABILITIES
int
fget_cap(struct thread *td, int fd, const cap_rights_t *needrightsp,
- struct file **fpp, struct filecaps *havecapsp)
+ uint8_t *flagsp, struct file **fpp, struct filecaps *havecapsp)
{
struct filedesc *fdp = td->td_proc->p_fd;
int error;
@@ -2921,7 +2925,8 @@
*fpp = NULL;
for (;;) {
- error = fget_unlocked_seq(td, fd, needrightsp, &fp, &seq);
+ error = fget_unlocked_seq(td, fd, needrightsp, flagsp, &fp,
+ &seq);
if (error != 0)
return (error);
@@ -2952,10 +2957,10 @@
#else
int
fget_cap(struct thread *td, int fd, const cap_rights_t *needrightsp,
- struct file **fpp, struct filecaps *havecapsp)
+ uint8_t *flagsp, struct file **fpp, struct filecaps *havecapsp)
{
int error;
- error = fget_unlocked(td, fd, needrightsp, fpp);
+ error = fget_unlocked(td, fd, needrightsp, flagsp, fpp);
if (havecapsp != NULL && error == 0)
filecaps_fill(havecapsp);
@@ -3038,7 +3043,7 @@
#ifdef CAPABILITIES
int
-fgetvp_lookup_smr(struct nameidata *ndp, struct vnode **vpp, bool *fsearch)
+fgetvp_lookup_smr(struct nameidata *ndp, struct vnode **vpp, int *flagsp)
{
const struct filedescent *fde;
const struct fdescenttbl *fdt;
@@ -3048,7 +3053,7 @@
const cap_rights_t *haverights;
cap_rights_t rights;
seqc_t seq;
- int fd;
+ int fd, flags;
VFS_SMR_ASSERT_ENTERED();
@@ -3068,7 +3073,8 @@
return (EAGAIN);
if (__predict_false(cap_check_inline_transient(haverights, &rights)))
return (EAGAIN);
- *fsearch = ((fp->f_flag & FSEARCH) != 0);
+ flags = fp->f_flag & FSEARCH;
+ flags |= (fde->fde_flags & UF_BENEATH) != 0 ? O_RESOLVE_BENEATH : 0;
vp = fp->f_vnode;
if (__predict_false(vp == NULL)) {
return (EAGAIN);
@@ -3102,17 +3108,19 @@
#endif
}
*vpp = vp;
+ *flagsp = flags;
return (0);
}
#else
int
-fgetvp_lookup_smr(struct nameidata *ndp, struct vnode **vpp, bool *fsearch)
+fgetvp_lookup_smr(struct nameidata *ndp, struct vnode **vpp, int *flagsp)
{
+ const struct filedescent *fde;
const struct fdescenttbl *fdt;
struct filedesc *fdp;
struct file *fp;
struct vnode *vp;
- int fd;
+ int fd, flags;
VFS_SMR_ASSERT_ENTERED();
@@ -3121,9 +3129,12 @@
fdt = fdp->fd_files;
if (__predict_false((u_int)fd >= fdt->fdt_nfiles))
return (EBADF);
- fp = fdt->fdt_ofiles[fd].fde_file;
+ fde = &fdt->fdt_ofiles[fd];
+ fp = fde->fde_file;
if (__predict_false(fp == NULL))
return (EAGAIN);
+ flags = fp->f_flag & FSEARCH;
+ flags |= (fde->fde_flags & UF_BENEATH) != 0 ? O_RESOLVE_BENEATH : 0;
*fsearch = ((fp->f_flag & FSEARCH) != 0);
vp = fp->f_vnode;
if (__predict_false(vp == NULL || vp->v_type != VDIR)) {
@@ -3139,6 +3150,7 @@
return (EAGAIN);
filecaps_fill(&ndp->ni_filecaps);
*vpp = vp;
+ *flagsp = flags;
return (0);
}
#endif
@@ -3152,13 +3164,15 @@
struct componentname *cnp;
cap_rights_t rights;
int error;
+ uint8_t flags;
td = curthread;
rights = *ndp->ni_rightsneeded;
cap_rights_set_one(&rights, CAP_LOOKUP);
cnp = &ndp->ni_cnd;
- error = fget_cap(td, ndp->ni_dirfd, &rights, &fp, &ndp->ni_filecaps);
+ error = fget_cap(td, ndp->ni_dirfd, &rights, &flags, &fp,
+ &ndp->ni_filecaps);
if (__predict_false(error != 0))
return (error);
if (__predict_false(fp->f_ops == &badfileops)) {
@@ -3176,6 +3190,10 @@
*/
if ((fp->f_flag & FSEARCH) != 0)
cnp->cn_flags |= NOEXECCHECK;
+ if ((flags & UF_BENEATH) != 0) {
+ cnp->cn_flags |= RBENEATH;
+ ndp->ni_resflags |= NIRES_BENEATH;
+ }
fdrop(fp, td);
#ifdef CAPABILITIES
@@ -3223,7 +3241,7 @@
#ifdef CAPABILITIES
static int
fget_unlocked_seq(struct thread *td, int fd, const cap_rights_t *needrightsp,
- struct file **fpp, seqc_t *seqp)
+ uint8_t *flagsp, struct file **fpp, seqc_t *seqp)
{
struct filedesc *fdp;
const struct filedescent *fde;
@@ -3232,6 +3250,7 @@
seqc_t seq;
cap_rights_t haverights;
int error;
+ uint8_t flags;
fdp = td->td_proc->p_fd;
fdt = fdp->fd_files;
@@ -3243,6 +3262,7 @@
fde = &fdt->fdt_ofiles[fd];
haverights = *cap_rights_fde_inline(fde);
fp = fde->fde_file;
+ flags = fde->fde_flags;
if (__predict_false(fp == NULL)) {
if (seqc_consistent(fd_seqc(fdt, fd), seq))
return (EBADF);
@@ -3271,19 +3291,21 @@
fdrop(fp, td);
}
*fpp = fp;
- if (seqp != NULL) {
+ if (flagsp != NULL)
+ *flagsp = flags;
+ if (seqp != NULL)
*seqp = seq;
- }
return (0);
}
#else
static int
fget_unlocked_seq(struct thread *td, int fd, const cap_rights_t *needrightsp,
- struct file **fpp, seqc_t *seqp __unused)
+ uint8_t *flagsp, struct file **fpp, seqc_t *seqp __unused)
{
struct filedesc *fdp;
const struct fdescenttbl *fdt;
struct file *fp;
+ uint8_t flags;
fdp = td->td_proc->p_fd;
fdt = fdp->fd_files;
@@ -3292,6 +3314,7 @@
for (;;) {
fp = fdt->fdt_ofiles[fd].fde_file;
+ flags = fdt->fdt_ofiles[fd].fde_flags;
if (__predict_false(fp == NULL))
return (EBADF);
if (__predict_false(!refcount_acquire_if_not_zero(&fp->f_count))) {
@@ -3308,6 +3331,8 @@
break;
fdrop(fp, td);
}
+ if (flagsp != NULL)
+ *flagsp = flags;
*fpp = fp;
return (0);
}
@@ -3321,8 +3346,8 @@
* racing with itself.
*/
int
-fget_unlocked(struct thread *td, int fd, const cap_rights_t *needrightsp,
- struct file **fpp)
+fget_unlocked_flags(struct thread *td, int fd, const cap_rights_t *needrightsp,
+ uint8_t *flagsp, struct file **fpp)
{
struct filedesc *fdp;
#ifdef CAPABILITIES
@@ -3334,6 +3359,7 @@
seqc_t seq;
const cap_rights_t *haverights;
#endif
+ uint8_t flags;
fdp = td->td_proc->p_fd;
fdt = fdp->fd_files;
@@ -3346,8 +3372,10 @@
fde = &fdt->fdt_ofiles[fd];
haverights = cap_rights_fde_inline(fde);
fp = fde->fde_file;
+ flags = fde->fde_flags;
#else
fp = fdt->fdt_ofiles[fd].fde_file;
+ flags = fdt->fdt_ofiles[fd].fde_flags;
#endif
if (__predict_false(fp == NULL))
goto out_fallback;
@@ -3371,12 +3399,21 @@
#endif
goto out_fdrop;
*fpp = fp;
+ if (flagsp != NULL)
+ *flagsp = flags;
return (0);
out_fdrop:
fdrop(fp, td);
out_fallback:
*fpp = NULL;
- return (fget_unlocked_seq(td, fd, needrightsp, fpp, NULL));
+ return (fget_unlocked_seq(td, fd, needrightsp, flagsp, fpp, NULL));
+}
+
+int
+fget_unlocked(struct thread *td, int fd, const cap_rights_t *needrightsp,
+ struct file **fpp)
+{
+ return (fget_unlocked_flags(td, fd, needrightsp, NULL, fpp));
}
/*
@@ -3528,7 +3565,7 @@
fdp = td->td_proc->p_fd;
MPASS(cap_rights_is_set(rightsp, CAP_MMAP));
for (;;) {
- error = fget_unlocked_seq(td, fd, rightsp, &fp, &seq);
+ error = fget_unlocked_seq(td, fd, rightsp, NULL, &fp, &seq);
if (__predict_false(error != 0))
return (error);
if (__predict_false(fp->f_ops == &badfileops)) {
@@ -3583,7 +3620,7 @@
*fpp = NULL;
MPASS(cap_rights_is_set(rightsp, CAP_FCNTL));
for (;;) {
- error = fget_unlocked_seq(td, fd, rightsp, &fp, &seq);
+ error = fget_unlocked_seq(td, fd, rightsp, NULL, &fp, &seq);
if (error != 0)
return (error);
error = cap_fcntl_check(fdp, fd, needfcntl);
@@ -3645,7 +3682,7 @@
struct file *fp;
int error;
- error = fget_cap(td, fd, needrightsp, &fp, &caps);
+ error = fget_cap(td, fd, needrightsp, NULL, &fp, &caps);
if (error != 0)
return (error);
if (fp->f_ops == &badfileops) {
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -91,7 +91,7 @@
struct file *fp;
int error;
- error = fget_cap(td, fd, rightsp, &fp, havecapsp);
+ error = fget_cap(td, fd, rightsp, NULL, &fp, havecapsp);
if (__predict_false(error != 0))
return (error);
if (__predict_false(fp->f_type != DTYPE_SOCKET)) {
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -56,7 +56,6 @@
* need a proper out-of-band
*/
-#include <sys/cdefs.h>
#include "opt_ddb.h"
#include <sys/param.h>
@@ -66,6 +65,7 @@
#include <sys/fcntl.h>
#include <sys/file.h>
#include <sys/filedesc.h>
+#include <sys/jail.h>
#include <sys/kernel.h>
#include <sys/lock.h>
#include <sys/malloc.h>
@@ -3435,22 +3435,34 @@
free(fdep[0], M_FILECAPS);
}
+static bool
+restrict_rights(struct file *fp, struct thread *td)
+{
+ struct prison *prison1, *prison2;
+
+ prison1 = fp->f_cred->cr_prison;
+ prison2 = td->td_ucred->cr_prison;
+ return (prison1 != prison2 && prison1->pr_root != prison2->pr_root &&
+ prison2 != &prison0);
+}
+
static int
unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
{
struct thread *td = curthread; /* XXX */
struct cmsghdr *cm = mtod(control, struct cmsghdr *);
- int i;
int *fdp;
struct filedesc *fdesc = td->td_proc->p_fd;
struct filedescent **fdep;
void *data;
socklen_t clen = control->m_len, datalen;
- int error, newfds;
+ int error, fdflags, newfds;
u_int newlen;
UNP_LINK_UNLOCK_ASSERT();
+ fdflags = (flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
+
error = 0;
if (controlp != NULL) /* controlp == NULL => free control messages */
*controlp = NULL;
@@ -3492,11 +3504,16 @@
*controlp = NULL;
goto next;
}
- for (i = 0; i < newfds; i++, fdp++) {
- _finstall(fdesc, fdep[i]->fde_file, *fdp,
- (flags & MSG_CMSG_CLOEXEC) != 0 ? O_CLOEXEC : 0,
- &fdep[i]->fde_caps);
- unp_externalize_fp(fdep[i]->fde_file);
+ for (int i = 0; i < newfds; i++, fdp++) {
+ struct filecaps caps;
+ struct file *fp;
+
+ fp = fdep[i]->fde_file;
+ caps = fdep[i]->fde_caps;
+ _finstall(fdesc, fp, *fdp, fdflags |
+ (restrict_rights(fp, td) ?
+ O_RESOLVE_BENEATH : 0), &caps);
+ unp_externalize_fp(fp);
}
/*
diff --git a/sys/kern/vfs_acl.c b/sys/kern/vfs_acl.c
--- a/sys/kern/vfs_acl.c
+++ b/sys/kern/vfs_acl.c
@@ -434,7 +434,7 @@
AUDIT_ARG_FD(uap->filedes);
error = getvnode_path(td, uap->filedes,
- cap_rights_init_one(&rights, CAP_ACL_GET), &fp);
+ cap_rights_init_one(&rights, CAP_ACL_GET), NULL, &fp);
if (error == 0) {
error = vacl_get_acl(td, fp->f_vnode, uap->type, uap->aclp);
fdrop(fp, td);
@@ -569,7 +569,7 @@
AUDIT_ARG_FD(uap->filedes);
error = getvnode_path(td, uap->filedes,
- cap_rights_init_one(&rights, CAP_ACL_CHECK), &fp);
+ cap_rights_init_one(&rights, CAP_ACL_CHECK), NULL, &fp);
if (error == 0) {
error = vacl_aclcheck(td, fp->f_vnode, uap->type, uap->aclp);
fdrop(fp, td);
diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c
--- a/sys/kern/vfs_cache.c
+++ b/sys/kern/vfs_cache.c
@@ -4528,17 +4528,23 @@
{
struct nameidata *ndp;
struct componentname *cnp;
- int error;
- bool fsearch;
+ int error, flags;
ndp = fpl->ndp;
cnp = fpl->cnp;
- error = fgetvp_lookup_smr(ndp, vpp, &fsearch);
+ error = fgetvp_lookup_smr(ndp, vpp, &flags);
if (__predict_false(error != 0)) {
return (cache_fpl_aborted(fpl));
}
- fpl->fsearch = fsearch;
+ if (__predict_false((flags & O_RESOLVE_BENEATH) != 0)) {
+ _Static_assert((CACHE_FPL_SUPPORTED_CN_FLAGS & RBENEATH) == 0,
+ "RBENEATH supported by fplookup");
+ cache_fpl_smr_exit(fpl);
+ cache_fpl_aborted(fpl);
+ return (EOPNOTSUPP);
+ }
+ fpl->fsearch = (flags & FSEARCH) != 0;
if ((*vpp)->v_type != VDIR) {
if (!((cnp->cn_flags & EMPTYPATH) != 0 && cnp->cn_pnbuf[0] == '\0')) {
cache_fpl_smr_exit(fpl);
diff --git a/sys/kern/vfs_extattr.c b/sys/kern/vfs_extattr.c
--- a/sys/kern/vfs_extattr.c
+++ b/sys/kern/vfs_extattr.c
@@ -253,7 +253,7 @@
AUDIT_ARG_TEXT(attrname);
error = getvnode_path(td, fd,
- cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp);
+ cap_rights_init_one(&rights, CAP_EXTATTR_SET), NULL, &fp);
if (error)
return (error);
@@ -441,7 +441,7 @@
AUDIT_ARG_TEXT(attrname);
error = getvnode_path(td, fd,
- cap_rights_init_one(&rights, CAP_EXTATTR_GET), &fp);
+ cap_rights_init_one(&rights, CAP_EXTATTR_GET), NULL, &fp);
if (error)
return (error);
@@ -597,7 +597,7 @@
AUDIT_ARG_TEXT(attrname);
error = getvnode_path(td, fd,
- cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), &fp);
+ cap_rights_init_one(&rights, CAP_EXTATTR_DELETE), NULL, &fp);
if (error)
return (error);
@@ -764,7 +764,7 @@
AUDIT_ARG_FD(fd);
AUDIT_ARG_VALUE(attrnamespace);
error = getvnode_path(td, fd,
- cap_rights_init_one(&rights, CAP_EXTATTR_LIST), &fp);
+ cap_rights_init_one(&rights, CAP_EXTATTR_LIST), NULL, &fp);
if (error)
return (error);
diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c
--- a/sys/kern/vfs_syscalls.c
+++ b/sys/kern/vfs_syscalls.c
@@ -371,7 +371,7 @@
int error;
AUDIT_ARG_FD(fd);
- error = getvnode_path(td, fd, &cap_fstatfs_rights, &fp);
+ error = getvnode_path(td, fd, &cap_fstatfs_rights, NULL, &fp);
if (error != 0)
return (error);
vp = fp->f_vnode;
@@ -894,12 +894,17 @@
struct mount *mp;
struct file *fp;
int error;
+ uint8_t fdflags;
AUDIT_ARG_FD(uap->fd);
- error = getvnode_path(td, uap->fd, &cap_fchdir_rights,
+ error = getvnode_path(td, uap->fd, &cap_fchdir_rights, &fdflags,
&fp);
if (error != 0)
return (error);
+ if ((fdflags & UF_BENEATH) != 0) {
+ fdrop(fp, td);
+ return (ENOTCAPABLE);
+ }
vp = fp->f_vnode;
vrefact(vp);
fdrop(fp, td);
@@ -1042,10 +1047,15 @@
struct vnode *vp;
struct file *fp;
int error;
+ uint8_t fdflags;
- error = getvnode_path(td, uap->fd, &cap_fchroot_rights, &fp);
+ error = getvnode_path(td, uap->fd, &cap_fchroot_rights, &fdflags, &fp);
if (error != 0)
return (error);
+ if ((fdflags & UF_BENEATH) != 0) {
+ fdrop(fp, td);
+ return (ENOTCAPABLE);
+ }
vp = fp->f_vnode;
vrefact(vp);
fdrop(fp, td);
@@ -1310,6 +1320,10 @@
else
#endif
fcaps = NULL;
+ if ((nd.ni_resflags & NIRES_BENEATH) != 0)
+ flags |= O_RESOLVE_BENEATH;
+ else
+ flags &= ~O_RESOLVE_BENEATH;
error = finstall_refed(td, fp, &indx, flags, fcaps);
/* On success finstall_refed() consumes fcaps. */
if (error != 0) {
@@ -2014,7 +2028,7 @@
fp = NULL;
if (fd != FD_NONE) {
- error = getvnode_path(td, fd, &cap_no_rights, &fp);
+ error = getvnode_path(td, fd, &cap_no_rights, NULL, &fp);
if (error != 0)
return (error);
}
@@ -4410,12 +4424,12 @@
*/
int
getvnode_path(struct thread *td, int fd, const cap_rights_t *rightsp,
- struct file **fpp)
+ uint8_t *flagsp, struct file **fpp)
{
struct file *fp;
int error;
- error = fget_unlocked(td, fd, rightsp, &fp);
+ error = fget_unlocked_flags(td, fd, rightsp, flagsp, &fp);
if (error != 0)
return (error);
@@ -4452,7 +4466,7 @@
{
int error;
- error = getvnode_path(td, fd, rightsp, fpp);
+ error = getvnode_path(td, fd, rightsp, NULL, fpp);
if (__predict_false(error != 0))
return (error);
diff --git a/sys/sys/fcntl.h b/sys/sys/fcntl.h
--- a/sys/sys/fcntl.h
+++ b/sys/sys/fcntl.h
@@ -279,6 +279,8 @@
/* file descriptor flags (F_GETFD, F_SETFD) */
#define FD_CLOEXEC 1 /* close-on-exec flag */
+#define FD_BENEATH 2 /* all lookups relative to fd have
+ O_RESOLVE_BENEATH semantics */
/* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */
#define F_RDLCK 1 /* shared or read lock */
diff --git a/sys/sys/file.h b/sys/sys/file.h
--- a/sys/sys/file.h
+++ b/sys/sys/file.h
@@ -301,7 +301,7 @@
struct vnode **vpp);
int fgetvp_write(struct thread *td, int fd, const cap_rights_t *rightsp,
struct vnode **vpp);
-int fgetvp_lookup_smr(struct nameidata *ndp, struct vnode **vpp, bool *fsearch);
+int fgetvp_lookup_smr(struct nameidata *ndp, struct vnode **vpp, int *flagsp);
int fgetvp_lookup(struct nameidata *ndp, struct vnode **vpp);
static __inline __result_use_check bool
diff --git a/sys/sys/filedesc.h b/sys/sys/filedesc.h
--- a/sys/sys/filedesc.h
+++ b/sys/sys/filedesc.h
@@ -148,6 +148,7 @@
* Per-process open flags.
*/
#define UF_EXCLOSE 0x01 /* auto-close on exec */
+#define UF_BENEATH 0x02 /* lookups must be beneath this dir */
#ifdef _KERNEL
@@ -278,17 +279,20 @@
int getvnode(struct thread *td, int fd, const cap_rights_t *rightsp,
struct file **fpp);
int getvnode_path(struct thread *td, int fd, const cap_rights_t *rightsp,
- struct file **fpp);
+ uint8_t *flagsp, struct file **fpp);
void mountcheckdirs(struct vnode *olddp, struct vnode *newdp);
int fget_cap_noref(struct filedesc *fdp, int fd,
const cap_rights_t *needrightsp, struct file **fpp,
struct filecaps *havecapsp);
int fget_cap(struct thread *td, int fd, const cap_rights_t *needrightsp,
- struct file **fpp, struct filecaps *havecapsp);
+ uint8_t *flagsp, struct file **fpp, struct filecaps *havecapsp);
/* Return a referenced file from an unlocked descriptor. */
int fget_unlocked(struct thread *td, int fd,
const cap_rights_t *needrightsp, struct file **fpp);
+int fget_unlocked_flags(struct thread *td, int fd,
+ const cap_rights_t *needrightsp, uint8_t *flagsp,
+ struct file **fpp);
/* Return a file pointer without a ref. FILEDESC_IS_ONLY_USER must be true. */
int fget_only_user(struct filedesc *fdp, int fd,
const cap_rights_t *needrightsp, struct file **fpp);
diff --git a/sys/sys/namei.h b/sys/sys/namei.h
--- a/sys/sys/namei.h
+++ b/sys/sys/namei.h
@@ -196,6 +196,7 @@
#define NIRES_ABS 0x00000001 /* Path was absolute */
#define NIRES_STRICTREL 0x00000002 /* Restricted lookup result */
#define NIRES_EMPTYPATH 0x00000004 /* EMPTYPATH used */
+#define NIRES_BENEATH 0x00000008 /* O_RESOLVE_BENEATH is to be inherited */
/*
* Flags in ni_lcf, valid for the duration of the namei call.
diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile
--- a/tests/sys/kern/Makefile
+++ b/tests/sys/kern/Makefile
@@ -96,6 +96,8 @@
LIBADD.fdgrowtable_test+= util pthread kvm procstat
LIBADD.sigwait+= rt
LIBADD.ktrace_test+= sysdecode
+LIBADD.unix_passfd_dgram+= jail
+LIBADD.unix_passfd_stream+= jail
LIBADD.unix_stream+= pthread
NETBSD_ATF_TESTS_C+= lockf_test
diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c
--- a/tests/sys/kern/unix_passfd_test.c
+++ b/tests/sys/kern/unix_passfd_test.c
@@ -27,15 +27,19 @@
*/
#include <sys/param.h>
+#include <sys/jail.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/sysctl.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/un.h>
+#include <sys/wait.h>
+#include <err.h>
#include <errno.h>
#include <fcntl.h>
+#include <jail.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
@@ -1032,6 +1036,135 @@
closesocketpair(fd);
}
+ATF_TC_WITH_CLEANUP(cross_jail_dirfd);
+ATF_TC_HEAD(cross_jail_dirfd, tc)
+{
+ atf_tc_set_md_var(tc, "require.user", "root");
+}
+ATF_TC_BODY(cross_jail_dirfd, tc)
+{
+ int error, sock[2], jid1, jid2, status;
+ pid_t pid1, pid2;
+
+ domainsocketpair(sock);
+
+ error = mkdir("./a", 0755);
+ ATF_REQUIRE(error == 0);
+ error = mkdir("./b", 0755);
+ ATF_REQUIRE(error == 0);
+ error = mkdir("./c", 0755);
+ ATF_REQUIRE(error == 0);
+ error = mkdir("./a/c", 0755);
+ ATF_REQUIRE(error == 0);
+
+ jid1 = jail_setv(JAIL_CREATE,
+ "name", "passfd_test_cross_jail_dirfd1",
+ "path", "./a",
+ "persist", NULL,
+ NULL);
+ ATF_REQUIRE_MSG(jid1 >= 0, "jail_setv: %s", jail_errmsg);
+
+ jid2 = jail_setv(JAIL_CREATE,
+ "name", "passfd_test_cross_jail_dirfd2",
+ "path", "./b",
+ "persist", NULL,
+ NULL);
+ ATF_REQUIRE_MSG(jid2 >= 0, "jail_setv: %s", jail_errmsg);
+
+ pid1 = fork();
+ ATF_REQUIRE(pid1 >= 0);
+ if (pid1 == 0) {
+ ssize_t len;
+ int dfd, error;
+ char ch;
+
+ error = jail_attach(jid1);
+ if (error != 0)
+ err(1, "jail_attach");
+
+ dfd = open(".", O_RDONLY | O_DIRECTORY);
+ if (dfd < 0)
+ err(1, "open(\".\") in jail %d", jid1);
+
+ ch = 0;
+ len = sendfd_payload(sock[0], dfd, &ch, sizeof(ch));
+ if (len == -1)
+ err(1, "sendmsg");
+
+ _exit(0);
+ }
+
+ pid2 = fork();
+ ATF_REQUIRE(pid2 >= 0);
+ if (pid2 == 0) {
+ ssize_t len;
+ int dfd, dfd2, error, fd;
+ char ch;
+
+ error = jail_attach(jid2);
+ if (error != 0)
+ err(1, "jail_attach");
+
+ /* Get a directory from outside the jail root. */
+ len = recvfd_payload(sock[1], &dfd, &ch, sizeof(ch),
+ CMSG_SPACE(sizeof(int)), 0);
+ if (len == -1)
+ err(1, "recvmsg");
+
+ if ((fcntl(dfd, F_GETFD) & FD_BENEATH) == 0)
+ errx(1, "dfd does not have FD_BENEATH set");
+
+ /* Make sure we can't chdir. */
+ error = fchdir(dfd);
+ if (error == 0)
+ errx(1, "fchdir succeeded");
+ if (errno != ENOTCAPABLE)
+ err(1, "fchdir");
+
+ /* Make sure a dotdot access fails. */
+ fd = openat(dfd, "../c", O_RDONLY | O_DIRECTORY);
+ if (fd >= 0)
+ errx(1, "openat(\"../c\") succeeded");
+ if (errno != ENOTCAPABLE)
+ err(1, "openat");
+
+ /* Accesses within the sender's jail root are ok. */
+ fd = openat(dfd, "c", O_RDONLY | O_DIRECTORY);
+ if (fd < 0)
+ err(1, "openat(\"c\")");
+
+ dfd2 = openat(dfd, "", O_EMPTY_PATH | O_RDONLY | O_DIRECTORY);
+ if (dfd2 < 0)
+ err(1, "openat(\"\")");
+ if ((fcntl(dfd2, F_GETFD) & FD_BENEATH) == 0)
+ errx(1, "dfd2 does not have FD_BENEATH set");
+
+ _exit(0);
+ }
+
+ error = waitpid(pid1, &status, 0);
+ ATF_REQUIRE(error != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+ error = waitpid(pid2, &status, 0);
+ ATF_REQUIRE(error != -1);
+ ATF_REQUIRE(WIFEXITED(status));
+ ATF_REQUIRE(WEXITSTATUS(status) == 0);
+
+ closesocketpair(sock);
+}
+ATF_TC_CLEANUP(cross_jail_dirfd, tc)
+{
+ int jid;
+
+ jid = jail_getid("passfd_test_cross_jail_dirfd1");
+ if (jid >= 0 && jail_remove(jid) != 0)
+ err(1, "jail_remove");
+ jid = jail_getid("passfd_test_cross_jail_dirfd2");
+ if (jid >= 0 && jail_remove(jid) != 0)
+ err(1, "jail_remove");
+}
+
ATF_TP_ADD_TCS(tp)
{
@@ -1052,6 +1185,7 @@
ATF_TP_ADD_TC(tp, copyout_rights_error);
ATF_TP_ADD_TC(tp, empty_rights_message);
ATF_TP_ADD_TC(tp, control_creates_records);
+ ATF_TP_ADD_TC(tp, cross_jail_dirfd);
return (atf_no_error());
}

File Metadata

Mime Type
text/plain
Expires
Tue, Jan 27, 1:15 PM (2 h, 15 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28056209
Default Alt Text
D50371.id156498.diff (23 KB)

Event Timeline