Index: sys/kern/vfs_lookup.c =================================================================== --- sys/kern/vfs_lookup.c +++ sys/kern/vfs_lookup.c @@ -102,6 +102,7 @@ static void namei_cleanup_cnp(struct componentname *cnp) { + uma_zfree(namei_zone, cnp->cn_pnbuf); #ifdef DIAGNOSTIC cnp->cn_pnbuf = NULL; @@ -158,12 +159,16 @@ char *cp; /* pointer into pathname argument */ struct vnode *dp; /* the directory we are searching */ struct iovec aiov; /* uio for reading symbolic links */ + struct componentname *cnp; + struct thread *td; + struct proc *p; + cap_rights_t rights; struct uio auio; int error, linklen, startdir_used; - struct componentname *cnp = &ndp->ni_cnd; - struct thread *td = cnp->cn_thread; - struct proc *p = td->td_proc; + cnp = &ndp->ni_cnd; + td = cnp->cn_thread; + p = td->td_proc; ndp->ni_cnd.cn_cred = ndp->ni_cnd.cn_thread->td_ucred; KASSERT(cnp->cn_cred && p, ("namei: bad cred/proc")); KASSERT((cnp->cn_nameiop & (~OPMASK)) == 0, @@ -200,9 +205,10 @@ #ifdef CAPABILITY_MODE /* - * In capability mode, lookups must be "strictly relative" (i.e. - * not an absolute path, and not containing '..' components) to + * In capability mode, lookups must be "strictly relative" to * a real file descriptor, not the pseudo-descriptor AT_FDCWD. + * We ensure that the passed path is not absolute, and all + * '..' components do not escape the relative root. */ if (error == 0 && IN_CAPABILITY_MODE(td) && (cnp->cn_flags & NOCAPCHECK) == 0) { @@ -258,8 +264,6 @@ dp = fdp->fd_cdir; VREF(dp); } else { - cap_rights_t rights; - rights = ndp->ni_rightsneeded; cap_rights_set(&rights, CAP_LOOKUP); @@ -299,6 +303,10 @@ } SDT_PROBE3(vfs, namei, lookup, entry, dp, cnp->cn_pnbuf, cnp->cn_flags); + if (ndp->ni_strictrelative != 0) { + ndp->ni_capdir = dp; + vref(dp); + } for (;;) { ndp->ni_startdir = dp; error = lookup(ndp); @@ -386,6 +394,8 @@ vrele(ndp->ni_dvp); out: vrele(ndp->ni_rootdir); + if (ndp->ni_capdir != NULL) + vrele(ndp->ni_capdir); namei_cleanup_cnp(cnp); SDT_PROBE2(vfs, namei, lookup, return, error, NULL); return (error); @@ -618,9 +628,10 @@ /* * Handle "..": five special cases. - * 0. If doing a capability lookup, return ENOTCAPABLE (this is a - * fairly conservative design choice, but it's the only one that we - * are satisfied guarantees the property we're looking for). + * 0. If doing a capability lookup, return ENOTCAPABLE if the lookup + * would escape from the initial file descriptor directory. + * Checks are done before other verifications, to have + * cleaner semantic for e.g. jail roots or mp traversals. * 1. Return an error if this is the last component of * the name and the operation is DELETE or RENAME. * 2. If at root directory (e.g. after chroot) @@ -634,7 +645,7 @@ * the jail or chroot, don't let them out. */ if (cnp->cn_flags & ISDOTDOT) { - if (ndp->ni_strictrelative != 0) { + if (ndp->ni_strictrelative != 0 && dp == ndp->ni_capdir) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_CAPFAIL)) ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); @@ -1082,6 +1093,7 @@ ndp->ni_dirfd = dirfd; ndp->ni_startdir = startdir; ndp->ni_strictrelative = 0; + ndp->ni_capdir = NULL; if (rightsp != NULL) ndp->ni_rightsneeded = *rightsp; else Index: sys/sys/namei.h =================================================================== --- sys/sys/namei.h +++ sys/sys/namei.h @@ -71,6 +71,7 @@ struct vnode *ni_startdir; /* starting directory */ struct vnode *ni_rootdir; /* logical root directory */ struct vnode *ni_topdir; /* logical top directory */ + struct vnode *ni_capdir; /* logical top dir for cap lookups */ int ni_dirfd; /* starting directory for *at functions */ int ni_strictrelative; /* relative lookup only; no '..' */ /*