1 /* $NetBSD: kern_auth.c,v 1.86 2026/01/03 23:58:04 riastradh Exp $ */ 2 3 /*- 4 * Copyright (c) 2005, 2006 Elad Efrat <elad (at) NetBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 3. The name of the author may not be used to endorse or promote products 16 * derived from this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #define __KAUTH_PRIVATE 31 32 #include <sys/cdefs.h> 33 __KERNEL_RCSID(0, "$NetBSD: kern_auth.c,v 1.86 2026/01/03 23:58:04 riastradh Exp $"); 34 35 #include <sys/param.h> 36 #include <sys/types.h> 37 38 #include <sys/atomic.h> 39 #include <sys/kauth.h> 40 #include <sys/kmem.h> 41 #include <sys/pool.h> 42 #include <sys/proc.h> 43 #include <sys/queue.h> 44 #include <sys/rwlock.h> 45 #include <sys/sdt.h> 46 #include <sys/specificdata.h> 47 #include <sys/sysctl.h> 48 #include <sys/ucred.h> 49 #include <sys/vnode.h> 50 51 #include <secmodel/secmodel.h> 52 53 /* 54 * Secmodel-specific credentials. 55 */ 56 struct kauth_key { 57 secmodel_t ks_secmodel; /* secmodel */ 58 specificdata_key_t ks_key; /* key */ 59 }; 60 61 62 /* 63 * Listener. 64 */ 65 struct kauth_listener { 66 kauth_scope_callback_t func; /* callback */ 67 kauth_scope_t scope; /* scope backpointer */ 68 u_int refcnt; /* reference count */ 69 SIMPLEQ_ENTRY(kauth_listener) listener_next; /* listener list */ 70 }; 71 72 /* 73 * Scope. 74 */ 75 struct kauth_scope { 76 const char *id; /* scope name */ 77 void *cookie; /* user cookie */ 78 u_int nlisteners; /* # of listeners */ 79 SIMPLEQ_HEAD(, kauth_listener) listenq; /* listener list */ 80 SIMPLEQ_ENTRY(kauth_scope) next_scope; /* scope list */ 81 }; 82 83 static int kauth_cred_hook(kauth_cred_t, kauth_action_t, void *, void *); 84 85 /* List of scopes and its lock. */ 86 static SIMPLEQ_HEAD(, kauth_scope) scope_list = 87 SIMPLEQ_HEAD_INITIALIZER(scope_list); 88 89 /* Built-in scopes: generic, process. */ 90 static kauth_scope_t kauth_builtin_scope_generic; 91 static kauth_scope_t kauth_builtin_scope_system; 92 static kauth_scope_t kauth_builtin_scope_process; 93 static kauth_scope_t kauth_builtin_scope_network; 94 static kauth_scope_t kauth_builtin_scope_machdep; 95 static kauth_scope_t kauth_builtin_scope_device; 96 static kauth_scope_t kauth_builtin_scope_cred; 97 static kauth_scope_t kauth_builtin_scope_vnode; 98 99 static specificdata_domain_t kauth_domain; 100 static pool_cache_t kauth_cred_cache; 101 102 krwlock_t kauth_lock; 103 104 /* Allocate new, empty kauth credentials. */ 105 kauth_cred_t 106 kauth_cred_alloc(void) 107 { 108 kauth_cred_t cred; 109 110 cred = pool_cache_get(kauth_cred_cache, PR_WAITOK); 111 112 cred->cr_refcnt = 1; 113 cred->cr_uid = 0; 114 cred->cr_euid = 0; 115 cred->cr_svuid = 0; 116 cred->cr_gid = 0; 117 cred->cr_egid = 0; 118 cred->cr_svgid = 0; 119 cred->cr_ngroups = 0; 120 121 specificdata_init(kauth_domain, &cred->cr_sd); 122 kauth_cred_hook(cred, KAUTH_CRED_INIT, NULL, NULL); 123 124 return (cred); 125 } 126 127 /* Increment reference count to cred. */ 128 kauth_cred_t 129 kauth_cred_hold(kauth_cred_t cred) 130 { 131 KASSERT(cred != NULL); 132 KASSERT(cred != NOCRED); 133 KASSERT(cred != FSCRED); 134 KASSERT(cred->cr_refcnt > 0); 135 136 atomic_inc_uint(&cred->cr_refcnt); 137 return cred; 138 } 139 140 /* Decrease reference count to cred. If reached zero, free it. */ 141 void 142 kauth_cred_free(kauth_cred_t cred) 143 { 144 145 KASSERT(cred != NULL); 146 KASSERT(cred != NOCRED); 147 KASSERT(cred != FSCRED); 148 KASSERT(cred->cr_refcnt > 0); 149 ASSERT_SLEEPABLE(); 150 151 membar_release(); 152 if (atomic_dec_uint_nv(&cred->cr_refcnt) > 0) 153 return; 154 membar_acquire(); 155 156 kauth_cred_hook(cred, KAUTH_CRED_FREE, NULL, NULL); 157 specificdata_fini(kauth_domain, &cred->cr_sd); 158 pool_cache_put(kauth_cred_cache, cred); 159 } 160 161 static void 162 kauth_cred_clone1(kauth_cred_t from, kauth_cred_t to, bool copy_groups) 163 { 164 KASSERT(from != NULL); 165 KASSERT(from != NOCRED); 166 KASSERT(from != FSCRED); 167 KASSERT(to != NULL); 168 KASSERT(to != NOCRED); 169 KASSERT(to != FSCRED); 170 KASSERT(from->cr_refcnt > 0); 171 172 to->cr_uid = from->cr_uid; 173 to->cr_euid = from->cr_euid; 174 to->cr_svuid = from->cr_svuid; 175 to->cr_gid = from->cr_gid; 176 to->cr_egid = from->cr_egid; 177 to->cr_svgid = from->cr_svgid; 178 if (copy_groups) { 179 to->cr_ngroups = from->cr_ngroups; 180 memcpy(to->cr_groups, from->cr_groups, sizeof(to->cr_groups)); 181 } 182 183 kauth_cred_hook(from, KAUTH_CRED_COPY, to, NULL); 184 } 185 186 void 187 kauth_cred_clone(kauth_cred_t from, kauth_cred_t to) 188 { 189 kauth_cred_clone1(from, to, true); 190 } 191 192 /* 193 * Duplicate cred and return a new kauth_cred_t. 194 */ 195 kauth_cred_t 196 kauth_cred_dup(kauth_cred_t cred) 197 { 198 kauth_cred_t new_cred; 199 200 KASSERT(cred != NULL); 201 KASSERT(cred != NOCRED); 202 KASSERT(cred != FSCRED); 203 KASSERT(cred->cr_refcnt > 0); 204 205 new_cred = kauth_cred_alloc(); 206 207 kauth_cred_clone(cred, new_cred); 208 209 return (new_cred); 210 } 211 212 /* 213 * Similar to crcopy(), only on a kauth_cred_t. 214 * XXX: Is this even needed? [kauth_cred_copy] 215 */ 216 kauth_cred_t 217 kauth_cred_copy(kauth_cred_t cred) 218 { 219 kauth_cred_t new_cred; 220 221 KASSERT(cred != NULL); 222 KASSERT(cred != NOCRED); 223 KASSERT(cred != FSCRED); 224 KASSERT(cred->cr_refcnt > 0); 225 226 /* If the provided credentials already have one reference, use them. */ 227 if (cred->cr_refcnt == 1) 228 return (cred); 229 230 new_cred = kauth_cred_alloc(); 231 232 kauth_cred_clone(cred, new_cred); 233 234 kauth_cred_free(cred); 235 236 return (new_cred); 237 } 238 239 void 240 kauth_proc_fork(struct proc *parent, struct proc *child) 241 { 242 243 mutex_enter(parent->p_lock); 244 child->p_cred = kauth_cred_hold(parent->p_cred); 245 mutex_exit(parent->p_lock); 246 247 /* XXX: relies on parent process stalling during fork() */ 248 kauth_cred_hook(parent->p_cred, KAUTH_CRED_FORK, parent, 249 child); 250 } 251 252 void 253 kauth_proc_chroot(kauth_cred_t cred, struct cwdinfo *cwdi) 254 { 255 kauth_cred_hook(cred, KAUTH_CRED_CHROOT, cwdi, NULL); 256 } 257 258 uid_t 259 kauth_cred_getuid(kauth_cred_t cred) 260 { 261 KASSERT(cred != NULL); 262 KASSERT(cred != NOCRED); 263 KASSERT(cred != FSCRED); 264 265 return (cred->cr_uid); 266 } 267 268 uid_t 269 kauth_cred_geteuid(kauth_cred_t cred) 270 { 271 KASSERT(cred != NULL); 272 KASSERT(cred != NOCRED); 273 KASSERT(cred != FSCRED); 274 275 return (cred->cr_euid); 276 } 277 278 uid_t 279 kauth_cred_getsvuid(kauth_cred_t cred) 280 { 281 KASSERT(cred != NULL); 282 KASSERT(cred != NOCRED); 283 KASSERT(cred != FSCRED); 284 285 return (cred->cr_svuid); 286 } 287 288 gid_t 289 kauth_cred_getgid(kauth_cred_t cred) 290 { 291 KASSERT(cred != NULL); 292 KASSERT(cred != NOCRED); 293 KASSERT(cred != FSCRED); 294 295 return (cred->cr_gid); 296 } 297 298 gid_t 299 kauth_cred_getegid(kauth_cred_t cred) 300 { 301 KASSERT(cred != NULL); 302 KASSERT(cred != NOCRED); 303 KASSERT(cred != FSCRED); 304 305 return (cred->cr_egid); 306 } 307 308 gid_t 309 kauth_cred_getsvgid(kauth_cred_t cred) 310 { 311 KASSERT(cred != NULL); 312 KASSERT(cred != NOCRED); 313 KASSERT(cred != FSCRED); 314 315 return (cred->cr_svgid); 316 } 317 318 void 319 kauth_cred_setuid(kauth_cred_t cred, uid_t uid) 320 { 321 KASSERT(cred != NULL); 322 KASSERT(cred != NOCRED); 323 KASSERT(cred != FSCRED); 324 KASSERT(cred->cr_refcnt == 1); 325 326 cred->cr_uid = uid; 327 } 328 329 void 330 kauth_cred_seteuid(kauth_cred_t cred, uid_t uid) 331 { 332 KASSERT(cred != NULL); 333 KASSERT(cred != NOCRED); 334 KASSERT(cred != FSCRED); 335 KASSERT(cred->cr_refcnt == 1); 336 337 cred->cr_euid = uid; 338 } 339 340 void 341 kauth_cred_setsvuid(kauth_cred_t cred, uid_t uid) 342 { 343 KASSERT(cred != NULL); 344 KASSERT(cred != NOCRED); 345 KASSERT(cred != FSCRED); 346 KASSERT(cred->cr_refcnt == 1); 347 348 cred->cr_svuid = uid; 349 } 350 351 void 352 kauth_cred_setgid(kauth_cred_t cred, gid_t gid) 353 { 354 KASSERT(cred != NULL); 355 KASSERT(cred != NOCRED); 356 KASSERT(cred != FSCRED); 357 KASSERT(cred->cr_refcnt == 1); 358 359 cred->cr_gid = gid; 360 } 361 362 void 363 kauth_cred_setegid(kauth_cred_t cred, gid_t gid) 364 { 365 KASSERT(cred != NULL); 366 KASSERT(cred != NOCRED); 367 KASSERT(cred != FSCRED); 368 KASSERT(cred->cr_refcnt == 1); 369 370 cred->cr_egid = gid; 371 } 372 373 void 374 kauth_cred_setsvgid(kauth_cred_t cred, gid_t gid) 375 { 376 KASSERT(cred != NULL); 377 KASSERT(cred != NOCRED); 378 KASSERT(cred != FSCRED); 379 KASSERT(cred->cr_refcnt == 1); 380 381 cred->cr_svgid = gid; 382 } 383 384 /* Checks if gid is a member of the groups in cred. */ 385 int 386 kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp) 387 { 388 uint32_t i; 389 390 KASSERT(cred != NULL); 391 KASSERT(cred != NOCRED); 392 KASSERT(cred != FSCRED); 393 KASSERT(resultp != NULL); 394 395 *resultp = 0; 396 397 for (i = 0; i < cred->cr_ngroups; i++) 398 if (cred->cr_groups[i] == gid) { 399 *resultp = 1; 400 break; 401 } 402 403 return (0); 404 } 405 406 int 407 kauth_cred_groupmember(kauth_cred_t cred, gid_t gid) 408 { 409 int ismember, error; 410 411 KASSERT(cred != NULL); 412 KASSERT(cred != NOCRED); 413 KASSERT(cred != FSCRED); 414 415 if (kauth_cred_getegid(cred) == gid) 416 return 0; 417 418 error = kauth_cred_ismember_gid(cred, gid, &ismember); 419 if (error) 420 return error; 421 422 return ismember ? 0 : -1; 423 } 424 425 u_int 426 kauth_cred_ngroups(kauth_cred_t cred) 427 { 428 KASSERT(cred != NULL); 429 KASSERT(cred != NOCRED); 430 KASSERT(cred != FSCRED); 431 432 return (cred->cr_ngroups); 433 } 434 435 /* 436 * Return the group at index idx from the groups in cred. 437 */ 438 gid_t 439 kauth_cred_group(kauth_cred_t cred, u_int idx) 440 { 441 KASSERT(cred != NULL); 442 KASSERT(cred != NOCRED); 443 KASSERT(cred != FSCRED); 444 KASSERT(idx < cred->cr_ngroups); 445 446 return (cred->cr_groups[idx]); 447 } 448 449 /* XXX elad: gmuid is unused for now. */ 450 int 451 kauth_cred_setgroups(kauth_cred_t cred, const gid_t *grbuf, size_t len, 452 uid_t gmuid, enum uio_seg seg) 453 { 454 int error = 0; 455 456 KASSERT(cred != NULL); 457 KASSERT(cred != NOCRED); 458 KASSERT(cred != FSCRED); 459 KASSERT(cred->cr_refcnt == 1); 460 461 if (len > __arraycount(cred->cr_groups)) 462 return SET_ERROR(EINVAL); 463 464 if (len) { 465 if (seg == UIO_SYSSPACE) { 466 memcpy(cred->cr_groups, grbuf, 467 len * sizeof(cred->cr_groups[0])); 468 } else { 469 error = copyin(grbuf, cred->cr_groups, 470 len * sizeof(cred->cr_groups[0])); 471 if (error != 0) 472 len = 0; 473 } 474 } 475 memset(cred->cr_groups + len, 0xff, 476 sizeof(cred->cr_groups) - (len * sizeof(cred->cr_groups[0]))); 477 478 cred->cr_ngroups = len; 479 480 return error; 481 } 482 483 /* This supports sys_setgroups() */ 484 int 485 kauth_proc_setgroups(struct lwp *l, kauth_cred_t ncred) 486 { 487 kauth_cred_t cred; 488 int error; 489 490 /* 491 * At this point we could delete duplicate groups from ncred, 492 * and plausibly sort the list - but in general the later is 493 * a bad idea. 494 */ 495 proc_crmod_enter(); 496 /* Maybe we should use curproc here ? */ 497 cred = l->l_proc->p_cred; 498 499 kauth_cred_clone1(cred, ncred, false); 500 501 error = kauth_authorize_process(cred, KAUTH_PROCESS_SETID, 502 l->l_proc, NULL, NULL, NULL); 503 if (error != 0) { 504 proc_crmod_leave(cred, ncred, false); 505 return error; 506 } 507 508 /* Broadcast our credentials to the process and other LWPs. */ 509 proc_crmod_leave(ncred, cred, true); 510 return 0; 511 } 512 513 int 514 kauth_cred_getgroups(kauth_cred_t cred, gid_t *grbuf, size_t len, 515 enum uio_seg seg) 516 { 517 KASSERT(cred != NULL); 518 519 if (len > cred->cr_ngroups) 520 return SET_ERROR(EINVAL); 521 522 if (seg == UIO_USERSPACE) 523 return copyout(cred->cr_groups, grbuf, sizeof(*grbuf) * len); 524 memcpy(grbuf, cred->cr_groups, sizeof(*grbuf) * len); 525 526 return 0; 527 } 528 529 int 530 kauth_register_key(secmodel_t secmodel, kauth_key_t *result) 531 { 532 kauth_key_t k; 533 specificdata_key_t key; 534 int error; 535 536 KASSERT(result != NULL); 537 538 error = specificdata_key_create(kauth_domain, &key, NULL); 539 if (error) 540 return (error); 541 542 k = kmem_alloc(sizeof(*k), KM_SLEEP); 543 k->ks_secmodel = secmodel; 544 k->ks_key = key; 545 546 *result = k; 547 548 return (0); 549 } 550 551 int 552 kauth_deregister_key(kauth_key_t key) 553 { 554 KASSERT(key != NULL); 555 556 specificdata_key_delete(kauth_domain, key->ks_key); 557 kmem_free(key, sizeof(*key)); 558 559 return (0); 560 } 561 562 void * 563 kauth_cred_getdata(kauth_cred_t cred, kauth_key_t key) 564 { 565 KASSERT(cred != NULL); 566 KASSERT(cred != NOCRED); 567 KASSERT(cred != FSCRED); 568 KASSERT(key != NULL); 569 570 return (specificdata_getspecific(kauth_domain, &cred->cr_sd, 571 key->ks_key)); 572 } 573 574 void 575 kauth_cred_setdata(kauth_cred_t cred, kauth_key_t key, void *data) 576 { 577 KASSERT(cred != NULL); 578 KASSERT(cred != NOCRED); 579 KASSERT(cred != FSCRED); 580 KASSERT(key != NULL); 581 582 specificdata_setspecific(kauth_domain, &cred->cr_sd, key->ks_key, data); 583 } 584 585 /* 586 * Match uids in two credentials. 587 */ 588 int 589 kauth_cred_uidmatch(kauth_cred_t cred1, kauth_cred_t cred2) 590 { 591 KASSERT(cred1 != NULL); 592 KASSERT(cred1 != NOCRED); 593 KASSERT(cred1 != FSCRED); 594 KASSERT(cred2 != NULL); 595 KASSERT(cred2 != NOCRED); 596 KASSERT(cred2 != FSCRED); 597 598 if (cred1->cr_uid == cred2->cr_uid || 599 cred1->cr_euid == cred2->cr_uid || 600 cred1->cr_uid == cred2->cr_euid || 601 cred1->cr_euid == cred2->cr_euid) 602 return (1); 603 604 return (0); 605 } 606 607 u_int 608 kauth_cred_getrefcnt(kauth_cred_t cred) 609 { 610 KASSERT(cred != NULL); 611 KASSERT(cred != NOCRED); 612 KASSERT(cred != FSCRED); 613 614 return (cred->cr_refcnt); 615 } 616 617 /* 618 * Convert userland credentials (struct uucred) to kauth_cred_t. 619 * XXX: For NFS & puffs 620 */ 621 void 622 kauth_uucred_to_cred(kauth_cred_t cred, const struct uucred *uuc) 623 { 624 KASSERT(cred != NULL); 625 KASSERT(cred != NOCRED); 626 KASSERT(cred != FSCRED); 627 KASSERT(uuc != NULL); 628 629 cred->cr_refcnt = 1; 630 cred->cr_uid = uuc->cr_uid; 631 cred->cr_euid = uuc->cr_uid; 632 cred->cr_svuid = uuc->cr_uid; 633 cred->cr_gid = uuc->cr_gid; 634 cred->cr_egid = uuc->cr_gid; 635 cred->cr_svgid = uuc->cr_gid; 636 cred->cr_ngroups = uimin(uuc->cr_ngroups, NGROUPS); 637 kauth_cred_setgroups(cred, __UNCONST(uuc->cr_groups), 638 cred->cr_ngroups, -1, UIO_SYSSPACE); 639 } 640 641 /* 642 * Convert kauth_cred_t to userland credentials (struct uucred). 643 * XXX: For NFS & puffs 644 */ 645 void 646 kauth_cred_to_uucred(struct uucred *uuc, const kauth_cred_t cred) 647 { 648 KASSERT(cred != NULL); 649 KASSERT(cred != NOCRED); 650 KASSERT(cred != FSCRED); 651 KASSERT(uuc != NULL); 652 int ng; 653 654 ng = uimin(cred->cr_ngroups, NGROUPS); 655 uuc->cr_uid = cred->cr_euid; 656 uuc->cr_gid = cred->cr_egid; 657 uuc->cr_ngroups = ng; 658 kauth_cred_getgroups(cred, uuc->cr_groups, ng, UIO_SYSSPACE); 659 } 660 661 /* 662 * Compare kauth_cred_t and uucred credentials. 663 * XXX: Modelled after crcmp() for NFS. 664 */ 665 int 666 kauth_cred_uucmp(kauth_cred_t cred, const struct uucred *uuc) 667 { 668 KASSERT(cred != NULL); 669 KASSERT(cred != NOCRED); 670 KASSERT(cred != FSCRED); 671 KASSERT(uuc != NULL); 672 673 if (cred->cr_euid == uuc->cr_uid && 674 cred->cr_egid == uuc->cr_gid && 675 cred->cr_ngroups == (uint32_t)uuc->cr_ngroups) { 676 int i; 677 678 /* Check if all groups from uuc appear in cred. */ 679 for (i = 0; i < uuc->cr_ngroups; i++) { 680 int ismember; 681 682 ismember = 0; 683 if (kauth_cred_ismember_gid(cred, uuc->cr_groups[i], 684 &ismember) != 0 || !ismember) 685 return (1); 686 } 687 688 return (0); 689 } 690 691 return (1); 692 } 693 694 /* 695 * Make a struct ucred out of a kauth_cred_t. For compatibility. 696 */ 697 void 698 kauth_cred_toucred(kauth_cred_t cred, struct ki_ucred *uc) 699 { 700 KASSERT(cred != NULL); 701 KASSERT(cred != NOCRED); 702 KASSERT(cred != FSCRED); 703 KASSERT(uc != NULL); 704 705 uc->cr_ref = cred->cr_refcnt; 706 uc->cr_uid = cred->cr_euid; 707 uc->cr_gid = cred->cr_egid; 708 uc->cr_ngroups = uimin(cred->cr_ngroups, __arraycount(uc->cr_groups)); 709 memcpy(uc->cr_groups, cred->cr_groups, 710 uc->cr_ngroups * sizeof(uc->cr_groups[0])); 711 } 712 713 /* 714 * Make a struct pcred out of a kauth_cred_t. For compatibility. 715 */ 716 void 717 kauth_cred_topcred(kauth_cred_t cred, struct ki_pcred *pc) 718 { 719 KASSERT(cred != NULL); 720 KASSERT(cred != NOCRED); 721 KASSERT(cred != FSCRED); 722 KASSERT(pc != NULL); 723 724 pc->p_pad = NULL; 725 pc->p_ruid = cred->cr_uid; 726 pc->p_svuid = cred->cr_svuid; 727 pc->p_rgid = cred->cr_gid; 728 pc->p_svgid = cred->cr_svgid; 729 pc->p_refcnt = cred->cr_refcnt; 730 } 731 732 /* 733 * Return kauth_cred_t for the current LWP. 734 */ 735 kauth_cred_t 736 kauth_cred_get(void) 737 { 738 return (curlwp->l_cred); 739 } 740 741 /* 742 * Returns a scope matching the provided id. 743 * Requires the scope list lock to be held by the caller. 744 */ 745 static kauth_scope_t 746 kauth_ifindscope(const char *id) 747 { 748 kauth_scope_t scope; 749 750 KASSERT(rw_lock_held(&kauth_lock)); 751 752 scope = NULL; 753 SIMPLEQ_FOREACH(scope, &scope_list, next_scope) { 754 if (strcmp(scope->id, id) == 0) 755 break; 756 } 757 758 return (scope); 759 } 760 761 /* 762 * Register a new scope. 763 * 764 * id - identifier for the scope 765 * callback - the scope's default listener 766 * cookie - cookie to be passed to the listener(s) 767 */ 768 kauth_scope_t 769 kauth_register_scope(const char *id, kauth_scope_callback_t callback, 770 void *cookie) 771 { 772 kauth_scope_t scope; 773 kauth_listener_t listener = NULL; /* XXX gcc */ 774 775 /* Sanitize input */ 776 if (id == NULL) 777 return (NULL); 778 779 /* Allocate space for a new scope and listener. */ 780 scope = kmem_alloc(sizeof(*scope), KM_SLEEP); 781 if (callback != NULL) 782 listener = kmem_alloc(sizeof(*listener), KM_SLEEP); 783 784 /* 785 * Acquire scope list lock. 786 */ 787 rw_enter(&kauth_lock, RW_WRITER); 788 789 /* Check we don't already have a scope with the same id */ 790 if (kauth_ifindscope(id) != NULL) { 791 rw_exit(&kauth_lock); 792 793 kmem_free(scope, sizeof(*scope)); 794 if (callback != NULL) 795 kmem_free(listener, sizeof(*listener)); 796 797 return (NULL); 798 } 799 800 /* Initialize new scope with parameters */ 801 scope->id = id; 802 scope->cookie = cookie; 803 scope->nlisteners = 1; 804 805 SIMPLEQ_INIT(&scope->listenq); 806 807 /* Add default listener */ 808 if (callback != NULL) { 809 listener->func = callback; 810 listener->scope = scope; 811 listener->refcnt = 0; 812 SIMPLEQ_INSERT_HEAD(&scope->listenq, listener, listener_next); 813 } 814 815 /* Insert scope to scopes list */ 816 SIMPLEQ_INSERT_TAIL(&scope_list, scope, next_scope); 817 818 rw_exit(&kauth_lock); 819 820 return (scope); 821 } 822 823 /* 824 * Initialize the kernel authorization subsystem. 825 * 826 * Initialize the scopes list lock. 827 * Create specificdata domain. 828 * Register the credentials scope, used in kauth(9) internally. 829 * Register built-in scopes: generic, system, process, network, machdep, device. 830 */ 831 void 832 kauth_init(void) 833 { 834 rw_init(&kauth_lock); 835 836 kauth_cred_cache = pool_cache_init(sizeof(struct kauth_cred), 837 coherency_unit, 0, 0, "kcredpl", NULL, IPL_NONE, 838 NULL, NULL, NULL); 839 840 /* Create specificdata domain. */ 841 kauth_domain = specificdata_domain_create(); 842 843 /* Register credentials scope. */ 844 kauth_builtin_scope_cred = 845 kauth_register_scope(KAUTH_SCOPE_CRED, NULL, NULL); 846 847 /* Register generic scope. */ 848 kauth_builtin_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC, 849 NULL, NULL); 850 851 /* Register system scope. */ 852 kauth_builtin_scope_system = kauth_register_scope(KAUTH_SCOPE_SYSTEM, 853 NULL, NULL); 854 855 /* Register process scope. */ 856 kauth_builtin_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS, 857 NULL, NULL); 858 859 /* Register network scope. */ 860 kauth_builtin_scope_network = kauth_register_scope(KAUTH_SCOPE_NETWORK, 861 NULL, NULL); 862 863 /* Register machdep scope. */ 864 kauth_builtin_scope_machdep = kauth_register_scope(KAUTH_SCOPE_MACHDEP, 865 NULL, NULL); 866 867 /* Register device scope. */ 868 kauth_builtin_scope_device = kauth_register_scope(KAUTH_SCOPE_DEVICE, 869 NULL, NULL); 870 871 /* Register vnode scope. */ 872 kauth_builtin_scope_vnode = kauth_register_scope(KAUTH_SCOPE_VNODE, 873 NULL, NULL); 874 } 875 876 /* 877 * Deregister a scope. 878 * Requires scope list lock to be held by the caller. 879 * 880 * scope - the scope to deregister 881 */ 882 void 883 kauth_deregister_scope(kauth_scope_t scope) 884 { 885 if (scope != NULL) { 886 /* Remove scope from list */ 887 SIMPLEQ_REMOVE(&scope_list, scope, kauth_scope, next_scope); 888 kmem_free(scope, sizeof(*scope)); 889 } 890 } 891 892 /* 893 * Register a listener. 894 * 895 * id - scope identifier. 896 * callback - the callback routine for the listener. 897 * cookie - cookie to pass unmoidfied to the callback. 898 */ 899 kauth_listener_t 900 kauth_listen_scope(const char *id, kauth_scope_callback_t callback, 901 void *cookie) 902 { 903 kauth_scope_t scope; 904 kauth_listener_t listener; 905 906 listener = kmem_alloc(sizeof(*listener), KM_SLEEP); 907 rw_enter(&kauth_lock, RW_WRITER); 908 909 /* 910 * Find scope struct. 911 */ 912 scope = kauth_ifindscope(id); 913 if (scope == NULL) { 914 rw_exit(&kauth_lock); 915 kmem_free(listener, sizeof(*listener)); 916 return (NULL); 917 } 918 919 /* Allocate listener */ 920 921 /* Initialize listener with parameters */ 922 listener->func = callback; 923 listener->refcnt = 0; 924 925 /* Add listener to scope */ 926 SIMPLEQ_INSERT_TAIL(&scope->listenq, listener, listener_next); 927 928 /* Raise number of listeners on scope. */ 929 scope->nlisteners++; 930 listener->scope = scope; 931 932 rw_exit(&kauth_lock); 933 934 return (listener); 935 } 936 937 /* 938 * Deregister a listener. 939 * 940 * listener - listener reference as returned from kauth_listen_scope(). 941 */ 942 void 943 kauth_unlisten_scope(kauth_listener_t listener) 944 { 945 946 if (listener != NULL) { 947 rw_enter(&kauth_lock, RW_WRITER); 948 SIMPLEQ_REMOVE(&listener->scope->listenq, listener, 949 kauth_listener, listener_next); 950 listener->scope->nlisteners--; 951 rw_exit(&kauth_lock); 952 kmem_free(listener, sizeof(*listener)); 953 } 954 } 955 956 /* 957 * Authorize a request. 958 * 959 * scope - the scope of the request as defined by KAUTH_SCOPE_* or as 960 * returned from kauth_register_scope(). 961 * credential - credentials of the user ("actor") making the request. 962 * action - request identifier. 963 * arg[0-3] - passed unmodified to listener(s). 964 * 965 * Returns the aggregated result: 966 * - KAUTH_RESULT_ALLOW if there is at least one KAUTH_RESULT_ALLOW and 967 * zero KAUTH_DESULT_DENY 968 * - KAUTH_RESULT_DENY if there is at least one KAUTH_RESULT_DENY 969 * - KAUTH_RESULT_DEFER if there is nothing but KAUTH_RESULT_DEFER 970 */ 971 static int 972 kauth_authorize_action_internal(kauth_scope_t scope, kauth_cred_t cred, 973 kauth_action_t action, void *arg0, void *arg1, void *arg2, void *arg3) 974 { 975 kauth_listener_t listener; 976 int error, allow, fail; 977 978 KASSERT(cred != NULL); 979 KASSERT(action != 0); 980 981 /* Short-circuit requests coming from the kernel. */ 982 if (cred == NOCRED || cred == FSCRED) 983 return KAUTH_RESULT_ALLOW; 984 985 KASSERT(scope != NULL); 986 987 fail = 0; 988 allow = 0; 989 990 /* rw_enter(&kauth_lock, RW_READER); XXX not yet */ 991 SIMPLEQ_FOREACH(listener, &scope->listenq, listener_next) { 992 error = listener->func(cred, action, scope->cookie, arg0, 993 arg1, arg2, arg3); 994 995 if (error == KAUTH_RESULT_ALLOW) 996 allow = 1; 997 else if (error == KAUTH_RESULT_DENY) 998 fail = 1; 999 } 1000 /* rw_exit(&kauth_lock); */ 1001 1002 if (fail) 1003 return (KAUTH_RESULT_DENY); 1004 1005 if (allow) 1006 return (KAUTH_RESULT_ALLOW); 1007 1008 return (KAUTH_RESULT_DEFER); 1009 }; 1010 1011 int 1012 kauth_authorize_action(kauth_scope_t scope, kauth_cred_t cred, 1013 kauth_action_t action, void *arg0, void *arg1, void *arg2, void *arg3) 1014 { 1015 int r; 1016 1017 r = kauth_authorize_action_internal(scope, cred, action, arg0, arg1, 1018 arg2, arg3); 1019 1020 if (r == KAUTH_RESULT_DENY) 1021 return SET_ERROR(EPERM); 1022 1023 if (r == KAUTH_RESULT_ALLOW) 1024 return 0; 1025 1026 if (secmodel_nsecmodels() == 0) 1027 return 0; 1028 1029 return SET_ERROR(EPERM); 1030 } 1031 1032 /* 1033 * Generic scope authorization wrapper. 1034 */ 1035 int 1036 kauth_authorize_generic(kauth_cred_t cred, kauth_action_t action, void *arg0) 1037 { 1038 return (kauth_authorize_action(kauth_builtin_scope_generic, cred, 1039 action, arg0, NULL, NULL, NULL)); 1040 } 1041 1042 /* 1043 * System scope authorization wrapper. 1044 */ 1045 int 1046 kauth_authorize_system(kauth_cred_t cred, kauth_action_t action, 1047 enum kauth_system_req req, void *arg1, void *arg2, void *arg3) 1048 { 1049 return (kauth_authorize_action(kauth_builtin_scope_system, cred, 1050 action, (void *)req, arg1, arg2, arg3)); 1051 } 1052 1053 /* 1054 * Process scope authorization wrapper. 1055 */ 1056 int 1057 kauth_authorize_process(kauth_cred_t cred, kauth_action_t action, 1058 struct proc *p, void *arg1, void *arg2, void *arg3) 1059 { 1060 return (kauth_authorize_action(kauth_builtin_scope_process, cred, 1061 action, p, arg1, arg2, arg3)); 1062 } 1063 1064 /* 1065 * Network scope authorization wrapper. 1066 */ 1067 int 1068 kauth_authorize_network(kauth_cred_t cred, kauth_action_t action, 1069 enum kauth_network_req req, void *arg1, void *arg2, void *arg3) 1070 { 1071 return (kauth_authorize_action(kauth_builtin_scope_network, cred, 1072 action, (void *)req, arg1, arg2, arg3)); 1073 } 1074 1075 int 1076 kauth_authorize_machdep(kauth_cred_t cred, kauth_action_t action, 1077 void *arg0, void *arg1, void *arg2, void *arg3) 1078 { 1079 return (kauth_authorize_action(kauth_builtin_scope_machdep, cred, 1080 action, arg0, arg1, arg2, arg3)); 1081 } 1082 1083 int 1084 kauth_authorize_device(kauth_cred_t cred, kauth_action_t action, 1085 void *arg0, void *arg1, void *arg2, void *arg3) 1086 { 1087 return (kauth_authorize_action(kauth_builtin_scope_device, cred, 1088 action, arg0, arg1, arg2, arg3)); 1089 } 1090 1091 int 1092 kauth_authorize_device_tty(kauth_cred_t cred, kauth_action_t action, 1093 struct tty *tty) 1094 { 1095 return (kauth_authorize_action(kauth_builtin_scope_device, cred, 1096 action, tty, NULL, NULL, NULL)); 1097 } 1098 1099 int 1100 kauth_authorize_device_spec(kauth_cred_t cred, enum kauth_device_req req, 1101 struct vnode *vp) 1102 { 1103 return (kauth_authorize_action(kauth_builtin_scope_device, cred, 1104 KAUTH_DEVICE_RAWIO_SPEC, (void *)req, vp, NULL, NULL)); 1105 } 1106 1107 int 1108 kauth_authorize_device_passthru(kauth_cred_t cred, dev_t dev, u_long bits, 1109 void *data) 1110 { 1111 return (kauth_authorize_action(kauth_builtin_scope_device, cred, 1112 KAUTH_DEVICE_RAWIO_PASSTHRU, (void *)bits, (void *)(u_long)dev, 1113 data, NULL)); 1114 } 1115 1116 kauth_action_t 1117 kauth_accmode_to_action(accmode_t accmode) 1118 { 1119 kauth_action_t action = 0; 1120 1121 // XXX: Revisit we need to have a richer set of kauth primitives 1122 // We also get only the Unix perms here sometimes 1123 if (accmode & (VSTAT_PERMS|VREAD)) 1124 action |= KAUTH_VNODE_READ_DATA; 1125 if (accmode & (VMODIFY_PERMS|VADMIN_PERMS)) 1126 action |= KAUTH_VNODE_WRITE_DATA; 1127 if (accmode & VEXEC) 1128 action |= KAUTH_VNODE_EXECUTE; 1129 return action == 0 ? KAUTH_VNODE_ACCESS : action; 1130 } 1131 1132 kauth_action_t 1133 kauth_extattr_action(mode_t access_mode) 1134 { 1135 kauth_action_t action = 0; 1136 1137 if (access_mode & VREAD) 1138 action |= KAUTH_VNODE_READ_EXTATTRIBUTES; 1139 if (access_mode & VWRITE) 1140 action |= KAUTH_VNODE_WRITE_EXTATTRIBUTES; 1141 1142 return action; 1143 } 1144 1145 int 1146 kauth_authorize_vnode(kauth_cred_t cred, kauth_action_t action, 1147 struct vnode *vp, struct vnode *dvp, int fs_decision) 1148 { 1149 int error; 1150 1151 error = kauth_authorize_action_internal(kauth_builtin_scope_vnode, cred, 1152 action, vp, dvp, NULL, NULL); 1153 1154 if (error == KAUTH_RESULT_DENY) 1155 return SET_ERROR(EACCES); 1156 1157 if (error == KAUTH_RESULT_ALLOW) 1158 return 0; 1159 1160 /* 1161 * If the file-system does not support decision-before-action, we can 1162 * only short-circuit the operation (deny). If we're here, it means no 1163 * listener denied it, so our only alternative is to supposedly-allow 1164 * it and let the file-system have the last word. 1165 */ 1166 if (fs_decision == KAUTH_VNODE_REMOTEFS) 1167 return 0; 1168 1169 return fs_decision; 1170 } 1171 1172 static int 1173 kauth_cred_hook(kauth_cred_t cred, kauth_action_t action, void *arg0, 1174 void *arg1) 1175 { 1176 int r; 1177 1178 r = kauth_authorize_action(kauth_builtin_scope_cred, cred, action, 1179 arg0, arg1, NULL, NULL); 1180 1181 #ifdef DIAGNOSTIC 1182 if (!SIMPLEQ_EMPTY(&kauth_builtin_scope_cred->listenq)) 1183 KASSERT(r == 0); 1184 #endif /* DIAGNOSTIC */ 1185 1186 return (r); 1187 } 1188