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 2008 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <sys/atomic.h> 30 #include <sys/door.h> 31 #include <sys/proc.h> 32 #include <sys/cred_impl.h> 33 #include <sys/policy.h> 34 #include <sys/priv.h> 35 #include <sys/klpd.h> 36 #include <sys/errno.h> 37 #include <sys/kmem.h> 38 #include <sys/project.h> 39 #include <sys/systm.h> 40 #include <sys/sysmacros.h> 41 #include <sys/pathname.h> 42 #include <sys/varargs.h> 43 #include <sys/zone.h> 44 #include <netinet/in.h> 45 46 #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1)) 47 48 static kmutex_t klpd_mutex; 49 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; 60 61 62 /* 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. 68 * 69 * The reference count is atomically updated. 70 * 71 * But the registration probably needs to be updated under a lock. 72 */ 73 typedef struct credklpd { 74 kmutex_t crkl_lock; 75 klpd_reg_t *crkl_reg; 76 uint32_t crkl_ref; 77 } credklpd_t; 78 79 klpd_reg_t *klpd_list; 80 81 static void klpd_unlink(klpd_reg_t *); 82 static int klpd_unreg_dh(door_handle_t); 83 84 static credklpd_t *crklpd_alloc(void); 85 86 void crklpd_setreg(credklpd_t *, klpd_reg_t *); 87 88 extern size_t max_vnode_path; 89 90 void 91 klpd_rele(klpd_reg_t *p) 92 { 93 if (atomic_add_32_nv(&p->klpd_ref, -1) == 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)); 100 } 101 } 102 103 /* 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. 108 */ 109 static klpd_reg_t * 110 klpd_rele_next(klpd_reg_t *p) 111 { 112 klpd_reg_t *r = p->klpd_next; 113 114 klpd_rele(p); 115 return (r); 116 } 117 118 119 static void 120 klpd_hold(klpd_reg_t *p) 121 { 122 atomic_add_32(&p->klpd_ref, 1); 123 } 124 125 /* 126 * Remove registration from where it is registered. Returns next in list. 127 */ 128 static void 129 klpd_unlink(klpd_reg_t *p) 130 { 131 ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p); 132 133 if (p->klpd_refp != NULL) 134 *p->klpd_refp = p->klpd_next; 135 136 if (p->klpd_next != NULL) 137 p->klpd_next->klpd_refp = p->klpd_refp; 138 p->klpd_refp = NULL; 139 } 140 141 /* 142 * Remove the head of the klpd list and decrement its refcnt. 143 * The lock guarding the list should be held; this function is 144 * called when we are sure we want to remove the entry from the 145 * list but not so sure that the reference count has dropped back to 146 * 1 and is specifically intended to remove the non-list variants. 147 */ 148 void 149 klpd_remove(klpd_reg_t **pp) 150 { 151 klpd_reg_t *p = *pp; 152 if (p == NULL) 153 return; 154 ASSERT(p->klpd_next == NULL); 155 klpd_unlink(p); 156 klpd_rele(p); 157 } 158 159 /* 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. 163 */ 164 static klpd_reg_t * 165 klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single) 166 { 167 klpd_reg_t *old = *listp; 168 169 ASSERT(p->klpd_ref == 1); 170 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; 182 } 183 return (NULL); 184 } 185 186 /* 187 * The typical call consists of: 188 * - priv_set_t 189 * - some integer data (type, value) 190 * for now, it's just one bit. 191 */ 192 static klpd_head_t * 193 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap) 194 { 195 char *comp; 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; 201 202 klpd_arg_t *kap = NULL; 203 klpd_head_t *khp; 204 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); 216 217 comp = va_arg(ap, char *); 218 219 if (comp != NULL && *comp != '\0') 220 clen = strlen(comp) + 1; 221 else 222 clen = 0; 223 224 len += ROUNDUP(MAXPATHLEN, sizeof (uint_t)); 225 khp = kmem_zalloc(len, KM_SLEEP); 226 227 khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t); 228 kap = KLH_ARG(khp); 229 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); 234 } 235 if (clen != 0) { 236 plen = strlen(kap->kla_str); 237 if (plen + clen + 1 >= MAXPATHLEN) { 238 kmem_free(khp, len); 239 return (NULL); 240 } 241 /* Don't make root into a double "/" */ 242 if (plen <= 2) 243 plen = 0; 244 kap->kla_str[plen] = '/'; 245 bcopy(comp, &kap->kla_str[plen + 1], clen); 246 } 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; 259 } 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); 274 } 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; 282 } 283 return (khp); 284 } 285 286 static int 287 klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap) 288 { 289 door_arg_t da; 290 int res; 291 int dres; 292 klpd_head_t *klh; 293 294 if (p->klpd_door_pid == curproc->p_pid) 295 return (-1); 296 297 klh = klpd_marshall(p, req, ap); 298 299 if (klh == NULL) 300 return (-1); 301 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); 308 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); 326 } 327 } 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); 333 } 334 return (res); 335 } 336 337 uint32_t klpd_bad_locks; 338 339 int 340 klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap) 341 { 342 klpd_reg_t *p; 343 int rv = -1; 344 credklpd_t *ckp; 345 zone_t *ckzone; 346 347 /* 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. 353 */ 354 if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) || 355 mutex_owned(&curproc->p_crlock)) { 356 atomic_add_32(&klpd_bad_locks, 1); 357 return (-1); 358 } 359 360 /* 361 * Enforce the limit set for the call process (still). 362 */ 363 if (!priv_issubset(req, &CR_LPRIV(cr))) 364 return (-1); 365 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); 382 } 383 } 384 385 /* Try 2: get the project specific klpd */ 386 mutex_enter(&klpd_mutex); 387 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); 394 } 395 mutex_enter(&klpd_mutex); 396 klpd_rele(p); 397 mutex_exit(&klpd_mutex); 398 399 if (rv != -1) 400 return (rv == 0 ? 0 : -1); 401 } else { 402 mutex_exit(&klpd_mutex); 403 } 404 405 /* Try 3: get the global klpd list */ 406 ckzone = crgetzone(cr); 407 mutex_enter(&klpd_mutex); 408 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); 418 419 p = klpd_rele_next(p); 420 421 if (rv != -1) 422 break; 423 } else { 424 p = p->klpd_next; 425 } 426 } 427 mutex_exit(&klpd_mutex); 428 return (rv == 0 ? 0 : -1); 429 } 430 431 /* 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. 437 * 438 * MY_PID (pid 0) can be used to create or change the context for 439 * the current process, typically done after fork(). 440 * 441 * A negative value can be used to register a klpd globally. 442 * 443 * The per-credential klpd needs to be cleaned up when entering 444 * a zone or unsetting the flag. 445 */ 446 int 447 klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf) 448 { 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; 458 459 if (CR_FLAGS(cr) & PRIV_XPOLICY) 460 return (set_errno(EINVAL)); 461 462 if (copyin(psetbuf, &pset, sizeof (priv_set_t))) 463 return (set_errno(EFAULT)); 464 465 if (!priv_issubset(&pset, &CR_OEPRIV(cr))) 466 return (set_errno(EPERM)); 467 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)); 485 } 486 487 488 /* 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. 491 */ 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)); 499 } 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)); 505 } 506 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; 513 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(); 529 530 /* No need to lock, sole reference to ckp */ 531 kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE); 532 533 if (kpd != NULL) 534 klpd_rele(kpd); 535 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 */ 541 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)); 555 } 556 mutex_enter(&p->p_crlock); 557 crhold(pcr = p->p_cred); 558 mutex_exit(&pidlock); 559 mutex_exit(&p->p_crlock); 560 /* 561 * We're going to update the credential's ckp in place; 562 * this requires that it exists. 563 */ 564 ckp = crgetcrklpd(pcr); 565 if (ckp == NULL) { 566 crfree(pcr); 567 klpd_rele(kpd); 568 return (set_errno(EINVAL)); 569 } 570 crklpd_setreg(ckp, kpd); 571 crfree(pcr); 572 } 573 574 return (0); 575 } 576 577 static int 578 klpd_unreg_dh(door_handle_t dh) 579 { 580 klpd_reg_t *p; 581 582 mutex_enter(&klpd_mutex); 583 for (p = klpd_list; p != NULL; p = p->klpd_next) { 584 if (p->klpd_door == dh) 585 break; 586 } 587 if (p == NULL) { 588 mutex_exit(&klpd_mutex); 589 return (EINVAL); 590 } 591 if (p->klpd_indel != 0) { 592 mutex_exit(&klpd_mutex); 593 return (EAGAIN); 594 } 595 p->klpd_indel = 1; 596 klpd_rele(p); 597 mutex_exit(&klpd_mutex); 598 return (0); 599 } 600 601 int 602 klpd_unreg(int did, idtype_t type, id_t id) 603 { 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; 611 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)); 625 } 626 627 dh = door_ki_lookup(did); 628 if (dh == NULL) { 629 if (kpp != NULL) 630 project_rele(kpp); 631 return (set_errno(EINVAL)); 632 } 633 634 if (kpp != NULL) { 635 mutex_enter(&klpd_mutex); 636 if (kpp->kpj_klpd == NULL) 637 res = ESRCH; 638 else 639 klpd_remove(&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)); 650 } 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; 659 } 660 661 ckp = crgetcrklpd(p->p_cred); 662 if (ckp != NULL) { 663 crklpd_setreg(ckp, NULL); 664 } else { 665 res = ESRCH; 666 } 667 mutex_exit(&p->p_crlock); 668 669 out: 670 door_ki_rele(dh); 671 672 if (res != 0) 673 return (set_errno(res)); 674 return (0); 675 } 676 677 void 678 crklpd_hold(credklpd_t *crkpd) 679 { 680 atomic_add_32(&crkpd->crkl_ref, 1); 681 } 682 683 void 684 crklpd_rele(credklpd_t *crkpd) 685 { 686 if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 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)); 691 } 692 } 693 694 static credklpd_t * 695 crklpd_alloc(void) 696 { 697 credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP); 698 699 mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL); 700 res->crkl_ref = 1; 701 res->crkl_reg = NULL; 702 703 return (res); 704 } 705 706 void 707 crklpd_setreg(credklpd_t *crk, klpd_reg_t *new) 708 { 709 klpd_reg_t *old; 710 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); 718 } 719 mutex_exit(&crk->crkl_lock); 720 721 if (old != NULL) 722 klpd_rele(old); 723 } 724