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 @@ -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,7 +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, - cap_rights_t *needrightsp, struct file **fpp, seqc_t *seqp); + 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, @@ -2163,7 +2164,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 @@ -2910,7 +2912,7 @@ #ifdef CAPABILITIES int -fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp, +fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp, uint8_t *flagsp, struct file **fpp, struct filecaps *havecapsp) { struct filedesc *fdp = td->td_proc->p_fd; @@ -2920,7 +2922,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); @@ -2950,11 +2953,11 @@ } #else int -fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp, +fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp, 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); @@ -3151,13 +3154,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)) { @@ -3175,6 +3180,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 @@ -3222,7 +3231,7 @@ #ifdef CAPABILITIES static int fget_unlocked_seq(struct thread *td, int fd, 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; @@ -3231,6 +3240,7 @@ seqc_t seq; cap_rights_t haverights; int error; + uint8_t flags; fdp = td->td_proc->p_fd; fdt = fdp->fd_files; @@ -3241,6 +3251,7 @@ seq = seqc_read_notmodify(fd_seqc(fdt, fd)); fde = &fdt->fdt_ofiles[fd]; haverights = *cap_rights_fde_inline(fde); + flags = fde->fde_flags; fp = fde->fde_file; if (__predict_false(fp == NULL)) { if (seqc_consistent(fd_seqc(fdt, fd), seq)) @@ -3270,19 +3281,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, 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; @@ -3290,6 +3303,7 @@ return (EBADF); for (;;) { + flags = fdt->fdt_ofiles[fd].fde_flags; fp = fdt->fdt_ofiles[fd].fde_file; if (__predict_false(fp == NULL)) return (EBADF); @@ -3307,6 +3321,8 @@ break; fdrop(fp, td); } + if (flagsp != NULL) + *flagsp = flags; *fpp = fp; return (0); } @@ -3375,7 +3391,7 @@ fdrop(fp, td); out_fallback: *fpp = NULL; - return (fget_unlocked_seq(td, fd, needrightsp, fpp, NULL)); + return (fget_unlocked_seq(td, fd, needrightsp, NULL, fpp, NULL)); } /* @@ -3527,7 +3543,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)) { @@ -3580,7 +3596,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); @@ -3641,7 +3657,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 #include "opt_ddb.h" #include @@ -66,6 +65,7 @@ #include #include #include +#include #include #include #include @@ -3409,22 +3409,33 @@ free(fdep[0], M_FILECAPS); } +static bool +restrict_caps(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); +} + 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; @@ -3466,11 +3477,20 @@ *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; + bool xprison; + + fp = fdep[i]->fde_file; + caps = fdep[i]->fde_caps; + xprison = restrict_caps(fp, td); + if (xprison) + cap_rights_clear(&caps.fc_rights, + CAP_FCHDIR, CAP_FCHROOT); + _finstall(fdesc, fp, *fdp, fdflags | + (xprison ? O_RESOLVE_BENEATH : 0), &caps); + unp_externalize_fp(fp); } /* 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 @@ -1310,6 +1310,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) { 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 @@ -284,7 +285,7 @@ int fget_cap_noref(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, struct file **fpp, struct filecaps *havecapsp); int fget_cap(struct thread *td, int fd, 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, 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 @@ -195,6 +195,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.