5853 pfexec_call() error handling could be improved
[unleashed.git] / usr / src / uts / common / os / klpd.c
blob55734ae75714b828496f43b38bafe75f5860e32b
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 <netinet/in.h>
44 #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
46 static kmutex_t klpd_mutex;
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;
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.
67 * The reference count is atomically updated.
69 * But the registration probably needs to be updated under a lock.
71 typedef struct credklpd {
72 kmutex_t crkl_lock;
73 klpd_reg_t *crkl_reg;
74 uint32_t crkl_ref;
75 } credklpd_t;
77 klpd_reg_t *klpd_list;
79 static void klpd_unlink(klpd_reg_t *);
80 static int klpd_unreg_dh(door_handle_t);
82 static credklpd_t *crklpd_alloc(void);
84 void crklpd_setreg(credklpd_t *, klpd_reg_t *);
86 extern size_t max_vnode_path;
88 void
89 klpd_rele(klpd_reg_t *p)
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));
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.
107 static klpd_reg_t *
108 klpd_rele_next(klpd_reg_t *p)
110 klpd_reg_t *r = p->klpd_next;
112 klpd_rele(p);
113 return (r);
117 static void
118 klpd_hold(klpd_reg_t *p)
120 atomic_inc_32(&p->klpd_ref);
124 * Remove registration from where it is registered. Returns next in list.
126 static void
127 klpd_unlink(klpd_reg_t *p)
129 ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
131 if (p->klpd_refp != NULL)
132 *p->klpd_refp = p->klpd_next;
134 if (p->klpd_next != NULL)
135 p->klpd_next->klpd_refp = p->klpd_refp;
136 p->klpd_refp = NULL;
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.
146 void
147 klpd_freelist(klpd_reg_t **pp)
149 klpd_reg_t *p;
151 while ((p = *pp) != NULL) {
152 klpd_unlink(p);
153 klpd_rele(p);
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.
162 static klpd_reg_t *
163 klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
165 klpd_reg_t *old = *listp;
167 ASSERT(p->klpd_ref == 1);
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;
181 return (NULL);
185 * The typical call consists of:
186 * - priv_set_t
187 * - some integer data (type, value)
188 * for now, it's just one bit.
190 static klpd_head_t *
191 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
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;
200 klpd_arg_t *kap = NULL;
201 klpd_head_t *khp;
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);
215 tmp = va_arg(ap, char *);
217 if (tmp != NULL && *tmp != '\0')
218 clen = strlen(tmp) + 1;
219 else
220 clen = 0;
222 len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
223 khp = kmem_zalloc(len, KM_SLEEP);
225 khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
226 kap = KLH_ARG(khp);
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);
233 if (clen != 0) {
234 plen = strlen(kap->kla_str);
235 if (plen + clen + 1 >= MAXPATHLEN) {
236 kmem_free(khp, len);
237 return (NULL);
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);
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;
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);
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;
281 return (khp);
284 static int
285 klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
287 door_arg_t da;
288 int res;
289 int dres;
290 klpd_head_t *klh;
292 if (p->klpd_door_pid == curproc->p_pid)
293 return (-1);
295 klh = klpd_marshall(p, req, ap);
297 if (klh == NULL)
298 return (-1);
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);
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);
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);
332 return (res);
335 uint32_t klpd_bad_locks;
338 klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
340 klpd_reg_t *p;
341 int rv = -1;
342 credklpd_t *ckp;
343 zone_t *ckzone;
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.
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);
359 * Enforce the limit set for the call process (still).
361 if (!priv_issubset(req, &CR_LPRIV(cr)))
362 return (-1);
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);
383 /* Try 2: get the project specific klpd */
384 mutex_enter(&klpd_mutex);
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);
393 mutex_enter(&klpd_mutex);
394 klpd_rele(p);
395 mutex_exit(&klpd_mutex);
397 if (rv != -1)
398 return (rv == 0 ? 0 : -1);
399 } else {
400 mutex_exit(&klpd_mutex);
403 /* Try 3: get the global klpd list */
404 ckzone = crgetzone(cr);
405 mutex_enter(&klpd_mutex);
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);
417 p = klpd_rele_next(p);
419 if (rv != -1)
420 break;
421 } else {
422 p = p->klpd_next;
425 mutex_exit(&klpd_mutex);
426 return (rv == 0 ? 0 : -1);
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.
436 * MY_PID (pid 0) can be used to create or change the context for
437 * the current process, typically done after fork().
439 * A negative value can be used to register a klpd globally.
441 * The per-credential klpd needs to be cleaned up when entering
442 * a zone or unsetting the flag.
445 klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
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;
457 if (CR_FLAGS(cr) & PRIV_XPOLICY)
458 return (set_errno(EINVAL));
460 if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
461 return (set_errno(EFAULT));
463 if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
464 return (set_errno(EPERM));
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));
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.
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));
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));
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;
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();
528 /* No need to lock, sole reference to ckp */
529 kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
531 if (kpd != NULL)
532 klpd_rele(kpd);
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 */
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));
554 mutex_enter(&p->p_crlock);
555 crhold(pcr = p->p_cred);
556 mutex_exit(&pidlock);
557 mutex_exit(&p->p_crlock);
559 * We're going to update the credential's ckp in place;
560 * this requires that it exists.
562 ckp = crgetcrklpd(pcr);
563 if (ckp == NULL) {
564 crfree(pcr);
565 klpd_rele(kpd);
566 return (set_errno(EINVAL));
568 crklpd_setreg(ckp, kpd);
569 crfree(pcr);
572 return (0);
575 static int
576 klpd_unreg_dh(door_handle_t dh)
578 klpd_reg_t *p;
580 mutex_enter(&klpd_mutex);
581 for (p = klpd_list; p != NULL; p = p->klpd_next) {
582 if (p->klpd_door == dh)
583 break;
585 if (p == NULL) {
586 mutex_exit(&klpd_mutex);
587 return (EINVAL);
589 if (p->klpd_indel != 0) {
590 mutex_exit(&klpd_mutex);
591 return (EAGAIN);
593 p->klpd_indel = 1;
594 klpd_rele(p);
595 mutex_exit(&klpd_mutex);
596 return (0);
600 klpd_unreg(int did, idtype_t type, id_t id)
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;
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));
625 dh = door_ki_lookup(did);
626 if (dh == NULL) {
627 if (kpp != NULL)
628 project_rele(kpp);
629 return (set_errno(EINVAL));
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));
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;
659 ckp = crgetcrklpd(p->p_cred);
660 if (ckp != NULL) {
661 crklpd_setreg(ckp, NULL);
662 } else {
663 res = ESRCH;
665 mutex_exit(&p->p_crlock);
667 out:
668 door_ki_rele(dh);
670 if (res != 0)
671 return (set_errno(res));
672 return (0);
675 void
676 crklpd_hold(credklpd_t *crkpd)
678 atomic_inc_32(&crkpd->crkl_ref);
681 void
682 crklpd_rele(credklpd_t *crkpd)
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));
692 static credklpd_t *
693 crklpd_alloc(void)
695 credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
697 mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
698 res->crkl_ref = 1;
699 res->crkl_reg = NULL;
701 return (res);
704 void
705 crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
707 klpd_reg_t *old;
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);
717 mutex_exit(&crk->crkl_lock);
719 if (old != NULL)
720 klpd_rele(old);
723 /* Allocate and register the pfexec specific callback */
725 pfexec_reg(int did)
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());
733 if (err != 0)
734 return (set_errno(err));
736 dh = door_ki_lookup(did);
737 if (dh == NULL || door_ki_info(dh, &di) != 0)
738 return (set_errno(EBADF));
740 pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
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);
752 return (0);
756 pfexec_unreg(int did)
758 door_handle_t dh;
759 int err = 0;
760 zone_t *myzone = crgetzone(CRED());
761 klpd_reg_t *pfd;
763 dh = door_ki_lookup(did);
764 if (dh == NULL)
765 return (set_errno(EBADF));
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;
775 mutex_exit(&myzone->zone_lock);
776 door_ki_rele(dh);
778 * crfree() cannot be called with zone_lock held; it is called
779 * indirectly through closing the door handle
781 if (pfd != NULL)
782 klpd_rele(pfd);
783 if (err != 0)
784 return (set_errno(err));
785 return (0);
788 static int
789 get_path(char *buf, const char *path, int len)
791 size_t lc;
792 char *s;
794 if (len < 0)
795 len = strlen(path);
797 if (*path == '/' && len < MAXPATHLEN) {
798 (void) strcpy(buf, path);
799 return (0);
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.
809 if (dogetcwd(buf, MAXPATHLEN) != 0)
810 return (-1);
812 lc = strlen(buf);
814 while (len > 3 && strncmp("../", path, 3) == 0) {
815 len -= 3;
816 path += 3;
818 s = strrchr(buf, '/');
819 if (s == NULL || s == buf)
820 return (-1);
822 *s = '\0';
823 lc = s - buf;
825 /* Add a "/" and a NUL */
826 if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
827 return (-1);
829 buf[lc] = '/';
830 (void) strcpy(buf + lc + 1, path);
832 return (0);
836 * Perform the pfexec upcall.
838 * The pfexec upcall is different from the klpd_upcall in that a failure
839 * will lead to a denial of execution.
842 pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
843 boolean_t *scrub)
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);
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);
863 if (pfd == NULL)
864 return (0);
866 if (pfd->klpd_door_pid == curproc->p_pid) {
867 klpd_rele(pfd);
868 return (0);
871 pap = kmem_zalloc(pasize, KM_SLEEP);
873 if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
874 goto out1;
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);
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);
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;
903 prp = (pfexec_reply_t *)da.rbuf;
905 * Check the size of the result and the alignment of the
906 * privilege sets.
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;
916 * Get results:
917 * allow/allow with additional credentials/disallow[*]
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.
926 if (!prp->pfr_allowed) {
927 err = EACCES;
928 goto out;
930 if (!prp->pfr_setcred) {
931 err = 0;
932 goto out;
934 ncr = crdup((cred_t *)cr);
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.
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;
947 *scrub = prp->pfr_scrubenv;
949 if (prp->pfr_clearflag)
950 CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
952 /* We cannot exceed our Limit set, no matter what */
953 iset = PFEXEC_REPLY_IPRIV(prp);
955 if (iset != NULL) {
956 if (!priv_issubset(iset, &CR_LPRIV(ncr)))
957 goto out;
958 priv_union(iset, &CR_IPRIV(ncr));
961 /* Nor can we increate our Limit set itself */
962 lset = PFEXEC_REPLY_LPRIV(prp);
964 if (lset != NULL) {
965 if (!priv_issubset(lset, &CR_LPRIV(ncr)))
966 goto out;
967 CR_LPRIV(ncr) = *lset;
970 /* Exec will do the standard set operations */
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);
985 return (err);
989 get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
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);
1001 mutex_enter(&myzone->zone_lock);
1002 if ((pfd = myzone->zone_pfexecd) != NULL)
1003 klpd_hold(pfd);
1004 mutex_exit(&myzone->zone_lock);
1006 if (pfd == NULL)
1007 return (-1);
1009 if (pfd->klpd_door_pid == curproc->p_pid) {
1010 klpd_rele(pfd);
1011 return (0);
1014 pap = kmem_zalloc(pasize, KM_SLEEP);
1016 if (get_path(pap->pfa_path, respn, -1) == -1)
1017 goto out1;
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 */
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);
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;
1045 * Check the size of the result, it's a privilege set.
1047 if (da.rsize != sizeof (priv_set_t))
1048 goto out;
1050 fset = (priv_set_t *)da.rbuf;
1053 * We restrict the forced privileges with whatever is available in
1054 * the current zone.
1056 zkcr = zone_kcred();
1057 priv_intersect(&CR_LPRIV(zkcr), fset);
1060 * But we fail if the forced privileges are not found in the current
1061 * Limit set.
1063 if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1064 err = EACCES;
1065 } else if (!priv_isemptyset(fset)) {
1066 err = 0;
1067 *set = *fset;
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);
1079 check_user_privs(const cred_t *cr, const priv_set_t *set)
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;
1090 mutex_enter(&myzone->zone_lock);
1091 if ((pfd = myzone->zone_pfexecd) != NULL)
1092 klpd_hold(pfd);
1093 mutex_exit(&myzone->zone_lock);
1095 if (pfd == NULL)
1096 return (-1);
1098 if (pfd->klpd_door_pid == curproc->p_pid) {
1099 klpd_rele(pfd);
1100 return (0);
1103 pap = kmem_zalloc(pasize, KM_SLEEP);
1105 *(priv_set_t *)&pap->pfa_buf = *set;
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);
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);
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;
1133 * Check the size of the result.
1135 if (da.rsize != sizeof (res))
1136 goto out;
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);