loader: remove devdesc comments and assertions
[unleashed.git] / kernel / os / klpd.c
blob8592b47021505e08eebe2ecc2bd3cf82fdd01ac9
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2015, Joyent, Inc.
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>
46 #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
48 static kmutex_t klpd_mutex;
50 typedef 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;
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.
69 * The reference count is atomically updated.
71 * But the registration probably needs to be updated under a lock.
73 typedef struct credklpd {
74 kmutex_t crkl_lock;
75 klpd_reg_t *crkl_reg;
76 uint32_t crkl_ref;
77 } credklpd_t;
79 klpd_reg_t *klpd_list;
81 static void klpd_unlink(klpd_reg_t *);
82 static int klpd_unreg_dh(door_handle_t);
84 static credklpd_t *crklpd_alloc(void);
86 void crklpd_setreg(credklpd_t *, klpd_reg_t *);
88 extern size_t max_vnode_path;
90 void
91 klpd_rele(klpd_reg_t *p)
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));
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.
109 static klpd_reg_t *
110 klpd_rele_next(klpd_reg_t *p)
112 klpd_reg_t *r = p->klpd_next;
114 klpd_rele(p);
115 return (r);
119 static void
120 klpd_hold(klpd_reg_t *p)
122 atomic_inc_32(&p->klpd_ref);
126 * Remove registration from where it is registered. Returns next in list.
128 static void
129 klpd_unlink(klpd_reg_t *p)
131 ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
133 if (p->klpd_refp != NULL)
134 *p->klpd_refp = p->klpd_next;
136 if (p->klpd_next != NULL)
137 p->klpd_next->klpd_refp = p->klpd_refp;
138 p->klpd_refp = NULL;
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.
148 void
149 klpd_freelist(klpd_reg_t **pp)
151 klpd_reg_t *p;
153 while ((p = *pp) != NULL) {
154 klpd_unlink(p);
155 klpd_rele(p);
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.
164 static klpd_reg_t *
165 klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
167 klpd_reg_t *old = *listp;
169 ASSERT(p->klpd_ref == 1);
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;
183 return (NULL);
187 * The typical call consists of:
188 * - priv_set_t
189 * - some integer data (type, value)
190 * for now, it's just one bit.
192 static klpd_head_t *
193 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
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;
202 klpd_arg_t *kap = NULL;
203 klpd_head_t *khp;
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);
217 tmp = va_arg(ap, char *);
219 if (tmp != NULL && *tmp != '\0')
220 clen = strlen(tmp) + 1;
221 else
222 clen = 0;
224 len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
225 khp = kmem_zalloc(len, KM_SLEEP);
227 khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
228 kap = KLH_ARG(khp);
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);
235 if (clen != 0) {
236 plen = strlen(kap->kla_str);
237 if (plen + clen + 1 >= MAXPATHLEN) {
238 kmem_free(khp, len);
239 return (NULL);
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);
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;
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);
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;
283 return (khp);
286 static int
287 klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
289 door_arg_t da;
290 int res;
291 int dres;
292 klpd_head_t *klh;
294 if (p->klpd_door_pid == curproc->p_pid)
295 return (-1);
297 klh = klpd_marshall(p, req, ap);
299 if (klh == NULL)
300 return (-1);
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);
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);
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);
334 return (res);
337 uint32_t klpd_bad_locks;
340 klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
342 klpd_reg_t *p;
343 int rv = -1;
344 credklpd_t *ckp;
345 zone_t *ckzone;
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.
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);
361 * Enforce the limit set for the call process (still).
363 if (!priv_issubset(req, &CR_LPRIV(cr)))
364 return (-1);
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);
385 /* Try 2: get the project specific klpd */
386 mutex_enter(&klpd_mutex);
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);
395 mutex_enter(&klpd_mutex);
396 klpd_rele(p);
397 mutex_exit(&klpd_mutex);
399 if (rv != -1)
400 return (rv == 0 ? 0 : -1);
401 } else {
402 mutex_exit(&klpd_mutex);
405 /* Try 3: get the global klpd list */
406 ckzone = crgetzone(cr);
407 mutex_enter(&klpd_mutex);
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);
419 p = klpd_rele_next(p);
421 if (rv != -1)
422 break;
423 } else {
424 p = p->klpd_next;
427 mutex_exit(&klpd_mutex);
428 return (rv == 0 ? 0 : -1);
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.
438 * MY_PID (pid 0) can be used to create or change the context for
439 * the current process, typically done after fork().
441 * A negative value can be used to register a klpd globally.
443 * The per-credential klpd needs to be cleaned up when entering
444 * a zone or unsetting the flag.
447 klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
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;
459 if (CR_FLAGS(cr) & PRIV_XPOLICY)
460 return (set_errno(EINVAL));
462 if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
463 return (set_errno(EFAULT));
465 if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
466 return (set_errno(EPERM));
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));
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.
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));
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));
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;
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();
530 /* No need to lock, sole reference to ckp */
531 kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
533 if (kpd != NULL)
534 klpd_rele(kpd);
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 */
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));
556 mutex_enter(&p->p_crlock);
557 crhold(pcr = p->p_cred);
558 mutex_exit(&pidlock);
559 mutex_exit(&p->p_crlock);
561 * We're going to update the credential's ckp in place;
562 * this requires that it exists.
564 ckp = crgetcrklpd(pcr);
565 if (ckp == NULL) {
566 crfree(pcr);
567 klpd_rele(kpd);
568 return (set_errno(EINVAL));
570 crklpd_setreg(ckp, kpd);
571 crfree(pcr);
574 return (0);
577 static int
578 klpd_unreg_dh(door_handle_t dh)
580 klpd_reg_t *p;
582 mutex_enter(&klpd_mutex);
583 for (p = klpd_list; p != NULL; p = p->klpd_next) {
584 if (p->klpd_door == dh)
585 break;
587 if (p == NULL) {
588 mutex_exit(&klpd_mutex);
589 return (EINVAL);
591 if (p->klpd_indel != 0) {
592 mutex_exit(&klpd_mutex);
593 return (EAGAIN);
595 p->klpd_indel = 1;
596 klpd_rele(p);
597 mutex_exit(&klpd_mutex);
598 return (0);
602 klpd_unreg(int did, idtype_t type, id_t id)
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;
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));
627 dh = door_ki_lookup(did);
628 if (dh == NULL) {
629 if (kpp != NULL)
630 project_rele(kpp);
631 return (set_errno(EINVAL));
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));
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;
661 ckp = crgetcrklpd(p->p_cred);
662 if (ckp != NULL) {
663 crklpd_setreg(ckp, NULL);
664 } else {
665 res = ESRCH;
667 mutex_exit(&p->p_crlock);
669 out:
670 door_ki_rele(dh);
672 if (res != 0)
673 return (set_errno(res));
674 return (0);
677 void
678 crklpd_hold(credklpd_t *crkpd)
680 atomic_inc_32(&crkpd->crkl_ref);
683 void
684 crklpd_rele(credklpd_t *crkpd)
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));
694 static credklpd_t *
695 crklpd_alloc(void)
697 credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
699 mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
700 res->crkl_ref = 1;
701 res->crkl_reg = NULL;
703 return (res);
706 void
707 crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
709 klpd_reg_t *old;
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);
719 mutex_exit(&crk->crkl_lock);
721 if (old != NULL)
722 klpd_rele(old);
725 /* Allocate and register the pfexec specific callback */
727 pfexec_reg(int did)
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());
735 if (err != 0)
736 return (set_errno(err));
738 dh = door_ki_lookup(did);
739 if (dh == NULL || door_ki_info(dh, &di) != 0)
740 return (set_errno(EBADF));
742 pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
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);
754 return (0);
758 pfexec_unreg(int did)
760 door_handle_t dh;
761 int err = 0;
762 zone_t *myzone = crgetzone(CRED());
763 klpd_reg_t *pfd;
765 dh = door_ki_lookup(did);
766 if (dh == NULL)
767 return (set_errno(EBADF));
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;
777 mutex_exit(&myzone->zone_lock);
778 door_ki_rele(dh);
780 * crfree() cannot be called with zone_lock held; it is called
781 * indirectly through closing the door handle
783 if (pfd != NULL)
784 klpd_rele(pfd);
785 if (err != 0)
786 return (set_errno(err));
787 return (0);
790 static int
791 get_path(char *buf, const char *path, int len)
793 size_t lc;
794 char *s;
796 if (len < 0)
797 len = strlen(path);
799 if (*path == '/' && len < MAXPATHLEN) {
800 (void) strcpy(buf, path);
801 return (0);
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.
811 if (dogetcwd(buf, MAXPATHLEN) != 0)
812 return (-1);
814 lc = strlen(buf);
816 while (len > 3 && strncmp("../", path, 3) == 0) {
817 len -= 3;
818 path += 3;
820 s = strrchr(buf, '/');
821 if (s == NULL || s == buf)
822 return (-1);
824 *s = '\0';
825 lc = s - buf;
827 /* Add a "/" and a NUL */
828 if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
829 return (-1);
831 buf[lc] = '/';
832 (void) strcpy(buf + lc + 1, path);
834 return (0);
838 * Perform the pfexec upcall.
840 * The pfexec upcall is different from the klpd_upcall in that a failure
841 * will lead to a denial of execution.
844 pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
845 boolean_t *scrub)
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);
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);
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);
873 if (pfd->klpd_door_pid == curproc->p_pid) {
874 klpd_rele(pfd);
875 return (0);
878 pap = kmem_zalloc(pasize, KM_SLEEP);
880 if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
881 goto out1;
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);
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);
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;
913 prp = (pfexec_reply_t *)da.rbuf;
915 * Check the size of the result and the alignment of the
916 * privilege sets.
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;
926 * Get results:
927 * allow/allow with additional credentials/disallow[*]
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.
936 if (!prp->pfr_allowed) {
937 err = EACCES;
938 goto out;
940 if (!prp->pfr_setcred) {
941 err = 0;
942 goto out;
944 ncr = crdup((cred_t *)cr);
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.
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;
957 *scrub = prp->pfr_scrubenv;
959 if (prp->pfr_clearflag)
960 CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
962 /* We cannot exceed our Limit set, no matter what */
963 iset = PFEXEC_REPLY_IPRIV(prp);
965 if (iset != NULL) {
966 if (!priv_issubset(iset, &CR_LPRIV(ncr)))
967 goto out;
968 priv_union(iset, &CR_IPRIV(ncr));
971 /* Nor can we increate our Limit set itself */
972 lset = PFEXEC_REPLY_LPRIV(prp);
974 if (lset != NULL) {
975 if (!priv_issubset(lset, &CR_LPRIV(ncr)))
976 goto out;
977 CR_LPRIV(ncr) = *lset;
980 /* Exec will do the standard set operations */
982 err = 0;
983 out:
984 if (da.rbuf != (char *)&pr)
985 kmem_free(da.rbuf, da.rsize);
986 out1:
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);
995 return (err);
999 get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
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);
1011 mutex_enter(&myzone->zone_lock);
1012 if ((pfd = myzone->zone_pfexecd) != NULL)
1013 klpd_hold(pfd);
1014 mutex_exit(&myzone->zone_lock);
1016 if (pfd == NULL)
1017 return (-1);
1019 if (pfd->klpd_door_pid == curproc->p_pid) {
1020 klpd_rele(pfd);
1021 return (0);
1024 pap = kmem_zalloc(pasize, KM_SLEEP);
1026 if (get_path(pap->pfa_path, respn, -1) == -1)
1027 goto out1;
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 */
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);
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;
1055 * Check the size of the result, it's a privilege set.
1057 if (da.rsize != sizeof (priv_set_t))
1058 goto out;
1060 fset = (priv_set_t *)da.rbuf;
1063 * We restrict the forced privileges with whatever is available in
1064 * the current zone.
1066 zkcr = zone_kcred();
1067 priv_intersect(&CR_LPRIV(zkcr), fset);
1070 * But we fail if the forced privileges are not found in the current
1071 * Limit set.
1073 if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1074 err = EACCES;
1075 } else if (!priv_isemptyset(fset)) {
1076 err = 0;
1077 *set = *fset;
1079 out:
1080 if (da.rbuf != (char *)&pmem)
1081 kmem_free(da.rbuf, da.rsize);
1082 out1:
1083 kmem_free(pap, pasize);
1084 klpd_rele(pfd);
1085 return (err);
1089 check_user_privs(const cred_t *cr, const priv_set_t *set)
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;
1100 mutex_enter(&myzone->zone_lock);
1101 if ((pfd = myzone->zone_pfexecd) != NULL)
1102 klpd_hold(pfd);
1103 mutex_exit(&myzone->zone_lock);
1105 if (pfd == NULL)
1106 return (-1);
1108 if (pfd->klpd_door_pid == curproc->p_pid) {
1109 klpd_rele(pfd);
1110 return (0);
1113 pap = kmem_zalloc(pasize, KM_SLEEP);
1115 *(priv_set_t *)&pap->pfa_buf = *set;
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);
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);
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;
1143 * Check the size of the result.
1145 if (da.rsize != sizeof (res))
1146 goto out;
1148 if (*(uint32_t *)da.rbuf == 1)
1149 err = 0;
1150 out:
1151 if (da.rbuf != (char *)&res)
1152 kmem_free(da.rbuf, da.rsize);
1153 out1:
1154 kmem_free(pap, pasize);
1155 klpd_rele(pfd);
1156 return (err);