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