xref: /openbsd-src/sys/kern/kern_unveil.c (revision 2b46a8cbba384f799321fc554d4d1c95563ea0a2)
1*2b46a8cbSderaadt /*	$OpenBSD: kern_unveil.c,v 1.55 2022/12/05 23:18:37 deraadt Exp $	*/
28b23add8Sbeck 
38b23add8Sbeck /*
41939e486Sbeck  * Copyright (c) 2017-2019 Bob Beck <beck@openbsd.org>
58b23add8Sbeck  *
68b23add8Sbeck  * Permission to use, copy, modify, and distribute this software for any
78b23add8Sbeck  * purpose with or without fee is hereby granted, provided that the above
88b23add8Sbeck  * copyright notice and this permission notice appear in all copies.
98b23add8Sbeck  *
108b23add8Sbeck  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
118b23add8Sbeck  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
128b23add8Sbeck  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
138b23add8Sbeck  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
148b23add8Sbeck  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
158b23add8Sbeck  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
168b23add8Sbeck  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
178b23add8Sbeck  */
188b23add8Sbeck 
198b23add8Sbeck #include <sys/param.h>
208b23add8Sbeck 
21fa19a603Sbluhm #include <sys/acct.h>
228b23add8Sbeck #include <sys/mount.h>
232369e5b2Sbeck #include <sys/filedesc.h>
248b23add8Sbeck #include <sys/proc.h>
258b23add8Sbeck #include <sys/namei.h>
268b23add8Sbeck #include <sys/vnode.h>
278b23add8Sbeck #include <sys/types.h>
288b23add8Sbeck #include <sys/malloc.h>
298b23add8Sbeck #include <sys/tree.h>
30b077271dSderaadt #include <sys/lock.h>
318b23add8Sbeck 
328b23add8Sbeck #include <sys/syscall.h>
338b23add8Sbeck #include <sys/syscallargs.h>
348b23add8Sbeck #include <sys/systm.h>
358b23add8Sbeck 
368b23add8Sbeck #include <sys/pledge.h>
378b23add8Sbeck 
38dd1a4184Santon struct unvname {
39dd1a4184Santon 	char 			*un_name;
40dd1a4184Santon 	size_t 			un_namesize;
41dd1a4184Santon 	u_char			un_flags;
42dd1a4184Santon 	RBT_ENTRY(unvnmae)	un_rbt;
43dd1a4184Santon };
44dd1a4184Santon 
45dd1a4184Santon RBT_HEAD(unvname_rbt, unvname);
46dd1a4184Santon 
47dd1a4184Santon struct unveil {
48dd1a4184Santon 	struct vnode		*uv_vp;
49dd1a4184Santon 	ssize_t			uv_cover;
50dd1a4184Santon 	struct unvname_rbt	uv_names;
51dd1a4184Santon 	struct rwlock		uv_lock;
52dd1a4184Santon 	u_char			uv_flags;
53dd1a4184Santon };
54dd1a4184Santon 
558b23add8Sbeck /* #define DEBUG_UNVEIL */
5662aa2271Ssemarie #ifdef DEBUG_UNVEIL
5762aa2271Ssemarie #define	DPRINTF(x...)	do { printf(x); } while (0)
5862aa2271Ssemarie #else
5962aa2271Ssemarie #define	DPRINTF(x...)
6062aa2271Ssemarie #endif
618b23add8Sbeck 
628b23add8Sbeck #define UNVEIL_MAX_VNODES	128
638b23add8Sbeck #define UNVEIL_MAX_NAMES	128
648b23add8Sbeck 
658b23add8Sbeck static inline int
unvname_compare(const struct unvname * n1,const struct unvname * n2)668b23add8Sbeck unvname_compare(const struct unvname *n1, const struct unvname *n2)
678b23add8Sbeck {
688b23add8Sbeck 	if (n1->un_namesize == n2->un_namesize)
698b23add8Sbeck 		return (memcmp(n1->un_name, n2->un_name, n1->un_namesize));
708b23add8Sbeck 	else
718b23add8Sbeck 		return (n1->un_namesize - n2->un_namesize);
728b23add8Sbeck }
738b23add8Sbeck 
748b23add8Sbeck struct unvname *
unvname_new(const char * name,size_t size,u_char flags)75e1a6e226Sbeck unvname_new(const char *name, size_t size, u_char flags)
768b23add8Sbeck {
778b23add8Sbeck 	struct unvname *ret = malloc(sizeof(struct unvname), M_PROC, M_WAITOK);
788b23add8Sbeck 	ret->un_name = malloc(size, M_PROC, M_WAITOK);
798b23add8Sbeck 	memcpy(ret->un_name, name, size);
808b23add8Sbeck 	ret->un_namesize = size;
818b23add8Sbeck 	ret->un_flags = flags;
828b23add8Sbeck 	return ret;
838b23add8Sbeck }
848b23add8Sbeck 
858b23add8Sbeck void
unvname_delete(struct unvname * name)868b23add8Sbeck unvname_delete(struct unvname *name)
878b23add8Sbeck {
88ab8e3451Sderaadt 	free(name->un_name, M_PROC, name->un_namesize);
898b23add8Sbeck 	free(name, M_PROC, sizeof(struct unvname));
908b23add8Sbeck }
918b23add8Sbeck 
928b23add8Sbeck RBT_PROTOTYPE(unvname_rbt, unvname, un_rbt, unvname_compare);
938b23add8Sbeck RBT_GENERATE(unvname_rbt, unvname, un_rbt, unvname_compare);
948b23add8Sbeck 
958b23add8Sbeck int
unveil_delete_names(struct unveil * uv)968b23add8Sbeck unveil_delete_names(struct unveil *uv)
978b23add8Sbeck {
988b23add8Sbeck 	struct unvname *unvn, *next;
998b23add8Sbeck 	int ret = 0;
1008b23add8Sbeck 
1018b23add8Sbeck 	rw_enter_write(&uv->uv_lock);
1028b23add8Sbeck 	RBT_FOREACH_SAFE(unvn, unvname_rbt, &uv->uv_names, next) {
1038b23add8Sbeck 		RBT_REMOVE(unvname_rbt, &uv->uv_names, unvn);
1048b23add8Sbeck 		unvname_delete(unvn);
1058b23add8Sbeck 		ret++;
1068b23add8Sbeck 	}
1078b23add8Sbeck 	rw_exit_write(&uv->uv_lock);
10862aa2271Ssemarie 
10962aa2271Ssemarie 	DPRINTF("deleted %d names\n", ret);
1108b23add8Sbeck 	return ret;
1118b23add8Sbeck }
1128b23add8Sbeck 
113a239dbafSanton int
unveil_add_name_unlocked(struct unveil * uv,char * name,u_char flags)1147d77064cSguenther unveil_add_name_unlocked(struct unveil *uv, char *name, u_char flags)
1158b23add8Sbeck {
1168b23add8Sbeck 	struct unvname *unvn;
1178b23add8Sbeck 
1188b23add8Sbeck 	unvn = unvname_new(name, strlen(name) + 1, flags);
119a239dbafSanton 	if (RBT_INSERT(unvname_rbt, &uv->uv_names, unvn) != NULL) {
120a239dbafSanton 		/* Name already present. */
121a239dbafSanton 		unvname_delete(unvn);
122a239dbafSanton 		return 0;
123a239dbafSanton 	}
12462aa2271Ssemarie 
12562aa2271Ssemarie 	DPRINTF("added name %s underneath vnode %p\n", name, uv->uv_vp);
126a239dbafSanton 	return 1;
1278b23add8Sbeck }
1288b23add8Sbeck 
129a239dbafSanton int
unveil_add_name(struct unveil * uv,char * name,u_char flags)1307d77064cSguenther unveil_add_name(struct unveil *uv, char *name, u_char flags)
1317d77064cSguenther {
132a239dbafSanton 	int ret;
133a239dbafSanton 
1347d77064cSguenther 	rw_enter_write(&uv->uv_lock);
135a239dbafSanton 	ret = unveil_add_name_unlocked(uv, name, flags);
1367d77064cSguenther 	rw_exit_write(&uv->uv_lock);
137a239dbafSanton 	return ret;
1387d77064cSguenther }
1397d77064cSguenther 
1408b23add8Sbeck struct unvname *
unveil_namelookup(struct unveil * uv,char * name)1418b23add8Sbeck unveil_namelookup(struct unveil *uv, char *name)
1428b23add8Sbeck {
1438b23add8Sbeck 	struct unvname n, *ret = NULL;
1448b23add8Sbeck 
1458b23add8Sbeck 	rw_enter_read(&uv->uv_lock);
1468b23add8Sbeck 
14762aa2271Ssemarie 	DPRINTF("%s: looking up name %s (%p) in vnode %p\n",
14862aa2271Ssemarie 	    __func__, name, name, uv->uv_vp);
1498b23add8Sbeck 
1508b23add8Sbeck 	KASSERT(uv->uv_vp != NULL);
1518b23add8Sbeck 
1528b23add8Sbeck 	n.un_name = name;
1538b23add8Sbeck 	n.un_namesize = strlen(name) + 1;
1548b23add8Sbeck 
1558b23add8Sbeck 	ret = RBT_FIND(unvname_rbt, &uv->uv_names, &n);
1568b23add8Sbeck 
1578b23add8Sbeck 	rw_exit_read(&uv->uv_lock);
1588b23add8Sbeck 
15962aa2271Ssemarie 	DPRINTF("%s: %s name %s in vnode %p\n", __func__,
16062aa2271Ssemarie 	    (ret == NULL) ? "no match for" : "matched",
1618b23add8Sbeck 	    name, uv->uv_vp);
1628b23add8Sbeck 	return ret;
1638b23add8Sbeck }
1648b23add8Sbeck 
1658b23add8Sbeck void
unveil_destroy(struct process * ps)1668b23add8Sbeck unveil_destroy(struct process *ps)
1678b23add8Sbeck {
1688b23add8Sbeck 	size_t i;
1698b23add8Sbeck 
1708b23add8Sbeck 	for (i = 0; ps->ps_uvpaths != NULL && i < ps->ps_uvvcount; i++) {
1718b23add8Sbeck 		struct unveil *uv = ps->ps_uvpaths + i;
1728b23add8Sbeck 
1738b23add8Sbeck 		struct vnode *vp = uv->uv_vp;
1748b23add8Sbeck 		/* skip any vnodes zapped by unveil_removevnode */
1758b23add8Sbeck 		if (vp != NULL) {
1768b23add8Sbeck 			vp->v_uvcount--;
17762aa2271Ssemarie 
17862aa2271Ssemarie 			DPRINTF("unveil: %s(%d): removing vnode %p uvcount %d "
1798b23add8Sbeck 			    "in position %ld\n",
1808b23add8Sbeck 			    ps->ps_comm, ps->ps_pid, vp, vp->v_uvcount, i);
1818b23add8Sbeck 			vrele(vp);
1828b23add8Sbeck 		}
1838b23add8Sbeck 		ps->ps_uvncount -= unveil_delete_names(uv);
1848b23add8Sbeck 		uv->uv_vp = NULL;
1858b23add8Sbeck 		uv->uv_flags = 0;
1868b23add8Sbeck 	}
1878b23add8Sbeck 
1888b23add8Sbeck 	KASSERT(ps->ps_uvncount == 0);
1898b23add8Sbeck 	free(ps->ps_uvpaths, M_PROC, UNVEIL_MAX_VNODES *
1908b23add8Sbeck 	    sizeof(struct unveil));
1918b23add8Sbeck 	ps->ps_uvvcount = 0;
1928b23add8Sbeck 	ps->ps_uvpaths = NULL;
1938b23add8Sbeck }
1948b23add8Sbeck 
195fe520198Sbeck void
unveil_copy(struct process * parent,struct process * child)196fe520198Sbeck unveil_copy(struct process *parent, struct process *child)
1978b23add8Sbeck {
1988b23add8Sbeck 	size_t i;
1998b23add8Sbeck 
2009b678605Sclaudio 	child->ps_uvdone = parent->ps_uvdone;
201fe520198Sbeck 	if (parent->ps_uvvcount == 0)
202fe520198Sbeck 		return;
203fe520198Sbeck 
204585157b0Sbeck 	child->ps_uvpaths = mallocarray(UNVEIL_MAX_VNODES,
205585157b0Sbeck 	    sizeof(struct unveil), M_PROC, M_WAITOK|M_ZERO);
2068b23add8Sbeck 
207fe520198Sbeck 	child->ps_uvncount = 0;
208fe520198Sbeck 	for (i = 0; parent->ps_uvpaths != NULL && i < parent->ps_uvvcount;
209fe520198Sbeck 	     i++) {
210fe520198Sbeck 		struct unveil *from = parent->ps_uvpaths + i;
211fe520198Sbeck 		struct unveil *to = child->ps_uvpaths + i;
2128b23add8Sbeck 		struct unvname *unvn, *next;
2138b23add8Sbeck 
214fe520198Sbeck 		to->uv_vp = from->uv_vp;
215fe520198Sbeck 		if (to->uv_vp != NULL) {
216fe520198Sbeck 			vref(to->uv_vp);
217fe520198Sbeck 			to->uv_vp->v_uvcount++;
2188b23add8Sbeck 		}
219fe520198Sbeck 		rw_init(&to->uv_lock, "unveil");
220fe520198Sbeck 		RBT_INIT(unvname_rbt, &to->uv_names);
221fe520198Sbeck 		rw_enter_read(&from->uv_lock);
222fe520198Sbeck 		RBT_FOREACH_SAFE(unvn, unvname_rbt, &from->uv_names, next) {
223a239dbafSanton 			if (unveil_add_name_unlocked(&child->ps_uvpaths[i],
224a239dbafSanton 				    unvn->un_name, unvn->un_flags))
225fe520198Sbeck 				child->ps_uvncount++;
2268b23add8Sbeck 		}
227fe520198Sbeck 		rw_exit_read(&from->uv_lock);
228fe520198Sbeck 		to->uv_flags = from->uv_flags;
229585157b0Sbeck 		to->uv_cover = from->uv_cover;
2308b23add8Sbeck 	}
231fe520198Sbeck 	child->ps_uvvcount = parent->ps_uvvcount;
2328b23add8Sbeck }
2338b23add8Sbeck 
234585157b0Sbeck /*
235585157b0Sbeck  * Walk up from vnode dp, until we find a matching unveil, or the root vnode
23610478431Sclaudio  * returns -1 if no unveil to be found above dp or if dp is the root vnode.
237585157b0Sbeck  */
238585157b0Sbeck ssize_t
unveil_find_cover(struct vnode * dp,struct proc * p)2392369e5b2Sbeck unveil_find_cover(struct vnode *dp, struct proc *p)
240585157b0Sbeck {
2412369e5b2Sbeck 	struct vnode *vp = NULL, *parent = NULL, *root;
242585157b0Sbeck 	ssize_t ret = -1;
243585157b0Sbeck 	int error;
244585157b0Sbeck 
2452369e5b2Sbeck 	/* use the correct root to stop at, chrooted or not.. */
2462369e5b2Sbeck 	root = p->p_fd->fd_rdir ? p->p_fd->fd_rdir : rootvnode;
247585157b0Sbeck 	vp = dp;
248585157b0Sbeck 
24910478431Sclaudio 	while (vp != root) {
250585157b0Sbeck 		struct componentname cn = {
251585157b0Sbeck 			.cn_nameiop = LOOKUP,
252585157b0Sbeck 			.cn_flags = ISLASTCN | ISDOTDOT | RDONLY,
253585157b0Sbeck 			.cn_proc = p,
254585157b0Sbeck 			.cn_cred = p->p_ucred,
255585157b0Sbeck 			.cn_pnbuf = NULL,
256585157b0Sbeck 			.cn_nameptr = "..",
257585157b0Sbeck 			.cn_namelen = 2,
258585157b0Sbeck 			.cn_consume = 0
259585157b0Sbeck 		};
2602369e5b2Sbeck 
2612369e5b2Sbeck 		/*
2621939e486Sbeck 		 * If we are at the root of a filesystem, and we are
2631939e486Sbeck 		 * still mounted somewhere, take the .. in the above
2641939e486Sbeck 		 * filesystem.
2652369e5b2Sbeck 		 */
2661939e486Sbeck 		if (vp != root && (vp->v_flag & VROOT)) {
2671939e486Sbeck 			if (vp->v_mount == NULL)
2681939e486Sbeck 				return -1;
2691939e486Sbeck 			vp = vp->v_mount->mnt_vnodecovered ?
2702369e5b2Sbeck 			    vp->v_mount->mnt_vnodecovered : vp;
2711939e486Sbeck 		}
2722369e5b2Sbeck 
273585157b0Sbeck 		if (vget(vp, LK_EXCLUSIVE|LK_RETRY) != 0)
274585157b0Sbeck 			return -1;
2752369e5b2Sbeck 		/* Get parent vnode of vp using lookup of '..' */
276585157b0Sbeck 		/* This returns with vp unlocked but ref'ed*/
277585157b0Sbeck 		error = VOP_LOOKUP(vp, &parent, &cn);
278585157b0Sbeck 		if (error) {
279585157b0Sbeck 			if (!(cn.cn_flags & PDIRUNLOCK))
280585157b0Sbeck 				vput(vp);
281585157b0Sbeck 			else {
282585157b0Sbeck 				/*
283585157b0Sbeck 				 * This corner case should not happen because
284585157b0Sbeck 				 * we have not set LOCKPARENT in the flags
285585157b0Sbeck 				 */
28662aa2271Ssemarie 				DPRINTF("vnode %p PDIRUNLOCK on error\n", vp);
287585157b0Sbeck 				vrele(vp);
288585157b0Sbeck 			}
289585157b0Sbeck 			break;
290585157b0Sbeck 		}
291585157b0Sbeck 
292585157b0Sbeck 		vrele(vp);
2937ac2b76bSguenther 		(void) unveil_lookup(parent, p->p_p, &ret);
294585157b0Sbeck 		vput(parent);
295585157b0Sbeck 
2962369e5b2Sbeck 		if (ret >= 0)
2972369e5b2Sbeck 			break;
2982369e5b2Sbeck 
299585157b0Sbeck 		if (vp == parent) {
300585157b0Sbeck 			ret = -1;
301585157b0Sbeck 			break;
302585157b0Sbeck 		}
303585157b0Sbeck 		vp = parent;
304585157b0Sbeck 		parent = NULL;
30510478431Sclaudio 	}
306585157b0Sbeck 	return ret;
307585157b0Sbeck }
308585157b0Sbeck 
309585157b0Sbeck 
3108b23add8Sbeck struct unveil *
unveil_lookup(struct vnode * vp,struct process * pr,ssize_t * position)3117ac2b76bSguenther unveil_lookup(struct vnode *vp, struct process *pr, ssize_t *position)
3128b23add8Sbeck {
3138b23add8Sbeck 	struct unveil *uv = pr->ps_uvpaths;
314f5e54ad9Sclaudio 	ssize_t i;
315f5e54ad9Sclaudio 
316585157b0Sbeck 	if (position != NULL)
317585157b0Sbeck 		*position = -1;
3188b23add8Sbeck 
3198b23add8Sbeck 	if (vp->v_uvcount == 0)
3208b23add8Sbeck 		return NULL;
3218b23add8Sbeck 
322f5e54ad9Sclaudio 	for (i = 0; i < pr->ps_uvvcount; i++) {
323f5e54ad9Sclaudio 		if (vp == uv[i].uv_vp) {
324f5e54ad9Sclaudio 			KASSERT(uv[i].uv_vp->v_uvcount > 0);
325f5e54ad9Sclaudio 			KASSERT(uv[i].uv_vp->v_usecount > 0);
326585157b0Sbeck 			if (position != NULL)
327f5e54ad9Sclaudio 				*position = i;
328f5e54ad9Sclaudio 			return &uv[i];
3298b23add8Sbeck 		}
3308b23add8Sbeck 	}
3318b23add8Sbeck 	return NULL;
3328b23add8Sbeck }
3338b23add8Sbeck 
3348b23add8Sbeck int
unveil_parsepermissions(const char * permissions,u_char * perms)335e1a6e226Sbeck unveil_parsepermissions(const char *permissions, u_char *perms)
3368b23add8Sbeck {
3378b23add8Sbeck 	size_t i = 0;
3388b23add8Sbeck 	char c;
3398b23add8Sbeck 
3402abaea44Sclaudio 	*perms = UNVEIL_USERSET;
34104b561efSderaadt 	while ((c = permissions[i++]) != '\0') {
3428b23add8Sbeck 		switch (c) {
3438b23add8Sbeck 		case 'r':
344e1a6e226Sbeck 			*perms |= UNVEIL_READ;
3458b23add8Sbeck 			break;
3468b23add8Sbeck 		case 'w':
347e1a6e226Sbeck 			*perms |= UNVEIL_WRITE;
3488b23add8Sbeck 			break;
3498b23add8Sbeck 		case 'x':
350e1a6e226Sbeck 			*perms |= UNVEIL_EXEC;
3518b23add8Sbeck 			break;
3528b23add8Sbeck 		case 'c':
353e1a6e226Sbeck 			*perms |= UNVEIL_CREATE;
3548b23add8Sbeck 			break;
3558b23add8Sbeck 		default:
3568b23add8Sbeck 			return -1;
3578b23add8Sbeck 		}
3588b23add8Sbeck 	}
3598b23add8Sbeck 	return 0;
3608b23add8Sbeck }
3618b23add8Sbeck 
3628b23add8Sbeck int
unveil_setflags(u_char * flags,u_char nflags)363e1a6e226Sbeck unveil_setflags(u_char *flags, u_char nflags)
3648b23add8Sbeck {
3658b23add8Sbeck #if 0
3668b23add8Sbeck 	if (((~(*flags)) & nflags) != 0) {
36762aa2271Ssemarie 		DPRINTF("Flags escalation %llX -> %llX\n", *flags, nflags);
3688b23add8Sbeck 		return 1;
3698b23add8Sbeck 	}
3708b23add8Sbeck #endif
3718b23add8Sbeck 	*flags = nflags;
3728b23add8Sbeck 	return 1;
3738b23add8Sbeck }
3748b23add8Sbeck 
3758b23add8Sbeck struct unveil *
unveil_add_vnode(struct proc * p,struct vnode * vp)3767ac2b76bSguenther unveil_add_vnode(struct proc *p, struct vnode *vp)
3778b23add8Sbeck {
3787ac2b76bSguenther 	struct process *pr = p->p_p;
3798b23add8Sbeck 	struct unveil *uv = NULL;
3801bef2b77Sclaudio 	ssize_t i;
381a4b48aa3Sbeck 
382a4b48aa3Sbeck 	KASSERT(pr->ps_uvvcount < UNVEIL_MAX_VNODES);
383a4b48aa3Sbeck 
3841bef2b77Sclaudio 	uv = &pr->ps_uvpaths[pr->ps_uvvcount++];
3858b23add8Sbeck 	rw_init(&uv->uv_lock, "unveil");
3868b23add8Sbeck 	RBT_INIT(unvname_rbt, &uv->uv_names);
3878b23add8Sbeck 	uv->uv_vp = vp;
3885cab2156Sclaudio 	uv->uv_flags = 0;
389585157b0Sbeck 
390585157b0Sbeck 	/* find out what we are covered by */
3917ac2b76bSguenther 	uv->uv_cover = unveil_find_cover(vp, p);
392585157b0Sbeck 
393585157b0Sbeck 	/*
394585157b0Sbeck 	 * Find anyone covered by what we are covered by
395585157b0Sbeck 	 * and re-check what covers them (we could have
396585157b0Sbeck 	 * interposed a cover)
397585157b0Sbeck 	 */
3981bef2b77Sclaudio 	for (i = 0; i < pr->ps_uvvcount - 1; i++) {
399585157b0Sbeck 		if (pr->ps_uvpaths[i].uv_cover == uv->uv_cover)
4001bef2b77Sclaudio 			pr->ps_uvpaths[i].uv_cover =
4011bef2b77Sclaudio 			    unveil_find_cover(pr->ps_uvpaths[i].uv_vp, p);
402585157b0Sbeck 	}
403585157b0Sbeck 
4048b23add8Sbeck 	return (uv);
4058b23add8Sbeck }
4068b23add8Sbeck 
4078b23add8Sbeck int
unveil_add(struct proc * p,struct nameidata * ndp,const char * permissions)40804b561efSderaadt unveil_add(struct proc *p, struct nameidata *ndp, const char *permissions)
4098b23add8Sbeck {
4108b23add8Sbeck 	struct process *pr = p->p_p;
4118b23add8Sbeck 	struct vnode *vp;
4128b23add8Sbeck 	struct unveil *uv;
4138b23add8Sbeck 	int directory_add;
4148b23add8Sbeck 	int ret = EINVAL;
415e1a6e226Sbeck 	u_char flags;
4168b23add8Sbeck 
4178b23add8Sbeck 	KASSERT(ISSET(ndp->ni_cnd.cn_flags, HASBUF)); /* must have SAVENAME */
4188b23add8Sbeck 
41904b561efSderaadt 	if (unveil_parsepermissions(permissions, &flags) == -1)
4208b23add8Sbeck 		goto done;
4218b23add8Sbeck 
4228b23add8Sbeck 	if (pr->ps_uvpaths == NULL) {
4238b23add8Sbeck 		pr->ps_uvpaths = mallocarray(UNVEIL_MAX_VNODES,
4248b23add8Sbeck 		    sizeof(struct unveil), M_PROC, M_WAITOK|M_ZERO);
4258b23add8Sbeck 	}
4268b23add8Sbeck 
4270fdd9a14Sclaudio 	if (pr->ps_uvvcount >= UNVEIL_MAX_VNODES ||
4288b23add8Sbeck 	    pr->ps_uvncount >= UNVEIL_MAX_NAMES) {
4298b23add8Sbeck 		ret = E2BIG;
4308b23add8Sbeck 		goto done;
4318b23add8Sbeck 	}
4328b23add8Sbeck 
4338b23add8Sbeck 	/* Are we a directory? or something else */
4348b23add8Sbeck 	directory_add = ndp->ni_vp != NULL && ndp->ni_vp->v_type == VDIR;
4358b23add8Sbeck 
4368b23add8Sbeck 	if (directory_add)
4378b23add8Sbeck 		vp = ndp->ni_vp;
4388b23add8Sbeck 	else
4398b23add8Sbeck 		vp = ndp->ni_dvp;
4408b23add8Sbeck 
4418b23add8Sbeck 	KASSERT(vp->v_type == VDIR);
4428b23add8Sbeck 	vref(vp);
4438b23add8Sbeck 	vp->v_uvcount++;
4447ac2b76bSguenther 	if ((uv = unveil_lookup(vp, pr, NULL)) != NULL) {
4458b23add8Sbeck 		/*
4468b23add8Sbeck 		 * We already have unveiled this directory
4478b23add8Sbeck 		 * vnode
4488b23add8Sbeck 		 */
4498b23add8Sbeck 		vp->v_uvcount--;
4508b23add8Sbeck 		vrele(vp);
4518b23add8Sbeck 
4528b23add8Sbeck 		/*
4538b23add8Sbeck 		 * If we are adding a directory which was already
4548b23add8Sbeck 		 * unveiled containing only specific terminals,
4558b23add8Sbeck 		 * unrestrict it.
4568b23add8Sbeck 		 */
4578b23add8Sbeck 		if (directory_add) {
45862aa2271Ssemarie 			DPRINTF("unveil: %s(%d): updating directory vnode %p"
4598b23add8Sbeck 			    " to unrestricted uvcount %d\n",
4608b23add8Sbeck 			    pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
46162aa2271Ssemarie 
4628b23add8Sbeck 			if (!unveil_setflags(&uv->uv_flags, flags))
4638b23add8Sbeck 				ret = EPERM;
4648b23add8Sbeck 			else
4658b23add8Sbeck 				ret = 0;
4668b23add8Sbeck 			goto done;
4678b23add8Sbeck 		}
4688b23add8Sbeck 
4698b23add8Sbeck 		/*
4708b23add8Sbeck 		 * If we are adding a terminal that is already unveiled, just
4718b23add8Sbeck 		 * replace the flags and we are done
4728b23add8Sbeck 		 */
4738b23add8Sbeck 		if (!directory_add) {
4748b23add8Sbeck 			struct unvname *tname;
4758b23add8Sbeck 			if ((tname = unveil_namelookup(uv,
4768b23add8Sbeck 			    ndp->ni_cnd.cn_nameptr)) != NULL) {
47762aa2271Ssemarie 				DPRINTF("unveil: %s(%d): changing flags for %s"
4788b23add8Sbeck 				    "in vnode %p, uvcount %d\n",
4798b23add8Sbeck 				    pr->ps_comm, pr->ps_pid, tname->un_name, vp,
4808b23add8Sbeck 				    vp->v_uvcount);
48162aa2271Ssemarie 
4828b23add8Sbeck 				if (!unveil_setflags(&tname->un_flags, flags))
4838b23add8Sbeck 					ret = EPERM;
4848b23add8Sbeck 				else
4858b23add8Sbeck 					ret = 0;
4868b23add8Sbeck 				goto done;
4878b23add8Sbeck 			}
4888b23add8Sbeck 		}
4898b23add8Sbeck 
4908b23add8Sbeck 	} else {
4918b23add8Sbeck 		/*
4928b23add8Sbeck 		 * New unveil involving this directory vnode.
4938b23add8Sbeck 		 */
4947ac2b76bSguenther 		uv = unveil_add_vnode(p, vp);
4958b23add8Sbeck 	}
4968b23add8Sbeck 
4978b23add8Sbeck 	/*
4988b23add8Sbeck 	 * At this stage with have a unveil in uv with a vnode for a
4998b23add8Sbeck 	 * directory. If the component we are adding is a directory,
5008b23add8Sbeck 	 * we are done. Otherwise, we add the component name the name
5018b23add8Sbeck 	 * list in uv.
5028b23add8Sbeck 	 */
5038b23add8Sbeck 
5048b23add8Sbeck 	if (directory_add) {
5058b23add8Sbeck 		uv->uv_flags = flags;
5068b23add8Sbeck 		ret = 0;
50762aa2271Ssemarie 
50862aa2271Ssemarie 		DPRINTF("unveil: %s(%d): added unrestricted directory vnode %p"
5098b23add8Sbeck 		    ", uvcount %d\n",
5108b23add8Sbeck 		    pr->ps_comm, pr->ps_pid, vp, vp->v_uvcount);
5118b23add8Sbeck 		goto done;
5128b23add8Sbeck 	}
5138b23add8Sbeck 
514a239dbafSanton 	if (unveil_add_name(uv, ndp->ni_cnd.cn_nameptr, flags))
5158b23add8Sbeck 		pr->ps_uvncount++;
5168b23add8Sbeck 	ret = 0;
5178b23add8Sbeck 
51862aa2271Ssemarie 	DPRINTF("unveil: %s(%d): added name %s beneath %s vnode %p,"
5198b23add8Sbeck 	    " uvcount %d\n",
5208b23add8Sbeck 	    pr->ps_comm, pr->ps_pid, ndp->ni_cnd.cn_nameptr,
5218b23add8Sbeck 	    uv->uv_flags ? "unrestricted" : "restricted",
5228b23add8Sbeck 	    vp, vp->v_uvcount);
5238b23add8Sbeck 
5248b23add8Sbeck  done:
5258b23add8Sbeck 	return ret;
5268b23add8Sbeck }
5278b23add8Sbeck 
5288b23add8Sbeck /*
5298b23add8Sbeck  * XXX this will probably change.
530678831beSjsg  * XXX collapse down later once debug surely unneeded
5318b23add8Sbeck  */
5328b23add8Sbeck int
unveil_flagmatch(struct nameidata * ni,u_char flags)533e1a6e226Sbeck unveil_flagmatch(struct nameidata *ni, u_char flags)
5348b23add8Sbeck {
5358b23add8Sbeck 	if (flags == 0) {
53662aa2271Ssemarie 		DPRINTF("All operations forbidden for 0 flags\n");
5378b23add8Sbeck 		return 0;
5388b23add8Sbeck 	}
539e1a6e226Sbeck 	if (ni->ni_unveil & UNVEIL_READ) {
540e1a6e226Sbeck 		if ((flags & UNVEIL_READ) == 0) {
54162aa2271Ssemarie 			DPRINTF("unveil lacks UNVEIL_READ\n");
5428b23add8Sbeck 			return 0;
5438b23add8Sbeck 		}
5448b23add8Sbeck 	}
545e1a6e226Sbeck 	if (ni->ni_unveil & UNVEIL_WRITE) {
546e1a6e226Sbeck 		if ((flags & UNVEIL_WRITE) == 0) {
54762aa2271Ssemarie 			DPRINTF("unveil lacks UNVEIL_WRITE\n");
5488b23add8Sbeck 			return 0;
5498b23add8Sbeck 		}
5508b23add8Sbeck 	}
551e1a6e226Sbeck 	if (ni->ni_unveil & UNVEIL_EXEC) {
552e1a6e226Sbeck 		if ((flags & UNVEIL_EXEC) == 0) {
55362aa2271Ssemarie 			DPRINTF("unveil lacks UNVEIL_EXEC\n");
5548b23add8Sbeck 			return 0;
5558b23add8Sbeck 		}
5568b23add8Sbeck 	}
557e1a6e226Sbeck 	if (ni->ni_unveil & UNVEIL_CREATE) {
558e1a6e226Sbeck 		if ((flags & UNVEIL_CREATE) == 0) {
55962aa2271Ssemarie 			DPRINTF("unveil lacks UNVEIL_CREATE\n");
5608b23add8Sbeck 			return 0;
5618b23add8Sbeck 		}
5628b23add8Sbeck 	}
5638b23add8Sbeck 	return 1;
5648b23add8Sbeck }
5658b23add8Sbeck 
56610478431Sclaudio /*
56710478431Sclaudio  * When traversing up towards the root figure out the proper unveil for
56810478431Sclaudio  * the parent directory.
56910478431Sclaudio  */
570585157b0Sbeck struct unveil *
unveil_covered(struct unveil * uv,struct vnode * dvp,struct proc * p)57110478431Sclaudio unveil_covered(struct unveil *uv, struct vnode *dvp, struct proc *p)
57210478431Sclaudio {
573585157b0Sbeck 	if (uv && uv->uv_vp == dvp) {
57410478431Sclaudio 		/* if at the root, chrooted or not, return the current uv */
57510478431Sclaudio 		if (dvp == (p->p_fd->fd_rdir ? p->p_fd->fd_rdir : rootvnode))
57610478431Sclaudio 			return uv;
577585157b0Sbeck 		if (uv->uv_cover >=0) {
57810478431Sclaudio 			KASSERT(uv->uv_cover < p->p_p->ps_uvvcount);
57910478431Sclaudio 			return &p->p_p->ps_uvpaths[uv->uv_cover];
580585157b0Sbeck 		}
581585157b0Sbeck 		return NULL;
582585157b0Sbeck 	}
583585157b0Sbeck 	return uv;
584585157b0Sbeck }
585585157b0Sbeck 
586585157b0Sbeck 
587585157b0Sbeck /*
58893c275fdSbeck  * Start a relative path lookup. Ensure we find whatever unveil covered
58993c275fdSbeck  * where we start from, either by having a saved current working directory
59093c275fdSbeck  * unveil, or by walking up and finding a cover the hard way if we are
59193c275fdSbeck  * doing a non AT_FDCWD relative lookup. Caller passes a NULL dp
59293c275fdSbeck  * if we are using AT_FDCWD.
593585157b0Sbeck  */
594585157b0Sbeck void
unveil_start_relative(struct proc * p,struct nameidata * ni,struct vnode * dp)59593c275fdSbeck unveil_start_relative(struct proc *p, struct nameidata *ni, struct vnode *dp)
596585157b0Sbeck {
5977ac2b76bSguenther 	struct process *pr = p->p_p;
59893c275fdSbeck 	struct unveil *uv = NULL;
599dd490a11Sclaudio 	ssize_t uvi;
600585157b0Sbeck 
601efacc20bSsemarie 	if (pr->ps_uvpaths == NULL)
602efacc20bSsemarie 		return;
603efacc20bSsemarie 
6047ac2b76bSguenther 	uv = unveil_lookup(dp, pr, NULL);
60593c275fdSbeck 	if (uv == NULL) {
60693c275fdSbeck 		uvi = unveil_find_cover(dp, p);
60793c275fdSbeck 		if (uvi >= 0) {
6087ac2b76bSguenther 			KASSERT(uvi < pr->ps_uvvcount);
6097ac2b76bSguenther 			uv = &pr->ps_uvpaths[uvi];
61093c275fdSbeck 		}
61193c275fdSbeck 	}
61293c275fdSbeck 
61393c275fdSbeck 	/*
614dbaaa455Sclaudio 	 * Store this match for later use. Flags are checked at the end.
615585157b0Sbeck 	 */
616dbaaa455Sclaudio 	if (uv) {
61762aa2271Ssemarie 		DPRINTF("unveil: %s(%d): relative unveil at %p matches",
6187ac2b76bSguenther 		    pr->ps_comm, pr->ps_pid, uv);
61962aa2271Ssemarie 
620585157b0Sbeck 		ni->ni_unveil_match = uv;
621585157b0Sbeck 	}
622585157b0Sbeck }
623585157b0Sbeck 
6248b23add8Sbeck /*
6258b23add8Sbeck  * unveil checking - for component directories in a namei lookup.
6268b23add8Sbeck  */
6278b23add8Sbeck void
unveil_check_component(struct proc * p,struct nameidata * ni,struct vnode * dp)6288b23add8Sbeck unveil_check_component(struct proc *p, struct nameidata *ni, struct vnode *dp)
6298b23add8Sbeck {
6307ac2b76bSguenther 	struct process *pr = p->p_p;
6318b23add8Sbeck 	struct unveil *uv = NULL;
6328b23add8Sbeck 
6330fdd9a14Sclaudio 	if (ni->ni_pledge == PLEDGE_UNVEIL || pr->ps_uvpaths == NULL)
634efacc20bSsemarie 		return;
635efacc20bSsemarie 	if (ni->ni_cnd.cn_flags & BYPASSUNVEIL)
636efacc20bSsemarie 		return;
637efacc20bSsemarie 
638585157b0Sbeck 	if (ni->ni_cnd.cn_flags & ISDOTDOT) {
639585157b0Sbeck 		/*
640585157b0Sbeck 		 * adjust unveil match as necessary
641585157b0Sbeck 		 */
64210478431Sclaudio 		uv = unveil_covered(ni->ni_unveil_match, dp, p);
643efacc20bSsemarie 
6441939e486Sbeck 		/* clear the match when we DOTDOT above it */
645dbaaa455Sclaudio 		if (ni->ni_unveil_match && ni->ni_unveil_match->uv_vp == dp)
6461939e486Sbeck 			ni->ni_unveil_match = NULL;
647efacc20bSsemarie 	} else
6487ac2b76bSguenther 		uv = unveil_lookup(dp, pr, NULL);
649585157b0Sbeck 
650585157b0Sbeck 	if (uv != NULL) {
651dbaaa455Sclaudio 		/* update match */
6528b23add8Sbeck 		ni->ni_unveil_match = uv;
65362aa2271Ssemarie 
65462aa2271Ssemarie 		DPRINTF("unveil: %s(%d): component directory match for "
655dbaaa455Sclaudio 		    "vnode %p\n", pr->ps_comm, pr->ps_pid, dp);
6568b23add8Sbeck 	}
6578b23add8Sbeck }
6588b23add8Sbeck 
6598b23add8Sbeck /*
6608b23add8Sbeck  * unveil checking - only done after namei lookup has succeeded on
661048c2096Sjasper  * the last component of a namei lookup.
6628b23add8Sbeck  */
6638b23add8Sbeck int
unveil_check_final(struct proc * p,struct nameidata * ni)6648b23add8Sbeck unveil_check_final(struct proc *p, struct nameidata *ni)
6658b23add8Sbeck {
6667ac2b76bSguenther 	struct process *pr = p->p_p;
6677ed2ac75Sclaudio 	struct unveil *uv = NULL, *nuv;
6688b23add8Sbeck 	struct unvname *tname = NULL;
6698b23add8Sbeck 
6707ac2b76bSguenther 	if (ni->ni_pledge == PLEDGE_UNVEIL || pr->ps_uvpaths == NULL)
6718b23add8Sbeck 		return (0);
6728b23add8Sbeck 
6738b23add8Sbeck 	if (ni->ni_cnd.cn_flags & BYPASSUNVEIL) {
67462aa2271Ssemarie 		DPRINTF("unveil: %s(%d): BYPASSUNVEIL.\n",
6757ac2b76bSguenther 		    pr->ps_comm, pr->ps_pid);
67662aa2271Ssemarie 
6778b23add8Sbeck 		return (0);
6788b23add8Sbeck 	}
679dbaaa455Sclaudio 
6808b23add8Sbeck 	if (ni->ni_vp != NULL && ni->ni_vp->v_type == VDIR) {
681585157b0Sbeck 		/* We are matching a directory terminal component */
6827ac2b76bSguenther 		uv = unveil_lookup(ni->ni_vp, pr, NULL);
6832abaea44Sclaudio 		if (uv == NULL || (uv->uv_flags & UNVEIL_USERSET) == 0) {
68462aa2271Ssemarie 			DPRINTF("unveil: %s(%d) no match for vnode %p\n",
6857ac2b76bSguenther 			    pr->ps_comm, pr->ps_pid, ni->ni_vp);
68662aa2271Ssemarie 
6872abaea44Sclaudio 			if (uv != NULL)
6882abaea44Sclaudio 				ni->ni_unveil_match = uv;
6898b23add8Sbeck 			goto done;
6908b23add8Sbeck 		}
6918b23add8Sbeck 		if (!unveil_flagmatch(ni, uv->uv_flags)) {
69262aa2271Ssemarie 			DPRINTF("unveil: %s(%d) flag mismatch for directory"
6938b23add8Sbeck 			    " vnode %p\n",
6947ac2b76bSguenther 			    pr->ps_comm, pr->ps_pid, ni->ni_vp);
69562aa2271Ssemarie 
6967ac2b76bSguenther 			pr->ps_acflag |= AUNVEIL;
6972abaea44Sclaudio 			if (uv->uv_flags & UNVEIL_MASK)
6988b23add8Sbeck 				return EACCES;
699f3784bceSbeck 			else
700f3784bceSbeck 				return ENOENT;
701f3784bceSbeck 
7028b23add8Sbeck 		}
703dbaaa455Sclaudio 		/* directory and flags match, success */
70462aa2271Ssemarie 		DPRINTF("unveil: %s(%d): matched directory \"%s\" at vnode %p\n",
705dbaaa455Sclaudio 		    pr->ps_comm, pr->ps_pid, ni->ni_cnd.cn_nameptr,
706dbaaa455Sclaudio 		    uv->uv_vp);
70762aa2271Ssemarie 
708dbaaa455Sclaudio 		return (0);
709585157b0Sbeck 	}
710dbaaa455Sclaudio 
711585157b0Sbeck 	/* Otherwise, we are matching a non-terminal component */
7127ac2b76bSguenther 	uv = unveil_lookup(ni->ni_dvp, pr, NULL);
7138b23add8Sbeck 	if (uv == NULL) {
71462aa2271Ssemarie 		DPRINTF("unveil: %s(%d) no match for directory vnode %p\n",
7157ac2b76bSguenther 		    pr->ps_comm, pr->ps_pid, ni->ni_dvp);
71662aa2271Ssemarie 
7178b23add8Sbeck 		goto done;
7188b23add8Sbeck 	}
719dbaaa455Sclaudio 	if ((tname = unveil_namelookup(uv, ni->ni_cnd.cn_nameptr)) == NULL) {
72062aa2271Ssemarie 		DPRINTF("unveil: %s(%d) no match for terminal '%s' in "
7218b23add8Sbeck 		    "directory vnode %p\n",
7227ac2b76bSguenther 		    pr->ps_comm, pr->ps_pid,
7238b23add8Sbeck 		    ni->ni_cnd.cn_nameptr, ni->ni_dvp);
72462aa2271Ssemarie 
725585157b0Sbeck 		/* no specific name, so check unveil directory flags */
726585157b0Sbeck 		if (!unveil_flagmatch(ni, uv->uv_flags)) {
72762aa2271Ssemarie 			DPRINTF("unveil: %s(%d) terminal "
728585157b0Sbeck 			    "'%s' flags mismatch in directory "
729585157b0Sbeck 			    "vnode %p\n",
7307ac2b76bSguenther 			    pr->ps_comm, pr->ps_pid,
731585157b0Sbeck 			    ni->ni_cnd.cn_nameptr, ni->ni_dvp);
73262aa2271Ssemarie 
733585157b0Sbeck 			/*
7341939e486Sbeck 			 * If dir has user set restrictions fail with
7352abaea44Sclaudio 			 * EACCES or ENOENT. Otherwise, use any covering
7362abaea44Sclaudio 			 * match that we found above this dir.
737585157b0Sbeck 			 */
738fa19a603Sbluhm 			if (uv->uv_flags & UNVEIL_USERSET) {
7397ac2b76bSguenther 				pr->ps_acflag |= AUNVEIL;
7402abaea44Sclaudio 				if (uv->uv_flags & UNVEIL_MASK)
741585157b0Sbeck 					return EACCES;
7422abaea44Sclaudio 				else
7432abaea44Sclaudio 					return ENOENT;
744fa19a603Sbluhm 			}
745dbaaa455Sclaudio 			/* start backtrack from this node */
746585157b0Sbeck 			ni->ni_unveil_match = uv;
7478b23add8Sbeck 			goto done;
7488b23add8Sbeck 		}
749dbaaa455Sclaudio 		/* directory flags match, success */
75062aa2271Ssemarie 		DPRINTF("unveil: %s(%d): matched \"%s\" underneath vnode %p\n",
751dbaaa455Sclaudio 		    pr->ps_comm, pr->ps_pid, ni->ni_cnd.cn_nameptr,
752dbaaa455Sclaudio 		    uv->uv_vp);
75362aa2271Ssemarie 
754dbaaa455Sclaudio 		return (0);
755dbaaa455Sclaudio 	}
7568b23add8Sbeck 	if (!unveil_flagmatch(ni, tname->un_flags)) {
757585157b0Sbeck 		/* do flags match for matched name */
75862aa2271Ssemarie 		DPRINTF("unveil: %s(%d) flag mismatch for terminal '%s'\n",
7597ac2b76bSguenther 		    pr->ps_comm, pr->ps_pid, tname->un_name);
76062aa2271Ssemarie 
7617ac2b76bSguenther 		pr->ps_acflag |= AUNVEIL;
7628b23add8Sbeck 		return EACCES;
7638b23add8Sbeck 	}
764dbaaa455Sclaudio 	/* name and flags match. success */
76562aa2271Ssemarie 	DPRINTF("unveil: %s(%d) matched terminal '%s'\n",
766dbaaa455Sclaudio 	    pr->ps_comm, pr->ps_pid, tname->un_name);
76762aa2271Ssemarie 
768dbaaa455Sclaudio 	return (0);
769585157b0Sbeck 
7708b23add8Sbeck done:
771dbaaa455Sclaudio 	/*
772dbaaa455Sclaudio 	 * last component did not match, check previous matches if
773dbaaa455Sclaudio 	 * access is allowed or not.
774dbaaa455Sclaudio 	 */
775dbaaa455Sclaudio 	for (uv = ni->ni_unveil_match; uv != NULL; uv = nuv) {
776dbaaa455Sclaudio 		if (unveil_flagmatch(ni, uv->uv_flags)) {
77762aa2271Ssemarie 			DPRINTF("unveil: %s(%d): matched \"%s\" underneath/at "
778dbaaa455Sclaudio 			    "vnode %p\n", pr->ps_comm, pr->ps_pid,
779dbaaa455Sclaudio 			    ni->ni_cnd.cn_nameptr, uv->uv_vp);
78062aa2271Ssemarie 
7818b23add8Sbeck 			return (0);
7828b23add8Sbeck 		}
783dbaaa455Sclaudio 		/* if node has any flags set then this is an access violation */
784dbaaa455Sclaudio 		if (uv->uv_flags & UNVEIL_USERSET) {
78562aa2271Ssemarie 			DPRINTF("unveil: %s(%d) flag mismatch for vnode %p\n",
786dbaaa455Sclaudio 			    pr->ps_comm, pr->ps_pid, uv->uv_vp);
78762aa2271Ssemarie 
7887ac2b76bSguenther 			pr->ps_acflag |= AUNVEIL;
7892abaea44Sclaudio 			if (uv->uv_flags & UNVEIL_MASK)
7901939e486Sbeck 				return EACCES;
7912abaea44Sclaudio 			else
7922abaea44Sclaudio 				return ENOENT;
7931939e486Sbeck 		}
79462aa2271Ssemarie 
79562aa2271Ssemarie 		DPRINTF("unveil: %s(%d) check cover for vnode %p, uv_cover %zd\n",
796dbaaa455Sclaudio 		    pr->ps_comm, pr->ps_pid, uv->uv_vp, uv->uv_cover);
79762aa2271Ssemarie 
798dbaaa455Sclaudio 		nuv = unveil_covered(uv, uv->uv_vp, p);
799dbaaa455Sclaudio 		if (nuv == uv)
800dbaaa455Sclaudio 			break;
801dbaaa455Sclaudio 	}
8027ac2b76bSguenther 	pr->ps_acflag |= AUNVEIL;
8038b23add8Sbeck 	return ENOENT;
8048b23add8Sbeck }
8058b23add8Sbeck 
8068b23add8Sbeck /*
8078b23add8Sbeck  * Scan all active processes to see if any of them have a unveil
8088b23add8Sbeck  * to this vnode. If so, NULL the vnode in their unveil list,
8098b23add8Sbeck  * vrele, drop the reference, and mark their unveil list
8108b23add8Sbeck  * as needing to have the hole shrunk the next time the process
8118b23add8Sbeck  * uses it for lookup.
8128b23add8Sbeck  */
8138b23add8Sbeck void
unveil_removevnode(struct vnode * vp)8148b23add8Sbeck unveil_removevnode(struct vnode *vp)
8158b23add8Sbeck {
8168b23add8Sbeck 	struct process *pr;
8178b23add8Sbeck 
8188b23add8Sbeck 	if (vp->v_uvcount == 0)
8198b23add8Sbeck 		return;
820413a2998Sbeck 
82162aa2271Ssemarie 	DPRINTF("%s: found vnode %p with count %d\n",
82262aa2271Ssemarie 	    __func__, vp, vp->v_uvcount);
82362aa2271Ssemarie 
824413a2998Sbeck 	vref(vp); /* make sure it is held till we are done */
825413a2998Sbeck 
8268b23add8Sbeck 	LIST_FOREACH(pr, &allprocess, ps_list) {
8278b23add8Sbeck 		struct unveil * uv;
8288b47f4acSderaadt 
8297ac2b76bSguenther 		if ((uv = unveil_lookup(vp, pr, NULL)) != NULL &&
830413a2998Sbeck 		    uv->uv_vp != NULL) {
8318b23add8Sbeck 			uv->uv_vp = NULL;
8328b23add8Sbeck 			uv->uv_flags = 0;
83362aa2271Ssemarie 
83462aa2271Ssemarie 			DPRINTF("%s: vnode %p now count %d\n",
83562aa2271Ssemarie 			    __func__, vp, vp->v_uvcount);
83662aa2271Ssemarie 
837413a2998Sbeck 			if (vp->v_uvcount > 0) {
8388b23add8Sbeck 				vrele(vp);
839413a2998Sbeck 				vp->v_uvcount--;
840413a2998Sbeck 			} else
841413a2998Sbeck 				panic("vp %p, v_uvcount of %d should be 0",
842413a2998Sbeck 				    vp, vp->v_uvcount);
843413a2998Sbeck 		}
844413a2998Sbeck 	}
845413a2998Sbeck 	KASSERT(vp->v_uvcount == 0);
846413a2998Sbeck 
847413a2998Sbeck 	vrele(vp); /* release our ref */
8488b23add8Sbeck }
849