1/* 2 * Copyright (c) 2004-2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. The rights granted to you under the License 10 * may not be used to create, or enable the creation or redistribution of, 11 * unlawful or unlicensed copies of an Apple operating system, or to 12 * circumvent, violate, or enable the circumvention or violation of, any 13 * terms of an Apple operating system software license agreement. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 18 * The Original Code and all software distributed under the License are 19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 23 * Please see the License for the specific language governing rights and 24 * limitations under the License. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28/* 29 * NOTICE: This file was modified by SPARTA, Inc. in 2005 to introduce 30 * support for mandatory and extensible security protections. This notice 31 * is included in support of clause 2.2 (b) of the Apple Public License, 32 * Version 2.0. 33 */ 34 35/* 36 * Kernel Authorization framework: Management of process/thread credentials 37 * and identity information. 38 */ 39 40#include <sys/param.h> /* XXX trim includes */ 41#include <sys/acct.h> 42#include <sys/systm.h> 43#include <sys/ucred.h> 44#include <sys/proc_internal.h> 45#include <sys/user.h> 46#include <sys/timeb.h> 47#include <sys/times.h> 48#include <sys/malloc.h> 49#include <sys/kauth.h> 50#include <sys/kernel.h> 51#include <sys/sdt.h> 52 53#include <security/audit/audit.h> 54 55#include <sys/mount.h> 56#include <sys/stat.h> /* For manifest constants in posix_cred_access */ 57#include <sys/sysproto.h> 58#include <mach/message.h> 59#include <mach/host_security.h> 60 61#include <libkern/OSAtomic.h> 62 63#include <kern/task.h> 64#include <kern/locks.h> 65#ifdef MACH_ASSERT 66# undef MACH_ASSERT 67#endif 68#define MACH_ASSERT 1 /* XXX so bogus */ 69#include <kern/assert.h> 70 71#if CONFIG_MACF 72#include <security/mac.h> 73#include <security/mac_framework.h> 74#include <security/_label.h> 75#endif 76 77void mach_kauth_cred_uthread_update( void ); 78 79#define CRED_DIAGNOSTIC 0 80 81# define NULLCRED_CHECK(_c) do {if (!IS_VALID_CRED(_c)) panic("%s: bad credential %p", __FUNCTION__,_c);} while(0) 82 83/* Set to 1 to turn on KAUTH_DEBUG for kern_credential.c */ 84#if 0 85#ifdef KAUTH_DEBUG 86#undef KAUTH_DEBUG 87#endif 88 89#ifdef K_UUID_FMT 90#undef K_UUID_FMT 91#endif 92 93#ifdef K_UUID_ARG 94#undef K_UUID_ARG 95#endif 96 97# define K_UUID_FMT "%08x:%08x:%08x:%08x" 98# define K_UUID_ARG(_u) *(int *)&_u.g_guid[0],*(int *)&_u.g_guid[4],*(int *)&_u.g_guid[8],*(int *)&_u.g_guid[12] 99# define KAUTH_DEBUG(fmt, args...) do { printf("%s:%d: " fmt "\n", __PRETTY_FUNCTION__, __LINE__ , ##args); } while (0) 100#endif 101 102/* 103 * Credential debugging; we can track entry into a function that might 104 * change a credential, and we can track actual credential changes that 105 * result. 106 * 107 * Note: Does *NOT* currently include per-thread credential changes 108 */ 109 110#if DEBUG_CRED 111#define DEBUG_CRED_ENTER printf 112#define DEBUG_CRED_CHANGE printf 113extern void kauth_cred_print(kauth_cred_t cred); 114 115#include <libkern/OSDebug.h> /* needed for get_backtrace( ) */ 116 117int is_target_cred( kauth_cred_t the_cred ); 118void get_backtrace( void ); 119 120static int sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, 121 __unused int arg2, struct sysctl_req *req ); 122static int 123sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, 124 __unused int arg2, struct sysctl_req *req ); 125 126#define MAX_STACK_DEPTH 8 127struct cred_backtrace { 128 int depth; 129 void * stack[ MAX_STACK_DEPTH ]; 130}; 131typedef struct cred_backtrace cred_backtrace; 132 133#define MAX_CRED_BUFFER_SLOTS 200 134struct cred_debug_buffer { 135 int next_slot; 136 cred_backtrace stack_buffer[ MAX_CRED_BUFFER_SLOTS ]; 137}; 138typedef struct cred_debug_buffer cred_debug_buffer; 139cred_debug_buffer * cred_debug_buf_p = NULL; 140 141#else /* !DEBUG_CRED */ 142 143#define DEBUG_CRED_ENTER(fmt, ...) do {} while (0) 144#define DEBUG_CRED_CHANGE(fmt, ...) do {} while (0) 145 146#endif /* !DEBUG_CRED */ 147 148#if CONFIG_EXT_RESOLVER 149/* 150 * Interface to external identity resolver. 151 * 152 * The architecture of the interface is simple; the external resolver calls 153 * in to get work, then calls back with completed work. It also calls us 154 * to let us know that it's (re)started, so that we can resubmit work if it 155 * times out. 156 */ 157 158static lck_mtx_t *kauth_resolver_mtx; 159#define KAUTH_RESOLVER_LOCK() lck_mtx_lock(kauth_resolver_mtx); 160#define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx); 161 162static volatile pid_t kauth_resolver_identity; 163static int kauth_identitysvc_has_registered; 164static int kauth_resolver_registered; 165static uint32_t kauth_resolver_sequence; 166static int kauth_resolver_timeout = 30; /* default: 30 seconds */ 167 168struct kauth_resolver_work { 169 TAILQ_ENTRY(kauth_resolver_work) kr_link; 170 struct kauth_identity_extlookup kr_work; 171 uint64_t kr_extend; 172 uint32_t kr_seqno; 173 int kr_refs; 174 int kr_flags; 175#define KAUTH_REQUEST_UNSUBMITTED (1<<0) 176#define KAUTH_REQUEST_SUBMITTED (1<<1) 177#define KAUTH_REQUEST_DONE (1<<2) 178 int kr_result; 179}; 180 181TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted; 182TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work) kauth_resolver_submitted; 183TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work) kauth_resolver_done; 184 185/* Number of resolver timeouts between logged complaints */ 186#define KAUTH_COMPLAINT_INTERVAL 1000 187int kauth_resolver_timeout_cnt = 0; 188 189static int kauth_resolver_submit(struct kauth_identity_extlookup *lkp, uint64_t extend_data); 190static int kauth_resolver_complete(user_addr_t message); 191static int kauth_resolver_getwork(user_addr_t message); 192static int kauth_resolver_getwork2(user_addr_t message); 193static __attribute__((noinline)) int __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__( 194 struct kauth_resolver_work *); 195 196#define KAUTH_CACHES_MAX_SIZE 10000 /* Max # entries for both groups and id caches */ 197 198struct kauth_identity { 199 TAILQ_ENTRY(kauth_identity) ki_link; 200 int ki_valid; 201 uid_t ki_uid; 202 gid_t ki_gid; 203 int ki_supgrpcnt; 204 gid_t ki_supgrps[NGROUPS]; 205 guid_t ki_guid; 206 ntsid_t ki_ntsid; 207 const char *ki_name; /* string name from string cache */ 208 /* 209 * Expiry times are the earliest time at which we will disregard the 210 * cached state and go to userland. Before then if the valid bit is 211 * set, we will return the cached value. If it's not set, we will 212 * not go to userland to resolve, just assume that there is no answer 213 * available. 214 */ 215 time_t ki_groups_expiry; 216 time_t ki_guid_expiry; 217 time_t ki_ntsid_expiry; 218}; 219 220static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities; 221static lck_mtx_t *kauth_identity_mtx; 222#define KAUTH_IDENTITY_LOCK() lck_mtx_lock(kauth_identity_mtx); 223#define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx); 224#define KAUTH_IDENTITY_CACHEMAX_DEFAULT 100 /* XXX default sizing? */ 225static int kauth_identity_cachemax = KAUTH_IDENTITY_CACHEMAX_DEFAULT; 226static int kauth_identity_count; 227 228static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, 229 ntsid_t *ntsidp, time_t ntsid_expiry, int supgrpcnt, gid_t *supgrps, time_t groups_expiry, 230 const char *name, int nametype); 231static void kauth_identity_register_and_free(struct kauth_identity *kip); 232static void kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip, uint64_t extend_data); 233static void kauth_identity_trimcache(int newsize); 234static void kauth_identity_lru(struct kauth_identity *kip); 235static int kauth_identity_guid_expired(struct kauth_identity *kip); 236static int kauth_identity_ntsid_expired(struct kauth_identity *kip); 237static int kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir, char *getname); 238static int kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir, char *getname); 239static int kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir, char *getname); 240static int kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir, char *getname); 241static int kauth_identity_find_nam(char *name, int valid, struct kauth_identity *kir); 242 243struct kauth_group_membership { 244 TAILQ_ENTRY(kauth_group_membership) gm_link; 245 uid_t gm_uid; /* the identity whose membership we're recording */ 246 gid_t gm_gid; /* group of which they are a member */ 247 time_t gm_expiry; /* TTL for the membership, or 0 for persistent entries */ 248 int gm_flags; 249#define KAUTH_GROUP_ISMEMBER (1<<0) 250}; 251 252TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups; 253static lck_mtx_t *kauth_groups_mtx; 254#define KAUTH_GROUPS_LOCK() lck_mtx_lock(kauth_groups_mtx); 255#define KAUTH_GROUPS_UNLOCK() lck_mtx_unlock(kauth_groups_mtx); 256#define KAUTH_GROUPS_CACHEMAX_DEFAULT 100 /* XXX default sizing? */ 257static int kauth_groups_cachemax = KAUTH_GROUPS_CACHEMAX_DEFAULT; 258static int kauth_groups_count; 259 260static int kauth_groups_expired(struct kauth_group_membership *gm); 261static void kauth_groups_lru(struct kauth_group_membership *gm); 262static void kauth_groups_updatecache(struct kauth_identity_extlookup *el); 263static void kauth_groups_trimcache(int newsize); 264 265#endif /* CONFIG_EXT_RESOLVER */ 266 267static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = KAUTH_CRED_PRIMES; 268static int kauth_cred_primes_index = 0; 269static int kauth_cred_table_size = 0; 270 271TAILQ_HEAD(kauth_cred_entry_head, ucred); 272static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL; 273 274#define KAUTH_CRED_HASH_DEBUG 0 275 276static int kauth_cred_add(kauth_cred_t new_cred); 277static boolean_t kauth_cred_remove(kauth_cred_t cred); 278static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key); 279static u_long kauth_cred_get_hashkey(kauth_cred_t cred); 280static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo); 281static boolean_t kauth_cred_unref_hashlocked(kauth_cred_t *credp); 282 283#if KAUTH_CRED_HASH_DEBUG 284static int kauth_cred_count = 0; 285static void kauth_cred_hash_print(void); 286static void kauth_cred_print(kauth_cred_t cred); 287#endif 288 289#if CONFIG_EXT_RESOLVER 290 291/* 292 * __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__ 293 * 294 * Description: Waits for the user space daemon to respond to the request 295 * we made. Function declared non inline to be visible in 296 * stackshots and spindumps as well as debugging. 297 * 298 * Parameters: workp Work queue entry. 299 * 300 * Returns: 0 on Success. 301 * EIO if Resolver is dead. 302 * EINTR thread interrupted in msleep 303 * EWOULDBLOCK thread timed out in msleep 304 * ERESTART returned by msleep. 305 * 306 */ 307static __attribute__((noinline)) int 308__KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__( 309 struct kauth_resolver_work *workp) 310{ 311 int error = 0; 312 struct timespec ts; 313 for (;;) { 314 /* we could compute a better timeout here */ 315 ts.tv_sec = kauth_resolver_timeout; 316 ts.tv_nsec = 0; 317 error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts); 318 /* request has been completed? */ 319 if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE)) 320 break; 321 /* woken because the resolver has died? */ 322 if (kauth_resolver_identity == 0) { 323 error = EIO; 324 break; 325 } 326 /* an error? */ 327 if (error != 0) 328 break; 329 } 330 return error; 331} 332 333 334/* 335 * kauth_resolver_init 336 * 337 * Description: Initialize the daemon side of the credential identity resolver 338 * 339 * Parameters: (void) 340 * 341 * Returns: (void) 342 * 343 * Notes: Initialize the credential identity resolver for use; the 344 * credential identity resolver is the KPI used by the user 345 * space credential identity resolver daemon to communicate 346 * with the kernel via the identitysvc() system call.. 347 * 348 * This is how membership in more than 16 groups (1 effective 349 * and 15 supplementary) is supported, and also how UID's, 350 * UUID's, and so on, are translated to/from POSIX credential 351 * values. 352 * 353 * The credential identity resolver operates by attempting to 354 * determine identity first from the credential, then from 355 * the kernel credential identity cache, and finally by 356 * enqueueing a request to a user space daemon. 357 * 358 * This function is called from kauth_init() in the file 359 * kern_authorization.c. 360 */ 361void 362kauth_resolver_init(void) 363{ 364 TAILQ_INIT(&kauth_resolver_unsubmitted); 365 TAILQ_INIT(&kauth_resolver_submitted); 366 TAILQ_INIT(&kauth_resolver_done); 367 kauth_resolver_sequence = 31337; 368 kauth_resolver_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/); 369} 370 371 372/* 373 * kauth_resolver_submit 374 * 375 * Description: Submit an external credential identity resolution request to 376 * the user space daemon. 377 * 378 * Parameters: lkp A pointer to an external 379 * lookup request 380 * extend_data extended data for kr_extend 381 * 382 * Returns: 0 Success 383 * EWOULDBLOCK No resolver registered 384 * EINTR Operation interrupted (e.g. by 385 * a signal) 386 * ENOMEM Could not allocate work item 387 * copyinstr:EFAULT Bad message from user space 388 * workp->kr_result:??? An error from the user space 389 * daemon (includes ENOENT!) 390 * 391 * Implicit returns: 392 * *lkp Modified 393 * 394 * Notes: Allocate a work queue entry, submit the work and wait for 395 * the operation to either complete or time out. Outstanding 396 * operations may also be cancelled. 397 * 398 * Submission is by means of placing the item on a work queue 399 * which is serviced by an external resolver thread calling 400 * into the kernel. The caller then sleeps until timeout, 401 * cancellation, or an external resolver thread calls in with 402 * a result message to kauth_resolver_complete(). All of these 403 * events wake the caller back up. 404 * 405 * This code is called from either kauth_cred_ismember_gid() 406 * for a group membership request, or it is called from 407 * kauth_cred_cache_lookup() when we get a cache miss. 408 */ 409static int 410kauth_resolver_submit(struct kauth_identity_extlookup *lkp, uint64_t extend_data) 411{ 412 struct kauth_resolver_work *workp, *killp; 413 struct timespec ts; 414 int error, shouldfree; 415 416 /* no point actually blocking if the resolver isn't up yet */ 417 if (kauth_resolver_identity == 0) { 418 /* 419 * We've already waited an initial <kauth_resolver_timeout> 420 * seconds with no result. 421 * 422 * Sleep on a stack address so no one wakes us before timeout; 423 * we sleep a half a second in case we are a high priority 424 * process, so that memberd doesn't starve while we are in a 425 * tight loop between user and kernel, eating all the CPU. 426 */ 427 error = tsleep(&ts, PZERO | PCATCH, "kr_submit", hz/2); 428 if (kauth_resolver_identity == 0) { 429 /* 430 * if things haven't changed while we were asleep, 431 * tell the caller we couldn't get an authoritative 432 * answer. 433 */ 434 return(EWOULDBLOCK); 435 } 436 } 437 438 MALLOC(workp, struct kauth_resolver_work *, sizeof(*workp), M_KAUTH, M_WAITOK); 439 if (workp == NULL) 440 return(ENOMEM); 441 442 workp->kr_work = *lkp; 443 workp->kr_extend = extend_data; 444 workp->kr_refs = 1; 445 workp->kr_flags = KAUTH_REQUEST_UNSUBMITTED; 446 workp->kr_result = 0; 447 448 /* 449 * We insert the request onto the unsubmitted queue, the call in from 450 * the resolver will it to the submitted thread when appropriate. 451 */ 452 KAUTH_RESOLVER_LOCK(); 453 workp->kr_seqno = workp->kr_work.el_seqno = kauth_resolver_sequence++; 454 workp->kr_work.el_result = KAUTH_EXTLOOKUP_INPROG; 455 456 /* 457 * XXX We *MUST NOT* attempt to coalesce identical work items due to 458 * XXX the inability to ensure order of update of the request item 459 * XXX extended data vs. the wakeup; instead, we let whoever is waiting 460 * XXX for each item repeat the update when they wake up. 461 */ 462 TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted, workp, kr_link); 463 464 /* 465 * Wake up an external resolver thread to deal with the new work; one 466 * may not be available, and if not, then the request will be grabbed 467 * when a resolver thread comes back into the kernel to request new 468 * work. 469 */ 470 wakeup_one((caddr_t)&kauth_resolver_unsubmitted); 471 error = __KERNEL_IS_WAITING_ON_EXTERNAL_CREDENTIAL_RESOLVER__(workp); 472 473 /* if the request was processed, copy the result */ 474 if (error == 0) 475 *lkp = workp->kr_work; 476 477 if (error == EWOULDBLOCK) { 478 if ((kauth_resolver_timeout_cnt++ % KAUTH_COMPLAINT_INTERVAL) == 0) { 479 printf("kauth external resolver timed out (%d timeout(s) of %d seconds).\n", 480 kauth_resolver_timeout_cnt, kauth_resolver_timeout); 481 } 482 483 if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) { 484 /* 485 * If the request timed out and was never collected, the resolver 486 * is dead and probably not coming back anytime soon. In this 487 * case we revert to no-resolver behaviour, and punt all the other 488 * sleeping requests to clear the backlog. 489 */ 490 KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead"); 491 492 /* 493 * Make the current resolver non-authoritative, and mark it as 494 * no longer registered to prevent kauth_cred_ismember_gid() 495 * enqueueing more work until a new one is registered. This 496 * mitigates the damage a crashing resolver may inflict. 497 */ 498 kauth_resolver_identity = 0; 499 kauth_resolver_registered = 0; 500 501 /* kill all the other requestes that are waiting as well */ 502 TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link) 503 wakeup(killp); 504 TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link) 505 wakeup(killp); 506 /* Cause all waiting-for-work threads to return EIO */ 507 wakeup((caddr_t)&kauth_resolver_unsubmitted); 508 } 509 } 510 511 /* 512 * drop our reference on the work item, and note whether we should 513 * free it or not 514 */ 515 if (--workp->kr_refs <= 0) { 516 /* work out which list we have to remove it from */ 517 if (workp->kr_flags & KAUTH_REQUEST_DONE) { 518 TAILQ_REMOVE(&kauth_resolver_done, workp, kr_link); 519 } else if (workp->kr_flags & KAUTH_REQUEST_SUBMITTED) { 520 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link); 521 } else if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) { 522 TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link); 523 } else { 524 KAUTH_DEBUG("RESOLVER - completed request has no valid queue"); 525 } 526 shouldfree = 1; 527 } else { 528 /* someone else still has a reference on this request */ 529 shouldfree = 0; 530 } 531 532 /* collect request result */ 533 if (error == 0) { 534 error = workp->kr_result; 535 } 536 KAUTH_RESOLVER_UNLOCK(); 537 538 /* 539 * If we dropped the last reference, free the request. 540 */ 541 if (shouldfree) { 542 FREE(workp, M_KAUTH); 543 } 544 545 KAUTH_DEBUG("RESOLVER - returning %d", error); 546 return(error); 547} 548 549 550/* 551 * identitysvc 552 * 553 * Description: System call interface for the external identity resolver. 554 * 555 * Parameters: uap->message Message from daemon to kernel 556 * 557 * Returns: 0 Successfully became resolver 558 * EPERM Not the resolver process 559 * kauth_authorize_generic:EPERM Not root user 560 * kauth_resolver_complete:EIO 561 * kauth_resolver_complete:EFAULT 562 * kauth_resolver_getwork:EINTR 563 * kauth_resolver_getwork:EFAULT 564 * 565 * Notes: This system call blocks until there is work enqueued, at 566 * which time the kernel wakes it up, and a message from the 567 * kernel is copied out to the identity resolution daemon, which 568 * proceed to attempt to resolve it. When the resolution has 569 * completed (successfully or not), the daemon called back into 570 * this system call to give the result to the kernel, and wait 571 * for the next request. 572 */ 573int 574identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused int32_t *retval) 575{ 576 int opcode = uap->opcode; 577 user_addr_t message = uap->message; 578 struct kauth_resolver_work *workp; 579 struct kauth_cache_sizes sz_arg; 580 int error; 581 pid_t new_id; 582 583 /* 584 * New server registering itself. 585 */ 586 if (opcode == KAUTH_EXTLOOKUP_REGISTER) { 587 new_id = current_proc()->p_pid; 588 if ((error = kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER)) != 0) { 589 KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id); 590 return(error); 591 } 592 KAUTH_RESOLVER_LOCK(); 593 if (kauth_resolver_identity != new_id) { 594 KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id, kauth_resolver_identity); 595 /* 596 * We have a new server, so assume that all the old requests have been lost. 597 */ 598 while ((workp = TAILQ_LAST(&kauth_resolver_submitted, kauth_resolver_submitted_head)) != NULL) { 599 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link); 600 workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED; 601 workp->kr_flags |= KAUTH_REQUEST_UNSUBMITTED; 602 TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted, workp, kr_link); 603 } 604 /* 605 * Allow user space resolver to override the 606 * external resolution timeout 607 */ 608 if (message > 30 && message < 10000) { 609 kauth_resolver_timeout = message; 610 KAUTH_DEBUG("RESOLVER - new resolver changes timeout to %d seconds\n", (int)message); 611 } 612 kauth_resolver_identity = new_id; 613 kauth_resolver_registered = 1; 614 kauth_identitysvc_has_registered = 1; 615 wakeup(&kauth_resolver_unsubmitted); 616 } 617 KAUTH_RESOLVER_UNLOCK(); 618 return(0); 619 } 620 621 /* 622 * Beyond this point, we must be the resolver process. 623 */ 624 if (current_proc()->p_pid != kauth_resolver_identity) { 625 KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid); 626 return(EPERM); 627 } 628 629 if (opcode == KAUTH_GET_CACHE_SIZES) { 630 KAUTH_IDENTITY_LOCK(); 631 sz_arg.kcs_id_size = kauth_identity_cachemax; 632 KAUTH_IDENTITY_UNLOCK(); 633 634 KAUTH_GROUPS_LOCK(); 635 sz_arg.kcs_group_size = kauth_groups_cachemax; 636 KAUTH_GROUPS_UNLOCK(); 637 638 if ((error = copyout(&sz_arg, uap->message, sizeof (sz_arg))) != 0) { 639 return (error); 640 } 641 642 return (0); 643 } else if (opcode == KAUTH_SET_CACHE_SIZES) { 644 if ((error = copyin(uap->message, &sz_arg, sizeof (sz_arg))) != 0) { 645 return (error); 646 } 647 648 if ((sz_arg.kcs_group_size > KAUTH_CACHES_MAX_SIZE) || 649 (sz_arg.kcs_id_size > KAUTH_CACHES_MAX_SIZE)) { 650 return (EINVAL); 651 } 652 653 KAUTH_IDENTITY_LOCK(); 654 kauth_identity_cachemax = sz_arg.kcs_id_size; 655 kauth_identity_trimcache(kauth_identity_cachemax); 656 KAUTH_IDENTITY_UNLOCK(); 657 658 KAUTH_GROUPS_LOCK(); 659 kauth_groups_cachemax = sz_arg.kcs_group_size; 660 kauth_groups_trimcache(kauth_groups_cachemax); 661 KAUTH_GROUPS_UNLOCK(); 662 663 return (0); 664 } else if (opcode == KAUTH_CLEAR_CACHES) { 665 KAUTH_IDENTITY_LOCK(); 666 kauth_identity_trimcache(0); 667 KAUTH_IDENTITY_UNLOCK(); 668 669 KAUTH_GROUPS_LOCK(); 670 kauth_groups_trimcache(0); 671 KAUTH_GROUPS_UNLOCK(); 672 } else if (opcode == KAUTH_EXTLOOKUP_DEREGISTER) { 673 /* 674 * Terminate outstanding requests; without an authoritative 675 * resolver, we are now back on our own authority. 676 */ 677 struct kauth_resolver_work *killp; 678 679 KAUTH_RESOLVER_LOCK(); 680 681 /* 682 * Clear the identity, but also mark it as unregistered so 683 * there is no explicit future expectation of us getting a 684 * new resolver any time soon. 685 */ 686 kauth_resolver_identity = 0; 687 kauth_resolver_registered = 0; 688 689 TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link) 690 wakeup(killp); 691 TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link) 692 wakeup(killp); 693 /* Cause all waiting-for-work threads to return EIO */ 694 wakeup((caddr_t)&kauth_resolver_unsubmitted); 695 KAUTH_RESOLVER_UNLOCK(); 696 } 697 698 /* 699 * Got a result returning? 700 */ 701 if (opcode & KAUTH_EXTLOOKUP_RESULT) { 702 if ((error = kauth_resolver_complete(message)) != 0) 703 return(error); 704 } 705 706 /* 707 * Caller wants to take more work? 708 */ 709 if (opcode & KAUTH_EXTLOOKUP_WORKER) { 710 if ((error = kauth_resolver_getwork(message)) != 0) 711 return(error); 712 } 713 714 return(0); 715} 716 717 718/* 719 * kauth_resolver_getwork_continue 720 * 721 * Description: Continuation for kauth_resolver_getwork 722 * 723 * Parameters: result Error code or 0 for the sleep 724 * that got us to this function 725 * 726 * Returns: 0 Success 727 * EINTR Interrupted (e.g. by signal) 728 * kauth_resolver_getwork2:EFAULT 729 * 730 * Notes: See kauth_resolver_getwork(0 and kauth_resolver_getwork2() for 731 * more information. 732 */ 733static int 734kauth_resolver_getwork_continue(int result) 735{ 736 thread_t thread; 737 struct uthread *ut; 738 user_addr_t message; 739 740 if (result) { 741 KAUTH_RESOLVER_UNLOCK(); 742 return(result); 743 } 744 745 /* 746 * If we lost a race with another thread/memberd restarting, then we 747 * need to go back to sleep to look for more work. If it was memberd 748 * restarting, then the msleep0() will error out here, as our thread 749 * will already be "dead". 750 */ 751 if (TAILQ_FIRST(&kauth_resolver_unsubmitted) == NULL) { 752 int error; 753 754 error = msleep0(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0, kauth_resolver_getwork_continue); 755 /* 756 * If this is a wakeup from another thread in the resolver 757 * deregistering it, error out the request-for-work thread 758 */ 759 if (!kauth_resolver_identity) 760 error = EIO; 761 KAUTH_RESOLVER_UNLOCK(); 762 return(error); 763 } 764 765 thread = current_thread(); 766 ut = get_bsdthread_info(thread); 767 message = ut->uu_kevent.uu_kauth.message; 768 return(kauth_resolver_getwork2(message)); 769} 770 771 772/* 773 * kauth_resolver_getwork2 774 * 775 * Decription: Common utility function to copy out a identity resolver work 776 * item from the kernel to user space as part of the user space 777 * identity resolver requesting work. 778 * 779 * Parameters: message message to user space 780 * 781 * Returns: 0 Success 782 * EFAULT Bad user space message address 783 * 784 * Notes: This common function exists to permit the use of continuations 785 * in the identity resolution process. This frees up the stack 786 * while we are waiting for the user space resolver to complete 787 * a request. This is specifically used so that our per thread 788 * cost can be small, and we will therefore be willing to run a 789 * larger number of threads in the user space identity resolver. 790 */ 791static int 792kauth_resolver_getwork2(user_addr_t message) 793{ 794 struct kauth_resolver_work *workp; 795 int error; 796 797 /* 798 * Note: We depend on the caller protecting us from a NULL work item 799 * queue, since we must have the kauth resolver lock on entry to this 800 * function. 801 */ 802 workp = TAILQ_FIRST(&kauth_resolver_unsubmitted); 803 804 /* 805 * Copy out the external lookup structure for the request, not 806 * including the el_extend field, which contains the address of the 807 * external buffer provided by the external resolver into which we 808 * copy the extension request information. 809 */ 810 /* BEFORE FIELD */ 811 if ((error = copyout(&workp->kr_work, message, offsetof(struct kauth_identity_extlookup, el_extend))) != 0) { 812 KAUTH_DEBUG("RESOLVER - error submitting work to resolve"); 813 goto out; 814 } 815 /* AFTER FIELD */ 816 if ((error = copyout(&workp->kr_work.el_info_reserved_1, 817 message + offsetof(struct kauth_identity_extlookup, el_info_reserved_1), 818 sizeof(struct kauth_identity_extlookup) - offsetof(struct kauth_identity_extlookup, el_info_reserved_1))) != 0) { 819 KAUTH_DEBUG("RESOLVER - error submitting work to resolve"); 820 goto out; 821 } 822 823 /* 824 * Handle extended requests here; if we have a request of a type where 825 * the kernel wants a translation of extended information, then we need 826 * to copy it out into the extended buffer, assuming the buffer is 827 * valid; we only attempt to get the buffer address if we have request 828 * data to copy into it. 829 */ 830 831 /* 832 * translate a user@domain string into a uid/gid/whatever 833 */ 834 if (workp->kr_work.el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM | KAUTH_EXTLOOKUP_VALID_GRNAM)) { 835 uint64_t uaddr; 836 837 error = copyin(message + offsetof(struct kauth_identity_extlookup, el_extend), &uaddr, sizeof(uaddr)); 838 if (!error) { 839 size_t actual; /* not used */ 840 /* 841 * Use copyoutstr() to reduce the copy size; we let 842 * this catch a NULL uaddr because we shouldn't be 843 * asking in that case anyway. 844 */ 845 error = copyoutstr(CAST_DOWN(void *,workp->kr_extend), uaddr, MAXPATHLEN, &actual); 846 } 847 if (error) { 848 KAUTH_DEBUG("RESOLVER - error submitting work to resolve"); 849 goto out; 850 } 851 } 852 TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link); 853 workp->kr_flags &= ~KAUTH_REQUEST_UNSUBMITTED; 854 workp->kr_flags |= KAUTH_REQUEST_SUBMITTED; 855 TAILQ_INSERT_TAIL(&kauth_resolver_submitted, workp, kr_link); 856 857out: 858 KAUTH_RESOLVER_UNLOCK(); 859 return(error); 860} 861 862 863/* 864 * kauth_resolver_getwork 865 * 866 * Description: Get a work item from the enqueued requests from the kernel and 867 * give it to the user space daemon. 868 * 869 * Parameters: message message to user space 870 * 871 * Returns: 0 Success 872 * EINTR Interrupted (e.g. by signal) 873 * kauth_resolver_getwork2:EFAULT 874 * 875 * Notes: This function blocks in a continuation if there are no work 876 * items available for processing at the time the user space 877 * identity resolution daemon makes a request for work. This 878 * permits a large number of threads to be used by the daemon, 879 * without using a lot of wired kernel memory when there are no 880 * actual request outstanding. 881 */ 882static int 883kauth_resolver_getwork(user_addr_t message) 884{ 885 struct kauth_resolver_work *workp; 886 int error; 887 888 KAUTH_RESOLVER_LOCK(); 889 error = 0; 890 while ((workp = TAILQ_FIRST(&kauth_resolver_unsubmitted)) == NULL) { 891 thread_t thread = current_thread(); 892 struct uthread *ut = get_bsdthread_info(thread); 893 894 ut->uu_kevent.uu_kauth.message = message; 895 error = msleep0(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0, kauth_resolver_getwork_continue); 896 KAUTH_RESOLVER_UNLOCK(); 897 /* 898 * If this is a wakeup from another thread in the resolver 899 * deregistering it, error out the request-for-work thread 900 */ 901 if (!kauth_resolver_identity) 902 error = EIO; 903 return(error); 904 } 905 return kauth_resolver_getwork2(message); 906} 907 908 909/* 910 * kauth_resolver_complete 911 * 912 * Description: Return a result from userspace. 913 * 914 * Parameters: message message from user space 915 * 916 * Returns: 0 Success 917 * EIO The resolver is dead 918 * copyin:EFAULT Bad message from user space 919 */ 920static int 921kauth_resolver_complete(user_addr_t message) 922{ 923 struct kauth_identity_extlookup extl; 924 struct kauth_resolver_work *workp; 925 struct kauth_resolver_work *killp; 926 int error, result; 927 928 /* 929 * Copy in the mesage, including the extension field, since we are 930 * copying into a local variable. 931 */ 932 if ((error = copyin(message, &extl, sizeof(extl))) != 0) { 933 KAUTH_DEBUG("RESOLVER - error getting completed work\n"); 934 return(error); 935 } 936 937 KAUTH_RESOLVER_LOCK(); 938 939 error = 0; 940 result = 0; 941 switch (extl.el_result) { 942 case KAUTH_EXTLOOKUP_INPROG: 943 { 944 static int once = 0; 945 946 /* XXX this should go away once memberd is updated */ 947 if (!once) { 948 printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n"); 949 once = 1; 950 } 951 } 952 /* FALLTHROUGH */ 953 954 case KAUTH_EXTLOOKUP_SUCCESS: 955 break; 956 957 case KAUTH_EXTLOOKUP_FATAL: 958 /* fatal error means the resolver is dead */ 959 KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity); 960 /* 961 * Terminate outstanding requests; without an authoritative 962 * resolver, we are now back on our own authority. Tag the 963 * resolver unregistered to prevent kauth_cred_ismember_gid() 964 * enqueueing more work until a new one is registered. This 965 * mitigates the damage a crashing resolver may inflict. 966 */ 967 kauth_resolver_identity = 0; 968 kauth_resolver_registered = 0; 969 970 TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link) 971 wakeup(killp); 972 TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link) 973 wakeup(killp); 974 /* Cause all waiting-for-work threads to return EIO */ 975 wakeup((caddr_t)&kauth_resolver_unsubmitted); 976 /* and return EIO to the caller */ 977 error = EIO; 978 break; 979 980 case KAUTH_EXTLOOKUP_BADRQ: 981 KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl.el_seqno); 982 result = EINVAL; 983 break; 984 985 case KAUTH_EXTLOOKUP_FAILURE: 986 KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl.el_seqno); 987 result = EIO; 988 break; 989 990 default: 991 KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl.el_result); 992 result = EIO; 993 break; 994 } 995 996 /* 997 * In the case of a fatal error, we assume that the resolver will 998 * restart quickly and re-collect all of the outstanding requests. 999 * Thus, we don't complete the request which returned the fatal 1000 * error status. 1001 */ 1002 if (extl.el_result != KAUTH_EXTLOOKUP_FATAL) { 1003 /* scan our list for this request */ 1004 TAILQ_FOREACH(workp, &kauth_resolver_submitted, kr_link) { 1005 /* found it? */ 1006 if (workp->kr_seqno == extl.el_seqno) { 1007 1008 /* 1009 * Get the request of the submitted queue so 1010 * that it is not cleaned up out from under 1011 * us by a timeout. 1012 */ 1013 TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link); 1014 workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED; 1015 workp->kr_flags |= KAUTH_REQUEST_DONE; 1016 workp->kr_result = result; 1017 1018 /* Copy the result message to the work item. */ 1019 memcpy(&workp->kr_work, &extl, sizeof(struct kauth_identity_extlookup)); 1020 1021 /* 1022 * Check if we have a result in the extension 1023 * field; if we do, then we need to separately 1024 * copy the data from the message el_extend 1025 * into the request buffer that's in the work 1026 * item. We have to do it here because we do 1027 * not want to wake up the waiter until the 1028 * data is in their buffer, and because the 1029 * actual request response may be destroyed 1030 * by the time the requester wakes up, and they 1031 * do not have access to the user space buffer 1032 * address. 1033 * 1034 * It is safe to drop and reacquire the lock 1035 * here because we've already removed the item 1036 * from the submission queue, but have not yet 1037 * moved it to the completion queue. Note that 1038 * near simultaneous requests may result in 1039 * duplication of requests for items in this 1040 * window. This should not be a performance 1041 * issue and is easily detectable by comparing 1042 * time to live on last response vs. time of 1043 * next request in the resolver logs. 1044 */ 1045 if (extl.el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM|KAUTH_EXTLOOKUP_VALID_GRNAM)) { 1046 size_t actual; /* notused */ 1047 1048 KAUTH_RESOLVER_UNLOCK(); 1049 error = copyinstr(extl.el_extend, CAST_DOWN(void *, workp->kr_extend), MAXPATHLEN, &actual); 1050 KAUTH_RESOLVER_LOCK(); 1051 } 1052 1053 /* 1054 * Move the completed work item to the 1055 * completion queue and wake up requester(s) 1056 */ 1057 TAILQ_INSERT_TAIL(&kauth_resolver_done, workp, kr_link); 1058 wakeup(workp); 1059 break; 1060 } 1061 } 1062 } 1063 /* 1064 * Note that it's OK for us not to find anything; if the request has 1065 * timed out the work record will be gone. 1066 */ 1067 KAUTH_RESOLVER_UNLOCK(); 1068 1069 return(error); 1070} 1071#endif /* CONFIG_EXT_RESOLVER */ 1072 1073 1074/* 1075 * Identity cache. 1076 */ 1077 1078#define KI_VALID_UID (1<<0) /* UID and GID are mutually exclusive */ 1079#define KI_VALID_GID (1<<1) 1080#define KI_VALID_GUID (1<<2) 1081#define KI_VALID_NTSID (1<<3) 1082#define KI_VALID_PWNAM (1<<4) /* Used for translation */ 1083#define KI_VALID_GRNAM (1<<5) /* Used for translation */ 1084#define KI_VALID_GROUPS (1<<6) 1085 1086#if CONFIG_EXT_RESOLVER 1087/* 1088 * kauth_identity_init 1089 * 1090 * Description: Initialize the kernel side of the credential identity resolver 1091 * 1092 * Parameters: (void) 1093 * 1094 * Returns: (void) 1095 * 1096 * Notes: Initialize the credential identity resolver for use; the 1097 * credential identity resolver is the KPI used to communicate 1098 * with a user space credential identity resolver daemon. 1099 * 1100 * This function is called from kauth_init() in the file 1101 * kern_authorization.c. 1102 */ 1103void 1104kauth_identity_init(void) 1105{ 1106 TAILQ_INIT(&kauth_identities); 1107 kauth_identity_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/); 1108} 1109 1110 1111/* 1112 * kauth_identity_alloc 1113 * 1114 * Description: Allocate and fill out a kauth_identity structure for 1115 * translation between {UID|GID}/GUID/NTSID 1116 * 1117 * Parameters: uid 1118 * 1119 * Returns: NULL Insufficient memory to satisfy 1120 * the request 1121 * !NULL A pointer to the allocated 1122 * structure, filled in 1123 * 1124 * Notes: It is illegal to translate between UID and GID; any given UUID 1125 * or NTSID can only refer to an NTSID or UUID (respectively), 1126 * and *either* a UID *or* a GID, but not both. 1127 */ 1128static struct kauth_identity * 1129kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, 1130 ntsid_t *ntsidp, time_t ntsid_expiry, int supgrpcnt, gid_t *supgrps, time_t groups_expiry, 1131 const char *name, int nametype) 1132{ 1133 struct kauth_identity *kip; 1134 1135 /* get and fill in a new identity */ 1136 MALLOC(kip, struct kauth_identity *, sizeof(*kip), M_KAUTH, M_WAITOK | M_ZERO); 1137 if (kip != NULL) { 1138 if (gid != KAUTH_GID_NONE) { 1139 kip->ki_gid = gid; 1140 kip->ki_valid = KI_VALID_GID; 1141 } 1142 if (uid != KAUTH_UID_NONE) { 1143 if (kip->ki_valid & KI_VALID_GID) 1144 panic("can't allocate kauth identity with both uid and gid"); 1145 kip->ki_uid = uid; 1146 kip->ki_valid = KI_VALID_UID; 1147 } 1148 if (supgrpcnt) { 1149 assert(supgrpcnt <= NGROUPS); 1150 assert(supgrps != NULL); 1151 if (kip->ki_valid & KI_VALID_GID) 1152 panic("can't allocate kauth identity with both gid and supplementary groups"); 1153 kip->ki_supgrpcnt = supgrpcnt; 1154 memcpy(kip->ki_supgrps, supgrps, sizeof(supgrps[0]) * supgrpcnt); 1155 kip->ki_valid |= KI_VALID_GROUPS; 1156 } 1157 kip->ki_groups_expiry = groups_expiry; 1158 if (guidp != NULL) { 1159 kip->ki_guid = *guidp; 1160 kip->ki_valid |= KI_VALID_GUID; 1161 } 1162 kip->ki_guid_expiry = guid_expiry; 1163 if (ntsidp != NULL) { 1164 kip->ki_ntsid = *ntsidp; 1165 kip->ki_valid |= KI_VALID_NTSID; 1166 } 1167 kip->ki_ntsid_expiry = ntsid_expiry; 1168 if (name != NULL) { 1169 kip->ki_name = name; 1170 kip->ki_valid |= nametype; 1171 } 1172 } 1173 return(kip); 1174} 1175 1176 1177/* 1178 * kauth_identity_register_and_free 1179 * 1180 * Description: Register an association between identity tokens. The passed 1181 * 'kip' is consumed by this function. 1182 * 1183 * Parameters: kip Pointer to kauth_identity 1184 * structure to register 1185 * 1186 * Returns: (void) 1187 * 1188 * Notes: The memory pointer to by 'kip' is assumed to have been 1189 * previously allocated via kauth_identity_alloc(). 1190 */ 1191static void 1192kauth_identity_register_and_free(struct kauth_identity *kip) 1193{ 1194 struct kauth_identity *ip; 1195 1196 /* 1197 * We search the cache for the UID listed in the incoming association. 1198 * If we already have an entry, the new information is merged. 1199 */ 1200 ip = NULL; 1201 KAUTH_IDENTITY_LOCK(); 1202 if (kip->ki_valid & KI_VALID_UID) { 1203 if (kip->ki_valid & KI_VALID_GID) 1204 panic("kauth_identity: can't insert record with both UID and GID as key"); 1205 TAILQ_FOREACH(ip, &kauth_identities, ki_link) 1206 if ((ip->ki_valid & KI_VALID_UID) && (ip->ki_uid == kip->ki_uid)) 1207 break; 1208 } else if (kip->ki_valid & KI_VALID_GID) { 1209 TAILQ_FOREACH(ip, &kauth_identities, ki_link) 1210 if ((ip->ki_valid & KI_VALID_GID) && (ip->ki_gid == kip->ki_gid)) 1211 break; 1212 } else { 1213 panic("kauth_identity: can't insert record without UID or GID as key"); 1214 } 1215 1216 if (ip != NULL) { 1217 /* we already have an entry, merge/overwrite */ 1218 if (kip->ki_valid & KI_VALID_GUID) { 1219 ip->ki_guid = kip->ki_guid; 1220 ip->ki_valid |= KI_VALID_GUID; 1221 } 1222 ip->ki_guid_expiry = kip->ki_guid_expiry; 1223 if (kip->ki_valid & KI_VALID_NTSID) { 1224 ip->ki_ntsid = kip->ki_ntsid; 1225 ip->ki_valid |= KI_VALID_NTSID; 1226 } 1227 ip->ki_ntsid_expiry = kip->ki_ntsid_expiry; 1228 /* a valid ki_name field overwrites the previous name field */ 1229 if (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)) { 1230 /* if there's an old one, discard it */ 1231 const char *oname = NULL; 1232 if (ip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)) 1233 oname = ip->ki_name; 1234 ip->ki_name = kip->ki_name; 1235 kip->ki_name = oname; 1236 } 1237 /* and discard the incoming entry */ 1238 ip = kip; 1239 } else { 1240 /* 1241 * if we don't have any information on this identity, add it; 1242 * if it pushes us over our limit, discard the oldest one. 1243 */ 1244 TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link); 1245 if (++kauth_identity_count > kauth_identity_cachemax) { 1246 ip = TAILQ_LAST(&kauth_identities, kauth_identity_head); 1247 TAILQ_REMOVE(&kauth_identities, ip, ki_link); 1248 kauth_identity_count--; 1249 } 1250 } 1251 KAUTH_IDENTITY_UNLOCK(); 1252 /* have to drop lock before freeing expired entry (it may be in use) */ 1253 if (ip != NULL) { 1254 /* if the ki_name field is used, clear it first */ 1255 if (ip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM)) 1256 vfs_removename(ip->ki_name); 1257 /* free the expired entry */ 1258 FREE(ip, M_KAUTH); 1259 } 1260} 1261 1262 1263/* 1264 * kauth_identity_updatecache 1265 * 1266 * Description: Given a lookup result, add any associations that we don't 1267 * currently have; replace ones which have changed. 1268 * 1269 * Parameters: elp External lookup result from 1270 * user space daemon to kernel 1271 * rkip pointer to returned kauth 1272 * identity, or NULL 1273 * extend_data Extended data (can vary) 1274 * 1275 * Returns: (void) 1276 * 1277 * Implicit returns: 1278 * *rkip Modified (if non-NULL) 1279 * 1280 * Notes: For extended information requests, this code relies on the fact 1281 * that elp->el_flags is never used as an rvalue, and is only 1282 * ever bit-tested for valid lookup information we are willing 1283 * to cache. 1284 * 1285 * XXX: We may have to do the same in the case that extended data was 1286 * passed out to user space to ensure that the request string 1287 * gets cached; we may also be able to use the rkip as an 1288 * input to avoid this. The jury is still out. 1289 * 1290 * XXX: This codes performance could be improved for multiple valid 1291 * results by combining the loop iteration in a single loop. 1292 */ 1293static void 1294kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip, uint64_t extend_data) 1295{ 1296 struct timeval tv; 1297 struct kauth_identity *kip; 1298 const char *speculative_name = NULL; 1299 1300 microuptime(&tv); 1301 1302 /* 1303 * If there is extended data, and that data represents a name rather 1304 * than something else, speculatively create an entry for it in the 1305 * string cache. We do this to avoid holding the KAUTH_IDENTITY_LOCK 1306 * over the allocation later. 1307 */ 1308 if (elp->el_flags & (KAUTH_EXTLOOKUP_VALID_PWNAM | KAUTH_EXTLOOKUP_VALID_GRNAM)) { 1309 const char *tmp = CAST_DOWN(const char *,extend_data); 1310 speculative_name = vfs_addname(tmp, strnlen(tmp, MAXPATHLEN - 1), 0, 0); 1311 } 1312 1313 /* user identity? */ 1314 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID) { 1315 KAUTH_IDENTITY_LOCK(); 1316 TAILQ_FOREACH(kip, &kauth_identities, ki_link) { 1317 /* matching record */ 1318 if ((kip->ki_valid & KI_VALID_UID) && (kip->ki_uid == elp->el_uid)) { 1319 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) { 1320 assert(elp->el_sup_grp_cnt <= NGROUPS); 1321 kip->ki_supgrpcnt = elp->el_sup_grp_cnt; 1322 memcpy(kip->ki_supgrps, elp->el_sup_groups, sizeof(elp->el_sup_groups[0]) * kip->ki_supgrpcnt); 1323 kip->ki_valid |= KI_VALID_GROUPS; 1324 kip->ki_groups_expiry = (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0; 1325 } 1326 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) { 1327 kip->ki_guid = elp->el_uguid; 1328 kip->ki_valid |= KI_VALID_GUID; 1329 } 1330 kip->ki_guid_expiry = (elp->el_uguid_valid) ? tv.tv_sec + elp->el_uguid_valid : 0; 1331 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) { 1332 kip->ki_ntsid = elp->el_usid; 1333 kip->ki_valid |= KI_VALID_NTSID; 1334 } 1335 kip->ki_ntsid_expiry = (elp->el_usid_valid) ? tv.tv_sec + elp->el_usid_valid : 0; 1336 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM) { 1337 const char *oname = kip->ki_name; 1338 kip->ki_name = speculative_name; 1339 speculative_name = NULL; 1340 kip->ki_valid |= KI_VALID_PWNAM; 1341 if (oname) { 1342 /* 1343 * free oname (if any) outside 1344 * the lock 1345 */ 1346 speculative_name = oname; 1347 } 1348 } 1349 kauth_identity_lru(kip); 1350 if (rkip != NULL) 1351 *rkip = *kip; 1352 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid)); 1353 break; 1354 } 1355 } 1356 KAUTH_IDENTITY_UNLOCK(); 1357 /* not found in cache, add new record */ 1358 if (kip == NULL) { 1359 kip = kauth_identity_alloc(elp->el_uid, KAUTH_GID_NONE, 1360 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) ? &elp->el_uguid : NULL, 1361 (elp->el_uguid_valid) ? tv.tv_sec + elp->el_uguid_valid : 0, 1362 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) ? &elp->el_usid : NULL, 1363 (elp->el_usid_valid) ? tv.tv_sec + elp->el_usid_valid : 0, 1364 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_grp_cnt : 0, 1365 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_groups : NULL, 1366 (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0, 1367 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM) ? speculative_name : NULL, 1368 KI_VALID_PWNAM); 1369 if (kip != NULL) { 1370 if (rkip != NULL) 1371 *rkip = *kip; 1372 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_PWNAM) 1373 speculative_name = NULL; 1374 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid)); 1375 kauth_identity_register_and_free(kip); 1376 } 1377 } 1378 } 1379 1380 /* group identity? (ignore, if we already processed it as a user) */ 1381 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID && !(elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID)) { 1382 KAUTH_IDENTITY_LOCK(); 1383 TAILQ_FOREACH(kip, &kauth_identities, ki_link) { 1384 /* matching record */ 1385 if ((kip->ki_valid & KI_VALID_GID) && (kip->ki_gid == elp->el_gid)) { 1386 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) { 1387 kip->ki_guid = elp->el_gguid; 1388 kip->ki_valid |= KI_VALID_GUID; 1389 } 1390 kip->ki_guid_expiry = (elp->el_gguid_valid) ? tv.tv_sec + elp->el_gguid_valid : 0; 1391 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) { 1392 kip->ki_ntsid = elp->el_gsid; 1393 kip->ki_valid |= KI_VALID_NTSID; 1394 } 1395 kip->ki_ntsid_expiry = (elp->el_gsid_valid) ? tv.tv_sec + elp->el_gsid_valid : 0; 1396 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM) { 1397 const char *oname = kip->ki_name; 1398 kip->ki_name = speculative_name; 1399 speculative_name = NULL; 1400 kip->ki_valid |= KI_VALID_GRNAM; 1401 if (oname) { 1402 /* 1403 * free oname (if any) outside 1404 * the lock 1405 */ 1406 speculative_name = oname; 1407 } 1408 } 1409 kauth_identity_lru(kip); 1410 if (rkip != NULL) 1411 *rkip = *kip; 1412 KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid)); 1413 break; 1414 } 1415 } 1416 KAUTH_IDENTITY_UNLOCK(); 1417 /* not found in cache, add new record */ 1418 if (kip == NULL) { 1419 kip = kauth_identity_alloc(KAUTH_UID_NONE, elp->el_gid, 1420 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) ? &elp->el_gguid : NULL, 1421 (elp->el_gguid_valid) ? tv.tv_sec + elp->el_gguid_valid : 0, 1422 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) ? &elp->el_gsid : NULL, 1423 (elp->el_gsid_valid) ? tv.tv_sec + elp->el_gsid_valid : 0, 1424 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_grp_cnt : 0, 1425 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_SUPGRPS) ? elp->el_sup_groups : NULL, 1426 (elp->el_member_valid) ? tv.tv_sec + elp->el_member_valid : 0, 1427 (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM) ? speculative_name : NULL, 1428 KI_VALID_GRNAM); 1429 if (kip != NULL) { 1430 if (rkip != NULL) 1431 *rkip = *kip; 1432 if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GRNAM) 1433 speculative_name = NULL; 1434 KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid)); 1435 kauth_identity_register_and_free(kip); 1436 } 1437 } 1438 } 1439 1440 /* If we have a name reference to drop, drop it here */ 1441 if (speculative_name != NULL) { 1442 vfs_removename(speculative_name); 1443 } 1444} 1445 1446 1447/* 1448 * Trim older entries from the identity cache. 1449 * 1450 * Must be called with the identity cache lock held. 1451 */ 1452static void 1453kauth_identity_trimcache(int newsize) { 1454 struct kauth_identity *kip; 1455 1456 lck_mtx_assert(kauth_identity_mtx, LCK_MTX_ASSERT_OWNED); 1457 1458 while (kauth_identity_count > newsize) { 1459 kip = TAILQ_LAST(&kauth_identities, kauth_identity_head); 1460 TAILQ_REMOVE(&kauth_identities, kip, ki_link); 1461 kauth_identity_count--; 1462 FREE(kip, M_KAUTH); 1463 } 1464} 1465 1466/* 1467 * kauth_identity_lru 1468 * 1469 * Description: Promote the entry to the head of the LRU, assumes the cache 1470 * is locked. 1471 * 1472 * Parameters: kip kauth identity to move to the 1473 * head of the LRU list, if it's 1474 * not already there 1475 * 1476 * Returns: (void) 1477 * 1478 * Notes: This is called even if the entry has expired; typically an 1479 * expired entry that's been looked up is about to be revalidated, 1480 * and having it closer to the head of the LRU means finding it 1481 * quickly again when the revalidation comes through. 1482 */ 1483static void 1484kauth_identity_lru(struct kauth_identity *kip) 1485{ 1486 if (kip != TAILQ_FIRST(&kauth_identities)) { 1487 TAILQ_REMOVE(&kauth_identities, kip, ki_link); 1488 TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link); 1489 } 1490} 1491 1492 1493/* 1494 * kauth_identity_guid_expired 1495 * 1496 * Description: Handle lazy expiration of GUID translations. 1497 * 1498 * Parameters: kip kauth identity to check for 1499 * GUID expiration 1500 * 1501 * Returns: 1 Expired 1502 * 0 Not expired 1503 */ 1504static int 1505kauth_identity_guid_expired(struct kauth_identity *kip) 1506{ 1507 struct timeval tv; 1508 1509 /* 1510 * Expiration time of 0 means this entry is persistent. 1511 */ 1512 if (kip->ki_guid_expiry == 0) 1513 return (0); 1514 1515 microuptime(&tv); 1516 KAUTH_DEBUG("CACHE - GUID expires @ %ld now %ld", kip->ki_guid_expiry, tv.tv_sec); 1517 1518 return((kip->ki_guid_expiry <= tv.tv_sec) ? 1 : 0); 1519} 1520 1521 1522/* 1523 * kauth_identity_ntsid_expired 1524 * 1525 * Description: Handle lazy expiration of NTSID translations. 1526 * 1527 * Parameters: kip kauth identity to check for 1528 * NTSID expiration 1529 * 1530 * Returns: 1 Expired 1531 * 0 Not expired 1532 */ 1533static int 1534kauth_identity_ntsid_expired(struct kauth_identity *kip) 1535{ 1536 struct timeval tv; 1537 1538 /* 1539 * Expiration time of 0 means this entry is persistent. 1540 */ 1541 if (kip->ki_ntsid_expiry == 0) 1542 return (0); 1543 1544 microuptime(&tv); 1545 KAUTH_DEBUG("CACHE - NTSID expires @ %ld now %ld", kip->ki_ntsid_expiry, tv.tv_sec); 1546 1547 return((kip->ki_ntsid_expiry <= tv.tv_sec) ? 1 : 0); 1548} 1549 1550/* 1551 * kauth_identity_groups_expired 1552 * 1553 * Description: Handle lazy expiration of supplemental group translations. 1554 * 1555 * Parameters: kip kauth identity to check for 1556 * groups expiration 1557 * 1558 * Returns: 1 Expired 1559 * 0 Not expired 1560 */ 1561static int 1562kauth_identity_groups_expired(struct kauth_identity *kip) 1563{ 1564 struct timeval tv; 1565 1566 /* 1567 * Expiration time of 0 means this entry is persistent. 1568 */ 1569 if (kip->ki_groups_expiry == 0) 1570 return (0); 1571 1572 microuptime(&tv); 1573 KAUTH_DEBUG("CACHE - GROUPS expires @ %ld now %ld\n", kip->ki_groups_expiry, tv.tv_sec); 1574 1575 return((kip->ki_groups_expiry <= tv.tv_sec) ? 1 : 0); 1576} 1577 1578/* 1579 * kauth_identity_find_uid 1580 * 1581 * Description: Search for an entry by UID 1582 * 1583 * Parameters: uid UID to find 1584 * kir Pointer to return area 1585 * getname Name buffer, if ki_name wanted 1586 * 1587 * Returns: 0 Found 1588 * ENOENT Not found 1589 * 1590 * Implicit returns: 1591 * *klr Modified, if found 1592 */ 1593static int 1594kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir, char *getname) 1595{ 1596 struct kauth_identity *kip; 1597 1598 KAUTH_IDENTITY_LOCK(); 1599 TAILQ_FOREACH(kip, &kauth_identities, ki_link) { 1600 if ((kip->ki_valid & KI_VALID_UID) && (uid == kip->ki_uid)) { 1601 kauth_identity_lru(kip); 1602 /* Copy via structure assignment */ 1603 *kir = *kip; 1604 /* If a name is wanted and one exists, copy it out */ 1605 if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))) 1606 strlcpy(getname, kip->ki_name, MAXPATHLEN); 1607 break; 1608 } 1609 } 1610 KAUTH_IDENTITY_UNLOCK(); 1611 return((kip == NULL) ? ENOENT : 0); 1612} 1613 1614 1615/* 1616 * kauth_identity_find_gid 1617 * 1618 * Description: Search for an entry by GID 1619 * 1620 * Parameters: gid GID to find 1621 * kir Pointer to return area 1622 * getname Name buffer, if ki_name wanted 1623 * 1624 * Returns: 0 Found 1625 * ENOENT Not found 1626 * 1627 * Implicit returns: 1628 * *klr Modified, if found 1629 */ 1630static int 1631kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir, char *getname) 1632{ 1633 struct kauth_identity *kip; 1634 1635 KAUTH_IDENTITY_LOCK(); 1636 TAILQ_FOREACH(kip, &kauth_identities, ki_link) { 1637 if ((kip->ki_valid & KI_VALID_GID) && (gid == kip->ki_gid)) { 1638 kauth_identity_lru(kip); 1639 /* Copy via structure assignment */ 1640 *kir = *kip; 1641 /* If a name is wanted and one exists, copy it out */ 1642 if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))) 1643 strlcpy(getname, kip->ki_name, MAXPATHLEN); 1644 break; 1645 } 1646 } 1647 KAUTH_IDENTITY_UNLOCK(); 1648 return((kip == NULL) ? ENOENT : 0); 1649} 1650 1651 1652/* 1653 * kauth_identity_find_guid 1654 * 1655 * Description: Search for an entry by GUID 1656 * 1657 * Parameters: guidp Pointer to GUID to find 1658 * kir Pointer to return area 1659 * getname Name buffer, if ki_name wanted 1660 * 1661 * Returns: 0 Found 1662 * ENOENT Not found 1663 * 1664 * Implicit returns: 1665 * *klr Modified, if found 1666 * 1667 * Note: The association may be expired, in which case the caller 1668 * may elect to call out to userland to revalidate. 1669 */ 1670static int 1671kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir, char *getname) 1672{ 1673 struct kauth_identity *kip; 1674 1675 KAUTH_IDENTITY_LOCK(); 1676 TAILQ_FOREACH(kip, &kauth_identities, ki_link) { 1677 if ((kip->ki_valid & KI_VALID_GUID) && (kauth_guid_equal(guidp, &kip->ki_guid))) { 1678 kauth_identity_lru(kip); 1679 /* Copy via structure assignment */ 1680 *kir = *kip; 1681 /* If a name is wanted and one exists, copy it out */ 1682 if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))) 1683 strlcpy(getname, kip->ki_name, MAXPATHLEN); 1684 break; 1685 } 1686 } 1687 KAUTH_IDENTITY_UNLOCK(); 1688 return((kip == NULL) ? ENOENT : 0); 1689} 1690 1691/* 1692 * kauth_identity_find_nam 1693 * 1694 * Description: Search for an entry by name 1695 * 1696 * Parameters: name Pointer to name to find 1697 * valid KI_VALID_PWNAM or KI_VALID_GRNAM 1698 * kir Pointer to return area 1699 * 1700 * Returns: 0 Found 1701 * ENOENT Not found 1702 * 1703 * Implicit returns: 1704 * *klr Modified, if found 1705 */ 1706static int 1707kauth_identity_find_nam(char *name, int valid, struct kauth_identity *kir) 1708{ 1709 struct kauth_identity *kip; 1710 1711 KAUTH_IDENTITY_LOCK(); 1712 TAILQ_FOREACH(kip, &kauth_identities, ki_link) { 1713 if ((kip->ki_valid & valid) && !strcmp(name, kip->ki_name)) { 1714 kauth_identity_lru(kip); 1715 /* Copy via structure assignment */ 1716 *kir = *kip; 1717 break; 1718 } 1719 } 1720 KAUTH_IDENTITY_UNLOCK(); 1721 return((kip == NULL) ? ENOENT : 0); 1722} 1723 1724 1725/* 1726 * kauth_identity_find_ntsid 1727 * 1728 * Description: Search for an entry by NTSID 1729 * 1730 * Parameters: ntsid Pointer to NTSID to find 1731 * kir Pointer to return area 1732 * getname Name buffer, if ki_name wanted 1733 * 1734 * Returns: 0 Found 1735 * ENOENT Not found 1736 * 1737 * Implicit returns: 1738 * *klr Modified, if found 1739 * 1740 * Note: The association may be expired, in which case the caller 1741 * may elect to call out to userland to revalidate. 1742 */ 1743static int 1744kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir, char *getname) 1745{ 1746 struct kauth_identity *kip; 1747 1748 KAUTH_IDENTITY_LOCK(); 1749 TAILQ_FOREACH(kip, &kauth_identities, ki_link) { 1750 if ((kip->ki_valid & KI_VALID_NTSID) && (kauth_ntsid_equal(ntsid, &kip->ki_ntsid))) { 1751 kauth_identity_lru(kip); 1752 /* Copy via structure assignment */ 1753 *kir = *kip; 1754 /* If a name is wanted and one exists, copy it out */ 1755 if (getname != NULL && (kip->ki_valid & (KI_VALID_PWNAM | KI_VALID_GRNAM))) 1756 strlcpy(getname, kip->ki_name, MAXPATHLEN); 1757 break; 1758 } 1759 } 1760 KAUTH_IDENTITY_UNLOCK(); 1761 return((kip == NULL) ? ENOENT : 0); 1762} 1763#endif /* CONFIG_EXT_RESOLVER */ 1764 1765 1766/* 1767 * GUID handling. 1768 */ 1769guid_t kauth_null_guid; 1770 1771 1772/* 1773 * kauth_guid_equal 1774 * 1775 * Description: Determine the equality of two GUIDs 1776 * 1777 * Parameters: guid1 Pointer to first GUID 1778 * guid2 Pointer to second GUID 1779 * 1780 * Returns: 0 If GUIDs are unequal 1781 * !0 If GUIDs are equal 1782 */ 1783int 1784kauth_guid_equal(guid_t *guid1, guid_t *guid2) 1785{ 1786 return(bcmp(guid1, guid2, sizeof(*guid1)) == 0); 1787} 1788 1789 1790/* 1791 * kauth_wellknown_guid 1792 * 1793 * Description: Determine if a GUID is a well-known GUID 1794 * 1795 * Parameters: guid Pointer to GUID to check 1796 * 1797 * Returns: KAUTH_WKG_NOT Not a well known GUID 1798 * KAUTH_WKG_EVERYBODY "Everybody" 1799 * KAUTH_WKG_NOBODY "Nobody" 1800 * KAUTH_WKG_OWNER "Other" 1801 * KAUTH_WKG_GROUP "Group" 1802 */ 1803int 1804kauth_wellknown_guid(guid_t *guid) 1805{ 1806 static char fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef}; 1807 uint32_t code; 1808 /* 1809 * All WKGs begin with the same 12 bytes. 1810 */ 1811 if (bcmp((void *)guid, fingerprint, 12) == 0) { 1812 /* 1813 * The final 4 bytes are our code (in network byte order). 1814 */ 1815 code = OSSwapHostToBigInt32(*(uint32_t *)&guid->g_guid[12]); 1816 switch(code) { 1817 case 0x0000000c: 1818 return(KAUTH_WKG_EVERYBODY); 1819 case 0xfffffffe: 1820 return(KAUTH_WKG_NOBODY); 1821 case 0x0000000a: 1822 return(KAUTH_WKG_OWNER); 1823 case 0x00000010: 1824 return(KAUTH_WKG_GROUP); 1825 } 1826 } 1827 return(KAUTH_WKG_NOT); 1828} 1829 1830 1831/* 1832 * kauth_ntsid_equal 1833 * 1834 * Description: Determine the equality of two NTSIDs (NT Security Identifiers) 1835 * 1836 * Parameters: sid1 Pointer to first NTSID 1837 * sid2 Pointer to second NTSID 1838 * 1839 * Returns: 0 If GUIDs are unequal 1840 * !0 If GUIDs are equal 1841 */ 1842int 1843kauth_ntsid_equal(ntsid_t *sid1, ntsid_t *sid2) 1844{ 1845 /* check sizes for equality, also sanity-check size while we're at it */ 1846 if ((KAUTH_NTSID_SIZE(sid1) == KAUTH_NTSID_SIZE(sid2)) && 1847 (KAUTH_NTSID_SIZE(sid1) <= sizeof(*sid1)) && 1848 bcmp(sid1, sid2, KAUTH_NTSID_SIZE(sid1)) == 0) 1849 return(1); 1850 return(0); 1851} 1852 1853 1854/* 1855 * Identity KPI 1856 * 1857 * We support four tokens representing identity: 1858 * - Credential reference 1859 * - UID 1860 * - GUID 1861 * - NT security identifier 1862 * 1863 * Of these, the UID is the ubiquitous identifier; cross-referencing should 1864 * be done using it. 1865 */ 1866 1867 1868 1869/* 1870 * kauth_cred_change_egid 1871 * 1872 * Description: Set EGID by changing the first element of cr_groups for the 1873 * passed credential; if the new EGID exists in the list of 1874 * groups already, then rotate the old EGID into its position, 1875 * otherwise replace it 1876 * 1877 * Parameters: cred Pointer to the credential to modify 1878 * new_egid The new EGID to set 1879 * 1880 * Returns: 0 The egid did not displace a member of 1881 * the supplementary group list 1882 * 1 The egid being set displaced a member 1883 * of the supplementary groups list 1884 * 1885 * Note: Utility function; internal use only because of locking. 1886 * 1887 * This function operates on the credential passed; the caller 1888 * must operate either on a newly allocated credential (one for 1889 * which there is no hash cache reference and no externally 1890 * visible pointer reference), or a template credential. 1891 */ 1892static int 1893kauth_cred_change_egid(kauth_cred_t cred, gid_t new_egid) 1894{ 1895 int i; 1896 int displaced = 1; 1897#if radar_4600026 1898 int is_member; 1899#endif /* radar_4600026 */ 1900 gid_t old_egid = kauth_cred_getgid(cred); 1901 posix_cred_t pcred = posix_cred_get(cred); 1902 1903 /* Ignoring the first entry, scan for a match for the new egid */ 1904 for (i = 1; i < pcred->cr_ngroups; i++) { 1905 /* 1906 * If we find a match, swap them so we don't lose overall 1907 * group information 1908 */ 1909 if (pcred->cr_groups[i] == new_egid) { 1910 pcred->cr_groups[i] = old_egid; 1911 DEBUG_CRED_CHANGE("kauth_cred_change_egid: unset displaced\n"); 1912 displaced = 0; 1913 break; 1914 } 1915 } 1916 1917#if radar_4600026 1918#error Fix radar 4600026 first!!! 1919 1920/* 1921This is correct for memberd behaviour, but incorrect for POSIX; to address 1922this, we would need to automatically opt-out any SUID/SGID binary, and force 1923it to use initgroups to opt back in. We take the approach of considering it 1924opt'ed out in any group of 16 displacement instead, since it's a much more 1925conservative approach (i.e. less likely to cause things to break). 1926*/ 1927 1928 /* 1929 * If we displaced a member of the supplementary groups list of the 1930 * credential, and we have not opted out of memberd, then if memberd 1931 * says that the credential is a member of the group, then it has not 1932 * actually been displaced. 1933 * 1934 * NB: This is typically a cold code path. 1935 */ 1936 if (displaced && !(pcred->cr_flags & CRF_NOMEMBERD) && 1937 kauth_cred_ismember_gid(cred, new_egid, &is_member) == 0 && 1938 is_member) { 1939 displaced = 0; 1940 DEBUG_CRED_CHANGE("kauth_cred_change_egid: reset displaced\n"); 1941 } 1942#endif /* radar_4600026 */ 1943 1944 /* set the new EGID into the old spot */ 1945 pcred->cr_groups[0] = new_egid; 1946 1947 return (displaced); 1948} 1949 1950 1951/* 1952 * kauth_cred_getuid 1953 * 1954 * Description: Fetch UID from credential 1955 * 1956 * Parameters: cred Credential to examine 1957 * 1958 * Returns: (uid_t) UID associated with credential 1959 */ 1960uid_t 1961kauth_cred_getuid(kauth_cred_t cred) 1962{ 1963 NULLCRED_CHECK(cred); 1964 return(posix_cred_get(cred)->cr_uid); 1965} 1966 1967 1968/* 1969 * kauth_cred_getruid 1970 * 1971 * Description: Fetch RUID from credential 1972 * 1973 * Parameters: cred Credential to examine 1974 * 1975 * Returns: (uid_t) RUID associated with credential 1976 */ 1977uid_t 1978kauth_cred_getruid(kauth_cred_t cred) 1979{ 1980 NULLCRED_CHECK(cred); 1981 return(posix_cred_get(cred)->cr_ruid); 1982} 1983 1984 1985/* 1986 * kauth_cred_getsvuid 1987 * 1988 * Description: Fetch SVUID from credential 1989 * 1990 * Parameters: cred Credential to examine 1991 * 1992 * Returns: (uid_t) SVUID associated with credential 1993 */ 1994uid_t 1995kauth_cred_getsvuid(kauth_cred_t cred) 1996{ 1997 NULLCRED_CHECK(cred); 1998 return(posix_cred_get(cred)->cr_svuid); 1999} 2000 2001 2002/* 2003 * kauth_cred_getgid 2004 * 2005 * Description: Fetch GID from credential 2006 * 2007 * Parameters: cred Credential to examine 2008 * 2009 * Returns: (gid_t) GID associated with credential 2010 */ 2011gid_t 2012kauth_cred_getgid(kauth_cred_t cred) 2013{ 2014 NULLCRED_CHECK(cred); 2015 return(posix_cred_get(cred)->cr_gid); 2016} 2017 2018 2019/* 2020 * kauth_cred_getrgid 2021 * 2022 * Description: Fetch RGID from credential 2023 * 2024 * Parameters: cred Credential to examine 2025 * 2026 * Returns: (gid_t) RGID associated with credential 2027 */ 2028gid_t 2029kauth_cred_getrgid(kauth_cred_t cred) 2030{ 2031 NULLCRED_CHECK(cred); 2032 return(posix_cred_get(cred)->cr_rgid); 2033} 2034 2035 2036/* 2037 * kauth_cred_getsvgid 2038 * 2039 * Description: Fetch SVGID from credential 2040 * 2041 * Parameters: cred Credential to examine 2042 * 2043 * Returns: (gid_t) SVGID associated with credential 2044 */ 2045gid_t 2046kauth_cred_getsvgid(kauth_cred_t cred) 2047{ 2048 NULLCRED_CHECK(cred); 2049 return(posix_cred_get(cred)->cr_svgid); 2050} 2051 2052 2053static int kauth_cred_cache_lookup(int from, int to, void *src, void *dst); 2054 2055#if CONFIG_EXT_RESOLVER == 0 2056/* 2057 * If there's no resolver, short-circuit the kauth_cred_x2y() lookups. 2058 */ 2059static __inline int 2060kauth_cred_cache_lookup(__unused int from, __unused int to, 2061 __unused void *src, __unused void *dst) 2062{ 2063 return (EWOULDBLOCK); 2064 2065} 2066#endif 2067 2068#if defined(CONFIG_EXT_RESOLVER) && (CONFIG_EXT_RESOLVER) 2069/* 2070 * Structure to hold supplemental groups. Used for impedance matching with 2071 * kauth_cred_cache_lookup below. 2072 */ 2073struct supgroups { 2074 int *count; 2075 gid_t *groups; 2076}; 2077 2078/* 2079 * kauth_cred_uid2groups 2080 * 2081 * Description: Fetch supplemental GROUPS from UID 2082 * 2083 * Parameters: uid UID to examine 2084 * groups pointer to an array of gid_ts 2085 * gcount pointer to the number of groups wanted/returned 2086 * 2087 * Returns: 0 Success 2088 * kauth_cred_cache_lookup:EINVAL 2089 * 2090 * Implicit returns: 2091 * *groups Modified, if successful 2092 * *gcount Modified, if successful 2093 * 2094 */ 2095static int 2096kauth_cred_uid2groups(uid_t *uid, gid_t *groups, int *gcount) 2097{ 2098 int rv; 2099 2100 struct supgroups supgroups; 2101 supgroups.count = gcount; 2102 supgroups.groups = groups; 2103 2104 rv = kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GROUPS, uid, &supgroups); 2105 2106 return (rv); 2107} 2108#endif 2109 2110/* 2111 * kauth_cred_guid2pwnam 2112 * 2113 * Description: Fetch PWNAM from GUID 2114 * 2115 * Parameters: guidp Pointer to GUID to examine 2116 * pwnam Pointer to user@domain buffer 2117 * 2118 * Returns: 0 Success 2119 * kauth_cred_cache_lookup:EINVAL 2120 * 2121 * Implicit returns: 2122 * *pwnam Modified, if successful 2123 * 2124 * Notes: pwnam is assumed to point to a buffer of MAXPATHLEN in size 2125 */ 2126int 2127kauth_cred_guid2pwnam(guid_t *guidp, char *pwnam) 2128{ 2129 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_PWNAM, guidp, pwnam)); 2130} 2131 2132 2133/* 2134 * kauth_cred_guid2grnam 2135 * 2136 * Description: Fetch GRNAM from GUID 2137 * 2138 * Parameters: guidp Pointer to GUID to examine 2139 * grnam Pointer to group@domain buffer 2140 * 2141 * Returns: 0 Success 2142 * kauth_cred_cache_lookup:EINVAL 2143 * 2144 * Implicit returns: 2145 * *grnam Modified, if successful 2146 * 2147 * Notes: grnam is assumed to point to a buffer of MAXPATHLEN in size 2148 */ 2149int 2150kauth_cred_guid2grnam(guid_t *guidp, char *grnam) 2151{ 2152 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GRNAM, guidp, grnam)); 2153} 2154 2155 2156/* 2157 * kauth_cred_pwnam2guid 2158 * 2159 * Description: Fetch PWNAM from GUID 2160 * 2161 * Parameters: pwnam String containing user@domain 2162 * guidp Pointer to buffer for GUID 2163 * 2164 * Returns: 0 Success 2165 * kauth_cred_cache_lookup:EINVAL 2166 * 2167 * Implicit returns: 2168 * *guidp Modified, if successful 2169 * 2170 * Notes: pwnam should not point to a request larger than MAXPATHLEN 2171 * bytes in size, including the NUL termination of the string. 2172 */ 2173int 2174kauth_cred_pwnam2guid(char *pwnam, guid_t *guidp) 2175{ 2176 return(kauth_cred_cache_lookup(KI_VALID_PWNAM, KI_VALID_GUID, pwnam, guidp)); 2177} 2178 2179 2180/* 2181 * kauth_cred_grnam2guid 2182 * 2183 * Description: Fetch GRNAM from GUID 2184 * 2185 * Parameters: grnam String containing group@domain 2186 * guidp Pointer to buffer for GUID 2187 * 2188 * Returns: 0 Success 2189 * kauth_cred_cache_lookup:EINVAL 2190 * 2191 * Implicit returns: 2192 * *guidp Modified, if successful 2193 * 2194 * Notes: grnam should not point to a request larger than MAXPATHLEN 2195 * bytes in size, including the NUL termination of the string. 2196 */ 2197int 2198kauth_cred_grnam2guid(char *grnam, guid_t *guidp) 2199{ 2200 return(kauth_cred_cache_lookup(KI_VALID_GRNAM, KI_VALID_GUID, grnam, guidp)); 2201} 2202 2203 2204/* 2205 * kauth_cred_guid2uid 2206 * 2207 * Description: Fetch UID from GUID 2208 * 2209 * Parameters: guidp Pointer to GUID to examine 2210 * uidp Pointer to buffer for UID 2211 * 2212 * Returns: 0 Success 2213 * kauth_cred_cache_lookup:EINVAL 2214 * 2215 * Implicit returns: 2216 * *uidp Modified, if successful 2217 */ 2218int 2219kauth_cred_guid2uid(guid_t *guidp, uid_t *uidp) 2220{ 2221 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_UID, guidp, uidp)); 2222} 2223 2224 2225/* 2226 * kauth_cred_guid2gid 2227 * 2228 * Description: Fetch GID from GUID 2229 * 2230 * Parameters: guidp Pointer to GUID to examine 2231 * gidp Pointer to buffer for GID 2232 * 2233 * Returns: 0 Success 2234 * kauth_cred_cache_lookup:EINVAL 2235 * 2236 * Implicit returns: 2237 * *gidp Modified, if successful 2238 */ 2239int 2240kauth_cred_guid2gid(guid_t *guidp, gid_t *gidp) 2241{ 2242 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GID, guidp, gidp)); 2243} 2244 2245 2246/* 2247 * kauth_cred_ntsid2uid 2248 * 2249 * Description: Fetch UID from NTSID 2250 * 2251 * Parameters: sidp Pointer to NTSID to examine 2252 * uidp Pointer to buffer for UID 2253 * 2254 * Returns: 0 Success 2255 * kauth_cred_cache_lookup:EINVAL 2256 * 2257 * Implicit returns: 2258 * *uidp Modified, if successful 2259 */ 2260int 2261kauth_cred_ntsid2uid(ntsid_t *sidp, uid_t *uidp) 2262{ 2263 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_UID, sidp, uidp)); 2264} 2265 2266 2267/* 2268 * kauth_cred_ntsid2gid 2269 * 2270 * Description: Fetch GID from NTSID 2271 * 2272 * Parameters: sidp Pointer to NTSID to examine 2273 * gidp Pointer to buffer for GID 2274 * 2275 * Returns: 0 Success 2276 * kauth_cred_cache_lookup:EINVAL 2277 * 2278 * Implicit returns: 2279 * *gidp Modified, if successful 2280 */ 2281int 2282kauth_cred_ntsid2gid(ntsid_t *sidp, gid_t *gidp) 2283{ 2284 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GID, sidp, gidp)); 2285} 2286 2287 2288/* 2289 * kauth_cred_ntsid2guid 2290 * 2291 * Description: Fetch GUID from NTSID 2292 * 2293 * Parameters: sidp Pointer to NTSID to examine 2294 * guidp Pointer to buffer for GUID 2295 * 2296 * Returns: 0 Success 2297 * kauth_cred_cache_lookup:EINVAL 2298 * 2299 * Implicit returns: 2300 * *guidp Modified, if successful 2301 */ 2302int 2303kauth_cred_ntsid2guid(ntsid_t *sidp, guid_t *guidp) 2304{ 2305 return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GUID, sidp, guidp)); 2306} 2307 2308 2309/* 2310 * kauth_cred_uid2guid 2311 * 2312 * Description: Fetch GUID from UID 2313 * 2314 * Parameters: uid UID to examine 2315 * guidp Pointer to buffer for GUID 2316 * 2317 * Returns: 0 Success 2318 * kauth_cred_cache_lookup:EINVAL 2319 * 2320 * Implicit returns: 2321 * *guidp Modified, if successful 2322 */ 2323int 2324kauth_cred_uid2guid(uid_t uid, guid_t *guidp) 2325{ 2326 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GUID, &uid, guidp)); 2327} 2328 2329 2330/* 2331 * kauth_cred_getguid 2332 * 2333 * Description: Fetch GUID from credential 2334 * 2335 * Parameters: cred Credential to examine 2336 * guidp Pointer to buffer for GUID 2337 * 2338 * Returns: 0 Success 2339 * kauth_cred_cache_lookup:EINVAL 2340 * 2341 * Implicit returns: 2342 * *guidp Modified, if successful 2343 */ 2344int 2345kauth_cred_getguid(kauth_cred_t cred, guid_t *guidp) 2346{ 2347 NULLCRED_CHECK(cred); 2348 return(kauth_cred_uid2guid(kauth_cred_getuid(cred), guidp)); 2349} 2350 2351 2352/* 2353 * kauth_cred_getguid 2354 * 2355 * Description: Fetch GUID from GID 2356 * 2357 * Parameters: gid GID to examine 2358 * guidp Pointer to buffer for GUID 2359 * 2360 * Returns: 0 Success 2361 * kauth_cred_cache_lookup:EINVAL 2362 * 2363 * Implicit returns: 2364 * *guidp Modified, if successful 2365 */ 2366int 2367kauth_cred_gid2guid(gid_t gid, guid_t *guidp) 2368{ 2369 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_GUID, &gid, guidp)); 2370} 2371 2372 2373/* 2374 * kauth_cred_uid2ntsid 2375 * 2376 * Description: Fetch NTSID from UID 2377 * 2378 * Parameters: uid UID to examine 2379 * sidp Pointer to buffer for NTSID 2380 * 2381 * Returns: 0 Success 2382 * kauth_cred_cache_lookup:EINVAL 2383 * 2384 * Implicit returns: 2385 * *sidp Modified, if successful 2386 */ 2387int 2388kauth_cred_uid2ntsid(uid_t uid, ntsid_t *sidp) 2389{ 2390 return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_NTSID, &uid, sidp)); 2391} 2392 2393 2394/* 2395 * kauth_cred_getntsid 2396 * 2397 * Description: Fetch NTSID from credential 2398 * 2399 * Parameters: cred Credential to examine 2400 * sidp Pointer to buffer for NTSID 2401 * 2402 * Returns: 0 Success 2403 * kauth_cred_cache_lookup:EINVAL 2404 * 2405 * Implicit returns: 2406 * *sidp Modified, if successful 2407 */ 2408int 2409kauth_cred_getntsid(kauth_cred_t cred, ntsid_t *sidp) 2410{ 2411 NULLCRED_CHECK(cred); 2412 return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred), sidp)); 2413} 2414 2415 2416/* 2417 * kauth_cred_gid2ntsid 2418 * 2419 * Description: Fetch NTSID from GID 2420 * 2421 * Parameters: gid GID to examine 2422 * sidp Pointer to buffer for NTSID 2423 * 2424 * Returns: 0 Success 2425 * kauth_cred_cache_lookup:EINVAL 2426 * 2427 * Implicit returns: 2428 * *sidp Modified, if successful 2429 */ 2430int 2431kauth_cred_gid2ntsid(gid_t gid, ntsid_t *sidp) 2432{ 2433 return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_NTSID, &gid, sidp)); 2434} 2435 2436 2437/* 2438 * kauth_cred_guid2ntsid 2439 * 2440 * Description: Fetch NTSID from GUID 2441 * 2442 * Parameters: guidp Pointer to GUID to examine 2443 * sidp Pointer to buffer for NTSID 2444 * 2445 * Returns: 0 Success 2446 * kauth_cred_cache_lookup:EINVAL 2447 * 2448 * Implicit returns: 2449 * *sidp Modified, if successful 2450 */ 2451int 2452kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp) 2453{ 2454 return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_NTSID, guidp, sidp)); 2455} 2456 2457 2458/* 2459 * kauth_cred_cache_lookup 2460 * 2461 * Description: Lookup a translation in the cache; if one is not found, and 2462 * the attempt was not fatal, submit the request to the resolver 2463 * instead, and wait for it to complete or be aborted. 2464 * 2465 * Parameters: from Identity information we have 2466 * to Identity information we want 2467 * src Pointer to buffer containing 2468 * the source identity 2469 * dst Pointer to buffer to receive 2470 * the target identity 2471 * 2472 * Returns: 0 Success 2473 * EINVAL Unknown source identity type 2474 */ 2475#if CONFIG_EXT_RESOLVER 2476static int 2477kauth_cred_cache_lookup(int from, int to, void *src, void *dst) 2478{ 2479 struct kauth_identity ki; 2480 struct kauth_identity_extlookup el; 2481 int error; 2482 uint64_t extend_data = 0ULL; 2483 int (* expired)(struct kauth_identity *kip); 2484 char *namebuf = NULL; 2485 2486 KAUTH_DEBUG("CACHE - translate %d to %d", from, to); 2487 2488 /* 2489 * Look for an existing cache entry for this association. 2490 * If the entry has not expired, return the cached information. 2491 * We do not cache user@domain translations here; they use too 2492 * much memory to hold onto forever, and can not be updated 2493 * atomically. 2494 */ 2495 if (to == KI_VALID_PWNAM || to == KI_VALID_GRNAM) { 2496 namebuf = dst; 2497 } 2498 ki.ki_valid = 0; 2499 switch(from) { 2500 case KI_VALID_UID: 2501 error = kauth_identity_find_uid(*(uid_t *)src, &ki, namebuf); 2502 break; 2503 case KI_VALID_GID: 2504 error = kauth_identity_find_gid(*(gid_t *)src, &ki, namebuf); 2505 break; 2506 case KI_VALID_GUID: 2507 error = kauth_identity_find_guid((guid_t *)src, &ki, namebuf); 2508 break; 2509 case KI_VALID_NTSID: 2510 error = kauth_identity_find_ntsid((ntsid_t *)src, &ki, namebuf); 2511 break; 2512 case KI_VALID_PWNAM: 2513 case KI_VALID_GRNAM: 2514 /* Names are unique in their 'from' space */ 2515 error = kauth_identity_find_nam((char *)src, from, &ki); 2516 break; 2517 default: 2518 return(EINVAL); 2519 } 2520 /* lookup failure or error */ 2521 if (error != 0) { 2522 /* any other error is fatal */ 2523 if (error != ENOENT) { 2524 /* XXX bogus check - this is not possible */ 2525 KAUTH_DEBUG("CACHE - cache search error %d", error); 2526 return(error); 2527 } 2528 } else { 2529 /* found a valid cached entry, check expiry */ 2530 switch(to) { 2531 case KI_VALID_GUID: 2532 expired = kauth_identity_guid_expired; 2533 break; 2534 case KI_VALID_NTSID: 2535 expired = kauth_identity_ntsid_expired; 2536 break; 2537 case KI_VALID_GROUPS: 2538 expired = kauth_identity_groups_expired; 2539 break; 2540 default: 2541 switch(from) { 2542 case KI_VALID_GUID: 2543 expired = kauth_identity_guid_expired; 2544 break; 2545 case KI_VALID_NTSID: 2546 expired = kauth_identity_ntsid_expired; 2547 break; 2548 default: 2549 expired = NULL; 2550 } 2551 } 2552 2553 /* 2554 * If no expiry function, or not expired, we have found 2555 * a hit. 2556 */ 2557 if (expired) { 2558 if (!expired(&ki)) { 2559 KAUTH_DEBUG("CACHE - entry valid, unexpired"); 2560 expired = NULL; /* must clear it is used as a flag */ 2561 } else { 2562 /* 2563 * We leave ki_valid set here; it contains a 2564 * translation but the TTL has expired. If we can't 2565 * get a result from the resolver, we will use it as 2566 * a better-than nothing alternative. 2567 */ 2568 2569 KAUTH_DEBUG("CACHE - expired entry found"); 2570 } 2571 } else { 2572 KAUTH_DEBUG("CACHE - no expiry function"); 2573 } 2574 2575 if (!expired) { 2576 /* do we have a translation? */ 2577 if (ki.ki_valid & to) { 2578 KAUTH_DEBUG("CACHE - found matching entry with valid 0x%08x", ki.ki_valid); 2579 DTRACE_PROC4(kauth__identity__cache__hit, int, from, int, to, void *, src, void *, dst); 2580 goto found; 2581 } else { 2582 /* 2583 * GUIDs and NTSIDs map to either a UID or a GID, but not both. 2584 * If we went looking for a translation from GUID or NTSID and 2585 * found a translation that wasn't for our desired type, then 2586 * don't bother calling the resolver. We know that this 2587 * GUID/NTSID can't translate to our desired type. 2588 */ 2589 switch(from) { 2590 case KI_VALID_GUID: 2591 case KI_VALID_NTSID: 2592 switch(to) { 2593 case KI_VALID_GID: 2594 if ((ki.ki_valid & KI_VALID_UID)) { 2595 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_GID); 2596 return (ENOENT); 2597 } 2598 break; 2599 case KI_VALID_UID: 2600 if ((ki.ki_valid & KI_VALID_GID)) { 2601 KAUTH_DEBUG("CACHE - unexpected entry 0x%08x & %x", ki.ki_valid, KI_VALID_UID); 2602 return (ENOENT); 2603 } 2604 break; 2605 } 2606 break; 2607 } 2608 } 2609 } 2610 } 2611 2612 /* 2613 * We failed to find a cache entry; call the resolver. 2614 * 2615 * Note: We ask for as much non-extended data as we can get, 2616 * and only provide (or ask for) extended information if 2617 * we have a 'from' (or 'to') which requires it. This 2618 * way we don't pay for the extra transfer overhead for 2619 * data we don't need. 2620 */ 2621 bzero(&el, sizeof(el)); 2622 el.el_info_pid = current_proc()->p_pid; 2623 switch(from) { 2624 case KI_VALID_UID: 2625 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID; 2626 el.el_uid = *(uid_t *)src; 2627 break; 2628 case KI_VALID_GID: 2629 el.el_flags = KAUTH_EXTLOOKUP_VALID_GID; 2630 el.el_gid = *(gid_t *)src; 2631 break; 2632 case KI_VALID_GUID: 2633 el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID; 2634 el.el_uguid = *(guid_t *)src; 2635 el.el_gguid = *(guid_t *)src; 2636 break; 2637 case KI_VALID_NTSID: 2638 el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID; 2639 el.el_usid = *(ntsid_t *)src; 2640 el.el_gsid = *(ntsid_t *)src; 2641 break; 2642 case KI_VALID_PWNAM: 2643 /* extra overhead */ 2644 el.el_flags = KAUTH_EXTLOOKUP_VALID_PWNAM; 2645 extend_data = CAST_USER_ADDR_T(src); 2646 break; 2647 case KI_VALID_GRNAM: 2648 /* extra overhead */ 2649 el.el_flags = KAUTH_EXTLOOKUP_VALID_GRNAM; 2650 extend_data = CAST_USER_ADDR_T(src); 2651 break; 2652 default: 2653 return(EINVAL); 2654 } 2655 /* 2656 * Here we ask for everything all at once, to avoid having to work 2657 * out what we really want now, or might want soon. 2658 * 2659 * Asking for SID translations when we don't know we need them right 2660 * now is going to cause excess work to be done if we're connected 2661 * to a network that thinks it can translate them. This list needs 2662 * to get smaller/smarter. 2663 */ 2664 el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID | 2665 KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID | 2666 KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID; 2667 if (to == KI_VALID_PWNAM) { 2668 /* extra overhead */ 2669 el.el_flags |= KAUTH_EXTLOOKUP_WANT_PWNAM; 2670 extend_data = CAST_USER_ADDR_T(dst); 2671 } 2672 if (to == KI_VALID_GRNAM) { 2673 /* extra overhead */ 2674 el.el_flags |= KAUTH_EXTLOOKUP_WANT_GRNAM; 2675 extend_data = CAST_USER_ADDR_T(dst); 2676 } 2677 if (to == KI_VALID_GROUPS) { 2678 /* Expensive and only useful for an NFS client not using kerberos */ 2679 el.el_flags |= KAUTH_EXTLOOKUP_WANT_SUPGRPS; 2680 if (ki.ki_valid & KI_VALID_GROUPS) { 2681 /* 2682 * Copy the current supplemental groups for the resolver. 2683 * The resolver should check these groups first and if 2684 * the user (uid) is still a member it should endeavor to 2685 * keep them in the list. Otherwise NFS clients could get 2686 * changing access to server file system objects on each 2687 * expiration. 2688 */ 2689 el.el_sup_grp_cnt = ki.ki_supgrpcnt; 2690 2691 memcpy(el.el_sup_groups, ki.ki_supgrps, sizeof (el.el_sup_groups[0]) * ki.ki_supgrpcnt); 2692 /* Let the resolver know these were the previous valid groups */ 2693 el.el_flags |= KAUTH_EXTLOOKUP_VALID_SUPGRPS; 2694 KAUTH_DEBUG("GROUPS: Sending previously valid GROUPS"); 2695 } else 2696 KAUTH_DEBUG("GROUPS: no valid groups to send"); 2697 } 2698 2699 /* Call resolver */ 2700 KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags); 2701 2702 DTRACE_PROC3(kauth__id__resolver__submitted, int, from, int, to, uintptr_t, src); 2703 2704 error = kauth_resolver_submit(&el, extend_data); 2705 2706 DTRACE_PROC2(kauth__id__resolver__returned, int, error, struct kauth_identity_extlookup *, &el) 2707 2708 KAUTH_DEBUG("CACHE - resolver returned %d", error); 2709 2710 /* was the external lookup successful? */ 2711 if (error == 0) { 2712 /* 2713 * Save the results from the lookup - we may have other 2714 * information, even if we didn't get a guid or the 2715 * extended data. 2716 * 2717 * If we came from a name, we know the extend_data is valid. 2718 */ 2719 if (from == KI_VALID_PWNAM) 2720 el.el_flags |= KAUTH_EXTLOOKUP_VALID_PWNAM; 2721 else if (from == KI_VALID_GRNAM) 2722 el.el_flags |= KAUTH_EXTLOOKUP_VALID_GRNAM; 2723 2724 kauth_identity_updatecache(&el, &ki, extend_data); 2725 2726 /* 2727 * Check to see if we have a valid cache entry 2728 * originating from the result. 2729 */ 2730 if (!(ki.ki_valid & to)) { 2731 error = ENOENT; 2732 } 2733 } 2734 if (error) 2735 return(error); 2736found: 2737 /* 2738 * Copy from the appropriate struct kauth_identity cache entry 2739 * structure into the destination buffer area. 2740 */ 2741 switch(to) { 2742 case KI_VALID_UID: 2743 *(uid_t *)dst = ki.ki_uid; 2744 break; 2745 case KI_VALID_GID: 2746 *(gid_t *)dst = ki.ki_gid; 2747 break; 2748 case KI_VALID_GUID: 2749 *(guid_t *)dst = ki.ki_guid; 2750 break; 2751 case KI_VALID_NTSID: 2752 *(ntsid_t *)dst = ki.ki_ntsid; 2753 break; 2754 case KI_VALID_GROUPS: { 2755 struct supgroups *gp = (struct supgroups *)dst; 2756 u_int32_t limit = ki.ki_supgrpcnt; 2757 2758 if (gp->count) { 2759 limit = MIN(ki.ki_supgrpcnt, *gp->count); 2760 *gp->count = limit; 2761 } 2762 2763 memcpy(gp->groups, ki.ki_supgrps, sizeof(gid_t) * limit); 2764 } 2765 break; 2766 case KI_VALID_PWNAM: 2767 case KI_VALID_GRNAM: 2768 /* handled in kauth_resolver_complete() */ 2769 break; 2770 default: 2771 return(EINVAL); 2772 } 2773 KAUTH_DEBUG("CACHE - returned successfully"); 2774 return(0); 2775} 2776 2777 2778/* 2779 * Group membership cache. 2780 * 2781 * XXX the linked-list implementation here needs to be optimized. 2782 */ 2783 2784/* 2785 * kauth_groups_init 2786 * 2787 * Description: Initialize the groups cache 2788 * 2789 * Parameters: (void) 2790 * 2791 * Returns: (void) 2792 * 2793 * Notes: Initialize the groups cache for use; the group cache is used 2794 * to avoid unnecessary calls out to user space. 2795 * 2796 * This function is called from kauth_init() in the file 2797 * kern_authorization.c. 2798 */ 2799void 2800kauth_groups_init(void) 2801{ 2802 TAILQ_INIT(&kauth_groups); 2803 kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/); 2804} 2805 2806 2807/* 2808 * kauth_groups_expired 2809 * 2810 * Description: Handle lazy expiration of group membership cache entries 2811 * 2812 * Parameters: gm group membership entry to 2813 * check for expiration 2814 * 2815 * Returns: 1 Expired 2816 * 0 Not expired 2817 */ 2818static int 2819kauth_groups_expired(struct kauth_group_membership *gm) 2820{ 2821 struct timeval tv; 2822 2823 /* 2824 * Expiration time of 0 means this entry is persistent. 2825 */ 2826 if (gm->gm_expiry == 0) 2827 return (0); 2828 2829 microuptime(&tv); 2830 2831 return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0); 2832} 2833 2834 2835/* 2836 * kauth_groups_lru 2837 * 2838 * Description: Promote the entry to the head of the LRU, assumes the cache 2839 * is locked. 2840 * 2841 * Parameters: kip group membership entry to move 2842 * to the head of the LRU list, 2843 * if it's not already there 2844 * 2845 * Returns: (void) 2846 * 2847 * Notes: This is called even if the entry has expired; typically an 2848 * expired entry that's been looked up is about to be revalidated, 2849 * and having it closer to the head of the LRU means finding it 2850 * quickly again when the revalidation comes through. 2851 */ 2852static void 2853kauth_groups_lru(struct kauth_group_membership *gm) 2854{ 2855 if (gm != TAILQ_FIRST(&kauth_groups)) { 2856 TAILQ_REMOVE(&kauth_groups, gm, gm_link); 2857 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link); 2858 } 2859} 2860 2861 2862/* 2863 * kauth_groups_updatecache 2864 * 2865 * Description: Given a lookup result, add any group cache associations that 2866 * we don't currently have. 2867 * 2868 * Parameters: elp External lookup result from 2869 * user space daemon to kernel 2870 * rkip pointer to returned kauth 2871 * identity, or NULL 2872 * 2873 * Returns: (void) 2874 */ 2875static void 2876kauth_groups_updatecache(struct kauth_identity_extlookup *el) 2877{ 2878 struct kauth_group_membership *gm; 2879 struct timeval tv; 2880 2881 /* need a valid response if we are to cache anything */ 2882 if ((el->el_flags & 2883 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) != 2884 (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) 2885 return; 2886 2887 microuptime(&tv); 2888 2889 /* 2890 * Search for an existing record for this association before inserting 2891 * a new one; if we find one, update it instead of creating a new one 2892 */ 2893 KAUTH_GROUPS_LOCK(); 2894 TAILQ_FOREACH(gm, &kauth_groups, gm_link) { 2895 if ((el->el_uid == gm->gm_uid) && 2896 (el->el_gid == gm->gm_gid)) { 2897 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) { 2898 gm->gm_flags |= KAUTH_GROUP_ISMEMBER; 2899 } else { 2900 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER; 2901 } 2902 gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0; 2903 kauth_groups_lru(gm); 2904 break; 2905 } 2906 } 2907 KAUTH_GROUPS_UNLOCK(); 2908 2909 /* if we found an entry to update, stop here */ 2910 if (gm != NULL) 2911 return; 2912 2913 /* allocate a new record */ 2914 MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK); 2915 if (gm != NULL) { 2916 gm->gm_uid = el->el_uid; 2917 gm->gm_gid = el->el_gid; 2918 if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) { 2919 gm->gm_flags |= KAUTH_GROUP_ISMEMBER; 2920 } else { 2921 gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER; 2922 } 2923 gm->gm_expiry = (el->el_member_valid) ? el->el_member_valid + tv.tv_sec : 0; 2924 } 2925 2926 /* 2927 * Insert the new entry. Note that it's possible to race ourselves 2928 * here and end up with duplicate entries in the list. Wasteful, but 2929 * harmless since the first into the list will never be looked up, 2930 * and thus will eventually just fall off the end. 2931 */ 2932 KAUTH_GROUPS_LOCK(); 2933 TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link); 2934 if (++kauth_groups_count > kauth_groups_cachemax) { 2935 gm = TAILQ_LAST(&kauth_groups, kauth_groups_head); 2936 TAILQ_REMOVE(&kauth_groups, gm, gm_link); 2937 kauth_groups_count--; 2938 } else { 2939 gm = NULL; 2940 } 2941 KAUTH_GROUPS_UNLOCK(); 2942 2943 /* free expired cache entry */ 2944 if (gm != NULL) 2945 FREE(gm, M_KAUTH); 2946} 2947 2948/* 2949 * Trim older entries from the group membership cache. 2950 * 2951 * Must be called with the group cache lock held. 2952 */ 2953static void 2954kauth_groups_trimcache(int new_size) { 2955 struct kauth_group_membership *gm; 2956 2957 lck_mtx_assert(kauth_groups_mtx, LCK_MTX_ASSERT_OWNED); 2958 2959 while (kauth_groups_count > new_size) { 2960 gm = TAILQ_LAST(&kauth_groups, kauth_groups_head); 2961 TAILQ_REMOVE(&kauth_groups, gm, gm_link); 2962 kauth_groups_count--; 2963 FREE(gm, M_KAUTH); 2964 } 2965} 2966#endif /* CONFIG_EXT_RESOLVER */ 2967 2968/* 2969 * Group membership KPI 2970 */ 2971 2972/* 2973 * kauth_cred_ismember_gid 2974 * 2975 * Description: Given a credential and a GID, determine if the GID is a member 2976 * of one of the supplementary groups associated with the given 2977 * credential 2978 * 2979 * Parameters: cred Credential to check in 2980 * gid GID to check for membership 2981 * resultp Pointer to int to contain the 2982 * result of the call 2983 * 2984 * Returns: 0 Success 2985 * ENOENT Could not perform lookup 2986 * kauth_resolver_submit:EWOULDBLOCK 2987 * kauth_resolver_submit:EINTR 2988 * kauth_resolver_submit:ENOMEM 2989 * kauth_resolver_submit:ENOENT User space daemon did not vend 2990 * this credential. 2991 * kauth_resolver_submit:??? Unlikely error from user space 2992 * 2993 * Implicit returns: 2994 * *resultp (modified) 1 Is member 2995 * 0 Is not member 2996 * 2997 * Notes: This function guarantees not to modify resultp when returning 2998 * an error. 2999 * 3000 * This function effectively checks the EGID as well, since the 3001 * EGID is cr_groups[0] as an implementation detail. 3002 */ 3003int 3004kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp) 3005{ 3006 posix_cred_t pcred = posix_cred_get(cred); 3007 int i; 3008 3009 /* 3010 * Check the per-credential list of override groups. 3011 * 3012 * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since 3013 * the cache should be used for that case. 3014 */ 3015 for (i = 0; i < pcred->cr_ngroups; i++) { 3016 if (gid == pcred->cr_groups[i]) { 3017 *resultp = 1; 3018 return(0); 3019 } 3020 } 3021 3022 /* 3023 * If we don't have a UID for group membership checks, the in-cred list 3024 * was authoritative and we can stop here. 3025 */ 3026 if (pcred->cr_gmuid == KAUTH_UID_NONE) { 3027 *resultp = 0; 3028 return(0); 3029 } 3030 3031#if CONFIG_EXT_RESOLVER 3032 struct kauth_group_membership *gm; 3033 struct kauth_identity_extlookup el; 3034 int error; 3035 3036 /* 3037 * If the resolver hasn't checked in yet, we are early in the boot 3038 * phase and the local group list is complete and authoritative. 3039 */ 3040 if (!kauth_resolver_registered) { 3041 *resultp = 0; 3042 return(0); 3043 } 3044 3045 /* TODO: */ 3046 /* XXX check supplementary groups */ 3047 /* XXX check whiteout groups */ 3048 /* XXX nesting of supplementary/whiteout groups? */ 3049 3050 /* 3051 * Check the group cache. 3052 */ 3053 KAUTH_GROUPS_LOCK(); 3054 TAILQ_FOREACH(gm, &kauth_groups, gm_link) { 3055 if ((gm->gm_uid == pcred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) { 3056 kauth_groups_lru(gm); 3057 break; 3058 } 3059 } 3060 3061 /* did we find a membership entry? */ 3062 if (gm != NULL) 3063 *resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0; 3064 KAUTH_GROUPS_UNLOCK(); 3065 3066 /* if we did, we can return now */ 3067 if (gm != NULL) { 3068 DTRACE_PROC2(kauth__group__cache__hit, int, pcred->cr_gmuid, int, gid); 3069 return(0); 3070 } 3071 3072 /* nothing in the cache, need to go to userland */ 3073 bzero(&el, sizeof(el)); 3074 el.el_info_pid = current_proc()->p_pid; 3075 el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP; 3076 el.el_uid = pcred->cr_gmuid; 3077 el.el_gid = gid; 3078 el.el_member_valid = 0; /* XXX set by resolver? */ 3079 3080 DTRACE_PROC2(kauth__group__resolver__submitted, int, el.el_uid, int, el.el_gid); 3081 3082 error = kauth_resolver_submit(&el, 0ULL); 3083 3084 DTRACE_PROC2(kauth__group__resolver__returned, int, error, int, el.el_flags); 3085 3086 if (error != 0) 3087 return(error); 3088 /* save the results from the lookup */ 3089 kauth_groups_updatecache(&el); 3090 3091 /* if we successfully ascertained membership, report */ 3092 if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) { 3093 *resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0; 3094 return(0); 3095 } 3096 3097 return(ENOENT); 3098#else 3099 *resultp = 0; 3100 return(0); 3101#endif 3102} 3103 3104/* 3105 * kauth_cred_ismember_guid 3106 * 3107 * Description: Determine whether the supplied credential is a member of the 3108 * group nominated by GUID. 3109 * 3110 * Parameters: cred Credential to check in 3111 * guidp Pointer to GUID whose group 3112 * we are testing for membership 3113 * resultp Pointer to int to contain the 3114 * result of the call 3115 * 3116 * Returns: 0 Success 3117 * kauth_cred_guid2gid:EINVAL 3118 * kauth_cred_ismember_gid:ENOENT 3119 * kauth_resolver_submit:ENOENT User space daemon did not vend 3120 * this credential. 3121 * kauth_cred_ismember_gid:EWOULDBLOCK 3122 * kauth_cred_ismember_gid:EINTR 3123 * kauth_cred_ismember_gid:ENOMEM 3124 * kauth_cred_ismember_gid:??? Unlikely error from user space 3125 * 3126 * Implicit returns: 3127 * *resultp (modified) 1 Is member 3128 * 0 Is not member 3129 */ 3130int 3131kauth_cred_ismember_guid(__unused kauth_cred_t cred, guid_t *guidp, int *resultp) 3132{ 3133 int error = 0; 3134 3135 switch (kauth_wellknown_guid(guidp)) { 3136 case KAUTH_WKG_NOBODY: 3137 *resultp = 0; 3138 break; 3139 case KAUTH_WKG_EVERYBODY: 3140 *resultp = 1; 3141 break; 3142 default: 3143#if CONFIG_EXT_RESOLVER 3144 { 3145 struct kauth_identity ki; 3146 gid_t gid; 3147#if 6603280 3148 /* 3149 * Grovel the identity cache looking for this GUID. 3150 * If we find it, and it is for a user record, return 3151 * false because it's not a group. 3152 * 3153 * This is necessary because we don't have -ve caching 3154 * of group memberships, and we really want to avoid 3155 * calling out to the resolver if at all possible. 3156 * 3157 * Because we're called by the ACL evaluator, and the 3158 * ACL evaluator is likely to encounter ACEs for users, 3159 * this is expected to be a common case. 3160 */ 3161 ki.ki_valid = 0; 3162 if ((error = kauth_identity_find_guid(guidp, &ki, NULL)) == 0 && 3163 !kauth_identity_guid_expired(&ki)) { 3164 if (ki.ki_valid & KI_VALID_GID) { 3165 /* It's a group after all... */ 3166 gid = ki.ki_gid; 3167 goto do_check; 3168 } 3169 if (ki.ki_valid & KI_VALID_UID) { 3170 *resultp = 0; 3171 return (0); 3172 } 3173 } 3174#endif /* 6603280 */ 3175 /* 3176 * Attempt to translate the GUID to a GID. Even if 3177 * this fails, we will have primed the cache if it is 3178 * a user record and we'll see it above the next time 3179 * we're asked. 3180 */ 3181 if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) { 3182 /* 3183 * If we have no guid -> gid translation, it's not a group and 3184 * thus the cred can't be a member. 3185 */ 3186 if (error == ENOENT) { 3187 *resultp = 0; 3188 error = 0; 3189 } 3190 } else { 3191 do_check: 3192 error = kauth_cred_ismember_gid(cred, gid, resultp); 3193 } 3194 } 3195#else /* CONFIG_EXT_RESOLVER */ 3196 error = ENOENT; 3197#endif /* CONFIG_EXT_RESOLVER */ 3198 break; 3199 } 3200 return(error); 3201} 3202 3203/* 3204 * kauth_cred_gid_subset 3205 * 3206 * Description: Given two credentials, determine if all GIDs associated with 3207 * the first are also associated with the second 3208 * 3209 * Parameters: cred1 Credential to check for 3210 * cred2 Credential to check in 3211 * resultp Pointer to int to contain the 3212 * result of the call 3213 * 3214 * Returns: 0 Success 3215 * non-zero See kauth_cred_ismember_gid for 3216 * error codes 3217 * 3218 * Implicit returns: 3219 * *resultp (modified) 1 Is subset 3220 * 0 Is not subset 3221 * 3222 * Notes: This function guarantees not to modify resultp when returning 3223 * an error. 3224 */ 3225int 3226kauth_cred_gid_subset(kauth_cred_t cred1, kauth_cred_t cred2, int *resultp) 3227{ 3228 int i, err, res = 1; 3229 gid_t gid; 3230 posix_cred_t pcred1 = posix_cred_get(cred1); 3231 posix_cred_t pcred2 = posix_cred_get(cred2); 3232 3233 /* First, check the local list of groups */ 3234 for (i = 0; i < pcred1->cr_ngroups; i++) { 3235 gid = pcred1->cr_groups[i]; 3236 if ((err = kauth_cred_ismember_gid(cred2, gid, &res)) != 0) { 3237 return err; 3238 } 3239 3240 if (!res && gid != pcred2->cr_rgid && gid != pcred2->cr_svgid) { 3241 *resultp = 0; 3242 return 0; 3243 } 3244 } 3245 3246 /* Check real gid */ 3247 if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_rgid, &res)) != 0) { 3248 return err; 3249 } 3250 3251 if (!res && pcred1->cr_rgid != pcred2->cr_rgid && 3252 pcred1->cr_rgid != pcred2->cr_svgid) { 3253 *resultp = 0; 3254 return 0; 3255 } 3256 3257 /* Finally, check saved gid */ 3258 if ((err = kauth_cred_ismember_gid(cred2, pcred1->cr_svgid, &res)) != 0){ 3259 return err; 3260 } 3261 3262 if (!res && pcred1->cr_svgid != pcred2->cr_rgid && 3263 pcred1->cr_svgid != pcred2->cr_svgid) { 3264 *resultp = 0; 3265 return 0; 3266 } 3267 3268 *resultp = 1; 3269 return 0; 3270} 3271 3272 3273/* 3274 * kauth_cred_issuser 3275 * 3276 * Description: Fast replacement for issuser() 3277 * 3278 * Parameters: cred Credential to check for super 3279 * user privileges 3280 * 3281 * Returns: 0 Not super user 3282 * !0 Is super user 3283 * 3284 * Notes: This function uses a magic number which is not a manifest 3285 * constant; this is bad practice. 3286 */ 3287int 3288kauth_cred_issuser(kauth_cred_t cred) 3289{ 3290 return(kauth_cred_getuid(cred) == 0); 3291} 3292 3293 3294/* 3295 * Credential KPI 3296 */ 3297 3298/* lock protecting credential hash table */ 3299static lck_mtx_t *kauth_cred_hash_mtx; 3300#define KAUTH_CRED_HASH_LOCK() lck_mtx_lock(kauth_cred_hash_mtx); 3301#define KAUTH_CRED_HASH_UNLOCK() lck_mtx_unlock(kauth_cred_hash_mtx); 3302#if KAUTH_CRED_HASH_DEBUG 3303#define KAUTH_CRED_HASH_LOCK_ASSERT() lck_mtx_assert(kauth_cred_hash_mtx, LCK_MTX_ASSERT_OWNED) 3304#else /* !KAUTH_CRED_HASH_DEBUG */ 3305#define KAUTH_CRED_HASH_LOCK_ASSERT() 3306#endif /* !KAUTH_CRED_HASH_DEBUG */ 3307 3308 3309/* 3310 * kauth_cred_init 3311 * 3312 * Description: Initialize the credential hash cache 3313 * 3314 * Parameters: (void) 3315 * 3316 * Returns: (void) 3317 * 3318 * Notes: Intialize the credential hash cache for use; the credential 3319 * hash cache is used convert duplicate credentials into a 3320 * single reference counted credential in order to save wired 3321 * kernel memory. In practice, this generally means a desktop 3322 * system runs with a few tens of credentials, instead of one 3323 * per process, one per thread, one per vnode cache entry, and 3324 * so on. This generally results in savings of 200K or more 3325 * (potentially much more on server systems). 3326 * 3327 * The hash cache internally has a reference on the credential 3328 * for itself as a means of avoiding a reclaim race for a 3329 * credential in the process of having it's last non-hash 3330 * reference released. This would otherwise result in the 3331 * possibility of a freed credential that was still in uses due 3332 * a race. This use is protected by the KAUTH_CRED_HASH_LOCK. 3333 * 3334 * On final release, the hash reference is droped, and the 3335 * credential is freed back to the system. 3336 * 3337 * This function is called from kauth_init() in the file 3338 * kern_authorization.c. 3339 */ 3340void 3341kauth_cred_init(void) 3342{ 3343 int i; 3344 3345 kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/); 3346 kauth_cred_table_size = kauth_cred_primes[kauth_cred_primes_index]; 3347 3348 /*allocate credential hash table */ 3349 MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *, 3350 (sizeof(struct kauth_cred_entry_head) * kauth_cred_table_size), 3351 M_KAUTH, M_WAITOK | M_ZERO); 3352 if (kauth_cred_table_anchor == NULL) 3353 panic("startup: kauth_cred_init"); 3354 for (i = 0; i < kauth_cred_table_size; i++) { 3355 TAILQ_INIT(&kauth_cred_table_anchor[i]); 3356 } 3357} 3358 3359 3360/* 3361 * kauth_getuid 3362 * 3363 * Description: Get the current thread's effective UID. 3364 * 3365 * Parameters: (void) 3366 * 3367 * Returns: (uid_t) The effective UID of the 3368 * current thread 3369 */ 3370uid_t 3371kauth_getuid(void) 3372{ 3373 return(kauth_cred_getuid(kauth_cred_get())); 3374} 3375 3376 3377/* 3378 * kauth_getruid 3379 * 3380 * Description: Get the current thread's real UID. 3381 * 3382 * Parameters: (void) 3383 * 3384 * Returns: (uid_t) The real UID of the current 3385 * thread 3386 */ 3387uid_t 3388kauth_getruid(void) 3389{ 3390 return(kauth_cred_getruid(kauth_cred_get())); 3391} 3392 3393 3394/* 3395 * kauth_getgid 3396 * 3397 * Description: Get the current thread's effective GID. 3398 * 3399 * Parameters: (void) 3400 * 3401 * Returns: (gid_t) The effective GID of the 3402 * current thread 3403 */ 3404gid_t 3405kauth_getgid(void) 3406{ 3407 return(kauth_cred_getgid(kauth_cred_get())); 3408} 3409 3410 3411/* 3412 * kauth_getgid 3413 * 3414 * Description: Get the current thread's real GID. 3415 * 3416 * Parameters: (void) 3417 * 3418 * Returns: (gid_t) The real GID of the current 3419 * thread 3420 */ 3421gid_t 3422kauth_getrgid(void) 3423{ 3424 return(kauth_cred_getrgid(kauth_cred_get())); 3425} 3426 3427 3428/* 3429 * kauth_cred_get 3430 * 3431 * Description: Returns a pointer to the current thread's credential 3432 * 3433 * Parameters: (void) 3434 * 3435 * Returns: (kauth_cred_t) Pointer to the current thread's 3436 * credential 3437 * 3438 * Notes: This function does not take a reference; because of this, the 3439 * caller MUST NOT do anything that would let the thread's 3440 * credential change while using the returned value, without 3441 * first explicitly taking their own reference. 3442 * 3443 * If a caller intends to take a reference on the resulting 3444 * credential pointer from calling this function, it is strongly 3445 * recommended that the caller use kauth_cred_get_with_ref() 3446 * instead, to protect against any future changes to the cred 3447 * locking protocols; such changes could otherwise potentially 3448 * introduce race windows in the callers code. 3449 */ 3450kauth_cred_t 3451kauth_cred_get(void) 3452{ 3453 struct proc *p; 3454 struct uthread *uthread; 3455 3456 uthread = get_bsdthread_info(current_thread()); 3457 /* sanity */ 3458 if (uthread == NULL) 3459 panic("thread wants credential but has no BSD thread info"); 3460 /* 3461 * We can lazy-bind credentials to threads, as long as their processes 3462 * have them. 3463 * 3464 * XXX If we later inline this function, the code in this block 3465 * XXX should probably be called out in a function. 3466 */ 3467 if (uthread->uu_ucred == NOCRED) { 3468 if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL) 3469 panic("thread wants credential but has no BSD process"); 3470 uthread->uu_ucred = kauth_cred_proc_ref(p); 3471 } 3472 return(uthread->uu_ucred); 3473} 3474 3475void 3476mach_kauth_cred_uthread_update(void) 3477{ 3478 uthread_t uthread; 3479 proc_t proc; 3480 3481 uthread = get_bsdthread_info(current_thread()); 3482 proc = current_proc(); 3483 3484 kauth_cred_uthread_update(uthread, proc); 3485} 3486 3487/* 3488 * kauth_cred_uthread_update 3489 * 3490 * Description: Given a uthread, a proc, and whether or not the proc is locked, 3491 * late-bind the uthread cred to the proc cred. 3492 * 3493 * Parameters: uthread_t The uthread to update 3494 * proc_t The process to update to 3495 * 3496 * Returns: (void) 3497 * 3498 * Notes: This code is common code called from system call or trap entry 3499 * in the case that the process thread may have been changed 3500 * since the last time the thread entered the kernel. It is 3501 * generally only called with the current uthread and process as 3502 * parameters. 3503 */ 3504void 3505kauth_cred_uthread_update(uthread_t uthread, proc_t proc) 3506{ 3507 if (uthread->uu_ucred != proc->p_ucred && 3508 (uthread->uu_flag & UT_SETUID) == 0) { 3509 kauth_cred_t old = uthread->uu_ucred; 3510 uthread->uu_ucred = kauth_cred_proc_ref(proc); 3511 if (IS_VALID_CRED(old)) 3512 kauth_cred_unref(&old); 3513 } 3514} 3515 3516 3517/* 3518 * kauth_cred_get_with_ref 3519 * 3520 * Description: Takes a reference on the current thread's credential, and then 3521 * returns a pointer to it to the caller. 3522 * 3523 * Parameters: (void) 3524 * 3525 * Returns: (kauth_cred_t) Pointer to the current thread's 3526 * newly referenced credential 3527 * 3528 * Notes: This function takes a reference on the credential before 3529 * returning it to the caller. 3530 * 3531 * It is the responsibility of the calling code to release this 3532 * reference when the credential is no longer in use. 3533 * 3534 * Since the returned reference may be a persistent reference 3535 * (e.g. one cached in another data structure with a lifetime 3536 * longer than the calling function), this release may be delayed 3537 * until such time as the persistent reference is to be destroyed. 3538 * An example of this would be the per vnode credential cache used 3539 * to accelerate lookup operations. 3540 */ 3541kauth_cred_t 3542kauth_cred_get_with_ref(void) 3543{ 3544 struct proc *procp; 3545 struct uthread *uthread; 3546 3547 uthread = get_bsdthread_info(current_thread()); 3548 /* sanity checks */ 3549 if (uthread == NULL) 3550 panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__); 3551 if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL) 3552 panic("%s - thread wants credential but has no BSD process", __FUNCTION__); 3553 3554 /* 3555 * We can lazy-bind credentials to threads, as long as their processes 3556 * have them. 3557 * 3558 * XXX If we later inline this function, the code in this block 3559 * XXX should probably be called out in a function. 3560 */ 3561 if (uthread->uu_ucred == NOCRED) { 3562 /* take reference for new cred in thread */ 3563 uthread->uu_ucred = kauth_cred_proc_ref(procp); 3564 } 3565 /* take a reference for our caller */ 3566 kauth_cred_ref(uthread->uu_ucred); 3567 return(uthread->uu_ucred); 3568} 3569 3570 3571/* 3572 * kauth_cred_proc_ref 3573 * 3574 * Description: Takes a reference on the current process's credential, and 3575 * then returns a pointer to it to the caller. 3576 * 3577 * Parameters: procp Process whose credential we 3578 * intend to take a reference on 3579 * 3580 * Returns: (kauth_cred_t) Pointer to the process's 3581 * newly referenced credential 3582 * 3583 * Locks: PROC_LOCK is held before taking the reference and released 3584 * after the refeence is taken to protect the p_ucred field of 3585 * the process referred to by procp. 3586 * 3587 * Notes: This function takes a reference on the credential before 3588 * returning it to the caller. 3589 * 3590 * It is the responsibility of the calling code to release this 3591 * reference when the credential is no longer in use. 3592 * 3593 * Since the returned reference may be a persistent reference 3594 * (e.g. one cached in another data structure with a lifetime 3595 * longer than the calling function), this release may be delayed 3596 * until such time as the persistent reference is to be destroyed. 3597 * An example of this would be the per vnode credential cache used 3598 * to accelerate lookup operations. 3599 */ 3600kauth_cred_t 3601kauth_cred_proc_ref(proc_t procp) 3602{ 3603 kauth_cred_t cred; 3604 3605 proc_lock(procp); 3606 cred = proc_ucred(procp); 3607 kauth_cred_ref(cred); 3608 proc_unlock(procp); 3609 return(cred); 3610} 3611 3612 3613/* 3614 * kauth_cred_alloc 3615 * 3616 * Description: Allocate a new credential 3617 * 3618 * Parameters: (void) 3619 * 3620 * Returns: !NULL Newly allocated credential 3621 * NULL Insufficient memory 3622 * 3623 * Notes: The newly allocated credential is zero'ed as part of the 3624 * allocation process, with the exception of the reference 3625 * count, which is set to 1 to indicate a single reference 3626 * held by the caller. 3627 * 3628 * Since newly allocated credentials have no external pointers 3629 * referencing them, prior to making them visible in an externally 3630 * visible pointer (e.g. by adding them to the credential hash 3631 * cache) is the only legal time in which an existing credential 3632 * can be safely iinitialized or modified directly. 3633 * 3634 * After initialization, the caller is expected to call the 3635 * function kauth_cred_add() to add the credential to the hash 3636 * cache, after which time it's frozen and becomes publically 3637 * visible. 3638 * 3639 * The release protocol depends on kauth_hash_add() being called 3640 * before kauth_cred_rele() (there is a diagnostic panic which 3641 * will trigger if this protocol is not observed). 3642 * 3643 * XXX: This function really ought to be static, rather than being 3644 * exported as KPI, since a failure of kauth_cred_add() can only 3645 * be handled by an explicit free of the credential; such frees 3646 * depend on knowlegdge of the allocation method used, which is 3647 * permitted to change between kernel revisions. 3648 * 3649 * XXX: In the insufficient resource case, this code panic's rather 3650 * than returning a NULL pointer; the code that calls this 3651 * function needs to be audited before this can be changed. 3652 */ 3653kauth_cred_t 3654kauth_cred_alloc(void) 3655{ 3656 kauth_cred_t newcred; 3657 3658 MALLOC_ZONE(newcred, kauth_cred_t, sizeof(*newcred), M_CRED, M_WAITOK); 3659 if (newcred != 0) { 3660 posix_cred_t newpcred = posix_cred_get(newcred); 3661 bzero(newcred, sizeof(*newcred)); 3662 newcred->cr_ref = 1; 3663 newcred->cr_audit.as_aia_p = audit_default_aia_p; 3664 /* must do this, or cred has same group membership as uid 0 */ 3665 newpcred->cr_gmuid = KAUTH_UID_NONE; 3666#if CRED_DIAGNOSTIC 3667 } else { 3668 panic("kauth_cred_alloc: couldn't allocate credential"); 3669#endif 3670 } 3671 3672#if KAUTH_CRED_HASH_DEBUG 3673 kauth_cred_count++; 3674#endif 3675 3676#if CONFIG_MACF 3677 mac_cred_label_init(newcred); 3678#endif 3679 3680 return(newcred); 3681} 3682 3683 3684/* 3685 * kauth_cred_create 3686 * 3687 * Description: Look to see if we already have a known credential in the hash 3688 * cache; if one is found, bump the reference count and return 3689 * it. If there are no credentials that match the given 3690 * credential, then allocate a new credential. 3691 * 3692 * Parameters: cred Template for credential to 3693 * be created 3694 * 3695 * Returns: (kauth_cred_t) The credential that was found 3696 * in the hash or created 3697 * NULL kauth_cred_add() failed, or 3698 * there was not an egid specified 3699 * 3700 * Notes: The gmuid is hard-defaulted to the UID specified. Since we 3701 * maintain this field, we can't expect callers to know how it 3702 * needs to be set. Callers should be prepared for this field 3703 * to be overwritten. 3704 * 3705 * XXX: This code will tight-loop if memory for a new credential is 3706 * persistently unavailable; this is perhaps not the wisest way 3707 * to handle this condition, but current callers do not expect 3708 * a failure. 3709 */ 3710kauth_cred_t 3711kauth_cred_create(kauth_cred_t cred) 3712{ 3713 kauth_cred_t found_cred, new_cred = NULL; 3714 posix_cred_t pcred = posix_cred_get(cred); 3715 int is_member = 0; 3716 3717 KAUTH_CRED_HASH_LOCK_ASSERT(); 3718 3719 if (pcred->cr_flags & CRF_NOMEMBERD) { 3720 pcred->cr_gmuid = KAUTH_UID_NONE; 3721 } else { 3722 /* 3723 * If the template credential is not opting out of external 3724 * group membership resolution, then we need to check that 3725 * the UID we will be using is resolvable by the external 3726 * resolver. If it's not, then we opt it out anyway, since 3727 * all future external resolution requests will be failing 3728 * anyway, and potentially taking a long time to do it. We 3729 * use gid 0 because we always know it will exist and not 3730 * trigger additional lookups. This is OK, because we end up 3731 * precatching the information here as a result. 3732 */ 3733 if (!kauth_cred_ismember_gid(cred, 0, &is_member)) { 3734 /* 3735 * It's a recognized value; we don't really care about 3736 * the answer, so long as it's something the external 3737 * resolver could have vended. 3738 */ 3739 pcred->cr_gmuid = pcred->cr_uid; 3740 } else { 3741 /* 3742 * It's not something the external resolver could 3743 * have vended, so we don't want to ask it more 3744 * questions about the credential in the future. This 3745 * speeds up future lookups, as long as the caller 3746 * caches results; otherwise, it the same recurring 3747 * cost. Since most credentials are used multiple 3748 * times, we still get some performance win from this. 3749 */ 3750 pcred->cr_gmuid = KAUTH_UID_NONE; 3751 pcred->cr_flags |= CRF_NOMEMBERD; 3752 } 3753 } 3754 3755 /* Caller *must* specify at least the egid in cr_groups[0] */ 3756 if (pcred->cr_ngroups < 1) 3757 return(NULL); 3758 3759 for (;;) { 3760 KAUTH_CRED_HASH_LOCK(); 3761 found_cred = kauth_cred_find(cred); 3762 if (found_cred != NULL) { 3763 /* 3764 * Found an existing credential so we'll bump 3765 * reference count and return 3766 */ 3767 kauth_cred_ref(found_cred); 3768 KAUTH_CRED_HASH_UNLOCK(); 3769 return(found_cred); 3770 } 3771 KAUTH_CRED_HASH_UNLOCK(); 3772 3773 /* 3774 * No existing credential found. Create one and add it to 3775 * our hash table. 3776 */ 3777 new_cred = kauth_cred_alloc(); 3778 if (new_cred != NULL) { 3779 int err; 3780 posix_cred_t new_pcred = posix_cred_get(new_cred); 3781 new_pcred->cr_uid = pcred->cr_uid; 3782 new_pcred->cr_ruid = pcred->cr_ruid; 3783 new_pcred->cr_svuid = pcred->cr_svuid; 3784 new_pcred->cr_rgid = pcred->cr_rgid; 3785 new_pcred->cr_svgid = pcred->cr_svgid; 3786 new_pcred->cr_gmuid = pcred->cr_gmuid; 3787 new_pcred->cr_ngroups = pcred->cr_ngroups; 3788 bcopy(&pcred->cr_groups[0], &new_pcred->cr_groups[0], sizeof(new_pcred->cr_groups)); 3789#if CONFIG_AUDIT 3790 bcopy(&cred->cr_audit, &new_cred->cr_audit, 3791 sizeof(new_cred->cr_audit)); 3792#endif 3793 new_pcred->cr_flags = pcred->cr_flags; 3794 3795 KAUTH_CRED_HASH_LOCK(); 3796 err = kauth_cred_add(new_cred); 3797 KAUTH_CRED_HASH_UNLOCK(); 3798 3799 /* Retry if kauth_cred_add returns non zero value */ 3800 if (err == 0) 3801 break; 3802#if CONFIG_MACF 3803 mac_cred_label_destroy(new_cred); 3804#endif 3805 AUDIT_SESSION_UNREF(new_cred); 3806 3807 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED); 3808 new_cred = NULL; 3809 } 3810 } 3811 3812 return(new_cred); 3813} 3814 3815 3816/* 3817 * kauth_cred_setresuid 3818 * 3819 * Description: Update the given credential using the UID arguments. The given 3820 * UIDs are used to set the effective UID, real UID, saved UID, 3821 * and GMUID (used for group membership checking). 3822 * 3823 * Parameters: cred The original credential 3824 * ruid The new real UID 3825 * euid The new effective UID 3826 * svuid The new saved UID 3827 * gmuid KAUTH_UID_NONE -or- the new 3828 * group membership UID 3829 * 3830 * Returns: (kauth_cred_t) The updated credential 3831 * 3832 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid 3833 * setting, so if you don't want it to change, pass it the 3834 * previous value, explicitly. 3835 * 3836 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 3837 * if it returns a credential other than the one it is passed, 3838 * will have dropped the reference on the passed credential. All 3839 * callers should be aware of this, and treat this function as an 3840 * unref + ref, potentially on different credentials. 3841 * 3842 * Because of this, the caller is expected to take its own 3843 * reference on the credential passed as the first parameter, 3844 * and be prepared to release the reference on the credential 3845 * that is returned to them, if it is not intended to be a 3846 * persistent reference. 3847 */ 3848kauth_cred_t 3849kauth_cred_setresuid(kauth_cred_t cred, uid_t ruid, uid_t euid, uid_t svuid, uid_t gmuid) 3850{ 3851 struct ucred temp_cred; 3852 posix_cred_t temp_pcred = posix_cred_get(&temp_cred); 3853 posix_cred_t pcred = posix_cred_get(cred); 3854 3855 NULLCRED_CHECK(cred); 3856 3857 /* 3858 * We don't need to do anything if the UIDs we are changing are 3859 * already the same as the UIDs passed in 3860 */ 3861 if ((euid == KAUTH_UID_NONE || pcred->cr_uid == euid) && 3862 (ruid == KAUTH_UID_NONE || pcred->cr_ruid == ruid) && 3863 (svuid == KAUTH_UID_NONE || pcred->cr_svuid == svuid) && 3864 (pcred->cr_gmuid == gmuid)) { 3865 /* no change needed */ 3866 return(cred); 3867 } 3868 3869 /* 3870 * Look up in cred hash table to see if we have a matching credential 3871 * with the new values; this is done by calling kauth_cred_update(). 3872 */ 3873 bcopy(cred, &temp_cred, sizeof(temp_cred)); 3874 if (euid != KAUTH_UID_NONE) { 3875 temp_pcred->cr_uid = euid; 3876 } 3877 if (ruid != KAUTH_UID_NONE) { 3878 temp_pcred->cr_ruid = ruid; 3879 } 3880 if (svuid != KAUTH_UID_NONE) { 3881 temp_pcred->cr_svuid = svuid; 3882 } 3883 3884 /* 3885 * If we are setting the gmuid to KAUTH_UID_NONE, then we want to 3886 * opt out of participation in external group resolution, unless we 3887 * unless we explicitly opt back in later. 3888 */ 3889 if ((temp_pcred->cr_gmuid = gmuid) == KAUTH_UID_NONE) { 3890 temp_pcred->cr_flags |= CRF_NOMEMBERD; 3891 } 3892 3893 return(kauth_cred_update(cred, &temp_cred, TRUE)); 3894} 3895 3896 3897/* 3898 * kauth_cred_setresgid 3899 * 3900 * Description: Update the given credential using the GID arguments. The given 3901 * GIDs are used to set the effective GID, real GID, and saved 3902 * GID. 3903 * 3904 * Parameters: cred The original credential 3905 * rgid The new real GID 3906 * egid The new effective GID 3907 * svgid The new saved GID 3908 * 3909 * Returns: (kauth_cred_t) The updated credential 3910 * 3911 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 3912 * if it returns a credential other than the one it is passed, 3913 * will have dropped the reference on the passed credential. All 3914 * callers should be aware of this, and treat this function as an 3915 * unref + ref, potentially on different credentials. 3916 * 3917 * Because of this, the caller is expected to take its own 3918 * reference on the credential passed as the first parameter, 3919 * and be prepared to release the reference on the credential 3920 * that is returned to them, if it is not intended to be a 3921 * persistent reference. 3922 */ 3923kauth_cred_t 3924kauth_cred_setresgid(kauth_cred_t cred, gid_t rgid, gid_t egid, gid_t svgid) 3925{ 3926 struct ucred temp_cred; 3927 posix_cred_t temp_pcred = posix_cred_get(&temp_cred); 3928 posix_cred_t pcred = posix_cred_get(cred); 3929 3930 NULLCRED_CHECK(cred); 3931 DEBUG_CRED_ENTER("kauth_cred_setresgid %p %d %d %d\n", cred, rgid, egid, svgid); 3932 3933 /* 3934 * We don't need to do anything if the given GID are already the 3935 * same as the GIDs in the credential. 3936 */ 3937 if (pcred->cr_groups[0] == egid && 3938 pcred->cr_rgid == rgid && 3939 pcred->cr_svgid == svgid) { 3940 /* no change needed */ 3941 return(cred); 3942 } 3943 3944 /* 3945 * Look up in cred hash table to see if we have a matching credential 3946 * with the new values; this is done by calling kauth_cred_update(). 3947 */ 3948 bcopy(cred, &temp_cred, sizeof(temp_cred)); 3949 if (egid != KAUTH_GID_NONE) { 3950 /* displacing a supplementary group opts us out of memberd */ 3951 if (kauth_cred_change_egid(&temp_cred, egid)) { 3952 DEBUG_CRED_CHANGE("displaced!\n"); 3953 temp_pcred->cr_flags |= CRF_NOMEMBERD; 3954 temp_pcred->cr_gmuid = KAUTH_UID_NONE; 3955 } else { 3956 DEBUG_CRED_CHANGE("not displaced\n"); 3957 } 3958 } 3959 if (rgid != KAUTH_GID_NONE) { 3960 temp_pcred->cr_rgid = rgid; 3961 } 3962 if (svgid != KAUTH_GID_NONE) { 3963 temp_pcred->cr_svgid = svgid; 3964 } 3965 3966 return(kauth_cred_update(cred, &temp_cred, TRUE)); 3967} 3968 3969 3970/* 3971 * Update the given credential with the given groups. We only allocate a new 3972 * credential when the given gid actually results in changes to the existing 3973 * credential. 3974 * The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out) 3975 * which will be used for group membership checking. 3976 */ 3977/* 3978 * kauth_cred_setgroups 3979 * 3980 * Description: Update the given credential using the provide supplementary 3981 * group list and group membership UID 3982 * 3983 * Parameters: cred The original credential 3984 * groups Pointer to gid_t array which 3985 * contains the new group list 3986 * groupcount The count of valid groups which 3987 * are contained in 'groups' 3988 * gmuid KAUTH_UID_NONE -or- the new 3989 * group membership UID 3990 * 3991 * Returns: (kauth_cred_t) The updated credential 3992 * 3993 * Note: gmuid is different in that a KAUTH_UID_NONE is a valid 3994 * setting, so if you don't want it to change, pass it the 3995 * previous value, explicitly. 3996 * 3997 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 3998 * if it returns a credential other than the one it is passed, 3999 * will have dropped the reference on the passed credential. All 4000 * callers should be aware of this, and treat this function as an 4001 * unref + ref, potentially on different credentials. 4002 * 4003 * Because of this, the caller is expected to take its own 4004 * reference on the credential passed as the first parameter, 4005 * and be prepared to release the reference on the credential 4006 * that is returned to them, if it is not intended to be a 4007 * persistent reference. 4008 * 4009 * XXX: Changes are determined in ordinal order - if the caller passes 4010 * in the same groups list that is already present in the 4011 * credential, but the members are in a different order, even if 4012 * the EGID is not modified (i.e. cr_groups[0] is the same), it 4013 * is considered a modification to the credential, and a new 4014 * credential is created. 4015 * 4016 * This should perhaps be better optimized, but it is considered 4017 * to be the caller's problem. 4018 */ 4019kauth_cred_t 4020kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid) 4021{ 4022 int i; 4023 struct ucred temp_cred; 4024 posix_cred_t temp_pcred = posix_cred_get(&temp_cred); 4025 posix_cred_t pcred; 4026 4027 NULLCRED_CHECK(cred); 4028 4029 pcred = posix_cred_get(cred); 4030 4031 /* 4032 * We don't need to do anything if the given list of groups does not 4033 * change. 4034 */ 4035 if ((pcred->cr_gmuid == gmuid) && (pcred->cr_ngroups == groupcount)) { 4036 for (i = 0; i < groupcount; i++) { 4037 if (pcred->cr_groups[i] != groups[i]) 4038 break; 4039 } 4040 if (i == groupcount) { 4041 /* no change needed */ 4042 return(cred); 4043 } 4044 } 4045 4046 /* 4047 * Look up in cred hash table to see if we have a matching credential 4048 * with new values. If we are setting or clearing the gmuid, then 4049 * update the cr_flags, since clearing it is sticky. This permits an 4050 * opt-out of memberd processing using setgroups(), and an opt-in 4051 * using initgroups(). This is required for POSIX conformance. 4052 */ 4053 bcopy(cred, &temp_cred, sizeof(temp_cred)); 4054 temp_pcred->cr_ngroups = groupcount; 4055 bcopy(groups, temp_pcred->cr_groups, sizeof(temp_pcred->cr_groups)); 4056 temp_pcred->cr_gmuid = gmuid; 4057 if (gmuid == KAUTH_UID_NONE) 4058 temp_pcred->cr_flags |= CRF_NOMEMBERD; 4059 else 4060 temp_pcred->cr_flags &= ~CRF_NOMEMBERD; 4061 4062 return(kauth_cred_update(cred, &temp_cred, TRUE)); 4063} 4064 4065/* 4066 * Notes: The return value exists to account for the possibility of a 4067 * kauth_cred_t without a POSIX label. This will be the case in 4068 * the future (see posix_cred_get() below, for more details). 4069 */ 4070#if CONFIG_EXT_RESOLVER 4071int kauth_external_supplementary_groups_supported = 1; 4072 4073SYSCTL_INT(_kern, OID_AUTO, ds_supgroups_supported, CTLFLAG_RW | CTLFLAG_LOCKED, &kauth_external_supplementary_groups_supported, 0, ""); 4074#endif 4075 4076int 4077kauth_cred_getgroups(kauth_cred_t cred, gid_t *grouplist, int *countp) 4078{ 4079 int limit = NGROUPS; 4080 posix_cred_t pcred; 4081 4082 pcred = posix_cred_get(cred); 4083 4084#if CONFIG_EXT_RESOLVER 4085 /* 4086 * If we've not opted out of using the resolver, then convert the cred to a list 4087 * of supplemental groups. We do this only if there has been a resolver to talk to, 4088 * since we may be too early in boot, or in an environment that isn't using DS. 4089 */ 4090 if (kauth_identitysvc_has_registered && kauth_external_supplementary_groups_supported && (pcred->cr_flags & CRF_NOMEMBERD) == 0) { 4091 uid_t uid = kauth_cred_getuid(cred); 4092 int err; 4093 4094 err = kauth_cred_uid2groups(&uid, grouplist, countp); 4095 if (!err) 4096 return 0; 4097 4098 /* On error just fall through */ 4099 KAUTH_DEBUG("kauth_cred_getgroups failed %d\n", err); 4100 } 4101#endif /* CONFIG_EXT_RESOLVER */ 4102 4103 /* 4104 * If they just want a copy of the groups list, they may not care 4105 * about the actual count. If they specify an input count, however, 4106 * treat it as an indicator of the buffer size available in grouplist, 4107 * and limit the returned list to that size. 4108 */ 4109 if (countp) { 4110 limit = MIN(*countp, pcred->cr_ngroups); 4111 *countp = limit; 4112 } 4113 4114 memcpy(grouplist, pcred->cr_groups, sizeof(gid_t) * limit); 4115 4116 return 0; 4117} 4118 4119 4120/* 4121 * kauth_cred_setuidgid 4122 * 4123 * Description: Update the given credential using the UID and GID arguments. 4124 * The given UID is used to set the effective UID, real UID, and 4125 * saved UID. The given GID is used to set the effective GID, 4126 * real GID, and saved GID. 4127 * 4128 * Parameters: cred The original credential 4129 * uid The new UID to use 4130 * gid The new GID to use 4131 * 4132 * Returns: (kauth_cred_t) The updated credential 4133 * 4134 * Notes: We set the gmuid to uid if the credential we are inheriting 4135 * from has not opted out of memberd participation; otherwise 4136 * we set it to KAUTH_UID_NONE 4137 * 4138 * This code is only ever called from the per-thread credential 4139 * code path in the "set per thread credential" case; and in 4140 * posix_spawn() in the case that the POSIX_SPAWN_RESETIDS 4141 * flag is set. 4142 * 4143 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 4144 * if it returns a credential other than the one it is passed, 4145 * will have dropped the reference on the passed credential. All 4146 * callers should be aware of this, and treat this function as an 4147 * unref + ref, potentially on different credentials. 4148 * 4149 * Because of this, the caller is expected to take its own 4150 * reference on the credential passed as the first parameter, 4151 * and be prepared to release the reference on the credential 4152 * that is returned to them, if it is not intended to be a 4153 * persistent reference. 4154 */ 4155kauth_cred_t 4156kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid) 4157{ 4158 struct ucred temp_cred; 4159 posix_cred_t temp_pcred = posix_cred_get(&temp_cred); 4160 posix_cred_t pcred; 4161 4162 NULLCRED_CHECK(cred); 4163 4164 pcred = posix_cred_get(cred); 4165 4166 /* 4167 * We don't need to do anything if the effective, real and saved 4168 * user IDs are already the same as the user ID passed into us. 4169 */ 4170 if (pcred->cr_uid == uid && pcred->cr_ruid == uid && pcred->cr_svuid == uid && 4171 pcred->cr_gid == gid && pcred->cr_rgid == gid && pcred->cr_svgid == gid) { 4172 /* no change needed */ 4173 return(cred); 4174 } 4175 4176 /* 4177 * Look up in cred hash table to see if we have a matching credential 4178 * with the new values. 4179 */ 4180 bzero(&temp_cred, sizeof(temp_cred)); 4181 temp_pcred->cr_uid = uid; 4182 temp_pcred->cr_ruid = uid; 4183 temp_pcred->cr_svuid = uid; 4184 temp_pcred->cr_flags = pcred->cr_flags; 4185 /* inherit the opt-out of memberd */ 4186 if (pcred->cr_flags & CRF_NOMEMBERD) { 4187 temp_pcred->cr_gmuid = KAUTH_UID_NONE; 4188 temp_pcred->cr_flags |= CRF_NOMEMBERD; 4189 } else { 4190 temp_pcred->cr_gmuid = uid; 4191 temp_pcred->cr_flags &= ~CRF_NOMEMBERD; 4192 } 4193 temp_pcred->cr_ngroups = 1; 4194 /* displacing a supplementary group opts us out of memberd */ 4195 if (kauth_cred_change_egid(&temp_cred, gid)) { 4196 temp_pcred->cr_gmuid = KAUTH_UID_NONE; 4197 temp_pcred->cr_flags |= CRF_NOMEMBERD; 4198 } 4199 temp_pcred->cr_rgid = gid; 4200 temp_pcred->cr_svgid = gid; 4201#if CONFIG_MACF 4202 temp_cred.cr_label = cred->cr_label; 4203#endif 4204 4205 return(kauth_cred_update(cred, &temp_cred, TRUE)); 4206} 4207 4208 4209/* 4210 * kauth_cred_setsvuidgid 4211 * 4212 * Description: Function used by execve to set the saved uid and gid values 4213 * for suid/sgid programs 4214 * 4215 * Parameters: cred The credential to update 4216 * uid The saved uid to set 4217 * gid The saved gid to set 4218 * 4219 * Returns: (kauth_cred_t) The updated credential 4220 * 4221 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 4222 * if it returns a credential other than the one it is passed, 4223 * will have dropped the reference on the passed credential. All 4224 * callers should be aware of this, and treat this function as an 4225 * unref + ref, potentially on different credentials. 4226 * 4227 * Because of this, the caller is expected to take its own 4228 * reference on the credential passed as the first parameter, 4229 * and be prepared to release the reference on the credential 4230 * that is returned to them, if it is not intended to be a 4231 * persistent reference. 4232 */ 4233kauth_cred_t 4234kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid) 4235{ 4236 struct ucred temp_cred; 4237 posix_cred_t temp_pcred = posix_cred_get(&temp_cred); 4238 posix_cred_t pcred; 4239 4240 NULLCRED_CHECK(cred); 4241 4242 pcred = posix_cred_get(cred); 4243 4244 DEBUG_CRED_ENTER("kauth_cred_setsvuidgid: %p u%d->%d g%d->%d\n", cred, cred->cr_svuid, uid, cred->cr_svgid, gid); 4245 4246 /* 4247 * We don't need to do anything if the effective, real and saved 4248 * uids are already the same as the uid provided. This check is 4249 * likely insufficient. 4250 */ 4251 if (pcred->cr_svuid == uid && pcred->cr_svgid == gid) { 4252 /* no change needed */ 4253 return(cred); 4254 } 4255 DEBUG_CRED_CHANGE("kauth_cred_setsvuidgid: cred change\n"); 4256 4257 /* look up in cred hash table to see if we have a matching credential 4258 * with new values. 4259 */ 4260 bcopy(cred, &temp_cred, sizeof(temp_cred)); 4261 temp_pcred->cr_svuid = uid; 4262 temp_pcred->cr_svgid = gid; 4263 4264 return(kauth_cred_update(cred, &temp_cred, TRUE)); 4265} 4266 4267 4268/* 4269 * kauth_cred_setauditinfo 4270 * 4271 * Description: Update the given credential using the given au_session_t. 4272 * 4273 * Parameters: cred The original credential 4274 * auditinfo_p Pointer to ne audit information 4275 * 4276 * Returns: (kauth_cred_t) The updated credential 4277 * 4278 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 4279 * if it returns a credential other than the one it is passed, 4280 * will have dropped the reference on the passed credential. All 4281 * callers should be aware of this, and treat this function as an 4282 * unref + ref, potentially on different credentials. 4283 * 4284 * Because of this, the caller is expected to take its own 4285 * reference on the credential passed as the first parameter, 4286 * and be prepared to release the reference on the credential 4287 * that is returned to them, if it is not intended to be a 4288 * persistent reference. 4289 */ 4290kauth_cred_t 4291kauth_cred_setauditinfo(kauth_cred_t cred, au_session_t *auditinfo_p) 4292{ 4293 struct ucred temp_cred; 4294 4295 NULLCRED_CHECK(cred); 4296 4297 /* 4298 * We don't need to do anything if the audit info is already the 4299 * same as the audit info in the credential provided. 4300 */ 4301 if (bcmp(&cred->cr_audit, auditinfo_p, sizeof(cred->cr_audit)) == 0) { 4302 /* no change needed */ 4303 return(cred); 4304 } 4305 4306 bcopy(cred, &temp_cred, sizeof(temp_cred)); 4307 bcopy(auditinfo_p, &temp_cred.cr_audit, sizeof(temp_cred.cr_audit)); 4308 4309 return(kauth_cred_update(cred, &temp_cred, FALSE)); 4310} 4311 4312#if CONFIG_MACF 4313/* 4314 * kauth_cred_label_update 4315 * 4316 * Description: Update the MAC label associated with a credential 4317 * 4318 * Parameters: cred The original credential 4319 * label The MAC label to set 4320 * 4321 * Returns: (kauth_cred_t) The updated credential 4322 * 4323 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 4324 * if it returns a credential other than the one it is passed, 4325 * will have dropped the reference on the passed credential. All 4326 * callers should be aware of this, and treat this function as an 4327 * unref + ref, potentially on different credentials. 4328 * 4329 * Because of this, the caller is expected to take its own 4330 * reference on the credential passed as the first parameter, 4331 * and be prepared to release the reference on the credential 4332 * that is returned to them, if it is not intended to be a 4333 * persistent reference. 4334 */ 4335kauth_cred_t 4336kauth_cred_label_update(kauth_cred_t cred, struct label *label) 4337{ 4338 kauth_cred_t newcred; 4339 struct ucred temp_cred; 4340 4341 bcopy(cred, &temp_cred, sizeof(temp_cred)); 4342 4343 mac_cred_label_init(&temp_cred); 4344 mac_cred_label_associate(cred, &temp_cred); 4345 mac_cred_label_update(&temp_cred, label); 4346 4347 newcred = kauth_cred_update(cred, &temp_cred, TRUE); 4348 mac_cred_label_destroy(&temp_cred); 4349 return (newcred); 4350} 4351 4352/* 4353 * kauth_cred_label_update_execve 4354 * 4355 * Description: Update the MAC label associated with a credential as 4356 * part of exec 4357 * 4358 * Parameters: cred The original credential 4359 * vp The exec vnode 4360 * scriptl The script MAC label 4361 * execl The executable MAC label 4362 * disjointp Pointer to flag to set if old 4363 * and returned credentials are 4364 * disjoint 4365 * 4366 * Returns: (kauth_cred_t) The updated credential 4367 * 4368 * Implicit returns: 4369 * *disjointp Set to 1 for disjoint creds 4370 * 4371 * IMPORTANT: This function is implemented via kauth_cred_update(), which, 4372 * if it returns a credential other than the one it is passed, 4373 * will have dropped the reference on the passed credential. All 4374 * callers should be aware of this, and treat this function as an 4375 * unref + ref, potentially on different credentials. 4376 * 4377 * Because of this, the caller is expected to take its own 4378 * reference on the credential passed as the first parameter, 4379 * and be prepared to release the reference on the credential 4380 * that is returned to them, if it is not intended to be a 4381 * persistent reference. 4382 */ 4383 4384static 4385kauth_cred_t 4386kauth_cred_label_update_execve(kauth_cred_t cred, vfs_context_t ctx, 4387 struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl, 4388 struct label *execl, unsigned int *csflags, void *macextensions, int *disjointp, int *labelupdateerror) 4389{ 4390 kauth_cred_t newcred; 4391 struct ucred temp_cred; 4392 4393 bcopy(cred, &temp_cred, sizeof(temp_cred)); 4394 4395 mac_cred_label_init(&temp_cred); 4396 mac_cred_label_associate(cred, &temp_cred); 4397 mac_cred_label_update_execve(ctx, &temp_cred, 4398 vp, offset, scriptvp, scriptl, execl, csflags, 4399 macextensions, disjointp, labelupdateerror); 4400 4401 newcred = kauth_cred_update(cred, &temp_cred, TRUE); 4402 mac_cred_label_destroy(&temp_cred); 4403 return (newcred); 4404} 4405 4406/* 4407 * kauth_proc_label_update 4408 * 4409 * Description: Update the label inside the credential associated with the process. 4410 * 4411 * Parameters: p The process to modify 4412 * label The label to place in the process credential 4413 * 4414 * Notes: The credential associated with the process may change as a result 4415 * of this call. The caller should not assume the process reference to 4416 * the old credential still exists. 4417 */ 4418int kauth_proc_label_update(struct proc *p, struct label *label) 4419{ 4420 kauth_cred_t my_cred, my_new_cred; 4421 4422 my_cred = kauth_cred_proc_ref(p); 4423 4424 DEBUG_CRED_ENTER("kauth_proc_label_update: %p\n", my_cred); 4425 4426 /* get current credential and take a reference while we muck with it */ 4427 for (;;) { 4428 4429 /* 4430 * Set the credential with new info. If there is no change, 4431 * we get back the same credential we passed in; if there is 4432 * a change, we drop the reference on the credential we 4433 * passed in. The subsequent compare is safe, because it is 4434 * a pointer compare rather than a contents compare. 4435 */ 4436 my_new_cred = kauth_cred_label_update(my_cred, label); 4437 if (my_cred != my_new_cred) { 4438 4439 DEBUG_CRED_CHANGE("kauth_proc_setlabel_unlocked CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 4440 4441 proc_lock(p); 4442 /* 4443 * We need to protect for a race where another thread 4444 * also changed the credential after we took our 4445 * reference. If p_ucred has changed then we should 4446 * restart this again with the new cred. 4447 */ 4448 if (p->p_ucred != my_cred) { 4449 proc_unlock(p); 4450 kauth_cred_unref(&my_new_cred); 4451 my_cred = kauth_cred_proc_ref(p); 4452 /* try again */ 4453 continue; 4454 } 4455 p->p_ucred = my_new_cred; 4456 /* update cred on proc */ 4457 PROC_UPDATE_CREDS_ONPROC(p); 4458 4459 mac_proc_set_enforce(p, MAC_ALL_ENFORCE); 4460 proc_unlock(p); 4461 } 4462 break; 4463 } 4464 /* Drop old proc reference or our extra reference */ 4465 kauth_cred_unref(&my_cred); 4466 4467 return (0); 4468} 4469 4470/* 4471 * kauth_proc_label_update_execve 4472 * 4473 * Description: Update the label inside the credential associated with the 4474 * process as part of a transitioning execve. The label will 4475 * be updated by the policies as part of this processing, not 4476 * provided up front. 4477 * 4478 * Parameters: p The process to modify 4479 * ctx The context of the exec 4480 * vp The vnode being exec'ed 4481 * scriptl The script MAC label 4482 * execl The executable MAC label 4483 * lupdateerror The error place holder for MAC label authority 4484 * to update about possible termination 4485 * 4486 * Returns: 0 Label update did not make credential 4487 * disjoint 4488 * 1 Label update caused credential to be 4489 * disjoint 4490 * 4491 * Notes: The credential associated with the process WILL change as a 4492 * result of this call. The caller should not assume the process 4493 * reference to the old credential still exists. 4494 */ 4495 4496void 4497kauth_proc_label_update_execve(struct proc *p, vfs_context_t ctx, 4498 struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *scriptl, 4499 struct label *execl, unsigned int *csflags, void *macextensions, int *disjoint, int *update_return) 4500{ 4501 kauth_cred_t my_cred, my_new_cred; 4502 my_cred = kauth_cred_proc_ref(p); 4503 4504 DEBUG_CRED_ENTER("kauth_proc_label_update_execve: %p\n", my_cred); 4505 4506 /* get current credential and take a reference while we muck with it */ 4507 for (;;) { 4508 4509 /* 4510 * Set the credential with new info. If there is no change, 4511 * we get back the same credential we passed in; if there is 4512 * a change, we drop the reference on the credential we 4513 * passed in. The subsequent compare is safe, because it is 4514 * a pointer compare rather than a contents compare. 4515 */ 4516 my_new_cred = kauth_cred_label_update_execve(my_cred, ctx, vp, offset, scriptvp, scriptl, execl, csflags, macextensions, disjoint, update_return); 4517 if (my_cred != my_new_cred) { 4518 4519 DEBUG_CRED_CHANGE("kauth_proc_label_update_execve_unlocked CH(%d): %p/0x%08x -> %p/0x%08x\n", p->p_pid, my_cred, my_cred->cr_flags, my_new_cred, my_new_cred->cr_flags); 4520 4521 proc_lock(p); 4522 /* 4523 * We need to protect for a race where another thread 4524 * also changed the credential after we took our 4525 * reference. If p_ucred has changed then we should 4526 * restart this again with the new cred. 4527 */ 4528 if (p->p_ucred != my_cred) { 4529 proc_unlock(p); 4530 kauth_cred_unref(&my_new_cred); 4531 my_cred = kauth_cred_proc_ref(p); 4532 /* try again */ 4533 continue; 4534 } 4535 p->p_ucred = my_new_cred; 4536 /* update cred on proc */ 4537 PROC_UPDATE_CREDS_ONPROC(p); 4538 mac_proc_set_enforce(p, MAC_ALL_ENFORCE); 4539 proc_unlock(p); 4540 } 4541 break; 4542 } 4543 /* Drop old proc reference or our extra reference */ 4544 kauth_cred_unref(&my_cred); 4545} 4546 4547#if 1 4548/* 4549 * for temporary binary compatibility 4550 */ 4551kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, struct label *label); 4552kauth_cred_t 4553kauth_cred_setlabel(kauth_cred_t cred, struct label *label) 4554{ 4555 return kauth_cred_label_update(cred, label); 4556} 4557 4558int kauth_proc_setlabel(struct proc *p, struct label *label); 4559int 4560kauth_proc_setlabel(struct proc *p, struct label *label) 4561{ 4562 return kauth_proc_label_update(p, label); 4563} 4564#endif 4565 4566#else 4567 4568/* this is a temp hack to cover us when MACF is not built in a kernel configuration. 4569 * Since we cannot build our export lists based on the kernel configuration we need 4570 * to define a stub. 4571 */ 4572kauth_cred_t 4573kauth_cred_label_update(__unused kauth_cred_t cred, __unused void *label) 4574{ 4575 return(NULL); 4576} 4577 4578int 4579kauth_proc_label_update(__unused struct proc *p, __unused void *label) 4580{ 4581 return (0); 4582} 4583 4584#if 1 4585/* 4586 * for temporary binary compatibility 4587 */ 4588kauth_cred_t kauth_cred_setlabel(kauth_cred_t cred, void *label); 4589kauth_cred_t 4590kauth_cred_setlabel(__unused kauth_cred_t cred, __unused void *label) 4591{ 4592 return NULL; 4593} 4594 4595int kauth_proc_setlabel(struct proc *p, void *label); 4596int 4597kauth_proc_setlabel(__unused struct proc *p, __unused void *label) 4598{ 4599 return (0); 4600} 4601#endif 4602#endif 4603 4604/* 4605 * kauth_cred_ref 4606 * 4607 * Description: Add a reference to the passed credential 4608 * 4609 * Parameters: cred The credential to reference 4610 * 4611 * Returns: (void) 4612 * 4613 * Notes: This function adds a reference to the provided credential; 4614 * the existing reference on the credential is assumed to be 4615 * held stable over this operation by taking the appropriate 4616 * lock to protect the pointer from which it is being referenced, 4617 * if necessary (e.g. the proc lock is held over the call if the 4618 * credential being referenced is from p_ucred, the vnode lock 4619 * if from the per vnode name cache cred cache, and so on). 4620 * 4621 * This is safe from the kauth_cred_unref() path, since an atomic 4622 * add is used, and the unref path specifically checks to see that 4623 * the value has not been changed to add a reference between the 4624 * time the credential is unreferenced by another pointer and the 4625 * time it is unreferenced from the cred hash cache. 4626 */ 4627void 4628kauth_cred_ref(kauth_cred_t cred) 4629{ 4630 int old_value; 4631 4632 NULLCRED_CHECK(cred); 4633 4634 old_value = OSAddAtomicLong(1, (long*)&cred->cr_ref); 4635 4636 if (old_value < 1) 4637 panic("kauth_cred_ref: trying to take a reference on a cred with no references"); 4638 4639#if 0 // use this to watch a specific credential 4640 if ( is_target_cred( cred ) != 0 ) { 4641 get_backtrace( ); 4642 } 4643#endif 4644 4645 return; 4646} 4647 4648 4649/* 4650 * kauth_cred_unref_hashlocked 4651 * 4652 * Description: release a credential reference; when the last reference is 4653 * released, the credential will be freed. 4654 * 4655 * Parameters: credp Pointer to address containing 4656 * credential to be freed 4657 * 4658 * Returns: TRUE if the credential must be destroyed by the caller. 4659 * FALSE otherwise. 4660 * 4661 * Implicit returns: 4662 * *credp Set to NOCRED 4663 * 4664 * Notes: This function assumes the credential hash lock is held. 4665 * 4666 * This function is internal use only, since the hash lock is 4667 * scoped to this compilation unit. 4668 * 4669 * This function destroys the contents of the pointer passed by 4670 * the caller to prevent the caller accidentally attempting to 4671 * release a given reference twice in error. 4672 * 4673 * The last reference is considered to be released when a release 4674 * of a credential of a reference count of 2 occurs; this is an 4675 * intended effect, to take into account the reference held by 4676 * the credential hash, which is released at the same time. 4677 */ 4678static boolean_t 4679kauth_cred_unref_hashlocked(kauth_cred_t *credp) 4680{ 4681 int old_value; 4682 boolean_t destroy_it = FALSE; 4683 4684 KAUTH_CRED_HASH_LOCK_ASSERT(); 4685 NULLCRED_CHECK(*credp); 4686 4687 old_value = OSAddAtomicLong(-1, (long*)&(*credp)->cr_ref); 4688 4689#if DIAGNOSTIC 4690 if (old_value == 0) 4691 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no references", current_proc()->p_comm, *credp); 4692 if (old_value == 1) 4693 panic("%s:0x%08x kauth_cred_unref_hashlocked: dropping a reference on a cred with no hash entry", current_proc()->p_comm, *credp); 4694#endif 4695 4696#if 0 // use this to watch a specific credential 4697 if ( is_target_cred( *credp ) != 0 ) { 4698 get_backtrace( ); 4699 } 4700#endif 4701 4702 /* 4703 * If the old_value is 2, then we have just released the last external 4704 * reference to this credential 4705 */ 4706 if (old_value < 3) { 4707 /* The last absolute reference is our credential hash table */ 4708 destroy_it = kauth_cred_remove(*credp); 4709 } 4710 4711 if (destroy_it == FALSE) { 4712 *credp = NOCRED; 4713 } 4714 4715 return (destroy_it); 4716} 4717 4718 4719/* 4720 * kauth_cred_unref 4721 * 4722 * Description: Release a credential reference while holding the credential 4723 * hash lock; when the last reference is released, the credential 4724 * will be freed. 4725 * 4726 * Parameters: credp Pointer to address containing 4727 * credential to be freed 4728 * 4729 * Returns: (void) 4730 * 4731 * Implicit returns: 4732 * *credp Set to NOCRED 4733 * 4734 * Notes: See kauth_cred_unref_hashlocked() for more information. 4735 * 4736 */ 4737void 4738kauth_cred_unref(kauth_cred_t *credp) 4739{ 4740 boolean_t destroy_it; 4741 4742 KAUTH_CRED_HASH_LOCK(); 4743 destroy_it = kauth_cred_unref_hashlocked(credp); 4744 KAUTH_CRED_HASH_UNLOCK(); 4745 4746 if (destroy_it == TRUE) { 4747 assert(*credp != NOCRED); 4748#if CONFIG_MACF 4749 mac_cred_label_destroy(*credp); 4750#endif 4751 AUDIT_SESSION_UNREF(*credp); 4752 4753 (*credp)->cr_ref = 0; 4754 FREE_ZONE(*credp, sizeof(*(*credp)), M_CRED); 4755 *credp = NOCRED; 4756 } 4757} 4758 4759 4760#ifndef __LP64__ 4761/* 4762 * kauth_cred_rele 4763 * 4764 * Description: release a credential reference; when the last reference is 4765 * released, the credential will be freed 4766 * 4767 * Parameters: cred Credential to release 4768 * 4769 * Returns: (void) 4770 * 4771 * DEPRECATED: This interface is obsolete due to a failure to clear out the 4772 * clear the pointer in the caller to avoid multiple releases of 4773 * the same credential. The currently recommended interface is 4774 * kauth_cred_unref(). 4775 */ 4776void 4777kauth_cred_rele(kauth_cred_t cred) 4778{ 4779 kauth_cred_unref(&cred); 4780} 4781#endif /* !__LP64__ */ 4782 4783 4784/* 4785 * kauth_cred_dup 4786 * 4787 * Description: Duplicate a credential via alloc and copy; the new credential 4788 * has only it's own 4789 * 4790 * Parameters: cred The credential to duplicate 4791 * 4792 * Returns: (kauth_cred_t) The duplicate credential 4793 * 4794 * Notes: The typical value to calling this routine is if you are going 4795 * to modify an existing credential, and expect to need a new one 4796 * from the hash cache. 4797 * 4798 * This should probably not be used in the majority of cases; 4799 * if you are using it instead of kauth_cred_create(), you are 4800 * likely making a mistake. 4801 * 4802 * The newly allocated credential is copied as part of the 4803 * allocation process, with the exception of the reference 4804 * count, which is set to 1 to indicate a single reference 4805 * held by the caller. 4806 * 4807 * Since newly allocated credentials have no external pointers 4808 * referencing them, prior to making them visible in an externally 4809 * visible pointer (e.g. by adding them to the credential hash 4810 * cache) is the only legal time in which an existing credential 4811 * can be safely initialized or modified directly. 4812 * 4813 * After initialization, the caller is expected to call the 4814 * function kauth_cred_add() to add the credential to the hash 4815 * cache, after which time it's frozen and becomes publicly 4816 * visible. 4817 * 4818 * The release protocol depends on kauth_hash_add() being called 4819 * before kauth_cred_rele() (there is a diagnostic panic which 4820 * will trigger if this protocol is not observed). 4821 * 4822 */ 4823kauth_cred_t 4824kauth_cred_dup(kauth_cred_t cred) 4825{ 4826 kauth_cred_t newcred; 4827#if CONFIG_MACF 4828 struct label *temp_label; 4829#endif 4830 4831#if CRED_DIAGNOSTIC 4832 if (cred == NOCRED || cred == FSCRED) 4833 panic("kauth_cred_dup: bad credential"); 4834#endif 4835 newcred = kauth_cred_alloc(); 4836 if (newcred != NULL) { 4837#if CONFIG_MACF 4838 temp_label = newcred->cr_label; 4839#endif 4840 bcopy(cred, newcred, sizeof(*newcred)); 4841#if CONFIG_MACF 4842 newcred->cr_label = temp_label; 4843 mac_cred_label_associate(cred, newcred); 4844#endif 4845 AUDIT_SESSION_REF(cred); 4846 newcred->cr_ref = 1; 4847 } 4848 return(newcred); 4849} 4850 4851/* 4852 * kauth_cred_copy_real 4853 * 4854 * Description: Returns a credential based on the passed credential but which 4855 * reflects the real rather than effective UID and GID. 4856 * 4857 * Parameters: cred The credential from which to 4858 * derive the new credential 4859 * 4860 * Returns: (kauth_cred_t) The copied credential 4861 * 4862 * IMPORTANT: This function DOES NOT utilize kauth_cred_update(); as a 4863 * result, the caller is responsible for dropping BOTH the 4864 * additional reference on the passed cred (if any), and the 4865 * credential returned by this function. The drop should be 4866 * via the kauth_cred_unref() KPI. 4867 */ 4868kauth_cred_t 4869kauth_cred_copy_real(kauth_cred_t cred) 4870{ 4871 kauth_cred_t newcred = NULL, found_cred; 4872 struct ucred temp_cred; 4873 posix_cred_t temp_pcred = posix_cred_get(&temp_cred); 4874 posix_cred_t pcred = posix_cred_get(cred); 4875 4876 /* if the credential is already 'real', just take a reference */ 4877 if ((pcred->cr_ruid == pcred->cr_uid) && 4878 (pcred->cr_rgid == pcred->cr_gid)) { 4879 kauth_cred_ref(cred); 4880 return(cred); 4881 } 4882 4883 /* 4884 * Look up in cred hash table to see if we have a matching credential 4885 * with the new values. 4886 */ 4887 bcopy(cred, &temp_cred, sizeof(temp_cred)); 4888 temp_pcred->cr_uid = pcred->cr_ruid; 4889 /* displacing a supplementary group opts us out of memberd */ 4890 if (kauth_cred_change_egid(&temp_cred, pcred->cr_rgid)) { 4891 temp_pcred->cr_flags |= CRF_NOMEMBERD; 4892 temp_pcred->cr_gmuid = KAUTH_UID_NONE; 4893 } 4894 /* 4895 * If the cred is not opted out, make sure we are using the r/euid 4896 * for group checks 4897 */ 4898 if (temp_pcred->cr_gmuid != KAUTH_UID_NONE) 4899 temp_pcred->cr_gmuid = pcred->cr_ruid; 4900 4901 for (;;) { 4902 int err; 4903 4904 KAUTH_CRED_HASH_LOCK(); 4905 found_cred = kauth_cred_find(&temp_cred); 4906 if (found_cred == cred) { 4907 /* same cred so just bail */ 4908 KAUTH_CRED_HASH_UNLOCK(); 4909 return(cred); 4910 } 4911 if (found_cred != NULL) { 4912 /* 4913 * Found a match so we bump reference count on new 4914 * one. We leave the old one alone. 4915 */ 4916 kauth_cred_ref(found_cred); 4917 KAUTH_CRED_HASH_UNLOCK(); 4918 return(found_cred); 4919 } 4920 4921 /* 4922 * Must allocate a new credential, copy in old credential 4923 * data and update the real user and group IDs. 4924 */ 4925 newcred = kauth_cred_dup(&temp_cred); 4926 err = kauth_cred_add(newcred); 4927 KAUTH_CRED_HASH_UNLOCK(); 4928 4929 /* Retry if kauth_cred_add() fails */ 4930 if (err == 0) 4931 break; 4932#if CONFIG_MACF 4933 mac_cred_label_destroy(newcred); 4934#endif 4935 AUDIT_SESSION_UNREF(newcred); 4936 4937 FREE_ZONE(newcred, sizeof(*newcred), M_CRED); 4938 newcred = NULL; 4939 } 4940 4941 return(newcred); 4942} 4943 4944 4945/* 4946 * kauth_cred_update 4947 * 4948 * Description: Common code to update a credential 4949 * 4950 * Parameters: old_cred Reference counted credential 4951 * to update 4952 * model_cred Non-reference counted model 4953 * credential to apply to the 4954 * credential to be updated 4955 * retain_auditinfo Flag as to whether or not the 4956 * audit information should be 4957 * copied from the old_cred into 4958 * the model_cred 4959 * 4960 * Returns: (kauth_cred_t) The updated credential 4961 * 4962 * IMPORTANT: This function will potentially return a credential other than 4963 * the one it is passed, and if so, it will have dropped the 4964 * reference on the passed credential. All callers should be 4965 * aware of this, and treat this function as an unref + ref, 4966 * potentially on different credentials. 4967 * 4968 * Because of this, the caller is expected to take its own 4969 * reference on the credential passed as the first parameter, 4970 * and be prepared to release the reference on the credential 4971 * that is returned to them, if it is not intended to be a 4972 * persistent reference. 4973 */ 4974static kauth_cred_t 4975kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred, 4976 boolean_t retain_auditinfo) 4977{ 4978 kauth_cred_t found_cred, new_cred = NULL; 4979 4980 /* 4981 * Make sure we carry the auditinfo forward to the new credential 4982 * unless we are actually updating the auditinfo. 4983 */ 4984 if (retain_auditinfo) { 4985 bcopy(&old_cred->cr_audit, &model_cred->cr_audit, 4986 sizeof(model_cred->cr_audit)); 4987 } 4988 4989 for (;;) { 4990 int err; 4991 4992 KAUTH_CRED_HASH_LOCK(); 4993 found_cred = kauth_cred_find(model_cred); 4994 if (found_cred == old_cred) { 4995 /* same cred so just bail */ 4996 KAUTH_CRED_HASH_UNLOCK(); 4997 return(old_cred); 4998 } 4999 if (found_cred != NULL) { 5000 boolean_t destroy_it; 5001 5002 DEBUG_CRED_CHANGE("kauth_cred_update(cache hit): %p -> %p\n", old_cred, found_cred); 5003 /* 5004 * Found a match so we bump reference count on new 5005 * one and decrement reference count on the old one. 5006 */ 5007 kauth_cred_ref(found_cred); 5008 destroy_it = kauth_cred_unref_hashlocked(&old_cred); 5009 KAUTH_CRED_HASH_UNLOCK(); 5010 if (destroy_it == TRUE) { 5011 assert(old_cred != NOCRED); 5012#if CONFIG_MACF 5013 mac_cred_label_destroy(old_cred); 5014#endif 5015 AUDIT_SESSION_UNREF(old_cred); 5016 5017 old_cred->cr_ref = 0; 5018 FREE_ZONE(old_cred, sizeof(*old_cred), M_CRED); 5019 old_cred = NOCRED; 5020 5021 } 5022 return(found_cred); 5023 } 5024 5025 /* 5026 * Must allocate a new credential using the model. also 5027 * adds the new credential to the credential hash table. 5028 */ 5029 new_cred = kauth_cred_dup(model_cred); 5030 err = kauth_cred_add(new_cred); 5031 KAUTH_CRED_HASH_UNLOCK(); 5032 5033 /* retry if kauth_cred_add returns non zero value */ 5034 if (err == 0) 5035 break; 5036#if CONFIG_MACF 5037 mac_cred_label_destroy(new_cred); 5038#endif 5039 AUDIT_SESSION_UNREF(new_cred); 5040 5041 FREE_ZONE(new_cred, sizeof(*new_cred), M_CRED); 5042 new_cred = NULL; 5043 } 5044 5045 DEBUG_CRED_CHANGE("kauth_cred_update(cache miss): %p -> %p\n", old_cred, new_cred); 5046 kauth_cred_unref(&old_cred); 5047 return(new_cred); 5048} 5049 5050 5051/* 5052 * kauth_cred_add 5053 * 5054 * Description: Add the given credential to our credential hash table and 5055 * take an additional reference to account for our use of the 5056 * credential in the hash table 5057 * 5058 * Parameters: new_cred Credential to insert into cred 5059 * hash cache 5060 * 5061 * Returns: 0 Success 5062 * -1 Hash insertion failed: caller 5063 * should retry 5064 * 5065 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK 5066 * 5067 * Notes: The 'new_cred' MUST NOT already be in the cred hash cache 5068 */ 5069static int 5070kauth_cred_add(kauth_cred_t new_cred) 5071{ 5072 u_long hash_key; 5073 5074 KAUTH_CRED_HASH_LOCK_ASSERT(); 5075 5076 hash_key = kauth_cred_get_hashkey(new_cred); 5077 hash_key %= kauth_cred_table_size; 5078 5079 /* race fix - there is a window where another matching credential 5080 * could have been inserted between the time this one was created and we 5081 * got the hash lock. If we find a match return an error and have the 5082 * the caller retry. 5083 */ 5084 if (kauth_cred_find(new_cred) != NULL) { 5085 return(-1); 5086 } 5087 5088 /* take a reference for our use in credential hash table */ 5089 kauth_cred_ref(new_cred); 5090 5091 /* insert the credential into the hash table */ 5092 TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link); 5093 5094 return(0); 5095} 5096 5097 5098/* 5099 * kauth_cred_remove 5100 * 5101 * Description: Remove the given credential from our credential hash table 5102 * 5103 * Parameters: cred Credential to remove from cred 5104 * hash cache 5105 * 5106 * Returns: TRUE if the cred was found & removed from the hash; FALSE if not. 5107 * 5108 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK 5109 * 5110 * Notes: The check for the reference increment after entry is generally 5111 * agree to be safe, since we use atomic operations, and the 5112 * following code occurs with the hash lock held; in theory, this 5113 * protects us from the 2->1 reference that gets us here. 5114 */ 5115static boolean_t 5116kauth_cred_remove(kauth_cred_t cred) 5117{ 5118 u_long hash_key; 5119 kauth_cred_t found_cred; 5120 5121 hash_key = kauth_cred_get_hashkey(cred); 5122 hash_key %= kauth_cred_table_size; 5123 5124 /* Avoid race */ 5125 if (cred->cr_ref < 1) 5126 panic("cred reference underflow"); 5127 if (cred->cr_ref > 1) 5128 return (FALSE); /* someone else got a ref */ 5129 5130 /* Find cred in the credential hash table */ 5131 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) { 5132 if (found_cred == cred) { 5133 /* found a match, remove it from the hash table */ 5134 TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link); 5135#if KAUTH_CRED_HASH_DEBUG 5136 kauth_cred_count--; 5137#endif 5138 return (TRUE); 5139 } 5140 } 5141 5142 /* Did not find a match... this should not happen! XXX Make panic? */ 5143 printf("%s:%d - %s - %s - did not find a match for %p\n", __FILE__, __LINE__, __FUNCTION__, current_proc()->p_comm, cred); 5144 return (FALSE); 5145} 5146 5147 5148/* 5149 * kauth_cred_find 5150 * 5151 * Description: Using the given credential data, look for a match in our 5152 * credential hash table 5153 * 5154 * Parameters: cred Credential to lookup in cred 5155 * hash cache 5156 * 5157 * Returns: NULL Not found 5158 * !NULL Matching credential already in 5159 * cred hash cache 5160 * 5161 * Locks: Caller is expected to hold KAUTH_CRED_HASH_LOCK 5162 */ 5163kauth_cred_t 5164kauth_cred_find(kauth_cred_t cred) 5165{ 5166 u_long hash_key; 5167 kauth_cred_t found_cred; 5168 posix_cred_t pcred = posix_cred_get(cred); 5169 5170 KAUTH_CRED_HASH_LOCK_ASSERT(); 5171 5172#if KAUTH_CRED_HASH_DEBUG 5173 static int test_count = 0; 5174 5175 test_count++; 5176 if ((test_count % 200) == 0) { 5177 kauth_cred_hash_print(); 5178 } 5179#endif 5180 5181 hash_key = kauth_cred_get_hashkey(cred); 5182 hash_key %= kauth_cred_table_size; 5183 5184 /* Find cred in the credential hash table */ 5185 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) { 5186 boolean_t match; 5187 posix_cred_t found_pcred = posix_cred_get(found_cred); 5188 5189 /* 5190 * don't worry about the label unless the flags in 5191 * either credential tell us to. 5192 */ 5193 match = (bcmp(found_pcred, pcred, sizeof (*pcred)) == 0) ? TRUE : FALSE; 5194 match = match && ((bcmp(&found_cred->cr_audit, &cred->cr_audit, 5195 sizeof(cred->cr_audit)) == 0) ? TRUE : FALSE); 5196#if CONFIG_MACF 5197 if (((found_pcred->cr_flags & CRF_MAC_ENFORCE) != 0) || 5198 ((pcred->cr_flags & CRF_MAC_ENFORCE) != 0)) { 5199 match = match && mac_cred_label_compare(found_cred->cr_label, 5200 cred->cr_label); 5201 } 5202#endif 5203 if (match) { 5204 /* found a match */ 5205 return(found_cred); 5206 } 5207 } 5208 /* No match found */ 5209 5210 return(NULL); 5211} 5212 5213 5214/* 5215 * kauth_cred_hash 5216 * 5217 * Description: Generates a hash key using data that makes up a credential; 5218 * based on ElfHash 5219 * 5220 * Parameters: datap Pointer to data to hash 5221 * data_len Count of bytes to hash 5222 * start_key Start key value 5223 * 5224 * Returns: (u_long) Returned hash key 5225 */ 5226static inline u_long 5227kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key) 5228{ 5229 u_long hash_key = start_key; 5230 u_long temp; 5231 5232 while (data_len > 0) { 5233 hash_key = (hash_key << 4) + *datap++; 5234 temp = hash_key & 0xF0000000; 5235 if (temp) { 5236 hash_key ^= temp >> 24; 5237 } 5238 hash_key &= ~temp; 5239 data_len--; 5240 } 5241 return(hash_key); 5242} 5243 5244 5245/* 5246 * kauth_cred_get_hashkey 5247 * 5248 * Description: Generate a hash key using data that makes up a credential; 5249 * based on ElfHash. We hash on the entire credential data, 5250 * not including the ref count or the TAILQ, which are mutable; 5251 * everything else isn't. 5252 * 5253 * Parameters: cred Credential for which hash is 5254 * desired 5255 * 5256 * Returns: (u_long) Returned hash key 5257 * 5258 * Notes: When actually moving the POSIX credential into a real label, 5259 * remember to update this hash computation. 5260 */ 5261static u_long 5262kauth_cred_get_hashkey(kauth_cred_t cred) 5263{ 5264#if CONFIG_MACF 5265 posix_cred_t pcred = posix_cred_get(cred); 5266#endif 5267 u_long hash_key = 0; 5268 5269 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_posix, 5270 sizeof (struct posix_cred), 5271 hash_key); 5272 hash_key = kauth_cred_hash((uint8_t *)&cred->cr_audit, 5273 sizeof(struct au_session), 5274 hash_key); 5275#if CONFIG_MACF 5276 if (pcred->cr_flags & CRF_MAC_ENFORCE) { 5277 hash_key = kauth_cred_hash((uint8_t *)cred->cr_label, 5278 sizeof (struct label), 5279 hash_key); 5280 } 5281#endif 5282 return(hash_key); 5283} 5284 5285 5286#if KAUTH_CRED_HASH_DEBUG 5287/* 5288 * kauth_cred_hash_print 5289 * 5290 * Description: Print out cred hash cache table information for debugging 5291 * purposes, including the credential contents 5292 * 5293 * Parameters: (void) 5294 * 5295 * Returns: (void) 5296 * 5297 * Implicit returns: Results in console output 5298 */ 5299static void 5300kauth_cred_hash_print(void) 5301{ 5302 int i, j; 5303 kauth_cred_t found_cred; 5304 5305 printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count); 5306 /* count slot hits, misses, collisions, and max depth */ 5307 for (i = 0; i < kauth_cred_table_size; i++) { 5308 printf("[%02d] ", i); 5309 j = 0; 5310 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) { 5311 if (j > 0) { 5312 printf("---- "); 5313 } 5314 j++; 5315 kauth_cred_print(found_cred); 5316 printf("\n"); 5317 } 5318 if (j == 0) { 5319 printf("NOCRED \n"); 5320 } 5321 } 5322} 5323#endif /* KAUTH_CRED_HASH_DEBUG */ 5324 5325 5326#if (defined(KAUTH_CRED_HASH_DEBUG) && (KAUTH_CRED_HASH_DEBUG != 0)) || defined(DEBUG_CRED) 5327/* 5328 * kauth_cred_print 5329 * 5330 * Description: Print out an individual credential's contents for debugging 5331 * purposes 5332 * 5333 * Parameters: cred The credential to print out 5334 * 5335 * Returns: (void) 5336 * 5337 * Implicit returns: Results in console output 5338 */ 5339void 5340kauth_cred_print(kauth_cred_t cred) 5341{ 5342 int i; 5343 5344 printf("%p - refs %lu flags 0x%08x uids e%d r%d sv%d gm%d ", cred, cred->cr_ref, cred->cr_flags, cred->cr_uid, cred->cr_ruid, cred->cr_svuid, cred->cr_gmuid); 5345 printf("group count %d gids ", cred->cr_ngroups); 5346 for (i = 0; i < NGROUPS; i++) { 5347 if (i == 0) 5348 printf("e"); 5349 printf("%d ", cred->cr_groups[i]); 5350 } 5351 printf("r%d sv%d ", cred->cr_rgid, cred->cr_svgid); 5352 printf("auditinfo_addr %d %d %d %d %d %d\n", 5353 cred->cr_audit.s_aia_p->ai_auid, 5354 cred->cr_audit.as_mask.am_success, 5355 cred->cr_audit.as_mask.am_failure, 5356 cred->cr_audit.as_aia_p->ai_termid.at_port, 5357 cred->cr_audit.as_aia_p->ai_termid.at_addr[0], 5358 cred->cr_audit.as_aia_p->ai_asid); 5359} 5360 5361int is_target_cred( kauth_cred_t the_cred ) 5362{ 5363 if ( the_cred->cr_uid != 0 ) 5364 return( 0 ); 5365 if ( the_cred->cr_ruid != 0 ) 5366 return( 0 ); 5367 if ( the_cred->cr_svuid != 0 ) 5368 return( 0 ); 5369 if ( the_cred->cr_ngroups != 11 ) 5370 return( 0 ); 5371 if ( the_cred->cr_groups[0] != 11 ) 5372 return( 0 ); 5373 if ( the_cred->cr_groups[1] != 81 ) 5374 return( 0 ); 5375 if ( the_cred->cr_groups[2] != 63947 ) 5376 return( 0 ); 5377 if ( the_cred->cr_groups[3] != 80288 ) 5378 return( 0 ); 5379 if ( the_cred->cr_groups[4] != 89006 ) 5380 return( 0 ); 5381 if ( the_cred->cr_groups[5] != 52173 ) 5382 return( 0 ); 5383 if ( the_cred->cr_groups[6] != 84524 ) 5384 return( 0 ); 5385 if ( the_cred->cr_groups[7] != 79 ) 5386 return( 0 ); 5387 if ( the_cred->cr_groups[8] != 80292 ) 5388 return( 0 ); 5389 if ( the_cred->cr_groups[9] != 80 ) 5390 return( 0 ); 5391 if ( the_cred->cr_groups[10] != 90824 ) 5392 return( 0 ); 5393 if ( the_cred->cr_rgid != 11 ) 5394 return( 0 ); 5395 if ( the_cred->cr_svgid != 11 ) 5396 return( 0 ); 5397 if ( the_cred->cr_gmuid != 3475 ) 5398 return( 0 ); 5399 if ( the_cred->cr_audit.as_aia_p->ai_auid != 3475 ) 5400 return( 0 ); 5401/* 5402 if ( the_cred->cr_audit.as_mask.am_success != 0 ) 5403 return( 0 ); 5404 if ( the_cred->cr_audit.as_mask.am_failure != 0 ) 5405 return( 0 ); 5406 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_port != 0 ) 5407 return( 0 ); 5408 if ( the_cred->cr_audit.as_aia_p->ai_termid.at_addr[0] != 0 ) 5409 return( 0 ); 5410 if ( the_cred->cr_audit.as_aia_p->ai_asid != 0 ) 5411 return( 0 ); 5412 if ( the_cred->cr_flags != 0 ) 5413 return( 0 ); 5414*/ 5415 return( -1 ); // found target cred 5416} 5417 5418void get_backtrace( void ) 5419{ 5420 int my_slot; 5421 void * my_stack[ MAX_STACK_DEPTH ]; 5422 int i, my_depth; 5423 5424 if ( cred_debug_buf_p == NULL ) { 5425 MALLOC(cred_debug_buf_p, cred_debug_buffer *, sizeof(*cred_debug_buf_p), M_KAUTH, M_WAITOK); 5426 bzero(cred_debug_buf_p, sizeof(*cred_debug_buf_p)); 5427 } 5428 5429 if ( cred_debug_buf_p->next_slot > (MAX_CRED_BUFFER_SLOTS - 1) ) { 5430 /* buffer is full */ 5431 return; 5432 } 5433 5434 my_depth = OSBacktrace(&my_stack[0], MAX_STACK_DEPTH); 5435 if ( my_depth == 0 ) { 5436 printf("%s - OSBacktrace failed \n", __FUNCTION__); 5437 return; 5438 } 5439 5440 /* fill new backtrace */ 5441 my_slot = cred_debug_buf_p->next_slot; 5442 cred_debug_buf_p->next_slot++; 5443 cred_debug_buf_p->stack_buffer[ my_slot ].depth = my_depth; 5444 for ( i = 0; i < my_depth; i++ ) { 5445 cred_debug_buf_p->stack_buffer[ my_slot ].stack[ i ] = my_stack[ i ]; 5446 } 5447 5448 return; 5449} 5450 5451 5452/* subset of struct ucred for use in sysctl_dump_creds */ 5453struct debug_ucred { 5454 void *credp; 5455 u_long cr_ref; /* reference count */ 5456 uid_t cr_uid; /* effective user id */ 5457 uid_t cr_ruid; /* real user id */ 5458 uid_t cr_svuid; /* saved user id */ 5459 short cr_ngroups; /* number of groups in advisory list */ 5460 gid_t cr_groups[NGROUPS]; /* advisory group list */ 5461 gid_t cr_rgid; /* real group id */ 5462 gid_t cr_svgid; /* saved group id */ 5463 uid_t cr_gmuid; /* UID for group membership purposes */ 5464 struct auditinfo_addr cr_audit; /* user auditing data. */ 5465 void *cr_label; /* MACF label */ 5466 int cr_flags; /* flags on credential */ 5467}; 5468typedef struct debug_ucred debug_ucred; 5469 5470SYSCTL_PROC(_kern, OID_AUTO, dump_creds, CTLFLAG_RD, 5471 NULL, 0, sysctl_dump_creds, "S,debug_ucred", "List of credentials in the cred hash"); 5472 5473/* accessed by: 5474 * err = sysctlbyname( "kern.dump_creds", bufp, &len, NULL, 0 ); 5475 */ 5476 5477static int 5478sysctl_dump_creds( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req ) 5479{ 5480 int i, j, counter = 0; 5481 int error; 5482 size_t space; 5483 kauth_cred_t found_cred; 5484 debug_ucred * cred_listp; 5485 debug_ucred * nextp; 5486 5487 /* This is a readonly node. */ 5488 if (req->newptr != USER_ADDR_NULL) 5489 return (EPERM); 5490 5491 /* calculate space needed */ 5492 for (i = 0; i < kauth_cred_table_size; i++) { 5493 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) { 5494 counter++; 5495 } 5496 } 5497 5498 /* they are querying us so just return the space required. */ 5499 if (req->oldptr == USER_ADDR_NULL) { 5500 counter += 10; // add in some padding; 5501 req->oldidx = counter * sizeof(debug_ucred); 5502 return 0; 5503 } 5504 5505 MALLOC( cred_listp, debug_ucred *, req->oldlen, M_TEMP, M_WAITOK ); 5506 if ( cred_listp == NULL ) { 5507 return (ENOMEM); 5508 } 5509 5510 /* fill in creds to send back */ 5511 nextp = cred_listp; 5512 space = 0; 5513 for (i = 0; i < kauth_cred_table_size; i++) { 5514 TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) { 5515 nextp->credp = found_cred; 5516 nextp->cr_ref = found_cred->cr_ref; 5517 nextp->cr_uid = found_cred->cr_uid; 5518 nextp->cr_ruid = found_cred->cr_ruid; 5519 nextp->cr_svuid = found_cred->cr_svuid; 5520 nextp->cr_ngroups = found_cred->cr_ngroups; 5521 for ( j = 0; j < nextp->cr_ngroups; j++ ) { 5522 nextp->cr_groups[ j ] = found_cred->cr_groups[ j ]; 5523 } 5524 nextp->cr_rgid = found_cred->cr_rgid; 5525 nextp->cr_svgid = found_cred->cr_svgid; 5526 nextp->cr_gmuid = found_cred->cr_gmuid; 5527 nextp->cr_audit.ai_auid = 5528 found_cred->cr_audit.as_aia_p->ai_auid; 5529 nextp->cr_audit.ai_mask.am_success = 5530 found_cred->cr_audit.as_mask.am_success; 5531 nextp->cr_audit.ai_mask.am_failure = 5532 found_cred->cr_audit.as_mask.am_failure; 5533 nextp->cr_audit.ai_termid.at_port = 5534 found_cred->cr_audit.as_aia_p->ai_termid.at_port; 5535 nextp->cr_audit.ai_termid.at_type = 5536 found_cred->cr_audit.as_aia_p->ai_termid.at_type; 5537 nextp->cr_audit.ai_termid.at_addr[0] = 5538 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[0]; 5539 nextp->cr_audit.ai_termid.at_addr[1] = 5540 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[1]; 5541 nextp->cr_audit.ai_termid.at_addr[2] = 5542 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[2]; 5543 nextp->cr_audit.ai_termid.at_addr[3] = 5544 found_cred->cr_audit.as_aia_p->ai_termid.at_addr[3]; 5545 nextp->cr_audit.ai_asid = 5546 found_cred->cr_audit.as_aia_p->ai_asid; 5547 nextp->cr_audit.ai_flags = 5548 found_cred->cr_audit.as_aia_p->ai_flags; 5549 nextp->cr_label = found_cred->cr_label; 5550 nextp->cr_flags = found_cred->cr_flags; 5551 nextp++; 5552 space += sizeof(debug_ucred); 5553 if ( space > req->oldlen ) { 5554 FREE(cred_listp, M_TEMP); 5555 return (ENOMEM); 5556 } 5557 } 5558 } 5559 req->oldlen = space; 5560 error = SYSCTL_OUT(req, cred_listp, req->oldlen); 5561 FREE(cred_listp, M_TEMP); 5562 return (error); 5563} 5564 5565 5566SYSCTL_PROC(_kern, OID_AUTO, cred_bt, CTLFLAG_RD, 5567 NULL, 0, sysctl_dump_cred_backtraces, "S,cred_debug_buffer", "dump credential backtrace"); 5568 5569/* accessed by: 5570 * err = sysctlbyname( "kern.cred_bt", bufp, &len, NULL, 0 ); 5571 */ 5572 5573static int 5574sysctl_dump_cred_backtraces( __unused struct sysctl_oid *oidp, __unused void *arg1, __unused int arg2, struct sysctl_req *req ) 5575{ 5576 int i, j; 5577 int error; 5578 size_t space; 5579 cred_debug_buffer * bt_bufp; 5580 cred_backtrace * nextp; 5581 5582 /* This is a readonly node. */ 5583 if (req->newptr != USER_ADDR_NULL) 5584 return (EPERM); 5585 5586 if ( cred_debug_buf_p == NULL ) { 5587 return (EAGAIN); 5588 } 5589 5590 /* calculate space needed */ 5591 space = sizeof( cred_debug_buf_p->next_slot ); 5592 space += (sizeof( cred_backtrace ) * cred_debug_buf_p->next_slot); 5593 5594 /* they are querying us so just return the space required. */ 5595 if (req->oldptr == USER_ADDR_NULL) { 5596 req->oldidx = space; 5597 return 0; 5598 } 5599 5600 if ( space > req->oldlen ) { 5601 return (ENOMEM); 5602 } 5603 5604 MALLOC( bt_bufp, cred_debug_buffer *, req->oldlen, M_TEMP, M_WAITOK ); 5605 if ( bt_bufp == NULL ) { 5606 return (ENOMEM); 5607 } 5608 5609 /* fill in backtrace info to send back */ 5610 bt_bufp->next_slot = cred_debug_buf_p->next_slot; 5611 space = sizeof(bt_bufp->next_slot); 5612 5613 nextp = &bt_bufp->stack_buffer[ 0 ]; 5614 for (i = 0; i < cred_debug_buf_p->next_slot; i++) { 5615 nextp->depth = cred_debug_buf_p->stack_buffer[ i ].depth; 5616 for ( j = 0; j < nextp->depth; j++ ) { 5617 nextp->stack[ j ] = cred_debug_buf_p->stack_buffer[ i ].stack[ j ]; 5618 } 5619 space += sizeof(*nextp); 5620 nextp++; 5621 } 5622 req->oldlen = space; 5623 error = SYSCTL_OUT(req, bt_bufp, req->oldlen); 5624 FREE(bt_bufp, M_TEMP); 5625 return (error); 5626} 5627 5628#endif /* KAUTH_CRED_HASH_DEBUG || DEBUG_CRED */ 5629 5630 5631/* 5632 ********************************************************************** 5633 * The following routines will be moved to a policy_posix.c module at 5634 * some future point. 5635 ********************************************************************** 5636 */ 5637 5638/* 5639 * posix_cred_create 5640 * 5641 * Description: Helper function to create a kauth_cred_t credential that is 5642 * initally labelled with a specific POSIX credential label 5643 * 5644 * Parameters: pcred The posix_cred_t to use as the initial 5645 * label value 5646 * 5647 * Returns: (kauth_cred_t) The credential that was found in the 5648 * hash or creates 5649 * NULL kauth_cred_add() failed, or there was 5650 * no egid specified, or we failed to 5651 * attach a label to the new credential 5652 * 5653 * Notes: This function currently wraps kauth_cred_create(), and is the 5654 * only consumer of that ill-fated function, apart from bsd_init(). 5655 * It exists solely to support the NFS server code creation of 5656 * credentials based on the over-the-wire RPC calls containing 5657 * traditional POSIX credential information being tunneled to 5658 * the server host from the client machine. 5659 * 5660 * In the future, we hope this function goes away. 5661 * 5662 * In the short term, it creates a temporary credential, puts 5663 * the POSIX information from NFS into it, and then calls 5664 * kauth_cred_create(), as an internal implementation detail. 5665 * 5666 * If we have to keep it around in the medium term, it will 5667 * create a new kauth_cred_t, then label it with a POSIX label 5668 * corresponding to the contents of the kauth_cred_t. If the 5669 * policy_posix MACF module is not loaded, it will instead 5670 * substitute a posix_cred_t which GRANTS all access (effectively 5671 * a "root" credential) in order to not prevent NFS from working 5672 * in the case that we are not supporting POSIX credentials. 5673 */ 5674kauth_cred_t 5675posix_cred_create(posix_cred_t pcred) 5676{ 5677 struct ucred temp_cred; 5678 5679 bzero(&temp_cred, sizeof(temp_cred)); 5680 temp_cred.cr_posix = *pcred; 5681 5682 return kauth_cred_create(&temp_cred); 5683} 5684 5685 5686/* 5687 * posix_cred_get 5688 * 5689 * Description: Given a kauth_cred_t, return the POSIX credential label, if 5690 * any, which is associated with it. 5691 * 5692 * Parameters: cred The credential to obtain the label from 5693 * 5694 * Returns: posix_cred_t The POSIX credential label 5695 * 5696 * Notes: In the event that the policy_posix MACF module IS NOT loaded, 5697 * this function will return a pointer to a posix_cred_t which 5698 * GRANTS all access (effectively, a "root" credential). This is 5699 * necessary to support legacy code which insists on tightly 5700 * integrating POSIX credentials into its APIs, including, but 5701 * not limited to, System V IPC mechanisms, POSIX IPC mechanisms, 5702 * NFSv3, signals, dtrace, and a large number of kauth routines 5703 * used to implement POSIX permissions related system calls. 5704 * 5705 * In the event that the policy_posix MACF module IS loaded, and 5706 * there is no POSIX label on the kauth_cred_t credential, this 5707 * function will return a pointer to a posix_cred_t which DENIES 5708 * all access (effectively, a "deny rights granted by POSIX" 5709 * credential). This is necessary to support the concept of a 5710 * transiently loaded POSIX policy, or kauth_cred_t credentials 5711 * which can not be used in conjunctions with POSIX permissions 5712 * checks. 5713 * 5714 * This function currently returns the address of the cr_posix 5715 * field of the supplied kauth_cred_t credential, and as such 5716 * currently can not fail. In the future, this will not be the 5717 * case. 5718 */ 5719posix_cred_t 5720posix_cred_get(kauth_cred_t cred) 5721{ 5722 return(&cred->cr_posix); 5723} 5724 5725 5726/* 5727 * posix_cred_label 5728 * 5729 * Description: Label a kauth_cred_t with a POSIX credential label 5730 * 5731 * Parameters: cred The credential to label 5732 * pcred The POSIX credential t label it with 5733 * 5734 * Returns: (void) 5735 * 5736 * Notes: This function is currently void in order to permit it to fit 5737 * in with the current MACF framework label methods which allow 5738 * labeling to fail silently. This is like acceptable for 5739 * mandatory access controls, but not for POSIX, since those 5740 * access controls are advisory. We will need to consider a 5741 * return value in a future version of the MACF API. 5742 * 5743 * This operation currently cannot fail, as currently the POSIX 5744 * credential is a subfield of the kauth_cred_t (ucred), which 5745 * MUST be valid. In the future, this will not be the case. 5746 */ 5747void 5748posix_cred_label(kauth_cred_t cred, posix_cred_t pcred) 5749{ 5750 cred->cr_posix = *pcred; /* structure assign for now */ 5751} 5752 5753 5754/* 5755 * posix_cred_access 5756 * 5757 * Description: Perform a POSIX access check for a protected object 5758 * 5759 * Parameters: cred The credential to check 5760 * object_uid The POSIX UID of the protected object 5761 * object_gid The POSIX GID of the protected object 5762 * object_mode The POSIX mode of the protected object 5763 * mode_req The requested POSIX access rights 5764 * 5765 * Returns 0 Access is granted 5766 * EACCES Access is denied 5767 * 5768 * Notes: This code optimizes the case where the world and group rights 5769 * would both grant the requested rights to avoid making a group 5770 * membership query. This is a big performance win in the case 5771 * where this is true. 5772 */ 5773int 5774posix_cred_access(kauth_cred_t cred, id_t object_uid, id_t object_gid, mode_t object_mode, mode_t mode_req) 5775{ 5776 int is_member; 5777 mode_t mode_owner = (object_mode & S_IRWXU); 5778 mode_t mode_group = (object_mode & S_IRWXG) << 3; 5779 mode_t mode_world = (object_mode & S_IRWXO) << 6; 5780 5781 /* 5782 * Check first for owner rights 5783 */ 5784 if (kauth_cred_getuid(cred) == object_uid && (mode_req & mode_owner) == mode_req) 5785 return (0); 5786 5787 /* 5788 * Combined group and world rights check, if we don't have owner rights 5789 * 5790 * OPTIMIZED: If group and world rights would grant the same bits, and 5791 * they set of requested bits is in both, then we can simply check the 5792 * world rights, avoiding a group membership check, which is expensive. 5793 */ 5794 if ((mode_req & mode_group & mode_world) == mode_req) { 5795 return (0); 5796 } else { 5797 /* 5798 * NON-OPTIMIZED: requires group membership check. 5799 */ 5800 if ((mode_req & mode_group) != mode_req) { 5801 /* 5802 * exclusion group : treat errors as "is a member" 5803 * 5804 * NON-OPTIMIZED: +group would deny; must check group 5805 */ 5806 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) { 5807 /* 5808 * DENY: +group denies 5809 */ 5810 return (EACCES); 5811 } else { 5812 if ((mode_req & mode_world) != mode_req) { 5813 /* 5814 * DENY: both -group & world would deny 5815 */ 5816 return (EACCES); 5817 } else { 5818 /* 5819 * ALLOW: allowed by -group and +world 5820 */ 5821 return (0); 5822 } 5823 } 5824 } else { 5825 /* 5826 * inclusion group; treat errors as "not a member" 5827 * 5828 * NON-OPTIMIZED: +group allows, world denies; must 5829 * check group 5830 */ 5831 if (!kauth_cred_ismember_gid(cred, object_gid, &is_member) && is_member) { 5832 /* 5833 * ALLOW: allowed by +group 5834 */ 5835 return (0); 5836 } else { 5837 if ((mode_req & mode_world) != mode_req) { 5838 /* 5839 * DENY: both -group & world would deny 5840 */ 5841 return (EACCES); 5842 } else { 5843 /* 5844 * ALLOW: allowed by -group and +world 5845 */ 5846 return (0); 5847 } 5848 } 5849 } 5850 } 5851} 5852