diff --git a/sys/kern/kern_descrip.c b/sys/kern/kern_descrip.c index f626f5a38ca8..ef7aea82a609 100644 --- a/sys/kern/kern_descrip.c +++ b/sys/kern/kern_descrip.c @@ -3054,7 +3054,7 @@ fgetvp_lookup_smr(struct nameidata *ndp, struct vnode **vpp, bool *fsearch) ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || ndp->ni_filecaps.fc_nioctls != -1) { #ifdef notyet - ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; + ndp->ni_lcf |= NI_LCF_STRICTREL; #else return (EAGAIN); #endif @@ -3146,7 +3146,7 @@ fgetvp_lookup(struct nameidata *ndp, struct vnode **vpp) 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_lcf |= NI_LCF_STRICTRELATIVE; + ndp->ni_lcf |= NI_LCF_STRICTREL; ndp->ni_resflags |= NIRES_STRICTREL; } #endif diff --git a/sys/kern/kern_exec.c b/sys/kern/kern_exec.c index ae2b624c2659..6727872b5b10 100644 --- a/sys/kern/kern_exec.c +++ b/sys/kern/kern_exec.c @@ -454,6 +454,8 @@ do_execve(struct thread *td, struct image_args *args, struct mac *mac_p, interpret: if (args->fname != NULL) { #ifdef CAPABILITY_MODE + if (CAP_TRACING(td)) + ktrcapfail(CAPFAIL_NAMEI, args->fname); /* * While capability mode can't reach this point via direct * path arguments to execve(), we also don't allow diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c index 13fb0915cdaa..5347378c2b4d 100644 --- a/sys/kern/uipc_shm.c +++ b/sys/kern/uipc_shm.c @@ -1177,8 +1177,12 @@ kern_shm_open2(struct thread *td, const char *userpath, int flags, mode_t mode, /* * shm_open(2) is only allowed for anonymous objects. */ - if (IN_CAPABILITY_MODE(td) && (userpath != SHM_ANON)) - return (ECAPMODE); + if (userpath != SHM_ANON) { + if (CAP_TRACING(td)) + ktrcapfail(CAPFAIL_NAMEI, userpath); + if (IN_CAPABILITY_MODE(td)) + return (ECAPMODE); + } #endif AUDIT_ARG_FFLAGS(flags); diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c index b2d03d932b99..58b993dea5f2 100644 --- a/sys/kern/uipc_syscalls.c +++ b/sys/kern/uipc_syscalls.c @@ -199,8 +199,12 @@ kern_bindat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) int error; #ifdef CAPABILITY_MODE - if (IN_CAPABILITY_MODE(td) && (dirfd == AT_FDCWD)) - return (ECAPMODE); + if (dirfd == AT_FDCWD) { + if (CAP_TRACING(td)) + ktrcapfail(CAPFAIL_NAMEI, "AT_FDCWD"); + if (IN_CAPABILITY_MODE(td)) + return (ECAPMODE); + } #endif AUDIT_ARG_FD(fd); @@ -468,8 +472,12 @@ kern_connectat(struct thread *td, int dirfd, int fd, struct sockaddr *sa) int error; #ifdef CAPABILITY_MODE - if (IN_CAPABILITY_MODE(td) && (dirfd == AT_FDCWD)) - return (ECAPMODE); + if (dirfd == AT_FDCWD) { + if (CAP_TRACING(td)) + ktrcapfail(CAPFAIL_NAMEI, "AT_FDCWD"); + if (IN_CAPABILITY_MODE(td)) + return (ECAPMODE); + } #endif AUDIT_ARG_FD(fd); diff --git a/sys/kern/vfs_cache.c b/sys/kern/vfs_cache.c index f8626be7bda7..375b682e1f3a 100644 --- a/sys/kern/vfs_cache.c +++ b/sys/kern/vfs_cache.c @@ -4483,7 +4483,7 @@ cache_can_fplookup(struct cache_fpl *fpl) cache_fpl_aborted_early(fpl); return (false); } - if (IN_CAPABILITY_MODE(td)) { + if (IN_CAPABILITY_MODE(td) || CAP_TRACING(td)) { cache_fpl_aborted_early(fpl); return (false); } diff --git a/sys/kern/vfs_lookup.c b/sys/kern/vfs_lookup.c index 4104400d77bb..7d708bac607e 100644 --- a/sys/kern/vfs_lookup.c +++ b/sys/kern/vfs_lookup.c @@ -236,14 +236,17 @@ nameicap_check_dotdot(struct nameidata *ndp, struct vnode *dp) struct mount *mp; if (dp == NULL || dp->v_type != VDIR || (ndp->ni_lcf & - NI_LCF_STRICTRELATIVE) == 0) + NI_LCF_STRICTREL) == 0) return (0); + if (__predict_false((ndp->ni_lcf & (NI_LCF_STRICTREL_KTR | + NI_LCF_CAP_DOTDOT_KTR)) == NI_LCF_STRICTREL_KTR)) + NI_CAP_VIOLATION(ndp, ndp->ni_cnd.cn_pnbuf); if ((ndp->ni_lcf & NI_LCF_CAP_DOTDOT) == 0) return (ENOTCAPABLE); mp = dp->v_mount; if (lookup_cap_dotdot_nonlocal == 0 && mp != NULL && (mp->mnt_flag & MNT_LOCAL) == 0) - return (ENOTCAPABLE); + goto capfail; TAILQ_FOREACH_REVERSE(nt, &ndp->ni_cap_tracker, nameicap_tracker_head, nm_link) { if (dp == nt->dp) { @@ -253,6 +256,10 @@ nameicap_check_dotdot(struct nameidata *ndp, struct vnode *dp) return (0); } } + +capfail: + if (__predict_false((ndp->ni_lcf & NI_LCF_STRICTREL_KTR) != 0)) + NI_CAP_VIOLATION(ndp, ndp->ni_cnd.cn_pnbuf); return (ENOTCAPABLE); } @@ -271,12 +278,12 @@ namei_handle_root(struct nameidata *ndp, struct vnode **dpp) struct componentname *cnp; cnp = &ndp->ni_cnd; - if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0) { -#ifdef KTRACE - if (KTRPOINT(curthread, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); -#endif - return (ENOTCAPABLE); + if (__predict_false((ndp->ni_lcf & (NI_LCF_STRICTREL | + NI_LCF_STRICTREL_KTR)) != 0)) { + if ((ndp->ni_lcf & NI_LCF_STRICTREL_KTR) != 0) + NI_CAP_VIOLATION(ndp, cnp->cn_pnbuf); + if ((ndp->ni_lcf & NI_LCF_STRICTREL) != 0) + return (ENOTCAPABLE); } while (*(cnp->cn_nameptr) == '/') { cnp->cn_nameptr++; @@ -317,15 +324,17 @@ namei_setup(struct nameidata *ndp, struct vnode **dpp, struct pwd **pwdp) * previously walked by us, which prevents an escape from * the relative root. */ - if (IN_CAPABILITY_MODE(td) && (cnp->cn_flags & NOCAPCHECK) == 0) { - ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; - ndp->ni_resflags |= NIRES_STRICTREL; - if (ndp->ni_dirfd == AT_FDCWD) { -#ifdef KTRACE - if (KTRPOINT(td, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); -#endif - return (ECAPMODE); + if ((cnp->cn_flags & NOCAPCHECK) == 0) { + if (CAP_TRACING(td)) { + ndp->ni_lcf |= NI_LCF_STRICTREL_KTR; + if (ndp->ni_dirfd == AT_FDCWD) + NI_CAP_VIOLATION(ndp, "AT_FDCWD"); + } + if (IN_CAPABILITY_MODE(td)) { + ndp->ni_lcf |= NI_LCF_STRICTREL; + ndp->ni_resflags |= NIRES_STRICTREL; + if (ndp->ni_dirfd == AT_FDCWD) + return (ECAPMODE); } } #endif @@ -368,8 +377,8 @@ namei_setup(struct nameidata *ndp, struct vnode **dpp, struct pwd **pwdp) if (error == 0 && (cnp->cn_flags & RBENEATH) != 0) { if (cnp->cn_pnbuf[0] == '/') { error = ENOTCAPABLE; - } else if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0) { - ndp->ni_lcf |= NI_LCF_STRICTRELATIVE | + } else if ((ndp->ni_lcf & NI_LCF_STRICTREL) == 0) { + ndp->ni_lcf |= NI_LCF_STRICTREL | NI_LCF_CAP_DOTDOT; } } @@ -391,9 +400,12 @@ namei_setup(struct nameidata *ndp, struct vnode **dpp, struct pwd **pwdp) pwd_drop(pwd); return (error); } - if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) != 0 && - lookup_cap_dotdot != 0) - ndp->ni_lcf |= NI_LCF_CAP_DOTDOT; + if (lookup_cap_dotdot != 0) { + if ((ndp->ni_lcf & NI_LCF_STRICTREL_KTR) != 0) + ndp->ni_lcf |= NI_LCF_CAP_DOTDOT_KTR; + if ((ndp->ni_lcf & NI_LCF_STRICTREL) != 0) + ndp->ni_lcf |= NI_LCF_CAP_DOTDOT; + } SDT_PROBE4(vfs, namei, lookup, entry, *dpp, cnp->cn_pnbuf, cnp->cn_flags, false); *pwdp = pwd; @@ -1168,12 +1180,11 @@ vfs_lookup(struct nameidata *ndp) * result of dotdot lookup. */ if (cnp->cn_flags & ISDOTDOT) { - if ((ndp->ni_lcf & (NI_LCF_STRICTRELATIVE | NI_LCF_CAP_DOTDOT)) - == NI_LCF_STRICTRELATIVE) { -#ifdef KTRACE - if (KTRPOINT(curthread, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); -#endif + if (__predict_false((ndp->ni_lcf & (NI_LCF_STRICTREL_KTR | + NI_LCF_CAP_DOTDOT_KTR)) == NI_LCF_STRICTREL_KTR)) + NI_CAP_VIOLATION(ndp, cnp->cn_pnbuf); + if (__predict_false((ndp->ni_lcf & (NI_LCF_STRICTREL | + NI_LCF_CAP_DOTDOT)) == NI_LCF_STRICTREL)) { error = ENOTCAPABLE; goto bad; } @@ -1190,10 +1201,14 @@ vfs_lookup(struct nameidata *ndp) bool isroot = dp == ndp->ni_rootdir || dp == ndp->ni_topdir || dp == rootvnode || pr != NULL; - if (isroot && (ndp->ni_lcf & - NI_LCF_STRICTRELATIVE) != 0) { - error = ENOTCAPABLE; - goto capdotdot; + if (__predict_false(isroot && (ndp->ni_lcf & + (NI_LCF_STRICTREL | NI_LCF_STRICTREL_KTR)) != 0)) { + if ((ndp->ni_lcf & NI_LCF_STRICTREL_KTR) != 0) + NI_CAP_VIOLATION(ndp, cnp->cn_pnbuf); + if ((ndp->ni_lcf & NI_LCF_STRICTREL) != 0) { + error = ENOTCAPABLE; + goto capdotdot; + } } if (isroot || ((dp->v_vflag & VV_ROOT) != 0 && (cnp->cn_flags & NOCROSSMOUNT) != 0)) { @@ -1218,10 +1233,6 @@ vfs_lookup(struct nameidata *ndp) error = nameicap_check_dotdot(ndp, dp); if (error != 0) { capdotdot: -#ifdef KTRACE - if (KTRPOINT(curthread, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); -#endif goto bad; } } @@ -1374,13 +1385,8 @@ vfs_lookup(struct nameidata *ndp) } if (cnp->cn_flags & ISDOTDOT) { error = nameicap_check_dotdot(ndp, ndp->ni_vp); - if (error != 0) { -#ifdef KTRACE - if (KTRPOINT(curthread, KTR_CAPFAIL)) - ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); -#endif + if (error != 0) goto bad2; - } } if (*ndp->ni_next == '/') { cnp->cn_nameptr = ndp->ni_next; diff --git a/sys/sys/namei.h b/sys/sys/namei.h index 1b84bc1df488..cb69fc96b112 100644 --- a/sys/sys/namei.h +++ b/sys/sys/namei.h @@ -196,8 +196,17 @@ int cache_fplookup(struct nameidata *ndp, enum cache_fpl_status *status, /* * Flags in ni_lcf, valid for the duration of the namei call. */ -#define NI_LCF_STRICTRELATIVE 0x0001 /* relative lookup only */ +#define NI_LCF_STRICTREL 0x0001 /* relative lookup only */ #define NI_LCF_CAP_DOTDOT 0x0002 /* ".." in strictrelative case */ +/* Track capability restrictions seperately for violation ktracing. */ +#define NI_LCF_STRICTREL_KTR 0x0004 /* trace relative lookups */ +#define NI_LCF_CAP_DOTDOT_KTR 0x0008 /* ".." in strictrelative case */ +#define NI_LCF_KTR_FLAGS (NI_LCF_STRICTREL_KTR | NI_LCF_CAP_DOTDOT_KTR) + +#define NI_CAP_VIOLATION(ndp, path) do { \ + ktrcapfail(CAPFAIL_NAMEI, (path)); \ + (ndp)->ni_lcf &= ~NI_LCF_KTR_FLAGS; \ +} while (0) /* * Initialization of a nameidata structure.