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