Index: sys/fs/nfsserver/nfs_nfsdport.c =================================================================== --- sys/fs/nfsserver/nfs_nfsdport.c +++ sys/fs/nfsserver/nfs_nfsdport.c @@ -349,7 +349,7 @@ *retdirp = NULL; cnp->cn_nameptr = cnp->cn_pnbuf; - ndp->ni_strictrelative = 0; + ndp->ni_nonrelativeerrno = 0; /* * Extract and set starting directory. */ Index: sys/kern/vfs_lookup.c =================================================================== --- sys/kern/vfs_lookup.c +++ sys/kern/vfs_lookup.c @@ -182,7 +182,8 @@ */ if (error == 0 && IN_CAPABILITY_MODE(td) && (cnp->cn_flags & NOCAPCHECK) == 0) { - ndp->ni_strictrelative = 1; + if (ndp->ni_nonrelativeerrno != EPERM) + ndp->ni_nonrelativeerrno = ENOTCAPABLE; if (ndp->ni_dirfd == AT_FDCWD) { #ifdef KTRACE if (KTRPOINT(td, KTR_CAPFAIL)) @@ -242,14 +243,17 @@ * If file descriptor doesn't have all rights, * all lookups relative to it must also be * strictly relative. + * + * Don't overwrite a value of EPERM, however: + * we need to honour a user O_BENEATH request. */ CAP_ALL(&rights); - if (!cap_rights_contains(&ndp->ni_filecaps.fc_rights, - &rights) || - ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || - ndp->ni_filecaps.fc_nioctls != -1) { - ndp->ni_strictrelative = 1; - } + if ((!cap_rights_contains(&ndp->ni_filecaps.fc_rights, + &rights) || + ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || + ndp->ni_filecaps.fc_nioctls != -1) && + ndp->ni_nonrelativeerrno != EPERM) + ndp->ni_nonrelativeerrno = ENOTCAPABLE; #endif } if (error != 0 || dp != NULL) { @@ -281,13 +285,13 @@ cnp->cn_nameptr = cnp->cn_pnbuf; if (*(cnp->cn_nameptr) == '/') { vrele(dp); - if (ndp->ni_strictrelative != 0) { + if (ndp->ni_nonrelativeerrno != 0) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_CAPFAIL)) ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); #endif namei_cleanup_cnp(cnp); - return (ENOTCAPABLE); + return (ndp->ni_nonrelativeerrno); } while (*(cnp->cn_nameptr) == '/') { cnp->cn_nameptr++; @@ -622,12 +626,12 @@ * the jail or chroot, don't let them out. */ if (cnp->cn_flags & ISDOTDOT) { - if (ndp->ni_strictrelative != 0) { + if (ndp->ni_nonrelativeerrno != 0) { #ifdef KTRACE if (KTRPOINT(curthread, KTR_CAPFAIL)) ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); #endif - error = ENOTCAPABLE; + error = ndp->ni_nonrelativeerrno; goto bad; } if ((cnp->cn_flags & ISLASTCN) != 0 && @@ -1062,7 +1066,7 @@ ndp->ni_dirp = namep; ndp->ni_dirfd = dirfd; ndp->ni_startdir = startdir; - ndp->ni_strictrelative = 0; + ndp->ni_nonrelativeerrno = 0; if (rightsp != NULL) ndp->ni_rightsneeded = *rightsp; else Index: sys/kern/vfs_syscalls.c =================================================================== --- sys/kern/vfs_syscalls.c +++ sys/kern/vfs_syscalls.c @@ -1049,7 +1049,7 @@ struct vnode *vp; struct nameidata nd; cap_rights_t rights; - int cmode, error, indx; + int beneath, cmode, error, indx; indx = -1; @@ -1058,6 +1058,7 @@ /* XXX: audit dirfd */ cap_rights_init(&rights, CAP_LOOKUP); flags_to_rights(flags, &rights); + beneath = ((flags & O_BENEATH) == O_BENEATH); /* * Only one of the O_EXEC, O_RDONLY, O_WRONLY and O_RDWR flags * may be specified. @@ -1086,6 +1087,8 @@ cmode = ((mode & ~fdp->fd_cmask) & ALLPERMS) & ~S_ISTXT; NDINIT_ATRIGHTS(&nd, LOOKUP, FOLLOW | AUDITVNODE1, pathseg, path, fd, &rights, td); + if (beneath) + nd.ni_nonrelativeerrno = EPERM; td->td_dupfd = -1; /* XXX check for fdopen */ error = vn_open(&nd, &flags, cmode, fp); if (error != 0) { @@ -1100,11 +1103,11 @@ /* * Handle special fdopen() case. bleh. * - * Don't do this for relative (capability) lookups; we don't + * Don't do this for capability or O_BENEATH lookups: we don't * understand exactly what would happen, and we don't think * that it ever should. */ - if (nd.ni_strictrelative == 0 && + if (nd.ni_nonrelativeerrno == 0 && (error == ENODEV || error == ENXIO) && td->td_dupfd >= 0) { error = dupfdopen(td, fdp, td->td_dupfd, flags, error, @@ -1150,7 +1153,8 @@ struct filecaps *fcaps; #ifdef CAPABILITIES - if (nd.ni_strictrelative == 1) + if (nd.ni_nonrelativeerrno != 0 && + cap_rights_is_valid(&nd.ni_filecaps.fc_rights)) fcaps = &nd.ni_filecaps; else #endif Index: sys/sys/fcntl.h =================================================================== --- sys/sys/fcntl.h +++ sys/sys/fcntl.h @@ -131,6 +131,7 @@ #if __BSD_VISIBLE #define O_VERIFY 0x00200000 /* open only after verification */ +#define O_BENEATH 0x00400000 /* stay beneath a specified directory */ #endif /* Index: sys/sys/namei.h =================================================================== --- sys/sys/namei.h +++ sys/sys/namei.h @@ -73,7 +73,7 @@ struct vnode *ni_rootdir; /* logical root directory */ struct vnode *ni_topdir; /* logical top directory */ int ni_dirfd; /* starting directory for *at functions */ - int ni_strictrelative; /* relative lookup only; no '..' */ + int ni_nonrelativeerrno; /* errno for non-relative (..) lookup */ /* * Results: returned from namei */