darwin-xnu/bsd/kern/kern_credential.c
<<
>>
Prefs
   1/*
   2 * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
   3 *
   4 * @APPLE_LICENSE_HEADER_START@
   5 * 
   6 * The contents of this file constitute Original Code as defined in and
   7 * are subject to the Apple Public Source License Version 1.1 (the
   8 * "License").  You may not use this file except in compliance with the
   9 * License.  Please obtain a copy of the License at
  10 * http://www.apple.com/publicsource and read it before using this file.
  11 * 
  12 * This Original Code and all software distributed under the License are
  13 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  14 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  15 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  16 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
  17 * License for the specific language governing rights and limitations
  18 * under the License.
  19 * 
  20 * @APPLE_LICENSE_HEADER_END@
  21 */
  22
  23/*
  24 * Kernel Authorization framework: Management of process/thread credentials and identity information.
  25 */
  26
  27
  28#include <sys/param.h>  /* XXX trim includes */
  29#include <sys/acct.h>
  30#include <sys/systm.h>
  31#include <sys/ucred.h>
  32#include <sys/proc_internal.h>
  33#include <sys/user.h>
  34#include <sys/timeb.h>
  35#include <sys/times.h>
  36#include <sys/malloc.h>
  37#include <sys/kauth.h>
  38#include <sys/kernel.h>
  39
  40#include <bsm/audit_kernel.h>
  41
  42#include <sys/mount.h>
  43#include <sys/sysproto.h>
  44#include <mach/message.h>
  45#include <mach/host_security.h>
  46
  47#include <libkern/OSAtomic.h>
  48
  49#include <kern/task.h>
  50#include <kern/lock.h>
  51#ifdef MACH_ASSERT
  52# undef MACH_ASSERT
  53#endif
  54#define MACH_ASSERT 1   /* XXX so bogus */
  55#include <kern/assert.h>
  56
  57#define CRED_DIAGNOSTIC 1
  58
  59# define NULLCRED_CHECK(_c)     do {if (((_c) == NOCRED) || ((_c) == FSCRED)) panic("bad credential %p", _c);} while(0)
  60
  61/*
  62 * Interface to external identity resolver.
  63 *
  64 * The architecture of the interface is simple; the external resolver calls in to
  65 * get work, then calls back with completed work.  It also calls us to let us know
  66 * that it's (re)started, so that we can resubmit work if it times out.
  67 */
  68
  69static lck_mtx_t *kauth_resolver_mtx;
  70#define KAUTH_RESOLVER_LOCK()   lck_mtx_lock(kauth_resolver_mtx);
  71#define KAUTH_RESOLVER_UNLOCK() lck_mtx_unlock(kauth_resolver_mtx);
  72
  73static volatile pid_t   kauth_resolver_identity;
  74static int      kauth_resolver_registered;
  75static uint32_t kauth_resolver_sequence;
  76
  77struct kauth_resolver_work {
  78        TAILQ_ENTRY(kauth_resolver_work) kr_link;
  79        struct kauth_identity_extlookup kr_work;
  80        uint32_t        kr_seqno;
  81        int             kr_refs;
  82        int             kr_flags;
  83#define KAUTH_REQUEST_UNSUBMITTED       (1<<0)
  84#define KAUTH_REQUEST_SUBMITTED         (1<<1)
  85#define KAUTH_REQUEST_DONE              (1<<2)
  86        int             kr_result;
  87};
  88
  89TAILQ_HEAD(kauth_resolver_unsubmitted_head, kauth_resolver_work) kauth_resolver_unsubmitted;
  90TAILQ_HEAD(kauth_resolver_submitted_head, kauth_resolver_work)  kauth_resolver_submitted;
  91TAILQ_HEAD(kauth_resolver_done_head, kauth_resolver_work)       kauth_resolver_done;
  92
  93static int      kauth_resolver_submit(struct kauth_identity_extlookup *lkp);
  94static int      kauth_resolver_complete(user_addr_t message);
  95static int      kauth_resolver_getwork(user_addr_t message);
  96
  97#define KAUTH_CRED_PRIMES_COUNT 7
  98static const int kauth_cred_primes[KAUTH_CRED_PRIMES_COUNT] = {97, 241, 397, 743, 1499, 3989, 7499};
  99static int      kauth_cred_primes_index = 0;
 100static int      kauth_cred_table_size = 0;
 101
 102TAILQ_HEAD(kauth_cred_entry_head, ucred);
 103static struct kauth_cred_entry_head * kauth_cred_table_anchor = NULL;
 104
 105#define KAUTH_CRED_HASH_DEBUG 0
 106
 107static int kauth_cred_add(kauth_cred_t new_cred);
 108static void kauth_cred_remove(kauth_cred_t cred);
 109static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key);
 110static u_long kauth_cred_get_hashkey(kauth_cred_t cred);
 111static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t new_cred, boolean_t retain_auditinfo);
 112
 113#if KAUTH_CRED_HASH_DEBUG
 114static int      kauth_cred_count = 0;
 115static void kauth_cred_hash_print(void);
 116static void kauth_cred_print(kauth_cred_t cred);
 117#endif
 118
 119void
 120kauth_resolver_init(void)
 121{
 122        TAILQ_INIT(&kauth_resolver_unsubmitted);
 123        TAILQ_INIT(&kauth_resolver_submitted);
 124        TAILQ_INIT(&kauth_resolver_done);
 125        kauth_resolver_sequence = 31337;
 126        kauth_resolver_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
 127}
 128
 129/*
 130 * Allocate a work queue entry, submit the work and wait for completion.
 131 *
 132 * XXX do we want an 'interruptible' flag vs. always being interruptible?
 133 */
 134static int
 135kauth_resolver_submit(struct kauth_identity_extlookup *lkp)
 136{
 137        struct kauth_resolver_work *workp, *killp;
 138        struct timespec ts;
 139        int     error, shouldfree;
 140        
 141        /* no point actually blocking if the resolver isn't up yet */
 142        if (kauth_resolver_identity == 0) {
 143                /*
 144                 * We've already waited an initial 30 seconds with no result.
 145                 * Sleep on a stack address so no one wakes us before timeout;
 146                 * we sleep a half a second in case we are a high priority
 147                 * process, so that memberd doesn't starve while we are in a
 148                 * tight loop between user and kernel, eating all the CPU.
 149                 */
 150                error = tsleep(&ts, PZERO | PCATCH, "kr_submit", hz/2);
 151                if (kauth_resolver_identity == 0) {
 152                        /*
 153                         * if things haven't changed while we were asleep,
 154                         * tell the caller we couldn't get an authoritative
 155                         * answer.
 156                         */
 157                        return(EWOULDBLOCK);
 158                }
 159        }
 160                
 161        MALLOC(workp, struct kauth_resolver_work *, sizeof(*workp), M_KAUTH, M_WAITOK);
 162        if (workp == NULL)
 163                return(ENOMEM);
 164
 165        workp->kr_work = *lkp;
 166        workp->kr_refs = 1;
 167        workp->kr_flags = KAUTH_REQUEST_UNSUBMITTED;
 168        workp->kr_result = 0;
 169
 170        /*
 171         * We insert the request onto the unsubmitted queue, the call in from the
 172         * resolver will it to the submitted thread when appropriate.
 173         */
 174        KAUTH_RESOLVER_LOCK();
 175        workp->kr_seqno = workp->kr_work.el_seqno = kauth_resolver_sequence++;
 176        workp->kr_work.el_result = KAUTH_EXTLOOKUP_INPROG;
 177
 178        /* XXX as an optimisation, we could check the queue for identical items and coalesce */
 179        TAILQ_INSERT_TAIL(&kauth_resolver_unsubmitted, workp, kr_link);
 180
 181        wakeup_one((caddr_t)&kauth_resolver_unsubmitted);
 182        for (;;) {
 183                /* we could compute a better timeout here */
 184                ts.tv_sec = 30;
 185                ts.tv_nsec = 0;
 186                error = msleep(workp, kauth_resolver_mtx, PCATCH, "kr_submit", &ts);
 187                /* request has been completed? */
 188                if ((error == 0) && (workp->kr_flags & KAUTH_REQUEST_DONE))
 189                        break;
 190                /* woken because the resolver has died? */
 191                if (kauth_resolver_identity == 0) {
 192                        error = EIO;
 193                        break;
 194                }
 195                /* an error? */
 196                if (error != 0)
 197                        break;
 198        }
 199        /* if the request was processed, copy the result */
 200        if (error == 0)
 201                *lkp = workp->kr_work;
 202        
 203        /*
 204         * If the request timed out and was never collected, the resolver is dead and
 205         * probably not coming back anytime soon.  In this case we revert to no-resolver
 206         * behaviour, and punt all the other sleeping requests to clear the backlog.
 207         */
 208        if ((error == EWOULDBLOCK) && (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED)) {
 209                KAUTH_DEBUG("RESOLVER - request timed out without being collected for processing, resolver dead");
 210                kauth_resolver_identity = 0;
 211                /* kill all the other requestes that are waiting as well */
 212                TAILQ_FOREACH(killp, &kauth_resolver_submitted, kr_link)
 213                    wakeup(killp);
 214                TAILQ_FOREACH(killp, &kauth_resolver_unsubmitted, kr_link)
 215                    wakeup(killp);
 216        }
 217        
 218        /* drop our reference on the work item, and note whether we should free it or not */
 219        if (--workp->kr_refs <= 0) {
 220                /* work out which list we have to remove it from */
 221                if (workp->kr_flags & KAUTH_REQUEST_DONE) {
 222                        TAILQ_REMOVE(&kauth_resolver_done, workp, kr_link);
 223                } else if (workp->kr_flags & KAUTH_REQUEST_SUBMITTED) {
 224                        TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
 225                } else if (workp->kr_flags & KAUTH_REQUEST_UNSUBMITTED) {
 226                        TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
 227                } else {
 228                        KAUTH_DEBUG("RESOLVER - completed request has no valid queue");
 229                }
 230                shouldfree = 1;
 231        } else {
 232                /* someone else still has a reference on this request */
 233                shouldfree = 0;
 234        }
 235        /* collect request result */
 236        if (error == 0)
 237                error = workp->kr_result;
 238        KAUTH_RESOLVER_UNLOCK();
 239        /*
 240         * If we dropped the last reference, free the request.
 241         */
 242        if (shouldfree)
 243                FREE(workp, M_KAUTH);
 244
 245        KAUTH_DEBUG("RESOLVER - returning %d", error);
 246        return(error);
 247}
 248
 249/*
 250 * System call interface for the external identity resolver.
 251 */
 252int
 253identitysvc(__unused struct proc *p, struct identitysvc_args *uap, __unused register_t *retval)
 254{
 255        int opcode = uap->opcode;
 256        user_addr_t message = uap->message;
 257        struct kauth_resolver_work *workp;
 258        int error;
 259        pid_t new_id;
 260
 261        /*
 262         * New server registering itself.
 263         */
 264        if (opcode == KAUTH_EXTLOOKUP_REGISTER) {
 265                new_id = current_proc()->p_pid;
 266                if ((error = kauth_authorize_generic(kauth_cred_get(), KAUTH_GENERIC_ISSUSER)) != 0) {
 267                        KAUTH_DEBUG("RESOLVER - pid %d refused permission to become identity resolver", new_id);
 268                        return(error);
 269                }
 270                KAUTH_RESOLVER_LOCK();
 271                if (kauth_resolver_identity != new_id) {
 272                        KAUTH_DEBUG("RESOLVER - new resolver %d taking over from old %d", new_id, kauth_resolver_identity);
 273                        /*
 274                         * We have a new server, so assume that all the old requests have been lost.
 275                         */
 276                        while ((workp = TAILQ_LAST(&kauth_resolver_submitted, kauth_resolver_submitted_head)) != NULL) {
 277                                TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
 278                                workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
 279                                workp->kr_flags |= KAUTH_REQUEST_UNSUBMITTED;
 280                                TAILQ_INSERT_HEAD(&kauth_resolver_unsubmitted, workp, kr_link);
 281                        }
 282                        kauth_resolver_identity = new_id;
 283                        kauth_resolver_registered = 1;
 284                        wakeup(&kauth_resolver_unsubmitted);
 285                }
 286                KAUTH_RESOLVER_UNLOCK();
 287                return(0);
 288        }
 289
 290        /*
 291         * Beyond this point, we must be the resolver process.
 292         */
 293        if (current_proc()->p_pid != kauth_resolver_identity) {
 294                KAUTH_DEBUG("RESOLVER - call from bogus resolver %d\n", current_proc()->p_pid);
 295                return(EPERM);
 296        }
 297        
 298        /*
 299         * Got a result returning?
 300         */
 301        if (opcode & KAUTH_EXTLOOKUP_RESULT) {
 302                if ((error = kauth_resolver_complete(message)) != 0)
 303                        return(error);
 304        }
 305
 306        /*
 307         * Caller wants to take more work?
 308         */
 309        if (opcode & KAUTH_EXTLOOKUP_WORKER) {
 310                if ((error = kauth_resolver_getwork(message)) != 0)
 311                        return(error);
 312        }
 313
 314        return(0);
 315}
 316
 317/*
 318 * Get work for a caller.
 319 */
 320static int
 321kauth_resolver_getwork(user_addr_t message)
 322{
 323        struct kauth_resolver_work *workp;
 324        int             error;
 325
 326        KAUTH_RESOLVER_LOCK();
 327        error = 0;
 328        while ((workp = TAILQ_FIRST(&kauth_resolver_unsubmitted)) == NULL) {
 329                error = msleep(&kauth_resolver_unsubmitted, kauth_resolver_mtx, PCATCH, "GRGetWork", 0);
 330                if (error != 0)
 331                        break;
 332        }
 333        if (workp != NULL) {
 334                if ((error = copyout(&workp->kr_work, message, sizeof(workp->kr_work))) != 0) {
 335                        KAUTH_DEBUG("RESOLVER - error submitting work to resolve");
 336                        goto out;
 337                }
 338                TAILQ_REMOVE(&kauth_resolver_unsubmitted, workp, kr_link);
 339                workp->kr_flags &= ~KAUTH_REQUEST_UNSUBMITTED;
 340                workp->kr_flags |= KAUTH_REQUEST_SUBMITTED;
 341                TAILQ_INSERT_TAIL(&kauth_resolver_submitted, workp, kr_link);
 342        }
 343
 344out:
 345        KAUTH_RESOLVER_UNLOCK();
 346        return(error);
 347}
 348
 349/*
 350 * Return a result from userspace.
 351 */
 352static int
 353kauth_resolver_complete(user_addr_t message)
 354{
 355        struct kauth_identity_extlookup extl;
 356        struct kauth_resolver_work *workp;
 357        int error, result;
 358
 359        if ((error = copyin(message, &extl, sizeof(extl))) != 0) {
 360                KAUTH_DEBUG("RESOLVER - error getting completed work\n");
 361                return(error);
 362        }
 363
 364        KAUTH_RESOLVER_LOCK();
 365
 366        error = 0;
 367        result = 0;
 368        switch (extl.el_result) {
 369        case KAUTH_EXTLOOKUP_INPROG:
 370        {
 371                static int once = 0;
 372
 373                /* XXX this should go away once memberd is updated */
 374                if (!once) {
 375                        printf("kauth_resolver: memberd is not setting valid result codes (assuming always successful)\n");
 376                        once = 1;
 377                }
 378        }
 379        /* FALLTHROUGH */
 380        case KAUTH_EXTLOOKUP_SUCCESS:
 381                break;
 382
 383        case KAUTH_EXTLOOKUP_FATAL:
 384                /* fatal error means the resolver is dead */
 385                KAUTH_DEBUG("RESOLVER - resolver %d died, waiting for a new one", kauth_resolver_identity);
 386                kauth_resolver_identity = 0;
 387                /* XXX should we terminate all outstanding requests? */
 388                error = EIO;
 389                break;
 390        case KAUTH_EXTLOOKUP_BADRQ:
 391                KAUTH_DEBUG("RESOLVER - resolver reported invalid request %d", extl.el_seqno);
 392                result = EINVAL;
 393                break;
 394        case KAUTH_EXTLOOKUP_FAILURE:
 395                KAUTH_DEBUG("RESOLVER - resolver reported transient failure for request %d", extl.el_seqno);
 396                result = EIO;
 397                break;
 398        default:
 399                KAUTH_DEBUG("RESOLVER - resolver returned unexpected status %d", extl.el_result);
 400                result = EIO;
 401                break;
 402        }
 403
 404        /*
 405         * In the case of a fatal error, we assume that the resolver will restart
 406         * quickly and re-collect all of the outstanding requests.  Thus, we don't
 407         * complete the request which returned the fatal error status.
 408         */
 409        if (extl.el_result != KAUTH_EXTLOOKUP_FATAL) {
 410                /* scan our list for this request */
 411                TAILQ_FOREACH(workp, &kauth_resolver_submitted, kr_link) {
 412                        /* found it? */
 413                        if (workp->kr_seqno == extl.el_seqno) {
 414                                /* copy result */
 415                                workp->kr_work = extl;
 416                                /* move onto completed list and wake up requester(s) */
 417                                TAILQ_REMOVE(&kauth_resolver_submitted, workp, kr_link);
 418                                workp->kr_flags &= ~KAUTH_REQUEST_SUBMITTED;
 419                                workp->kr_flags |= KAUTH_REQUEST_DONE;
 420                                workp->kr_result = result;
 421                                TAILQ_INSERT_TAIL(&kauth_resolver_done, workp, kr_link);
 422                                wakeup(workp);
 423                                break;
 424                        }
 425                }
 426        }
 427        /*
 428         * Note that it's OK for us not to find anything; if the request has
 429         * timed out the work record will be gone.
 430         */
 431        KAUTH_RESOLVER_UNLOCK();
 432        
 433        return(error);
 434}
 435
 436
 437/*
 438 * Identity cache.
 439 */
 440
 441struct kauth_identity {
 442        TAILQ_ENTRY(kauth_identity) ki_link;
 443        int     ki_valid;
 444#define KI_VALID_UID    (1<<0)          /* UID and GID are mutually exclusive */
 445#define KI_VALID_GID    (1<<1)
 446#define KI_VALID_GUID   (1<<2)
 447#define KI_VALID_NTSID  (1<<3)
 448        uid_t   ki_uid;
 449        gid_t   ki_gid;
 450        guid_t  ki_guid;
 451        ntsid_t ki_ntsid;
 452        /*
 453         * Expiry times are the earliest time at which we will disregard the cached state and go to
 454         * userland.  Before then if the valid bit is set, we will return the cached value.  If it's
 455         * not set, we will not go to userland to resolve, just assume that there is no answer
 456         * available.
 457         */
 458        time_t  ki_guid_expiry;
 459        time_t  ki_ntsid_expiry;
 460};
 461
 462static TAILQ_HEAD(kauth_identity_head, kauth_identity) kauth_identities;
 463#define KAUTH_IDENTITY_CACHEMAX         100     /* XXX sizing? */
 464static int kauth_identity_count;
 465
 466static lck_mtx_t *kauth_identity_mtx;
 467#define KAUTH_IDENTITY_LOCK()   lck_mtx_lock(kauth_identity_mtx);
 468#define KAUTH_IDENTITY_UNLOCK() lck_mtx_unlock(kauth_identity_mtx);
 469
 470
 471static struct kauth_identity *kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry,
 472    ntsid_t *ntsidp, time_t ntsid_expiry);
 473static void     kauth_identity_register(struct kauth_identity *kip);
 474static void     kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *kip);
 475static void     kauth_identity_lru(struct kauth_identity *kip);
 476static int      kauth_identity_guid_expired(struct kauth_identity *kip);
 477static int      kauth_identity_ntsid_expired(struct kauth_identity *kip);
 478static int      kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir);
 479static int      kauth_identity_find_gid(gid_t gid, struct kauth_identity *kir);
 480static int      kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir);
 481static int      kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir);
 482
 483void
 484kauth_identity_init(void)
 485{
 486        TAILQ_INIT(&kauth_identities);
 487        kauth_identity_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
 488}
 489
 490static int
 491kauth_identity_resolve(__unused struct kauth_identity_extlookup *el)
 492{
 493        return(kauth_resolver_submit(el));
 494}
 495
 496static struct kauth_identity *
 497kauth_identity_alloc(uid_t uid, gid_t gid, guid_t *guidp, time_t guid_expiry, ntsid_t *ntsidp, time_t ntsid_expiry)
 498{
 499        struct kauth_identity *kip;
 500        
 501        /* get and fill in a new identity */
 502        MALLOC(kip, struct kauth_identity *, sizeof(*kip), M_KAUTH, M_WAITOK | M_ZERO);
 503        if (kip != NULL) {
 504                if (gid != KAUTH_GID_NONE) {
 505                        kip->ki_gid = gid;
 506                        kip->ki_valid = KI_VALID_GID;
 507                }
 508                if (uid != KAUTH_UID_NONE) {
 509                        if (kip->ki_valid & KI_VALID_GID)
 510                                panic("can't allocate kauth identity with both uid and gid");
 511                        kip->ki_uid = uid;
 512                        kip->ki_valid = KI_VALID_UID;
 513                }
 514                if (guidp != NULL) {
 515                        kip->ki_guid = *guidp;
 516                        kip->ki_valid |= KI_VALID_GUID;
 517                }
 518                kip->ki_guid_expiry = guid_expiry;
 519                if (ntsidp != NULL) {
 520                        kip->ki_ntsid = *ntsidp;
 521                        kip->ki_valid |= KI_VALID_NTSID;
 522                }
 523                kip->ki_ntsid_expiry = ntsid_expiry;
 524        }
 525        return(kip);
 526}
 527
 528/*
 529 * Register an association between identity tokens.
 530 */
 531static void
 532kauth_identity_register(struct kauth_identity *kip)
 533{
 534        struct kauth_identity *ip;
 535
 536        /*
 537         * We search the cache for the UID listed in the incoming association.  If we
 538         * already have an entry, the new information is merged.
 539         */
 540        ip = NULL;
 541        KAUTH_IDENTITY_LOCK();
 542        if (kip->ki_valid & KI_VALID_UID) {
 543                if (kip->ki_valid & KI_VALID_GID)
 544                        panic("kauth_identity: can't insert record with both UID and GID as key");
 545                TAILQ_FOREACH(ip, &kauth_identities, ki_link)
 546                    if ((ip->ki_valid & KI_VALID_UID) && (ip->ki_uid == kip->ki_uid))
 547                                break;
 548        } else if (kip->ki_valid & KI_VALID_GID) {
 549                TAILQ_FOREACH(ip, &kauth_identities, ki_link)
 550                    if ((ip->ki_valid & KI_VALID_GID) && (ip->ki_gid == kip->ki_gid))
 551                                break;
 552        } else {
 553                panic("kauth_identity: can't insert record without UID or GID as key");
 554        }
 555                
 556        if (ip != NULL) {
 557                /* we already have an entry, merge/overwrite */
 558                if (kip->ki_valid & KI_VALID_GUID) {
 559                        ip->ki_guid = kip->ki_guid;
 560                        ip->ki_valid |= KI_VALID_GUID;
 561                }
 562                ip->ki_guid_expiry = kip->ki_guid_expiry;
 563                if (kip->ki_valid & KI_VALID_NTSID) {
 564                        ip->ki_ntsid = kip->ki_ntsid;
 565                        ip->ki_valid |= KI_VALID_NTSID;
 566                }
 567                ip->ki_ntsid_expiry = kip->ki_ntsid_expiry;
 568                /* and discard the incoming identity */
 569                FREE(kip, M_KAUTH);
 570                ip = NULL;
 571        } else {
 572                /* don't have any information on this identity, so just add it */
 573                TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
 574                if (++kauth_identity_count > KAUTH_IDENTITY_CACHEMAX) {
 575                        ip = TAILQ_LAST(&kauth_identities, kauth_identity_head);
 576                        TAILQ_REMOVE(&kauth_identities, ip, ki_link);
 577                        kauth_identity_count--;
 578                }
 579        }
 580        KAUTH_IDENTITY_UNLOCK();
 581        /* have to drop lock before freeing expired entry */
 582        if (ip != NULL)
 583                FREE(ip, M_KAUTH);
 584}
 585
 586/*
 587 * Given a lookup result, add any associations that we don't
 588 * currently have.
 589 */
 590static void
 591kauth_identity_updatecache(struct kauth_identity_extlookup *elp, struct kauth_identity *rkip)
 592{
 593        struct timeval tv;
 594        struct kauth_identity *kip;
 595
 596        microuptime(&tv);
 597        
 598        /* user identity? */
 599        if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UID) {
 600                KAUTH_IDENTITY_LOCK();
 601                TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
 602                        /* matching record */
 603                        if ((kip->ki_valid & KI_VALID_UID) && (kip->ki_uid == elp->el_uid)) {
 604                                if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) {
 605                                        kip->ki_guid = elp->el_uguid;
 606                                        kip->ki_valid |= KI_VALID_GUID;
 607                                }
 608                                kip->ki_guid_expiry = tv.tv_sec + elp->el_uguid_valid;
 609                                if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) {
 610                                        kip->ki_ntsid = elp->el_usid;
 611                                        kip->ki_valid |= KI_VALID_NTSID;
 612                                }
 613                                kip->ki_ntsid_expiry = tv.tv_sec + elp->el_usid_valid;
 614                                kauth_identity_lru(kip);
 615                                if (rkip != NULL)
 616                                        *rkip = *kip;
 617                                KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
 618                                break;
 619                        }
 620                }
 621                KAUTH_IDENTITY_UNLOCK();
 622                /* not found in cache, add new record */
 623                if (kip == NULL) {
 624                        kip = kauth_identity_alloc(elp->el_uid, KAUTH_GID_NONE,
 625                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_UGUID) ? &elp->el_uguid : NULL,
 626                            tv.tv_sec + elp->el_uguid_valid,
 627                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_USID) ? &elp->el_usid : NULL,
 628                            tv.tv_sec + elp->el_usid_valid);
 629                        if (kip != NULL) {
 630                                if (rkip != NULL)
 631                                        *rkip = *kip;
 632                                KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
 633                                kauth_identity_register(kip);
 634                        }
 635                }
 636        }
 637
 638        /* group identity? */
 639        if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GID) {
 640                KAUTH_IDENTITY_LOCK();
 641                TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
 642                        /* matching record */
 643                        if ((kip->ki_valid & KI_VALID_GID) && (kip->ki_gid == elp->el_gid)) {
 644                                if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) {
 645                                        kip->ki_guid = elp->el_gguid;
 646                                        kip->ki_valid |= KI_VALID_GUID;
 647                                }
 648                                kip->ki_guid_expiry = tv.tv_sec + elp->el_gguid_valid;
 649                                if (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) {
 650                                        kip->ki_ntsid = elp->el_gsid;
 651                                        kip->ki_valid |= KI_VALID_NTSID;
 652                                }
 653                                kip->ki_ntsid_expiry = tv.tv_sec + elp->el_gsid_valid;
 654                                kauth_identity_lru(kip);
 655                                if (rkip != NULL)
 656                                        *rkip = *kip;
 657                                KAUTH_DEBUG("CACHE - refreshed %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
 658                                break;
 659                        }
 660                }
 661                KAUTH_IDENTITY_UNLOCK();
 662                /* not found in cache, add new record */
 663                if (kip == NULL) {
 664                        kip = kauth_identity_alloc(KAUTH_UID_NONE, elp->el_gid,
 665                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GGUID) ? &elp->el_gguid : NULL,
 666                            tv.tv_sec + elp->el_gguid_valid,
 667                            (elp->el_flags & KAUTH_EXTLOOKUP_VALID_GSID) ? &elp->el_gsid : NULL,
 668                            tv.tv_sec + elp->el_gsid_valid);
 669                        if (kip != NULL) {
 670                                if (rkip != NULL)
 671                                        *rkip = *kip;
 672                                KAUTH_DEBUG("CACHE - learned %d is " K_UUID_FMT, kip->ki_uid, K_UUID_ARG(kip->ki_guid));
 673                                kauth_identity_register(kip);
 674                        }
 675                }
 676        }
 677
 678}
 679
 680/*
 681 * Promote the entry to the head of the LRU, assumes the cache is locked.
 682 *
 683 * This is called even if the entry has expired; typically an expired entry
 684 * that's been looked up is about to be revalidated, and having it closer to
 685 * the head of the LRU means finding it quickly again when the revalidation
 686 * comes through.
 687 */
 688static void
 689kauth_identity_lru(struct kauth_identity *kip)
 690{
 691        if (kip != TAILQ_FIRST(&kauth_identities)) {
 692                TAILQ_REMOVE(&kauth_identities, kip, ki_link);
 693                TAILQ_INSERT_HEAD(&kauth_identities, kip, ki_link);
 694        }
 695}
 696
 697/*
 698 * Handly lazy expiration of translations.
 699 */
 700static int
 701kauth_identity_guid_expired(struct kauth_identity *kip)
 702{
 703        struct timeval tv;
 704
 705        microuptime(&tv);
 706        KAUTH_DEBUG("CACHE - GUID expires @ %d now %d", kip->ki_guid_expiry, tv.tv_sec);
 707        return((kip->ki_guid_expiry <= tv.tv_sec) ? 1 : 0);
 708}
 709
 710static int
 711kauth_identity_ntsid_expired(struct kauth_identity *kip)
 712{
 713        struct timeval tv;
 714
 715        microuptime(&tv);
 716        KAUTH_DEBUG("CACHE - NTSID expires @ %d now %d", kip->ki_ntsid_expiry, tv.tv_sec);
 717        return((kip->ki_ntsid_expiry <= tv.tv_sec) ? 1 : 0);
 718}
 719
 720/*
 721 * Search for an entry by UID.  Returns a copy of the entry, ENOENT if no valid
 722 * association exists for the UID.
 723 */
 724static int
 725kauth_identity_find_uid(uid_t uid, struct kauth_identity *kir)
 726{
 727        struct kauth_identity *kip;
 728
 729        KAUTH_IDENTITY_LOCK();
 730        TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
 731                if ((kip->ki_valid & KI_VALID_UID) && (uid == kip->ki_uid)) {
 732                        kauth_identity_lru(kip);
 733                        *kir = *kip;
 734                        break;
 735                }
 736        }
 737        KAUTH_IDENTITY_UNLOCK();
 738        return((kip == NULL) ? ENOENT : 0);
 739}
 740
 741
 742/*
 743 * Search for an entry by GID. Returns a copy of the entry, ENOENT if no valid
 744 * association exists for the GID.
 745 */
 746static int
 747kauth_identity_find_gid(uid_t gid, struct kauth_identity *kir)
 748{
 749        struct kauth_identity *kip;
 750
 751        KAUTH_IDENTITY_LOCK();
 752        TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
 753                if ((kip->ki_valid & KI_VALID_GID) && (gid == kip->ki_gid)) {
 754                        kauth_identity_lru(kip);
 755                        *kir = *kip;
 756                        break;
 757                }
 758        }
 759        KAUTH_IDENTITY_UNLOCK();
 760        return((kip == NULL) ? ENOENT : 0);
 761}
 762
 763
 764/*
 765 * Search for an entry by GUID. Returns a copy of the entry, ENOENT if no valid
 766 * association exists for the GUID.  Note that the association may be expired,
 767 * in which case the caller may elect to call out to userland to revalidate.
 768 */
 769static int
 770kauth_identity_find_guid(guid_t *guidp, struct kauth_identity *kir)
 771{
 772        struct kauth_identity *kip;
 773
 774        KAUTH_IDENTITY_LOCK();
 775        TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
 776                if ((kip->ki_valid & KI_VALID_GUID) && (kauth_guid_equal(guidp, &kip->ki_guid))) {
 777                        kauth_identity_lru(kip);
 778                        *kir = *kip;
 779                        break;
 780                }
 781        }
 782        KAUTH_IDENTITY_UNLOCK();
 783        return((kip == NULL) ? ENOENT : 0);
 784}
 785
 786/*
 787 * Search for an entry by NT Security ID. Returns a copy of the entry, ENOENT if no valid
 788 * association exists for the SID.  Note that the association may be expired,
 789 * in which case the caller may elect to call out to userland to revalidate.
 790 */
 791static int
 792kauth_identity_find_ntsid(ntsid_t *ntsid, struct kauth_identity *kir)
 793{
 794        struct kauth_identity *kip;
 795
 796        KAUTH_IDENTITY_LOCK();
 797        TAILQ_FOREACH(kip, &kauth_identities, ki_link) {
 798                if ((kip->ki_valid & KI_VALID_NTSID) && (kauth_ntsid_equal(ntsid, &kip->ki_ntsid))) {
 799                        kauth_identity_lru(kip);
 800                        *kir = *kip;
 801                        break;
 802                }
 803        }
 804        KAUTH_IDENTITY_UNLOCK();
 805        return((kip == NULL) ? ENOENT : 0);
 806}
 807
 808/*
 809 * GUID handling.
 810 */
 811guid_t kauth_null_guid;
 812
 813int
 814kauth_guid_equal(guid_t *guid1, guid_t *guid2)
 815{
 816        return(!bcmp(guid1, guid2, sizeof(*guid1)));
 817}
 818
 819/*
 820 * Look for well-known GUIDs.
 821 */
 822int
 823kauth_wellknown_guid(guid_t *guid)
 824{
 825        static char     fingerprint[] = {0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef, 0xab, 0xcd, 0xef};
 826        int             code;
 827        /*
 828         * All WKGs begin with the same 12 bytes.
 829         */
 830        if (!bcmp((void *)guid, fingerprint, 12)) {
 831                /*
 832                 * The final 4 bytes are our code.
 833                 */
 834                code = *(u_int32_t *)&guid->g_guid[12];
 835                switch(code) {
 836                case 0x0000000c:
 837                        return(KAUTH_WKG_EVERYBODY);
 838                case 0xfffffffe:
 839                        return(KAUTH_WKG_NOBODY);
 840                case 0x0000000a:
 841                        return(KAUTH_WKG_OWNER);
 842                case 0x00000010:
 843                        return(KAUTH_WKG_GROUP);
 844                }
 845        }
 846        return(KAUTH_WKG_NOT);
 847}
 848
 849
 850/*
 851 * NT Security Identifier handling.
 852 */
 853int
 854kauth_ntsid_equal(ntsid_t *sid1, ntsid_t *sid2)
 855{
 856        /* check sizes for equality, also sanity-check size while we're at it */
 857        if ((KAUTH_NTSID_SIZE(sid1) == KAUTH_NTSID_SIZE(sid2)) &&
 858            (KAUTH_NTSID_SIZE(sid1) <= sizeof(*sid1)) &&
 859            !bcmp(sid1, sid2, KAUTH_NTSID_SIZE(sid1)))
 860                return(1);
 861        return(0);
 862}
 863
 864/*
 865 * Identity KPI
 866 *
 867 * We support four tokens representing identity:
 868 *  - Credential reference
 869 *  - UID
 870 *  - GUID
 871 *  - NT security identifier
 872 *
 873 * Of these, the UID is the ubiquitous identifier; cross-referencing should
 874 * be done using it.
 875 */
 876
 877static int      kauth_cred_cache_lookup(int from, int to, void *src, void *dst);
 878
 879/*
 880 * Fetch UID from credential.
 881 */
 882uid_t
 883kauth_cred_getuid(kauth_cred_t cred)
 884{
 885        NULLCRED_CHECK(cred);
 886        return(cred->cr_uid);
 887}
 888
 889/*
 890 * Fetch GID from credential.
 891 */
 892uid_t
 893kauth_cred_getgid(kauth_cred_t cred)
 894{
 895        NULLCRED_CHECK(cred);
 896        return(cred->cr_gid);
 897}
 898
 899/*
 900 * Fetch UID from GUID.
 901 */
 902int
 903kauth_cred_guid2uid(guid_t *guidp, uid_t *uidp)
 904{
 905        return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_UID, guidp, uidp));
 906}
 907
 908/*
 909 * Fetch GID from GUID.
 910 */
 911int
 912kauth_cred_guid2gid(guid_t *guidp, gid_t *gidp)
 913{
 914        return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_GID, guidp, gidp));
 915}
 916
 917/*
 918 * Fetch UID from NT SID.
 919 */
 920int
 921kauth_cred_ntsid2uid(ntsid_t *sidp, uid_t *uidp)
 922{
 923        return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_UID, sidp, uidp));
 924}
 925
 926/*
 927 * Fetch GID from NT SID.
 928 */
 929int
 930kauth_cred_ntsid2gid(ntsid_t *sidp, gid_t *gidp)
 931{
 932        return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GID, sidp, gidp));
 933}
 934
 935/*
 936 * Fetch GUID from NT SID.
 937 */
 938int
 939kauth_cred_ntsid2guid(ntsid_t *sidp, guid_t *guidp)
 940{
 941        return(kauth_cred_cache_lookup(KI_VALID_NTSID, KI_VALID_GUID, sidp, guidp));
 942}
 943
 944/*
 945 * Fetch GUID from UID.
 946 */
 947int
 948kauth_cred_uid2guid(uid_t uid, guid_t *guidp)
 949{
 950        return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_GUID, &uid, guidp));
 951}
 952
 953/*
 954 * Fetch user GUID from credential.
 955 */
 956int
 957kauth_cred_getguid(kauth_cred_t cred, guid_t *guidp)
 958{
 959        NULLCRED_CHECK(cred);
 960        return(kauth_cred_uid2guid(kauth_cred_getuid(cred), guidp));
 961}
 962
 963/*
 964 * Fetch GUID from GID.
 965 */
 966int
 967kauth_cred_gid2guid(gid_t gid, guid_t *guidp)
 968{
 969        return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_GUID, &gid, guidp));
 970}
 971
 972/*
 973 * Fetch NT SID from UID.
 974 */
 975int
 976kauth_cred_uid2ntsid(uid_t uid, ntsid_t *sidp)
 977{
 978        return(kauth_cred_cache_lookup(KI_VALID_UID, KI_VALID_NTSID, &uid, sidp));
 979}
 980
 981/*
 982 * Fetch NT SID from credential.
 983 */
 984int
 985kauth_cred_getntsid(kauth_cred_t cred, ntsid_t *sidp)
 986{
 987        NULLCRED_CHECK(cred);
 988        return(kauth_cred_uid2ntsid(kauth_cred_getuid(cred), sidp));
 989}
 990
 991/*
 992 * Fetch NT SID from GID.
 993 */
 994int
 995kauth_cred_gid2ntsid(gid_t gid, ntsid_t *sidp)
 996{
 997        return(kauth_cred_cache_lookup(KI_VALID_GID, KI_VALID_NTSID, &gid, sidp));
 998}
 999
1000/*
1001 * Fetch NT SID from GUID.
1002 */
1003int
1004kauth_cred_guid2ntsid(guid_t *guidp, ntsid_t *sidp)
1005{
1006        return(kauth_cred_cache_lookup(KI_VALID_GUID, KI_VALID_NTSID, guidp, sidp));
1007}
1008
1009
1010
1011/*
1012 * Lookup a translation in the cache.
1013 */
1014static int
1015kauth_cred_cache_lookup(int from, int to, void *src, void *dst)
1016{
1017        struct kauth_identity ki;
1018        struct kauth_identity_extlookup el;
1019        int error;
1020        int (* expired)(struct kauth_identity *kip);
1021
1022        KAUTH_DEBUG("CACHE - translate %d to %d", from, to);
1023        
1024        /*
1025         * Look for an existing cache entry for this association.
1026         * If the entry has not expired, return the cached information.
1027         */
1028        ki.ki_valid = 0;
1029        switch(from) {
1030        case KI_VALID_UID:
1031                error = kauth_identity_find_uid(*(uid_t *)src, &ki);
1032                break;
1033        case KI_VALID_GID:
1034                error = kauth_identity_find_gid(*(gid_t *)src, &ki);
1035                break;
1036        case KI_VALID_GUID:
1037                error = kauth_identity_find_guid((guid_t *)src, &ki);
1038                break;
1039        case KI_VALID_NTSID:
1040                error = kauth_identity_find_ntsid((ntsid_t *)src, &ki);
1041                break;
1042        default:
1043                return(EINVAL);
1044        }
1045        /* lookup failure or error */
1046        if (error != 0) {
1047                /* any other error is fatal */
1048                if (error != ENOENT) {
1049                        KAUTH_DEBUG("CACHE - cache search error %d", error);
1050                        return(error);
1051                }
1052        } else {
1053                /* do we have a translation? */
1054                if (ki.ki_valid & to) {
1055                        /* found a valid cached entry, check expiry */
1056                        switch(to) {
1057                        case KI_VALID_GUID:
1058                                expired = kauth_identity_guid_expired;
1059                                break;
1060                        case KI_VALID_NTSID:
1061                                expired = kauth_identity_ntsid_expired;
1062                                break;
1063                        default:
1064                                switch(from) {
1065                                case KI_VALID_GUID:
1066                                        expired = kauth_identity_guid_expired;
1067                                        break;
1068                                case KI_VALID_NTSID:
1069                                        expired = kauth_identity_ntsid_expired;
1070                                        break;
1071                                default:
1072                                        expired = NULL;
1073                                }
1074                        }
1075                        KAUTH_DEBUG("CACHE - found matching entry with valid %d", ki.ki_valid);
1076                        /*
1077                         * If no expiry function, or not expired, we have found
1078                         * a hit.
1079                         */
1080                        if (!expired) {
1081                                KAUTH_DEBUG("CACHE - no expiry function");
1082                                goto found;
1083                        }
1084                        if (!expired(&ki)) {
1085                                KAUTH_DEBUG("CACHE - entry valid, unexpired");
1086                                goto found;
1087                        }
1088                        /*
1089                         * We leave ki_valid set here; it contains a translation but the TTL has
1090                         * expired.  If we can't get a result from the resolver, we will
1091                         * use it as a better-than nothing alternative.
1092                         */
1093                        KAUTH_DEBUG("CACHE - expired entry found");
1094                }
1095        }
1096
1097        /*
1098         * Call the resolver.  We ask for as much data as we can get.
1099         */
1100        switch(from) {
1101        case KI_VALID_UID:
1102                el.el_flags = KAUTH_EXTLOOKUP_VALID_UID;
1103                el.el_uid = *(uid_t *)src;
1104                break;
1105        case KI_VALID_GID:
1106                el.el_flags = KAUTH_EXTLOOKUP_VALID_GID;
1107                el.el_gid = *(gid_t *)src;
1108                break;
1109        case KI_VALID_GUID:
1110                el.el_flags = KAUTH_EXTLOOKUP_VALID_UGUID | KAUTH_EXTLOOKUP_VALID_GGUID;
1111                el.el_uguid = *(guid_t *)src;
1112                el.el_gguid = *(guid_t *)src;
1113                break;
1114        case KI_VALID_NTSID:
1115                el.el_flags = KAUTH_EXTLOOKUP_VALID_USID | KAUTH_EXTLOOKUP_VALID_GSID;
1116                el.el_usid = *(ntsid_t *)src;
1117                el.el_gsid = *(ntsid_t *)src;
1118                break;
1119        default:
1120                return(EINVAL);
1121        }
1122        /*
1123         * Here we ask for everything all at once, to avoid having to work
1124         * out what we really want now, or might want soon.
1125         *
1126         * Asking for SID translations when we don't know we need them right
1127         * now is going to cause excess work to be done if we're connected
1128         * to a network that thinks it can translate them.  This list needs
1129         * to get smaller/smarter.
1130         */
1131        el.el_flags |= KAUTH_EXTLOOKUP_WANT_UID | KAUTH_EXTLOOKUP_WANT_GID |
1132            KAUTH_EXTLOOKUP_WANT_UGUID | KAUTH_EXTLOOKUP_WANT_GGUID |
1133            KAUTH_EXTLOOKUP_WANT_USID | KAUTH_EXTLOOKUP_WANT_GSID;
1134        KAUTH_DEBUG("CACHE - calling resolver for %x", el.el_flags);
1135        error = kauth_identity_resolve(&el);
1136        KAUTH_DEBUG("CACHE - resolver returned %d", error);
1137        /* was the lookup successful? */
1138        if (error == 0) {
1139                /*
1140                 * Save the results from the lookup - may have other information even if we didn't
1141                 * get a guid.
1142                 */
1143                kauth_identity_updatecache(&el, &ki);
1144        }
1145        /*
1146         * Check to see if we have a valid result.
1147         */
1148        if (!error && !(ki.ki_valid & to))
1149                error = ENOENT;
1150        if (error)
1151                return(error);
1152found:
1153        switch(to) {
1154        case KI_VALID_UID:
1155                *(uid_t *)dst = ki.ki_uid;
1156                break;
1157        case KI_VALID_GID:
1158                *(gid_t *)dst = ki.ki_gid;
1159                break;
1160        case KI_VALID_GUID:
1161                *(guid_t *)dst = ki.ki_guid;
1162                break;
1163        case KI_VALID_NTSID:
1164                *(ntsid_t *)dst = ki.ki_ntsid;
1165                break;
1166        default:
1167                return(EINVAL);
1168        }
1169        KAUTH_DEBUG("CACHE - returned successfully");
1170        return(0);
1171}
1172
1173
1174/*
1175 * Group membership cache.
1176 *
1177 * XXX the linked-list implementation here needs to be optimized.
1178 */
1179
1180struct kauth_group_membership {
1181        TAILQ_ENTRY(kauth_group_membership) gm_link;
1182        uid_t   gm_uid;         /* the identity whose membership we're recording */
1183        gid_t   gm_gid;         /* group of which they are a member */
1184        time_t  gm_expiry;      /* TTL for the membership */
1185        int     gm_flags;
1186#define KAUTH_GROUP_ISMEMBER    (1<<0)
1187};
1188
1189TAILQ_HEAD(kauth_groups_head, kauth_group_membership) kauth_groups;
1190#define KAUTH_GROUPS_CACHEMAX           100     /* XXX sizing? */
1191static int kauth_groups_count;
1192
1193static lck_mtx_t *kauth_groups_mtx;
1194#define KAUTH_GROUPS_LOCK()     lck_mtx_lock(kauth_groups_mtx);
1195#define KAUTH_GROUPS_UNLOCK()   lck_mtx_unlock(kauth_groups_mtx);
1196
1197static int      kauth_groups_expired(struct kauth_group_membership *gm);
1198static void     kauth_groups_lru(struct kauth_group_membership *gm);
1199static void     kauth_groups_updatecache(struct kauth_identity_extlookup *el);
1200
1201void
1202kauth_groups_init(void)
1203{
1204        TAILQ_INIT(&kauth_groups);
1205        kauth_groups_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
1206}
1207
1208static int
1209kauth_groups_expired(struct kauth_group_membership *gm)
1210{
1211        struct timeval tv;
1212
1213        microuptime(&tv);
1214        return((gm->gm_expiry <= tv.tv_sec) ? 1 : 0);
1215}
1216
1217static void
1218kauth_groups_lru(struct kauth_group_membership *gm)
1219{
1220        if (gm != TAILQ_FIRST(&kauth_groups)) {
1221                TAILQ_REMOVE(&kauth_groups, gm, gm_link);
1222                TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
1223        }
1224}
1225
1226static void
1227kauth_groups_updatecache(struct kauth_identity_extlookup *el)
1228{
1229        struct kauth_group_membership *gm;
1230        struct timeval tv;
1231        
1232        /* need a valid response if we are to cache anything */
1233        if ((el->el_flags &
1234                (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP)) !=
1235            (KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_VALID_MEMBERSHIP))
1236                return;
1237
1238        microuptime(&tv);
1239
1240        /* search for an existing record for this association before inserting */
1241        KAUTH_GROUPS_LOCK();
1242        TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
1243                if ((el->el_uid == gm->gm_uid) &&
1244                    (el->el_gid == gm->gm_gid)) {
1245                        if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
1246                                gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
1247                        } else {
1248                                gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
1249                        }
1250                        gm->gm_expiry = el->el_member_valid + tv.tv_sec;
1251                        kauth_groups_lru(gm);
1252                        break;
1253                }
1254        }
1255        KAUTH_GROUPS_UNLOCK();
1256
1257        /* if we found an entry to update, stop here */
1258        if (gm != NULL)
1259                return;
1260
1261        /* allocate a new record */
1262        MALLOC(gm, struct kauth_group_membership *, sizeof(*gm), M_KAUTH, M_WAITOK);
1263        if (gm != NULL) {
1264                gm->gm_uid = el->el_uid;
1265                gm->gm_gid = el->el_gid;
1266                if (el->el_flags & KAUTH_EXTLOOKUP_ISMEMBER) {
1267                        gm->gm_flags |= KAUTH_GROUP_ISMEMBER;
1268                } else {
1269                        gm->gm_flags &= ~KAUTH_GROUP_ISMEMBER;
1270                }
1271                gm->gm_expiry = el->el_member_valid + tv.tv_sec;
1272        }               
1273
1274        /*
1275         * Insert the new entry.  Note that it's possible to race ourselves here
1276         * and end up with duplicate entries in the list.  Wasteful, but harmless
1277         * since the first into the list will never be looked up, and thus will
1278         * eventually just fall off the end.
1279         */
1280        KAUTH_GROUPS_LOCK();
1281        TAILQ_INSERT_HEAD(&kauth_groups, gm, gm_link);
1282        if (kauth_groups_count++ > KAUTH_GROUPS_CACHEMAX) {
1283                gm = TAILQ_LAST(&kauth_groups, kauth_groups_head);
1284                TAILQ_REMOVE(&kauth_groups, gm, gm_link);
1285                kauth_groups_count--;
1286        } else {
1287                gm = NULL;
1288        }
1289        KAUTH_GROUPS_UNLOCK();
1290
1291        /* free expired cache entry */
1292        if (gm != NULL)
1293                FREE(gm, M_KAUTH);
1294}
1295
1296/*
1297 * Group membership KPI
1298 */
1299/*
1300 * This function guarantees not to modify resultp when returning an error.
1301 */
1302int
1303kauth_cred_ismember_gid(kauth_cred_t cred, gid_t gid, int *resultp)
1304{
1305        struct kauth_group_membership *gm;
1306        struct kauth_identity_extlookup el;
1307        int i, error;
1308
1309        /*
1310         * Check the per-credential list of override groups.
1311         *
1312         * We can conditionalise this on cred->cr_gmuid == KAUTH_UID_NONE since
1313         * the cache should be used for that case.
1314         */
1315        for (i = 0; i < cred->cr_ngroups; i++) {
1316                if (gid == cred->cr_groups[i]) {
1317                        *resultp = 1;
1318                        return(0);
1319                }
1320        }
1321
1322        /*
1323         * If we don't have a UID for group membership checks, the in-cred list
1324         * was authoritative and we can stop here.
1325         */
1326        if (cred->cr_gmuid == KAUTH_UID_NONE) {
1327                *resultp = 0;
1328                return(0);
1329        }
1330                
1331        
1332        /*
1333         * If the resolver hasn't checked in yet, we are early in the boot phase and
1334         * the local group list is complete and authoritative.
1335         */
1336        if (!kauth_resolver_registered) {
1337                *resultp = 0;
1338                return(0);
1339        }
1340        
1341        /* TODO: */
1342        /* XXX check supplementary groups */
1343        /* XXX check whiteout groups */
1344        /* XXX nesting of supplementary/whiteout groups? */
1345
1346        /*
1347         * Check the group cache.
1348         */
1349        KAUTH_GROUPS_LOCK();
1350        TAILQ_FOREACH(gm, &kauth_groups, gm_link) {
1351                if ((gm->gm_uid == cred->cr_gmuid) && (gm->gm_gid == gid) && !kauth_groups_expired(gm)) {
1352                        kauth_groups_lru(gm);
1353                        break;
1354                }
1355        }
1356
1357        /* did we find a membership entry? */
1358        if (gm != NULL)
1359                *resultp = (gm->gm_flags & KAUTH_GROUP_ISMEMBER) ? 1 : 0;
1360        KAUTH_GROUPS_UNLOCK();
1361
1362        /* if we did, we can return now */
1363        if (gm != NULL)
1364                return(0);
1365        
1366        /* nothing in the cache, need to go to userland */
1367        el.el_flags = KAUTH_EXTLOOKUP_VALID_UID | KAUTH_EXTLOOKUP_VALID_GID | KAUTH_EXTLOOKUP_WANT_MEMBERSHIP;
1368        el.el_uid = cred->cr_gmuid;
1369        el.el_gid = gid;
1370        error = kauth_identity_resolve(&el);
1371        if (error != 0)
1372                return(error);
1373        /* save the results from the lookup */
1374        kauth_groups_updatecache(&el);
1375
1376        /* if we successfully ascertained membership, report */
1377        if (el.el_flags & KAUTH_EXTLOOKUP_VALID_MEMBERSHIP) {
1378                *resultp = (el.el_flags & KAUTH_EXTLOOKUP_ISMEMBER) ? 1 : 0;
1379                return(0);
1380        }
1381
1382        return(ENOENT);
1383}
1384
1385/*
1386 * Determine whether the supplied credential is a member of the
1387 * group nominated by GUID.
1388 */
1389int
1390kauth_cred_ismember_guid(kauth_cred_t cred, guid_t *guidp, int *resultp)
1391{
1392        gid_t gid;
1393        int error, wkg;
1394
1395        error = 0;
1396        wkg = kauth_wellknown_guid(guidp);
1397        switch(wkg) {
1398        case KAUTH_WKG_NOBODY:
1399                *resultp = 0;
1400                break;
1401        case KAUTH_WKG_EVERYBODY:
1402                *resultp = 1;
1403                break;
1404        default:
1405                /* translate guid to gid */
1406                if ((error = kauth_cred_guid2gid(guidp, &gid)) != 0) {
1407                        /*
1408                         * If we have no guid -> gid translation, it's not a group and
1409                         * thus the cred can't be a member.
1410                         */
1411                        if (error == ENOENT) {
1412                                *resultp = 0;
1413                                error = 0;
1414                        }
1415                } else {
1416                        error = kauth_cred_ismember_gid(cred, gid, resultp);
1417                }
1418        }
1419        return(error);
1420}
1421
1422/*
1423 * Fast replacement for issuser()
1424 */
1425int
1426kauth_cred_issuser(kauth_cred_t cred)
1427{
1428        return(cred->cr_uid == 0);
1429}
1430
1431/*
1432 * Credential KPI
1433 */
1434
1435/* lock protecting credential hash table */
1436static lck_mtx_t *kauth_cred_hash_mtx;
1437#define KAUTH_CRED_HASH_LOCK()          lck_mtx_lock(kauth_cred_hash_mtx);
1438#define KAUTH_CRED_HASH_UNLOCK()        lck_mtx_unlock(kauth_cred_hash_mtx);
1439
1440void
1441kauth_cred_init(void)
1442{
1443        int             i;
1444        
1445        kauth_cred_hash_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0/*LCK_ATTR_NULL*/);
1446        kauth_cred_table_size = kauth_cred_primes[kauth_cred_primes_index];
1447
1448        /*allocate credential hash table */
1449        MALLOC(kauth_cred_table_anchor, struct kauth_cred_entry_head *, 
1450                        (sizeof(struct kauth_cred_entry_head) * kauth_cred_table_size), 
1451                        M_KAUTH, M_WAITOK | M_ZERO);
1452        for (i = 0; i < kauth_cred_table_size; i++) {
1453                TAILQ_INIT(&kauth_cred_table_anchor[i]);
1454        }
1455}
1456
1457/*
1458 * Return the current thread's effective UID.
1459 */
1460uid_t
1461kauth_getuid(void)
1462{
1463        return(kauth_cred_get()->cr_uid);
1464}
1465
1466/*
1467 * Return the current thread's real UID.
1468 */
1469uid_t
1470kauth_getruid(void)
1471{
1472        return(kauth_cred_get()->cr_ruid);
1473}
1474
1475/*
1476 * Return the current thread's effective GID.
1477 */
1478gid_t
1479kauth_getgid(void)
1480{
1481        return(kauth_cred_get()->cr_groups[0]);
1482}
1483
1484/*
1485 * Return the current thread's real GID.
1486 */
1487gid_t
1488kauth_getrgid(void)
1489{
1490        return(kauth_cred_get()->cr_rgid);
1491}
1492
1493/*
1494 * Returns a pointer to the current thread's credential, does not take a
1495 * reference (so the caller must not do anything that would let the thread's
1496 * credential change while using the returned value).
1497 */
1498kauth_cred_t
1499kauth_cred_get(void)
1500{
1501        struct proc *p;
1502        struct uthread *uthread;
1503
1504        uthread = get_bsdthread_info(current_thread());
1505        /* sanity */
1506        if (uthread == NULL)
1507                panic("thread wants credential but has no BSD thread info");
1508        /*
1509         * We can lazy-bind credentials to threads, as long as their processes have them.
1510         * If we later inline this function, the code in this block should probably be
1511         * called out in a function.
1512         */
1513        if (uthread->uu_ucred == NOCRED) {
1514                if ((p = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
1515                        panic("thread wants credential but has no BSD process");
1516                proc_lock(p);
1517                kauth_cred_ref(uthread->uu_ucred = p->p_ucred);
1518                proc_unlock(p);
1519        }
1520        return(uthread->uu_ucred);
1521}
1522
1523/*
1524 * Returns a pointer to the current thread's credential, takes a reference.
1525 */
1526kauth_cred_t
1527kauth_cred_get_with_ref(void)
1528{
1529        struct proc *procp;
1530        struct uthread *uthread;
1531
1532        uthread = get_bsdthread_info(current_thread());
1533        /* sanity checks */
1534        if (uthread == NULL)
1535                panic("%s - thread wants credential but has no BSD thread info", __FUNCTION__);
1536        if ((procp = (proc_t) get_bsdtask_info(get_threadtask(current_thread()))) == NULL)
1537                panic("%s - thread wants credential but has no BSD process", __FUNCTION__);
1538
1539        /*
1540         * We can lazy-bind credentials to threads, as long as their processes have them.
1541         * If we later inline this function, the code in this block should probably be
1542         * called out in a function.
1543         */
1544        proc_lock(procp);
1545        if (uthread->uu_ucred == NOCRED) {
1546                /* take reference for new cred in thread */
1547                kauth_cred_ref(uthread->uu_ucred = proc_ucred(procp));
1548        }
1549        /* take a reference for our caller */
1550        kauth_cred_ref(uthread->uu_ucred);
1551        proc_unlock(procp);
1552        return(uthread->uu_ucred);
1553}
1554
1555/*
1556 * Returns a pointer to the given process's credential, takes a reference.
1557 */
1558kauth_cred_t
1559kauth_cred_proc_ref(proc_t procp)
1560{
1561        kauth_cred_t    cred;
1562        
1563        proc_lock(procp);
1564        cred = proc_ucred(procp);
1565        kauth_cred_ref(cred);
1566        proc_unlock(procp);
1567        return(cred);
1568}
1569
1570/*
1571 * Allocates a new credential.
1572 */
1573kauth_cred_t
1574kauth_cred_alloc(void)
1575{
1576        kauth_cred_t newcred;
1577        
1578        MALLOC(newcred, kauth_cred_t, sizeof(*newcred), M_KAUTH, M_WAITOK | M_ZERO);
1579        if (newcred != 0) {
1580                newcred->cr_ref = 1;
1581                /* must do this, or cred has same group membership as uid 0 */
1582                newcred->cr_gmuid = KAUTH_UID_NONE;
1583#if CRED_DIAGNOSTIC
1584        } else {
1585                panic("kauth_cred_alloc: couldn't allocate credential");
1586#endif          
1587        }
1588
1589#if KAUTH_CRED_HASH_DEBUG
1590        kauth_cred_count++;
1591#endif
1592
1593        return(newcred);
1594}
1595
1596/*
1597 * Looks to see if we already have a known credential and if found bumps the
1598 *      reference count and returns it.  If there are no credentials that match 
1599 *      the given credential then we allocate a new credential.
1600 *
1601 * Note that the gmuid is hard-defaulted to the UID specified.  Since we maintain
1602 * this field, we can't expect callers to know how it needs to be set.  Callers
1603 * should be prepared for this field to be overwritten.
1604 */
1605kauth_cred_t
1606kauth_cred_create(kauth_cred_t cred)
1607{
1608        kauth_cred_t    found_cred, new_cred = NULL;
1609
1610        cred->cr_gmuid = cred->cr_uid;
1611        
1612        for (;;) {
1613                KAUTH_CRED_HASH_LOCK();
1614                found_cred = kauth_cred_find(cred);
1615                if (found_cred != NULL) {
1616                        /* found an existing credential so we'll bump reference count and return */
1617                        kauth_cred_ref(found_cred);
1618                        KAUTH_CRED_HASH_UNLOCK();
1619                        return(found_cred);
1620                }
1621                KAUTH_CRED_HASH_UNLOCK();
1622        
1623                /* no existing credential found.  create one and add it to our hash table */
1624                new_cred = kauth_cred_alloc();
1625                if (new_cred != NULL) {
1626                        int             err;
1627                        new_cred->cr_uid = cred->cr_uid;
1628                        new_cred->cr_ruid = cred->cr_ruid;
1629                        new_cred->cr_svuid = cred->cr_svuid;
1630                        new_cred->cr_rgid = cred->cr_rgid;
1631                        new_cred->cr_svgid = cred->cr_svgid;
1632                        new_cred->cr_gmuid = cred->cr_gmuid;
1633                        new_cred->cr_ngroups = cred->cr_ngroups;        
1634                        bcopy(&cred->cr_groups[0], &new_cred->cr_groups[0], sizeof(new_cred->cr_groups));
1635                        KAUTH_CRED_HASH_LOCK();
1636                        err = kauth_cred_add(new_cred);
1637                        KAUTH_CRED_HASH_UNLOCK();
1638                        
1639                        /* retry if kauth_cred_add returns non zero value */
1640                        if (err == 0)
1641                                break;
1642                        FREE(new_cred, M_KAUTH);
1643                        new_cred = NULL;
1644                }
1645        }
1646
1647        return(new_cred);
1648}
1649
1650/*
1651 * Update the given credential using the uid argument.  The given uid is used
1652 *      set the effective user ID, real user ID, and saved user ID.  We only 
1653 *      allocate a new credential when the given uid actually results in changes to
1654 *      the existing credential.
1655 */
1656kauth_cred_t
1657kauth_cred_setuid(kauth_cred_t cred, uid_t uid)
1658{
1659        struct ucred temp_cred;
1660
1661        NULLCRED_CHECK(cred);
1662
1663        /* don't need to do anything if the effective, real and saved user IDs are
1664         * already the same as the user ID passed in
1665         */
1666        if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid) {
1667                /* no change needed */
1668                return(cred);
1669        }
1670
1671        /* look up in cred hash table to see if we have a matching credential
1672         * with new values.
1673         */
1674        bcopy(cred, &temp_cred, sizeof(temp_cred));
1675        temp_cred.cr_uid = uid;
1676        temp_cred.cr_ruid = uid;
1677        temp_cred.cr_svuid = uid;
1678        temp_cred.cr_gmuid = uid;
1679
1680        return(kauth_cred_update(cred, &temp_cred, TRUE));
1681}
1682
1683/*
1684 * Update the given credential using the euid argument.  The given uid is used
1685 *      set the effective user ID.  We only allocate a new credential when the given 
1686 *      uid actually results in changes to the existing credential.
1687 */
1688kauth_cred_t
1689kauth_cred_seteuid(kauth_cred_t cred, uid_t euid)
1690{
1691        struct ucred temp_cred;
1692
1693        NULLCRED_CHECK(cred);
1694
1695        /* don't need to do anything if the given effective user ID is already the 
1696         *      same as the effective user ID in the credential.
1697         */
1698        if (cred->cr_uid == euid) {
1699                /* no change needed */
1700                return(cred);
1701        }
1702
1703        /* look up in cred hash table to see if we have a matching credential
1704         * with new values.
1705         */
1706        bcopy(cred, &temp_cred, sizeof(temp_cred));
1707        temp_cred.cr_uid = euid;
1708
1709        return(kauth_cred_update(cred, &temp_cred, TRUE));
1710}
1711
1712/*
1713 * Update the given credential using the gid argument.  The given gid is used
1714 *      set the effective group ID, real group ID, and saved group ID.  We only 
1715 *      allocate a new credential when the given gid actually results in changes to
1716 *      the existing credential.
1717 */
1718kauth_cred_t
1719kauth_cred_setgid(kauth_cred_t cred, gid_t gid)
1720{
1721        struct ucred    temp_cred;
1722
1723        NULLCRED_CHECK(cred);
1724
1725        /* don't need to do anything if the given group ID is already the 
1726         *      same as the group ID in the credential.
1727         */
1728        if (cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
1729                /* no change needed */
1730                return(cred);
1731        }
1732
1733        /* look up in cred hash table to see if we have a matching credential
1734         * with new values.
1735         */
1736        bcopy(cred, &temp_cred, sizeof(temp_cred));
1737        temp_cred.cr_groups[0] = gid;
1738        temp_cred.cr_rgid = gid;
1739        temp_cred.cr_svgid = gid;
1740
1741        return(kauth_cred_update(cred, &temp_cred, TRUE));
1742}
1743
1744/*
1745 * Update the given credential using the egid argument.  The given gid is used
1746 *      set the effective user ID.  We only allocate a new credential when the given 
1747 *      gid actually results in changes to the existing credential.
1748 */
1749kauth_cred_t
1750kauth_cred_setegid(kauth_cred_t cred, gid_t egid)
1751{
1752        struct ucred temp_cred;
1753
1754        NULLCRED_CHECK(cred);
1755
1756        /* don't need to do anything if the given group ID is already the 
1757         *      same as the group Id in the credential.
1758         */
1759        if (cred->cr_groups[0] == egid) {
1760                /* no change needed */
1761                return(cred);
1762        }
1763
1764        /* look up in cred hash table to see if we have a matching credential
1765         * with new values.
1766         */
1767        bcopy(cred, &temp_cred, sizeof(temp_cred));
1768        temp_cred.cr_groups[0] = egid;
1769
1770        return(kauth_cred_update(cred, &temp_cred, TRUE));
1771}
1772
1773/*
1774 * Update the given credential with the given groups.  We only allocate a new 
1775 *      credential when the given gid actually results in changes to the existing 
1776 *      credential.
1777 *      The gmuid argument supplies a new uid (or KAUTH_UID_NONE to opt out)
1778 *      which will be used for group membership checking.
1779 */
1780kauth_cred_t
1781kauth_cred_setgroups(kauth_cred_t cred, gid_t *groups, int groupcount, uid_t gmuid)
1782{
1783        int             i;
1784        struct ucred temp_cred;
1785
1786        NULLCRED_CHECK(cred);
1787
1788        /* don't need to do anything if the given list of groups does not change.
1789         */
1790        if ((cred->cr_gmuid == gmuid) && (cred->cr_ngroups == groupcount)) {
1791                for (i = 0; i < groupcount; i++) {
1792                        if (cred->cr_groups[i] != groups[i])
1793                                break;
1794                }
1795                if (i == groupcount) {
1796                        /* no change needed */
1797                        return(cred);
1798                }
1799        }
1800
1801        /* look up in cred hash table to see if we have a matching credential
1802         * with new values.
1803         */
1804        bcopy(cred, &temp_cred, sizeof(temp_cred));
1805        temp_cred.cr_ngroups = groupcount;
1806        bcopy(groups, temp_cred.cr_groups, sizeof(temp_cred.cr_groups));
1807        temp_cred.cr_gmuid = gmuid;
1808
1809        return(kauth_cred_update(cred, &temp_cred, TRUE));
1810}
1811
1812/*
1813 * Update the given credential using the uid and gid arguments.  The given uid 
1814 *      is used set the effective user ID, real user ID, and saved user ID.  
1815 *      The given gid is used set the effective group ID, real group ID, and saved 
1816 *      group ID.
1817 *      We only allocate a new credential when the given uid and gid actually results 
1818 *      in changes to the existing credential.
1819 */
1820kauth_cred_t
1821kauth_cred_setuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
1822{
1823        struct ucred temp_cred;
1824
1825        NULLCRED_CHECK(cred);
1826
1827        /* don't need to do anything if the effective, real and saved user IDs are
1828         * already the same as the user ID passed in
1829         */
1830        if (cred->cr_uid == uid && cred->cr_ruid == uid && cred->cr_svuid == uid &&
1831                cred->cr_groups[0] == gid && cred->cr_rgid == gid && cred->cr_svgid == gid) {
1832                /* no change needed */
1833                return(cred);
1834        }
1835
1836        /* look up in cred hash table to see if we have a matching credential
1837         * with new values.
1838         */
1839        bzero(&temp_cred, sizeof(temp_cred));
1840        temp_cred.cr_uid = uid;
1841        temp_cred.cr_ruid = uid;
1842        temp_cred.cr_svuid = uid;
1843        temp_cred.cr_gmuid = uid;
1844        temp_cred.cr_ngroups = 1;
1845        temp_cred.cr_groups[0] = gid;
1846        temp_cred.cr_rgid = gid;
1847        temp_cred.cr_svgid = gid;
1848
1849        return(kauth_cred_update(cred, &temp_cred, TRUE));
1850}
1851
1852/*
1853 * Update the given credential using the uid and gid arguments.  The given uid 
1854 *      is used to set the saved user ID.  The given gid is used to set the 
1855 *      saved group ID.
1856 *      We only allocate a new credential when the given uid and gid actually results 
1857 *      in changes to the existing credential.
1858 */
1859kauth_cred_t
1860kauth_cred_setsvuidgid(kauth_cred_t cred, uid_t uid, gid_t gid)
1861{
1862        struct ucred temp_cred;
1863
1864        NULLCRED_CHECK(cred);
1865
1866        /* don't need to do anything if the effective, real and saved user IDs are
1867         * already the same as the user ID passed in
1868         */
1869        if (cred->cr_svuid == uid && cred->cr_svgid == gid) {
1870                /* no change needed */
1871                return(cred);
1872        }
1873
1874        /* look up in cred hash table to see if we have a matching credential
1875         * with new values.
1876         */
1877        bcopy(cred, &temp_cred, sizeof(temp_cred));
1878        temp_cred.cr_svuid = uid;
1879        temp_cred.cr_svgid = gid;
1880
1881        return(kauth_cred_update(cred, &temp_cred, TRUE));
1882}
1883
1884/*
1885 * Update the given credential using the given auditinfo_t.
1886 *      We only allocate a new credential when the given auditinfo_t actually results 
1887 *      in changes to the existing credential.
1888 */
1889kauth_cred_t
1890kauth_cred_setauditinfo(kauth_cred_t cred, auditinfo_t *auditinfo_p)
1891{
1892        struct ucred temp_cred;
1893
1894        NULLCRED_CHECK(cred);
1895
1896        /* don't need to do anything if the audit info is already the same as the 
1897         * audit info in the credential passed in
1898         */
1899        if (bcmp(&cred->cr_au, auditinfo_p, sizeof(cred->cr_au)) == 0) {
1900                /* no change needed */
1901                return(cred);
1902        }
1903
1904        /* look up in cred hash table to see if we have a matching credential
1905         * with new values.
1906         */
1907        bcopy(cred, &temp_cred, sizeof(temp_cred));
1908        bcopy(auditinfo_p, &temp_cred.cr_au, sizeof(temp_cred.cr_au));
1909
1910        return(kauth_cred_update(cred, &temp_cred, FALSE));
1911}
1912
1913/*
1914 * Add a reference to the passed credential.
1915 */
1916void
1917kauth_cred_ref(kauth_cred_t cred)
1918{
1919        int             old_value;
1920        
1921        NULLCRED_CHECK(cred);
1922
1923        old_value = OSAddAtomic(1, &cred->cr_ref);
1924
1925        if (old_value < 1)
1926                panic("kauth_cred_ref: trying to take a reference on a cred with no references");
1927                
1928        return;
1929}
1930
1931/*
1932 * Drop a reference from the passed credential, potentially destroying it.
1933 */
1934void
1935kauth_cred_rele(kauth_cred_t cred)
1936{
1937        int             old_value;
1938
1939        NULLCRED_CHECK(cred);
1940
1941        KAUTH_CRED_HASH_LOCK();
1942        old_value = OSAddAtomic(-1, &cred->cr_ref);
1943
1944#if DIAGNOSTIC
1945        if (old_value == 0)
1946                panic("kauth_cred_rele: dropping a reference on a cred with no references");
1947#endif
1948
1949        if (old_value < 3) {
1950                /* the last reference is our credential hash table */
1951                kauth_cred_remove(cred);
1952        }
1953        KAUTH_CRED_HASH_UNLOCK();
1954}
1955
1956/*
1957 * Duplicate a credential.
1958 *      NOTE - caller should call kauth_cred_add after any credential changes are made.
1959 */
1960kauth_cred_t
1961kauth_cred_dup(kauth_cred_t cred)
1962{
1963        kauth_cred_t newcred;
1964        
1965#if CRED_DIAGNOSTIC
1966        if (cred == NOCRED || cred == FSCRED)
1967                panic("kauth_cred_dup: bad credential");
1968#endif
1969        newcred = kauth_cred_alloc();
1970        if (newcred != NULL) {
1971                bcopy(cred, newcred, sizeof(*newcred));
1972                newcred->cr_ref = 1;
1973        }
1974        return(newcred);
1975}
1976
1977/*
1978 * Returns a credential based on the passed credential but which
1979 * reflects the real rather than effective UID and GID.
1980 * NOTE - we do NOT decrement cred reference count on passed in credential
1981 */
1982kauth_cred_t
1983kauth_cred_copy_real(kauth_cred_t cred)
1984{
1985        kauth_cred_t newcred = NULL, found_cred;
1986        struct ucred temp_cred;
1987
1988        /* if the credential is already 'real', just take a reference */
1989        if ((cred->cr_ruid == cred->cr_uid) &&
1990            (cred->cr_rgid == cred->cr_gid)) {
1991                kauth_cred_ref(cred);
1992                return(cred);
1993        }
1994
1995        /* look up in cred hash table to see if we have a matching credential
1996         * with new values.
1997         */
1998        bcopy(cred, &temp_cred, sizeof(temp_cred));
1999        temp_cred.cr_uid = cred->cr_ruid;
2000        temp_cred.cr_groups[0] = cred->cr_rgid;
2001        /* if the cred is not opted out, make sure we are using the r/euid for group checks */
2002        if (temp_cred.cr_gmuid != KAUTH_UID_NONE)
2003                temp_cred.cr_gmuid = cred->cr_ruid;
2004
2005        for (;;) {
2006                int             err;
2007                
2008                KAUTH_CRED_HASH_LOCK();
2009                found_cred = kauth_cred_find(&temp_cred);
2010                if (found_cred == cred) {
2011                        /* same cred so just bail */
2012                        KAUTH_CRED_HASH_UNLOCK();
2013                        return(cred); 
2014                }
2015                if (found_cred != NULL) {
2016                        /* found a match so we bump reference count on new one and decrement 
2017                         * reference count on the old one.
2018                         */
2019                        kauth_cred_ref(found_cred);
2020                        KAUTH_CRED_HASH_UNLOCK();
2021                        return(found_cred);
2022                }
2023        
2024                /* must allocate a new credential, copy in old credential data and update
2025                 * with real user and group IDs.
2026                 */
2027                newcred = kauth_cred_dup(&temp_cred);
2028                err = kauth_cred_add(newcred);
2029                KAUTH_CRED_HASH_UNLOCK();
2030
2031                /* retry if kauth_cred_add returns non zero value */
2032                if (err == 0)
2033                        break;
2034                FREE(newcred, M_KAUTH);
2035                newcred = NULL;
2036        }
2037        
2038        return(newcred);
2039}
2040        
2041/*
2042 * common code to update a credential.  model_cred is a temporary, non reference
2043 * counted credential used only for comparison and modeling purposes.  old_cred
2044 * is a live reference counted credential that we intend to update using model_cred
2045 * as our model.
2046 */
2047static kauth_cred_t kauth_cred_update(kauth_cred_t old_cred, kauth_cred_t model_cred, boolean_t retain_auditinfo)
2048{       
2049        kauth_cred_t found_cred, new_cred = NULL;
2050        
2051        /* make sure we carry the auditinfo forward to the new credential unless
2052         * we are actually updating the auditinfo.
2053         */
2054        if (retain_auditinfo)
2055                bcopy(&old_cred->cr_au, &model_cred->cr_au, sizeof(model_cred->cr_au));
2056        
2057        for (;;) {
2058                int             err;
2059
2060                KAUTH_CRED_HASH_LOCK();
2061                found_cred = kauth_cred_find(model_cred);
2062                if (found_cred == old_cred) {
2063                        /* same cred so just bail */
2064                        KAUTH_CRED_HASH_UNLOCK();
2065                        return(old_cred); 
2066                }
2067                if (found_cred != NULL) {
2068                        /* found a match so we bump reference count on new one and decrement 
2069                         * reference count on the old one.
2070                         */
2071                        kauth_cred_ref(found_cred);
2072                        KAUTH_CRED_HASH_UNLOCK();
2073                        kauth_cred_rele(old_cred);
2074                        return(found_cred);
2075                }
2076        
2077                /* must allocate a new credential using the model.  also
2078                 * adds the new credential to the credential hash table.
2079                 */
2080                new_cred = kauth_cred_dup(model_cred);
2081                err = kauth_cred_add(new_cred);
2082                KAUTH_CRED_HASH_UNLOCK();
2083
2084                /* retry if kauth_cred_add returns non zero value */
2085                if (err == 0)
2086                        break;
2087                FREE(new_cred, M_KAUTH);
2088                new_cred = NULL;
2089        }
2090
2091        kauth_cred_rele(old_cred);
2092        return(new_cred);
2093}
2094
2095/* 
2096 *      Add the given credential to our credential hash table and take an additional
2097 *      reference to account for our use of the credential in the hash table.
2098 *      NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2099 */
2100static int kauth_cred_add(kauth_cred_t new_cred)
2101{
2102        u_long                  hash_key;
2103        
2104        hash_key = kauth_cred_get_hashkey(new_cred);
2105        hash_key %= kauth_cred_table_size;
2106
2107        /* race fix - there is a window where another matching credential 
2108         * could have been inserted between the time this one was created and we
2109         * got the hash lock.  If we find a match return an error and have the 
2110         * the caller retry.
2111         */
2112        if (kauth_cred_find(new_cred) != NULL) {
2113                return(-1);
2114        }
2115        
2116        /* take a reference for our use in credential hash table */ 
2117        kauth_cred_ref(new_cred);
2118
2119        /* insert the credential into the hash table */
2120        TAILQ_INSERT_HEAD(&kauth_cred_table_anchor[hash_key], new_cred, cr_link);
2121        
2122        return(0);
2123}
2124
2125/* 
2126 *      Remove the given credential from our credential hash table.
2127 *      NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2128 */
2129static void kauth_cred_remove(kauth_cred_t cred)
2130{
2131        u_long                  hash_key;
2132        kauth_cred_t    found_cred;
2133
2134        hash_key = kauth_cred_get_hashkey(cred);
2135        hash_key %= kauth_cred_table_size;
2136
2137        /* avoid race */
2138        if (cred->cr_ref < 1)
2139                panic("cred reference underflow");
2140        if (cred->cr_ref > 1)
2141                return;         /* someone else got a ref */
2142                
2143        /* find cred in the credential hash table */
2144        TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
2145                if (found_cred == cred) {
2146                        /* found a match, remove it from the hash table */
2147                        TAILQ_REMOVE(&kauth_cred_table_anchor[hash_key], found_cred, cr_link);
2148                        FREE(cred, M_KAUTH);
2149#if KAUTH_CRED_HASH_DEBUG
2150                        kauth_cred_count--;
2151#endif
2152                        return;
2153                }
2154        }
2155
2156        /* did not find a match.  this should not happen! */
2157        printf("%s - %d - %s - did not find a match \n", __FILE__, __LINE__, __FUNCTION__);
2158        return;
2159}
2160
2161/* 
2162 *      Using the given credential data, look for a match in our credential hash
2163 *      table.
2164 *      NOTE - expects caller to hold KAUTH_CRED_HASH_LOCK!
2165 */
2166kauth_cred_t kauth_cred_find(kauth_cred_t cred)
2167{
2168        u_long                  hash_key;
2169        kauth_cred_t    found_cred;
2170        
2171#if KAUTH_CRED_HASH_DEBUG
2172        static int              test_count = 0; 
2173
2174        test_count++;
2175        if ((test_count % 200) == 0) {
2176                kauth_cred_hash_print();
2177        }
2178#endif
2179
2180        hash_key = kauth_cred_get_hashkey(cred);
2181        hash_key %= kauth_cred_table_size;
2182
2183        /* find cred in the credential hash table */
2184        TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[hash_key], cr_link) {
2185                if (bcmp(&found_cred->cr_uid, &cred->cr_uid, (sizeof(struct ucred) - offsetof(struct ucred, cr_uid))) == 0) {
2186                        /* found a match */
2187                        return(found_cred);
2188                }
2189        }
2190        /* no match found */
2191        return(NULL);
2192}
2193
2194/*
2195 * Generates a hash key using data that makes up a credential.  Based on ElfHash.
2196 */
2197static u_long kauth_cred_get_hashkey(kauth_cred_t cred)
2198{
2199        u_long  hash_key = 0;
2200        
2201        hash_key = kauth_cred_hash((uint8_t *)&cred->cr_uid, 
2202                                                           (sizeof(struct ucred) - offsetof(struct ucred, cr_uid)), 
2203                                                           hash_key);
2204        return(hash_key);
2205}
2206
2207/*
2208 * Generates a hash key using data that makes up a credential.  Based on ElfHash.
2209 */
2210static inline u_long kauth_cred_hash(const uint8_t *datap, int data_len, u_long start_key)
2211{
2212        u_long  hash_key = start_key;
2213        u_long  temp;
2214
2215        while (data_len > 0) {
2216                hash_key = (hash_key << 4) + *datap++;
2217                temp = hash_key & 0xF0000000;
2218                if (temp) {
2219                        hash_key ^= temp >> 24;
2220                }
2221                hash_key &= ~temp;
2222                data_len--;
2223        }
2224        return(hash_key);
2225}
2226
2227#if KAUTH_CRED_HASH_DEBUG
2228static void kauth_cred_hash_print(void) 
2229{
2230        int                     i, j;
2231        kauth_cred_t    found_cred;
2232                
2233        printf("\n\t kauth credential hash table statistics - current cred count %d \n", kauth_cred_count);
2234        /* count slot hits, misses, collisions, and max depth */
2235        for (i = 0; i < kauth_cred_table_size; i++) {
2236                printf("[%02d] ", i);
2237                j = 0;
2238                TAILQ_FOREACH(found_cred, &kauth_cred_table_anchor[i], cr_link) {
2239                        if (j > 0) {
2240                                printf("---- ");
2241                        }
2242                        j++;
2243                        kauth_cred_print(found_cred);
2244                        printf("\n");
2245                }
2246                if (j == 0) {
2247                        printf("NOCRED \n");
2248                }
2249        }
2250}
2251
2252
2253static void kauth_cred_print(kauth_cred_t cred) 
2254{
2255        int     i;
2256        
2257        printf("0x%02X - refs %d uids %d %d %d ", cred, cred->cr_ref, cred->cr_uid, cred->cr_ruid, cred->cr_svuid);
2258        printf("group count %d gids ", cred->cr_ngroups);
2259        for (i = 0; i < NGROUPS; i++) {
2260                printf("%d ", cred->cr_groups[i]);
2261        }
2262        printf("%d %d %d ", cred->cr_rgid, cred->cr_svgid, cred->cr_gmuid);
2263        printf("auditinfo %d %d %d %d %d %d ", 
2264                cred->cr_au.ai_auid, cred->cr_au.ai_mask.am_success, cred->cr_au.ai_mask.am_failure, 
2265                cred->cr_au.ai_termid.port, cred->cr_au.ai_termid.machine, cred->cr_au.ai_asid);
2266        
2267}
2268#endif
2269
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.