xref: /onnv-gate/usr/src/uts/common/os/klpd.c (revision 12273:63678502e95e)
16134Scasper /*
26134Scasper  * CDDL HEADER START
36134Scasper  *
46134Scasper  * The contents of this file are subject to the terms of the
56134Scasper  * Common Development and Distribution License (the "License").
66134Scasper  * You may not use this file except in compliance with the License.
76134Scasper  *
86134Scasper  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
96134Scasper  * or http://www.opensolaris.org/os/licensing.
106134Scasper  * See the License for the specific language governing permissions
116134Scasper  * and limitations under the License.
126134Scasper  *
136134Scasper  * When distributing Covered Code, include this CDDL HEADER in each
146134Scasper  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
156134Scasper  * If applicable, add the following below this CDDL HEADER, with the
166134Scasper  * fields enclosed by brackets "[]" replaced with your own identifying
176134Scasper  * information: Portions Copyright [yyyy] [name of copyright owner]
186134Scasper  *
196134Scasper  * CDDL HEADER END
206134Scasper  */
216134Scasper 
226134Scasper /*
23*12273SCasper.Dik@Sun.COM  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
246134Scasper  */
256134Scasper 
266134Scasper #include <sys/atomic.h>
276134Scasper #include <sys/door.h>
286134Scasper #include <sys/proc.h>
296134Scasper #include <sys/cred_impl.h>
306134Scasper #include <sys/policy.h>
316134Scasper #include <sys/priv.h>
326134Scasper #include <sys/klpd.h>
336134Scasper #include <sys/errno.h>
346134Scasper #include <sys/kmem.h>
356134Scasper #include <sys/project.h>
366134Scasper #include <sys/systm.h>
376134Scasper #include <sys/sysmacros.h>
386134Scasper #include <sys/pathname.h>
396134Scasper #include <sys/varargs.h>
406134Scasper #include <sys/zone.h>
416134Scasper #include <netinet/in.h>
426134Scasper 
436134Scasper #define	ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
446134Scasper 
456134Scasper static kmutex_t klpd_mutex;
466134Scasper 
476134Scasper typedef struct klpd_reg {
486134Scasper 	struct klpd_reg *klpd_next;
496134Scasper 	struct klpd_reg **klpd_refp;
506134Scasper 	door_handle_t 	klpd_door;
516134Scasper 	pid_t		klpd_door_pid;
526134Scasper 	priv_set_t	klpd_pset;
536134Scasper 	cred_t		*klpd_cred;
546134Scasper 	int		klpd_indel;		/* Disabled */
556134Scasper 	uint32_t	klpd_ref;
566134Scasper } klpd_reg_t;
576134Scasper 
586134Scasper 
596134Scasper /*
606134Scasper  * This data structure hangs off the credential of a process; the
616134Scasper  * credential is finalized and cannot be changed; but this structure
626134Scasper  * can be changed when a new door server for the particular group
636134Scasper  * needs to be registered.  It is refcounted and shared between
646134Scasper  * processes with common ancestry.
656134Scasper  *
666134Scasper  * The reference count is atomically updated.
676134Scasper  *
686134Scasper  * But the registration probably needs to be updated under a lock.
696134Scasper  */
706134Scasper typedef struct credklpd {
716134Scasper 	kmutex_t	crkl_lock;
726134Scasper 	klpd_reg_t	*crkl_reg;
736134Scasper 	uint32_t	crkl_ref;
746134Scasper } credklpd_t;
756134Scasper 
766134Scasper klpd_reg_t *klpd_list;
776134Scasper 
786134Scasper static void klpd_unlink(klpd_reg_t *);
796134Scasper static int klpd_unreg_dh(door_handle_t);
806134Scasper 
816134Scasper static credklpd_t *crklpd_alloc(void);
826134Scasper 
836134Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *);
846134Scasper 
856134Scasper extern size_t max_vnode_path;
866134Scasper 
876134Scasper void
klpd_rele(klpd_reg_t * p)886134Scasper klpd_rele(klpd_reg_t *p)
896134Scasper {
906134Scasper 	if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
916134Scasper 		if (p->klpd_refp != NULL)
926134Scasper 			klpd_unlink(p);
936134Scasper 		if (p->klpd_cred != NULL)
946134Scasper 			crfree(p->klpd_cred);
956134Scasper 		door_ki_rele(p->klpd_door);
966134Scasper 		kmem_free(p, sizeof (*p));
976134Scasper 	}
986134Scasper }
996134Scasper 
1006134Scasper /*
1016134Scasper  * In order to be able to walk the lists, we can't unlink the entry
1026134Scasper  * until the reference count drops to 0.  If we remove it too soon,
1036134Scasper  * list walkers will terminate when they happen to call a now orphaned
1046134Scasper  * entry.
1056134Scasper  */
1066134Scasper static klpd_reg_t *
klpd_rele_next(klpd_reg_t * p)1076134Scasper klpd_rele_next(klpd_reg_t *p)
1086134Scasper {
1096134Scasper 	klpd_reg_t *r = p->klpd_next;
1106134Scasper 
1116134Scasper 	klpd_rele(p);
1126134Scasper 	return (r);
1136134Scasper }
1146134Scasper 
1156134Scasper 
1166134Scasper static void
klpd_hold(klpd_reg_t * p)1176134Scasper klpd_hold(klpd_reg_t *p)
1186134Scasper {
1196134Scasper 	atomic_add_32(&p->klpd_ref, 1);
1206134Scasper }
1216134Scasper 
1226134Scasper /*
1236134Scasper  * Remove registration from where it is registered.  Returns next in list.
1246134Scasper  */
1256134Scasper static void
klpd_unlink(klpd_reg_t * p)1266134Scasper klpd_unlink(klpd_reg_t *p)
1276134Scasper {
1286134Scasper 	ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
1296134Scasper 
1306134Scasper 	if (p->klpd_refp != NULL)
1316134Scasper 		*p->klpd_refp = p->klpd_next;
1326134Scasper 
1336134Scasper 	if (p->klpd_next != NULL)
1346134Scasper 		p->klpd_next->klpd_refp = p->klpd_refp;
1356134Scasper 	p->klpd_refp = NULL;
1366134Scasper }
1376134Scasper 
1386134Scasper /*
139*12273SCasper.Dik@Sun.COM  * Remove all elements of the klpd list and decrement their refcnts.
1406134Scasper  * The lock guarding the list should be held; this function is
141*12273SCasper.Dik@Sun.COM  * called when we are sure we want to destroy the list completely
142*12273SCasper.Dik@Sun.COM  * list but not so sure that the reference counts of all elements have
143*12273SCasper.Dik@Sun.COM  * dropped back to 1.
1446134Scasper  */
1456134Scasper void
klpd_freelist(klpd_reg_t ** pp)146*12273SCasper.Dik@Sun.COM klpd_freelist(klpd_reg_t **pp)
1476134Scasper {
148*12273SCasper.Dik@Sun.COM 	klpd_reg_t *p;
149*12273SCasper.Dik@Sun.COM 
150*12273SCasper.Dik@Sun.COM 	while ((p = *pp) != NULL) {
151*12273SCasper.Dik@Sun.COM 		klpd_unlink(p);
152*12273SCasper.Dik@Sun.COM 		klpd_rele(p);
153*12273SCasper.Dik@Sun.COM 	}
1546134Scasper }
1556134Scasper 
1566134Scasper /*
1576134Scasper  * Link new entry in list.  The Boolean argument specifies whether this
1586134Scasper  * list can contain only a single item or multiple items.
1596134Scasper  * Returns the entry which needs to be released if single is B_TRUE.
1606134Scasper  */
1616134Scasper static klpd_reg_t *
klpd_link(klpd_reg_t * p,klpd_reg_t ** listp,boolean_t single)1626134Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
1636134Scasper {
1646134Scasper 	klpd_reg_t *old = *listp;
1656134Scasper 
1666134Scasper 	ASSERT(p->klpd_ref == 1);
1676134Scasper 
1686134Scasper 	ASSERT(old == NULL || *old->klpd_refp == old);
1696134Scasper 	p->klpd_refp = listp;
1706134Scasper 	p->klpd_next = single ? NULL : old;
1716134Scasper 	*listp = p;
1726134Scasper 	if (old != NULL) {
1736134Scasper 		if (single) {
1746134Scasper 			ASSERT(old->klpd_next == NULL);
1756134Scasper 			old->klpd_refp = NULL;
1766134Scasper 			return (old);
1776134Scasper 		} else
1786134Scasper 			old->klpd_refp = &p->klpd_next;
1796134Scasper 	}
1806134Scasper 	return (NULL);
1816134Scasper }
1826134Scasper 
1836134Scasper /*
1846134Scasper  * The typical call consists of:
1856134Scasper  *	- priv_set_t
1866134Scasper  *	- some integer data (type, value)
1876134Scasper  * for now, it's just one bit.
1886134Scasper  */
1896134Scasper static klpd_head_t *
klpd_marshall(klpd_reg_t * p,const priv_set_t * rq,va_list ap)1906134Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
1916134Scasper {
192*12273SCasper.Dik@Sun.COM 	char	*tmp;
1936134Scasper 	uint_t	type;
1946134Scasper 	vnode_t *vp;
1956134Scasper 	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
1966134Scasper 	size_t	plen, clen;
1976134Scasper 	int	proto;
1986134Scasper 
1996134Scasper 	klpd_arg_t *kap = NULL;
2006134Scasper 	klpd_head_t *khp;
2016134Scasper 
2026134Scasper 	type = va_arg(ap, uint_t);
2036134Scasper 	switch (type) {
2046134Scasper 	case KLPDARG_NOMORE:
2056134Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
2066134Scasper 		khp->klh_argoff = 0;
2076134Scasper 		break;
2086134Scasper 	case KLPDARG_VNODE:
2096134Scasper 		len += offsetof(klpd_arg_t, kla_str);
2106134Scasper 		vp = va_arg(ap, vnode_t *);
2116134Scasper 		if (vp == NULL)
2126134Scasper 			return (NULL);
2136134Scasper 
214*12273SCasper.Dik@Sun.COM 		tmp = va_arg(ap, char *);
2156134Scasper 
216*12273SCasper.Dik@Sun.COM 		if (tmp != NULL && *tmp != '\0')
217*12273SCasper.Dik@Sun.COM 			clen = strlen(tmp) + 1;
2186134Scasper 		else
2196134Scasper 			clen = 0;
2206134Scasper 
2216134Scasper 		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
2226134Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
2236134Scasper 
2246134Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
2256134Scasper 		kap = KLH_ARG(khp);
2266134Scasper 
2276134Scasper 		if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
2286134Scasper 		    vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
2296134Scasper 			kmem_free(khp, len);
2306134Scasper 			return (NULL);
2316134Scasper 		}
2326134Scasper 		if (clen != 0) {
2336134Scasper 			plen = strlen(kap->kla_str);
2346134Scasper 			if (plen + clen + 1 >= MAXPATHLEN) {
2356134Scasper 				kmem_free(khp, len);
2366134Scasper 				return (NULL);
2376134Scasper 			}
2386134Scasper 			/* Don't make root into a double "/" */
2396134Scasper 			if (plen <= 2)
2406134Scasper 				plen = 0;
2416134Scasper 			kap->kla_str[plen] = '/';
242*12273SCasper.Dik@Sun.COM 			bcopy(tmp, &kap->kla_str[plen + 1], clen);
2436134Scasper 		}
2446134Scasper 		break;
2456134Scasper 	case KLPDARG_PORT:
2466134Scasper 		proto = va_arg(ap, int);
2476134Scasper 		switch (proto) {
2486134Scasper 		case IPPROTO_TCP:	type = KLPDARG_TCPPORT;
2496134Scasper 					break;
2506134Scasper 		case IPPROTO_UDP:	type = KLPDARG_UDPPORT;
2516134Scasper 					break;
2526134Scasper 		case IPPROTO_SCTP:	type = KLPDARG_SCTPPORT;
2536134Scasper 					break;
2546134Scasper 		case PROTO_SDP:		type = KLPDARG_SDPPORT;
2556134Scasper 					break;
2566134Scasper 		}
2576134Scasper 		/* FALLTHROUGH */
2586134Scasper 	case KLPDARG_INT:
2596134Scasper 	case KLPDARG_TCPPORT:
2606134Scasper 	case KLPDARG_UDPPORT:
2616134Scasper 	case KLPDARG_SCTPPORT:
2626134Scasper 	case KLPDARG_SDPPORT:
2636134Scasper 		len += sizeof (*kap);
2646134Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
2656134Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
2666134Scasper 		kap = KLH_ARG(khp);
2676134Scasper 		kap->kla_int = va_arg(ap, int);
2686134Scasper 		break;
2696134Scasper 	default:
2706134Scasper 		return (NULL);
2716134Scasper 	}
2726134Scasper 	khp->klh_vers = KLPDCALL_VERS;
2736134Scasper 	khp->klh_len = len;
2746134Scasper 	khp->klh_privoff = sizeof (*khp);
2756134Scasper 	*KLH_PRIVSET(khp) = *rq;
2766134Scasper 	if (kap != NULL) {
2776134Scasper 		kap->kla_type = type;
2786134Scasper 		kap->kla_dlen = len - khp->klh_argoff;
2796134Scasper 	}
2806134Scasper 	return (khp);
2816134Scasper }
2826134Scasper 
2836134Scasper static int
klpd_do_call(klpd_reg_t * p,const priv_set_t * req,va_list ap)2846134Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
2856134Scasper {
2866134Scasper 	door_arg_t da;
2876134Scasper 	int res;
2886134Scasper 	int dres;
2896134Scasper 	klpd_head_t *klh;
2906134Scasper 
2916134Scasper 	if (p->klpd_door_pid == curproc->p_pid)
2926134Scasper 		return (-1);
2936134Scasper 
2946134Scasper 	klh = klpd_marshall(p, req, ap);
2956134Scasper 
2966134Scasper 	if (klh == NULL)
2976134Scasper 		return (-1);
2986134Scasper 
2996134Scasper 	da.data_ptr = (char *)klh;
3006134Scasper 	da.data_size = klh->klh_len;
3016134Scasper 	da.desc_ptr = NULL;
3026134Scasper 	da.desc_num = 0;
3036134Scasper 	da.rbuf = (char *)&res;
3046134Scasper 	da.rsize = sizeof (res);
3056134Scasper 
3066997Sjwadams 	while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
3076997Sjwadams 	    SIZE_MAX, 0)) != 0) {
3086134Scasper 		switch (dres) {
3096134Scasper 		case EAGAIN:
3106134Scasper 			delay(1);
3116134Scasper 			continue;
3126134Scasper 		case EINVAL:
3136134Scasper 		case EBADF:
3146134Scasper 			/* Bad door, don't call it again. */
3156134Scasper 			(void) klpd_unreg_dh(p->klpd_door);
3166134Scasper 			/* FALLTHROUGH */
3176134Scasper 		case EINTR:
3186134Scasper 			/* Pending signal, nothing we can do. */
3196134Scasper 			/* FALLTHROUGH */
3206134Scasper 		default:
3216134Scasper 			kmem_free(klh, klh->klh_len);
3226134Scasper 			return (-1);
3236134Scasper 		}
3246134Scasper 	}
3256134Scasper 	kmem_free(klh, klh->klh_len);
3266134Scasper 	/* Bogus return value, must be a failure */
3276134Scasper 	if (da.rbuf != (char *)&res) {
3286134Scasper 		kmem_free(da.rbuf, da.rsize);
3296134Scasper 		return (-1);
3306134Scasper 	}
3316134Scasper 	return (res);
3326134Scasper }
3336134Scasper 
3346134Scasper uint32_t klpd_bad_locks;
3356134Scasper 
3366134Scasper int
klpd_call(const cred_t * cr,const priv_set_t * req,va_list ap)3376134Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
3386134Scasper {
3396134Scasper 	klpd_reg_t *p;
3406134Scasper 	int rv = -1;
3416134Scasper 	credklpd_t *ckp;
3426134Scasper 	zone_t *ckzone;
3436134Scasper 
3446134Scasper 	/*
3456134Scasper 	 * These locks must not be held when this code is called;
3466134Scasper 	 * callbacks to userland with these locks held will result
3476134Scasper 	 * in issues.  That said, the code at the call sides was
3486134Scasper 	 * restructured not to call with any of the locks held and
3496134Scasper 	 * no policies operate by default on most processes.
3506134Scasper 	 */
3516134Scasper 	if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
3526134Scasper 	    mutex_owned(&curproc->p_crlock)) {
3536134Scasper 		atomic_add_32(&klpd_bad_locks, 1);
3546134Scasper 		return (-1);
3556134Scasper 	}
3566134Scasper 
3576134Scasper 	/*
3586134Scasper 	 * Enforce the limit set for the call process (still).
3596134Scasper 	 */
3606134Scasper 	if (!priv_issubset(req, &CR_LPRIV(cr)))
3616134Scasper 		return (-1);
3626134Scasper 
3636134Scasper 	/* Try 1: get the credential specific klpd */
3646134Scasper 	if ((ckp = crgetcrklpd(cr)) != NULL) {
3656134Scasper 		mutex_enter(&ckp->crkl_lock);
3666134Scasper 		if ((p = ckp->crkl_reg) != NULL &&
3676134Scasper 		    p->klpd_indel == 0 &&
3686134Scasper 		    priv_issubset(req, &p->klpd_pset)) {
3696134Scasper 			klpd_hold(p);
3706134Scasper 			mutex_exit(&ckp->crkl_lock);
3716134Scasper 			rv = klpd_do_call(p, req, ap);
3726134Scasper 			mutex_enter(&ckp->crkl_lock);
3736134Scasper 			klpd_rele(p);
3746134Scasper 			mutex_exit(&ckp->crkl_lock);
3756134Scasper 			if (rv != -1)
3766134Scasper 				return (rv == 0 ? 0 : -1);
3776134Scasper 		} else {
3786134Scasper 			mutex_exit(&ckp->crkl_lock);
3796134Scasper 		}
3806134Scasper 	}
3816134Scasper 
3826134Scasper 	/* Try 2: get the project specific klpd */
3836134Scasper 	mutex_enter(&klpd_mutex);
3846134Scasper 
3856134Scasper 	if ((p = curproj->kpj_klpd) != NULL) {
3866134Scasper 		klpd_hold(p);
3876134Scasper 		mutex_exit(&klpd_mutex);
3886134Scasper 		if (p->klpd_indel == 0 &&
3896134Scasper 		    priv_issubset(req, &p->klpd_pset)) {
3906134Scasper 			rv = klpd_do_call(p, req, ap);
3916134Scasper 		}
3926134Scasper 		mutex_enter(&klpd_mutex);
3936134Scasper 		klpd_rele(p);
3946134Scasper 		mutex_exit(&klpd_mutex);
3956134Scasper 
3966134Scasper 		if (rv != -1)
3976134Scasper 			return (rv == 0 ? 0 : -1);
3986134Scasper 	} else {
3996134Scasper 		mutex_exit(&klpd_mutex);
4006134Scasper 	}
4016134Scasper 
4026134Scasper 	/* Try 3: get the global klpd list */
4036134Scasper 	ckzone = crgetzone(cr);
4046134Scasper 	mutex_enter(&klpd_mutex);
4056134Scasper 
4066134Scasper 	for (p = klpd_list; p != NULL; ) {
4076134Scasper 		zone_t *kkzone = crgetzone(p->klpd_cred);
4086134Scasper 		if ((kkzone == &zone0 || kkzone == ckzone) &&
4096134Scasper 		    p->klpd_indel == 0 &&
4106134Scasper 		    priv_issubset(req, &p->klpd_pset)) {
4116134Scasper 			klpd_hold(p);
4126134Scasper 			mutex_exit(&klpd_mutex);
4136134Scasper 			rv = klpd_do_call(p, req, ap);
4146134Scasper 			mutex_enter(&klpd_mutex);
4156134Scasper 
4166134Scasper 			p = klpd_rele_next(p);
4176134Scasper 
4186134Scasper 			if (rv != -1)
4196134Scasper 				break;
4206134Scasper 		} else {
4216134Scasper 			p = p->klpd_next;
4226134Scasper 		}
4236134Scasper 	}
4246134Scasper 	mutex_exit(&klpd_mutex);
4256134Scasper 	return (rv == 0 ? 0 : -1);
4266134Scasper }
4276134Scasper 
4286134Scasper /*
4296134Scasper  * Register the klpd.
4306134Scasper  * If the pid_t passed in is positive, update the registration for
4316134Scasper  * the specific process; that is only possible if the process already
4326134Scasper  * has a registration on it.  This change of registration will affect
4336134Scasper  * all processes which share common ancestry.
4346134Scasper  *
4356134Scasper  * MY_PID (pid 0) can be used to create or change the context for
4366134Scasper  * the current process, typically done after fork().
4376134Scasper  *
4386134Scasper  * A negative value can be used to register a klpd globally.
4396134Scasper  *
4406134Scasper  * The per-credential klpd needs to be cleaned up when entering
4416134Scasper  * a zone or unsetting the flag.
4426134Scasper  */
4436134Scasper int
klpd_reg(int did,idtype_t type,id_t id,priv_set_t * psetbuf)4446134Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
4456134Scasper {
4466134Scasper 	cred_t *cr = CRED();
4476134Scasper 	door_handle_t dh;
4486134Scasper 	klpd_reg_t *kpd;
4496134Scasper 	priv_set_t pset;
4506134Scasper 	door_info_t di;
4516134Scasper 	credklpd_t *ckp = NULL;
4526134Scasper 	pid_t pid = -1;
4536134Scasper 	projid_t proj = -1;
4546134Scasper 	kproject_t *kpp = NULL;
4556134Scasper 
4566134Scasper 	if (CR_FLAGS(cr) & PRIV_XPOLICY)
4576134Scasper 		return (set_errno(EINVAL));
4586134Scasper 
4596134Scasper 	if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
4606134Scasper 		return (set_errno(EFAULT));
4616134Scasper 
4626134Scasper 	if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
4636134Scasper 		return (set_errno(EPERM));
4646134Scasper 
4656134Scasper 	switch (type) {
4666134Scasper 	case P_PID:
4676134Scasper 		pid = (pid_t)id;
4686134Scasper 		if (pid == P_MYPID)
4696134Scasper 			pid = curproc->p_pid;
4706134Scasper 		if (pid == curproc->p_pid)
4716134Scasper 			ckp = crklpd_alloc();
4726134Scasper 		break;
4736134Scasper 	case P_PROJID:
4746134Scasper 		proj = (projid_t)id;
4756134Scasper 		kpp = project_hold_by_id(proj, crgetzone(cr),
4766134Scasper 		    PROJECT_HOLD_FIND);
4776134Scasper 		if (kpp == NULL)
4786134Scasper 			return (set_errno(ESRCH));
4796134Scasper 		break;
4806134Scasper 	default:
4816134Scasper 		return (set_errno(ENOTSUP));
4826134Scasper 	}
4836134Scasper 
4846134Scasper 
4856134Scasper 	/*
4866134Scasper 	 * Verify the door passed in; it must be a door and we won't
4876134Scasper 	 * allow processes to be called on their own behalf.
4886134Scasper 	 */
4896134Scasper 	dh = door_ki_lookup(did);
4906134Scasper 	if (dh == NULL || door_ki_info(dh, &di) != 0) {
4916134Scasper 		if (ckp != NULL)
4926134Scasper 			crklpd_rele(ckp);
4936134Scasper 		if (kpp != NULL)
4946134Scasper 			project_rele(kpp);
4956134Scasper 		return (set_errno(EBADF));
4966134Scasper 	}
4976134Scasper 	if (type == P_PID && pid == di.di_target) {
4986134Scasper 		if (ckp != NULL)
4996134Scasper 			crklpd_rele(ckp);
5006134Scasper 		ASSERT(kpp == NULL);
5016134Scasper 		return (set_errno(EINVAL));
5026134Scasper 	}
5036134Scasper 
5046134Scasper 	kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
5056134Scasper 	crhold(kpd->klpd_cred = cr);
5066134Scasper 	kpd->klpd_door = dh;
5076134Scasper 	kpd->klpd_door_pid = di.di_target;
5086134Scasper 	kpd->klpd_ref = 1;
5096134Scasper 	kpd->klpd_pset = pset;
5106134Scasper 
5116134Scasper 	if (kpp != NULL) {
5126134Scasper 		mutex_enter(&klpd_mutex);
5136134Scasper 		kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
5146134Scasper 		mutex_exit(&klpd_mutex);
5156134Scasper 		if (kpd != NULL)
5166134Scasper 			klpd_rele(kpd);
5176134Scasper 		project_rele(kpp);
5186134Scasper 	} else if ((int)pid < 0) {
5196134Scasper 		/* Global daemon */
5206134Scasper 		mutex_enter(&klpd_mutex);
5216134Scasper 		(void) klpd_link(kpd, &klpd_list, B_FALSE);
5226134Scasper 		mutex_exit(&klpd_mutex);
5236134Scasper 	} else if (pid == curproc->p_pid) {
5246134Scasper 		proc_t *p = curproc;
5256134Scasper 		cred_t *newcr = cralloc();
5266134Scasper 
5276134Scasper 		/* No need to lock, sole reference to ckp */
5286134Scasper 		kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
5296134Scasper 
5306134Scasper 		if (kpd != NULL)
5316134Scasper 			klpd_rele(kpd);
5326134Scasper 
5336134Scasper 		mutex_enter(&p->p_crlock);
5346134Scasper 		cr = p->p_cred;
5356134Scasper 		crdup_to(cr, newcr);
5366134Scasper 		crsetcrklpd(newcr, ckp);
5376134Scasper 		p->p_cred = newcr;	/* Already held for p_cred */
5386134Scasper 
5396134Scasper 		crhold(newcr);		/* Hold once for the current thread */
5406134Scasper 		mutex_exit(&p->p_crlock);
5416134Scasper 		crfree(cr);		/* One for the p_cred */
5426134Scasper 		crset(p, newcr);
5436134Scasper 	} else {
5446134Scasper 		proc_t *p;
5456134Scasper 		cred_t *pcr;
5466134Scasper 		mutex_enter(&pidlock);
5476134Scasper 		p = prfind(pid);
5486134Scasper 		if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
5496134Scasper 			mutex_exit(&pidlock);
5506134Scasper 			klpd_rele(kpd);
5516134Scasper 			return (set_errno(p == NULL ? ESRCH : EPERM));
5526134Scasper 		}
5536134Scasper 		mutex_enter(&p->p_crlock);
5546134Scasper 		crhold(pcr = p->p_cred);
5556134Scasper 		mutex_exit(&pidlock);
5566134Scasper 		mutex_exit(&p->p_crlock);
5576134Scasper 		/*
5586134Scasper 		 * We're going to update the credential's ckp in place;
5596134Scasper 		 * this requires that it exists.
5606134Scasper 		 */
5616134Scasper 		ckp = crgetcrklpd(pcr);
5626134Scasper 		if (ckp == NULL) {
5636134Scasper 			crfree(pcr);
5646134Scasper 			klpd_rele(kpd);
5656134Scasper 			return (set_errno(EINVAL));
5666134Scasper 		}
5676134Scasper 		crklpd_setreg(ckp, kpd);
5686134Scasper 		crfree(pcr);
5696134Scasper 	}
5706134Scasper 
5716134Scasper 	return (0);
5726134Scasper }
5736134Scasper 
5746134Scasper static int
klpd_unreg_dh(door_handle_t dh)5756134Scasper klpd_unreg_dh(door_handle_t dh)
5766134Scasper {
5776134Scasper 	klpd_reg_t *p;
5786134Scasper 
5796134Scasper 	mutex_enter(&klpd_mutex);
5806134Scasper 	for (p = klpd_list; p != NULL; p = p->klpd_next) {
5816134Scasper 		if (p->klpd_door == dh)
5826134Scasper 			break;
5836134Scasper 	}
5846134Scasper 	if (p == NULL) {
5856134Scasper 		mutex_exit(&klpd_mutex);
5866134Scasper 		return (EINVAL);
5876134Scasper 	}
5886134Scasper 	if (p->klpd_indel != 0) {
5896134Scasper 		mutex_exit(&klpd_mutex);
5906134Scasper 		return (EAGAIN);
5916134Scasper 	}
5926134Scasper 	p->klpd_indel = 1;
5936134Scasper 	klpd_rele(p);
5946134Scasper 	mutex_exit(&klpd_mutex);
5956134Scasper 	return (0);
5966134Scasper }
5976134Scasper 
5986134Scasper int
klpd_unreg(int did,idtype_t type,id_t id)5996134Scasper klpd_unreg(int did, idtype_t type, id_t id)
6006134Scasper {
6016134Scasper 	door_handle_t dh;
6026134Scasper 	int res = 0;
6036134Scasper 	proc_t *p;
6046134Scasper 	pid_t pid;
6056134Scasper 	projid_t proj;
6066134Scasper 	kproject_t *kpp = NULL;
6076134Scasper 	credklpd_t *ckp;
6086134Scasper 
6096134Scasper 	switch (type) {
6106134Scasper 	case P_PID:
6116134Scasper 		pid = (pid_t)id;
6126134Scasper 		break;
6136134Scasper 	case P_PROJID:
6146134Scasper 		proj = (projid_t)id;
6156134Scasper 		kpp = project_hold_by_id(proj, crgetzone(CRED()),
6166134Scasper 		    PROJECT_HOLD_FIND);
6176134Scasper 		if (kpp == NULL)
6186134Scasper 			return (set_errno(ESRCH));
6196134Scasper 		break;
6206134Scasper 	default:
6216134Scasper 		return (set_errno(ENOTSUP));
6226134Scasper 	}
6236134Scasper 
6246134Scasper 	dh = door_ki_lookup(did);
6256134Scasper 	if (dh == NULL) {
6266134Scasper 		if (kpp != NULL)
6276134Scasper 			project_rele(kpp);
6286134Scasper 		return (set_errno(EINVAL));
6296134Scasper 	}
6306134Scasper 
6316134Scasper 	if (kpp != NULL) {
6326134Scasper 		mutex_enter(&klpd_mutex);
6336134Scasper 		if (kpp->kpj_klpd == NULL)
6346134Scasper 			res = ESRCH;
6356134Scasper 		else
636*12273SCasper.Dik@Sun.COM 			klpd_freelist(&kpp->kpj_klpd);
6376134Scasper 		mutex_exit(&klpd_mutex);
6386134Scasper 		project_rele(kpp);
6396134Scasper 		goto out;
6406134Scasper 	} else if ((int)pid > 0) {
6416134Scasper 		mutex_enter(&pidlock);
6426134Scasper 		p = prfind(pid);
6436134Scasper 		if (p == NULL) {
6446134Scasper 			mutex_exit(&pidlock);
6456134Scasper 			door_ki_rele(dh);
6466134Scasper 			return (set_errno(ESRCH));
6476134Scasper 		}
6486134Scasper 		mutex_enter(&p->p_crlock);
6496134Scasper 		mutex_exit(&pidlock);
6506134Scasper 	} else if (pid == 0) {
6516134Scasper 		p = curproc;
6526134Scasper 		mutex_enter(&p->p_crlock);
6536134Scasper 	} else {
6546134Scasper 		res = klpd_unreg_dh(dh);
6556134Scasper 		goto out;
6566134Scasper 	}
6576134Scasper 
6586134Scasper 	ckp = crgetcrklpd(p->p_cred);
6596134Scasper 	if (ckp != NULL) {
6606134Scasper 		crklpd_setreg(ckp, NULL);
6616134Scasper 	} else {
6626134Scasper 		res = ESRCH;
6636134Scasper 	}
6646134Scasper 	mutex_exit(&p->p_crlock);
6656134Scasper 
6666134Scasper out:
6676134Scasper 	door_ki_rele(dh);
6686134Scasper 
6696134Scasper 	if (res != 0)
6706134Scasper 		return (set_errno(res));
6716134Scasper 	return (0);
6726134Scasper }
6736134Scasper 
6746134Scasper void
crklpd_hold(credklpd_t * crkpd)6756134Scasper crklpd_hold(credklpd_t *crkpd)
6766134Scasper {
6776134Scasper 	atomic_add_32(&crkpd->crkl_ref, 1);
6786134Scasper }
6796134Scasper 
6806134Scasper void
crklpd_rele(credklpd_t * crkpd)6816134Scasper crklpd_rele(credklpd_t *crkpd)
6826134Scasper {
6836134Scasper 	if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
6846134Scasper 		if (crkpd->crkl_reg != NULL)
6856134Scasper 			klpd_rele(crkpd->crkl_reg);
6866134Scasper 		mutex_destroy(&crkpd->crkl_lock);
6876134Scasper 		kmem_free(crkpd, sizeof (*crkpd));
6886134Scasper 	}
6896134Scasper }
6906134Scasper 
6916134Scasper static credklpd_t *
crklpd_alloc(void)6926134Scasper crklpd_alloc(void)
6936134Scasper {
6946134Scasper 	credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
6956134Scasper 
6966134Scasper 	mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
6976134Scasper 	res->crkl_ref = 1;
6986134Scasper 	res->crkl_reg = NULL;
6996134Scasper 
7006134Scasper 	return (res);
7016134Scasper }
7026134Scasper 
7036134Scasper void
crklpd_setreg(credklpd_t * crk,klpd_reg_t * new)7046134Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
7056134Scasper {
7066134Scasper 	klpd_reg_t *old;
7076134Scasper 
7086134Scasper 	mutex_enter(&crk->crkl_lock);
7096134Scasper 	if (new == NULL) {
7106134Scasper 		old = crk->crkl_reg;
7116134Scasper 		if (old != NULL)
7126134Scasper 			klpd_unlink(old);
7136134Scasper 	} else {
7146134Scasper 		old = klpd_link(new, &crk->crkl_reg, B_TRUE);
7156134Scasper 	}
7166134Scasper 	mutex_exit(&crk->crkl_lock);
7176134Scasper 
7186134Scasper 	if (old != NULL)
7196134Scasper 		klpd_rele(old);
7206134Scasper }
721*12273SCasper.Dik@Sun.COM 
722*12273SCasper.Dik@Sun.COM /* Allocate and register the pfexec specific callback */
723*12273SCasper.Dik@Sun.COM int
pfexec_reg(int did)724*12273SCasper.Dik@Sun.COM pfexec_reg(int did)
725*12273SCasper.Dik@Sun.COM {
726*12273SCasper.Dik@Sun.COM 	door_handle_t dh;
727*12273SCasper.Dik@Sun.COM 	int err = secpolicy_pfexec_register(CRED());
728*12273SCasper.Dik@Sun.COM 	klpd_reg_t *pfx;
729*12273SCasper.Dik@Sun.COM 	door_info_t di;
730*12273SCasper.Dik@Sun.COM 	zone_t *myzone = crgetzone(CRED());
731*12273SCasper.Dik@Sun.COM 
732*12273SCasper.Dik@Sun.COM 	if (err != 0)
733*12273SCasper.Dik@Sun.COM 		return (set_errno(err));
734*12273SCasper.Dik@Sun.COM 
735*12273SCasper.Dik@Sun.COM 	dh = door_ki_lookup(did);
736*12273SCasper.Dik@Sun.COM 	if (dh == NULL || door_ki_info(dh, &di) != 0)
737*12273SCasper.Dik@Sun.COM 		return (set_errno(EBADF));
738*12273SCasper.Dik@Sun.COM 
739*12273SCasper.Dik@Sun.COM 	pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
740*12273SCasper.Dik@Sun.COM 
741*12273SCasper.Dik@Sun.COM 	pfx->klpd_door = dh;
742*12273SCasper.Dik@Sun.COM 	pfx->klpd_door_pid = di.di_target;
743*12273SCasper.Dik@Sun.COM 	pfx->klpd_ref = 1;
744*12273SCasper.Dik@Sun.COM 	pfx->klpd_cred = NULL;
745*12273SCasper.Dik@Sun.COM 	mutex_enter(&myzone->zone_lock);
746*12273SCasper.Dik@Sun.COM 	pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
747*12273SCasper.Dik@Sun.COM 	mutex_exit(&myzone->zone_lock);
748*12273SCasper.Dik@Sun.COM 	if (pfx != NULL)
749*12273SCasper.Dik@Sun.COM 		klpd_rele(pfx);
750*12273SCasper.Dik@Sun.COM 
751*12273SCasper.Dik@Sun.COM 	return (0);
752*12273SCasper.Dik@Sun.COM }
753*12273SCasper.Dik@Sun.COM 
754*12273SCasper.Dik@Sun.COM int
pfexec_unreg(int did)755*12273SCasper.Dik@Sun.COM pfexec_unreg(int did)
756*12273SCasper.Dik@Sun.COM {
757*12273SCasper.Dik@Sun.COM 	door_handle_t dh;
758*12273SCasper.Dik@Sun.COM 	int err = 0;
759*12273SCasper.Dik@Sun.COM 	zone_t *myzone = crgetzone(CRED());
760*12273SCasper.Dik@Sun.COM 	klpd_reg_t *pfd;
761*12273SCasper.Dik@Sun.COM 
762*12273SCasper.Dik@Sun.COM 	dh = door_ki_lookup(did);
763*12273SCasper.Dik@Sun.COM 	if (dh == NULL)
764*12273SCasper.Dik@Sun.COM 		return (set_errno(EBADF));
765*12273SCasper.Dik@Sun.COM 
766*12273SCasper.Dik@Sun.COM 	mutex_enter(&myzone->zone_lock);
767*12273SCasper.Dik@Sun.COM 	pfd = myzone->zone_pfexecd;
768*12273SCasper.Dik@Sun.COM 	if (pfd != NULL && pfd->klpd_door == dh) {
769*12273SCasper.Dik@Sun.COM 		klpd_unlink(pfd);
770*12273SCasper.Dik@Sun.COM 	} else {
771*12273SCasper.Dik@Sun.COM 		pfd = NULL;
772*12273SCasper.Dik@Sun.COM 		err = EINVAL;
773*12273SCasper.Dik@Sun.COM 	}
774*12273SCasper.Dik@Sun.COM 	mutex_exit(&myzone->zone_lock);
775*12273SCasper.Dik@Sun.COM 	door_ki_rele(dh);
776*12273SCasper.Dik@Sun.COM 	/*
777*12273SCasper.Dik@Sun.COM 	 * crfree() cannot be called with zone_lock held; it is called
778*12273SCasper.Dik@Sun.COM 	 * indirectly through closing the door handle
779*12273SCasper.Dik@Sun.COM 	 */
780*12273SCasper.Dik@Sun.COM 	if (pfd != NULL)
781*12273SCasper.Dik@Sun.COM 		klpd_rele(pfd);
782*12273SCasper.Dik@Sun.COM 	if (err != 0)
783*12273SCasper.Dik@Sun.COM 		return (set_errno(err));
784*12273SCasper.Dik@Sun.COM 	return (0);
785*12273SCasper.Dik@Sun.COM }
786*12273SCasper.Dik@Sun.COM 
787*12273SCasper.Dik@Sun.COM static int
get_path(char * buf,const char * path,int len)788*12273SCasper.Dik@Sun.COM get_path(char *buf, const char *path, int len)
789*12273SCasper.Dik@Sun.COM {
790*12273SCasper.Dik@Sun.COM 	size_t lc;
791*12273SCasper.Dik@Sun.COM 	char *s;
792*12273SCasper.Dik@Sun.COM 
793*12273SCasper.Dik@Sun.COM 	if (len < 0)
794*12273SCasper.Dik@Sun.COM 		len = strlen(path);
795*12273SCasper.Dik@Sun.COM 
796*12273SCasper.Dik@Sun.COM 	if (*path == '/' && len < MAXPATHLEN) {
797*12273SCasper.Dik@Sun.COM 		(void) strcpy(buf, path);
798*12273SCasper.Dik@Sun.COM 		return (0);
799*12273SCasper.Dik@Sun.COM 	}
800*12273SCasper.Dik@Sun.COM 	/*
801*12273SCasper.Dik@Sun.COM 	 * Build the pathname using the current directory + resolve pathname.
802*12273SCasper.Dik@Sun.COM 	 * The resolve pathname either starts with a normal component and
803*12273SCasper.Dik@Sun.COM 	 * we can just concatenate them or it starts with one
804*12273SCasper.Dik@Sun.COM 	 * or more ".." component and we can remove those; the
805*12273SCasper.Dik@Sun.COM 	 * last one cannot be a ".." and the current directory has
806*12273SCasper.Dik@Sun.COM 	 * more components than the number of ".." in the resolved pathname.
807*12273SCasper.Dik@Sun.COM 	 */
808*12273SCasper.Dik@Sun.COM 	if (dogetcwd(buf, MAXPATHLEN) != 0)
809*12273SCasper.Dik@Sun.COM 		return (-1);
810*12273SCasper.Dik@Sun.COM 
811*12273SCasper.Dik@Sun.COM 	lc = strlen(buf);
812*12273SCasper.Dik@Sun.COM 
813*12273SCasper.Dik@Sun.COM 	while (len > 3 && strncmp("../", path, 3) == 0) {
814*12273SCasper.Dik@Sun.COM 		len -= 3;
815*12273SCasper.Dik@Sun.COM 		path += 3;
816*12273SCasper.Dik@Sun.COM 
817*12273SCasper.Dik@Sun.COM 		s = strrchr(buf, '/');
818*12273SCasper.Dik@Sun.COM 		if (s == NULL || s == buf)
819*12273SCasper.Dik@Sun.COM 			return (-1);
820*12273SCasper.Dik@Sun.COM 
821*12273SCasper.Dik@Sun.COM 		*s = '\0';
822*12273SCasper.Dik@Sun.COM 		lc = s - buf;
823*12273SCasper.Dik@Sun.COM 	}
824*12273SCasper.Dik@Sun.COM 	/* Add a "/" and a NUL */
825*12273SCasper.Dik@Sun.COM 	if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
826*12273SCasper.Dik@Sun.COM 		return (-1);
827*12273SCasper.Dik@Sun.COM 
828*12273SCasper.Dik@Sun.COM 	buf[lc] = '/';
829*12273SCasper.Dik@Sun.COM 	(void) strcpy(buf + lc + 1, path);
830*12273SCasper.Dik@Sun.COM 
831*12273SCasper.Dik@Sun.COM 	return (0);
832*12273SCasper.Dik@Sun.COM }
833*12273SCasper.Dik@Sun.COM 
834*12273SCasper.Dik@Sun.COM /*
835*12273SCasper.Dik@Sun.COM  * Perform the pfexec upcall.
836*12273SCasper.Dik@Sun.COM  *
837*12273SCasper.Dik@Sun.COM  * The pfexec upcall is different from the klpd_upcall in that a failure
838*12273SCasper.Dik@Sun.COM  * will lead to a denial of execution.
839*12273SCasper.Dik@Sun.COM  */
840*12273SCasper.Dik@Sun.COM int
pfexec_call(const cred_t * cr,struct pathname * rpnp,cred_t ** pfcr,boolean_t * scrub)841*12273SCasper.Dik@Sun.COM pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
842*12273SCasper.Dik@Sun.COM     boolean_t *scrub)
843*12273SCasper.Dik@Sun.COM {
844*12273SCasper.Dik@Sun.COM 	klpd_reg_t *pfd;
845*12273SCasper.Dik@Sun.COM 	pfexec_arg_t *pap;
846*12273SCasper.Dik@Sun.COM 	pfexec_reply_t pr, *prp;
847*12273SCasper.Dik@Sun.COM 	door_arg_t da;
848*12273SCasper.Dik@Sun.COM 	int dres;
849*12273SCasper.Dik@Sun.COM 	cred_t *ncr = NULL;
850*12273SCasper.Dik@Sun.COM 	int err = -1;
851*12273SCasper.Dik@Sun.COM 	priv_set_t *iset;
852*12273SCasper.Dik@Sun.COM 	priv_set_t *lset;
853*12273SCasper.Dik@Sun.COM 	zone_t *myzone = crgetzone(CRED());
854*12273SCasper.Dik@Sun.COM 	size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
855*12273SCasper.Dik@Sun.COM 
856*12273SCasper.Dik@Sun.COM 	/* Find registration */
857*12273SCasper.Dik@Sun.COM 	mutex_enter(&myzone->zone_lock);
858*12273SCasper.Dik@Sun.COM 	if ((pfd = myzone->zone_pfexecd) != NULL)
859*12273SCasper.Dik@Sun.COM 		klpd_hold(pfd);
860*12273SCasper.Dik@Sun.COM 	mutex_exit(&myzone->zone_lock);
861*12273SCasper.Dik@Sun.COM 
862*12273SCasper.Dik@Sun.COM 	if (pfd == NULL)
863*12273SCasper.Dik@Sun.COM 		return (0);
864*12273SCasper.Dik@Sun.COM 
865*12273SCasper.Dik@Sun.COM 	if (pfd->klpd_door_pid == curproc->p_pid) {
866*12273SCasper.Dik@Sun.COM 		klpd_rele(pfd);
867*12273SCasper.Dik@Sun.COM 		return (0);
868*12273SCasper.Dik@Sun.COM 	}
869*12273SCasper.Dik@Sun.COM 
870*12273SCasper.Dik@Sun.COM 	pap = kmem_zalloc(pasize, KM_SLEEP);
871*12273SCasper.Dik@Sun.COM 
872*12273SCasper.Dik@Sun.COM 	if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
873*12273SCasper.Dik@Sun.COM 		goto out1;
874*12273SCasper.Dik@Sun.COM 
875*12273SCasper.Dik@Sun.COM 	pap->pfa_vers = PFEXEC_ARG_VERS;
876*12273SCasper.Dik@Sun.COM 	pap->pfa_call = PFEXEC_EXEC_ATTRS;
877*12273SCasper.Dik@Sun.COM 	pap->pfa_len = pasize;
878*12273SCasper.Dik@Sun.COM 	pap->pfa_uid = crgetruid(cr);
879*12273SCasper.Dik@Sun.COM 
880*12273SCasper.Dik@Sun.COM 	da.data_ptr = (char *)pap;
881*12273SCasper.Dik@Sun.COM 	da.data_size = pap->pfa_len;
882*12273SCasper.Dik@Sun.COM 	da.desc_ptr = NULL;
883*12273SCasper.Dik@Sun.COM 	da.desc_num = 0;
884*12273SCasper.Dik@Sun.COM 	da.rbuf = (char *)&pr;
885*12273SCasper.Dik@Sun.COM 	da.rsize = sizeof (pr);
886*12273SCasper.Dik@Sun.COM 
887*12273SCasper.Dik@Sun.COM 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
888*12273SCasper.Dik@Sun.COM 		switch (dres) {
889*12273SCasper.Dik@Sun.COM 		case EAGAIN:
890*12273SCasper.Dik@Sun.COM 			delay(1);
891*12273SCasper.Dik@Sun.COM 			continue;
892*12273SCasper.Dik@Sun.COM 		case EINVAL:
893*12273SCasper.Dik@Sun.COM 		case EBADF:
894*12273SCasper.Dik@Sun.COM 			/* FALLTHROUGH */
895*12273SCasper.Dik@Sun.COM 		case EINTR:
896*12273SCasper.Dik@Sun.COM 			/* FALLTHROUGH */
897*12273SCasper.Dik@Sun.COM 		default:
898*12273SCasper.Dik@Sun.COM 			goto out;
899*12273SCasper.Dik@Sun.COM 		}
900*12273SCasper.Dik@Sun.COM 	}
901*12273SCasper.Dik@Sun.COM 
902*12273SCasper.Dik@Sun.COM 	prp = (pfexec_reply_t *)da.rbuf;
903*12273SCasper.Dik@Sun.COM 	/*
904*12273SCasper.Dik@Sun.COM 	 * Check the size of the result and the alignment of the
905*12273SCasper.Dik@Sun.COM 	 * privilege sets.
906*12273SCasper.Dik@Sun.COM 	 */
907*12273SCasper.Dik@Sun.COM 	if (da.rsize < sizeof (pr) ||
908*12273SCasper.Dik@Sun.COM 	    prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
909*12273SCasper.Dik@Sun.COM 	    prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
910*12273SCasper.Dik@Sun.COM 	    (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
911*12273SCasper.Dik@Sun.COM 	    (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0)
912*12273SCasper.Dik@Sun.COM 		goto out;
913*12273SCasper.Dik@Sun.COM 
914*12273SCasper.Dik@Sun.COM 	/*
915*12273SCasper.Dik@Sun.COM 	 * Get results:
916*12273SCasper.Dik@Sun.COM 	 *	allow/allow with additional credentials/disallow[*]
917*12273SCasper.Dik@Sun.COM 	 *
918*12273SCasper.Dik@Sun.COM 	 *	euid, uid, egid, gid, privs, and limitprivs
919*12273SCasper.Dik@Sun.COM 	 * We now have somewhat more flexibility we could even set E and P
920*12273SCasper.Dik@Sun.COM 	 * judiciously but that would break some currently valid assumptions
921*12273SCasper.Dik@Sun.COM 	 *	[*] Disallow is not readily supported by always including
922*12273SCasper.Dik@Sun.COM 	 *	the Basic Solaris User profile in all user's profiles.
923*12273SCasper.Dik@Sun.COM 	 */
924*12273SCasper.Dik@Sun.COM 
925*12273SCasper.Dik@Sun.COM 	if (!prp->pfr_allowed) {
926*12273SCasper.Dik@Sun.COM 		err = EACCES;
927*12273SCasper.Dik@Sun.COM 		goto out;
928*12273SCasper.Dik@Sun.COM 	}
929*12273SCasper.Dik@Sun.COM 	if (!prp->pfr_setcred) {
930*12273SCasper.Dik@Sun.COM 		err = 0;
931*12273SCasper.Dik@Sun.COM 		goto out;
932*12273SCasper.Dik@Sun.COM 	}
933*12273SCasper.Dik@Sun.COM 	ncr = crdup((cred_t *)cr);
934*12273SCasper.Dik@Sun.COM 
935*12273SCasper.Dik@Sun.COM 	/*
936*12273SCasper.Dik@Sun.COM 	 * Generate the new credential set scrubenv if ruid != euid (or set)
937*12273SCasper.Dik@Sun.COM 	 * the "I'm set-uid flag" but that is not inherited so scrubbing
938*12273SCasper.Dik@Sun.COM 	 * the environment is a requirement.
939*12273SCasper.Dik@Sun.COM 	 */
940*12273SCasper.Dik@Sun.COM 	/* Set uids or gids, note that -1 will do the right thing */
941*12273SCasper.Dik@Sun.COM 	if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
942*12273SCasper.Dik@Sun.COM 		goto out;
943*12273SCasper.Dik@Sun.COM 	if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0)
944*12273SCasper.Dik@Sun.COM 		goto out;
945*12273SCasper.Dik@Sun.COM 
946*12273SCasper.Dik@Sun.COM 	*scrub = prp->pfr_scrubenv;
947*12273SCasper.Dik@Sun.COM 
948*12273SCasper.Dik@Sun.COM 	if (prp->pfr_clearflag)
949*12273SCasper.Dik@Sun.COM 		CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
950*12273SCasper.Dik@Sun.COM 
951*12273SCasper.Dik@Sun.COM 	/* We cannot exceed our Limit set, no matter what */
952*12273SCasper.Dik@Sun.COM 	iset = PFEXEC_REPLY_IPRIV(prp);
953*12273SCasper.Dik@Sun.COM 
954*12273SCasper.Dik@Sun.COM 	if (iset != NULL) {
955*12273SCasper.Dik@Sun.COM 		if (!priv_issubset(iset, &CR_LPRIV(ncr)))
956*12273SCasper.Dik@Sun.COM 			goto out;
957*12273SCasper.Dik@Sun.COM 		priv_union(iset, &CR_IPRIV(ncr));
958*12273SCasper.Dik@Sun.COM 	}
959*12273SCasper.Dik@Sun.COM 
960*12273SCasper.Dik@Sun.COM 	/* Nor can we increate our Limit set itself */
961*12273SCasper.Dik@Sun.COM 	lset = PFEXEC_REPLY_LPRIV(prp);
962*12273SCasper.Dik@Sun.COM 
963*12273SCasper.Dik@Sun.COM 	if (lset != NULL) {
964*12273SCasper.Dik@Sun.COM 		if (!priv_issubset(lset, &CR_LPRIV(ncr)))
965*12273SCasper.Dik@Sun.COM 			goto out;
966*12273SCasper.Dik@Sun.COM 		CR_LPRIV(ncr) = *lset;
967*12273SCasper.Dik@Sun.COM 	}
968*12273SCasper.Dik@Sun.COM 
969*12273SCasper.Dik@Sun.COM 	/* Exec will do the standard set operations */
970*12273SCasper.Dik@Sun.COM 
971*12273SCasper.Dik@Sun.COM 	err = 0;
972*12273SCasper.Dik@Sun.COM out:
973*12273SCasper.Dik@Sun.COM 	if (da.rbuf != (char *)&pr)
974*12273SCasper.Dik@Sun.COM 		kmem_free(da.rbuf, da.rsize);
975*12273SCasper.Dik@Sun.COM out1:
976*12273SCasper.Dik@Sun.COM 	kmem_free(pap, pasize);
977*12273SCasper.Dik@Sun.COM 	klpd_rele(pfd);
978*12273SCasper.Dik@Sun.COM 	if (ncr != NULL) {
979*12273SCasper.Dik@Sun.COM 		if (err == 0)
980*12273SCasper.Dik@Sun.COM 			*pfcr = ncr;
981*12273SCasper.Dik@Sun.COM 		else
982*12273SCasper.Dik@Sun.COM 			crfree(ncr);
983*12273SCasper.Dik@Sun.COM 	}
984*12273SCasper.Dik@Sun.COM 	return (err);
985*12273SCasper.Dik@Sun.COM }
986*12273SCasper.Dik@Sun.COM 
987*12273SCasper.Dik@Sun.COM int
get_forced_privs(const cred_t * cr,const char * respn,priv_set_t * set)988*12273SCasper.Dik@Sun.COM get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
989*12273SCasper.Dik@Sun.COM {
990*12273SCasper.Dik@Sun.COM 	klpd_reg_t *pfd;
991*12273SCasper.Dik@Sun.COM 	pfexec_arg_t *pap;
992*12273SCasper.Dik@Sun.COM 	door_arg_t da;
993*12273SCasper.Dik@Sun.COM 	int dres;
994*12273SCasper.Dik@Sun.COM 	int err = -1;
995*12273SCasper.Dik@Sun.COM 	priv_set_t *fset, pmem;
996*12273SCasper.Dik@Sun.COM 	cred_t *zkcr;
997*12273SCasper.Dik@Sun.COM 	zone_t *myzone = crgetzone(cr);
998*12273SCasper.Dik@Sun.COM 	size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
999*12273SCasper.Dik@Sun.COM 
1000*12273SCasper.Dik@Sun.COM 	mutex_enter(&myzone->zone_lock);
1001*12273SCasper.Dik@Sun.COM 	if ((pfd = myzone->zone_pfexecd) != NULL)
1002*12273SCasper.Dik@Sun.COM 		klpd_hold(pfd);
1003*12273SCasper.Dik@Sun.COM 	mutex_exit(&myzone->zone_lock);
1004*12273SCasper.Dik@Sun.COM 
1005*12273SCasper.Dik@Sun.COM 	if (pfd == NULL)
1006*12273SCasper.Dik@Sun.COM 		return (-1);
1007*12273SCasper.Dik@Sun.COM 
1008*12273SCasper.Dik@Sun.COM 	if (pfd->klpd_door_pid == curproc->p_pid) {
1009*12273SCasper.Dik@Sun.COM 		klpd_rele(pfd);
1010*12273SCasper.Dik@Sun.COM 		return (0);
1011*12273SCasper.Dik@Sun.COM 	}
1012*12273SCasper.Dik@Sun.COM 
1013*12273SCasper.Dik@Sun.COM 	pap = kmem_zalloc(pasize, KM_SLEEP);
1014*12273SCasper.Dik@Sun.COM 
1015*12273SCasper.Dik@Sun.COM 	if (get_path(pap->pfa_path, respn, -1) == -1)
1016*12273SCasper.Dik@Sun.COM 		goto out1;
1017*12273SCasper.Dik@Sun.COM 
1018*12273SCasper.Dik@Sun.COM 	pap->pfa_vers = PFEXEC_ARG_VERS;
1019*12273SCasper.Dik@Sun.COM 	pap->pfa_call = PFEXEC_FORCED_PRIVS;
1020*12273SCasper.Dik@Sun.COM 	pap->pfa_len = pasize;
1021*12273SCasper.Dik@Sun.COM 	pap->pfa_uid = (uid_t)-1;			/* Not relevant */
1022*12273SCasper.Dik@Sun.COM 
1023*12273SCasper.Dik@Sun.COM 	da.data_ptr = (char *)pap;
1024*12273SCasper.Dik@Sun.COM 	da.data_size = pap->pfa_len;
1025*12273SCasper.Dik@Sun.COM 	da.desc_ptr = NULL;
1026*12273SCasper.Dik@Sun.COM 	da.desc_num = 0;
1027*12273SCasper.Dik@Sun.COM 	da.rbuf = (char *)&pmem;
1028*12273SCasper.Dik@Sun.COM 	da.rsize = sizeof (pmem);
1029*12273SCasper.Dik@Sun.COM 
1030*12273SCasper.Dik@Sun.COM 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1031*12273SCasper.Dik@Sun.COM 		switch (dres) {
1032*12273SCasper.Dik@Sun.COM 		case EAGAIN:
1033*12273SCasper.Dik@Sun.COM 			delay(1);
1034*12273SCasper.Dik@Sun.COM 			continue;
1035*12273SCasper.Dik@Sun.COM 		case EINVAL:
1036*12273SCasper.Dik@Sun.COM 		case EBADF:
1037*12273SCasper.Dik@Sun.COM 		case EINTR:
1038*12273SCasper.Dik@Sun.COM 		default:
1039*12273SCasper.Dik@Sun.COM 			goto out;
1040*12273SCasper.Dik@Sun.COM 		}
1041*12273SCasper.Dik@Sun.COM 	}
1042*12273SCasper.Dik@Sun.COM 
1043*12273SCasper.Dik@Sun.COM 	/*
1044*12273SCasper.Dik@Sun.COM 	 * Check the size of the result, it's a privilege set.
1045*12273SCasper.Dik@Sun.COM 	 */
1046*12273SCasper.Dik@Sun.COM 	if (da.rsize != sizeof (priv_set_t))
1047*12273SCasper.Dik@Sun.COM 		goto out;
1048*12273SCasper.Dik@Sun.COM 
1049*12273SCasper.Dik@Sun.COM 	fset = (priv_set_t *)da.rbuf;
1050*12273SCasper.Dik@Sun.COM 
1051*12273SCasper.Dik@Sun.COM 	/*
1052*12273SCasper.Dik@Sun.COM 	 * We restrict the forced privileges with whatever is available in
1053*12273SCasper.Dik@Sun.COM 	 * the current zone.
1054*12273SCasper.Dik@Sun.COM 	 */
1055*12273SCasper.Dik@Sun.COM 	zkcr = zone_kcred();
1056*12273SCasper.Dik@Sun.COM 	priv_intersect(&CR_LPRIV(zkcr), fset);
1057*12273SCasper.Dik@Sun.COM 
1058*12273SCasper.Dik@Sun.COM 	/*
1059*12273SCasper.Dik@Sun.COM 	 * But we fail if the forced privileges are not found in the current
1060*12273SCasper.Dik@Sun.COM 	 * Limit set.
1061*12273SCasper.Dik@Sun.COM 	 */
1062*12273SCasper.Dik@Sun.COM 	if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1063*12273SCasper.Dik@Sun.COM 		err = EACCES;
1064*12273SCasper.Dik@Sun.COM 	} else if (!priv_isemptyset(fset)) {
1065*12273SCasper.Dik@Sun.COM 		err = 0;
1066*12273SCasper.Dik@Sun.COM 		*set = *fset;
1067*12273SCasper.Dik@Sun.COM 	}
1068*12273SCasper.Dik@Sun.COM out:
1069*12273SCasper.Dik@Sun.COM 	if (da.rbuf != (char *)&pmem)
1070*12273SCasper.Dik@Sun.COM 		kmem_free(da.rbuf, da.rsize);
1071*12273SCasper.Dik@Sun.COM out1:
1072*12273SCasper.Dik@Sun.COM 	kmem_free(pap, pasize);
1073*12273SCasper.Dik@Sun.COM 	klpd_rele(pfd);
1074*12273SCasper.Dik@Sun.COM 	return (err);
1075*12273SCasper.Dik@Sun.COM }
1076*12273SCasper.Dik@Sun.COM 
1077*12273SCasper.Dik@Sun.COM int
check_user_privs(const cred_t * cr,const priv_set_t * set)1078*12273SCasper.Dik@Sun.COM check_user_privs(const cred_t *cr, const priv_set_t *set)
1079*12273SCasper.Dik@Sun.COM {
1080*12273SCasper.Dik@Sun.COM 	klpd_reg_t *pfd;
1081*12273SCasper.Dik@Sun.COM 	pfexec_arg_t *pap;
1082*12273SCasper.Dik@Sun.COM 	door_arg_t da;
1083*12273SCasper.Dik@Sun.COM 	int dres;
1084*12273SCasper.Dik@Sun.COM 	int err = -1;
1085*12273SCasper.Dik@Sun.COM 	zone_t *myzone = crgetzone(cr);
1086*12273SCasper.Dik@Sun.COM 	size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t));
1087*12273SCasper.Dik@Sun.COM 	uint32_t res;
1088*12273SCasper.Dik@Sun.COM 
1089*12273SCasper.Dik@Sun.COM 	mutex_enter(&myzone->zone_lock);
1090*12273SCasper.Dik@Sun.COM 	if ((pfd = myzone->zone_pfexecd) != NULL)
1091*12273SCasper.Dik@Sun.COM 		klpd_hold(pfd);
1092*12273SCasper.Dik@Sun.COM 	mutex_exit(&myzone->zone_lock);
1093*12273SCasper.Dik@Sun.COM 
1094*12273SCasper.Dik@Sun.COM 	if (pfd == NULL)
1095*12273SCasper.Dik@Sun.COM 		return (-1);
1096*12273SCasper.Dik@Sun.COM 
1097*12273SCasper.Dik@Sun.COM 	if (pfd->klpd_door_pid == curproc->p_pid) {
1098*12273SCasper.Dik@Sun.COM 		klpd_rele(pfd);
1099*12273SCasper.Dik@Sun.COM 		return (0);
1100*12273SCasper.Dik@Sun.COM 	}
1101*12273SCasper.Dik@Sun.COM 
1102*12273SCasper.Dik@Sun.COM 	pap = kmem_zalloc(pasize, KM_SLEEP);
1103*12273SCasper.Dik@Sun.COM 
1104*12273SCasper.Dik@Sun.COM 	*(priv_set_t *)&pap->pfa_buf = *set;
1105*12273SCasper.Dik@Sun.COM 
1106*12273SCasper.Dik@Sun.COM 	pap->pfa_vers = PFEXEC_ARG_VERS;
1107*12273SCasper.Dik@Sun.COM 	pap->pfa_call = PFEXEC_USER_PRIVS;
1108*12273SCasper.Dik@Sun.COM 	pap->pfa_len = pasize;
1109*12273SCasper.Dik@Sun.COM 	pap->pfa_uid = crgetruid(cr);
1110*12273SCasper.Dik@Sun.COM 
1111*12273SCasper.Dik@Sun.COM 	da.data_ptr = (char *)pap;
1112*12273SCasper.Dik@Sun.COM 	da.data_size = pap->pfa_len;
1113*12273SCasper.Dik@Sun.COM 	da.desc_ptr = NULL;
1114*12273SCasper.Dik@Sun.COM 	da.desc_num = 0;
1115*12273SCasper.Dik@Sun.COM 	da.rbuf = (char *)&res;
1116*12273SCasper.Dik@Sun.COM 	da.rsize = sizeof (res);
1117*12273SCasper.Dik@Sun.COM 
1118*12273SCasper.Dik@Sun.COM 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1119*12273SCasper.Dik@Sun.COM 		switch (dres) {
1120*12273SCasper.Dik@Sun.COM 		case EAGAIN:
1121*12273SCasper.Dik@Sun.COM 			delay(1);
1122*12273SCasper.Dik@Sun.COM 			continue;
1123*12273SCasper.Dik@Sun.COM 		case EINVAL:
1124*12273SCasper.Dik@Sun.COM 		case EBADF:
1125*12273SCasper.Dik@Sun.COM 		case EINTR:
1126*12273SCasper.Dik@Sun.COM 		default:
1127*12273SCasper.Dik@Sun.COM 			goto out;
1128*12273SCasper.Dik@Sun.COM 		}
1129*12273SCasper.Dik@Sun.COM 	}
1130*12273SCasper.Dik@Sun.COM 
1131*12273SCasper.Dik@Sun.COM 	/*
1132*12273SCasper.Dik@Sun.COM 	 * Check the size of the result.
1133*12273SCasper.Dik@Sun.COM 	 */
1134*12273SCasper.Dik@Sun.COM 	if (da.rsize != sizeof (res))
1135*12273SCasper.Dik@Sun.COM 		goto out;
1136*12273SCasper.Dik@Sun.COM 
1137*12273SCasper.Dik@Sun.COM 	if (*(uint32_t *)da.rbuf == 1)
1138*12273SCasper.Dik@Sun.COM 		err = 0;
1139*12273SCasper.Dik@Sun.COM out:
1140*12273SCasper.Dik@Sun.COM 	if (da.rbuf != (char *)&res)
1141*12273SCasper.Dik@Sun.COM 		kmem_free(da.rbuf, da.rsize);
1142*12273SCasper.Dik@Sun.COM out1:
1143*12273SCasper.Dik@Sun.COM 	kmem_free(pap, pasize);
1144*12273SCasper.Dik@Sun.COM 	klpd_rele(pfd);
1145*12273SCasper.Dik@Sun.COM 	return (err);
1146*12273SCasper.Dik@Sun.COM }
1147