1ddf7fe9casper/*
2ddf7fe9casper * CDDL HEADER START
3ddf7fe9casper *
4ddf7fe9casper * The contents of this file are subject to the terms of the
5ddf7fe9casper * Common Development and Distribution License (the "License").
6ddf7fe9casper * You may not use this file except in compliance with the License.
7ddf7fe9casper *
8ddf7fe9casper * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9ddf7fe9casper * or http://www.opensolaris.org/os/licensing.
10ddf7fe9casper * See the License for the specific language governing permissions
11ddf7fe9casper * and limitations under the License.
12ddf7fe9casper *
13ddf7fe9casper * When distributing Covered Code, include this CDDL HEADER in each
14ddf7fe9casper * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15ddf7fe9casper * If applicable, add the following below this CDDL HEADER, with the
16ddf7fe9casper * fields enclosed by brackets "[]" replaced with your own identifying
17ddf7fe9casper * information: Portions Copyright [yyyy] [name of copyright owner]
18ddf7fe9casper *
19ddf7fe9casper * CDDL HEADER END
20ddf7fe9casper */
21ddf7fe9casper
22ddf7fe9casper/*
23134a1f4Casper H.S. Dik * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24b01b59eRobert Mustacchi * Copyright 2015, Joyent, Inc.
25ddf7fe9casper */
26ddf7fe9casper
27ddf7fe9casper#include <sys/atomic.h>
28ddf7fe9casper#include <sys/door.h>
29ddf7fe9casper#include <sys/proc.h>
30ddf7fe9casper#include <sys/cred_impl.h>
31ddf7fe9casper#include <sys/policy.h>
32ddf7fe9casper#include <sys/priv.h>
33ddf7fe9casper#include <sys/klpd.h>
34ddf7fe9casper#include <sys/errno.h>
35ddf7fe9casper#include <sys/kmem.h>
36ddf7fe9casper#include <sys/project.h>
37ddf7fe9casper#include <sys/systm.h>
38ddf7fe9casper#include <sys/sysmacros.h>
39ddf7fe9casper#include <sys/pathname.h>
40ddf7fe9casper#include <sys/varargs.h>
41ddf7fe9casper#include <sys/zone.h>
427d8cb57Alex Wilson#include <sys/cmn_err.h>
437d8cb57Alex Wilson#include <sys/sdt.h>
44ddf7fe9casper#include <netinet/in.h>
45ddf7fe9casper
46ddf7fe9casper#define	ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
47ddf7fe9casper
48ddf7fe9casperstatic kmutex_t klpd_mutex;
49ddf7fe9casper
50ddf7fe9caspertypedef struct klpd_reg {
51ddf7fe9casper	struct klpd_reg *klpd_next;
52ddf7fe9casper	struct klpd_reg **klpd_refp;
53ddf7fe9casper	door_handle_t 	klpd_door;
54ddf7fe9casper	pid_t		klpd_door_pid;
55ddf7fe9casper	priv_set_t	klpd_pset;
56ddf7fe9casper	cred_t		*klpd_cred;
57ddf7fe9casper	int		klpd_indel;		/* Disabled */
58ddf7fe9casper	uint32_t	klpd_ref;
59ddf7fe9casper} klpd_reg_t;
60ddf7fe9casper
61ddf7fe9casper
62ddf7fe9casper/*
63ddf7fe9casper * This data structure hangs off the credential of a process; the
64ddf7fe9casper * credential is finalized and cannot be changed; but this structure
65ddf7fe9casper * can be changed when a new door server for the particular group
66ddf7fe9casper * needs to be registered.  It is refcounted and shared between
67ddf7fe9casper * processes with common ancestry.
68ddf7fe9casper *
69ddf7fe9casper * The reference count is atomically updated.
70ddf7fe9casper *
71ddf7fe9casper * But the registration probably needs to be updated under a lock.
72ddf7fe9casper */
73ddf7fe9caspertypedef struct credklpd {
74ddf7fe9casper	kmutex_t	crkl_lock;
75ddf7fe9casper	klpd_reg_t	*crkl_reg;
76ddf7fe9casper	uint32_t	crkl_ref;
77ddf7fe9casper} credklpd_t;
78ddf7fe9casper
79ddf7fe9casperklpd_reg_t *klpd_list;
80ddf7fe9casper
81ddf7fe9casperstatic void klpd_unlink(klpd_reg_t *);
82ddf7fe9casperstatic int klpd_unreg_dh(door_handle_t);
83ddf7fe9casper
84ddf7fe9casperstatic credklpd_t *crklpd_alloc(void);
85ddf7fe9casper
86ddf7fe9caspervoid crklpd_setreg(credklpd_t *, klpd_reg_t *);
87ddf7fe9casper
88ddf7fe9casperextern size_t max_vnode_path;
89ddf7fe9casper
90ddf7fe9caspervoid
91ddf7fe9casperklpd_rele(klpd_reg_t *p)
92ddf7fe9casper{
931a5e258Josef 'Jeff' Sipek	if (atomic_dec_32_nv(&p->klpd_ref) == 0) {
94ddf7fe9casper		if (p->klpd_refp != NULL)
95ddf7fe9casper			klpd_unlink(p);
96ddf7fe9casper		if (p->klpd_cred != NULL)
97ddf7fe9casper			crfree(p->klpd_cred);
98ddf7fe9casper		door_ki_rele(p->klpd_door);
99ddf7fe9casper		kmem_free(p, sizeof (*p));
100ddf7fe9casper	}
101ddf7fe9casper}
102ddf7fe9casper
103ddf7fe9casper/*
104ddf7fe9casper * In order to be able to walk the lists, we can't unlink the entry
105ddf7fe9casper * until the reference count drops to 0.  If we remove it too soon,
106ddf7fe9casper * list walkers will terminate when they happen to call a now orphaned
107ddf7fe9casper * entry.
108ddf7fe9casper */
109ddf7fe9casperstatic klpd_reg_t *
110ddf7fe9casperklpd_rele_next(klpd_reg_t *p)
111ddf7fe9casper{
112ddf7fe9casper	klpd_reg_t *r = p->klpd_next;
113ddf7fe9casper
114ddf7fe9casper	klpd_rele(p);
115ddf7fe9casper	return (r);
116ddf7fe9casper}
117ddf7fe9casper
118ddf7fe9casper
119ddf7fe9casperstatic void
120ddf7fe9casperklpd_hold(klpd_reg_t *p)
121ddf7fe9casper{
1221a5e258Josef 'Jeff' Sipek	atomic_inc_32(&p->klpd_ref);
123ddf7fe9casper}
124ddf7fe9casper
125ddf7fe9casper/*
126ddf7fe9casper * Remove registration from where it is registered.  Returns next in list.
127ddf7fe9casper */
128ddf7fe9casperstatic void
129ddf7fe9casperklpd_unlink(klpd_reg_t *p)
130ddf7fe9casper{
131ddf7fe9casper	ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
132ddf7fe9casper
133ddf7fe9casper	if (p->klpd_refp != NULL)
134ddf7fe9casper		*p->klpd_refp = p->klpd_next;
135ddf7fe9casper
136ddf7fe9casper	if (p->klpd_next != NULL)
137ddf7fe9casper		p->klpd_next->klpd_refp = p->klpd_refp;
138ddf7fe9casper	p->klpd_refp = NULL;
139ddf7fe9casper}
140ddf7fe9casper
141ddf7fe9casper/*
142134a1f4Casper H.S. Dik * Remove all elements of the klpd list and decrement their refcnts.
143ddf7fe9casper * The lock guarding the list should be held; this function is
144134a1f4Casper H.S. Dik * called when we are sure we want to destroy the list completely
145134a1f4Casper H.S. Dik * list but not so sure that the reference counts of all elements have
146134a1f4Casper H.S. Dik * dropped back to 1.
147ddf7fe9casper */
148ddf7fe9caspervoid
149134a1f4Casper H.S. Dikklpd_freelist(klpd_reg_t **pp)
150ddf7fe9casper{
151134a1f4Casper H.S. Dik	klpd_reg_t *p;
152134a1f4Casper H.S. Dik
153134a1f4Casper H.S. Dik	while ((p = *pp) != NULL) {
154134a1f4Casper H.S. Dik		klpd_unlink(p);
155134a1f4Casper H.S. Dik		klpd_rele(p);
156134a1f4Casper H.S. Dik	}
157ddf7fe9casper}
158ddf7fe9casper
159ddf7fe9casper/*
160ddf7fe9casper * Link new entry in list.  The Boolean argument specifies whether this
161ddf7fe9casper * list can contain only a single item or multiple items.
162ddf7fe9casper * Returns the entry which needs to be released if single is B_TRUE.
163ddf7fe9casper */
164ddf7fe9casperstatic klpd_reg_t *
165ddf7fe9casperklpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
166ddf7fe9casper{
167ddf7fe9casper	klpd_reg_t *old = *listp;
168ddf7fe9casper
169ddf7fe9casper	ASSERT(p->klpd_ref == 1);
170ddf7fe9casper
171ddf7fe9casper	ASSERT(old == NULL || *old->klpd_refp == old);
172ddf7fe9casper	p->klpd_refp = listp;
173ddf7fe9casper	p->klpd_next = single ? NULL : old;
174ddf7fe9casper	*listp = p;
175ddf7fe9casper	if (old != NULL) {
176ddf7fe9casper		if (single) {
177ddf7fe9casper			ASSERT(old->klpd_next == NULL);
178ddf7fe9casper			old->klpd_refp = NULL;
179ddf7fe9casper			return (old);
180ddf7fe9casper		} else
181ddf7fe9casper			old->klpd_refp = &p->klpd_next;
182ddf7fe9casper	}
183ddf7fe9casper	return (NULL);
184ddf7fe9casper}
185ddf7fe9casper
186ddf7fe9casper/*
187ddf7fe9casper * The typical call consists of:
188ddf7fe9casper *	- priv_set_t
189ddf7fe9casper *	- some integer data (type, value)
190ddf7fe9casper * for now, it's just one bit.
191ddf7fe9casper */
192ddf7fe9casperstatic klpd_head_t *
193ddf7fe9casperklpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
194ddf7fe9casper{
195134a1f4Casper H.S. Dik	char	*tmp;
196ddf7fe9casper	uint_t	type;
197ddf7fe9casper	vnode_t *vp;
198ddf7fe9casper	size_t	len = sizeof (priv_set_t) + sizeof (klpd_head_t);
199ddf7fe9casper	size_t	plen, clen;
200ddf7fe9casper	int	proto;
201ddf7fe9casper
202ddf7fe9casper	klpd_arg_t *kap = NULL;
203ddf7fe9casper	klpd_head_t *khp;
204ddf7fe9casper
205ddf7fe9casper	type = va_arg(ap, uint_t);
206ddf7fe9casper	switch (type) {
207ddf7fe9casper	case KLPDARG_NOMORE:
208ddf7fe9casper		khp = kmem_zalloc(len, KM_SLEEP);
209ddf7fe9casper		khp->klh_argoff = 0;
210ddf7fe9casper		break;
211ddf7fe9casper	case KLPDARG_VNODE:
212ddf7fe9casper		len += offsetof(klpd_arg_t, kla_str);
213ddf7fe9casper		vp = va_arg(ap, vnode_t *);
214ddf7fe9casper		if (vp == NULL)
215ddf7fe9casper			return (NULL);
216ddf7fe9casper
217134a1f4Casper H.S. Dik		tmp = va_arg(ap, char *);
218ddf7fe9casper
219134a1f4Casper H.S. Dik		if (tmp != NULL && *tmp != '\0')
220134a1f4Casper H.S. Dik			clen = strlen(tmp) + 1;
221ddf7fe9casper		else
222ddf7fe9casper			clen = 0;
223ddf7fe9casper
224ddf7fe9casper		len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
225ddf7fe9casper		khp = kmem_zalloc(len, KM_SLEEP);
226ddf7fe9casper
227ddf7fe9casper		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
228ddf7fe9casper		kap = KLH_ARG(khp);
229ddf7fe9casper
230ddf7fe9casper		if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
231ddf7fe9casper		    vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
232ddf7fe9casper			kmem_free(khp, len);
233ddf7fe9casper			return (NULL);
234ddf7fe9casper		}
235ddf7fe9casper		if (clen != 0) {
236ddf7fe9casper			plen = strlen(kap->kla_str);
237ddf7fe9casper			if (plen + clen + 1 >= MAXPATHLEN) {
238ddf7fe9casper				kmem_free(khp, len);
239ddf7fe9casper				return (NULL);
240ddf7fe9casper			}
241ddf7fe9casper			/* Don't make root into a double "/" */
242ddf7fe9casper			if (plen <= 2)
243ddf7fe9casper				plen = 0;
244ddf7fe9casper			kap->kla_str[plen] = '/';
245134a1f4Casper H.S. Dik			bcopy(tmp, &kap->kla_str[plen + 1], clen);
246ddf7fe9casper		}
247ddf7fe9casper		break;
248ddf7fe9casper	case KLPDARG_PORT:
249ddf7fe9casper		proto = va_arg(ap, int);
250ddf7fe9casper		switch (proto) {
251ddf7fe9casper		case IPPROTO_TCP:	type = KLPDARG_TCPPORT;
252ddf7fe9casper					break;
253ddf7fe9casper		case IPPROTO_UDP:	type = KLPDARG_UDPPORT;
254ddf7fe9casper					break;
255ddf7fe9casper		case IPPROTO_SCTP:	type = KLPDARG_SCTPPORT;
256ddf7fe9casper					break;
257ddf7fe9casper		case PROTO_SDP:		type = KLPDARG_SDPPORT;
258ddf7fe9casper					break;
259ddf7fe9casper		}
260ddf7fe9casper		/* FALLTHROUGH */
261ddf7fe9casper	case KLPDARG_INT:
262ddf7fe9casper	case KLPDARG_TCPPORT:
263ddf7fe9casper	case KLPDARG_UDPPORT:
264ddf7fe9casper	case KLPDARG_SCTPPORT:
265ddf7fe9casper	case KLPDARG_SDPPORT:
266ddf7fe9casper		len += sizeof (*kap);
267ddf7fe9casper		khp = kmem_zalloc(len, KM_SLEEP);
268ddf7fe9casper		khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
269ddf7fe9casper		kap = KLH_ARG(khp);
270ddf7fe9casper		kap->kla_int = va_arg(ap, int);
271ddf7fe9casper		break;
272ddf7fe9casper	default:
273ddf7fe9casper		return (NULL);
274ddf7fe9casper	}
275ddf7fe9casper	khp->klh_vers = KLPDCALL_VERS;
276ddf7fe9casper	khp->klh_len = len;
277ddf7fe9casper	khp->klh_privoff = sizeof (*khp);
278ddf7fe9casper	*KLH_PRIVSET(khp) = *rq;
279ddf7fe9casper	if (kap != NULL) {
280ddf7fe9casper		kap->kla_type = type;
281ddf7fe9casper		kap->kla_dlen = len - khp->klh_argoff;
282ddf7fe9casper	}
283ddf7fe9casper	return (khp);
284ddf7fe9casper}
285ddf7fe9casper
286ddf7fe9casperstatic int
287ddf7fe9casperklpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
288ddf7fe9casper{
289ddf7fe9casper	door_arg_t da;
290ddf7fe9casper	int res;
291ddf7fe9casper	int dres;
292ddf7fe9casper	klpd_head_t *klh;
293ddf7fe9casper
294ddf7fe9casper	if (p->klpd_door_pid == curproc->p_pid)
295ddf7fe9casper		return (-1);
296ddf7fe9casper
297ddf7fe9casper	klh = klpd_marshall(p, req, ap);
298ddf7fe9casper
299ddf7fe9casper	if (klh == NULL)
300ddf7fe9casper		return (-1);
301ddf7fe9casper
302ddf7fe9casper	da.data_ptr = (char *)klh;
303ddf7fe9casper	da.data_size = klh->klh_len;
304ddf7fe9casper	da.desc_ptr = NULL;
305ddf7fe9casper	da.desc_num = 0;
306ddf7fe9casper	da.rbuf = (char *)&res;
307ddf7fe9casper	da.rsize = sizeof (res);
308ddf7fe9casper
309323a81djwadams	while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
310323a81djwadams	    SIZE_MAX, 0)) != 0) {
311ddf7fe9casper		switch (dres) {
312ddf7fe9casper		case EAGAIN:
313ddf7fe9casper			delay(1);
314ddf7fe9casper			continue;
315ddf7fe9casper		case EINVAL:
316ddf7fe9casper		case EBADF:
317ddf7fe9casper			/* Bad door, don't call it again. */
318ddf7fe9casper			(void) klpd_unreg_dh(p->klpd_door);
319ddf7fe9casper			/* FALLTHROUGH */
320ddf7fe9casper		case EINTR:
321ddf7fe9casper			/* Pending signal, nothing we can do. */
322ddf7fe9casper			/* FALLTHROUGH */
323ddf7fe9casper		default:
324ddf7fe9casper			kmem_free(klh, klh->klh_len);
325ddf7fe9casper			return (-1);
326ddf7fe9casper		}
327ddf7fe9casper	}
328ddf7fe9casper	kmem_free(klh, klh->klh_len);
329ddf7fe9casper	/* Bogus return value, must be a failure */
330ddf7fe9casper	if (da.rbuf != (char *)&res) {
331ddf7fe9casper		kmem_free(da.rbuf, da.rsize);
332ddf7fe9casper		return (-1);
333ddf7fe9casper	}
334ddf7fe9casper	return (res);
335ddf7fe9casper}
336ddf7fe9casper
337ddf7fe9casperuint32_t klpd_bad_locks;
338ddf7fe9casper
339ddf7fe9casperint
340ddf7fe9casperklpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
341ddf7fe9casper{
342ddf7fe9casper	klpd_reg_t *p;
343ddf7fe9casper	int rv = -1;
344ddf7fe9casper	credklpd_t *ckp;
345ddf7fe9casper	zone_t *ckzone;
346ddf7fe9casper
347ddf7fe9casper	/*
348ddf7fe9casper	 * These locks must not be held when this code is called;
349ddf7fe9casper	 * callbacks to userland with these locks held will result
350ddf7fe9casper	 * in issues.  That said, the code at the call sides was
351ddf7fe9casper	 * restructured not to call with any of the locks held and
352ddf7fe9casper	 * no policies operate by default on most processes.
353ddf7fe9casper	 */
354ddf7fe9casper	if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
355ddf7fe9casper	    mutex_owned(&curproc->p_crlock)) {
3561a5e258Josef 'Jeff' Sipek		atomic_inc_32(&klpd_bad_locks);
357ddf7fe9casper		return (-1);
358ddf7fe9casper	}
359ddf7fe9casper
360ddf7fe9casper	/*
361ddf7fe9casper	 * Enforce the limit set for the call process (still).
362ddf7fe9casper	 */
363ddf7fe9casper	if (!priv_issubset(req, &CR_LPRIV(cr)))
364ddf7fe9casper		return (-1);
365ddf7fe9casper
366ddf7fe9casper	/* Try 1: get the credential specific klpd */
367ddf7fe9casper	if ((ckp = crgetcrklpd(cr)) != NULL) {
368ddf7fe9casper		mutex_enter(&ckp->crkl_lock);
369ddf7fe9casper		if ((p = ckp->crkl_reg) != NULL &&
370ddf7fe9casper		    p->klpd_indel == 0 &&
371ddf7fe9casper		    priv_issubset(req, &p->klpd_pset)) {
372ddf7fe9casper			klpd_hold(p);
373ddf7fe9casper			mutex_exit(&ckp->crkl_lock);
374ddf7fe9casper			rv = klpd_do_call(p, req, ap);
375ddf7fe9casper			mutex_enter(&ckp->crkl_lock);
376ddf7fe9casper			klpd_rele(p);
377ddf7fe9casper			mutex_exit(&ckp->crkl_lock);
378ddf7fe9casper			if (rv != -1)
379ddf7fe9casper				return (rv == 0 ? 0 : -1);
380ddf7fe9casper		} else {
381ddf7fe9casper			mutex_exit(&ckp->crkl_lock);
382ddf7fe9casper		}
383ddf7fe9casper	}
384ddf7fe9casper
385ddf7fe9casper	/* Try 2: get the project specific klpd */
386ddf7fe9casper	mutex_enter(&klpd_mutex);
387ddf7fe9casper
388ddf7fe9casper	if ((p = curproj->kpj_klpd) != NULL) {
389ddf7fe9casper		klpd_hold(p);
390ddf7fe9casper		mutex_exit(&klpd_mutex);
391ddf7fe9casper		if (p->klpd_indel == 0 &&
392ddf7fe9casper		    priv_issubset(req, &p->klpd_pset)) {
393ddf7fe9casper			rv = klpd_do_call(p, req, ap);
394ddf7fe9casper		}
395ddf7fe9casper		mutex_enter(&klpd_mutex);
396ddf7fe9casper		klpd_rele(p);
397ddf7fe9casper		mutex_exit(&klpd_mutex);
398ddf7fe9casper
399ddf7fe9casper		if (rv != -1)
400ddf7fe9casper			return (rv == 0 ? 0 : -1);
401ddf7fe9casper	} else {
402ddf7fe9casper		mutex_exit(&klpd_mutex);
403ddf7fe9casper	}
404ddf7fe9casper
405ddf7fe9casper	/* Try 3: get the global klpd list */
406ddf7fe9casper	ckzone = crgetzone(cr);
407ddf7fe9casper	mutex_enter(&klpd_mutex);
408ddf7fe9casper
409ddf7fe9casper	for (p = klpd_list; p != NULL; ) {
410ddf7fe9casper		zone_t *kkzone = crgetzone(p->klpd_cred);
411ddf7fe9casper		if ((kkzone == &zone0 || kkzone == ckzone) &&
412ddf7fe9casper		    p->klpd_indel == 0 &&
413ddf7fe9casper		    priv_issubset(req, &p->klpd_pset)) {
414ddf7fe9casper			klpd_hold(p);
415ddf7fe9casper			mutex_exit(&klpd_mutex);
416ddf7fe9casper			rv = klpd_do_call(p, req, ap);
417ddf7fe9casper			mutex_enter(&klpd_mutex);
418ddf7fe9casper
419ddf7fe9casper			p = klpd_rele_next(p);
420ddf7fe9casper
421ddf7fe9casper			if (rv != -1)
422ddf7fe9casper				break;
423ddf7fe9casper		} else {
424ddf7fe9casper			p = p->klpd_next;
425ddf7fe9casper		}
426ddf7fe9casper	}
427ddf7fe9casper	mutex_exit(&klpd_mutex);
428ddf7fe9casper	return (rv == 0 ? 0 : -1);
429ddf7fe9casper}
430ddf7fe9casper
431ddf7fe9casper/*
432ddf7fe9casper * Register the klpd.
433ddf7fe9casper * If the pid_t passed in is positive, update the registration for
434ddf7fe9casper * the specific process; that is only possible if the process already
435ddf7fe9casper * has a registration on it.  This change of registration will affect
436ddf7fe9casper * all processes which share common ancestry.
437ddf7fe9casper *
438ddf7fe9casper * MY_PID (pid 0) can be used to create or change the context for
439ddf7fe9casper * the current process, typically done after fork().
440ddf7fe9casper *
441ddf7fe9casper * A negative value can be used to register a klpd globally.
442ddf7fe9casper *
443ddf7fe9casper * The per-credential klpd needs to be cleaned up when entering
444ddf7fe9casper * a zone or unsetting the flag.
445ddf7fe9casper */
446ddf7fe9casperint
447ddf7fe9casperklpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
448ddf7fe9casper{
449ddf7fe9casper	cred_t *cr = CRED();
450ddf7fe9casper	door_handle_t dh;
451ddf7fe9casper	klpd_reg_t *kpd;
452ddf7fe9casper	priv_set_t pset;
453ddf7fe9casper	door_info_t di;
454ddf7fe9casper	credklpd_t *ckp = NULL;
455ddf7fe9casper	pid_t pid = -1;
456ddf7fe9casper	projid_t proj = -1;
457ddf7fe9casper	kproject_t *kpp = NULL;
458ddf7fe9casper
459ddf7fe9casper	if (CR_FLAGS(cr) & PRIV_XPOLICY)
460ddf7fe9casper		return (set_errno(EINVAL));
461ddf7fe9casper
462ddf7fe9casper	if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
463ddf7fe9casper		return (set_errno(EFAULT));
464ddf7fe9casper
465ddf7fe9casper	if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
466ddf7fe9casper		return (set_errno(EPERM));
467ddf7fe9casper
468ddf7fe9casper	switch (type) {
469ddf7fe9casper	case P_PID:
470ddf7fe9casper		pid = (pid_t)id;
471ddf7fe9casper		if (pid == P_MYPID)
472ddf7fe9casper			pid = curproc->p_pid;
473ddf7fe9casper		if (pid == curproc->p_pid)
474ddf7fe9casper			ckp = crklpd_alloc();
475ddf7fe9casper		break;
476ddf7fe9casper	case P_PROJID:
477ddf7fe9casper		proj = (projid_t)id;
478ddf7fe9casper		kpp = project_hold_by_id(proj, crgetzone(cr),
479ddf7fe9casper		    PROJECT_HOLD_FIND);
480ddf7fe9casper		if (kpp == NULL)
481ddf7fe9casper			return (set_errno(ESRCH));
482ddf7fe9casper		break;
483ddf7fe9casper	default:
484ddf7fe9casper		return (set_errno(ENOTSUP));
485ddf7fe9casper	}
486ddf7fe9casper
487ddf7fe9casper
488ddf7fe9casper	/*
489ddf7fe9casper	 * Verify the door passed in; it must be a door and we won't
490ddf7fe9casper	 * allow processes to be called on their own behalf.
491ddf7fe9casper	 */
492ddf7fe9casper	dh = door_ki_lookup(did);
493ddf7fe9casper	if (dh == NULL || door_ki_info(dh, &di) != 0) {
494ddf7fe9casper		if (ckp != NULL)
495ddf7fe9casper			crklpd_rele(ckp);
496ddf7fe9casper		if (kpp != NULL)
497ddf7fe9casper			project_rele(kpp);
498ddf7fe9casper		return (set_errno(EBADF));
499ddf7fe9casper	}
500ddf7fe9casper	if (type == P_PID && pid == di.di_target) {
501ddf7fe9casper		if (ckp != NULL)
502ddf7fe9casper			crklpd_rele(ckp);
503ddf7fe9casper		ASSERT(kpp == NULL);
504ddf7fe9casper		return (set_errno(EINVAL));
505ddf7fe9casper	}
506ddf7fe9casper
507ddf7fe9casper	kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
508ddf7fe9casper	crhold(kpd->klpd_cred = cr);
509ddf7fe9casper	kpd->klpd_door = dh;
510ddf7fe9casper	kpd->klpd_door_pid = di.di_target;
511ddf7fe9casper	kpd->klpd_ref = 1;
512ddf7fe9casper	kpd->klpd_pset = pset;
513ddf7fe9casper
514ddf7fe9casper	if (kpp != NULL) {
515ddf7fe9casper		mutex_enter(&klpd_mutex);
516ddf7fe9casper		kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
517ddf7fe9casper		mutex_exit(&klpd_mutex);
518ddf7fe9casper		if (kpd != NULL)
519ddf7fe9casper			klpd_rele(kpd);
520ddf7fe9casper		project_rele(kpp);
521ddf7fe9casper	} else if ((int)pid < 0) {
522ddf7fe9casper		/* Global daemon */
523ddf7fe9casper		mutex_enter(&klpd_mutex);
524ddf7fe9casper		(void) klpd_link(kpd, &klpd_list, B_FALSE);
525ddf7fe9casper		mutex_exit(&klpd_mutex);
526ddf7fe9casper	} else if (pid == curproc->p_pid) {
527ddf7fe9casper		proc_t *p = curproc;
528ddf7fe9casper		cred_t *newcr = cralloc();
529ddf7fe9casper
530ddf7fe9casper		/* No need to lock, sole reference to ckp */
531ddf7fe9casper		kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
532ddf7fe9casper
533ddf7fe9casper		if (kpd != NULL)
534ddf7fe9casper			klpd_rele(kpd);
535ddf7fe9casper
536ddf7fe9casper		mutex_enter(&p->p_crlock);
537ddf7fe9casper		cr = p->p_cred;
538ddf7fe9casper		crdup_to(cr, newcr);
539ddf7fe9casper		crsetcrklpd(newcr, ckp);
540ddf7fe9casper		p->p_cred = newcr;	/* Already held for p_cred */
541ddf7fe9casper
542ddf7fe9casper		crhold(newcr);		/* Hold once for the current thread */
543ddf7fe9casper		mutex_exit(&p->p_crlock);
544ddf7fe9casper		crfree(cr);		/* One for the p_cred */
545ddf7fe9casper		crset(p, newcr);
546ddf7fe9casper	} else {
547ddf7fe9casper		proc_t *p;
548ddf7fe9casper		cred_t *pcr;
549ddf7fe9casper		mutex_enter(&pidlock);
550ddf7fe9casper		p = prfind(pid);
551ddf7fe9casper		if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
552ddf7fe9casper			mutex_exit(&pidlock);
553ddf7fe9casper			klpd_rele(kpd);
554ddf7fe9casper			return (set_errno(p == NULL ? ESRCH : EPERM));
555ddf7fe9casper		}
556ddf7fe9casper		mutex_enter(&p->p_crlock);
557ddf7fe9casper		crhold(pcr = p->p_cred);
558ddf7fe9casper		mutex_exit(&pidlock);
559ddf7fe9casper		mutex_exit(&p->p_crlock);
560ddf7fe9casper		/*
561ddf7fe9casper		 * We're going to update the credential's ckp in place;
562ddf7fe9casper		 * this requires that it exists.
563ddf7fe9casper		 */
564ddf7fe9casper		ckp = crgetcrklpd(pcr);
565ddf7fe9casper		if (ckp == NULL) {
566ddf7fe9casper			crfree(pcr);
567ddf7fe9casper			klpd_rele(kpd);
568ddf7fe9casper			return (set_errno(EINVAL));
569ddf7fe9casper		}
570ddf7fe9casper		crklpd_setreg(ckp, kpd);
571ddf7fe9casper		crfree(pcr);
572ddf7fe9casper	}
573ddf7fe9casper
574ddf7fe9casper	return (0);
575ddf7fe9casper}
576ddf7fe9casper
577ddf7fe9casperstatic int
578ddf7fe9casperklpd_unreg_dh(door_handle_t dh)
579ddf7fe9casper{
580ddf7fe9casper	klpd_reg_t *p;
581ddf7fe9casper
582ddf7fe9casper	mutex_enter(&klpd_mutex);
583ddf7fe9casper	for (p = klpd_list; p != NULL; p = p->klpd_next) {
584ddf7fe9casper		if (p->klpd_door == dh)
585ddf7fe9casper			break;
586ddf7fe9casper	}
587ddf7fe9casper	if (p == NULL) {
588ddf7fe9casper		mutex_exit(&klpd_mutex);
589ddf7fe9casper		return (EINVAL);
590ddf7fe9casper	}
591ddf7fe9casper	if (p->klpd_indel != 0) {
592ddf7fe9casper		mutex_exit(&klpd_mutex);
593ddf7fe9casper		return (EAGAIN);
594ddf7fe9casper	}
595ddf7fe9casper	p->klpd_indel = 1;
596ddf7fe9casper	klpd_rele(p);
597ddf7fe9casper	mutex_exit(&klpd_mutex);
598ddf7fe9casper	return (0);
599ddf7fe9casper}
600ddf7fe9casper
601ddf7fe9casperint
602ddf7fe9casperklpd_unreg(int did, idtype_t type, id_t id)
603ddf7fe9casper{
604ddf7fe9casper	door_handle_t dh;
605ddf7fe9casper	int res = 0;
606ddf7fe9casper	proc_t *p;
607ddf7fe9casper	pid_t pid;
608ddf7fe9casper	projid_t proj;
609ddf7fe9casper	kproject_t *kpp = NULL;
610ddf7fe9casper	credklpd_t *ckp;
611ddf7fe9casper
612ddf7fe9casper	switch (type) {
613ddf7fe9casper	case P_PID:
614ddf7fe9casper		pid = (pid_t)id;
615ddf7fe9casper		break;
616ddf7fe9casper	case P_PROJID:
617ddf7fe9casper		proj = (projid_t)id;
618ddf7fe9casper		kpp = project_hold_by_id(proj, crgetzone(CRED()),
619ddf7fe9casper		    PROJECT_HOLD_FIND);
620ddf7fe9casper		if (kpp == NULL)
621ddf7fe9casper			return (set_errno(ESRCH));
622ddf7fe9casper		break;
623ddf7fe9casper	default:
624ddf7fe9casper		return (set_errno(ENOTSUP));
625ddf7fe9casper	}
626ddf7fe9casper
627ddf7fe9casper	dh = door_ki_lookup(did);
628ddf7fe9casper	if (dh == NULL) {
629ddf7fe9casper		if (kpp != NULL)
630ddf7fe9casper			project_rele(kpp);
631ddf7fe9casper		return (set_errno(EINVAL));
632ddf7fe9casper	}
633ddf7fe9casper
634ddf7fe9casper	if (kpp != NULL) {
635ddf7fe9casper		mutex_enter(&klpd_mutex);
636ddf7fe9casper		if (kpp->kpj_klpd == NULL)
637ddf7fe9casper			res = ESRCH;
638ddf7fe9casper		else
639134a1f4Casper H.S. Dik			klpd_freelist(&kpp->kpj_klpd);
640ddf7fe9casper		mutex_exit(&klpd_mutex);
641ddf7fe9casper		project_rele(kpp);
642ddf7fe9casper		goto out;
643ddf7fe9casper	} else if ((int)pid > 0) {
644ddf7fe9casper		mutex_enter(&pidlock);
645ddf7fe9casper		p = prfind(pid);
646ddf7fe9casper		if (p == NULL) {
647ddf7fe9casper			mutex_exit(&pidlock);
648ddf7fe9casper			door_ki_rele(dh);
649ddf7fe9casper			return (set_errno(ESRCH));
650ddf7fe9casper		}
651ddf7fe9casper		mutex_enter(&p->p_crlock);
652ddf7fe9casper		mutex_exit(&pidlock);
653ddf7fe9casper	} else if (pid == 0) {
654ddf7fe9casper		p = curproc;
655ddf7fe9casper		mutex_enter(&p->p_crlock);
656ddf7fe9casper	} else {
657ddf7fe9casper		res = klpd_unreg_dh(dh);
658ddf7fe9casper		goto out;
659ddf7fe9casper	}
660ddf7fe9casper
661ddf7fe9casper	ckp = crgetcrklpd(p->p_cred);
662ddf7fe9casper	if (ckp != NULL) {
663ddf7fe9casper		crklpd_setreg(ckp, NULL);
664ddf7fe9casper	} else {
665ddf7fe9casper		res = ESRCH;
666ddf7fe9casper	}
667ddf7fe9casper	mutex_exit(&p->p_crlock);
668ddf7fe9casper
669ddf7fe9casperout:
670ddf7fe9casper	door_ki_rele(dh);
671ddf7fe9casper
672ddf7fe9casper	if (res != 0)
673ddf7fe9casper		return (set_errno(res));
674ddf7fe9casper	return (0);
675ddf7fe9casper}
676ddf7fe9casper
677ddf7fe9caspervoid
678ddf7fe9caspercrklpd_hold(credklpd_t *crkpd)
679ddf7fe9casper{
6801a5e258Josef 'Jeff' Sipek	atomic_inc_32(&crkpd->crkl_ref);
681ddf7fe9casper}
682ddf7fe9casper
683ddf7fe9caspervoid
684ddf7fe9caspercrklpd_rele(credklpd_t *crkpd)
685ddf7fe9casper{
6861a5e258Josef 'Jeff' Sipek	if (atomic_dec_32_nv(&crkpd->crkl_ref) == 0) {
687ddf7fe9casper		if (crkpd->crkl_reg != NULL)
688ddf7fe9casper			klpd_rele(crkpd->crkl_reg);
689ddf7fe9casper		mutex_destroy(&crkpd->crkl_lock);
690ddf7fe9casper		kmem_free(crkpd, sizeof (*crkpd));
691ddf7fe9casper	}
692ddf7fe9casper}
693ddf7fe9casper
694ddf7fe9casperstatic credklpd_t *
695ddf7fe9caspercrklpd_alloc(void)
696ddf7fe9casper{
697ddf7fe9casper	credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
698ddf7fe9casper
699ddf7fe9casper	mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
700ddf7fe9casper	res->crkl_ref = 1;
701ddf7fe9casper	res->crkl_reg = NULL;
702ddf7fe9casper
703ddf7fe9casper	return (res);
704ddf7fe9casper}
705ddf7fe9casper
706ddf7fe9caspervoid
707ddf7fe9caspercrklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
708ddf7fe9casper{
709ddf7fe9casper	klpd_reg_t *old;
710ddf7fe9casper
711ddf7fe9casper	mutex_enter(&crk->crkl_lock);
712ddf7fe9casper	if (new == NULL) {
713ddf7fe9casper		old = crk->crkl_reg;
714ddf7fe9casper		if (old != NULL)
715ddf7fe9casper			klpd_unlink(old);
716ddf7fe9casper	} else {
717ddf7fe9casper		old = klpd_link(new, &crk->crkl_reg, B_TRUE);
718ddf7fe9casper	}
719ddf7fe9casper	mutex_exit(&crk->crkl_lock);
720ddf7fe9casper
721ddf7fe9casper	if (old != NULL)
722ddf7fe9casper		klpd_rele(old);
723ddf7fe9casper}
724134a1f4Casper H.S. Dik
725134a1f4Casper H.S. Dik/* Allocate and register the pfexec specific callback */
726134a1f4Casper H.S. Dikint
727134a1f4Casper H.S. Dikpfexec_reg(int did)
728134a1f4Casper H.S. Dik{
729134a1f4Casper H.S. Dik	door_handle_t dh;
730134a1f4Casper H.S. Dik	int err = secpolicy_pfexec_register(CRED());
731134a1f4Casper H.S. Dik	klpd_reg_t *pfx;
732134a1f4Casper H.S. Dik	door_info_t di;
733134a1f4Casper H.S. Dik	zone_t *myzone = crgetzone(CRED());
734134a1f4Casper H.S. Dik
735134a1f4Casper H.S. Dik	if (err != 0)
736134a1f4Casper H.S. Dik		return (set_errno(err));
737134a1f4Casper H.S. Dik
738134a1f4Casper H.S. Dik	dh = door_ki_lookup(did);
739134a1f4Casper H.S. Dik	if (dh == NULL || door_ki_info(dh, &di) != 0)
740134a1f4Casper H.S. Dik		return (set_errno(EBADF));
741134a1f4Casper H.S. Dik
742134a1f4Casper H.S. Dik	pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
743134a1f4Casper H.S. Dik
744134a1f4Casper H.S. Dik	pfx->klpd_door = dh;
745134a1f4Casper H.S. Dik	pfx->klpd_door_pid = di.di_target;
746134a1f4Casper H.S. Dik	pfx->klpd_ref = 1;
747134a1f4Casper H.S. Dik	pfx->klpd_cred = NULL;
748134a1f4Casper H.S. Dik	mutex_enter(&myzone->zone_lock);
749134a1f4Casper H.S. Dik	pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
750134a1f4Casper H.S. Dik	mutex_exit(&myzone->zone_lock);
751134a1f4Casper H.S. Dik	if (pfx != NULL)
752134a1f4Casper H.S. Dik		klpd_rele(pfx);
753134a1f4Casper H.S. Dik
754134a1f4Casper H.S. Dik	return (0);
755134a1f4Casper H.S. Dik}
756134a1f4Casper H.S. Dik
757134a1f4Casper H.S. Dikint
758134a1f4Casper H.S. Dikpfexec_unreg(int did)
759134a1f4Casper H.S. Dik{
760134a1f4Casper H.S. Dik	door_handle_t dh;
761134a1f4Casper H.S. Dik	int err = 0;
762134a1f4Casper H.S. Dik	zone_t *myzone = crgetzone(CRED());
763134a1f4Casper H.S. Dik	klpd_reg_t *pfd;
764134a1f4Casper H.S. Dik
765134a1f4Casper H.S. Dik	dh = door_ki_lookup(did);
766134a1f4Casper H.S. Dik	if (dh == NULL)
767134a1f4Casper H.S. Dik		return (set_errno(EBADF));
768134a1f4Casper H.S. Dik
769134a1f4Casper H.S. Dik	mutex_enter(&myzone->zone_lock);
770134a1f4Casper H.S. Dik	pfd = myzone->zone_pfexecd;
771134a1f4Casper H.S. Dik	if (pfd != NULL && pfd->klpd_door == dh) {
772134a1f4Casper H.S. Dik		klpd_unlink(pfd);
773134a1f4Casper H.S. Dik	} else {
774134a1f4Casper H.S. Dik		pfd = NULL;
775134a1f4Casper H.S. Dik		err = EINVAL;
776134a1f4Casper H.S. Dik	}
777134a1f4Casper H.S. Dik	mutex_exit(&myzone->zone_lock);
778134a1f4Casper H.S. Dik	door_ki_rele(dh);
779134a1f4Casper H.S. Dik	/*
780134a1f4Casper H.S. Dik	 * crfree() cannot be called with zone_lock held; it is called
781134a1f4Casper H.S. Dik	 * indirectly through closing the door handle
782134a1f4Casper H.S. Dik	 */
783134a1f4Casper H.S. Dik	if (pfd != NULL)
784134a1f4Casper H.S. Dik		klpd_rele(pfd);
785134a1f4Casper H.S. Dik	if (err != 0)
786134a1f4Casper H.S. Dik		return (set_errno(err));
787134a1f4Casper H.S. Dik	return (0);
788134a1f4Casper H.S. Dik}
789134a1f4Casper H.S. Dik
790134a1f4Casper H.S. Dikstatic int
791134a1f4Casper H.S. Dikget_path(char *buf, const char *path, int len)
792134a1f4Casper H.S. Dik{
793134a1f4Casper H.S. Dik	size_t lc;
794134a1f4Casper H.S. Dik	char *s;
795134a1f4Casper H.S. Dik
796134a1f4Casper H.S. Dik	if (len < 0)
797134a1f4Casper H.S. Dik		len = strlen(path);
798134a1f4Casper H.S. Dik
799134a1f4Casper H.S. Dik	if (*path == '/' && len < MAXPATHLEN) {
800134a1f4Casper H.S. Dik		(void) strcpy(buf, path);
801134a1f4Casper H.S. Dik		return (0);
802134a1f4Casper H.S. Dik	}
803134a1f4Casper H.S. Dik	/*
804134a1f4Casper H.S. Dik	 * Build the pathname using the current directory + resolve pathname.
805134a1f4Casper H.S. Dik	 * The resolve pathname either starts with a normal component and
806134a1f4Casper H.S. Dik	 * we can just concatenate them or it starts with one
807134a1f4Casper H.S. Dik	 * or more ".." component and we can remove those; the
808134a1f4Casper H.S. Dik	 * last one cannot be a ".." and the current directory has
809134a1f4Casper H.S. Dik	 * more components than the number of ".." in the resolved pathname.
810134a1f4Casper H.S. Dik	 */
811134a1f4Casper H.S. Dik	if (dogetcwd(buf, MAXPATHLEN) != 0)
812134a1f4Casper H.S. Dik		return (-1);
813134a1f4Casper H.S. Dik
814134a1f4Casper H.S. Dik	lc = strlen(buf);
815134a1f4Casper H.S. Dik
816134a1f4Casper H.S. Dik	while (len > 3 && strncmp("../", path, 3) == 0) {
817134a1f4Casper H.S. Dik		len -= 3;
818134a1f4Casper H.S. Dik		path += 3;
819134a1f4Casper H.S. Dik
820134a1f4Casper H.S. Dik		s = strrchr(buf, '/');
821134a1f4Casper H.S. Dik		if (s == NULL || s == buf)
822134a1f4Casper H.S. Dik			return (-1);
823134a1f4Casper H.S. Dik
824134a1f4Casper H.S. Dik		*s = '\0';
825134a1f4Casper H.S. Dik		lc = s - buf;
826134a1f4Casper H.S. Dik	}
827134a1f4Casper H.S. Dik	/* Add a "/" and a NUL */
828134a1f4Casper H.S. Dik	if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
829134a1f4Casper H.S. Dik		return (-1);
830134a1f4Casper H.S. Dik
831134a1f4Casper H.S. Dik	buf[lc] = '/';
832134a1f4Casper H.S. Dik	(void) strcpy(buf + lc + 1, path);
833134a1f4Casper H.S. Dik
834134a1f4Casper H.S. Dik	return (0);
835134a1f4Casper H.S. Dik}
836134a1f4Casper H.S. Dik
837134a1f4Casper H.S. Dik/*
838134a1f4Casper H.S. Dik * Perform the pfexec upcall.
839134a1f4Casper H.S. Dik *
840134a1f4Casper H.S. Dik * The pfexec upcall is different from the klpd_upcall in that a failure
841134a1f4Casper H.S. Dik * will lead to a denial of execution.
842134a1f4Casper H.S. Dik */
843134a1f4Casper H.S. Dikint
844134a1f4Casper H.S. Dikpfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
845134a1f4Casper H.S. Dik    boolean_t *scrub)
846134a1f4Casper H.S. Dik{
847134a1f4Casper H.S. Dik	klpd_reg_t *pfd;
848134a1f4Casper H.S. Dik	pfexec_arg_t *pap;
849134a1f4Casper H.S. Dik	pfexec_reply_t pr, *prp;
850134a1f4Casper H.S. Dik	door_arg_t da;
851134a1f4Casper H.S. Dik	int dres;
852134a1f4Casper H.S. Dik	cred_t *ncr = NULL;
853b01b59eRobert Mustacchi	int err = EACCES;
854134a1f4Casper H.S. Dik	priv_set_t *iset;
855134a1f4Casper H.S. Dik	priv_set_t *lset;
856134a1f4Casper H.S. Dik	zone_t *myzone = crgetzone(CRED());
857134a1f4Casper H.S. Dik	size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
858134a1f4Casper H.S. Dik
859134a1f4Casper H.S. Dik	/* Find registration */
860134a1f4Casper H.S. Dik	mutex_enter(&myzone->zone_lock);
861134a1f4Casper H.S. Dik	if ((pfd = myzone->zone_pfexecd) != NULL)
862134a1f4Casper H.S. Dik		klpd_hold(pfd);
863134a1f4Casper H.S. Dik	mutex_exit(&myzone->zone_lock);
864134a1f4Casper H.S. Dik
8657d8cb57Alex Wilson	if (pfd == NULL) {
8667d8cb57Alex Wilson		DTRACE_PROBE2(pfexecd__not__running,
8677d8cb57Alex Wilson		    zone_t *, myzone, char *, rpnp->pn_path);
8687d8cb57Alex Wilson		uprintf("pfexecd not running; pid %d privileges not "
8697d8cb57Alex Wilson		    "elevated\n", curproc->p_pid);
870134a1f4Casper H.S. Dik		return (0);
8717d8cb57Alex Wilson	}
872134a1f4Casper H.S. Dik
873134a1f4Casper H.S. Dik	if (pfd->klpd_door_pid == curproc->p_pid) {
874134a1f4Casper H.S. Dik		klpd_rele(pfd);
875134a1f4Casper H.S. Dik		return (0);
876134a1f4Casper H.S. Dik	}
877134a1f4Casper H.S. Dik
878134a1f4Casper H.S. Dik	pap = kmem_zalloc(pasize, KM_SLEEP);
879134a1f4Casper H.S. Dik
880134a1f4Casper H.S. Dik	if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
881134a1f4Casper H.S. Dik		goto out1;
882134a1f4Casper H.S. Dik
883134a1f4Casper H.S. Dik	pap->pfa_vers = PFEXEC_ARG_VERS;
884134a1f4Casper H.S. Dik	pap->pfa_call = PFEXEC_EXEC_ATTRS;
885134a1f4Casper H.S. Dik	pap->pfa_len = pasize;
886134a1f4Casper H.S. Dik	pap->pfa_uid = crgetruid(cr);
887134a1f4Casper H.S. Dik
888134a1f4Casper H.S. Dik	da.data_ptr = (char *)pap;
889134a1f4Casper H.S. Dik	da.data_size = pap->pfa_len;
890134a1f4Casper H.S. Dik	da.desc_ptr = NULL;
891134a1f4Casper H.S. Dik	da.desc_num = 0;
892134a1f4Casper H.S. Dik	da.rbuf = (char *)&pr;
893134a1f4Casper H.S. Dik	da.rsize = sizeof (pr);
894134a1f4Casper H.S. Dik
895134a1f4Casper H.S. Dik	while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
896134a1f4Casper H.S. Dik		switch (dres) {
897134a1f4Casper H.S. Dik		case EAGAIN:
898134a1f4Casper H.S. Dik			delay(1);
899134a1f4Casper H.S. Dik			continue;
900134a1f4Casper H.S. Dik		case EINVAL:
901134a1f4Casper H.S. Dik		case EBADF:
902134a1f4Casper H.S. Dik			/* FALLTHROUGH */
903134a1f4Casper H.S. Dik		case EINTR:
904134a1f4Casper H.S. Dik			/* FALLTHROUGH */
905134a1f4Casper H.S. Dik		default:
9067d8cb57Alex Wilson			DTRACE_PROBE4(pfexecd__failure,
9077d8cb57Alex Wilson			    int, dres, zone_t *, myzone,
9087d8cb57Alex Wilson			    char *, rpnp->pn_path, klpd_reg_t *, pfd);
909134a1f4Casper H.S. Dik			goto out;
910134a1f4Casper H.S. Dik		}
911134a1f4Casper H.S. Dik	}
912134a1f4Casper H.S. Dik
913134a1f4Casper H.S. Dik	prp = (pfexec_reply_t *)da.rbuf;
914134a1f4Casper H.S. Dik	/*
915134a1f4Casper H.S. Dik	 * Check the size of the result and the alignment of the
916134a1f4Casper H.S. Dik	 * privilege sets.
917134a1f4Casper H.S. Dik	 */
918134a1f4Casper H.S. Dik	if (da.rsize < sizeof (pr) ||
919134a1f4Casper H.S. Dik	    prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
920134a1f4Casper H.S. Dik	    prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
921134a1f4Casper H.S. Dik	    (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
922b01b59eRobert Mustacchi	    (prp->pfr_ioff & (sizeof (priv_chunk_t) - 1)) != 0)
923134a1f4Casper H.S. Dik		goto out;
924134a1f4Casper H.S. Dik
925134a1f4Casper H.S. Dik	/*
926134a1f4Casper H.S. Dik	 * Get results:
927134a1f4Casper H.S. Dik	 *	allow/allow with additional credentials/disallow[*]
928134a1f4Casper H.S. Dik	 *
929134a1f4Casper H.S. Dik	 *	euid, uid, egid, gid, privs, and limitprivs
930134a1f4Casper H.S. Dik	 * We now have somewhat more flexibility we could even set E and P
931134a1f4Casper H.S. Dik	 * judiciously but that would break some currently valid assumptions
932134a1f4Casper H.S. Dik	 *	[*] Disallow is not readily supported by always including
933134a1f4Casper H.S. Dik	 *	the Basic Solaris User profile in all user's profiles.
934134a1f4Casper H.S. Dik	 */
935134a1f4Casper H.S. Dik
936134a1f4Casper H.S. Dik	if (!prp->pfr_allowed) {
937134a1f4Casper H.S. Dik		err = EACCES;
938134a1f4Casper H.S. Dik		goto out;
939134a1f4Casper H.S. Dik	}
940134a1f4Casper H.S. Dik	if (!prp->pfr_setcred) {
941134a1f4Casper H.S. Dik		err = 0;
942134a1f4Casper H.S. Dik		goto out;
943134a1f4Casper H.S. Dik	}
944134a1f4Casper H.S. Dik	ncr = crdup((cred_t *)cr);
945134a1f4Casper H.S. Dik
946134a1f4Casper H.S. Dik	/*
947134a1f4Casper H.S. Dik	 * Generate the new credential set scrubenv if ruid != euid (or set)
948134a1f4Casper H.S. Dik	 * the "I'm set-uid flag" but that is not inherited so scrubbing
949134a1f4Casper H.S. Dik	 * the environment is a requirement.
950134a1f4Casper H.S. Dik	 */
951134a1f4Casper H.S. Dik	/* Set uids or gids, note that -1 will do the right thing */
952134a1f4Casper H.S. Dik	if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
953134a1f4Casper H.S. Dik		goto out;
954