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