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