xref: /onnv-gate/usr/src/uts/common/os/klpd.c (revision 6997:056043f166c6)
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 /*
236134Scasper  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
246134Scasper  * Use is subject to license terms.
256134Scasper  */
266134Scasper 
276134Scasper #pragma ident	"%Z%%M%	%I%	%E% SMI"
286134Scasper 
296134Scasper #include <sys/atomic.h>
306134Scasper #include <sys/door.h>
316134Scasper #include <sys/proc.h>
326134Scasper #include <sys/cred_impl.h>
336134Scasper #include <sys/policy.h>
346134Scasper #include <sys/priv.h>
356134Scasper #include <sys/klpd.h>
366134Scasper #include <sys/errno.h>
376134Scasper #include <sys/kmem.h>
386134Scasper #include <sys/project.h>
396134Scasper #include <sys/systm.h>
406134Scasper #include <sys/sysmacros.h>
416134Scasper #include <sys/pathname.h>
426134Scasper #include <sys/varargs.h>
436134Scasper #include <sys/zone.h>
446134Scasper #include <netinet/in.h>
456134Scasper 
466134Scasper #define	ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
476134Scasper 
486134Scasper static kmutex_t klpd_mutex;
496134Scasper 
506134Scasper typedef struct klpd_reg {
516134Scasper 	struct klpd_reg *klpd_next;
526134Scasper 	struct klpd_reg **klpd_refp;
536134Scasper 	door_handle_t 	klpd_door;
546134Scasper 	pid_t		klpd_door_pid;
556134Scasper 	priv_set_t	klpd_pset;
566134Scasper 	cred_t		*klpd_cred;
576134Scasper 	int		klpd_indel;		/* Disabled */
586134Scasper 	uint32_t	klpd_ref;
596134Scasper } klpd_reg_t;
606134Scasper 
616134Scasper 
626134Scasper /*
636134Scasper  * This data structure hangs off the credential of a process; the
646134Scasper  * credential is finalized and cannot be changed; but this structure
656134Scasper  * can be changed when a new door server for the particular group
666134Scasper  * needs to be registered.  It is refcounted and shared between
676134Scasper  * processes with common ancestry.
686134Scasper  *
696134Scasper  * The reference count is atomically updated.
706134Scasper  *
716134Scasper  * But the registration probably needs to be updated under a lock.
726134Scasper  */
736134Scasper typedef struct credklpd {
746134Scasper 	kmutex_t	crkl_lock;
756134Scasper 	klpd_reg_t	*crkl_reg;
766134Scasper 	uint32_t	crkl_ref;
776134Scasper } credklpd_t;
786134Scasper 
796134Scasper klpd_reg_t *klpd_list;
806134Scasper 
816134Scasper static void klpd_unlink(klpd_reg_t *);
826134Scasper static int klpd_unreg_dh(door_handle_t);
836134Scasper 
846134Scasper static credklpd_t *crklpd_alloc(void);
856134Scasper 
866134Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *);
876134Scasper 
886134Scasper extern size_t max_vnode_path;
896134Scasper 
906134Scasper void
916134Scasper klpd_rele(klpd_reg_t *p)
926134Scasper {
936134Scasper 	if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
946134Scasper 		if (p->klpd_refp != NULL)
956134Scasper 			klpd_unlink(p);
966134Scasper 		if (p->klpd_cred != NULL)
976134Scasper 			crfree(p->klpd_cred);
986134Scasper 		door_ki_rele(p->klpd_door);
996134Scasper 		kmem_free(p, sizeof (*p));
1006134Scasper 	}
1016134Scasper }
1026134Scasper 
1036134Scasper /*
1046134Scasper  * In order to be able to walk the lists, we can't unlink the entry
1056134Scasper  * until the reference count drops to 0.  If we remove it too soon,
1066134Scasper  * list walkers will terminate when they happen to call a now orphaned
1076134Scasper  * entry.
1086134Scasper  */
1096134Scasper static klpd_reg_t *
1106134Scasper klpd_rele_next(klpd_reg_t *p)
1116134Scasper {
1126134Scasper 	klpd_reg_t *r = p->klpd_next;
1136134Scasper 
1146134Scasper 	klpd_rele(p);
1156134Scasper 	return (r);
1166134Scasper }
1176134Scasper 
1186134Scasper 
1196134Scasper static void
1206134Scasper klpd_hold(klpd_reg_t *p)
1216134Scasper {
1226134Scasper 	atomic_add_32(&p->klpd_ref, 1);
1236134Scasper }
1246134Scasper 
1256134Scasper /*
1266134Scasper  * Remove registration from where it is registered.  Returns next in list.
1276134Scasper  */
1286134Scasper static void
1296134Scasper klpd_unlink(klpd_reg_t *p)
1306134Scasper {
1316134Scasper 	ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
1326134Scasper 
1336134Scasper 	if (p->klpd_refp != NULL)
1346134Scasper 		*p->klpd_refp = p->klpd_next;
1356134Scasper 
1366134Scasper 	if (p->klpd_next != NULL)
1376134Scasper 		p->klpd_next->klpd_refp = p->klpd_refp;
1386134Scasper 	p->klpd_refp = NULL;
1396134Scasper }
1406134Scasper 
1416134Scasper /*
1426134Scasper  * Remove the head of the klpd list and decrement its refcnt.
1436134Scasper  * The lock guarding the list should be held; this function is
1446134Scasper  * called when we are sure we want to remove the entry from the
1456134Scasper  * list but not so sure that the reference count has dropped back to
1466134Scasper  * 1 and is specifically intended to remove the non-list variants.
1476134Scasper  */
1486134Scasper void
1496134Scasper klpd_remove(klpd_reg_t **pp)
1506134Scasper {
1516134Scasper 	klpd_reg_t *p = *pp;
1526134Scasper 	if (p == NULL)
1536134Scasper 		return;
1546134Scasper 	ASSERT(p->klpd_next == NULL);
1556134Scasper 	klpd_unlink(p);
1566134Scasper 	klpd_rele(p);
1576134Scasper }
1586134Scasper 
1596134Scasper /*
1606134Scasper  * Link new entry in list.  The Boolean argument specifies whether this
1616134Scasper  * list can contain only a single item or multiple items.
1626134Scasper  * Returns the entry which needs to be released if single is B_TRUE.
1636134Scasper  */
1646134Scasper static klpd_reg_t *
1656134Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
1666134Scasper {
1676134Scasper 	klpd_reg_t *old = *listp;
1686134Scasper 
1696134Scasper 	ASSERT(p->klpd_ref == 1);
1706134Scasper 
1716134Scasper 	ASSERT(old == NULL || *old->klpd_refp == old);
1726134Scasper 	p->klpd_refp = listp;
1736134Scasper 	p->klpd_next = single ? NULL : old;
1746134Scasper 	*listp = p;
1756134Scasper 	if (old != NULL) {
1766134Scasper 		if (single) {
1776134Scasper 			ASSERT(old->klpd_next == NULL);
1786134Scasper 			old->klpd_refp = NULL;
1796134Scasper 			return (old);
1806134Scasper 		} else
1816134Scasper 			old->klpd_refp = &p->klpd_next;
1826134Scasper 	}
1836134Scasper 	return (NULL);
1846134Scasper }
1856134Scasper 
1866134Scasper /*
1876134Scasper  * The typical call consists of:
1886134Scasper  *	- priv_set_t
1896134Scasper  *	- some integer data (type, value)
1906134Scasper  * for now, it's just one bit.
1916134Scasper  */
1926134Scasper static klpd_head_t *
1936134Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
1946134Scasper {
1956134Scasper 	char	*comp;
1966134Scasper 	uint_t	type;
1976134Scasper 	vnode_t *vp;
1986134Scasper 	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
1996134Scasper 	size_t	plen, clen;
2006134Scasper 	int	proto;
2016134Scasper 
2026134Scasper 	klpd_arg_t *kap = NULL;
2036134Scasper 	klpd_head_t *khp;
2046134Scasper 
2056134Scasper 	type = va_arg(ap, uint_t);
2066134Scasper 	switch (type) {
2076134Scasper 	case KLPDARG_NOMORE:
2086134Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
2096134Scasper 		khp->klh_argoff = 0;
2106134Scasper 		break;
2116134Scasper 	case KLPDARG_VNODE:
2126134Scasper 		len += offsetof(klpd_arg_t, kla_str);
2136134Scasper 		vp = va_arg(ap, vnode_t *);
2146134Scasper 		if (vp == NULL)
2156134Scasper 			return (NULL);
2166134Scasper 
2176134Scasper 		comp = va_arg(ap, char *);
2186134Scasper 
2196134Scasper 		if (comp != NULL && *comp != '\0')
2206134Scasper 			clen = strlen(comp) + 1;
2216134Scasper 		else
2226134Scasper 			clen = 0;
2236134Scasper 
2246134Scasper 		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
2256134Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
2266134Scasper 
2276134Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
2286134Scasper 		kap = KLH_ARG(khp);
2296134Scasper 
2306134Scasper 		if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
2316134Scasper 		    vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
2326134Scasper 			kmem_free(khp, len);
2336134Scasper 			return (NULL);
2346134Scasper 		}
2356134Scasper 		if (clen != 0) {
2366134Scasper 			plen = strlen(kap->kla_str);
2376134Scasper 			if (plen + clen + 1 >= MAXPATHLEN) {
2386134Scasper 				kmem_free(khp, len);
2396134Scasper 				return (NULL);
2406134Scasper 			}
2416134Scasper 			/* Don't make root into a double "/" */
2426134Scasper 			if (plen <= 2)
2436134Scasper 				plen = 0;
2446134Scasper 			kap->kla_str[plen] = '/';
2456134Scasper 			bcopy(comp, &kap->kla_str[plen + 1], clen);
2466134Scasper 		}
2476134Scasper 		break;
2486134Scasper 	case KLPDARG_PORT:
2496134Scasper 		proto = va_arg(ap, int);
2506134Scasper 		switch (proto) {
2516134Scasper 		case IPPROTO_TCP:	type = KLPDARG_TCPPORT;
2526134Scasper 					break;
2536134Scasper 		case IPPROTO_UDP:	type = KLPDARG_UDPPORT;
2546134Scasper 					break;
2556134Scasper 		case IPPROTO_SCTP:	type = KLPDARG_SCTPPORT;
2566134Scasper 					break;
2576134Scasper 		case PROTO_SDP:		type = KLPDARG_SDPPORT;
2586134Scasper 					break;
2596134Scasper 		}
2606134Scasper 		/* FALLTHROUGH */
2616134Scasper 	case KLPDARG_INT:
2626134Scasper 	case KLPDARG_TCPPORT:
2636134Scasper 	case KLPDARG_UDPPORT:
2646134Scasper 	case KLPDARG_SCTPPORT:
2656134Scasper 	case KLPDARG_SDPPORT:
2666134Scasper 		len += sizeof (*kap);
2676134Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
2686134Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
2696134Scasper 		kap = KLH_ARG(khp);
2706134Scasper 		kap->kla_int = va_arg(ap, int);
2716134Scasper 		break;
2726134Scasper 	default:
2736134Scasper 		return (NULL);
2746134Scasper 	}
2756134Scasper 	khp->klh_vers = KLPDCALL_VERS;
2766134Scasper 	khp->klh_len = len;
2776134Scasper 	khp->klh_privoff = sizeof (*khp);
2786134Scasper 	*KLH_PRIVSET(khp) = *rq;
2796134Scasper 	if (kap != NULL) {
2806134Scasper 		kap->kla_type = type;
2816134Scasper 		kap->kla_dlen = len - khp->klh_argoff;
2826134Scasper 	}
2836134Scasper 	return (khp);
2846134Scasper }
2856134Scasper 
2866134Scasper static int
2876134Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
2886134Scasper {
2896134Scasper 	door_arg_t da;
2906134Scasper 	int res;
2916134Scasper 	int dres;
2926134Scasper 	klpd_head_t *klh;
2936134Scasper 
2946134Scasper 	if (p->klpd_door_pid == curproc->p_pid)
2956134Scasper 		return (-1);
2966134Scasper 
2976134Scasper 	klh = klpd_marshall(p, req, ap);
2986134Scasper 
2996134Scasper 	if (klh == NULL)
3006134Scasper 		return (-1);
3016134Scasper 
3026134Scasper 	da.data_ptr = (char *)klh;
3036134Scasper 	da.data_size = klh->klh_len;
3046134Scasper 	da.desc_ptr = NULL;
3056134Scasper 	da.desc_num = 0;
3066134Scasper 	da.rbuf = (char *)&res;
3076134Scasper 	da.rsize = sizeof (res);
3086134Scasper 
309*6997Sjwadams 	while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
310*6997Sjwadams 	    SIZE_MAX, 0)) != 0) {
3116134Scasper 		switch (dres) {
3126134Scasper 		case EAGAIN:
3136134Scasper 			delay(1);
3146134Scasper 			continue;
3156134Scasper 		case EINVAL:
3166134Scasper 		case EBADF:
3176134Scasper 			/* Bad door, don't call it again. */
3186134Scasper 			(void) klpd_unreg_dh(p->klpd_door);
3196134Scasper 			/* FALLTHROUGH */
3206134Scasper 		case EINTR:
3216134Scasper 			/* Pending signal, nothing we can do. */
3226134Scasper 			/* FALLTHROUGH */
3236134Scasper 		default:
3246134Scasper 			kmem_free(klh, klh->klh_len);
3256134Scasper 			return (-1);
3266134Scasper 		}
3276134Scasper 	}
3286134Scasper 	kmem_free(klh, klh->klh_len);
3296134Scasper 	/* Bogus return value, must be a failure */
3306134Scasper 	if (da.rbuf != (char *)&res) {
3316134Scasper 		kmem_free(da.rbuf, da.rsize);
3326134Scasper 		return (-1);
3336134Scasper 	}
3346134Scasper 	return (res);
3356134Scasper }
3366134Scasper 
3376134Scasper uint32_t klpd_bad_locks;
3386134Scasper 
3396134Scasper int
3406134Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
3416134Scasper {
3426134Scasper 	klpd_reg_t *p;
3436134Scasper 	int rv = -1;
3446134Scasper 	credklpd_t *ckp;
3456134Scasper 	zone_t *ckzone;
3466134Scasper 
3476134Scasper 	/*
3486134Scasper 	 * These locks must not be held when this code is called;
3496134Scasper 	 * callbacks to userland with these locks held will result
3506134Scasper 	 * in issues.  That said, the code at the call sides was
3516134Scasper 	 * restructured not to call with any of the locks held and
3526134Scasper 	 * no policies operate by default on most processes.
3536134Scasper 	 */
3546134Scasper 	if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
3556134Scasper 	    mutex_owned(&curproc->p_crlock)) {
3566134Scasper 		atomic_add_32(&klpd_bad_locks, 1);
3576134Scasper 		return (-1);
3586134Scasper 	}
3596134Scasper 
3606134Scasper 	/*
3616134Scasper 	 * Enforce the limit set for the call process (still).
3626134Scasper 	 */
3636134Scasper 	if (!priv_issubset(req, &CR_LPRIV(cr)))
3646134Scasper 		return (-1);
3656134Scasper 
3666134Scasper 	/* Try 1: get the credential specific klpd */
3676134Scasper 	if ((ckp = crgetcrklpd(cr)) != NULL) {
3686134Scasper 		mutex_enter(&ckp->crkl_lock);
3696134Scasper 		if ((p = ckp->crkl_reg) != NULL &&
3706134Scasper 		    p->klpd_indel == 0 &&
3716134Scasper 		    priv_issubset(req, &p->klpd_pset)) {
3726134Scasper 			klpd_hold(p);
3736134Scasper 			mutex_exit(&ckp->crkl_lock);
3746134Scasper 			rv = klpd_do_call(p, req, ap);
3756134Scasper 			mutex_enter(&ckp->crkl_lock);
3766134Scasper 			klpd_rele(p);
3776134Scasper 			mutex_exit(&ckp->crkl_lock);
3786134Scasper 			if (rv != -1)
3796134Scasper 				return (rv == 0 ? 0 : -1);
3806134Scasper 		} else {
3816134Scasper 			mutex_exit(&ckp->crkl_lock);
3826134Scasper 		}
3836134Scasper 	}
3846134Scasper 
3856134Scasper 	/* Try 2: get the project specific klpd */
3866134Scasper 	mutex_enter(&klpd_mutex);
3876134Scasper 
3886134Scasper 	if ((p = curproj->kpj_klpd) != NULL) {
3896134Scasper 		klpd_hold(p);
3906134Scasper 		mutex_exit(&klpd_mutex);
3916134Scasper 		if (p->klpd_indel == 0 &&
3926134Scasper 		    priv_issubset(req, &p->klpd_pset)) {
3936134Scasper 			rv = klpd_do_call(p, req, ap);
3946134Scasper 		}
3956134Scasper 		mutex_enter(&klpd_mutex);
3966134Scasper 		klpd_rele(p);
3976134Scasper 		mutex_exit(&klpd_mutex);
3986134Scasper 
3996134Scasper 		if (rv != -1)
4006134Scasper 			return (rv == 0 ? 0 : -1);
4016134Scasper 	} else {
4026134Scasper 		mutex_exit(&klpd_mutex);
4036134Scasper 	}
4046134Scasper 
4056134Scasper 	/* Try 3: get the global klpd list */
4066134Scasper 	ckzone = crgetzone(cr);
4076134Scasper 	mutex_enter(&klpd_mutex);
4086134Scasper 
4096134Scasper 	for (p = klpd_list; p != NULL; ) {
4106134Scasper 		zone_t *kkzone = crgetzone(p->klpd_cred);
4116134Scasper 		if ((kkzone == &zone0 || kkzone == ckzone) &&
4126134Scasper 		    p->klpd_indel == 0 &&
4136134Scasper 		    priv_issubset(req, &p->klpd_pset)) {
4146134Scasper 			klpd_hold(p);
4156134Scasper 			mutex_exit(&klpd_mutex);
4166134Scasper 			rv = klpd_do_call(p, req, ap);
4176134Scasper 			mutex_enter(&klpd_mutex);
4186134Scasper 
4196134Scasper 			p = klpd_rele_next(p);
4206134Scasper 
4216134Scasper 			if (rv != -1)
4226134Scasper 				break;
4236134Scasper 		} else {
4246134Scasper 			p = p->klpd_next;
4256134Scasper 		}
4266134Scasper 	}
4276134Scasper 	mutex_exit(&klpd_mutex);
4286134Scasper 	return (rv == 0 ? 0 : -1);
4296134Scasper }
4306134Scasper 
4316134Scasper /*
4326134Scasper  * Register the klpd.
4336134Scasper  * If the pid_t passed in is positive, update the registration for
4346134Scasper  * the specific process; that is only possible if the process already
4356134Scasper  * has a registration on it.  This change of registration will affect
4366134Scasper  * all processes which share common ancestry.
4376134Scasper  *
4386134Scasper  * MY_PID (pid 0) can be used to create or change the context for
4396134Scasper  * the current process, typically done after fork().
4406134Scasper  *
4416134Scasper  * A negative value can be used to register a klpd globally.
4426134Scasper  *
4436134Scasper  * The per-credential klpd needs to be cleaned up when entering
4446134Scasper  * a zone or unsetting the flag.
4456134Scasper  */
4466134Scasper int
4476134Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
4486134Scasper {
4496134Scasper 	cred_t *cr = CRED();
4506134Scasper 	door_handle_t dh;
4516134Scasper 	klpd_reg_t *kpd;
4526134Scasper 	priv_set_t pset;
4536134Scasper 	door_info_t di;
4546134Scasper 	credklpd_t *ckp = NULL;
4556134Scasper 	pid_t pid = -1;
4566134Scasper 	projid_t proj = -1;
4576134Scasper 	kproject_t *kpp = NULL;
4586134Scasper 
4596134Scasper 	if (CR_FLAGS(cr) & PRIV_XPOLICY)
4606134Scasper 		return (set_errno(EINVAL));
4616134Scasper 
4626134Scasper 	if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
4636134Scasper 		return (set_errno(EFAULT));
4646134Scasper 
4656134Scasper 	if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
4666134Scasper 		return (set_errno(EPERM));
4676134Scasper 
4686134Scasper 	switch (type) {
4696134Scasper 	case P_PID:
4706134Scasper 		pid = (pid_t)id;
4716134Scasper 		if (pid == P_MYPID)
4726134Scasper 			pid = curproc->p_pid;
4736134Scasper 		if (pid == curproc->p_pid)
4746134Scasper 			ckp = crklpd_alloc();
4756134Scasper 		break;
4766134Scasper 	case P_PROJID:
4776134Scasper 		proj = (projid_t)id;
4786134Scasper 		kpp = project_hold_by_id(proj, crgetzone(cr),
4796134Scasper 		    PROJECT_HOLD_FIND);
4806134Scasper 		if (kpp == NULL)
4816134Scasper 			return (set_errno(ESRCH));
4826134Scasper 		break;
4836134Scasper 	default:
4846134Scasper 		return (set_errno(ENOTSUP));
4856134Scasper 	}
4866134Scasper 
4876134Scasper 
4886134Scasper 	/*
4896134Scasper 	 * Verify the door passed in; it must be a door and we won't
4906134Scasper 	 * allow processes to be called on their own behalf.
4916134Scasper 	 */
4926134Scasper 	dh = door_ki_lookup(did);
4936134Scasper 	if (dh == NULL || door_ki_info(dh, &di) != 0) {
4946134Scasper 		if (ckp != NULL)
4956134Scasper 			crklpd_rele(ckp);
4966134Scasper 		if (kpp != NULL)
4976134Scasper 			project_rele(kpp);
4986134Scasper 		return (set_errno(EBADF));
4996134Scasper 	}
5006134Scasper 	if (type == P_PID && pid == di.di_target) {
5016134Scasper 		if (ckp != NULL)
5026134Scasper 			crklpd_rele(ckp);
5036134Scasper 		ASSERT(kpp == NULL);
5046134Scasper 		return (set_errno(EINVAL));
5056134Scasper 	}
5066134Scasper 
5076134Scasper 	kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
5086134Scasper 	crhold(kpd->klpd_cred = cr);
5096134Scasper 	kpd->klpd_door = dh;
5106134Scasper 	kpd->klpd_door_pid = di.di_target;
5116134Scasper 	kpd->klpd_ref = 1;
5126134Scasper 	kpd->klpd_pset = pset;
5136134Scasper 
5146134Scasper 	if (kpp != NULL) {
5156134Scasper 		mutex_enter(&klpd_mutex);
5166134Scasper 		kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
5176134Scasper 		mutex_exit(&klpd_mutex);
5186134Scasper 		if (kpd != NULL)
5196134Scasper 			klpd_rele(kpd);
5206134Scasper 		project_rele(kpp);
5216134Scasper 	} else if ((int)pid < 0) {
5226134Scasper 		/* Global daemon */
5236134Scasper 		mutex_enter(&klpd_mutex);
5246134Scasper 		(void) klpd_link(kpd, &klpd_list, B_FALSE);
5256134Scasper 		mutex_exit(&klpd_mutex);
5266134Scasper 	} else if (pid == curproc->p_pid) {
5276134Scasper 		proc_t *p = curproc;
5286134Scasper 		cred_t *newcr = cralloc();
5296134Scasper 
5306134Scasper 		/* No need to lock, sole reference to ckp */
5316134Scasper 		kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
5326134Scasper 
5336134Scasper 		if (kpd != NULL)
5346134Scasper 			klpd_rele(kpd);
5356134Scasper 
5366134Scasper 		mutex_enter(&p->p_crlock);
5376134Scasper 		cr = p->p_cred;
5386134Scasper 		crdup_to(cr, newcr);
5396134Scasper 		crsetcrklpd(newcr, ckp);
5406134Scasper 		p->p_cred = newcr;	/* Already held for p_cred */
5416134Scasper 
5426134Scasper 		crhold(newcr);		/* Hold once for the current thread */
5436134Scasper 		mutex_exit(&p->p_crlock);
5446134Scasper 		crfree(cr);		/* One for the p_cred */
5456134Scasper 		crset(p, newcr);
5466134Scasper 	} else {
5476134Scasper 		proc_t *p;
5486134Scasper 		cred_t *pcr;
5496134Scasper 		mutex_enter(&pidlock);
5506134Scasper 		p = prfind(pid);
5516134Scasper 		if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
5526134Scasper 			mutex_exit(&pidlock);
5536134Scasper 			klpd_rele(kpd);
5546134Scasper 			return (set_errno(p == NULL ? ESRCH : EPERM));
5556134Scasper 		}
5566134Scasper 		mutex_enter(&p->p_crlock);
5576134Scasper 		crhold(pcr = p->p_cred);
5586134Scasper 		mutex_exit(&pidlock);
5596134Scasper 		mutex_exit(&p->p_crlock);
5606134Scasper 		/*
5616134Scasper 		 * We're going to update the credential's ckp in place;
5626134Scasper 		 * this requires that it exists.
5636134Scasper 		 */
5646134Scasper 		ckp = crgetcrklpd(pcr);
5656134Scasper 		if (ckp == NULL) {
5666134Scasper 			crfree(pcr);
5676134Scasper 			klpd_rele(kpd);
5686134Scasper 			return (set_errno(EINVAL));
5696134Scasper 		}
5706134Scasper 		crklpd_setreg(ckp, kpd);
5716134Scasper 		crfree(pcr);
5726134Scasper 	}
5736134Scasper 
5746134Scasper 	return (0);
5756134Scasper }
5766134Scasper 
5776134Scasper static int
5786134Scasper klpd_unreg_dh(door_handle_t dh)
5796134Scasper {
5806134Scasper 	klpd_reg_t *p;
5816134Scasper 
5826134Scasper 	mutex_enter(&klpd_mutex);
5836134Scasper 	for (p = klpd_list; p != NULL; p = p->klpd_next) {
5846134Scasper 		if (p->klpd_door == dh)
5856134Scasper 			break;
5866134Scasper 	}
5876134Scasper 	if (p == NULL) {
5886134Scasper 		mutex_exit(&klpd_mutex);
5896134Scasper 		return (EINVAL);
5906134Scasper 	}
5916134Scasper 	if (p->klpd_indel != 0) {
5926134Scasper 		mutex_exit(&klpd_mutex);
5936134Scasper 		return (EAGAIN);
5946134Scasper 	}
5956134Scasper 	p->klpd_indel = 1;
5966134Scasper 	klpd_rele(p);
5976134Scasper 	mutex_exit(&klpd_mutex);
5986134Scasper 	return (0);
5996134Scasper }
6006134Scasper 
6016134Scasper int
6026134Scasper klpd_unreg(int did, idtype_t type, id_t id)
6036134Scasper {
6046134Scasper 	door_handle_t dh;
6056134Scasper 	int res = 0;
6066134Scasper 	proc_t *p;
6076134Scasper 	pid_t pid;
6086134Scasper 	projid_t proj;
6096134Scasper 	kproject_t *kpp = NULL;
6106134Scasper 	credklpd_t *ckp;
6116134Scasper 
6126134Scasper 	switch (type) {
6136134Scasper 	case P_PID:
6146134Scasper 		pid = (pid_t)id;
6156134Scasper 		break;
6166134Scasper 	case P_PROJID:
6176134Scasper 		proj = (projid_t)id;
6186134Scasper 		kpp = project_hold_by_id(proj, crgetzone(CRED()),
6196134Scasper 		    PROJECT_HOLD_FIND);
6206134Scasper 		if (kpp == NULL)
6216134Scasper 			return (set_errno(ESRCH));
6226134Scasper 		break;
6236134Scasper 	default:
6246134Scasper 		return (set_errno(ENOTSUP));
6256134Scasper 	}
6266134Scasper 
6276134Scasper 	dh = door_ki_lookup(did);
6286134Scasper 	if (dh == NULL) {
6296134Scasper 		if (kpp != NULL)
6306134Scasper 			project_rele(kpp);
6316134Scasper 		return (set_errno(EINVAL));
6326134Scasper 	}
6336134Scasper 
6346134Scasper 	if (kpp != NULL) {
6356134Scasper 		mutex_enter(&klpd_mutex);
6366134Scasper 		if (kpp->kpj_klpd == NULL)
6376134Scasper 			res = ESRCH;
6386134Scasper 		else
6396134Scasper 			klpd_remove(&kpp->kpj_klpd);
6406134Scasper 		mutex_exit(&klpd_mutex);
6416134Scasper 		project_rele(kpp);
6426134Scasper 		goto out;
6436134Scasper 	} else if ((int)pid > 0) {
6446134Scasper 		mutex_enter(&pidlock);
6456134Scasper 		p = prfind(pid);
6466134Scasper 		if (p == NULL) {
6476134Scasper 			mutex_exit(&pidlock);
6486134Scasper 			door_ki_rele(dh);
6496134Scasper 			return (set_errno(ESRCH));
6506134Scasper 		}
6516134Scasper 		mutex_enter(&p->p_crlock);
6526134Scasper 		mutex_exit(&pidlock);
6536134Scasper 	} else if (pid == 0) {
6546134Scasper 		p = curproc;
6556134Scasper 		mutex_enter(&p->p_crlock);
6566134Scasper 	} else {
6576134Scasper 		res = klpd_unreg_dh(dh);
6586134Scasper 		goto out;
6596134Scasper 	}
6606134Scasper 
6616134Scasper 	ckp = crgetcrklpd(p->p_cred);
6626134Scasper 	if (ckp != NULL) {
6636134Scasper 		crklpd_setreg(ckp, NULL);
6646134Scasper 	} else {
6656134Scasper 		res = ESRCH;
6666134Scasper 	}
6676134Scasper 	mutex_exit(&p->p_crlock);
6686134Scasper 
6696134Scasper out:
6706134Scasper 	door_ki_rele(dh);
6716134Scasper 
6726134Scasper 	if (res != 0)
6736134Scasper 		return (set_errno(res));
6746134Scasper 	return (0);
6756134Scasper }
6766134Scasper 
6776134Scasper void
6786134Scasper crklpd_hold(credklpd_t *crkpd)
6796134Scasper {
6806134Scasper 	atomic_add_32(&crkpd->crkl_ref, 1);
6816134Scasper }
6826134Scasper 
6836134Scasper void
6846134Scasper crklpd_rele(credklpd_t *crkpd)
6856134Scasper {
6866134Scasper 	if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
6876134Scasper 		if (crkpd->crkl_reg != NULL)
6886134Scasper 			klpd_rele(crkpd->crkl_reg);
6896134Scasper 		mutex_destroy(&crkpd->crkl_lock);
6906134Scasper 		kmem_free(crkpd, sizeof (*crkpd));
6916134Scasper 	}
6926134Scasper }
6936134Scasper 
6946134Scasper static credklpd_t *
6956134Scasper crklpd_alloc(void)
6966134Scasper {
6976134Scasper 	credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
6986134Scasper 
6996134Scasper 	mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
7006134Scasper 	res->crkl_ref = 1;
7016134Scasper 	res->crkl_reg = NULL;
7026134Scasper 
7036134Scasper 	return (res);
7046134Scasper }
7056134Scasper 
7066134Scasper void
7076134Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
7086134Scasper {
7096134Scasper 	klpd_reg_t *old;
7106134Scasper 
7116134Scasper 	mutex_enter(&crk->crkl_lock);
7126134Scasper 	if (new == NULL) {
7136134Scasper 		old = crk->crkl_reg;
7146134Scasper 		if (old != NULL)
7156134Scasper 			klpd_unlink(old);
7166134Scasper 	} else {
7176134Scasper 		old = klpd_link(new, &crk->crkl_reg, B_TRUE);
7186134Scasper 	}
7196134Scasper 	mutex_exit(&crk->crkl_lock);
7206134Scasper 
7216134Scasper 	if (old != NULL)
7226134Scasper 		klpd_rele(old);
7236134Scasper }
724