xref: /titanic_53/usr/src/uts/common/os/klpd.c (revision ddf7fe95b8ad67aa16deb427a0b78f4dd4ff22b1)
1*ddf7fe95Scasper /*
2*ddf7fe95Scasper  * CDDL HEADER START
3*ddf7fe95Scasper  *
4*ddf7fe95Scasper  * The contents of this file are subject to the terms of the
5*ddf7fe95Scasper  * Common Development and Distribution License (the "License").
6*ddf7fe95Scasper  * You may not use this file except in compliance with the License.
7*ddf7fe95Scasper  *
8*ddf7fe95Scasper  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*ddf7fe95Scasper  * or http://www.opensolaris.org/os/licensing.
10*ddf7fe95Scasper  * See the License for the specific language governing permissions
11*ddf7fe95Scasper  * and limitations under the License.
12*ddf7fe95Scasper  *
13*ddf7fe95Scasper  * When distributing Covered Code, include this CDDL HEADER in each
14*ddf7fe95Scasper  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*ddf7fe95Scasper  * If applicable, add the following below this CDDL HEADER, with the
16*ddf7fe95Scasper  * fields enclosed by brackets "[]" replaced with your own identifying
17*ddf7fe95Scasper  * information: Portions Copyright [yyyy] [name of copyright owner]
18*ddf7fe95Scasper  *
19*ddf7fe95Scasper  * CDDL HEADER END
20*ddf7fe95Scasper  */
21*ddf7fe95Scasper 
22*ddf7fe95Scasper /*
23*ddf7fe95Scasper  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*ddf7fe95Scasper  * Use is subject to license terms.
25*ddf7fe95Scasper  */
26*ddf7fe95Scasper 
27*ddf7fe95Scasper #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*ddf7fe95Scasper 
29*ddf7fe95Scasper #include <sys/atomic.h>
30*ddf7fe95Scasper #include <sys/door.h>
31*ddf7fe95Scasper #include <sys/proc.h>
32*ddf7fe95Scasper #include <sys/cred_impl.h>
33*ddf7fe95Scasper #include <sys/policy.h>
34*ddf7fe95Scasper #include <sys/priv.h>
35*ddf7fe95Scasper #include <sys/klpd.h>
36*ddf7fe95Scasper #include <sys/errno.h>
37*ddf7fe95Scasper #include <sys/kmem.h>
38*ddf7fe95Scasper #include <sys/project.h>
39*ddf7fe95Scasper #include <sys/systm.h>
40*ddf7fe95Scasper #include <sys/sysmacros.h>
41*ddf7fe95Scasper #include <sys/pathname.h>
42*ddf7fe95Scasper #include <sys/varargs.h>
43*ddf7fe95Scasper #include <sys/zone.h>
44*ddf7fe95Scasper #include <netinet/in.h>
45*ddf7fe95Scasper 
46*ddf7fe95Scasper #define	ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
47*ddf7fe95Scasper 
48*ddf7fe95Scasper static kmutex_t klpd_mutex;
49*ddf7fe95Scasper 
50*ddf7fe95Scasper typedef struct klpd_reg {
51*ddf7fe95Scasper 	struct klpd_reg *klpd_next;
52*ddf7fe95Scasper 	struct klpd_reg **klpd_refp;
53*ddf7fe95Scasper 	door_handle_t 	klpd_door;
54*ddf7fe95Scasper 	pid_t		klpd_door_pid;
55*ddf7fe95Scasper 	priv_set_t	klpd_pset;
56*ddf7fe95Scasper 	cred_t		*klpd_cred;
57*ddf7fe95Scasper 	int		klpd_indel;		/* Disabled */
58*ddf7fe95Scasper 	uint32_t	klpd_ref;
59*ddf7fe95Scasper } klpd_reg_t;
60*ddf7fe95Scasper 
61*ddf7fe95Scasper 
62*ddf7fe95Scasper /*
63*ddf7fe95Scasper  * This data structure hangs off the credential of a process; the
64*ddf7fe95Scasper  * credential is finalized and cannot be changed; but this structure
65*ddf7fe95Scasper  * can be changed when a new door server for the particular group
66*ddf7fe95Scasper  * needs to be registered.  It is refcounted and shared between
67*ddf7fe95Scasper  * processes with common ancestry.
68*ddf7fe95Scasper  *
69*ddf7fe95Scasper  * The reference count is atomically updated.
70*ddf7fe95Scasper  *
71*ddf7fe95Scasper  * But the registration probably needs to be updated under a lock.
72*ddf7fe95Scasper  */
73*ddf7fe95Scasper typedef struct credklpd {
74*ddf7fe95Scasper 	kmutex_t	crkl_lock;
75*ddf7fe95Scasper 	klpd_reg_t	*crkl_reg;
76*ddf7fe95Scasper 	uint32_t	crkl_ref;
77*ddf7fe95Scasper } credklpd_t;
78*ddf7fe95Scasper 
79*ddf7fe95Scasper klpd_reg_t *klpd_list;
80*ddf7fe95Scasper 
81*ddf7fe95Scasper static void klpd_unlink(klpd_reg_t *);
82*ddf7fe95Scasper static int klpd_unreg_dh(door_handle_t);
83*ddf7fe95Scasper 
84*ddf7fe95Scasper static credklpd_t *crklpd_alloc(void);
85*ddf7fe95Scasper 
86*ddf7fe95Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *);
87*ddf7fe95Scasper 
88*ddf7fe95Scasper extern size_t max_vnode_path;
89*ddf7fe95Scasper 
90*ddf7fe95Scasper void
91*ddf7fe95Scasper klpd_rele(klpd_reg_t *p)
92*ddf7fe95Scasper {
93*ddf7fe95Scasper 	if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
94*ddf7fe95Scasper 		if (p->klpd_refp != NULL)
95*ddf7fe95Scasper 			klpd_unlink(p);
96*ddf7fe95Scasper 		if (p->klpd_cred != NULL)
97*ddf7fe95Scasper 			crfree(p->klpd_cred);
98*ddf7fe95Scasper 		door_ki_rele(p->klpd_door);
99*ddf7fe95Scasper 		kmem_free(p, sizeof (*p));
100*ddf7fe95Scasper 	}
101*ddf7fe95Scasper }
102*ddf7fe95Scasper 
103*ddf7fe95Scasper /*
104*ddf7fe95Scasper  * In order to be able to walk the lists, we can't unlink the entry
105*ddf7fe95Scasper  * until the reference count drops to 0.  If we remove it too soon,
106*ddf7fe95Scasper  * list walkers will terminate when they happen to call a now orphaned
107*ddf7fe95Scasper  * entry.
108*ddf7fe95Scasper  */
109*ddf7fe95Scasper static klpd_reg_t *
110*ddf7fe95Scasper klpd_rele_next(klpd_reg_t *p)
111*ddf7fe95Scasper {
112*ddf7fe95Scasper 	klpd_reg_t *r = p->klpd_next;
113*ddf7fe95Scasper 
114*ddf7fe95Scasper 	klpd_rele(p);
115*ddf7fe95Scasper 	return (r);
116*ddf7fe95Scasper }
117*ddf7fe95Scasper 
118*ddf7fe95Scasper 
119*ddf7fe95Scasper static void
120*ddf7fe95Scasper klpd_hold(klpd_reg_t *p)
121*ddf7fe95Scasper {
122*ddf7fe95Scasper 	atomic_add_32(&p->klpd_ref, 1);
123*ddf7fe95Scasper }
124*ddf7fe95Scasper 
125*ddf7fe95Scasper /*
126*ddf7fe95Scasper  * Remove registration from where it is registered.  Returns next in list.
127*ddf7fe95Scasper  */
128*ddf7fe95Scasper static void
129*ddf7fe95Scasper klpd_unlink(klpd_reg_t *p)
130*ddf7fe95Scasper {
131*ddf7fe95Scasper 	ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
132*ddf7fe95Scasper 
133*ddf7fe95Scasper 	if (p->klpd_refp != NULL)
134*ddf7fe95Scasper 		*p->klpd_refp = p->klpd_next;
135*ddf7fe95Scasper 
136*ddf7fe95Scasper 	if (p->klpd_next != NULL)
137*ddf7fe95Scasper 		p->klpd_next->klpd_refp = p->klpd_refp;
138*ddf7fe95Scasper 	p->klpd_refp = NULL;
139*ddf7fe95Scasper }
140*ddf7fe95Scasper 
141*ddf7fe95Scasper /*
142*ddf7fe95Scasper  * Remove the head of the klpd list and decrement its refcnt.
143*ddf7fe95Scasper  * The lock guarding the list should be held; this function is
144*ddf7fe95Scasper  * called when we are sure we want to remove the entry from the
145*ddf7fe95Scasper  * list but not so sure that the reference count has dropped back to
146*ddf7fe95Scasper  * 1 and is specifically intended to remove the non-list variants.
147*ddf7fe95Scasper  */
148*ddf7fe95Scasper void
149*ddf7fe95Scasper klpd_remove(klpd_reg_t **pp)
150*ddf7fe95Scasper {
151*ddf7fe95Scasper 	klpd_reg_t *p = *pp;
152*ddf7fe95Scasper 	if (p == NULL)
153*ddf7fe95Scasper 		return;
154*ddf7fe95Scasper 	ASSERT(p->klpd_next == NULL);
155*ddf7fe95Scasper 	klpd_unlink(p);
156*ddf7fe95Scasper 	klpd_rele(p);
157*ddf7fe95Scasper }
158*ddf7fe95Scasper 
159*ddf7fe95Scasper /*
160*ddf7fe95Scasper  * Link new entry in list.  The Boolean argument specifies whether this
161*ddf7fe95Scasper  * list can contain only a single item or multiple items.
162*ddf7fe95Scasper  * Returns the entry which needs to be released if single is B_TRUE.
163*ddf7fe95Scasper  */
164*ddf7fe95Scasper static klpd_reg_t *
165*ddf7fe95Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
166*ddf7fe95Scasper {
167*ddf7fe95Scasper 	klpd_reg_t *old = *listp;
168*ddf7fe95Scasper 
169*ddf7fe95Scasper 	ASSERT(p->klpd_ref == 1);
170*ddf7fe95Scasper 
171*ddf7fe95Scasper 	ASSERT(old == NULL || *old->klpd_refp == old);
172*ddf7fe95Scasper 	p->klpd_refp = listp;
173*ddf7fe95Scasper 	p->klpd_next = single ? NULL : old;
174*ddf7fe95Scasper 	*listp = p;
175*ddf7fe95Scasper 	if (old != NULL) {
176*ddf7fe95Scasper 		if (single) {
177*ddf7fe95Scasper 			ASSERT(old->klpd_next == NULL);
178*ddf7fe95Scasper 			old->klpd_refp = NULL;
179*ddf7fe95Scasper 			return (old);
180*ddf7fe95Scasper 		} else
181*ddf7fe95Scasper 			old->klpd_refp = &p->klpd_next;
182*ddf7fe95Scasper 	}
183*ddf7fe95Scasper 	return (NULL);
184*ddf7fe95Scasper }
185*ddf7fe95Scasper 
186*ddf7fe95Scasper /*
187*ddf7fe95Scasper  * The typical call consists of:
188*ddf7fe95Scasper  *	- priv_set_t
189*ddf7fe95Scasper  *	- some integer data (type, value)
190*ddf7fe95Scasper  * for now, it's just one bit.
191*ddf7fe95Scasper  */
192*ddf7fe95Scasper static klpd_head_t *
193*ddf7fe95Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
194*ddf7fe95Scasper {
195*ddf7fe95Scasper 	char	*comp;
196*ddf7fe95Scasper 	uint_t	type;
197*ddf7fe95Scasper 	vnode_t *vp;
198*ddf7fe95Scasper 	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
199*ddf7fe95Scasper 	size_t	plen, clen;
200*ddf7fe95Scasper 	int	proto;
201*ddf7fe95Scasper 
202*ddf7fe95Scasper 	klpd_arg_t *kap = NULL;
203*ddf7fe95Scasper 	klpd_head_t *khp;
204*ddf7fe95Scasper 
205*ddf7fe95Scasper 	type = va_arg(ap, uint_t);
206*ddf7fe95Scasper 	switch (type) {
207*ddf7fe95Scasper 	case KLPDARG_NOMORE:
208*ddf7fe95Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
209*ddf7fe95Scasper 		khp->klh_argoff = 0;
210*ddf7fe95Scasper 		break;
211*ddf7fe95Scasper 	case KLPDARG_VNODE:
212*ddf7fe95Scasper 		len += offsetof(klpd_arg_t, kla_str);
213*ddf7fe95Scasper 		vp = va_arg(ap, vnode_t *);
214*ddf7fe95Scasper 		if (vp == NULL)
215*ddf7fe95Scasper 			return (NULL);
216*ddf7fe95Scasper 
217*ddf7fe95Scasper 		comp = va_arg(ap, char *);
218*ddf7fe95Scasper 
219*ddf7fe95Scasper 		if (comp != NULL && *comp != '\0')
220*ddf7fe95Scasper 			clen = strlen(comp) + 1;
221*ddf7fe95Scasper 		else
222*ddf7fe95Scasper 			clen = 0;
223*ddf7fe95Scasper 
224*ddf7fe95Scasper 		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
225*ddf7fe95Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
226*ddf7fe95Scasper 
227*ddf7fe95Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
228*ddf7fe95Scasper 		kap = KLH_ARG(khp);
229*ddf7fe95Scasper 
230*ddf7fe95Scasper 		if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
231*ddf7fe95Scasper 		    vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
232*ddf7fe95Scasper 			kmem_free(khp, len);
233*ddf7fe95Scasper 			return (NULL);
234*ddf7fe95Scasper 		}
235*ddf7fe95Scasper 		if (clen != 0) {
236*ddf7fe95Scasper 			plen = strlen(kap->kla_str);
237*ddf7fe95Scasper 			if (plen + clen + 1 >= MAXPATHLEN) {
238*ddf7fe95Scasper 				kmem_free(khp, len);
239*ddf7fe95Scasper 				return (NULL);
240*ddf7fe95Scasper 			}
241*ddf7fe95Scasper 			/* Don't make root into a double "/" */
242*ddf7fe95Scasper 			if (plen <= 2)
243*ddf7fe95Scasper 				plen = 0;
244*ddf7fe95Scasper 			kap->kla_str[plen] = '/';
245*ddf7fe95Scasper 			bcopy(comp, &kap->kla_str[plen + 1], clen);
246*ddf7fe95Scasper 		}
247*ddf7fe95Scasper 		break;
248*ddf7fe95Scasper 	case KLPDARG_PORT:
249*ddf7fe95Scasper 		proto = va_arg(ap, int);
250*ddf7fe95Scasper 		switch (proto) {
251*ddf7fe95Scasper 		case IPPROTO_TCP:	type = KLPDARG_TCPPORT;
252*ddf7fe95Scasper 					break;
253*ddf7fe95Scasper 		case IPPROTO_UDP:	type = KLPDARG_UDPPORT;
254*ddf7fe95Scasper 					break;
255*ddf7fe95Scasper 		case IPPROTO_SCTP:	type = KLPDARG_SCTPPORT;
256*ddf7fe95Scasper 					break;
257*ddf7fe95Scasper 		case PROTO_SDP:		type = KLPDARG_SDPPORT;
258*ddf7fe95Scasper 					break;
259*ddf7fe95Scasper 		}
260*ddf7fe95Scasper 		/* FALLTHROUGH */
261*ddf7fe95Scasper 	case KLPDARG_INT:
262*ddf7fe95Scasper 	case KLPDARG_TCPPORT:
263*ddf7fe95Scasper 	case KLPDARG_UDPPORT:
264*ddf7fe95Scasper 	case KLPDARG_SCTPPORT:
265*ddf7fe95Scasper 	case KLPDARG_SDPPORT:
266*ddf7fe95Scasper 		len += sizeof (*kap);
267*ddf7fe95Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
268*ddf7fe95Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
269*ddf7fe95Scasper 		kap = KLH_ARG(khp);
270*ddf7fe95Scasper 		kap->kla_int = va_arg(ap, int);
271*ddf7fe95Scasper 		break;
272*ddf7fe95Scasper 	default:
273*ddf7fe95Scasper 		return (NULL);
274*ddf7fe95Scasper 	}
275*ddf7fe95Scasper 	khp->klh_vers = KLPDCALL_VERS;
276*ddf7fe95Scasper 	khp->klh_len = len;
277*ddf7fe95Scasper 	khp->klh_privoff = sizeof (*khp);
278*ddf7fe95Scasper 	*KLH_PRIVSET(khp) = *rq;
279*ddf7fe95Scasper 	if (kap != NULL) {
280*ddf7fe95Scasper 		kap->kla_type = type;
281*ddf7fe95Scasper 		kap->kla_dlen = len - khp->klh_argoff;
282*ddf7fe95Scasper 	}
283*ddf7fe95Scasper 	return (khp);
284*ddf7fe95Scasper }
285*ddf7fe95Scasper 
286*ddf7fe95Scasper static int
287*ddf7fe95Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
288*ddf7fe95Scasper {
289*ddf7fe95Scasper 	door_arg_t da;
290*ddf7fe95Scasper 	int res;
291*ddf7fe95Scasper 	int dres;
292*ddf7fe95Scasper 	klpd_head_t *klh;
293*ddf7fe95Scasper 
294*ddf7fe95Scasper 	if (p->klpd_door_pid == curproc->p_pid)
295*ddf7fe95Scasper 		return (-1);
296*ddf7fe95Scasper 
297*ddf7fe95Scasper 	klh = klpd_marshall(p, req, ap);
298*ddf7fe95Scasper 
299*ddf7fe95Scasper 	if (klh == NULL)
300*ddf7fe95Scasper 		return (-1);
301*ddf7fe95Scasper 
302*ddf7fe95Scasper 	da.data_ptr = (char *)klh;
303*ddf7fe95Scasper 	da.data_size = klh->klh_len;
304*ddf7fe95Scasper 	da.desc_ptr = NULL;
305*ddf7fe95Scasper 	da.desc_num = 0;
306*ddf7fe95Scasper 	da.rbuf = (char *)&res;
307*ddf7fe95Scasper 	da.rsize = sizeof (res);
308*ddf7fe95Scasper 
309*ddf7fe95Scasper 	while ((dres = door_ki_upcall(p->klpd_door, &da)) != 0) {
310*ddf7fe95Scasper 		switch (dres) {
311*ddf7fe95Scasper 		case EAGAIN:
312*ddf7fe95Scasper 			delay(1);
313*ddf7fe95Scasper 			continue;
314*ddf7fe95Scasper 		case EINVAL:
315*ddf7fe95Scasper 		case EBADF:
316*ddf7fe95Scasper 			/* Bad door, don't call it again. */
317*ddf7fe95Scasper 			(void) klpd_unreg_dh(p->klpd_door);
318*ddf7fe95Scasper 			/* FALLTHROUGH */
319*ddf7fe95Scasper 		case EINTR:
320*ddf7fe95Scasper 			/* Pending signal, nothing we can do. */
321*ddf7fe95Scasper 			/* FALLTHROUGH */
322*ddf7fe95Scasper 		default:
323*ddf7fe95Scasper 			kmem_free(klh, klh->klh_len);
324*ddf7fe95Scasper 			return (-1);
325*ddf7fe95Scasper 		}
326*ddf7fe95Scasper 	}
327*ddf7fe95Scasper 	kmem_free(klh, klh->klh_len);
328*ddf7fe95Scasper 	/* Bogus return value, must be a failure */
329*ddf7fe95Scasper 	if (da.rbuf != (char *)&res) {
330*ddf7fe95Scasper 		kmem_free(da.rbuf, da.rsize);
331*ddf7fe95Scasper 		return (-1);
332*ddf7fe95Scasper 	}
333*ddf7fe95Scasper 	return (res);
334*ddf7fe95Scasper }
335*ddf7fe95Scasper 
336*ddf7fe95Scasper uint32_t klpd_bad_locks;
337*ddf7fe95Scasper 
338*ddf7fe95Scasper int
339*ddf7fe95Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
340*ddf7fe95Scasper {
341*ddf7fe95Scasper 	klpd_reg_t *p;
342*ddf7fe95Scasper 	int rv = -1;
343*ddf7fe95Scasper 	credklpd_t *ckp;
344*ddf7fe95Scasper 	zone_t *ckzone;
345*ddf7fe95Scasper 
346*ddf7fe95Scasper 	/*
347*ddf7fe95Scasper 	 * These locks must not be held when this code is called;
348*ddf7fe95Scasper 	 * callbacks to userland with these locks held will result
349*ddf7fe95Scasper 	 * in issues.  That said, the code at the call sides was
350*ddf7fe95Scasper 	 * restructured not to call with any of the locks held and
351*ddf7fe95Scasper 	 * no policies operate by default on most processes.
352*ddf7fe95Scasper 	 */
353*ddf7fe95Scasper 	if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
354*ddf7fe95Scasper 	    mutex_owned(&curproc->p_crlock)) {
355*ddf7fe95Scasper 		atomic_add_32(&klpd_bad_locks, 1);
356*ddf7fe95Scasper 		return (-1);
357*ddf7fe95Scasper 	}
358*ddf7fe95Scasper 
359*ddf7fe95Scasper 	/*
360*ddf7fe95Scasper 	 * Enforce the limit set for the call process (still).
361*ddf7fe95Scasper 	 */
362*ddf7fe95Scasper 	if (!priv_issubset(req, &CR_LPRIV(cr)))
363*ddf7fe95Scasper 		return (-1);
364*ddf7fe95Scasper 
365*ddf7fe95Scasper 	/* Try 1: get the credential specific klpd */
366*ddf7fe95Scasper 	if ((ckp = crgetcrklpd(cr)) != NULL) {
367*ddf7fe95Scasper 		mutex_enter(&ckp->crkl_lock);
368*ddf7fe95Scasper 		if ((p = ckp->crkl_reg) != NULL &&
369*ddf7fe95Scasper 		    p->klpd_indel == 0 &&
370*ddf7fe95Scasper 		    priv_issubset(req, &p->klpd_pset)) {
371*ddf7fe95Scasper 			klpd_hold(p);
372*ddf7fe95Scasper 			mutex_exit(&ckp->crkl_lock);
373*ddf7fe95Scasper 			rv = klpd_do_call(p, req, ap);
374*ddf7fe95Scasper 			mutex_enter(&ckp->crkl_lock);
375*ddf7fe95Scasper 			klpd_rele(p);
376*ddf7fe95Scasper 			mutex_exit(&ckp->crkl_lock);
377*ddf7fe95Scasper 			if (rv != -1)
378*ddf7fe95Scasper 				return (rv == 0 ? 0 : -1);
379*ddf7fe95Scasper 		} else {
380*ddf7fe95Scasper 			mutex_exit(&ckp->crkl_lock);
381*ddf7fe95Scasper 		}
382*ddf7fe95Scasper 	}
383*ddf7fe95Scasper 
384*ddf7fe95Scasper 	/* Try 2: get the project specific klpd */
385*ddf7fe95Scasper 	mutex_enter(&klpd_mutex);
386*ddf7fe95Scasper 
387*ddf7fe95Scasper 	if ((p = curproj->kpj_klpd) != NULL) {
388*ddf7fe95Scasper 		klpd_hold(p);
389*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
390*ddf7fe95Scasper 		if (p->klpd_indel == 0 &&
391*ddf7fe95Scasper 		    priv_issubset(req, &p->klpd_pset)) {
392*ddf7fe95Scasper 			rv = klpd_do_call(p, req, ap);
393*ddf7fe95Scasper 		}
394*ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
395*ddf7fe95Scasper 		klpd_rele(p);
396*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
397*ddf7fe95Scasper 
398*ddf7fe95Scasper 		if (rv != -1)
399*ddf7fe95Scasper 			return (rv == 0 ? 0 : -1);
400*ddf7fe95Scasper 	} else {
401*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
402*ddf7fe95Scasper 	}
403*ddf7fe95Scasper 
404*ddf7fe95Scasper 	/* Try 3: get the global klpd list */
405*ddf7fe95Scasper 	ckzone = crgetzone(cr);
406*ddf7fe95Scasper 	mutex_enter(&klpd_mutex);
407*ddf7fe95Scasper 
408*ddf7fe95Scasper 	for (p = klpd_list; p != NULL; ) {
409*ddf7fe95Scasper 		zone_t *kkzone = crgetzone(p->klpd_cred);
410*ddf7fe95Scasper 		if ((kkzone == &zone0 || kkzone == ckzone) &&
411*ddf7fe95Scasper 		    p->klpd_indel == 0 &&
412*ddf7fe95Scasper 		    priv_issubset(req, &p->klpd_pset)) {
413*ddf7fe95Scasper 			klpd_hold(p);
414*ddf7fe95Scasper 			mutex_exit(&klpd_mutex);
415*ddf7fe95Scasper 			rv = klpd_do_call(p, req, ap);
416*ddf7fe95Scasper 			mutex_enter(&klpd_mutex);
417*ddf7fe95Scasper 
418*ddf7fe95Scasper 			p = klpd_rele_next(p);
419*ddf7fe95Scasper 
420*ddf7fe95Scasper 			if (rv != -1)
421*ddf7fe95Scasper 				break;
422*ddf7fe95Scasper 		} else {
423*ddf7fe95Scasper 			p = p->klpd_next;
424*ddf7fe95Scasper 		}
425*ddf7fe95Scasper 	}
426*ddf7fe95Scasper 	mutex_exit(&klpd_mutex);
427*ddf7fe95Scasper 	return (rv == 0 ? 0 : -1);
428*ddf7fe95Scasper }
429*ddf7fe95Scasper 
430*ddf7fe95Scasper /*
431*ddf7fe95Scasper  * Register the klpd.
432*ddf7fe95Scasper  * If the pid_t passed in is positive, update the registration for
433*ddf7fe95Scasper  * the specific process; that is only possible if the process already
434*ddf7fe95Scasper  * has a registration on it.  This change of registration will affect
435*ddf7fe95Scasper  * all processes which share common ancestry.
436*ddf7fe95Scasper  *
437*ddf7fe95Scasper  * MY_PID (pid 0) can be used to create or change the context for
438*ddf7fe95Scasper  * the current process, typically done after fork().
439*ddf7fe95Scasper  *
440*ddf7fe95Scasper  * A negative value can be used to register a klpd globally.
441*ddf7fe95Scasper  *
442*ddf7fe95Scasper  * The per-credential klpd needs to be cleaned up when entering
443*ddf7fe95Scasper  * a zone or unsetting the flag.
444*ddf7fe95Scasper  */
445*ddf7fe95Scasper int
446*ddf7fe95Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
447*ddf7fe95Scasper {
448*ddf7fe95Scasper 	cred_t *cr = CRED();
449*ddf7fe95Scasper 	door_handle_t dh;
450*ddf7fe95Scasper 	klpd_reg_t *kpd;
451*ddf7fe95Scasper 	priv_set_t pset;
452*ddf7fe95Scasper 	door_info_t di;
453*ddf7fe95Scasper 	credklpd_t *ckp = NULL;
454*ddf7fe95Scasper 	pid_t pid = -1;
455*ddf7fe95Scasper 	projid_t proj = -1;
456*ddf7fe95Scasper 	kproject_t *kpp = NULL;
457*ddf7fe95Scasper 
458*ddf7fe95Scasper 	if (CR_FLAGS(cr) & PRIV_XPOLICY)
459*ddf7fe95Scasper 		return (set_errno(EINVAL));
460*ddf7fe95Scasper 
461*ddf7fe95Scasper 	if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
462*ddf7fe95Scasper 		return (set_errno(EFAULT));
463*ddf7fe95Scasper 
464*ddf7fe95Scasper 	if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
465*ddf7fe95Scasper 		return (set_errno(EPERM));
466*ddf7fe95Scasper 
467*ddf7fe95Scasper 	switch (type) {
468*ddf7fe95Scasper 	case P_PID:
469*ddf7fe95Scasper 		pid = (pid_t)id;
470*ddf7fe95Scasper 		if (pid == P_MYPID)
471*ddf7fe95Scasper 			pid = curproc->p_pid;
472*ddf7fe95Scasper 		if (pid == curproc->p_pid)
473*ddf7fe95Scasper 			ckp = crklpd_alloc();
474*ddf7fe95Scasper 		break;
475*ddf7fe95Scasper 	case P_PROJID:
476*ddf7fe95Scasper 		proj = (projid_t)id;
477*ddf7fe95Scasper 		kpp = project_hold_by_id(proj, crgetzone(cr),
478*ddf7fe95Scasper 		    PROJECT_HOLD_FIND);
479*ddf7fe95Scasper 		if (kpp == NULL)
480*ddf7fe95Scasper 			return (set_errno(ESRCH));
481*ddf7fe95Scasper 		break;
482*ddf7fe95Scasper 	default:
483*ddf7fe95Scasper 		return (set_errno(ENOTSUP));
484*ddf7fe95Scasper 	}
485*ddf7fe95Scasper 
486*ddf7fe95Scasper 
487*ddf7fe95Scasper 	/*
488*ddf7fe95Scasper 	 * Verify the door passed in; it must be a door and we won't
489*ddf7fe95Scasper 	 * allow processes to be called on their own behalf.
490*ddf7fe95Scasper 	 */
491*ddf7fe95Scasper 	dh = door_ki_lookup(did);
492*ddf7fe95Scasper 	if (dh == NULL || door_ki_info(dh, &di) != 0) {
493*ddf7fe95Scasper 		if (ckp != NULL)
494*ddf7fe95Scasper 			crklpd_rele(ckp);
495*ddf7fe95Scasper 		if (kpp != NULL)
496*ddf7fe95Scasper 			project_rele(kpp);
497*ddf7fe95Scasper 		return (set_errno(EBADF));
498*ddf7fe95Scasper 	}
499*ddf7fe95Scasper 	if (type == P_PID && pid == di.di_target) {
500*ddf7fe95Scasper 		if (ckp != NULL)
501*ddf7fe95Scasper 			crklpd_rele(ckp);
502*ddf7fe95Scasper 		ASSERT(kpp == NULL);
503*ddf7fe95Scasper 		return (set_errno(EINVAL));
504*ddf7fe95Scasper 	}
505*ddf7fe95Scasper 
506*ddf7fe95Scasper 	kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
507*ddf7fe95Scasper 	crhold(kpd->klpd_cred = cr);
508*ddf7fe95Scasper 	kpd->klpd_door = dh;
509*ddf7fe95Scasper 	kpd->klpd_door_pid = di.di_target;
510*ddf7fe95Scasper 	kpd->klpd_ref = 1;
511*ddf7fe95Scasper 	kpd->klpd_pset = pset;
512*ddf7fe95Scasper 
513*ddf7fe95Scasper 	if (kpp != NULL) {
514*ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
515*ddf7fe95Scasper 		kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
516*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
517*ddf7fe95Scasper 		if (kpd != NULL)
518*ddf7fe95Scasper 			klpd_rele(kpd);
519*ddf7fe95Scasper 		project_rele(kpp);
520*ddf7fe95Scasper 	} else if ((int)pid < 0) {
521*ddf7fe95Scasper 		/* Global daemon */
522*ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
523*ddf7fe95Scasper 		(void) klpd_link(kpd, &klpd_list, B_FALSE);
524*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
525*ddf7fe95Scasper 	} else if (pid == curproc->p_pid) {
526*ddf7fe95Scasper 		proc_t *p = curproc;
527*ddf7fe95Scasper 		cred_t *newcr = cralloc();
528*ddf7fe95Scasper 
529*ddf7fe95Scasper 		/* No need to lock, sole reference to ckp */
530*ddf7fe95Scasper 		kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
531*ddf7fe95Scasper 
532*ddf7fe95Scasper 		if (kpd != NULL)
533*ddf7fe95Scasper 			klpd_rele(kpd);
534*ddf7fe95Scasper 
535*ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
536*ddf7fe95Scasper 		cr = p->p_cred;
537*ddf7fe95Scasper 		crdup_to(cr, newcr);
538*ddf7fe95Scasper 		crsetcrklpd(newcr, ckp);
539*ddf7fe95Scasper 		p->p_cred = newcr;	/* Already held for p_cred */
540*ddf7fe95Scasper 
541*ddf7fe95Scasper 		crhold(newcr);		/* Hold once for the current thread */
542*ddf7fe95Scasper 		mutex_exit(&p->p_crlock);
543*ddf7fe95Scasper 		crfree(cr);		/* One for the p_cred */
544*ddf7fe95Scasper 		crset(p, newcr);
545*ddf7fe95Scasper 	} else {
546*ddf7fe95Scasper 		proc_t *p;
547*ddf7fe95Scasper 		cred_t *pcr;
548*ddf7fe95Scasper 		mutex_enter(&pidlock);
549*ddf7fe95Scasper 		p = prfind(pid);
550*ddf7fe95Scasper 		if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
551*ddf7fe95Scasper 			mutex_exit(&pidlock);
552*ddf7fe95Scasper 			klpd_rele(kpd);
553*ddf7fe95Scasper 			return (set_errno(p == NULL ? ESRCH : EPERM));
554*ddf7fe95Scasper 		}
555*ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
556*ddf7fe95Scasper 		crhold(pcr = p->p_cred);
557*ddf7fe95Scasper 		mutex_exit(&pidlock);
558*ddf7fe95Scasper 		mutex_exit(&p->p_crlock);
559*ddf7fe95Scasper 		/*
560*ddf7fe95Scasper 		 * We're going to update the credential's ckp in place;
561*ddf7fe95Scasper 		 * this requires that it exists.
562*ddf7fe95Scasper 		 */
563*ddf7fe95Scasper 		ckp = crgetcrklpd(pcr);
564*ddf7fe95Scasper 		if (ckp == NULL) {
565*ddf7fe95Scasper 			crfree(pcr);
566*ddf7fe95Scasper 			klpd_rele(kpd);
567*ddf7fe95Scasper 			return (set_errno(EINVAL));
568*ddf7fe95Scasper 		}
569*ddf7fe95Scasper 		crklpd_setreg(ckp, kpd);
570*ddf7fe95Scasper 		crfree(pcr);
571*ddf7fe95Scasper 	}
572*ddf7fe95Scasper 
573*ddf7fe95Scasper 	return (0);
574*ddf7fe95Scasper }
575*ddf7fe95Scasper 
576*ddf7fe95Scasper static int
577*ddf7fe95Scasper klpd_unreg_dh(door_handle_t dh)
578*ddf7fe95Scasper {
579*ddf7fe95Scasper 	klpd_reg_t *p;
580*ddf7fe95Scasper 
581*ddf7fe95Scasper 	mutex_enter(&klpd_mutex);
582*ddf7fe95Scasper 	for (p = klpd_list; p != NULL; p = p->klpd_next) {
583*ddf7fe95Scasper 		if (p->klpd_door == dh)
584*ddf7fe95Scasper 			break;
585*ddf7fe95Scasper 	}
586*ddf7fe95Scasper 	if (p == NULL) {
587*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
588*ddf7fe95Scasper 		return (EINVAL);
589*ddf7fe95Scasper 	}
590*ddf7fe95Scasper 	if (p->klpd_indel != 0) {
591*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
592*ddf7fe95Scasper 		return (EAGAIN);
593*ddf7fe95Scasper 	}
594*ddf7fe95Scasper 	p->klpd_indel = 1;
595*ddf7fe95Scasper 	klpd_rele(p);
596*ddf7fe95Scasper 	mutex_exit(&klpd_mutex);
597*ddf7fe95Scasper 	return (0);
598*ddf7fe95Scasper }
599*ddf7fe95Scasper 
600*ddf7fe95Scasper int
601*ddf7fe95Scasper klpd_unreg(int did, idtype_t type, id_t id)
602*ddf7fe95Scasper {
603*ddf7fe95Scasper 	door_handle_t dh;
604*ddf7fe95Scasper 	int res = 0;
605*ddf7fe95Scasper 	proc_t *p;
606*ddf7fe95Scasper 	pid_t pid;
607*ddf7fe95Scasper 	projid_t proj;
608*ddf7fe95Scasper 	kproject_t *kpp = NULL;
609*ddf7fe95Scasper 	credklpd_t *ckp;
610*ddf7fe95Scasper 
611*ddf7fe95Scasper 	switch (type) {
612*ddf7fe95Scasper 	case P_PID:
613*ddf7fe95Scasper 		pid = (pid_t)id;
614*ddf7fe95Scasper 		break;
615*ddf7fe95Scasper 	case P_PROJID:
616*ddf7fe95Scasper 		proj = (projid_t)id;
617*ddf7fe95Scasper 		kpp = project_hold_by_id(proj, crgetzone(CRED()),
618*ddf7fe95Scasper 		    PROJECT_HOLD_FIND);
619*ddf7fe95Scasper 		if (kpp == NULL)
620*ddf7fe95Scasper 			return (set_errno(ESRCH));
621*ddf7fe95Scasper 		break;
622*ddf7fe95Scasper 	default:
623*ddf7fe95Scasper 		return (set_errno(ENOTSUP));
624*ddf7fe95Scasper 	}
625*ddf7fe95Scasper 
626*ddf7fe95Scasper 	dh = door_ki_lookup(did);
627*ddf7fe95Scasper 	if (dh == NULL) {
628*ddf7fe95Scasper 		if (kpp != NULL)
629*ddf7fe95Scasper 			project_rele(kpp);
630*ddf7fe95Scasper 		return (set_errno(EINVAL));
631*ddf7fe95Scasper 	}
632*ddf7fe95Scasper 
633*ddf7fe95Scasper 	if (kpp != NULL) {
634*ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
635*ddf7fe95Scasper 		if (kpp->kpj_klpd == NULL)
636*ddf7fe95Scasper 			res = ESRCH;
637*ddf7fe95Scasper 		else
638*ddf7fe95Scasper 			klpd_remove(&kpp->kpj_klpd);
639*ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
640*ddf7fe95Scasper 		project_rele(kpp);
641*ddf7fe95Scasper 		goto out;
642*ddf7fe95Scasper 	} else if ((int)pid > 0) {
643*ddf7fe95Scasper 		mutex_enter(&pidlock);
644*ddf7fe95Scasper 		p = prfind(pid);
645*ddf7fe95Scasper 		if (p == NULL) {
646*ddf7fe95Scasper 			mutex_exit(&pidlock);
647*ddf7fe95Scasper 			door_ki_rele(dh);
648*ddf7fe95Scasper 			return (set_errno(ESRCH));
649*ddf7fe95Scasper 		}
650*ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
651*ddf7fe95Scasper 		mutex_exit(&pidlock);
652*ddf7fe95Scasper 	} else if (pid == 0) {
653*ddf7fe95Scasper 		p = curproc;
654*ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
655*ddf7fe95Scasper 	} else {
656*ddf7fe95Scasper 		res = klpd_unreg_dh(dh);
657*ddf7fe95Scasper 		goto out;
658*ddf7fe95Scasper 	}
659*ddf7fe95Scasper 
660*ddf7fe95Scasper 	ckp = crgetcrklpd(p->p_cred);
661*ddf7fe95Scasper 	if (ckp != NULL) {
662*ddf7fe95Scasper 		crklpd_setreg(ckp, NULL);
663*ddf7fe95Scasper 	} else {
664*ddf7fe95Scasper 		res = ESRCH;
665*ddf7fe95Scasper 	}
666*ddf7fe95Scasper 	mutex_exit(&p->p_crlock);
667*ddf7fe95Scasper 
668*ddf7fe95Scasper out:
669*ddf7fe95Scasper 	door_ki_rele(dh);
670*ddf7fe95Scasper 
671*ddf7fe95Scasper 	if (res != 0)
672*ddf7fe95Scasper 		return (set_errno(res));
673*ddf7fe95Scasper 	return (0);
674*ddf7fe95Scasper }
675*ddf7fe95Scasper 
676*ddf7fe95Scasper void
677*ddf7fe95Scasper crklpd_hold(credklpd_t *crkpd)
678*ddf7fe95Scasper {
679*ddf7fe95Scasper 	atomic_add_32(&crkpd->crkl_ref, 1);
680*ddf7fe95Scasper }
681*ddf7fe95Scasper 
682*ddf7fe95Scasper void
683*ddf7fe95Scasper crklpd_rele(credklpd_t *crkpd)
684*ddf7fe95Scasper {
685*ddf7fe95Scasper 	if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
686*ddf7fe95Scasper 		if (crkpd->crkl_reg != NULL)
687*ddf7fe95Scasper 			klpd_rele(crkpd->crkl_reg);
688*ddf7fe95Scasper 		mutex_destroy(&crkpd->crkl_lock);
689*ddf7fe95Scasper 		kmem_free(crkpd, sizeof (*crkpd));
690*ddf7fe95Scasper 	}
691*ddf7fe95Scasper }
692*ddf7fe95Scasper 
693*ddf7fe95Scasper static credklpd_t *
694*ddf7fe95Scasper crklpd_alloc(void)
695*ddf7fe95Scasper {
696*ddf7fe95Scasper 	credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
697*ddf7fe95Scasper 
698*ddf7fe95Scasper 	mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
699*ddf7fe95Scasper 	res->crkl_ref = 1;
700*ddf7fe95Scasper 	res->crkl_reg = NULL;
701*ddf7fe95Scasper 
702*ddf7fe95Scasper 	return (res);
703*ddf7fe95Scasper }
704*ddf7fe95Scasper 
705*ddf7fe95Scasper void
706*ddf7fe95Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
707*ddf7fe95Scasper {
708*ddf7fe95Scasper 	klpd_reg_t *old;
709*ddf7fe95Scasper 
710*ddf7fe95Scasper 	mutex_enter(&crk->crkl_lock);
711*ddf7fe95Scasper 	if (new == NULL) {
712*ddf7fe95Scasper 		old = crk->crkl_reg;
713*ddf7fe95Scasper 		if (old != NULL)
714*ddf7fe95Scasper 			klpd_unlink(old);
715*ddf7fe95Scasper 	} else {
716*ddf7fe95Scasper 		old = klpd_link(new, &crk->crkl_reg, B_TRUE);
717*ddf7fe95Scasper 	}
718*ddf7fe95Scasper 	mutex_exit(&crk->crkl_lock);
719*ddf7fe95Scasper 
720*ddf7fe95Scasper 	if (old != NULL)
721*ddf7fe95Scasper 		klpd_rele(old);
722*ddf7fe95Scasper }
723