darwin-xnu/bsd/kern/kern_authorization.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 * Centralized authorisation framework.
  25 */
  26
  27#include <sys/appleapiopts.h>
  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/timeb.h>
  34#include <sys/times.h>
  35#include <sys/malloc.h>
  36#include <sys/vnode_internal.h>
  37#include <sys/kauth.h>
  38#include <sys/stat.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 <kern/locks.h>
  48
  49
  50/*
  51 * Authorization scopes.
  52 */
  53
  54lck_grp_t *kauth_lck_grp;
  55static lck_mtx_t *kauth_scope_mtx;
  56#define KAUTH_SCOPELOCK()       lck_mtx_lock(kauth_scope_mtx);
  57#define KAUTH_SCOPEUNLOCK()     lck_mtx_unlock(kauth_scope_mtx);
  58
  59/*
  60 * We support listeners for scopes that have not been registered yet.
  61 * If a listener comes in for a scope that is not active we hang the listener
  62 * off our kauth_dangling_listeners list and once the scope becomes active we
  63 * remove it from kauth_dangling_listeners and add it to the active scope.
  64 */
  65struct kauth_listener {
  66        TAILQ_ENTRY(kauth_listener)     kl_link;
  67        const char *                            kl_identifier;
  68        kauth_scope_callback_t          kl_callback;
  69        void *                                          kl_idata;
  70};
  71
  72/* XXX - kauth_todo - there is a race if a scope listener is removed while we
  73 * we are in the kauth_authorize_action code path.  We intentionally do not take
  74 * a scope lock in order to get the best possible performance.  we will fix this 
  75 * post Tiger. 
  76 * Until the race is fixed our kext clients are responsible for all active 
  77 * requests that may be in their callback code or on the way to their callback
  78 * code before they free kauth_listener.kl_callback or kauth_listener.kl_idata.
  79 * We keep copies of these in our kauth_local_listener in an attempt to limit 
  80 * our expose to unlisten race. 
  81 */
  82struct kauth_local_listener {
  83        kauth_listener_t                        kll_listenerp;
  84        kauth_scope_callback_t          kll_callback;
  85        void *                                          kll_idata;
  86};
  87typedef struct kauth_local_listener *kauth_local_listener_t;
  88
  89static TAILQ_HEAD(,kauth_listener) kauth_dangling_listeners;
  90
  91/* 
  92 * Scope listeners need to be reworked to be dynamic.
  93 * We intentionally used a static table to avoid locking issues with linked 
  94 * lists.  The listeners may be called quite often.
  95 * XXX - kauth_todo
  96 */
  97#define KAUTH_SCOPE_MAX_LISTENERS  15
  98
  99struct kauth_scope {
 100        TAILQ_ENTRY(kauth_scope)        ks_link;
 101        volatile struct kauth_local_listener  ks_listeners[KAUTH_SCOPE_MAX_LISTENERS];
 102        const char *                            ks_identifier;
 103        kauth_scope_callback_t          ks_callback;
 104        void *                                          ks_idata;
 105        u_int                                           ks_flags;
 106};
 107
 108/* values for kauth_scope.ks_flags */
 109#define KS_F_HAS_LISTENERS              (1 << 0)
 110
 111static TAILQ_HEAD(,kauth_scope) kauth_scopes;
 112
 113static int kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp);
 114static void     kauth_scope_init(void);
 115static kauth_scope_t kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata);
 116static kauth_listener_t kauth_alloc_listener(const char *identifier, kauth_scope_callback_t callback, void *idata);
 117#if 0
 118static int      kauth_scope_valid(kauth_scope_t scope);
 119#endif
 120
 121kauth_scope_t   kauth_scope_process;
 122static int      kauth_authorize_process_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
 123    uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3);
 124kauth_scope_t   kauth_scope_generic;
 125static int      kauth_authorize_generic_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
 126    uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
 127kauth_scope_t   kauth_scope_fileop;
 128
 129extern int              cansignal(struct proc *, kauth_cred_t, struct proc *, int);
 130extern char *   get_pathbuff(void);
 131extern void             release_pathbuff(char *path);
 132
 133/*
 134 * Initialization.
 135 */
 136void
 137kauth_init(void)
 138{
 139        lck_grp_attr_t  *grp_attributes;
 140
 141        TAILQ_INIT(&kauth_scopes);
 142        TAILQ_INIT(&kauth_dangling_listeners);
 143
 144        /* set up our lock group */
 145        grp_attributes = lck_grp_attr_alloc_init();
 146        kauth_lck_grp = lck_grp_alloc_init("kauth", grp_attributes);
 147        lck_grp_attr_free(grp_attributes);
 148
 149        /* bring up kauth subsystem components */
 150        kauth_cred_init();
 151        kauth_identity_init();
 152        kauth_groups_init();
 153        kauth_scope_init();
 154        kauth_resolver_init();
 155
 156        /* can't alloc locks after this */
 157        lck_grp_free(kauth_lck_grp);
 158        kauth_lck_grp = NULL;
 159}
 160
 161static void
 162kauth_scope_init(void)
 163{
 164        kauth_scope_mtx = lck_mtx_alloc_init(kauth_lck_grp, 0 /*LCK_ATTR_NULL*/);
 165        kauth_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS, kauth_authorize_process_callback, NULL);
 166        kauth_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC, kauth_authorize_generic_callback, NULL);
 167        kauth_scope_fileop = kauth_register_scope(KAUTH_SCOPE_FILEOP, NULL, NULL);
 168}
 169
 170/*
 171 * Scope registration.
 172 */
 173
 174static kauth_scope_t
 175kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 176{
 177        kauth_scope_t   sp;
 178
 179        /*
 180         * Allocate and populate the scope structure.
 181         */
 182        MALLOC(sp, kauth_scope_t, sizeof(*sp), M_KAUTH, M_WAITOK);
 183        if (sp == NULL)
 184                return(NULL);
 185        bzero(&sp->ks_listeners, sizeof(sp->ks_listeners));
 186        sp->ks_flags = 0;
 187        sp->ks_identifier = identifier;
 188        sp->ks_idata = idata;
 189        sp->ks_callback = callback;
 190        return(sp);
 191}
 192
 193static kauth_listener_t
 194kauth_alloc_listener(const char *identifier, kauth_scope_callback_t callback, void *idata)
 195{
 196        kauth_listener_t lsp;
 197
 198        /*
 199         * Allocate and populate the listener structure.
 200         */
 201        MALLOC(lsp, kauth_listener_t, sizeof(*lsp), M_KAUTH, M_WAITOK);
 202        if (lsp == NULL)
 203                return(NULL);
 204        lsp->kl_identifier = identifier;
 205        lsp->kl_idata = idata;
 206        lsp->kl_callback = callback;
 207        return(lsp);
 208}
 209
 210kauth_scope_t
 211kauth_register_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 212{
 213        kauth_scope_t           sp, tsp;
 214        kauth_listener_t        klp;
 215
 216        if ((sp = kauth_alloc_scope(identifier, callback, idata)) == NULL)
 217                return(NULL);
 218
 219        /*
 220         * Lock the list and insert.
 221         */
 222        KAUTH_SCOPELOCK();
 223        TAILQ_FOREACH(tsp, &kauth_scopes, ks_link) {
 224                /* duplicate! */
 225                if (strcmp(tsp->ks_identifier, identifier) == 0) {
 226                        KAUTH_SCOPEUNLOCK();
 227                        FREE(sp, M_KAUTH);
 228                        return(NULL);
 229                }
 230        }
 231        TAILQ_INSERT_TAIL(&kauth_scopes, sp, ks_link);
 232
 233        /*
 234         * Look for listeners waiting for this scope, move them to the active scope
 235         * listener table.
 236         * Note that we have to restart the scan every time we remove an entry
 237         * from the list, since we can't remove the current item from the list.
 238         */
 239restart:
 240        TAILQ_FOREACH(klp, &kauth_dangling_listeners, kl_link) {
 241                if (strcmp(klp->kl_identifier, sp->ks_identifier) == 0) {
 242                        /* found a match on the dangling listener list.  add it to the
 243                         * the active scope.
 244                         */
 245                        if (kauth_add_callback_to_scope(sp, klp) == 0) {
 246                                TAILQ_REMOVE(&kauth_dangling_listeners, klp, kl_link);
 247                        }
 248                        else {
 249#if 0
 250                                printf("%s - failed to add listener to scope \"%s\" \n", __FUNCTION__, sp->ks_identifier);
 251#endif
 252                                break;
 253                        }
 254                        goto restart;
 255                }
 256        }
 257
 258        KAUTH_SCOPEUNLOCK();
 259        return(sp);
 260}
 261
 262
 263
 264void
 265kauth_deregister_scope(kauth_scope_t scope)
 266{
 267        int             i;
 268
 269        KAUTH_SCOPELOCK();
 270
 271        TAILQ_REMOVE(&kauth_scopes, scope, ks_link);
 272        
 273        /* relocate listeners back to the waiting list */
 274        for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
 275                if (scope->ks_listeners[i].kll_listenerp != NULL) {
 276                        TAILQ_INSERT_TAIL(&kauth_dangling_listeners, scope->ks_listeners[i].kll_listenerp, kl_link);
 277                        scope->ks_listeners[i].kll_listenerp = NULL;
 278                        /* 
 279                         * XXX - kauth_todo - WARNING, do not clear kll_callback or
 280                         * kll_idata here.  they are part of our scope unlisten race hack
 281                         */
 282                }
 283        }
 284        KAUTH_SCOPEUNLOCK();
 285        FREE(scope, M_KAUTH);
 286        
 287        return;
 288}
 289
 290kauth_listener_t
 291kauth_listen_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
 292{
 293        kauth_listener_t klp;
 294        kauth_scope_t   sp;
 295
 296        if ((klp = kauth_alloc_listener(identifier, callback, idata)) == NULL)
 297                return(NULL);
 298
 299        /*
 300         * Lock the scope list and check to see whether this scope already exists.
 301         */
 302        KAUTH_SCOPELOCK();
 303        TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
 304                if (strcmp(sp->ks_identifier, identifier) == 0) {
 305                        /* scope exists, add it to scope listener table */
 306                        if (kauth_add_callback_to_scope(sp, klp) == 0) {
 307                                KAUTH_SCOPEUNLOCK();
 308                                return(klp);
 309                        }
 310                        /* table already full */
 311                        KAUTH_SCOPEUNLOCK();
 312                        FREE(klp, M_KAUTH);
 313                        return(NULL);
 314                }
 315        }
 316        
 317        /* scope doesn't exist, put on waiting list. */
 318        TAILQ_INSERT_TAIL(&kauth_dangling_listeners, klp, kl_link);
 319
 320        KAUTH_SCOPEUNLOCK();
 321
 322        return(klp);
 323}
 324
 325void
 326kauth_unlisten_scope(kauth_listener_t listener)
 327{
 328        kauth_scope_t           sp;
 329        kauth_listener_t        klp;
 330        int                                     i, listener_count, do_free;
 331        
 332        KAUTH_SCOPELOCK();
 333
 334        /* search the active scope for this listener */
 335        TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
 336                do_free = 0;
 337                if ((sp->ks_flags & KS_F_HAS_LISTENERS) != 0) {
 338                        listener_count = 0;
 339                        for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
 340                                if (sp->ks_listeners[i].kll_listenerp == listener) {
 341                                        sp->ks_listeners[i].kll_listenerp = NULL;
 342                                        do_free = 1;
 343                                        /* 
 344                                         * XXX - kauth_todo - WARNING, do not clear kll_callback or
 345                                         * kll_idata here.  they are part of our scope unlisten race hack
 346                                         */
 347                                }
 348                                else if (sp->ks_listeners[i].kll_listenerp != NULL) {
 349                                        listener_count++;
 350                                }
 351                        }
 352                        if (do_free) {
 353                                if (listener_count == 0) {
 354                                        sp->ks_flags &= ~KS_F_HAS_LISTENERS;
 355                                }
 356                                KAUTH_SCOPEUNLOCK();
 357                                FREE(listener, M_KAUTH);
 358                                return;
 359                        }
 360                }
 361        }
 362
 363        /* if not active, check the dangling list */
 364        TAILQ_FOREACH(klp, &kauth_dangling_listeners, kl_link) {
 365                if (klp == listener) {
 366                        TAILQ_REMOVE(&kauth_dangling_listeners, klp, kl_link);
 367                        KAUTH_SCOPEUNLOCK();
 368                        FREE(listener, M_KAUTH);
 369                        return;
 370                }
 371        }
 372
 373        KAUTH_SCOPEUNLOCK();
 374        return;
 375}
 376
 377/*
 378 * Authorization requests.
 379 */
 380int
 381kauth_authorize_action(kauth_scope_t scope, kauth_cred_t credential, kauth_action_t action,
 382    uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
 383{
 384        int result, ret, i;
 385
 386        /* ask the scope */
 387        if (scope->ks_callback != NULL)
 388                result = scope->ks_callback(credential, scope->ks_idata, action, arg0, arg1, arg2, arg3);
 389        else
 390                result = KAUTH_RESULT_DEFER;
 391
 392        /* check with listeners */
 393        if ((scope->ks_flags & KS_F_HAS_LISTENERS) != 0) {
 394                for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
 395                        /* XXX - kauth_todo - there is a race here if listener is removed - we will fix this post Tiger. 
 396                         * Until the race is fixed our kext clients are responsible for all active requests that may
 397                         * be in their callbacks or on the way to their callbacks before they free kl_callback or kl_idata.
 398                         * We keep copies of these in our kauth_local_listener in an attempt to limit our expose to 
 399                         * unlisten race. 
 400                         */
 401                        if (scope->ks_listeners[i].kll_listenerp == NULL || 
 402                                scope->ks_listeners[i].kll_callback == NULL) 
 403                                continue;
 404
 405                        ret = scope->ks_listeners[i].kll_callback(
 406                                        credential, scope->ks_listeners[i].kll_idata, 
 407                                        action, arg0, arg1, arg2, arg3);
 408                        if ((ret == KAUTH_RESULT_DENY) ||
 409                                (result == KAUTH_RESULT_DEFER))
 410                                result = ret;
 411                }
 412        }
 413
 414        /* we need an explicit allow, or the auth fails */
 415        /* XXX need a mechanism for auth failure to be signalled vs. denial */
 416        return(result == KAUTH_RESULT_ALLOW ? 0 : EPERM);
 417}
 418
 419/*
 420 * Default authorization handlers.
 421 */
 422int
 423kauth_authorize_allow(__unused kauth_cred_t credential, __unused void *idata, __unused kauth_action_t action,
 424     __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
 425{
 426
 427        return(KAUTH_RESULT_ALLOW);
 428}
 429
 430#if 0
 431/*
 432 * Debugging support.
 433 */
 434static int
 435kauth_scope_valid(kauth_scope_t scope)
 436{
 437        kauth_scope_t   sp;
 438
 439        KAUTH_SCOPELOCK();
 440        TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
 441                if (sp == scope)
 442                        break;
 443        }
 444        KAUTH_SCOPEUNLOCK();
 445        return((sp == NULL) ? 0 : 1);
 446}
 447#endif
 448
 449/*
 450 * Process authorization scope.
 451 */
 452
 453int
 454kauth_authorize_process(kauth_cred_t credential, kauth_action_t action, struct proc *process, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
 455{
 456        return(kauth_authorize_action(kauth_scope_process, credential, action, (uintptr_t)process, arg1, arg2, arg3));
 457}
 458
 459static int
 460kauth_authorize_process_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
 461    uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
 462{
 463        switch(action) {
 464        case KAUTH_PROCESS_CANSIGNAL:
 465                panic("KAUTH_PROCESS_CANSIGNAL not implemented");
 466                /* XXX credential wrong here */
 467                /* arg0 - process to signal
 468                 * arg1 - signal to send the process
 469                 */
 470                if (cansignal(current_proc(), credential, (struct proc *)arg0, (int)arg1))
 471                        return(KAUTH_RESULT_ALLOW);
 472                break;
 473        case KAUTH_PROCESS_CANTRACE:
 474                /* current_proc() - process that will do the tracing 
 475                 * arg0 - process to be traced 
 476                 * arg1 - pointer to int - reason (errno) for denial 
 477                 */
 478                if (cantrace(current_proc(), credential, (proc_t)arg0, (int *)arg1))
 479                        return(KAUTH_RESULT_ALLOW);
 480                break;
 481        }
 482
 483        /* no explicit result, so defer to others in the chain */
 484        return(KAUTH_RESULT_DEFER);
 485}
 486
 487/*
 488 * File system operation authorization scope.  This is really only a notification
 489 * of the file system operation, not an authorization check.  Thus the result is
 490 * not relevant.
 491 * arguments passed to KAUTH_FILEOP_OPEN listeners
 492 *              arg0 is pointer to vnode (vnode *) for given user path.
 493 *              arg1 is pointer to path (char *) passed in to open.
 494 * arguments passed to KAUTH_FILEOP_CLOSE listeners
 495 *              arg0 is pointer to vnode (vnode *) for file to be closed.
 496 *              arg1 is pointer to path (char *) of file to be closed.
 497 *              arg2 is close flags.
 498 * arguments passed to KAUTH_FILEOP_RENAME listeners
 499 *              arg0 is pointer to "from" path (char *).
 500 *              arg1 is pointer to "to" path (char *).
 501 * arguments passed to KAUTH_FILEOP_EXCHANGE listeners
 502 *              arg0 is pointer to file 1 path (char *).
 503 *              arg1 is pointer to file 2 path (char *).
 504 * arguments passed to KAUTH_FILEOP_EXEC listeners
 505 *              arg0 is pointer to vnode (vnode *) for executable.
 506 *              arg1 is pointer to path (char *) to executable.
 507 */
 508
 509int
 510kauth_authorize_fileop_has_listeners(void)
 511{
 512        /*
 513         * return 1 if we have any listeners for the fileop scope
 514         * otherwize return 0
 515         */
 516        if ((kauth_scope_fileop->ks_flags & KS_F_HAS_LISTENERS) != 0) {
 517                return(1);
 518        }
 519        return (0);
 520}
 521
 522int
 523kauth_authorize_fileop(kauth_cred_t credential, kauth_action_t action, uintptr_t arg0, uintptr_t arg1)
 524{
 525        char            *namep = NULL;
 526        int                     name_len;
 527        uintptr_t       arg2 = 0;
 528        
 529        /* we do not have a primary handler for the fileop scope so bail out if 
 530         * there are no listeners.
 531         */
 532        if ((kauth_scope_fileop->ks_flags & KS_F_HAS_LISTENERS) == 0) {
 533                return(0);
 534        }
 535
 536        if (action == KAUTH_FILEOP_OPEN || action == KAUTH_FILEOP_CLOSE || action == KAUTH_FILEOP_EXEC) {
 537                /* get path to the given vnode as a convenience to our listeners.
 538                 */
 539                namep = get_pathbuff();
 540                name_len = MAXPATHLEN;
 541                if (vn_getpath((vnode_t)arg0, namep, &name_len) != 0) {
 542                        release_pathbuff(namep);
 543                        return(0);
 544                }
 545                if (action == KAUTH_FILEOP_CLOSE) {
 546                        arg2 = arg1;  /* close has some flags that come in via arg1 */
 547                }
 548                arg1 = (uintptr_t)namep;
 549        }       
 550        kauth_authorize_action(kauth_scope_fileop, credential, action, arg0, arg1, arg2, 0);
 551        
 552        if (namep != NULL) {
 553                release_pathbuff(namep);
 554        }
 555        
 556        return(0);
 557}
 558
 559/*
 560 * Generic authorization scope.
 561 */
 562
 563int
 564kauth_authorize_generic(kauth_cred_t credential, kauth_action_t action)
 565{
 566        if (credential == NULL)
 567                panic("auth against NULL credential");
 568
 569        return(kauth_authorize_action(kauth_scope_generic, credential, action, 0, 0, 0, 0));
 570                
 571}
 572
 573static int
 574kauth_authorize_generic_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
 575     __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
 576{
 577        switch(action) {
 578        case KAUTH_GENERIC_ISSUSER:
 579                /* XXX == 0 ? */
 580                return((kauth_cred_getuid(credential) == 0) ?
 581                    KAUTH_RESULT_ALLOW : KAUTH_RESULT_DENY);
 582                break;
 583        }
 584
 585        /* no explicit result, so defer to others in the chain */
 586        return(KAUTH_RESULT_DEFER);
 587}
 588
 589/*
 590 * ACL evaluator.
 591 *
 592 * Determines whether the credential has the requested rights for an object secured by the supplied
 593 * ACL.
 594 *
 595 * Evaluation proceeds from the top down, with access denied if any ACE denies any of the requested
 596 * rights, or granted if all of the requested rights are satisfied by the ACEs so far.
 597 */
 598int
 599kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
 600{
 601        int applies, error, i;
 602        kauth_ace_t ace;
 603        guid_t guid;
 604        uint32_t rights;
 605        int wkguid;
 606
 607        /* always allowed to do nothing */
 608        if (eval->ae_requested == 0) {
 609                eval->ae_result = KAUTH_RESULT_ALLOW;
 610                return(0);
 611        }
 612
 613        eval->ae_residual = eval->ae_requested;
 614
 615        /*
 616         * Get our guid for comparison purposes.
 617         */
 618        if ((error = kauth_cred_getguid(cred, &guid)) != 0) {
 619                eval->ae_result = KAUTH_RESULT_DENY;
 620                KAUTH_DEBUG("    ACL - can't get credential GUID (%d), ACL denied", error);
 621                return(error);
 622        }
 623
 624        KAUTH_DEBUG("    ACL - %d entries, initial residual %x", eval->ae_count, eval->ae_residual);
 625        for (i = 0, ace = eval->ae_acl; i < eval->ae_count; i++, ace++) {
 626
 627                /*
 628                 * Skip inherit-only entries.
 629                 */
 630                if (ace->ace_flags & KAUTH_ACE_ONLY_INHERIT)
 631                        continue;
 632
 633                /*
 634                 * Expand generic rights, if appropriate.
 635                 */
 636                rights = ace->ace_rights;
 637                if (rights & KAUTH_ACE_GENERIC_ALL)
 638                        rights |= eval->ae_exp_gall;
 639                if (rights & KAUTH_ACE_GENERIC_READ)
 640                        rights |= eval->ae_exp_gread;
 641                if (rights & KAUTH_ACE_GENERIC_WRITE)
 642                        rights |= eval->ae_exp_gwrite;
 643                if (rights & KAUTH_ACE_GENERIC_EXECUTE)
 644                        rights |= eval->ae_exp_gexec;
 645
 646                /*
 647                 * Determine whether this entry applies to the current request.  This
 648                 * saves us checking the GUID if the entry has nothing to do with what
 649                 * we're currently doing.
 650                 */
 651                switch(ace->ace_flags & KAUTH_ACE_KINDMASK) {
 652                case KAUTH_ACE_PERMIT:
 653                        if (!(eval->ae_residual & rights))
 654                                continue;
 655                        break;
 656                case KAUTH_ACE_DENY:
 657                        if (!(eval->ae_requested & rights))
 658                                continue;
 659                        break;
 660                default:
 661                        /* we don't recognise this ACE, skip it */
 662                        continue;
 663                }
 664                
 665                /*
 666                 * Verify whether this entry applies to the credential.
 667                 */
 668                wkguid = kauth_wellknown_guid(&ace->ace_applicable);
 669                switch(wkguid) {
 670                case KAUTH_WKG_OWNER:
 671                        applies = eval->ae_options & KAUTH_AEVAL_IS_OWNER;
 672                        break;
 673                case KAUTH_WKG_GROUP:
 674                        applies = eval->ae_options & KAUTH_AEVAL_IN_GROUP;
 675                        break;
 676                /* we short-circuit these here rather than wasting time calling the group membership code */
 677                case KAUTH_WKG_EVERYBODY:
 678                        applies = 1;
 679                        break;
 680                case KAUTH_WKG_NOBODY:
 681                        applies = 0;
 682                        break;
 683
 684                default:
 685                        /* check to see whether it's exactly us, or a group we are a member of */
 686                        applies = kauth_guid_equal(&guid, &ace->ace_applicable);
 687                        KAUTH_DEBUG("    ACL - ACE applicable " K_UUID_FMT " caller " K_UUID_FMT " %smatched",
 688                            K_UUID_ARG(ace->ace_applicable), K_UUID_ARG(guid), applies ? "" : "not ");
 689                
 690                        if (!applies) {
 691                                error = kauth_cred_ismember_guid(cred, &ace->ace_applicable, &applies);
 692                                /*
 693                                 * If we can't resolve group membership, we have to limit misbehaviour.
 694                                 * If the ACE is an 'allow' ACE, assume the cred is not a member (avoid
 695                                 * granting excess access).  If the ACE is a 'deny' ACE, assume the cred
 696                                 * is a member (avoid failing to deny).
 697                                 */
 698                                if (error != 0) {
 699                                        KAUTH_DEBUG("    ACL[%d] - can't get membership, making pessimistic assumption", i);
 700                                        switch(ace->ace_flags & KAUTH_ACE_KINDMASK) {
 701                                        case KAUTH_ACE_PERMIT:
 702                                                applies = 0;
 703                                                break;
 704                                        case KAUTH_ACE_DENY:
 705                                                applies = 1;
 706                                                break;
 707                                        }
 708                                } else {
 709                                        KAUTH_DEBUG("    ACL - %s group member", applies ? "is" : "not");
 710                                }
 711                        } else {
 712                                KAUTH_DEBUG("    ACL - entry matches caller");
 713                        }
 714                }
 715                if (!applies)
 716                        continue;
 717
 718                /*
 719                 * Apply ACE to outstanding rights.
 720                 */
 721                switch(ace->ace_flags & KAUTH_ACE_KINDMASK) {
 722                case KAUTH_ACE_PERMIT:
 723                        /* satisfy any rights that this ACE grants */
 724                        eval->ae_residual = eval->ae_residual & ~rights;
 725                        KAUTH_DEBUG("    ACL[%d] - rights %x leave residual %x", i, rights, eval->ae_residual);
 726                        /* all rights satisfied? */
 727                        if (eval->ae_residual == 0) {
 728                                eval->ae_result = KAUTH_RESULT_ALLOW;
 729                                return(0);
 730                        }
 731                        break;
 732                case KAUTH_ACE_DENY:
 733                        /* deny the request if any of the requested rights is denied */
 734                        if (eval->ae_requested & rights) {
 735                                KAUTH_DEBUG("    ACL[%d] - denying based on %x", i, rights);
 736                                eval->ae_result = KAUTH_RESULT_DENY;
 737                                return(0);
 738                        }
 739                        break;
 740                default:
 741                        KAUTH_DEBUG("    ACL - unknown entry kind %d", ace->ace_flags & KAUTH_ACE_KINDMASK);
 742                        break;
 743                }
 744        }
 745        /* if not permitted, defer to other modes of authorisation */
 746        eval->ae_result = KAUTH_RESULT_DEFER;
 747        return(0);
 748}
 749
 750/*
 751 * Perform ACL inheritance and umask-ACL handling.
 752 *
 753 * Entries are inherited from the ACL on dvp.  A caller-supplied
 754 * ACL is in initial, and the result is output into product.
 755 * If the process has a umask ACL and one is not supplied, we use
 756 * the umask ACL.
 757 * If isdir is set, the resultant ACL is for a directory, otherwise it is for a file.
 758 */
 759int
 760kauth_acl_inherit(vnode_t dvp, kauth_acl_t initial, kauth_acl_t *product, int isdir, vfs_context_t ctx)
 761{
 762        int     entries, error, index;
 763        unsigned int i;
 764        struct vnode_attr dva;
 765        kauth_acl_t inherit, result;
 766
 767        /*
 768         * Fetch the ACL from the directory.  This should never fail.  Note that we don't
 769         * manage inheritance when the remote server is doing authorization; we just
 770         * want to compose the umask-ACL and any initial ACL.
 771         */
 772        inherit = NULL;
 773        if ((dvp != NULL) && !vfs_authopaque(vnode_mount(dvp))) {
 774                VATTR_INIT(&dva);
 775                VATTR_WANTED(&dva, va_acl);
 776                if ((error = vnode_getattr(dvp, &dva, ctx)) != 0) {
 777                        KAUTH_DEBUG("    ERROR - could not get parent directory ACL for inheritance");
 778                        return(error);
 779                }
 780                if (VATTR_IS_SUPPORTED(&dva, va_acl))
 781                        inherit = dva.va_acl;
 782        }
 783
 784        /*
 785         * Compute the number of entries in the result ACL by scanning the input lists.
 786         */
 787        entries = 0;
 788        if (inherit != NULL) {
 789                for (i = 0; i < inherit->acl_entrycount; i++) {
 790                        if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT))
 791                                entries++;
 792                }
 793        }
 794
 795        if (initial == NULL) {
 796                /* XXX 3634665 TODO: fetch umask ACL from the process, set in initial */
 797        }
 798
 799        if (initial != NULL) {
 800                entries += initial->acl_entrycount;
 801        }
 802
 803        /*
 804         * If there is no initial ACL, and no inheritable entries, the
 805         * object should have no ACL at all.
 806         * Note that this differs from the case where the initial ACL
 807         * is empty, in which case the object must also have an empty ACL.
 808         */
 809        if ((entries == 0) && (initial == NULL)) {
 810                *product = NULL;
 811                error = 0;
 812                goto out;
 813        }
 814        
 815        /*
 816         * Allocate the result buffer.
 817         */
 818        if ((result = kauth_acl_alloc(entries)) == NULL) {
 819                KAUTH_DEBUG("    ERROR - could not allocate %d-entry result buffer for inherited ACL");
 820                error = ENOMEM;
 821                goto out;
 822        }
 823
 824        /*
 825         * Composition is simply:
 826         *  - initial
 827         *  - inherited
 828         */
 829        index = 0;
 830        if (initial != NULL) {
 831                for (i = 0; i < initial->acl_entrycount; i++)
 832                        result->acl_ace[index++] = initial->acl_ace[i];
 833                KAUTH_DEBUG("    INHERIT - applied %d initial entries", index);
 834        }
 835        if (inherit != NULL) {
 836                for (i = 0; i < inherit->acl_entrycount; i++) {
 837                        /* inherit onto this object? */
 838                        if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT)) {
 839                                result->acl_ace[index] = inherit->acl_ace[i];
 840                                result->acl_ace[index].ace_flags |= KAUTH_ACE_INHERITED;
 841                                /* don't re-inherit? */
 842                                if (result->acl_ace[index].ace_flags & KAUTH_ACE_LIMIT_INHERIT)
 843                                        result->acl_ace[index].ace_flags &=
 844                                            ~(KAUTH_ACE_DIRECTORY_INHERIT | KAUTH_ACE_FILE_INHERIT | KAUTH_ACE_LIMIT_INHERIT);
 845                                index++;
 846                        }
 847                }
 848        }
 849        result->acl_entrycount = index;
 850        *product = result;
 851        KAUTH_DEBUG("    INHERIT - product ACL has %d entries", index);
 852        error = 0;
 853out:
 854        if (inherit != NULL)
 855                kauth_acl_free(inherit);
 856        return(error);
 857}
 858
 859/*
 860 * Optimistically copy in a kauth_filesec structure
 861 * Parameters:  xsecurity               user space kauth_filesec_t
 862 *              xsecdstpp               pointer to kauth_filesec_t
 863 *
 864 * Returns: 0 on success, EINVAL or EFAULT depending on failure mode.
 865 * Modifies: xsecdestpp, which contains a pointer to an allocated
 866 *           and copied-in kauth_filesec_t
 867 */
 868
 869int
 870kauth_copyinfilesec(user_addr_t xsecurity, kauth_filesec_t *xsecdestpp)
 871{
 872        user_addr_t uaddr, known_bound;
 873        int error;
 874        kauth_filesec_t fsec;
 875        u_int32_t count;
 876        size_t copysize;
 877        
 878        error = 0;
 879        fsec = NULL;
 880
 881        /*
 882         * Make a guess at the size of the filesec.  We start with the base
 883         * pointer, and look at how much room is left on the page, clipped
 884         * to a sensible upper bound.  If it turns out this isn't enough,
 885         * we'll size based on the actual ACL contents and come back again.
 886         *
 887         * The upper bound must be less than KAUTH_ACL_MAX_ENTRIES.  The
 888         * value here is fairly arbitrary.  It's ok to have a zero count.
 889         */
 890        known_bound = xsecurity + sizeof(struct kauth_filesec);
 891        uaddr = mach_vm_round_page(known_bound);
 892        count = (uaddr - known_bound) / sizeof(struct kauth_ace);
 893        if (count > 32)
 894                count = 32;
 895restart:
 896        if ((fsec = kauth_filesec_alloc(count)) == NULL) {
 897                error = ENOMEM;
 898                goto out;
 899        }
 900        copysize = KAUTH_FILESEC_SIZE(count);
 901        if ((error = copyin(xsecurity, (caddr_t)fsec, copysize)) != 0)
 902                goto out;
 903
 904        /* validate the filesec header */
 905        if (fsec->fsec_magic != KAUTH_FILESEC_MAGIC) {
 906                error = EINVAL;
 907                goto out;
 908        }
 909
 910        /*
 911         * Is there an ACL payload, and is it too big?
 912         */
 913        if ((fsec->fsec_entrycount != KAUTH_FILESEC_NOACL) &&
 914            (fsec->fsec_entrycount > count)) {
 915                if (fsec->fsec_entrycount > KAUTH_ACL_MAX_ENTRIES) {
 916                        error = EINVAL;
 917                        goto out;
 918                }
 919                count = fsec->fsec_entrycount;
 920                kauth_filesec_free(fsec);
 921                goto restart;
 922        }
 923        
 924out:
 925        if (error) {
 926                if (fsec)
 927                        kauth_filesec_free(fsec);
 928        } else {
 929                *xsecdestpp = fsec;
 930        }
 931        return(error);
 932}
 933
 934/*
 935 * Allocate a filesec structure.
 936 */
 937kauth_filesec_t
 938kauth_filesec_alloc(int count)
 939{
 940        kauth_filesec_t fsp;
 941        
 942        /* if the caller hasn't given us a valid size hint, assume the worst */
 943        if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES))
 944                return(NULL);
 945
 946        MALLOC(fsp, kauth_filesec_t, KAUTH_FILESEC_SIZE(count), M_KAUTH, M_WAITOK);
 947        if (fsp != NULL) {
 948                fsp->fsec_magic = KAUTH_FILESEC_MAGIC;
 949                fsp->fsec_owner = kauth_null_guid;
 950                fsp->fsec_group = kauth_null_guid;
 951                fsp->fsec_entrycount = KAUTH_FILESEC_NOACL;
 952                fsp->fsec_flags = 0;
 953        }
 954        return(fsp);
 955}       
 956
 957void
 958kauth_filesec_free(kauth_filesec_t fsp)
 959{
 960#ifdef KAUTH_DEBUG_ENABLE
 961        if (fsp == KAUTH_FILESEC_NONE)
 962                panic("freeing KAUTH_FILESEC_NONE");
 963        if (fsp == KAUTH_FILESEC_WANTED)
 964                panic("freeing KAUTH_FILESEC_WANTED");
 965#endif
 966        FREE(fsp, M_KAUTH);
 967}
 968
 969
 970/*
 971 * Allocate an ACL buffer.
 972 */
 973kauth_acl_t
 974kauth_acl_alloc(int count)
 975{
 976        kauth_acl_t     aclp;
 977        
 978        /* if the caller hasn't given us a valid size hint, assume the worst */
 979        if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES))
 980                return(NULL);
 981
 982        MALLOC(aclp, kauth_acl_t, KAUTH_ACL_SIZE(count), M_KAUTH, M_WAITOK);
 983        if (aclp != NULL) {
 984                aclp->acl_entrycount = 0;
 985                aclp->acl_flags = 0;
 986        }
 987        return(aclp);
 988}       
 989
 990void
 991kauth_acl_free(kauth_acl_t aclp)
 992{
 993        FREE(aclp, M_KAUTH);
 994}
 995
 996
 997/*
 998 * WARNING - caller must hold KAUTH_SCOPELOCK
 999 */
1000static int kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp)
1001{
1002        int             i;
1003
1004        for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
1005                if (sp->ks_listeners[i].kll_listenerp == NULL) {
1006                        sp->ks_listeners[i].kll_callback = klp->kl_callback;
1007                        sp->ks_listeners[i].kll_idata = klp->kl_idata;
1008                        sp->ks_listeners[i].kll_listenerp = klp;
1009                        sp->ks_flags |= KS_F_HAS_LISTENERS;
1010                        return(0);
1011                }
1012        }
1013        return(ENOSPC);
1014}
1015
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.