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 88 klpd_rele(klpd_reg_t *p) 89 { 90 if (atomic_dec_32_nv(&p->klpd_ref) == 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 * 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 117 klpd_hold(klpd_reg_t *p) 118 { 119 atomic_inc_32(&p->klpd_ref); 120 } 121 122 /* 123 * Remove registration from where it is registered. Returns next in list. 124 */ 125 static void 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 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 * 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 * 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 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 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_inc_32(&klpd_bad_locks); 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 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 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 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 675 crklpd_hold(credklpd_t *crkpd) 676 { 677 atomic_inc_32(&crkpd->crkl_ref); 678 } 679 680 void 681 crklpd_rele(credklpd_t *crkpd) 682 { 683 if (atomic_dec_32_nv(&crkpd->crkl_ref) == 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 * 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 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 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 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 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 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 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 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