xref: /illumos-gate/usr/src/uts/common/os/klpd.c (revision 134a1f4e3289b54e0f980e9cf05352e419a60bee)
1ddf7fe95Scasper /*
2ddf7fe95Scasper  * CDDL HEADER START
3ddf7fe95Scasper  *
4ddf7fe95Scasper  * The contents of this file are subject to the terms of the
5ddf7fe95Scasper  * Common Development and Distribution License (the "License").
6ddf7fe95Scasper  * You may not use this file except in compliance with the License.
7ddf7fe95Scasper  *
8ddf7fe95Scasper  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ddf7fe95Scasper  * or http://www.opensolaris.org/os/licensing.
10ddf7fe95Scasper  * See the License for the specific language governing permissions
11ddf7fe95Scasper  * and limitations under the License.
12ddf7fe95Scasper  *
13ddf7fe95Scasper  * When distributing Covered Code, include this CDDL HEADER in each
14ddf7fe95Scasper  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ddf7fe95Scasper  * If applicable, add the following below this CDDL HEADER, with the
16ddf7fe95Scasper  * fields enclosed by brackets "[]" replaced with your own identifying
17ddf7fe95Scasper  * information: Portions Copyright [yyyy] [name of copyright owner]
18ddf7fe95Scasper  *
19ddf7fe95Scasper  * CDDL HEADER END
20ddf7fe95Scasper  */
21ddf7fe95Scasper 
22ddf7fe95Scasper /*
23*134a1f4eSCasper H.S. Dik  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24ddf7fe95Scasper  */
25ddf7fe95Scasper 
26ddf7fe95Scasper #include <sys/atomic.h>
27ddf7fe95Scasper #include <sys/door.h>
28ddf7fe95Scasper #include <sys/proc.h>
29ddf7fe95Scasper #include <sys/cred_impl.h>
30ddf7fe95Scasper #include <sys/policy.h>
31ddf7fe95Scasper #include <sys/priv.h>
32ddf7fe95Scasper #include <sys/klpd.h>
33ddf7fe95Scasper #include <sys/errno.h>
34ddf7fe95Scasper #include <sys/kmem.h>
35ddf7fe95Scasper #include <sys/project.h>
36ddf7fe95Scasper #include <sys/systm.h>
37ddf7fe95Scasper #include <sys/sysmacros.h>
38ddf7fe95Scasper #include <sys/pathname.h>
39ddf7fe95Scasper #include <sys/varargs.h>
40ddf7fe95Scasper #include <sys/zone.h>
41ddf7fe95Scasper #include <netinet/in.h>
42ddf7fe95Scasper 
43ddf7fe95Scasper #define	ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
44ddf7fe95Scasper 
45ddf7fe95Scasper static kmutex_t klpd_mutex;
46ddf7fe95Scasper 
47ddf7fe95Scasper typedef struct klpd_reg {
48ddf7fe95Scasper 	struct klpd_reg *klpd_next;
49ddf7fe95Scasper 	struct klpd_reg **klpd_refp;
50ddf7fe95Scasper 	door_handle_t 	klpd_door;
51ddf7fe95Scasper 	pid_t		klpd_door_pid;
52ddf7fe95Scasper 	priv_set_t	klpd_pset;
53ddf7fe95Scasper 	cred_t		*klpd_cred;
54ddf7fe95Scasper 	int		klpd_indel;		/* Disabled */
55ddf7fe95Scasper 	uint32_t	klpd_ref;
56ddf7fe95Scasper } klpd_reg_t;
57ddf7fe95Scasper 
58ddf7fe95Scasper 
59ddf7fe95Scasper /*
60ddf7fe95Scasper  * This data structure hangs off the credential of a process; the
61ddf7fe95Scasper  * credential is finalized and cannot be changed; but this structure
62ddf7fe95Scasper  * can be changed when a new door server for the particular group
63ddf7fe95Scasper  * needs to be registered.  It is refcounted and shared between
64ddf7fe95Scasper  * processes with common ancestry.
65ddf7fe95Scasper  *
66ddf7fe95Scasper  * The reference count is atomically updated.
67ddf7fe95Scasper  *
68ddf7fe95Scasper  * But the registration probably needs to be updated under a lock.
69ddf7fe95Scasper  */
70ddf7fe95Scasper typedef struct credklpd {
71ddf7fe95Scasper 	kmutex_t	crkl_lock;
72ddf7fe95Scasper 	klpd_reg_t	*crkl_reg;
73ddf7fe95Scasper 	uint32_t	crkl_ref;
74ddf7fe95Scasper } credklpd_t;
75ddf7fe95Scasper 
76ddf7fe95Scasper klpd_reg_t *klpd_list;
77ddf7fe95Scasper 
78ddf7fe95Scasper static void klpd_unlink(klpd_reg_t *);
79ddf7fe95Scasper static int klpd_unreg_dh(door_handle_t);
80ddf7fe95Scasper 
81ddf7fe95Scasper static credklpd_t *crklpd_alloc(void);
82ddf7fe95Scasper 
83ddf7fe95Scasper void crklpd_setreg(credklpd_t *, klpd_reg_t *);
84ddf7fe95Scasper 
85ddf7fe95Scasper extern size_t max_vnode_path;
86ddf7fe95Scasper 
87ddf7fe95Scasper void
88ddf7fe95Scasper klpd_rele(klpd_reg_t *p)
89ddf7fe95Scasper {
90ddf7fe95Scasper 	if (atomic_add_32_nv(&p->klpd_ref, -1) == 0) {
91ddf7fe95Scasper 		if (p->klpd_refp != NULL)
92ddf7fe95Scasper 			klpd_unlink(p);
93ddf7fe95Scasper 		if (p->klpd_cred != NULL)
94ddf7fe95Scasper 			crfree(p->klpd_cred);
95ddf7fe95Scasper 		door_ki_rele(p->klpd_door);
96ddf7fe95Scasper 		kmem_free(p, sizeof (*p));
97ddf7fe95Scasper 	}
98ddf7fe95Scasper }
99ddf7fe95Scasper 
100ddf7fe95Scasper /*
101ddf7fe95Scasper  * In order to be able to walk the lists, we can't unlink the entry
102ddf7fe95Scasper  * until the reference count drops to 0.  If we remove it too soon,
103ddf7fe95Scasper  * list walkers will terminate when they happen to call a now orphaned
104ddf7fe95Scasper  * entry.
105ddf7fe95Scasper  */
106ddf7fe95Scasper static klpd_reg_t *
107ddf7fe95Scasper klpd_rele_next(klpd_reg_t *p)
108ddf7fe95Scasper {
109ddf7fe95Scasper 	klpd_reg_t *r = p->klpd_next;
110ddf7fe95Scasper 
111ddf7fe95Scasper 	klpd_rele(p);
112ddf7fe95Scasper 	return (r);
113ddf7fe95Scasper }
114ddf7fe95Scasper 
115ddf7fe95Scasper 
116ddf7fe95Scasper static void
117ddf7fe95Scasper klpd_hold(klpd_reg_t *p)
118ddf7fe95Scasper {
119ddf7fe95Scasper 	atomic_add_32(&p->klpd_ref, 1);
120ddf7fe95Scasper }
121ddf7fe95Scasper 
122ddf7fe95Scasper /*
123ddf7fe95Scasper  * Remove registration from where it is registered.  Returns next in list.
124ddf7fe95Scasper  */
125ddf7fe95Scasper static void
126ddf7fe95Scasper klpd_unlink(klpd_reg_t *p)
127ddf7fe95Scasper {
128ddf7fe95Scasper 	ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
129ddf7fe95Scasper 
130ddf7fe95Scasper 	if (p->klpd_refp != NULL)
131ddf7fe95Scasper 		*p->klpd_refp = p->klpd_next;
132ddf7fe95Scasper 
133ddf7fe95Scasper 	if (p->klpd_next != NULL)
134ddf7fe95Scasper 		p->klpd_next->klpd_refp = p->klpd_refp;
135ddf7fe95Scasper 	p->klpd_refp = NULL;
136ddf7fe95Scasper }
137ddf7fe95Scasper 
138ddf7fe95Scasper /*
139*134a1f4eSCasper H.S. Dik  * Remove all elements of the klpd list and decrement their refcnts.
140ddf7fe95Scasper  * The lock guarding the list should be held; this function is
141*134a1f4eSCasper H.S. Dik  * called when we are sure we want to destroy the list completely
142*134a1f4eSCasper H.S. Dik  * list but not so sure that the reference counts of all elements have
143*134a1f4eSCasper H.S. Dik  * dropped back to 1.
144ddf7fe95Scasper  */
145ddf7fe95Scasper void
146*134a1f4eSCasper H.S. Dik klpd_freelist(klpd_reg_t **pp)
147ddf7fe95Scasper {
148*134a1f4eSCasper H.S. Dik 	klpd_reg_t *p;
149*134a1f4eSCasper H.S. Dik 
150*134a1f4eSCasper H.S. Dik 	while ((p = *pp) != NULL) {
151*134a1f4eSCasper H.S. Dik 		klpd_unlink(p);
152*134a1f4eSCasper H.S. Dik 		klpd_rele(p);
153*134a1f4eSCasper H.S. Dik 	}
154ddf7fe95Scasper }
155ddf7fe95Scasper 
156ddf7fe95Scasper /*
157ddf7fe95Scasper  * Link new entry in list.  The Boolean argument specifies whether this
158ddf7fe95Scasper  * list can contain only a single item or multiple items.
159ddf7fe95Scasper  * Returns the entry which needs to be released if single is B_TRUE.
160ddf7fe95Scasper  */
161ddf7fe95Scasper static klpd_reg_t *
162ddf7fe95Scasper klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
163ddf7fe95Scasper {
164ddf7fe95Scasper 	klpd_reg_t *old = *listp;
165ddf7fe95Scasper 
166ddf7fe95Scasper 	ASSERT(p->klpd_ref == 1);
167ddf7fe95Scasper 
168ddf7fe95Scasper 	ASSERT(old == NULL || *old->klpd_refp == old);
169ddf7fe95Scasper 	p->klpd_refp = listp;
170ddf7fe95Scasper 	p->klpd_next = single ? NULL : old;
171ddf7fe95Scasper 	*listp = p;
172ddf7fe95Scasper 	if (old != NULL) {
173ddf7fe95Scasper 		if (single) {
174ddf7fe95Scasper 			ASSERT(old->klpd_next == NULL);
175ddf7fe95Scasper 			old->klpd_refp = NULL;
176ddf7fe95Scasper 			return (old);
177ddf7fe95Scasper 		} else
178ddf7fe95Scasper 			old->klpd_refp = &p->klpd_next;
179ddf7fe95Scasper 	}
180ddf7fe95Scasper 	return (NULL);
181ddf7fe95Scasper }
182ddf7fe95Scasper 
183ddf7fe95Scasper /*
184ddf7fe95Scasper  * The typical call consists of:
185ddf7fe95Scasper  *	- priv_set_t
186ddf7fe95Scasper  *	- some integer data (type, value)
187ddf7fe95Scasper  * for now, it's just one bit.
188ddf7fe95Scasper  */
189ddf7fe95Scasper static klpd_head_t *
190ddf7fe95Scasper klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
191ddf7fe95Scasper {
192*134a1f4eSCasper H.S. Dik 	char	*tmp;
193ddf7fe95Scasper 	uint_t	type;
194ddf7fe95Scasper 	vnode_t *vp;
195ddf7fe95Scasper 	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
196ddf7fe95Scasper 	size_t	plen, clen;
197ddf7fe95Scasper 	int	proto;
198ddf7fe95Scasper 
199ddf7fe95Scasper 	klpd_arg_t *kap = NULL;
200ddf7fe95Scasper 	klpd_head_t *khp;
201ddf7fe95Scasper 
202ddf7fe95Scasper 	type = va_arg(ap, uint_t);
203ddf7fe95Scasper 	switch (type) {
204ddf7fe95Scasper 	case KLPDARG_NOMORE:
205ddf7fe95Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
206ddf7fe95Scasper 		khp->klh_argoff = 0;
207ddf7fe95Scasper 		break;
208ddf7fe95Scasper 	case KLPDARG_VNODE:
209ddf7fe95Scasper 		len += offsetof(klpd_arg_t, kla_str);
210ddf7fe95Scasper 		vp = va_arg(ap, vnode_t *);
211ddf7fe95Scasper 		if (vp == NULL)
212ddf7fe95Scasper 			return (NULL);
213ddf7fe95Scasper 
214*134a1f4eSCasper H.S. Dik 		tmp = va_arg(ap, char *);
215ddf7fe95Scasper 
216*134a1f4eSCasper H.S. Dik 		if (tmp != NULL && *tmp != '\0')
217*134a1f4eSCasper H.S. Dik 			clen = strlen(tmp) + 1;
218ddf7fe95Scasper 		else
219ddf7fe95Scasper 			clen = 0;
220ddf7fe95Scasper 
221ddf7fe95Scasper 		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
222ddf7fe95Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
223ddf7fe95Scasper 
224ddf7fe95Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
225ddf7fe95Scasper 		kap = KLH_ARG(khp);
226ddf7fe95Scasper 
227ddf7fe95Scasper 		if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
228ddf7fe95Scasper 		    vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
229ddf7fe95Scasper 			kmem_free(khp, len);
230ddf7fe95Scasper 			return (NULL);
231ddf7fe95Scasper 		}
232ddf7fe95Scasper 		if (clen != 0) {
233ddf7fe95Scasper 			plen = strlen(kap->kla_str);
234ddf7fe95Scasper 			if (plen + clen + 1 >= MAXPATHLEN) {
235ddf7fe95Scasper 				kmem_free(khp, len);
236ddf7fe95Scasper 				return (NULL);
237ddf7fe95Scasper 			}
238ddf7fe95Scasper 			/* Don't make root into a double "/" */
239ddf7fe95Scasper 			if (plen <= 2)
240ddf7fe95Scasper 				plen = 0;
241ddf7fe95Scasper 			kap->kla_str[plen] = '/';
242*134a1f4eSCasper H.S. Dik 			bcopy(tmp, &kap->kla_str[plen + 1], clen);
243ddf7fe95Scasper 		}
244ddf7fe95Scasper 		break;
245ddf7fe95Scasper 	case KLPDARG_PORT:
246ddf7fe95Scasper 		proto = va_arg(ap, int);
247ddf7fe95Scasper 		switch (proto) {
248ddf7fe95Scasper 		case IPPROTO_TCP:	type = KLPDARG_TCPPORT;
249ddf7fe95Scasper 					break;
250ddf7fe95Scasper 		case IPPROTO_UDP:	type = KLPDARG_UDPPORT;
251ddf7fe95Scasper 					break;
252ddf7fe95Scasper 		case IPPROTO_SCTP:	type = KLPDARG_SCTPPORT;
253ddf7fe95Scasper 					break;
254ddf7fe95Scasper 		case PROTO_SDP:		type = KLPDARG_SDPPORT;
255ddf7fe95Scasper 					break;
256ddf7fe95Scasper 		}
257ddf7fe95Scasper 		/* FALLTHROUGH */
258ddf7fe95Scasper 	case KLPDARG_INT:
259ddf7fe95Scasper 	case KLPDARG_TCPPORT:
260ddf7fe95Scasper 	case KLPDARG_UDPPORT:
261ddf7fe95Scasper 	case KLPDARG_SCTPPORT:
262ddf7fe95Scasper 	case KLPDARG_SDPPORT:
263ddf7fe95Scasper 		len += sizeof (*kap);
264ddf7fe95Scasper 		khp = kmem_zalloc(len, KM_SLEEP);
265ddf7fe95Scasper 		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
266ddf7fe95Scasper 		kap = KLH_ARG(khp);
267ddf7fe95Scasper 		kap->kla_int = va_arg(ap, int);
268ddf7fe95Scasper 		break;
269ddf7fe95Scasper 	default:
270ddf7fe95Scasper 		return (NULL);
271ddf7fe95Scasper 	}
272ddf7fe95Scasper 	khp->klh_vers = KLPDCALL_VERS;
273ddf7fe95Scasper 	khp->klh_len = len;
274ddf7fe95Scasper 	khp->klh_privoff = sizeof (*khp);
275ddf7fe95Scasper 	*KLH_PRIVSET(khp) = *rq;
276ddf7fe95Scasper 	if (kap != NULL) {
277ddf7fe95Scasper 		kap->kla_type = type;
278ddf7fe95Scasper 		kap->kla_dlen = len - khp->klh_argoff;
279ddf7fe95Scasper 	}
280ddf7fe95Scasper 	return (khp);
281ddf7fe95Scasper }
282ddf7fe95Scasper 
283ddf7fe95Scasper static int
284ddf7fe95Scasper klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
285ddf7fe95Scasper {
286ddf7fe95Scasper 	door_arg_t da;
287ddf7fe95Scasper 	int res;
288ddf7fe95Scasper 	int dres;
289ddf7fe95Scasper 	klpd_head_t *klh;
290ddf7fe95Scasper 
291ddf7fe95Scasper 	if (p->klpd_door_pid == curproc->p_pid)
292ddf7fe95Scasper 		return (-1);
293ddf7fe95Scasper 
294ddf7fe95Scasper 	klh = klpd_marshall(p, req, ap);
295ddf7fe95Scasper 
296ddf7fe95Scasper 	if (klh == NULL)
297ddf7fe95Scasper 		return (-1);
298ddf7fe95Scasper 
299ddf7fe95Scasper 	da.data_ptr = (char *)klh;
300ddf7fe95Scasper 	da.data_size = klh->klh_len;
301ddf7fe95Scasper 	da.desc_ptr = NULL;
302ddf7fe95Scasper 	da.desc_num = 0;
303ddf7fe95Scasper 	da.rbuf = (char *)&res;
304ddf7fe95Scasper 	da.rsize = sizeof (res);
305ddf7fe95Scasper 
306323a81d9Sjwadams 	while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
307323a81d9Sjwadams 	    SIZE_MAX, 0)) != 0) {
308ddf7fe95Scasper 		switch (dres) {
309ddf7fe95Scasper 		case EAGAIN:
310ddf7fe95Scasper 			delay(1);
311ddf7fe95Scasper 			continue;
312ddf7fe95Scasper 		case EINVAL:
313ddf7fe95Scasper 		case EBADF:
314ddf7fe95Scasper 			/* Bad door, don't call it again. */
315ddf7fe95Scasper 			(void) klpd_unreg_dh(p->klpd_door);
316ddf7fe95Scasper 			/* FALLTHROUGH */
317ddf7fe95Scasper 		case EINTR:
318ddf7fe95Scasper 			/* Pending signal, nothing we can do. */
319ddf7fe95Scasper 			/* FALLTHROUGH */
320ddf7fe95Scasper 		default:
321ddf7fe95Scasper 			kmem_free(klh, klh->klh_len);
322ddf7fe95Scasper 			return (-1);
323ddf7fe95Scasper 		}
324ddf7fe95Scasper 	}
325ddf7fe95Scasper 	kmem_free(klh, klh->klh_len);
326ddf7fe95Scasper 	/* Bogus return value, must be a failure */
327ddf7fe95Scasper 	if (da.rbuf != (char *)&res) {
328ddf7fe95Scasper 		kmem_free(da.rbuf, da.rsize);
329ddf7fe95Scasper 		return (-1);
330ddf7fe95Scasper 	}
331ddf7fe95Scasper 	return (res);
332ddf7fe95Scasper }
333ddf7fe95Scasper 
334ddf7fe95Scasper uint32_t klpd_bad_locks;
335ddf7fe95Scasper 
336ddf7fe95Scasper int
337ddf7fe95Scasper klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
338ddf7fe95Scasper {
339ddf7fe95Scasper 	klpd_reg_t *p;
340ddf7fe95Scasper 	int rv = -1;
341ddf7fe95Scasper 	credklpd_t *ckp;
342ddf7fe95Scasper 	zone_t *ckzone;
343ddf7fe95Scasper 
344ddf7fe95Scasper 	/*
345ddf7fe95Scasper 	 * These locks must not be held when this code is called;
346ddf7fe95Scasper 	 * callbacks to userland with these locks held will result
347ddf7fe95Scasper 	 * in issues.  That said, the code at the call sides was
348ddf7fe95Scasper 	 * restructured not to call with any of the locks held and
349ddf7fe95Scasper 	 * no policies operate by default on most processes.
350ddf7fe95Scasper 	 */
351ddf7fe95Scasper 	if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
352ddf7fe95Scasper 	    mutex_owned(&curproc->p_crlock)) {
353ddf7fe95Scasper 		atomic_add_32(&klpd_bad_locks, 1);
354ddf7fe95Scasper 		return (-1);
355ddf7fe95Scasper 	}
356ddf7fe95Scasper 
357ddf7fe95Scasper 	/*
358ddf7fe95Scasper 	 * Enforce the limit set for the call process (still).
359ddf7fe95Scasper 	 */
360ddf7fe95Scasper 	if (!priv_issubset(req, &CR_LPRIV(cr)))
361ddf7fe95Scasper 		return (-1);
362ddf7fe95Scasper 
363ddf7fe95Scasper 	/* Try 1: get the credential specific klpd */
364ddf7fe95Scasper 	if ((ckp = crgetcrklpd(cr)) != NULL) {
365ddf7fe95Scasper 		mutex_enter(&ckp->crkl_lock);
366ddf7fe95Scasper 		if ((p = ckp->crkl_reg) != NULL &&
367ddf7fe95Scasper 		    p->klpd_indel == 0 &&
368ddf7fe95Scasper 		    priv_issubset(req, &p->klpd_pset)) {
369ddf7fe95Scasper 			klpd_hold(p);
370ddf7fe95Scasper 			mutex_exit(&ckp->crkl_lock);
371ddf7fe95Scasper 			rv = klpd_do_call(p, req, ap);
372ddf7fe95Scasper 			mutex_enter(&ckp->crkl_lock);
373ddf7fe95Scasper 			klpd_rele(p);
374ddf7fe95Scasper 			mutex_exit(&ckp->crkl_lock);
375ddf7fe95Scasper 			if (rv != -1)
376ddf7fe95Scasper 				return (rv == 0 ? 0 : -1);
377ddf7fe95Scasper 		} else {
378ddf7fe95Scasper 			mutex_exit(&ckp->crkl_lock);
379ddf7fe95Scasper 		}
380ddf7fe95Scasper 	}
381ddf7fe95Scasper 
382ddf7fe95Scasper 	/* Try 2: get the project specific klpd */
383ddf7fe95Scasper 	mutex_enter(&klpd_mutex);
384ddf7fe95Scasper 
385ddf7fe95Scasper 	if ((p = curproj->kpj_klpd) != NULL) {
386ddf7fe95Scasper 		klpd_hold(p);
387ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
388ddf7fe95Scasper 		if (p->klpd_indel == 0 &&
389ddf7fe95Scasper 		    priv_issubset(req, &p->klpd_pset)) {
390ddf7fe95Scasper 			rv = klpd_do_call(p, req, ap);
391ddf7fe95Scasper 		}
392ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
393ddf7fe95Scasper 		klpd_rele(p);
394ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
395ddf7fe95Scasper 
396ddf7fe95Scasper 		if (rv != -1)
397ddf7fe95Scasper 			return (rv == 0 ? 0 : -1);
398ddf7fe95Scasper 	} else {
399ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
400ddf7fe95Scasper 	}
401ddf7fe95Scasper 
402ddf7fe95Scasper 	/* Try 3: get the global klpd list */
403ddf7fe95Scasper 	ckzone = crgetzone(cr);
404ddf7fe95Scasper 	mutex_enter(&klpd_mutex);
405ddf7fe95Scasper 
406ddf7fe95Scasper 	for (p = klpd_list; p != NULL; ) {
407ddf7fe95Scasper 		zone_t *kkzone = crgetzone(p->klpd_cred);
408ddf7fe95Scasper 		if ((kkzone == &zone0 || kkzone == ckzone) &&
409ddf7fe95Scasper 		    p->klpd_indel == 0 &&
410ddf7fe95Scasper 		    priv_issubset(req, &p->klpd_pset)) {
411ddf7fe95Scasper 			klpd_hold(p);
412ddf7fe95Scasper 			mutex_exit(&klpd_mutex);
413ddf7fe95Scasper 			rv = klpd_do_call(p, req, ap);
414ddf7fe95Scasper 			mutex_enter(&klpd_mutex);
415ddf7fe95Scasper 
416ddf7fe95Scasper 			p = klpd_rele_next(p);
417ddf7fe95Scasper 
418ddf7fe95Scasper 			if (rv != -1)
419ddf7fe95Scasper 				break;
420ddf7fe95Scasper 		} else {
421ddf7fe95Scasper 			p = p->klpd_next;
422ddf7fe95Scasper 		}
423ddf7fe95Scasper 	}
424ddf7fe95Scasper 	mutex_exit(&klpd_mutex);
425ddf7fe95Scasper 	return (rv == 0 ? 0 : -1);
426ddf7fe95Scasper }
427ddf7fe95Scasper 
428ddf7fe95Scasper /*
429ddf7fe95Scasper  * Register the klpd.
430ddf7fe95Scasper  * If the pid_t passed in is positive, update the registration for
431ddf7fe95Scasper  * the specific process; that is only possible if the process already
432ddf7fe95Scasper  * has a registration on it.  This change of registration will affect
433ddf7fe95Scasper  * all processes which share common ancestry.
434ddf7fe95Scasper  *
435ddf7fe95Scasper  * MY_PID (pid 0) can be used to create or change the context for
436ddf7fe95Scasper  * the current process, typically done after fork().
437ddf7fe95Scasper  *
438ddf7fe95Scasper  * A negative value can be used to register a klpd globally.
439ddf7fe95Scasper  *
440ddf7fe95Scasper  * The per-credential klpd needs to be cleaned up when entering
441ddf7fe95Scasper  * a zone or unsetting the flag.
442ddf7fe95Scasper  */
443ddf7fe95Scasper int
444ddf7fe95Scasper klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
445ddf7fe95Scasper {
446ddf7fe95Scasper 	cred_t *cr = CRED();
447ddf7fe95Scasper 	door_handle_t dh;
448ddf7fe95Scasper 	klpd_reg_t *kpd;
449ddf7fe95Scasper 	priv_set_t pset;
450ddf7fe95Scasper 	door_info_t di;
451ddf7fe95Scasper 	credklpd_t *ckp = NULL;
452ddf7fe95Scasper 	pid_t pid = -1;
453ddf7fe95Scasper 	projid_t proj = -1;
454ddf7fe95Scasper 	kproject_t *kpp = NULL;
455ddf7fe95Scasper 
456ddf7fe95Scasper 	if (CR_FLAGS(cr) & PRIV_XPOLICY)
457ddf7fe95Scasper 		return (set_errno(EINVAL));
458ddf7fe95Scasper 
459ddf7fe95Scasper 	if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
460ddf7fe95Scasper 		return (set_errno(EFAULT));
461ddf7fe95Scasper 
462ddf7fe95Scasper 	if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
463ddf7fe95Scasper 		return (set_errno(EPERM));
464ddf7fe95Scasper 
465ddf7fe95Scasper 	switch (type) {
466ddf7fe95Scasper 	case P_PID:
467ddf7fe95Scasper 		pid = (pid_t)id;
468ddf7fe95Scasper 		if (pid == P_MYPID)
469ddf7fe95Scasper 			pid = curproc->p_pid;
470ddf7fe95Scasper 		if (pid == curproc->p_pid)
471ddf7fe95Scasper 			ckp = crklpd_alloc();
472ddf7fe95Scasper 		break;
473ddf7fe95Scasper 	case P_PROJID:
474ddf7fe95Scasper 		proj = (projid_t)id;
475ddf7fe95Scasper 		kpp = project_hold_by_id(proj, crgetzone(cr),
476ddf7fe95Scasper 		    PROJECT_HOLD_FIND);
477ddf7fe95Scasper 		if (kpp == NULL)
478ddf7fe95Scasper 			return (set_errno(ESRCH));
479ddf7fe95Scasper 		break;
480ddf7fe95Scasper 	default:
481ddf7fe95Scasper 		return (set_errno(ENOTSUP));
482ddf7fe95Scasper 	}
483ddf7fe95Scasper 
484ddf7fe95Scasper 
485ddf7fe95Scasper 	/*
486ddf7fe95Scasper 	 * Verify the door passed in; it must be a door and we won't
487ddf7fe95Scasper 	 * allow processes to be called on their own behalf.
488ddf7fe95Scasper 	 */
489ddf7fe95Scasper 	dh = door_ki_lookup(did);
490ddf7fe95Scasper 	if (dh == NULL || door_ki_info(dh, &di) != 0) {
491ddf7fe95Scasper 		if (ckp != NULL)
492ddf7fe95Scasper 			crklpd_rele(ckp);
493ddf7fe95Scasper 		if (kpp != NULL)
494ddf7fe95Scasper 			project_rele(kpp);
495ddf7fe95Scasper 		return (set_errno(EBADF));
496ddf7fe95Scasper 	}
497ddf7fe95Scasper 	if (type == P_PID && pid == di.di_target) {
498ddf7fe95Scasper 		if (ckp != NULL)
499ddf7fe95Scasper 			crklpd_rele(ckp);
500ddf7fe95Scasper 		ASSERT(kpp == NULL);
501ddf7fe95Scasper 		return (set_errno(EINVAL));
502ddf7fe95Scasper 	}
503ddf7fe95Scasper 
504ddf7fe95Scasper 	kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
505ddf7fe95Scasper 	crhold(kpd->klpd_cred = cr);
506ddf7fe95Scasper 	kpd->klpd_door = dh;
507ddf7fe95Scasper 	kpd->klpd_door_pid = di.di_target;
508ddf7fe95Scasper 	kpd->klpd_ref = 1;
509ddf7fe95Scasper 	kpd->klpd_pset = pset;
510ddf7fe95Scasper 
511ddf7fe95Scasper 	if (kpp != NULL) {
512ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
513ddf7fe95Scasper 		kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
514ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
515ddf7fe95Scasper 		if (kpd != NULL)
516ddf7fe95Scasper 			klpd_rele(kpd);
517ddf7fe95Scasper 		project_rele(kpp);
518ddf7fe95Scasper 	} else if ((int)pid < 0) {
519ddf7fe95Scasper 		/* Global daemon */
520ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
521ddf7fe95Scasper 		(void) klpd_link(kpd, &klpd_list, B_FALSE);
522ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
523ddf7fe95Scasper 	} else if (pid == curproc->p_pid) {
524ddf7fe95Scasper 		proc_t *p = curproc;
525ddf7fe95Scasper 		cred_t *newcr = cralloc();
526ddf7fe95Scasper 
527ddf7fe95Scasper 		/* No need to lock, sole reference to ckp */
528ddf7fe95Scasper 		kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
529ddf7fe95Scasper 
530ddf7fe95Scasper 		if (kpd != NULL)
531ddf7fe95Scasper 			klpd_rele(kpd);
532ddf7fe95Scasper 
533ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
534ddf7fe95Scasper 		cr = p->p_cred;
535ddf7fe95Scasper 		crdup_to(cr, newcr);
536ddf7fe95Scasper 		crsetcrklpd(newcr, ckp);
537ddf7fe95Scasper 		p->p_cred = newcr;	/* Already held for p_cred */
538ddf7fe95Scasper 
539ddf7fe95Scasper 		crhold(newcr);		/* Hold once for the current thread */
540ddf7fe95Scasper 		mutex_exit(&p->p_crlock);
541ddf7fe95Scasper 		crfree(cr);		/* One for the p_cred */
542ddf7fe95Scasper 		crset(p, newcr);
543ddf7fe95Scasper 	} else {
544ddf7fe95Scasper 		proc_t *p;
545ddf7fe95Scasper 		cred_t *pcr;
546ddf7fe95Scasper 		mutex_enter(&pidlock);
547ddf7fe95Scasper 		p = prfind(pid);
548ddf7fe95Scasper 		if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
549ddf7fe95Scasper 			mutex_exit(&pidlock);
550ddf7fe95Scasper 			klpd_rele(kpd);
551ddf7fe95Scasper 			return (set_errno(p == NULL ? ESRCH : EPERM));
552ddf7fe95Scasper 		}
553ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
554ddf7fe95Scasper 		crhold(pcr = p->p_cred);
555ddf7fe95Scasper 		mutex_exit(&pidlock);
556ddf7fe95Scasper 		mutex_exit(&p->p_crlock);
557ddf7fe95Scasper 		/*
558ddf7fe95Scasper 		 * We're going to update the credential's ckp in place;
559ddf7fe95Scasper 		 * this requires that it exists.
560ddf7fe95Scasper 		 */
561ddf7fe95Scasper 		ckp = crgetcrklpd(pcr);
562ddf7fe95Scasper 		if (ckp == NULL) {
563ddf7fe95Scasper 			crfree(pcr);
564ddf7fe95Scasper 			klpd_rele(kpd);
565ddf7fe95Scasper 			return (set_errno(EINVAL));
566ddf7fe95Scasper 		}
567ddf7fe95Scasper 		crklpd_setreg(ckp, kpd);
568ddf7fe95Scasper 		crfree(pcr);
569ddf7fe95Scasper 	}
570ddf7fe95Scasper 
571ddf7fe95Scasper 	return (0);
572ddf7fe95Scasper }
573ddf7fe95Scasper 
574ddf7fe95Scasper static int
575ddf7fe95Scasper klpd_unreg_dh(door_handle_t dh)
576ddf7fe95Scasper {
577ddf7fe95Scasper 	klpd_reg_t *p;
578ddf7fe95Scasper 
579ddf7fe95Scasper 	mutex_enter(&klpd_mutex);
580ddf7fe95Scasper 	for (p = klpd_list; p != NULL; p = p->klpd_next) {
581ddf7fe95Scasper 		if (p->klpd_door == dh)
582ddf7fe95Scasper 			break;
583ddf7fe95Scasper 	}
584ddf7fe95Scasper 	if (p == NULL) {
585ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
586ddf7fe95Scasper 		return (EINVAL);
587ddf7fe95Scasper 	}
588ddf7fe95Scasper 	if (p->klpd_indel != 0) {
589ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
590ddf7fe95Scasper 		return (EAGAIN);
591ddf7fe95Scasper 	}
592ddf7fe95Scasper 	p->klpd_indel = 1;
593ddf7fe95Scasper 	klpd_rele(p);
594ddf7fe95Scasper 	mutex_exit(&klpd_mutex);
595ddf7fe95Scasper 	return (0);
596ddf7fe95Scasper }
597ddf7fe95Scasper 
598ddf7fe95Scasper int
599ddf7fe95Scasper klpd_unreg(int did, idtype_t type, id_t id)
600ddf7fe95Scasper {
601ddf7fe95Scasper 	door_handle_t dh;
602ddf7fe95Scasper 	int res = 0;
603ddf7fe95Scasper 	proc_t *p;
604ddf7fe95Scasper 	pid_t pid;
605ddf7fe95Scasper 	projid_t proj;
606ddf7fe95Scasper 	kproject_t *kpp = NULL;
607ddf7fe95Scasper 	credklpd_t *ckp;
608ddf7fe95Scasper 
609ddf7fe95Scasper 	switch (type) {
610ddf7fe95Scasper 	case P_PID:
611ddf7fe95Scasper 		pid = (pid_t)id;
612ddf7fe95Scasper 		break;
613ddf7fe95Scasper 	case P_PROJID:
614ddf7fe95Scasper 		proj = (projid_t)id;
615ddf7fe95Scasper 		kpp = project_hold_by_id(proj, crgetzone(CRED()),
616ddf7fe95Scasper 		    PROJECT_HOLD_FIND);
617ddf7fe95Scasper 		if (kpp == NULL)
618ddf7fe95Scasper 			return (set_errno(ESRCH));
619ddf7fe95Scasper 		break;
620ddf7fe95Scasper 	default:
621ddf7fe95Scasper 		return (set_errno(ENOTSUP));
622ddf7fe95Scasper 	}
623ddf7fe95Scasper 
624ddf7fe95Scasper 	dh = door_ki_lookup(did);
625ddf7fe95Scasper 	if (dh == NULL) {
626ddf7fe95Scasper 		if (kpp != NULL)
627ddf7fe95Scasper 			project_rele(kpp);
628ddf7fe95Scasper 		return (set_errno(EINVAL));
629ddf7fe95Scasper 	}
630ddf7fe95Scasper 
631ddf7fe95Scasper 	if (kpp != NULL) {
632ddf7fe95Scasper 		mutex_enter(&klpd_mutex);
633ddf7fe95Scasper 		if (kpp->kpj_klpd == NULL)
634ddf7fe95Scasper 			res = ESRCH;
635ddf7fe95Scasper 		else
636*134a1f4eSCasper H.S. Dik 			klpd_freelist(&kpp->kpj_klpd);
637ddf7fe95Scasper 		mutex_exit(&klpd_mutex);
638ddf7fe95Scasper 		project_rele(kpp);
639ddf7fe95Scasper 		goto out;
640ddf7fe95Scasper 	} else if ((int)pid > 0) {
641ddf7fe95Scasper 		mutex_enter(&pidlock);
642ddf7fe95Scasper 		p = prfind(pid);
643ddf7fe95Scasper 		if (p == NULL) {
644ddf7fe95Scasper 			mutex_exit(&pidlock);
645ddf7fe95Scasper 			door_ki_rele(dh);
646ddf7fe95Scasper 			return (set_errno(ESRCH));
647ddf7fe95Scasper 		}
648ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
649ddf7fe95Scasper 		mutex_exit(&pidlock);
650ddf7fe95Scasper 	} else if (pid == 0) {
651ddf7fe95Scasper 		p = curproc;
652ddf7fe95Scasper 		mutex_enter(&p->p_crlock);
653ddf7fe95Scasper 	} else {
654ddf7fe95Scasper 		res = klpd_unreg_dh(dh);
655ddf7fe95Scasper 		goto out;
656ddf7fe95Scasper 	}
657ddf7fe95Scasper 
658ddf7fe95Scasper 	ckp = crgetcrklpd(p->p_cred);
659ddf7fe95Scasper 	if (ckp != NULL) {
660ddf7fe95Scasper 		crklpd_setreg(ckp, NULL);
661ddf7fe95Scasper 	} else {
662ddf7fe95Scasper 		res = ESRCH;
663ddf7fe95Scasper 	}
664ddf7fe95Scasper 	mutex_exit(&p->p_crlock);
665ddf7fe95Scasper 
666ddf7fe95Scasper out:
667ddf7fe95Scasper 	door_ki_rele(dh);
668ddf7fe95Scasper 
669ddf7fe95Scasper 	if (res != 0)
670ddf7fe95Scasper 		return (set_errno(res));
671ddf7fe95Scasper 	return (0);
672ddf7fe95Scasper }
673ddf7fe95Scasper 
674ddf7fe95Scasper void
675ddf7fe95Scasper crklpd_hold(credklpd_t *crkpd)
676ddf7fe95Scasper {
677ddf7fe95Scasper 	atomic_add_32(&crkpd->crkl_ref, 1);
678ddf7fe95Scasper }
679ddf7fe95Scasper 
680ddf7fe95Scasper void
681ddf7fe95Scasper crklpd_rele(credklpd_t *crkpd)
682ddf7fe95Scasper {
683ddf7fe95Scasper 	if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) {
684ddf7fe95Scasper 		if (crkpd->crkl_reg != NULL)
685ddf7fe95Scasper 			klpd_rele(crkpd->crkl_reg);
686ddf7fe95Scasper 		mutex_destroy(&crkpd->crkl_lock);
687ddf7fe95Scasper 		kmem_free(crkpd, sizeof (*crkpd));
688ddf7fe95Scasper 	}
689ddf7fe95Scasper }
690ddf7fe95Scasper 
691ddf7fe95Scasper static credklpd_t *
692ddf7fe95Scasper crklpd_alloc(void)
693ddf7fe95Scasper {
694ddf7fe95Scasper 	credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
695ddf7fe95Scasper 
696ddf7fe95Scasper 	mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
697ddf7fe95Scasper 	res->crkl_ref = 1;
698ddf7fe95Scasper 	res->crkl_reg = NULL;
699ddf7fe95Scasper 
700ddf7fe95Scasper 	return (res);
701ddf7fe95Scasper }
702ddf7fe95Scasper 
703ddf7fe95Scasper void
704ddf7fe95Scasper crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
705ddf7fe95Scasper {
706ddf7fe95Scasper 	klpd_reg_t *old;
707ddf7fe95Scasper 
708ddf7fe95Scasper 	mutex_enter(&crk->crkl_lock);
709ddf7fe95Scasper 	if (new == NULL) {
710ddf7fe95Scasper 		old = crk->crkl_reg;
711ddf7fe95Scasper 		if (old != NULL)
712ddf7fe95Scasper 			klpd_unlink(old);
713ddf7fe95Scasper 	} else {
714ddf7fe95Scasper 		old = klpd_link(new, &crk->crkl_reg, B_TRUE);
715ddf7fe95Scasper 	}
716ddf7fe95Scasper 	mutex_exit(&crk->crkl_lock);
717ddf7fe95Scasper 
718ddf7fe95Scasper 	if (old != NULL)
719ddf7fe95Scasper 		klpd_rele(old);
720ddf7fe95Scasper }
721*134a1f4eSCasper H.S. Dik 
722*134a1f4eSCasper H.S. Dik /* Allocate and register the pfexec specific callback */
723*134a1f4eSCasper H.S. Dik int
724*134a1f4eSCasper H.S. Dik pfexec_reg(int did)
725*134a1f4eSCasper H.S. Dik {
726*134a1f4eSCasper H.S. Dik 	door_handle_t dh;
727*134a1f4eSCasper H.S. Dik 	int err = secpolicy_pfexec_register(CRED());
728*134a1f4eSCasper H.S. Dik 	klpd_reg_t *pfx;
729*134a1f4eSCasper H.S. Dik 	door_info_t di;
730*134a1f4eSCasper H.S. Dik 	zone_t *myzone = crgetzone(CRED());
731*134a1f4eSCasper H.S. Dik 
732*134a1f4eSCasper H.S. Dik 	if (err != 0)
733*134a1f4eSCasper H.S. Dik 		return (set_errno(err));
734*134a1f4eSCasper H.S. Dik 
735*134a1f4eSCasper H.S. Dik 	dh = door_ki_lookup(did);
736*134a1f4eSCasper H.S. Dik 	if (dh == NULL || door_ki_info(dh, &di) != 0)
737*134a1f4eSCasper H.S. Dik 		return (set_errno(EBADF));
738*134a1f4eSCasper H.S. Dik 
739*134a1f4eSCasper H.S. Dik 	pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
740*134a1f4eSCasper H.S. Dik 
741*134a1f4eSCasper H.S. Dik 	pfx->klpd_door = dh;
742*134a1f4eSCasper H.S. Dik 	pfx->klpd_door_pid = di.di_target;
743*134a1f4eSCasper H.S. Dik 	pfx->klpd_ref = 1;
744*134a1f4eSCasper H.S. Dik 	pfx->klpd_cred = NULL;
745*134a1f4eSCasper H.S. Dik 	mutex_enter(&myzone->zone_lock);
746*134a1f4eSCasper H.S. Dik 	pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
747*134a1f4eSCasper H.S. Dik 	mutex_exit(&myzone->zone_lock);
748*134a1f4eSCasper H.S. Dik 	if (pfx != NULL)
749*134a1f4eSCasper H.S. Dik 		klpd_rele(pfx);
750*134a1f4eSCasper H.S. Dik 
751*134a1f4eSCasper H.S. Dik 	return (0);
752*134a1f4eSCasper H.S. Dik }
753*134a1f4eSCasper H.S. Dik 
754*134a1f4eSCasper H.S. Dik int
755*134a1f4eSCasper H.S. Dik pfexec_unreg(int did)
756*134a1f4eSCasper H.S. Dik {
757*134a1f4eSCasper H.S. Dik 	door_handle_t dh;
758*134a1f4eSCasper H.S. Dik 	int err = 0;
759*134a1f4eSCasper H.S. Dik 	zone_t *myzone = crgetzone(CRED());
760*134a1f4eSCasper H.S. Dik 	klpd_reg_t *pfd;
761*134a1f4eSCasper H.S. Dik 
762*134a1f4eSCasper H.S. Dik 	dh = door_ki_lookup(did);
763*134a1f4eSCasper H.S. Dik 	if (dh == NULL)
764*134a1f4eSCasper H.S. Dik 		return (set_errno(EBADF));
765*134a1f4eSCasper H.S. Dik 
766*134a1f4eSCasper H.S. Dik 	mutex_enter(&myzone->zone_lock);
767*134a1f4eSCasper H.S. Dik 	pfd = myzone->zone_pfexecd;
768*134a1f4eSCasper H.S. Dik 	if (pfd != NULL && pfd->klpd_door == dh) {
769*134a1f4eSCasper H.S. Dik 		klpd_unlink(pfd);
770*134a1f4eSCasper H.S. Dik 	} else {
771*134a1f4eSCasper H.S. Dik 		pfd = NULL;
772*134a1f4eSCasper H.S. Dik 		err = EINVAL;
773*134a1f4eSCasper H.S. Dik 	}
774*134a1f4eSCasper H.S. Dik 	mutex_exit(&myzone->zone_lock);
775*134a1f4eSCasper H.S. Dik 	door_ki_rele(dh);
776*134a1f4eSCasper H.S. Dik 	/*
777*134a1f4eSCasper H.S. Dik 	 * crfree() cannot be called with zone_lock held; it is called
778*134a1f4eSCasper H.S. Dik 	 * indirectly through closing the door handle
779*134a1f4eSCasper H.S. Dik 	 */
780*134a1f4eSCasper H.S. Dik 	if (pfd != NULL)
781*134a1f4eSCasper H.S. Dik 		klpd_rele(pfd);
782*134a1f4eSCasper H.S. Dik 	if (err != 0)
783*134a1f4eSCasper H.S. Dik 		return (set_errno(err));
784*134a1f4eSCasper H.S. Dik 	return (0);
785*134a1f4eSCasper H.S. Dik }
786*134a1f4eSCasper H.S. Dik 
787*134a1f4eSCasper H.S. Dik static int
788*134a1f4eSCasper H.S. Dik get_path(char *buf, const char *path, int len)
789*134a1f4eSCasper H.S. Dik {
790*134a1f4eSCasper H.S. Dik 	size_t lc;
791*134a1f4eSCasper H.S. Dik 	char *s;
792*134a1f4eSCasper H.S. Dik 
793*134a1f4eSCasper H.S. Dik 	if (len < 0)
794*134a1f4eSCasper H.S. Dik 		len = strlen(path);
795*134a1f4eSCasper H.S. Dik 
796*134a1f4eSCasper H.S. Dik 	if (*path == '/' && len < MAXPATHLEN) {
797*134a1f4eSCasper H.S. Dik 		(void) strcpy(buf, path);
798*134a1f4eSCasper H.S. Dik 		return (0);
799*134a1f4eSCasper H.S. Dik 	}
800*134a1f4eSCasper H.S. Dik 	/*
801*134a1f4eSCasper H.S. Dik 	 * Build the pathname using the current directory + resolve pathname.
802*134a1f4eSCasper H.S. Dik 	 * The resolve pathname either starts with a normal component and
803*134a1f4eSCasper H.S. Dik 	 * we can just concatenate them or it starts with one
804*134a1f4eSCasper H.S. Dik 	 * or more ".." component and we can remove those; the
805*134a1f4eSCasper H.S. Dik 	 * last one cannot be a ".." and the current directory has
806*134a1f4eSCasper H.S. Dik 	 * more components than the number of ".." in the resolved pathname.
807*134a1f4eSCasper H.S. Dik 	 */
808*134a1f4eSCasper H.S. Dik 	if (dogetcwd(buf, MAXPATHLEN) != 0)
809*134a1f4eSCasper H.S. Dik 		return (-1);
810*134a1f4eSCasper H.S. Dik 
811*134a1f4eSCasper H.S. Dik 	lc = strlen(buf);
812*134a1f4eSCasper H.S. Dik 
813*134a1f4eSCasper H.S. Dik 	while (len > 3 && strncmp("../", path, 3) == 0) {
814*134a1f4eSCasper H.S. Dik 		len -= 3;
815*134a1f4eSCasper H.S. Dik 		path += 3;
816*134a1f4eSCasper H.S. Dik 
817*134a1f4eSCasper H.S. Dik 		s = strrchr(buf, '/');
818*134a1f4eSCasper H.S. Dik 		if (s == NULL || s == buf)
819*134a1f4eSCasper H.S. Dik 			return (-1);
820*134a1f4eSCasper H.S. Dik 
821*134a1f4eSCasper H.S. Dik 		*s = '\0';
822*134a1f4eSCasper H.S. Dik 		lc = s - buf;
823*134a1f4eSCasper H.S. Dik 	}
824*134a1f4eSCasper H.S. Dik 	/* Add a "/" and a NUL */
825*134a1f4eSCasper H.S. Dik 	if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
826*134a1f4eSCasper H.S. Dik 		return (-1);
827*134a1f4eSCasper H.S. Dik 
828*134a1f4eSCasper H.S. Dik 	buf[lc] = '/';
829*134a1f4eSCasper H.S. Dik 	(void) strcpy(buf + lc + 1, path);
830*134a1f4eSCasper H.S. Dik 
831*134a1f4eSCasper H.S. Dik 	return (0);
832*134a1f4eSCasper H.S. Dik }
833*134a1f4eSCasper H.S. Dik 
834*134a1f4eSCasper H.S. Dik /*
835*134a1f4eSCasper H.S. Dik  * Perform the pfexec upcall.
836*134a1f4eSCasper H.S. Dik  *
837*134a1f4eSCasper H.S. Dik  * The pfexec upcall is different from the klpd_upcall in that a failure
838*134a1f4eSCasper H.S. Dik  * will lead to a denial of execution.
839*134a1f4eSCasper H.S. Dik  */
840*134a1f4eSCasper H.S. Dik int
841*134a1f4eSCasper H.S. Dik pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
842*134a1f4eSCasper H.S. Dik     boolean_t *scrub)
843*134a1f4eSCasper H.S. Dik {
844*134a1f4eSCasper H.S. Dik 	klpd_reg_t *pfd;
845*134a1f4eSCasper H.S. Dik 	pfexec_arg_t *pap;
846*134a1f4eSCasper H.S. Dik 	pfexec_reply_t pr, *prp;
847*134a1f4eSCasper H.S. Dik 	door_arg_t da;
848*134a1f4eSCasper H.S. Dik 	int dres;
849*134a1f4eSCasper H.S. Dik 	cred_t *ncr = NULL;
850*134a1f4eSCasper H.S. Dik 	int err = -1;
851*134a1f4eSCasper H.S. Dik 	priv_set_t *iset;
852*134a1f4eSCasper H.S. Dik 	priv_set_t *lset;
853*134a1f4eSCasper H.S. Dik 	zone_t *myzone = crgetzone(CRED());
854*134a1f4eSCasper H.S. Dik 	size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
855*134a1f4eSCasper H.S. Dik 
856*134a1f4eSCasper H.S. Dik 	/* Find registration */
857*134a1f4eSCasper H.S. Dik 	mutex_enter(&myzone->zone_lock);
858*134a1f4eSCasper H.S. Dik 	if ((pfd = myzone->zone_pfexecd) != NULL)
859*134a1f4eSCasper H.S. Dik 		klpd_hold(pfd);
860*134a1f4eSCasper H.S. Dik 	mutex_exit(&myzone->zone_lock);
861*134a1f4eSCasper H.S. Dik 
862*134a1f4eSCasper H.S. Dik 	if (pfd == NULL)
863*134a1f4eSCasper H.S. Dik 		return (0);
864*134a1f4eSCasper H.S. Dik 
865*134a1f4eSCasper H.S. Dik 	if (pfd->klpd_door_pid == curproc->p_pid) {
866*134a1f4eSCasper H.S. Dik 		klpd_rele(pfd);
867*134a1f4eSCasper H.S. Dik 		return (0);
868*134a1f4eSCasper H.S. Dik 	}
869*134a1f4eSCasper H.S. Dik 
870*134a1f4eSCasper H.S. Dik 	pap = kmem_zalloc(pasize, KM_SLEEP);
871*134a1f4eSCasper H.S. Dik 
872*134a1f4eSCasper H.S. Dik 	if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
873*134a1f4eSCasper H.S. Dik 		goto out1;
874*134a1f4eSCasper H.S. Dik 
875*134a1f4eSCasper H.S. Dik 	pap->pfa_vers = PFEXEC_ARG_VERS;
876*134a1f4eSCasper H.S. Dik 	pap->pfa_call = PFEXEC_EXEC_ATTRS;
877*134a1f4eSCasper H.S. Dik 	pap->pfa_len = pasize;
878*134a1f4eSCasper H.S. Dik 	pap->pfa_uid = crgetruid(cr);
879*134a1f4eSCasper H.S. Dik 
880*134a1f4eSCasper H.S. Dik 	da.data_ptr = (char *)pap;
881*134a1f4eSCasper H.S. Dik 	da.data_size = pap->pfa_len;
882*134a1f4eSCasper H.S. Dik 	da.desc_ptr = NULL;
883*134a1f4eSCasper H.S. Dik 	da.desc_num = 0;
884*134a1f4eSCasper H.S. Dik 	da.rbuf = (char *)&pr;
885*134a1f4eSCasper H.S. Dik 	da.rsize = sizeof (pr);
886*134a1f4eSCasper H.S. Dik 
887*134a1f4eSCasper H.S. Dik 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
888*134a1f4eSCasper H.S. Dik 		switch (dres) {
889*134a1f4eSCasper H.S. Dik 		case EAGAIN:
890*134a1f4eSCasper H.S. Dik 			delay(1);
891*134a1f4eSCasper H.S. Dik 			continue;
892*134a1f4eSCasper H.S. Dik 		case EINVAL:
893*134a1f4eSCasper H.S. Dik 		case EBADF:
894*134a1f4eSCasper H.S. Dik 			/* FALLTHROUGH */
895*134a1f4eSCasper H.S. Dik 		case EINTR:
896*134a1f4eSCasper H.S. Dik 			/* FALLTHROUGH */
897*134a1f4eSCasper H.S. Dik 		default:
898*134a1f4eSCasper H.S. Dik 			goto out;
899*134a1f4eSCasper H.S. Dik 		}
900*134a1f4eSCasper H.S. Dik 	}
901*134a1f4eSCasper H.S. Dik 
902*134a1f4eSCasper H.S. Dik 	prp = (pfexec_reply_t *)da.rbuf;
903*134a1f4eSCasper H.S. Dik 	/*
904*134a1f4eSCasper H.S. Dik 	 * Check the size of the result and the alignment of the
905*134a1f4eSCasper H.S. Dik 	 * privilege sets.
906*134a1f4eSCasper H.S. Dik 	 */
907*134a1f4eSCasper H.S. Dik 	if (da.rsize < sizeof (pr) ||
908*134a1f4eSCasper H.S. Dik 	    prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
909*134a1f4eSCasper H.S. Dik 	    prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
910*134a1f4eSCasper H.S. Dik 	    (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
911*134a1f4eSCasper H.S. Dik 	    (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0)
912*134a1f4eSCasper H.S. Dik 		goto out;
913*134a1f4eSCasper H.S. Dik 
914*134a1f4eSCasper H.S. Dik 	/*
915*134a1f4eSCasper H.S. Dik 	 * Get results:
916*134a1f4eSCasper H.S. Dik 	 *	allow/allow with additional credentials/disallow[*]
917*134a1f4eSCasper H.S. Dik 	 *
918*134a1f4eSCasper H.S. Dik 	 *	euid, uid, egid, gid, privs, and limitprivs
919*134a1f4eSCasper H.S. Dik 	 * We now have somewhat more flexibility we could even set E and P
920*134a1f4eSCasper H.S. Dik 	 * judiciously but that would break some currently valid assumptions
921*134a1f4eSCasper H.S. Dik 	 *	[*] Disallow is not readily supported by always including
922*134a1f4eSCasper H.S. Dik 	 *	the Basic Solaris User profile in all user's profiles.
923*134a1f4eSCasper H.S. Dik 	 */
924*134a1f4eSCasper H.S. Dik 
925*134a1f4eSCasper H.S. Dik 	if (!prp->pfr_allowed) {
926*134a1f4eSCasper H.S. Dik 		err = EACCES;
927*134a1f4eSCasper H.S. Dik 		goto out;
928*134a1f4eSCasper H.S. Dik 	}
929*134a1f4eSCasper H.S. Dik 	if (!prp->pfr_setcred) {
930*134a1f4eSCasper H.S. Dik 		err = 0;
931*134a1f4eSCasper H.S. Dik 		goto out;
932*134a1f4eSCasper H.S. Dik 	}
933*134a1f4eSCasper H.S. Dik 	ncr = crdup((cred_t *)cr);
934*134a1f4eSCasper H.S. Dik 
935*134a1f4eSCasper H.S. Dik 	/*
936*134a1f4eSCasper H.S. Dik 	 * Generate the new credential set scrubenv if ruid != euid (or set)
937*134a1f4eSCasper H.S. Dik 	 * the "I'm set-uid flag" but that is not inherited so scrubbing
938*134a1f4eSCasper H.S. Dik 	 * the environment is a requirement.
939*134a1f4eSCasper H.S. Dik 	 */
940*134a1f4eSCasper H.S. Dik 	/* Set uids or gids, note that -1 will do the right thing */
941*134a1f4eSCasper H.S. Dik 	if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
942*134a1f4eSCasper H.S. Dik 		goto out;
943*134a1f4eSCasper H.S. Dik 	if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0)
944*134a1f4eSCasper H.S. Dik 		goto out;
945*134a1f4eSCasper H.S. Dik 
946*134a1f4eSCasper H.S. Dik 	*scrub = prp->pfr_scrubenv;
947*134a1f4eSCasper H.S. Dik 
948*134a1f4eSCasper H.S. Dik 	if (prp->pfr_clearflag)
949*134a1f4eSCasper H.S. Dik 		CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
950*134a1f4eSCasper H.S. Dik 
951*134a1f4eSCasper H.S. Dik 	/* We cannot exceed our Limit set, no matter what */
952*134a1f4eSCasper H.S. Dik 	iset = PFEXEC_REPLY_IPRIV(prp);
953*134a1f4eSCasper H.S. Dik 
954*134a1f4eSCasper H.S. Dik 	if (iset != NULL) {
955*134a1f4eSCasper H.S. Dik 		if (!priv_issubset(iset, &CR_LPRIV(ncr)))
956*134a1f4eSCasper H.S. Dik 			goto out;
957*134a1f4eSCasper H.S. Dik 		priv_union(iset, &CR_IPRIV(ncr));
958*134a1f4eSCasper H.S. Dik 	}
959*134a1f4eSCasper H.S. Dik 
960*134a1f4eSCasper H.S. Dik 	/* Nor can we increate our Limit set itself */
961*134a1f4eSCasper H.S. Dik 	lset = PFEXEC_REPLY_LPRIV(prp);
962*134a1f4eSCasper H.S. Dik 
963*134a1f4eSCasper H.S. Dik 	if (lset != NULL) {
964*134a1f4eSCasper H.S. Dik 		if (!priv_issubset(lset, &CR_LPRIV(ncr)))
965*134a1f4eSCasper H.S. Dik 			goto out;
966*134a1f4eSCasper H.S. Dik 		CR_LPRIV(ncr) = *lset;
967*134a1f4eSCasper H.S. Dik 	}
968*134a1f4eSCasper H.S. Dik 
969*134a1f4eSCasper H.S. Dik 	/* Exec will do the standard set operations */
970*134a1f4eSCasper H.S. Dik 
971*134a1f4eSCasper H.S. Dik 	err = 0;
972*134a1f4eSCasper H.S. Dik out:
973*134a1f4eSCasper H.S. Dik 	if (da.rbuf != (char *)&pr)
974*134a1f4eSCasper H.S. Dik 		kmem_free(da.rbuf, da.rsize);
975*134a1f4eSCasper H.S. Dik out1:
976*134a1f4eSCasper H.S. Dik 	kmem_free(pap, pasize);
977*134a1f4eSCasper H.S. Dik 	klpd_rele(pfd);
978*134a1f4eSCasper H.S. Dik 	if (ncr != NULL) {
979*134a1f4eSCasper H.S. Dik 		if (err == 0)
980*134a1f4eSCasper H.S. Dik 			*pfcr = ncr;
981*134a1f4eSCasper H.S. Dik 		else
982*134a1f4eSCasper H.S. Dik 			crfree(ncr);
983*134a1f4eSCasper H.S. Dik 	}
984*134a1f4eSCasper H.S. Dik 	return (err);
985*134a1f4eSCasper H.S. Dik }
986*134a1f4eSCasper H.S. Dik 
987*134a1f4eSCasper H.S. Dik int
988*134a1f4eSCasper H.S. Dik get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
989*134a1f4eSCasper H.S. Dik {
990*134a1f4eSCasper H.S. Dik 	klpd_reg_t *pfd;
991*134a1f4eSCasper H.S. Dik 	pfexec_arg_t *pap;
992*134a1f4eSCasper H.S. Dik 	door_arg_t da;
993*134a1f4eSCasper H.S. Dik 	int dres;
994*134a1f4eSCasper H.S. Dik 	int err = -1;
995*134a1f4eSCasper H.S. Dik 	priv_set_t *fset, pmem;
996*134a1f4eSCasper H.S. Dik 	cred_t *zkcr;
997*134a1f4eSCasper H.S. Dik 	zone_t *myzone = crgetzone(cr);
998*134a1f4eSCasper H.S. Dik 	size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
999*134a1f4eSCasper H.S. Dik 
1000*134a1f4eSCasper H.S. Dik 	mutex_enter(&myzone->zone_lock);
1001*134a1f4eSCasper H.S. Dik 	if ((pfd = myzone->zone_pfexecd) != NULL)
1002*134a1f4eSCasper H.S. Dik 		klpd_hold(pfd);
1003*134a1f4eSCasper H.S. Dik 	mutex_exit(&myzone->zone_lock);
1004*134a1f4eSCasper H.S. Dik 
1005*134a1f4eSCasper H.S. Dik 	if (pfd == NULL)
1006*134a1f4eSCasper H.S. Dik 		return (-1);
1007*134a1f4eSCasper H.S. Dik 
1008*134a1f4eSCasper H.S. Dik 	if (pfd->klpd_door_pid == curproc->p_pid) {
1009*134a1f4eSCasper H.S. Dik 		klpd_rele(pfd);
1010*134a1f4eSCasper H.S. Dik 		return (0);
1011*134a1f4eSCasper H.S. Dik 	}
1012*134a1f4eSCasper H.S. Dik 
1013*134a1f4eSCasper H.S. Dik 	pap = kmem_zalloc(pasize, KM_SLEEP);
1014*134a1f4eSCasper H.S. Dik 
1015*134a1f4eSCasper H.S. Dik 	if (get_path(pap->pfa_path, respn, -1) == -1)
1016*134a1f4eSCasper H.S. Dik 		goto out1;
1017*134a1f4eSCasper H.S. Dik 
1018*134a1f4eSCasper H.S. Dik 	pap->pfa_vers = PFEXEC_ARG_VERS;
1019*134a1f4eSCasper H.S. Dik 	pap->pfa_call = PFEXEC_FORCED_PRIVS;
1020*134a1f4eSCasper H.S. Dik 	pap->pfa_len = pasize;
1021*134a1f4eSCasper H.S. Dik 	pap->pfa_uid = (uid_t)-1;			/* Not relevant */
1022*134a1f4eSCasper H.S. Dik 
1023*134a1f4eSCasper H.S. Dik 	da.data_ptr = (char *)pap;
1024*134a1f4eSCasper H.S. Dik 	da.data_size = pap->pfa_len;
1025*134a1f4eSCasper H.S. Dik 	da.desc_ptr = NULL;
1026*134a1f4eSCasper H.S. Dik 	da.desc_num = 0;
1027*134a1f4eSCasper H.S. Dik 	da.rbuf = (char *)&pmem;
1028*134a1f4eSCasper H.S. Dik 	da.rsize = sizeof (pmem);
1029*134a1f4eSCasper H.S. Dik 
1030*134a1f4eSCasper H.S. Dik 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1031*134a1f4eSCasper H.S. Dik 		switch (dres) {
1032*134a1f4eSCasper H.S. Dik 		case EAGAIN:
1033*134a1f4eSCasper H.S. Dik 			delay(1);
1034*134a1f4eSCasper H.S. Dik 			continue;
1035*134a1f4eSCasper H.S. Dik 		case EINVAL:
1036*134a1f4eSCasper H.S. Dik 		case EBADF:
1037*134a1f4eSCasper H.S. Dik 		case EINTR:
1038*134a1f4eSCasper H.S. Dik 		default:
1039*134a1f4eSCasper H.S. Dik 			goto out;
1040*134a1f4eSCasper H.S. Dik 		}
1041*134a1f4eSCasper H.S. Dik 	}
1042*134a1f4eSCasper H.S. Dik 
1043*134a1f4eSCasper H.S. Dik 	/*
1044*134a1f4eSCasper H.S. Dik 	 * Check the size of the result, it's a privilege set.
1045*134a1f4eSCasper H.S. Dik 	 */
1046*134a1f4eSCasper H.S. Dik 	if (da.rsize != sizeof (priv_set_t))
1047*134a1f4eSCasper H.S. Dik 		goto out;
1048*134a1f4eSCasper H.S. Dik 
1049*134a1f4eSCasper H.S. Dik 	fset = (priv_set_t *)da.rbuf;
1050*134a1f4eSCasper H.S. Dik 
1051*134a1f4eSCasper H.S. Dik 	/*
1052*134a1f4eSCasper H.S. Dik 	 * We restrict the forced privileges with whatever is available in
1053*134a1f4eSCasper H.S. Dik 	 * the current zone.
1054*134a1f4eSCasper H.S. Dik 	 */
1055*134a1f4eSCasper H.S. Dik 	zkcr = zone_kcred();
1056*134a1f4eSCasper H.S. Dik 	priv_intersect(&CR_LPRIV(zkcr), fset);
1057*134a1f4eSCasper H.S. Dik 
1058*134a1f4eSCasper H.S. Dik 	/*
1059*134a1f4eSCasper H.S. Dik 	 * But we fail if the forced privileges are not found in the current
1060*134a1f4eSCasper H.S. Dik 	 * Limit set.
1061*134a1f4eSCasper H.S. Dik 	 */
1062*134a1f4eSCasper H.S. Dik 	if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1063*134a1f4eSCasper H.S. Dik 		err = EACCES;
1064*134a1f4eSCasper H.S. Dik 	} else if (!priv_isemptyset(fset)) {
1065*134a1f4eSCasper H.S. Dik 		err = 0;
1066*134a1f4eSCasper H.S. Dik 		*set = *fset;
1067*134a1f4eSCasper H.S. Dik 	}
1068*134a1f4eSCasper H.S. Dik out:
1069*134a1f4eSCasper H.S. Dik 	if (da.rbuf != (char *)&pmem)
1070*134a1f4eSCasper H.S. Dik 		kmem_free(da.rbuf, da.rsize);
1071*134a1f4eSCasper H.S. Dik out1:
1072*134a1f4eSCasper H.S. Dik 	kmem_free(pap, pasize);
1073*134a1f4eSCasper H.S. Dik 	klpd_rele(pfd);
1074*134a1f4eSCasper H.S. Dik 	return (err);
1075*134a1f4eSCasper H.S. Dik }
1076*134a1f4eSCasper H.S. Dik 
1077*134a1f4eSCasper H.S. Dik int
1078*134a1f4eSCasper H.S. Dik check_user_privs(const cred_t *cr, const priv_set_t *set)
1079*134a1f4eSCasper H.S. Dik {
1080*134a1f4eSCasper H.S. Dik 	klpd_reg_t *pfd;
1081*134a1f4eSCasper H.S. Dik 	pfexec_arg_t *pap;
1082*134a1f4eSCasper H.S. Dik 	door_arg_t da;
1083*134a1f4eSCasper H.S. Dik 	int dres;
1084*134a1f4eSCasper H.S. Dik 	int err = -1;
1085*134a1f4eSCasper H.S. Dik 	zone_t *myzone = crgetzone(cr);
1086*134a1f4eSCasper H.S. Dik 	size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t));
1087*134a1f4eSCasper H.S. Dik 	uint32_t res;
1088*134a1f4eSCasper H.S. Dik 
1089*134a1f4eSCasper H.S. Dik 	mutex_enter(&myzone->zone_lock);
1090*134a1f4eSCasper H.S. Dik 	if ((pfd = myzone->zone_pfexecd) != NULL)
1091*134a1f4eSCasper H.S. Dik 		klpd_hold(pfd);
1092*134a1f4eSCasper H.S. Dik 	mutex_exit(&myzone->zone_lock);
1093*134a1f4eSCasper H.S. Dik 
1094*134a1f4eSCasper H.S. Dik 	if (pfd == NULL)
1095*134a1f4eSCasper H.S. Dik 		return (-1);
1096*134a1f4eSCasper H.S. Dik 
1097*134a1f4eSCasper H.S. Dik 	if (pfd->klpd_door_pid == curproc->p_pid) {
1098*134a1f4eSCasper H.S. Dik 		klpd_rele(pfd);
1099*134a1f4eSCasper H.S. Dik 		return (0);
1100*134a1f4eSCasper H.S. Dik 	}
1101*134a1f4eSCasper H.S. Dik 
1102*134a1f4eSCasper H.S. Dik 	pap = kmem_zalloc(pasize, KM_SLEEP);
1103*134a1f4eSCasper H.S. Dik 
1104*134a1f4eSCasper H.S. Dik 	*(priv_set_t *)&pap->pfa_buf = *set;
1105*134a1f4eSCasper H.S. Dik 
1106*134a1f4eSCasper H.S. Dik 	pap->pfa_vers = PFEXEC_ARG_VERS;
1107*134a1f4eSCasper H.S. Dik 	pap->pfa_call = PFEXEC_USER_PRIVS;
1108*134a1f4eSCasper H.S. Dik 	pap->pfa_len = pasize;
1109*134a1f4eSCasper H.S. Dik 	pap->pfa_uid = crgetruid(cr);
1110*134a1f4eSCasper H.S. Dik 
1111*134a1f4eSCasper H.S. Dik 	da.data_ptr = (char *)pap;
1112*134a1f4eSCasper H.S. Dik 	da.data_size = pap->pfa_len;
1113*134a1f4eSCasper H.S. Dik 	da.desc_ptr = NULL;
1114*134a1f4eSCasper H.S. Dik 	da.desc_num = 0;
1115*134a1f4eSCasper H.S. Dik 	da.rbuf = (char *)&res;
1116*134a1f4eSCasper H.S. Dik 	da.rsize = sizeof (res);
1117*134a1f4eSCasper H.S. Dik 
1118*134a1f4eSCasper H.S. Dik 	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1119*134a1f4eSCasper H.S. Dik 		switch (dres) {
1120*134a1f4eSCasper H.S. Dik 		case EAGAIN:
1121*134a1f4eSCasper H.S. Dik 			delay(1);
1122*134a1f4eSCasper H.S. Dik 			continue;
1123*134a1f4eSCasper H.S. Dik 		case EINVAL:
1124*134a1f4eSCasper H.S. Dik 		case EBADF:
1125*134a1f4eSCasper H.S. Dik 		case EINTR:
1126*134a1f4eSCasper H.S. Dik 		default:
1127*134a1f4eSCasper H.S. Dik 			goto out;
1128*134a1f4eSCasper H.S. Dik 		}
1129*134a1f4eSCasper H.S. Dik 	}
1130*134a1f4eSCasper H.S. Dik 
1131*134a1f4eSCasper H.S. Dik 	/*
1132*134a1f4eSCasper H.S. Dik 	 * Check the size of the result.
1133*134a1f4eSCasper H.S. Dik 	 */
1134*134a1f4eSCasper H.S. Dik 	if (da.rsize != sizeof (res))
1135*134a1f4eSCasper H.S. Dik 		goto out;
1136*134a1f4eSCasper H.S. Dik 
1137*134a1f4eSCasper H.S. Dik 	if (*(uint32_t *)da.rbuf == 1)
1138*134a1f4eSCasper H.S. Dik 		err = 0;
1139*134a1f4eSCasper H.S. Dik out:
1140*134a1f4eSCasper H.S. Dik 	if (da.rbuf != (char *)&res)
1141*134a1f4eSCasper H.S. Dik 		kmem_free(da.rbuf, da.rsize);
1142*134a1f4eSCasper H.S. Dik out1:
1143*134a1f4eSCasper H.S. Dik 	kmem_free(pap, pasize);
1144*134a1f4eSCasper H.S. Dik 	klpd_rele(pfd);
1145*134a1f4eSCasper H.S. Dik 	return (err);
1146*134a1f4eSCasper H.S. Dik }
1147