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(p->klpd_door, &da)) != 0) { 310 switch (dres) { 311 case EAGAIN: 312 delay(1); 313 continue; 314 case EINVAL: 315 case EBADF: 316 /* Bad door, don't call it again. */ 317 (void) klpd_unreg_dh(p->klpd_door); 318 /* FALLTHROUGH */ 319 case EINTR: 320 /* Pending signal, nothing we can do. */ 321 /* FALLTHROUGH */ 322 default: 323 kmem_free(klh, klh->klh_len); 324 return (-1); 325 } 326 } 327 kmem_free(klh, klh->klh_len); 328 /* Bogus return value, must be a failure */ 329 if (da.rbuf != (char *)&res) { 330 kmem_free(da.rbuf, da.rsize); 331 return (-1); 332 } 333 return (res); 334 } 335 336 uint32_t klpd_bad_locks; 337 338 int 339 klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap) 340 { 341 klpd_reg_t *p; 342 int rv = -1; 343 credklpd_t *ckp; 344 zone_t *ckzone; 345 346 /* 347 * These locks must not be held when this code is called; 348 * callbacks to userland with these locks held will result 349 * in issues. That said, the code at the call sides was 350 * restructured not to call with any of the locks held and 351 * no policies operate by default on most processes. 352 */ 353 if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) || 354 mutex_owned(&curproc->p_crlock)) { 355 atomic_add_32(&klpd_bad_locks, 1); 356 return (-1); 357 } 358 359 /* 360 * Enforce the limit set for the call process (still). 361 */ 362 if (!priv_issubset(req, &CR_LPRIV(cr))) 363 return (-1); 364 365 /* Try 1: get the credential specific klpd */ 366 if ((ckp = crgetcrklpd(cr)) != NULL) { 367 mutex_enter(&ckp->crkl_lock); 368 if ((p = ckp->crkl_reg) != NULL && 369 p->klpd_indel == 0 && 370 priv_issubset(req, &p->klpd_pset)) { 371 klpd_hold(p); 372 mutex_exit(&ckp->crkl_lock); 373 rv = klpd_do_call(p, req, ap); 374 mutex_enter(&ckp->crkl_lock); 375 klpd_rele(p); 376 mutex_exit(&ckp->crkl_lock); 377 if (rv != -1) 378 return (rv == 0 ? 0 : -1); 379 } else { 380 mutex_exit(&ckp->crkl_lock); 381 } 382 } 383 384 /* Try 2: get the project specific klpd */ 385 mutex_enter(&klpd_mutex); 386 387 if ((p = curproj->kpj_klpd) != NULL) { 388 klpd_hold(p); 389 mutex_exit(&klpd_mutex); 390 if (p->klpd_indel == 0 && 391 priv_issubset(req, &p->klpd_pset)) { 392 rv = klpd_do_call(p, req, ap); 393 } 394 mutex_enter(&klpd_mutex); 395 klpd_rele(p); 396 mutex_exit(&klpd_mutex); 397 398 if (rv != -1) 399 return (rv == 0 ? 0 : -1); 400 } else { 401 mutex_exit(&klpd_mutex); 402 } 403 404 /* Try 3: get the global klpd list */ 405 ckzone = crgetzone(cr); 406 mutex_enter(&klpd_mutex); 407 408 for (p = klpd_list; p != NULL; ) { 409 zone_t *kkzone = crgetzone(p->klpd_cred); 410 if ((kkzone == &zone0 || kkzone == ckzone) && 411 p->klpd_indel == 0 && 412 priv_issubset(req, &p->klpd_pset)) { 413 klpd_hold(p); 414 mutex_exit(&klpd_mutex); 415 rv = klpd_do_call(p, req, ap); 416 mutex_enter(&klpd_mutex); 417 418 p = klpd_rele_next(p); 419 420 if (rv != -1) 421 break; 422 } else { 423 p = p->klpd_next; 424 } 425 } 426 mutex_exit(&klpd_mutex); 427 return (rv == 0 ? 0 : -1); 428 } 429 430 /* 431 * Register the klpd. 432 * If the pid_t passed in is positive, update the registration for 433 * the specific process; that is only possible if the process already 434 * has a registration on it. This change of registration will affect 435 * all processes which share common ancestry. 436 * 437 * MY_PID (pid 0) can be used to create or change the context for 438 * the current process, typically done after fork(). 439 * 440 * A negative value can be used to register a klpd globally. 441 * 442 * The per-credential klpd needs to be cleaned up when entering 443 * a zone or unsetting the flag. 444 */ 445 int 446 klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf) 447 { 448 cred_t *cr = CRED(); 449 door_handle_t dh; 450 klpd_reg_t *kpd; 451 priv_set_t pset; 452 door_info_t di; 453 credklpd_t *ckp = NULL; 454 pid_t pid = -1; 455 projid_t proj = -1; 456 kproject_t *kpp = NULL; 457 458 if (CR_FLAGS(cr) & PRIV_XPOLICY) 459 return (set_errno(EINVAL)); 460 461 if (copyin(psetbuf, &pset, sizeof (priv_set_t))) 462 return (set_errno(EFAULT)); 463 464 if (!priv_issubset(&pset, &CR_OEPRIV(cr))) 465 return (set_errno(EPERM)); 466 467 switch (type) { 468 case P_PID: 469 pid = (pid_t)id; 470 if (pid == P_MYPID) 471 pid = curproc->p_pid; 472 if (pid == curproc->p_pid) 473 ckp = crklpd_alloc(); 474 break; 475 case P_PROJID: 476 proj = (projid_t)id; 477 kpp = project_hold_by_id(proj, crgetzone(cr), 478 PROJECT_HOLD_FIND); 479 if (kpp == NULL) 480 return (set_errno(ESRCH)); 481 break; 482 default: 483 return (set_errno(ENOTSUP)); 484 } 485 486 487 /* 488 * Verify the door passed in; it must be a door and we won't 489 * allow processes to be called on their own behalf. 490 */ 491 dh = door_ki_lookup(did); 492 if (dh == NULL || door_ki_info(dh, &di) != 0) { 493 if (ckp != NULL) 494 crklpd_rele(ckp); 495 if (kpp != NULL) 496 project_rele(kpp); 497 return (set_errno(EBADF)); 498 } 499 if (type == P_PID && pid == di.di_target) { 500 if (ckp != NULL) 501 crklpd_rele(ckp); 502 ASSERT(kpp == NULL); 503 return (set_errno(EINVAL)); 504 } 505 506 kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP); 507 crhold(kpd->klpd_cred = cr); 508 kpd->klpd_door = dh; 509 kpd->klpd_door_pid = di.di_target; 510 kpd->klpd_ref = 1; 511 kpd->klpd_pset = pset; 512 513 if (kpp != NULL) { 514 mutex_enter(&klpd_mutex); 515 kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE); 516 mutex_exit(&klpd_mutex); 517 if (kpd != NULL) 518 klpd_rele(kpd); 519 project_rele(kpp); 520 } else if ((int)pid < 0) { 521 /* Global daemon */ 522 mutex_enter(&klpd_mutex); 523 (void) klpd_link(kpd, &klpd_list, B_FALSE); 524 mutex_exit(&klpd_mutex); 525 } else if (pid == curproc->p_pid) { 526 proc_t *p = curproc; 527 cred_t *newcr = cralloc(); 528 529 /* No need to lock, sole reference to ckp */ 530 kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE); 531 532 if (kpd != NULL) 533 klpd_rele(kpd); 534 535 mutex_enter(&p->p_crlock); 536 cr = p->p_cred; 537 crdup_to(cr, newcr); 538 crsetcrklpd(newcr, ckp); 539 p->p_cred = newcr; /* Already held for p_cred */ 540 541 crhold(newcr); /* Hold once for the current thread */ 542 mutex_exit(&p->p_crlock); 543 crfree(cr); /* One for the p_cred */ 544 crset(p, newcr); 545 } else { 546 proc_t *p; 547 cred_t *pcr; 548 mutex_enter(&pidlock); 549 p = prfind(pid); 550 if (p == NULL || !prochasprocperm(p, curproc, CRED())) { 551 mutex_exit(&pidlock); 552 klpd_rele(kpd); 553 return (set_errno(p == NULL ? ESRCH : EPERM)); 554 } 555 mutex_enter(&p->p_crlock); 556 crhold(pcr = p->p_cred); 557 mutex_exit(&pidlock); 558 mutex_exit(&p->p_crlock); 559 /* 560 * We're going to update the credential's ckp in place; 561 * this requires that it exists. 562 */ 563 ckp = crgetcrklpd(pcr); 564 if (ckp == NULL) { 565 crfree(pcr); 566 klpd_rele(kpd); 567 return (set_errno(EINVAL)); 568 } 569 crklpd_setreg(ckp, kpd); 570 crfree(pcr); 571 } 572 573 return (0); 574 } 575 576 static int 577 klpd_unreg_dh(door_handle_t dh) 578 { 579 klpd_reg_t *p; 580 581 mutex_enter(&klpd_mutex); 582 for (p = klpd_list; p != NULL; p = p->klpd_next) { 583 if (p->klpd_door == dh) 584 break; 585 } 586 if (p == NULL) { 587 mutex_exit(&klpd_mutex); 588 return (EINVAL); 589 } 590 if (p->klpd_indel != 0) { 591 mutex_exit(&klpd_mutex); 592 return (EAGAIN); 593 } 594 p->klpd_indel = 1; 595 klpd_rele(p); 596 mutex_exit(&klpd_mutex); 597 return (0); 598 } 599 600 int 601 klpd_unreg(int did, idtype_t type, id_t id) 602 { 603 door_handle_t dh; 604 int res = 0; 605 proc_t *p; 606 pid_t pid; 607 projid_t proj; 608 kproject_t *kpp = NULL; 609 credklpd_t *ckp; 610 611 switch (type) { 612 case P_PID: 613 pid = (pid_t)id; 614 break; 615 case P_PROJID: 616 proj = (projid_t)id; 617 kpp = project_hold_by_id(proj, crgetzone(CRED()), 618 PROJECT_HOLD_FIND); 619 if (kpp == NULL) 620 return (set_errno(ESRCH)); 621 break; 622 default: 623 return (set_errno(ENOTSUP)); 624 } 625 626 dh = door_ki_lookup(did); 627 if (dh == NULL) { 628 if (kpp != NULL) 629 project_rele(kpp); 630 return (set_errno(EINVAL)); 631 } 632 633 if (kpp != NULL) { 634 mutex_enter(&klpd_mutex); 635 if (kpp->kpj_klpd == NULL) 636 res = ESRCH; 637 else 638 klpd_remove(&kpp->kpj_klpd); 639 mutex_exit(&klpd_mutex); 640 project_rele(kpp); 641 goto out; 642 } else if ((int)pid > 0) { 643 mutex_enter(&pidlock); 644 p = prfind(pid); 645 if (p == NULL) { 646 mutex_exit(&pidlock); 647 door_ki_rele(dh); 648 return (set_errno(ESRCH)); 649 } 650 mutex_enter(&p->p_crlock); 651 mutex_exit(&pidlock); 652 } else if (pid == 0) { 653 p = curproc; 654 mutex_enter(&p->p_crlock); 655 } else { 656 res = klpd_unreg_dh(dh); 657 goto out; 658 } 659 660 ckp = crgetcrklpd(p->p_cred); 661 if (ckp != NULL) { 662 crklpd_setreg(ckp, NULL); 663 } else { 664 res = ESRCH; 665 } 666 mutex_exit(&p->p_crlock); 667 668 out: 669 door_ki_rele(dh); 670 671 if (res != 0) 672 return (set_errno(res)); 673 return (0); 674 } 675 676 void 677 crklpd_hold(credklpd_t *crkpd) 678 { 679 atomic_add_32(&crkpd->crkl_ref, 1); 680 } 681 682 void 683 crklpd_rele(credklpd_t *crkpd) 684 { 685 if (atomic_add_32_nv(&crkpd->crkl_ref, -1) == 0) { 686 if (crkpd->crkl_reg != NULL) 687 klpd_rele(crkpd->crkl_reg); 688 mutex_destroy(&crkpd->crkl_lock); 689 kmem_free(crkpd, sizeof (*crkpd)); 690 } 691 } 692 693 static credklpd_t * 694 crklpd_alloc(void) 695 { 696 credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP); 697 698 mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL); 699 res->crkl_ref = 1; 700 res->crkl_reg = NULL; 701 702 return (res); 703 } 704 705 void 706 crklpd_setreg(credklpd_t *crk, klpd_reg_t *new) 707 { 708 klpd_reg_t *old; 709 710 mutex_enter(&crk->crkl_lock); 711 if (new == NULL) { 712 old = crk->crkl_reg; 713 if (old != NULL) 714 klpd_unlink(old); 715 } else { 716 old = klpd_link(new, &crk->crkl_reg, B_TRUE); 717 } 718 mutex_exit(&crk->crkl_lock); 719 720 if (old != NULL) 721 klpd_rele(old); 722 } 723