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