Changeset View
Changeset View
Standalone View
Standalone View
sys/kern/vfs_lookup.c
Show First 20 Lines • Show All 172 Lines • ▼ Show 20 Lines | SYSCTL_INT(_vfs, OID_AUTO, lookup_cap_dotdot_nonlocal, CTLFLAG_RWTUN, | ||||
&lookup_cap_dotdot_nonlocal, 0, | &lookup_cap_dotdot_nonlocal, 0, | ||||
"enables \"..\" components in path lookup in capability mode " | "enables \"..\" components in path lookup in capability mode " | ||||
"on non-local mount"); | "on non-local mount"); | ||||
static void | static void | ||||
nameicap_tracker_add(struct nameidata *ndp, struct vnode *dp) | nameicap_tracker_add(struct nameidata *ndp, struct vnode *dp) | ||||
{ | { | ||||
struct nameicap_tracker *nt; | struct nameicap_tracker *nt; | ||||
struct componentname *cnp; | |||||
if ((ndp->ni_lcf & NI_LCF_CAP_DOTDOT) == 0 || dp->v_type != VDIR) | if ((ndp->ni_lcf & NI_LCF_CAP_DOTDOT) == 0 || dp->v_type != VDIR) | ||||
return; | return; | ||||
if ((ndp->ni_lcf & (NI_LCF_BENEATH_ABS | NI_LCF_BENEATH_LATCHED)) == | cnp = &ndp->ni_cnd; | ||||
NI_LCF_BENEATH_ABS) { | if ((cnp->cn_flags & BENEATH) != 0 && | ||||
(ndp->ni_lcf & NI_LCF_BENEATH_LATCHED) == 0) { | |||||
MPASS((ndp->ni_lcf & NI_LCF_LATCH) != 0); | MPASS((ndp->ni_lcf & NI_LCF_LATCH) != 0); | ||||
if (dp != ndp->ni_beneath_latch) | if (dp != ndp->ni_beneath_latch) | ||||
return; | return; | ||||
ndp->ni_lcf |= NI_LCF_BENEATH_LATCHED; | ndp->ni_lcf |= NI_LCF_BENEATH_LATCHED; | ||||
} | } | ||||
nt = uma_zalloc(nt_zone, M_WAITOK); | nt = uma_zalloc(nt_zone, M_WAITOK); | ||||
vhold(dp); | vhold(dp); | ||||
nt->dp = dp; | nt->dp = dp; | ||||
Show All 16 Lines | if (clean_latch && (ndp->ni_lcf & NI_LCF_LATCH) != 0) { | ||||
ndp->ni_lcf &= ~NI_LCF_LATCH; | ndp->ni_lcf &= ~NI_LCF_LATCH; | ||||
vrele(ndp->ni_beneath_latch); | vrele(ndp->ni_beneath_latch); | ||||
} | } | ||||
} | } | ||||
/* | /* | ||||
* For dotdot lookups in capability mode, only allow the component | * For dotdot lookups in capability mode, only allow the component | ||||
* lookup to succeed if the resulting directory was already traversed | * lookup to succeed if the resulting directory was already traversed | ||||
* during the operation. Also fail dotdot lookups for non-local | * during the operation. This catches situations where already | ||||
* traversed directory is moved to different parent, and then we walk | |||||
* over it with dotdots. | |||||
* | |||||
* Also allow to force failure of dotdot lookups for non-local | |||||
* filesystems, where external agents might assist local lookups to | * filesystems, where external agents might assist local lookups to | ||||
* escape the compartment. | * escape the compartment. | ||||
*/ | */ | ||||
markj: s/walks/walk/ | |||||
static int | static int | ||||
nameicap_check_dotdot(struct nameidata *ndp, struct vnode *dp) | nameicap_check_dotdot(struct nameidata *ndp, struct vnode *dp) | ||||
{ | { | ||||
struct nameicap_tracker *nt; | struct nameicap_tracker *nt; | ||||
Done Inline ActionsThis is not true by default (lookup_cap_dotdot_nonlocal defaults to 1). markj: This is not true by default (lookup_cap_dotdot_nonlocal defaults to 1). | |||||
struct mount *mp; | struct mount *mp; | ||||
if ((ndp->ni_lcf & NI_LCF_CAP_DOTDOT) == 0 || dp == NULL || | if ((ndp->ni_lcf & NI_LCF_CAP_DOTDOT) == 0 || dp == NULL || | ||||
dp->v_type != VDIR) | dp->v_type != VDIR) | ||||
return (0); | return (0); | ||||
mp = dp->v_mount; | mp = dp->v_mount; | ||||
if (lookup_cap_dotdot_nonlocal == 0 && mp != NULL && | if (lookup_cap_dotdot_nonlocal == 0 && mp != NULL && | ||||
(mp->mnt_flag & MNT_LOCAL) == 0) | (mp->mnt_flag & MNT_LOCAL) == 0) | ||||
return (ENOTCAPABLE); | return (ENOTCAPABLE); | ||||
TAILQ_FOREACH_REVERSE(nt, &ndp->ni_cap_tracker, nameicap_tracker_head, | TAILQ_FOREACH_REVERSE(nt, &ndp->ni_cap_tracker, nameicap_tracker_head, | ||||
nm_link) { | nm_link) { | ||||
if (dp == nt->dp) | if ((ndp->ni_lcf & NI_LCF_LATCH) != 0 && | ||||
return (0); | ndp->ni_beneath_latch == nt->dp) { | ||||
} | |||||
if ((ndp->ni_lcf & NI_LCF_BENEATH_ABS) != 0) { | |||||
ndp->ni_lcf &= ~NI_LCF_BENEATH_LATCHED; | ndp->ni_lcf &= ~NI_LCF_BENEATH_LATCHED; | ||||
nameicap_cleanup(ndp, false); | nameicap_cleanup(ndp, false); | ||||
return (0); | return (0); | ||||
} | } | ||||
if (dp == nt->dp) | |||||
return (0); | |||||
} | |||||
return (ENOTCAPABLE); | return (ENOTCAPABLE); | ||||
} | } | ||||
static void | static void | ||||
namei_cleanup_cnp(struct componentname *cnp) | namei_cleanup_cnp(struct componentname *cnp) | ||||
{ | { | ||||
uma_zfree(namei_zone, cnp->cn_pnbuf); | uma_zfree(namei_zone, cnp->cn_pnbuf); | ||||
▲ Show 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | #ifdef CAPABILITY_MODE | ||||
* '..' components. | * '..' components. | ||||
* - If lookup_cap_dotdot is enabled, we verify that all '..' | * - If lookup_cap_dotdot is enabled, we verify that all '..' | ||||
* components lookups result in the directories which were | * components lookups result in the directories which were | ||||
* previously walked by us, which prevents an escape from | * previously walked by us, which prevents an escape from | ||||
* the relative root. | * the relative root. | ||||
*/ | */ | ||||
if (IN_CAPABILITY_MODE(td) && (cnp->cn_flags & NOCAPCHECK) == 0) { | if (IN_CAPABILITY_MODE(td) && (cnp->cn_flags & NOCAPCHECK) == 0) { | ||||
ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ||||
ndp->ni_resflags |= NIRES_STRICTREL; | |||||
if (ndp->ni_dirfd == AT_FDCWD) { | if (ndp->ni_dirfd == AT_FDCWD) { | ||||
#ifdef KTRACE | #ifdef KTRACE | ||||
if (KTRPOINT(td, KTR_CAPFAIL)) | if (KTRPOINT(td, KTR_CAPFAIL)) | ||||
ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); | ktrcapfail(CAPFAIL_LOOKUP, NULL, NULL); | ||||
#endif | #endif | ||||
return (ECAPMODE); | return (ECAPMODE); | ||||
} | } | ||||
} | } | ||||
▲ Show 20 Lines • Show All 62 Lines • ▼ Show 20 Lines | #ifdef CAPABILITIES | ||||
* strictly relative. | * strictly relative. | ||||
*/ | */ | ||||
CAP_ALL(&rights); | CAP_ALL(&rights); | ||||
if (!cap_rights_contains(&ndp->ni_filecaps.fc_rights, | if (!cap_rights_contains(&ndp->ni_filecaps.fc_rights, | ||||
&rights) || | &rights) || | ||||
ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || | ndp->ni_filecaps.fc_fcntls != CAP_FCNTL_ALL || | ||||
ndp->ni_filecaps.fc_nioctls != -1) { | ndp->ni_filecaps.fc_nioctls != -1) { | ||||
ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ndp->ni_lcf |= NI_LCF_STRICTRELATIVE; | ||||
ndp->ni_resflags |= NIRES_STRICTREL; | |||||
} | } | ||||
#endif | #endif | ||||
} | } | ||||
if (error == 0 && (*dpp)->v_type != VDIR) | if (error == 0 && (*dpp)->v_type != VDIR) | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
} | } | ||||
if (error == 0 && (cnp->cn_flags & BENEATH) != 0) { | if (error == 0 && (cnp->cn_flags & BENEATH) != 0) { | ||||
if (ndp->ni_dirfd == AT_FDCWD) { | if (ndp->ni_dirfd == AT_FDCWD) { | ||||
ndp->ni_beneath_latch = pwd->pwd_cdir; | ndp->ni_beneath_latch = pwd->pwd_cdir; | ||||
vrefact(ndp->ni_beneath_latch); | vrefact(ndp->ni_beneath_latch); | ||||
} else { | } else { | ||||
rights = *ndp->ni_rightsneeded; | rights = *ndp->ni_rightsneeded; | ||||
cap_rights_set_one(&rights, CAP_LOOKUP); | cap_rights_set_one(&rights, CAP_LOOKUP); | ||||
error = fgetvp_rights(td, ndp->ni_dirfd, &rights, | error = fgetvp_rights(td, ndp->ni_dirfd, &rights, | ||||
&dirfd_caps, &ndp->ni_beneath_latch); | &dirfd_caps, &ndp->ni_beneath_latch); | ||||
if (error == 0 && (*dpp)->v_type != VDIR) { | if (error == 0 && (*dpp)->v_type != VDIR) { | ||||
vrele(ndp->ni_beneath_latch); | vrele(ndp->ni_beneath_latch); | ||||
error = ENOTDIR; | error = ENOTDIR; | ||||
} | } | ||||
} | } | ||||
if (error == 0) | if (error == 0) | ||||
ndp->ni_lcf |= NI_LCF_LATCH; | ndp->ni_lcf |= NI_LCF_LATCH; | ||||
} | } | ||||
if (error == 0 && (cnp->cn_flags & RBENEATH) != 0) { | |||||
if (cnp->cn_pnbuf[0] == '/' || | |||||
(ndp->ni_lcf & NI_LCF_BENEATH_ABS) != 0) { | |||||
Done Inline ActionsThis check disallows BENEATH | RBENEATH, both for O_ and AT cases. kib: This check disallows BENEATH | RBENEATH, both for O_ and AT cases. | |||||
Done Inline ActionsHmm, doesn't it mean that O_BENEATH cannot be used with capability mode enabled, even if the lookup does not violate Capsicum's stricter rules? Maybe that's acceptable, though I would expect to see ENOTCAPABLE in that case. markj: Hmm, doesn't it mean that O_BENEATH cannot be used with capability mode enabled, even if the… | |||||
Done Inline ActionsIMO it would add to much clutter to the implementation. We can reconsider it later if asked for. kib: IMO it would add to much clutter to the implementation. We can reconsider it later if asked… | |||||
error = EINVAL; | |||||
} else if ((ndp->ni_lcf & NI_LCF_STRICTRELATIVE) == 0) { | |||||
ndp->ni_lcf |= NI_LCF_STRICTRELATIVE | | |||||
NI_LCF_CAP_DOTDOT; | |||||
} | |||||
} | |||||
/* | /* | ||||
* If we are auditing the kernel pathname, save the user pathname. | * If we are auditing the kernel pathname, save the user pathname. | ||||
*/ | */ | ||||
if (cnp->cn_flags & AUDITVNODE1) | if (cnp->cn_flags & AUDITVNODE1) | ||||
AUDIT_ARG_UPATH1_VP(td, ndp->ni_rootdir, *dpp, cnp->cn_pnbuf); | AUDIT_ARG_UPATH1_VP(td, ndp->ni_rootdir, *dpp, cnp->cn_pnbuf); | ||||
if (cnp->cn_flags & AUDITVNODE2) | if (cnp->cn_flags & AUDITVNODE2) | ||||
AUDIT_ARG_UPATH2_VP(td, ndp->ni_rootdir, *dpp, cnp->cn_pnbuf); | AUDIT_ARG_UPATH2_VP(td, ndp->ni_rootdir, *dpp, cnp->cn_pnbuf); | ||||
if (ndp->ni_startdir != NULL && !startdir_used) | if (ndp->ni_startdir != NULL && !startdir_used) | ||||
▲ Show 20 Lines • Show All 151 Lines • ▼ Show 20 Lines | for (;;) { | ||||
/* | /* | ||||
* If not a symbolic link, we're done. | * If not a symbolic link, we're done. | ||||
*/ | */ | ||||
if ((cnp->cn_flags & ISSYMLINK) == 0) { | if ((cnp->cn_flags & ISSYMLINK) == 0) { | ||||
if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) { | if ((cnp->cn_flags & (SAVENAME | SAVESTART)) == 0) { | ||||
namei_cleanup_cnp(cnp); | namei_cleanup_cnp(cnp); | ||||
} else | } else | ||||
cnp->cn_flags |= HASBUF; | cnp->cn_flags |= HASBUF; | ||||
if ((ndp->ni_lcf & (NI_LCF_BENEATH_ABS | | if ((ndp->ni_lcf & (NI_LCF_LATCH | | ||||
NI_LCF_BENEATH_LATCHED)) == NI_LCF_BENEATH_ABS) { | NI_LCF_BENEATH_LATCHED)) == NI_LCF_LATCH) { | ||||
NDFREE(ndp, 0); | NDFREE(ndp, 0); | ||||
error = ENOTCAPABLE; | error = ENOTCAPABLE; | ||||
} | } | ||||
nameicap_cleanup(ndp, true); | nameicap_cleanup(ndp, true); | ||||
SDT_PROBE3(vfs, namei, lookup, return, error, | SDT_PROBE3(vfs, namei, lookup, return, error, | ||||
(error == 0 ? ndp->ni_vp : NULL), false); | (error == 0 ? ndp->ni_vp : NULL), false); | ||||
pwd_drop(pwd); | pwd_drop(pwd); | ||||
if (error == 0) | if (error == 0) | ||||
▲ Show 20 Lines • Show All 790 Lines • ▼ Show 20 Lines | #endif | ||||
if ((cnp->cn_flags & LOCKLEAF) == 0) | if ((cnp->cn_flags & LOCKLEAF) == 0) | ||||
VOP_UNLOCK(dp); | VOP_UNLOCK(dp); | ||||
return (0); | return (0); | ||||
bad: | bad: | ||||
vput(dp); | vput(dp); | ||||
*vpp = NULL; | *vpp = NULL; | ||||
return (error); | return (error); | ||||
} | } | ||||
/* | /* | ||||
Done Inline ActionsI believe style prefers uint64_t to u_int64_t. markj: I believe style prefers uint64_t to u_int64_t. | |||||
* Free data allocated by namei(); see namei(9) for details. | * Free data allocated by namei(); see namei(9) for details. | ||||
*/ | */ | ||||
void | void | ||||
NDFREE_PNBUF(struct nameidata *ndp) | NDFREE_PNBUF(struct nameidata *ndp) | ||||
{ | { | ||||
if ((ndp->ni_cnd.cn_flags & HASBUF) != 0) { | if ((ndp->ni_cnd.cn_flags & HASBUF) != 0) { | ||||
MPASS((ndp->ni_cnd.cn_flags & (SAVENAME | SAVESTART)) != 0); | MPASS((ndp->ni_cnd.cn_flags & (SAVENAME | SAVESTART)) != 0); | ||||
▲ Show 20 Lines • Show All 228 Lines • Show Last 20 Lines |
s/walks/walk/