1/*
2 * Copyright (c) 2004-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29/*
30 * Centralized authorisation framework.
31 */
32
33#include <sys/appleapiopts.h>
34#include <sys/param.h> /* XXX trim includes */
35#include <sys/acct.h>
36#include <sys/systm.h>
37#include <sys/ucred.h>
38#include <sys/proc_internal.h>
39#include <sys/timeb.h>
40#include <sys/times.h>
41#include <sys/malloc.h>
42#include <sys/vnode_internal.h>
43#include <sys/kauth.h>
44#include <sys/stat.h>
45
46#include <security/audit/audit.h>
47
48#include <sys/mount.h>
49#include <sys/sysproto.h>
50#include <mach/message.h>
51
52#include <kern/locks.h>
53
54
55/*
56 * Authorization scopes.
57 */
58
59LCK_GRP_DECLARE(kauth_lck_grp, "kauth");
60static LCK_MTX_DECLARE(kauth_scope_mtx, &kauth_lck_grp);
61#define KAUTH_SCOPELOCK() lck_mtx_lock(&kauth_scope_mtx);
62#define KAUTH_SCOPEUNLOCK() lck_mtx_unlock(&kauth_scope_mtx);
63
64/*
65 * We support listeners for scopes that have not been registered yet.
66 * If a listener comes in for a scope that is not active we hang the listener
67 * off our kauth_dangling_listeners list and once the scope becomes active we
68 * remove it from kauth_dangling_listeners and add it to the active scope.
69 */
70struct kauth_listener {
71 TAILQ_ENTRY(kauth_listener) kl_link;
72 const char * kl_identifier;
73 kauth_scope_callback_t kl_callback;
74 void * kl_idata;
75};
76
77/* XXX - kauth_todo - there is a race if a scope listener is removed while we
78 * we are in the kauth_authorize_action code path. We intentionally do not take
79 * a scope lock in order to get the best possible performance. we will fix this
80 * post Tiger.
81 * Until the race is fixed our kext clients are responsible for all active
82 * requests that may be in their callback code or on the way to their callback
83 * code before they free kauth_listener.kl_callback or kauth_listener.kl_idata.
84 * We keep copies of these in our kauth_local_listener in an attempt to limit
85 * our expose to unlisten race.
86 */
87struct kauth_local_listener {
88 kauth_listener_t kll_listenerp;
89 kauth_scope_callback_t kll_callback;
90 void * kll_idata;
91};
92typedef struct kauth_local_listener *kauth_local_listener_t;
93
94static TAILQ_HEAD(, kauth_listener) kauth_dangling_listeners =
95 TAILQ_HEAD_INITIALIZER(kauth_dangling_listeners);
96
97/*
98 * Scope listeners need to be reworked to be dynamic.
99 * We intentionally used a static table to avoid locking issues with linked
100 * lists. The listeners may be called quite often.
101 * XXX - kauth_todo
102 */
103#define KAUTH_SCOPE_MAX_LISTENERS 15
104
105struct kauth_scope {
106 TAILQ_ENTRY(kauth_scope) ks_link;
107 volatile struct kauth_local_listener ks_listeners[KAUTH_SCOPE_MAX_LISTENERS];
108 const char * ks_identifier;
109 kauth_scope_callback_t ks_callback;
110 void * ks_idata;
111 u_int ks_flags;
112};
113
114/* values for kauth_scope.ks_flags */
115#define KS_F_HAS_LISTENERS (1 << 0)
116
117static TAILQ_HEAD(, kauth_scope) kauth_scopes = TAILQ_HEAD_INITIALIZER(kauth_scopes);
118
119static int kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp);
120static void kauth_scope_init(void);
121static kauth_scope_t kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata);
122static kauth_listener_t kauth_alloc_listener(const char *identifier, kauth_scope_callback_t callback, void *idata);
123#if 0
124static int kauth_scope_valid(kauth_scope_t scope);
125#endif
126
127kauth_scope_t kauth_scope_process;
128static int kauth_authorize_process_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
129 uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3);
130kauth_scope_t kauth_scope_generic;
131static int kauth_authorize_generic_callback(kauth_cred_t _credential, void *_idata, kauth_action_t _action,
132 uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3);
133kauth_scope_t kauth_scope_fileop;
134
135extern int cansignal(struct proc *, kauth_cred_t, struct proc *, int);
136extern char * get_pathbuff(void);
137extern void release_pathbuff(char *path);
138
139/*
140 * Initialization.
141 */
142void
143kauth_init(void)
144{
145 /* bring up kauth subsystem components */
146 kauth_scope_init();
147}
148
149static void
150kauth_scope_init(void)
151{
152 kauth_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS, callback: kauth_authorize_process_callback, NULL);
153 kauth_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC, callback: kauth_authorize_generic_callback, NULL);
154 kauth_scope_fileop = kauth_register_scope(KAUTH_SCOPE_FILEOP, NULL, NULL);
155}
156
157/*
158 * Scope registration.
159 */
160
161static kauth_scope_t
162kauth_alloc_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
163{
164 kauth_scope_t sp;
165
166 /*
167 * Allocate and populate the scope structure.
168 */
169 sp = kalloc_type(struct kauth_scope, Z_WAITOK | Z_ZERO | Z_NOFAIL);
170 sp->ks_flags = 0;
171 sp->ks_identifier = identifier;
172 sp->ks_idata = idata;
173 sp->ks_callback = callback;
174 return sp;
175}
176
177static kauth_listener_t
178kauth_alloc_listener(const char *identifier, kauth_scope_callback_t callback, void *idata)
179{
180 kauth_listener_t lsp;
181
182 /*
183 * Allocate and populate the listener structure.
184 */
185 lsp = kalloc_type(struct kauth_listener, Z_WAITOK | Z_NOFAIL);
186 lsp->kl_identifier = identifier;
187 lsp->kl_idata = idata;
188 lsp->kl_callback = callback;
189 return lsp;
190}
191
192kauth_scope_t
193kauth_register_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
194{
195 kauth_scope_t sp, tsp;
196 kauth_listener_t klp;
197
198 if ((sp = kauth_alloc_scope(identifier, callback, idata)) == NULL) {
199 return NULL;
200 }
201
202 /*
203 * Lock the list and insert.
204 */
205 KAUTH_SCOPELOCK();
206 TAILQ_FOREACH(tsp, &kauth_scopes, ks_link) {
207 /* duplicate! */
208 if (strncmp(s1: tsp->ks_identifier, s2: identifier,
209 n: strlen(s: tsp->ks_identifier) + 1) == 0) {
210 KAUTH_SCOPEUNLOCK();
211 kfree_type(struct kauth_scope, sp);
212 return NULL;
213 }
214 }
215 TAILQ_INSERT_TAIL(&kauth_scopes, sp, ks_link);
216
217 /*
218 * Look for listeners waiting for this scope, move them to the active scope
219 * listener table.
220 * Note that we have to restart the scan every time we remove an entry
221 * from the list, since we can't remove the current item from the list.
222 */
223restart:
224 TAILQ_FOREACH(klp, &kauth_dangling_listeners, kl_link) {
225 if (strncmp(s1: klp->kl_identifier, s2: sp->ks_identifier,
226 n: strlen(s: klp->kl_identifier) + 1) == 0) {
227 /* found a match on the dangling listener list. add it to the
228 * the active scope.
229 */
230 if (kauth_add_callback_to_scope(sp, klp) == 0) {
231 TAILQ_REMOVE(&kauth_dangling_listeners, klp, kl_link);
232 } else {
233#if 0
234 printf("%s - failed to add listener to scope \"%s\" \n", __FUNCTION__, sp->ks_identifier);
235#endif
236 break;
237 }
238 goto restart;
239 }
240 }
241
242 KAUTH_SCOPEUNLOCK();
243 return sp;
244}
245
246
247
248void
249kauth_deregister_scope(kauth_scope_t scope)
250{
251 int i;
252
253 KAUTH_SCOPELOCK();
254
255 TAILQ_REMOVE(&kauth_scopes, scope, ks_link);
256
257 /* relocate listeners back to the waiting list */
258 for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
259 if (scope->ks_listeners[i].kll_listenerp != NULL) {
260 TAILQ_INSERT_TAIL(&kauth_dangling_listeners, scope->ks_listeners[i].kll_listenerp, kl_link);
261 scope->ks_listeners[i].kll_listenerp = NULL;
262 /*
263 * XXX - kauth_todo - WARNING, do not clear kll_callback or
264 * kll_idata here. they are part of our scope unlisten race hack
265 */
266 }
267 }
268 KAUTH_SCOPEUNLOCK();
269 kfree_type(struct kauth_scope, scope);
270
271 return;
272}
273
274kauth_listener_t
275kauth_listen_scope(const char *identifier, kauth_scope_callback_t callback, void *idata)
276{
277 kauth_listener_t klp;
278 kauth_scope_t sp;
279
280 if ((klp = kauth_alloc_listener(identifier, callback, idata)) == NULL) {
281 return NULL;
282 }
283
284 /*
285 * Lock the scope list and check to see whether this scope already exists.
286 */
287 KAUTH_SCOPELOCK();
288 TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
289 if (strncmp(s1: sp->ks_identifier, s2: identifier,
290 n: strlen(s: sp->ks_identifier) + 1) == 0) {
291 /* scope exists, add it to scope listener table */
292 if (kauth_add_callback_to_scope(sp, klp) == 0) {
293 KAUTH_SCOPEUNLOCK();
294 return klp;
295 }
296 /* table already full */
297 KAUTH_SCOPEUNLOCK();
298 kfree_type(struct kauth_listener, klp);
299 return NULL;
300 }
301 }
302
303 /* scope doesn't exist, put on waiting list. */
304 TAILQ_INSERT_TAIL(&kauth_dangling_listeners, klp, kl_link);
305
306 KAUTH_SCOPEUNLOCK();
307
308 return klp;
309}
310
311void
312kauth_unlisten_scope(kauth_listener_t listener)
313{
314 kauth_scope_t sp;
315 kauth_listener_t klp;
316 int i, listener_count, do_free;
317
318 KAUTH_SCOPELOCK();
319
320 /* search the active scope for this listener */
321 TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
322 do_free = 0;
323 if ((sp->ks_flags & KS_F_HAS_LISTENERS) != 0) {
324 listener_count = 0;
325 for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
326 if (sp->ks_listeners[i].kll_listenerp == listener) {
327 sp->ks_listeners[i].kll_listenerp = NULL;
328 do_free = 1;
329 /*
330 * XXX - kauth_todo - WARNING, do not clear kll_callback or
331 * kll_idata here. they are part of our scope unlisten race hack
332 */
333 } else if (sp->ks_listeners[i].kll_listenerp != NULL) {
334 listener_count++;
335 }
336 }
337 if (do_free) {
338 if (listener_count == 0) {
339 sp->ks_flags &= ~KS_F_HAS_LISTENERS;
340 }
341 KAUTH_SCOPEUNLOCK();
342 kfree_type(struct kauth_listener, listener);
343 return;
344 }
345 }
346 }
347
348 /* if not active, check the dangling list */
349 TAILQ_FOREACH(klp, &kauth_dangling_listeners, kl_link) {
350 if (klp == listener) {
351 TAILQ_REMOVE(&kauth_dangling_listeners, klp, kl_link);
352 KAUTH_SCOPEUNLOCK();
353 kfree_type(struct kauth_listener, listener);
354 return;
355 }
356 }
357
358 KAUTH_SCOPEUNLOCK();
359 return;
360}
361
362/*
363 * Authorization requests.
364 *
365 * Returns: 0 Success
366 * EPERM Operation not permitted
367 *
368 * Imputed: *arg3, modified Callback return - depends on callback
369 * modification of *arg3, if any
370 */
371int
372kauth_authorize_action(kauth_scope_t scope, kauth_cred_t credential, kauth_action_t action,
373 uintptr_t arg0, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
374{
375 int result, ret, i;
376
377 /* ask the scope */
378 if (scope->ks_callback != NULL) {
379 result = scope->ks_callback(credential, scope->ks_idata, action, arg0, arg1, arg2, arg3);
380 } else {
381 result = KAUTH_RESULT_DEFER;
382 }
383
384 /* check with listeners */
385 if ((scope->ks_flags & KS_F_HAS_LISTENERS) != 0) {
386 for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
387 /* XXX - kauth_todo - there is a race here if listener is removed - we will fix this post Tiger.
388 * Until the race is fixed our kext clients are responsible for all active requests that may
389 * be in their callbacks or on the way to their callbacks before they free kl_callback or kl_idata.
390 * We keep copies of these in our kauth_local_listener in an attempt to limit our expose to
391 * unlisten race.
392 */
393 if (scope->ks_listeners[i].kll_listenerp == NULL ||
394 scope->ks_listeners[i].kll_callback == NULL) {
395 continue;
396 }
397
398 ret = scope->ks_listeners[i].kll_callback(
399 credential, scope->ks_listeners[i].kll_idata,
400 action, arg0, arg1, arg2, arg3);
401 if ((ret == KAUTH_RESULT_DENY) ||
402 (result == KAUTH_RESULT_DEFER)) {
403 result = ret;
404 }
405 }
406 }
407
408 /* we need an explicit allow, or the auth fails */
409 /* XXX need a mechanism for auth failure to be signalled vs. denial */
410 return result == KAUTH_RESULT_ALLOW ? 0 : EPERM;
411}
412
413/*
414 * Default authorization handlers.
415 */
416int
417kauth_authorize_allow(__unused kauth_cred_t credential, __unused void *idata, __unused kauth_action_t action,
418 __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
419{
420 return KAUTH_RESULT_ALLOW;
421}
422
423#if 0
424/*
425 * Debugging support.
426 */
427static int
428kauth_scope_valid(kauth_scope_t scope)
429{
430 kauth_scope_t sp;
431
432 KAUTH_SCOPELOCK();
433 TAILQ_FOREACH(sp, &kauth_scopes, ks_link) {
434 if (sp == scope) {
435 break;
436 }
437 }
438 KAUTH_SCOPEUNLOCK();
439 return (sp == NULL) ? 0 : 1;
440}
441#endif
442
443/*
444 * Process authorization scope.
445 */
446
447int
448kauth_authorize_process(kauth_cred_t credential, kauth_action_t action, struct proc *process, uintptr_t arg1, uintptr_t arg2, uintptr_t arg3)
449{
450 return kauth_authorize_action(scope: kauth_scope_process, credential, action, arg0: (uintptr_t)process, arg1, arg2, arg3);
451}
452
453static int
454kauth_authorize_process_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
455 uintptr_t arg0, uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
456{
457 switch (action) {
458 case KAUTH_PROCESS_CANSIGNAL:
459 panic("KAUTH_PROCESS_CANSIGNAL not implemented");
460 /* XXX credential wrong here */
461 /* arg0 - process to signal
462 * arg1 - signal to send the process
463 */
464 if (cansignal(current_proc(), credential, (struct proc *)arg0, (int)arg1)) {
465 return KAUTH_RESULT_ALLOW;
466 }
467 break;
468 case KAUTH_PROCESS_CANTRACE:
469 /* current_proc() - process that will do the tracing
470 * arg0 - process to be traced
471 * arg1 - pointer to int - reason (errno) for denial
472 */
473 if (cantrace(cur_procp: current_proc(), creds: credential, traced_procp: (proc_t)arg0, errp: (int *)arg1)) {
474 return KAUTH_RESULT_ALLOW;
475 }
476 break;
477 }
478
479 /* no explicit result, so defer to others in the chain */
480 return KAUTH_RESULT_DEFER;
481}
482
483/*
484 * File system operation authorization scope. This is really only a notification
485 * of the file system operation, not an authorization check. Thus the result is
486 * not relevant.
487 * arguments passed to KAUTH_FILEOP_OPEN listeners
488 * arg0 is pointer to vnode (vnode *) for given user path.
489 * arg1 is pointer to path (char *) passed in to open.
490 * arguments passed to KAUTH_FILEOP_CLOSE listeners
491 * arg0 is pointer to vnode (vnode *) for file to be closed.
492 * arg1 is pointer to path (char *) of file to be closed.
493 * arg2 is close flags.
494 * arguments passed to KAUTH_FILEOP_WILL_RENAME listeners
495 * arg0 is pointer to vnode (vnode *) of the file being renamed
496 * arg1 is pointer to the "from" path (char *)
497 * arg2 is pointer to the "to" path (char *)
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 ||
537 action == KAUTH_FILEOP_CLOSE ||
538 action == KAUTH_FILEOP_EXEC ||
539 action == KAUTH_FILEOP_WILL_RENAME) {
540 /* get path to the given vnode as a convenience to our listeners.
541 */
542 namep = get_pathbuff();
543 name_len = MAXPATHLEN;
544 if (vn_getpath(vp: (vnode_t)arg0, pathbuf: namep, len: &name_len) != 0) {
545 release_pathbuff(path: namep);
546 return 0;
547 }
548 if (action == KAUTH_FILEOP_CLOSE ||
549 action == KAUTH_FILEOP_WILL_RENAME) {
550 /*
551 * - Close has some flags that come in via arg1.
552 * - Will-rename wants to pass the vnode and
553 * both paths to the listeners ("to" path
554 * starts in arg1, moves to arg2).
555 */
556 arg2 = arg1;
557 }
558 arg1 = (uintptr_t)namep;
559 }
560 kauth_authorize_action(scope: kauth_scope_fileop, credential, action, arg0, arg1, arg2, arg3: 0);
561
562 if (namep != NULL) {
563 release_pathbuff(path: namep);
564 }
565
566 return 0;
567}
568
569/*
570 * Generic authorization scope.
571 */
572
573int
574kauth_authorize_generic(kauth_cred_t credential, kauth_action_t action)
575{
576 if (credential == NULL) {
577 panic("auth against NULL credential");
578 }
579
580 return kauth_authorize_action(scope: kauth_scope_generic, credential, action, arg0: 0, arg1: 0, arg2: 0, arg3: 0);
581}
582
583static int
584kauth_authorize_generic_callback(kauth_cred_t credential, __unused void *idata, kauth_action_t action,
585 __unused uintptr_t arg0, __unused uintptr_t arg1, __unused uintptr_t arg2, __unused uintptr_t arg3)
586{
587 switch (action) {
588 case KAUTH_GENERIC_ISSUSER:
589 /* XXX == 0 ? */
590 return (kauth_cred_getuid(cred: credential) == 0) ?
591 KAUTH_RESULT_ALLOW : KAUTH_RESULT_DENY;
592 }
593
594 /* no explicit result, so defer to others in the chain */
595 return KAUTH_RESULT_DEFER;
596}
597
598/*
599 * ACL evaluator.
600 *
601 * Determines whether the credential has the requested rights for an object secured by the supplied
602 * ACL.
603 *
604 * Evaluation proceeds from the top down, with access denied if any ACE denies any of the requested
605 * rights, or granted if all of the requested rights are satisfied by the ACEs so far.
606 */
607int
608kauth_acl_evaluate(kauth_cred_t cred, kauth_acl_eval_t eval)
609{
610 int applies, error, i, gotguid;
611 kauth_ace_t ace;
612 guid_t guid;
613 uint32_t rights;
614 int wkguid;
615
616 if (cred == NULL) {
617 KAUTH_DEBUG(" ACL - got NULL credential");
618 return EINVAL;
619 }
620
621 if (eval == NULL) {
622 KAUTH_DEBUG(" ACL - got NULL ACL evaluator");
623 return EINVAL;
624 }
625
626 /* always allowed to do nothing */
627 if (eval->ae_requested == 0) {
628 eval->ae_result = KAUTH_RESULT_ALLOW;
629 return 0;
630 }
631
632 eval->ae_residual = eval->ae_requested;
633 eval->ae_found_deny = FALSE;
634
635 /*
636 * Get our guid for comparison purposes.
637 */
638 if ((error = kauth_cred_getguid(cred: cred, guidp: &guid)) != 0) {
639 KAUTH_DEBUG(" ACL - can't get credential GUID (%d)", error);
640 error = 0;
641 gotguid = 0;
642 } else {
643 gotguid = 1;
644 }
645
646 KAUTH_DEBUG(" ACL - %d entries, initial residual %x", eval->ae_count, eval->ae_residual);
647 for (i = 0, ace = eval->ae_acl; i < eval->ae_count; i++, ace++) {
648 /*
649 * Skip inherit-only entries.
650 */
651 if (ace->ace_flags & KAUTH_ACE_ONLY_INHERIT) {
652 continue;
653 }
654
655 /*
656 * Expand generic rights, if appropriate.
657 */
658 rights = ace->ace_rights;
659 if (rights & KAUTH_ACE_GENERIC_ALL) {
660 rights |= eval->ae_exp_gall;
661 }
662 if (rights & KAUTH_ACE_GENERIC_READ) {
663 rights |= eval->ae_exp_gread;
664 }
665 if (rights & KAUTH_ACE_GENERIC_WRITE) {
666 rights |= eval->ae_exp_gwrite;
667 }
668 if (rights & KAUTH_ACE_GENERIC_EXECUTE) {
669 rights |= eval->ae_exp_gexec;
670 }
671
672 /*
673 * Determine whether this entry applies to the current request. This
674 * saves us checking the GUID if the entry has nothing to do with what
675 * we're currently doing.
676 */
677 switch (ace->ace_flags & KAUTH_ACE_KINDMASK) {
678 case KAUTH_ACE_PERMIT:
679 if (!(eval->ae_residual & rights)) {
680 continue;
681 }
682 break;
683 case KAUTH_ACE_DENY:
684 if (!(eval->ae_requested & rights)) {
685 continue;
686 }
687 eval->ae_found_deny = TRUE;
688 break;
689 default:
690 /* we don't recognise this ACE, skip it */
691 continue;
692 }
693
694 /*
695 * Verify whether this entry applies to the credential.
696 */
697 wkguid = kauth_wellknown_guid(guid: &ace->ace_applicable);
698 switch (wkguid) {
699 case KAUTH_WKG_OWNER:
700 applies = eval->ae_options & KAUTH_AEVAL_IS_OWNER;
701 break;
702 case KAUTH_WKG_GROUP:
703 if (!gotguid || (eval->ae_options & KAUTH_AEVAL_IN_GROUP_UNKNOWN)) {
704 applies = ((ace->ace_flags & KAUTH_ACE_KINDMASK) == KAUTH_ACE_DENY);
705 } else {
706 applies = eval->ae_options & KAUTH_AEVAL_IN_GROUP;
707 }
708 break;
709 /* we short-circuit these here rather than wasting time calling the group membership code */
710 case KAUTH_WKG_EVERYBODY:
711 applies = 1;
712 break;
713 case KAUTH_WKG_NOBODY:
714 applies = 0;
715 break;
716
717 default:
718 /* check to see whether it's exactly us, or a group we are a member of */
719 applies = !gotguid ? 0 : kauth_guid_equal(guid1: &guid, guid2: &ace->ace_applicable);
720 KAUTH_DEBUG(" ACL - ACE applicable " K_UUID_FMT " caller " K_UUID_FMT " %smatched",
721 K_UUID_ARG(ace->ace_applicable), K_UUID_ARG(guid), applies ? "" : "not ");
722
723 if (!applies) {
724 error = !gotguid ? ENOENT : kauth_cred_ismember_guid(cred: cred, guidp: &ace->ace_applicable, resultp: &applies);
725 /*
726 * If we can't resolve group membership, we have to limit misbehaviour.
727 * If the ACE is an 'allow' ACE, assume the cred is not a member (avoid
728 * granting excess access). If the ACE is a 'deny' ACE, assume the cred
729 * is a member (avoid failing to deny).
730 */
731 if (error != 0) {
732 KAUTH_DEBUG(" ACL[%d] - can't get membership, making pessimistic assumption", i);
733 switch (ace->ace_flags & KAUTH_ACE_KINDMASK) {
734 case KAUTH_ACE_PERMIT:
735 applies = 0;
736 break;
737 case KAUTH_ACE_DENY:
738 applies = 1;
739 break;
740 }
741 } else {
742 KAUTH_DEBUG(" ACL - %s group member", applies ? "is" : "not");
743 }
744 } else {
745 KAUTH_DEBUG(" ACL - entry matches caller");
746 }
747 }
748 if (!applies) {
749 continue;
750 }
751
752 /*
753 * Apply ACE to outstanding rights.
754 */
755 switch (ace->ace_flags & KAUTH_ACE_KINDMASK) {
756 case KAUTH_ACE_PERMIT:
757 /* satisfy any rights that this ACE grants */
758 eval->ae_residual = eval->ae_residual & ~rights;
759 KAUTH_DEBUG(" ACL[%d] - rights %x leave residual %x", i, rights, eval->ae_residual);
760 /* all rights satisfied? */
761 if (eval->ae_residual == 0) {
762 eval->ae_result = KAUTH_RESULT_ALLOW;
763 return 0;
764 }
765 break;
766 case KAUTH_ACE_DENY:
767 /* deny the request if any of the requested rights is denied */
768 if (eval->ae_requested & rights) {
769 KAUTH_DEBUG(" ACL[%d] - denying based on %x", i, rights);
770 eval->ae_result = KAUTH_RESULT_DENY;
771 return 0;
772 }
773 break;
774 default:
775 KAUTH_DEBUG(" ACL - unknown entry kind %d", ace->ace_flags & KAUTH_ACE_KINDMASK);
776 break;
777 }
778 }
779 /* if not permitted, defer to other modes of authorisation */
780 eval->ae_result = KAUTH_RESULT_DEFER;
781 return 0;
782}
783
784/*
785 * Perform ACL inheritance and umask-ACL handling.
786 *
787 * Entries are inherited from the ACL on dvp. A caller-supplied
788 * ACL is in initial, and the result is output into product.
789 * If the process has a umask ACL and one is not supplied, we use
790 * the umask ACL.
791 * If isdir is set, the resultant ACL is for a directory, otherwise it is for a file.
792 */
793int
794kauth_acl_inherit(vnode_t dvp, kauth_acl_t initial, kauth_acl_t *product, int isdir, vfs_context_t ctx)
795{
796 int entries, error, index;
797 unsigned int i;
798 struct vnode_attr dva;
799 kauth_acl_t inherit, result;
800
801 /*
802 * Fetch the ACL from the directory. This should never fail.
803 * Note that we don't manage inheritance when the remote server is
804 * doing authorization, since this means server enforcement of
805 * inheritance semantics; we just want to compose the initial
806 * ACL and any inherited ACE entries from the container object.
807 *
808 * XXX TODO: <rdar://3634665> wants a "umask ACL" from the process.
809 */
810 inherit = NULL;
811 /*
812 * If there is no initial ACL, or there is, and the initial ACLs
813 * flags do not request "no inheritance", then we inherit. This allows
814 * initial object creation via open_extended() and mkdir_extended()
815 * to reject inheritance for themselves and for inferior nodes by
816 * specifying a non-NULL inital ACL which has the KAUTH_ACL_NO_INHERIT
817 * flag set in the flags field.
818 */
819 if ((initial == NULL || !(initial->acl_flags & KAUTH_ACL_NO_INHERIT)) &&
820 (dvp != NULL) && !vfs_authopaque(mp: vnode_mount(vp: dvp))) {
821 VATTR_INIT(&dva);
822 VATTR_WANTED(&dva, va_acl);
823 if ((error = vnode_getattr(vp: dvp, vap: &dva, ctx)) != 0) {
824 KAUTH_DEBUG(" ERROR - could not get parent directory ACL for inheritance");
825 return error;
826 }
827 if (VATTR_IS_SUPPORTED(&dva, va_acl)) {
828 inherit = dva.va_acl;
829 }
830 }
831
832 /*
833 * Compute the number of entries in the result ACL by scanning the
834 * input lists.
835 */
836 entries = 0;
837 if (inherit != NULL) {
838 for (i = 0; i < inherit->acl_entrycount; i++) {
839 if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT)) {
840 entries++;
841 }
842 }
843 }
844
845 if (initial == NULL) {
846 /*
847 * XXX 3634665 TODO: if the initial ACL is not specfied by
848 * XXX the caller, fetch the umask ACL from the process,
849 * and use it in place of "initial".
850 */
851 }
852
853 if (initial != NULL) {
854 if (initial->acl_entrycount != KAUTH_FILESEC_NOACL) {
855 entries += initial->acl_entrycount;
856 } else {
857 initial = NULL;
858 }
859 }
860
861 /*
862 * If there is no initial ACL, and no inheritable entries, the
863 * object should be created with no ACL at all.
864 * Note that this differs from the case where the initial ACL
865 * is empty, in which case the object must also have an empty ACL.
866 */
867 if ((entries == 0) && (initial == NULL)) {
868 *product = NULL;
869 error = 0;
870 goto out;
871 }
872
873 /*
874 * Allocate the result buffer.
875 */
876 if ((result = kauth_acl_alloc(size: entries)) == NULL) {
877 KAUTH_DEBUG(" ERROR - could not allocate %d-entry result buffer for inherited ACL", entries);
878 error = ENOMEM;
879 goto out;
880 }
881
882 /*
883 * Composition is simply:
884 * - initial direct ACEs
885 * - inherited ACEs from new parent
886 */
887 index = 0;
888 if (initial != NULL) {
889 for (i = 0; i < initial->acl_entrycount; i++) {
890 if (!(initial->acl_ace[i].ace_flags & KAUTH_ACE_INHERITED)) {
891 result->acl_ace[index++] = initial->acl_ace[i];
892 }
893 }
894 KAUTH_DEBUG(" INHERIT - applied %d of %d initial entries", index, initial->acl_entrycount);
895 }
896 if (inherit != NULL) {
897 for (i = 0; i < inherit->acl_entrycount; i++) {
898 /*
899 * Inherit onto this object? We inherit only if
900 * the target object is a container object and the
901 * KAUTH_ACE_DIRECTORY_INHERIT bit is set, OR if
902 * if the target object is not a container, and
903 * the KAUTH_ACE_FILE_INHERIT bit is set.
904 */
905 if (inherit->acl_ace[i].ace_flags & (isdir ? KAUTH_ACE_DIRECTORY_INHERIT : KAUTH_ACE_FILE_INHERIT)) {
906 result->acl_ace[index] = inherit->acl_ace[i];
907 result->acl_ace[index].ace_flags |= KAUTH_ACE_INHERITED;
908 result->acl_ace[index].ace_flags &= ~KAUTH_ACE_ONLY_INHERIT;
909 /*
910 * We do not re-inherit inheritance flags
911 * if the ACE from the container has a
912 * KAUTH_ACE_LIMIT_INHERIT, OR if the new
913 * object is not itself a container (since
914 * inheritance is always container-based).
915 */
916 if ((result->acl_ace[index].ace_flags & KAUTH_ACE_LIMIT_INHERIT) || !isdir) {
917 result->acl_ace[index].ace_flags &=
918 ~(KAUTH_ACE_INHERIT_CONTROL_FLAGS);
919 }
920 index++;
921 }
922 }
923 }
924 result->acl_entrycount = index;
925 *product = result;
926 KAUTH_DEBUG(" INHERIT - product ACL has %d entries", index);
927 error = 0;
928out:
929 if (inherit != NULL) {
930 kauth_acl_free(fsp: inherit);
931 }
932 return error;
933}
934
935/*
936 * Optimistically copy in a kauth_filesec structure
937 *
938 * Parameters: xsecurity user space kauth_filesec_t
939 * xsecdstpp pointer to kauth_filesec_t to be
940 * modified to contain the contain a
941 * pointer to an allocated copy of the
942 * user space argument
943 *
944 * Returns: 0 Success
945 * ENOMEM Insufficient memory for the copy.
946 * EINVAL The user space data was invalid, or
947 * there were too many ACE entries.
948 * EFAULT The user space address was invalid;
949 * this may mean 'fsec_entrycount' in
950 * the user copy is corrupt/incorrect.
951 *
952 * Implicit returns: xsecdestpp, modified (only if successful!)
953 *
954 * Notes: The returned kauth_filesec_t is in host byte order
955 *
956 * The caller is responsible for freeing the returned
957 * kauth_filesec_t in the success case using the function
958 * kauth_filesec_free()
959 *
960 * Our largest initial guess is 32; this needs to move to
961 * a manifest constant in <sys/kauth.h>.
962 */
963int
964kauth_copyinfilesec(user_addr_t xsecurity, kauth_filesec_t *xsecdestpp)
965{
966 int error;
967 kauth_filesec_t fsec;
968 size_t count;
969 size_t copysize;
970
971 error = 0;
972 fsec = NULL;
973
974 /*
975 * Make a guess at the size of the filesec. We start with the base
976 * pointer, and look at how much room is left on the page, clipped
977 * to a sensible upper bound. If it turns out this isn't enough,
978 * we'll size based on the actual ACL contents and come back again.
979 *
980 * The upper bound must be less than KAUTH_ACL_MAX_ENTRIES. The
981 * value here is fairly arbitrary. It's ok to have a zero count.
982 *
983 * Because we're just using these values to make a guess about the
984 * number of entries, the actual address doesn't matter, only their
985 * relative offsets into the page. We take advantage of this to
986 * avoid an overflow in the rounding step (this is a user-provided
987 * parameter, so caution pays off).
988 */
989 {
990 user_addr_t known_bound = (xsecurity & PAGE_MASK) + KAUTH_FILESEC_SIZE(0);
991 user_addr_t uaddr = (user_addr_t)mach_vm_round_page(x: known_bound);
992 count = (uaddr - known_bound) / sizeof(struct kauth_ace);
993 }
994 if (count > 32) {
995 count = 32;
996 }
997restart:
998 if ((fsec = kauth_filesec_alloc(size: (int)count)) == NULL) {
999 error = ENOMEM;
1000 goto out;
1001 }
1002 copysize = KAUTH_FILESEC_SIZE(count);
1003 if ((error = copyin(xsecurity, (caddr_t)fsec, copysize)) != 0) {
1004 goto out;
1005 }
1006
1007 /* validate the filesec header */
1008 if (fsec->fsec_magic != KAUTH_FILESEC_MAGIC) {
1009 error = EINVAL;
1010 goto out;
1011 }
1012
1013 /*
1014 * Is there an ACL payload, and is it too big?
1015 */
1016 if ((fsec->fsec_entrycount != KAUTH_FILESEC_NOACL) &&
1017 (fsec->fsec_entrycount > count)) {
1018 if (fsec->fsec_entrycount > KAUTH_ACL_MAX_ENTRIES) {
1019 /* XXX This should be E2BIG */
1020 error = EINVAL;
1021 goto out;
1022 }
1023 count = fsec->fsec_entrycount;
1024 kauth_filesec_free(fsp: fsec);
1025 goto restart;
1026 }
1027
1028out:
1029 if (error) {
1030 if (fsec) {
1031 kauth_filesec_free(fsp: fsec);
1032 }
1033 } else {
1034 *xsecdestpp = fsec;
1035 AUDIT_ARG(opaque, fsec, copysize);
1036 }
1037 return error;
1038}
1039
1040/*
1041 * Allocate a block of memory containing a filesec structure, immediately
1042 * followed by 'count' kauth_ace structures.
1043 *
1044 * Parameters: count Number of kauth_ace structures needed
1045 *
1046 * Returns: !NULL A pointer to the allocated block
1047 * NULL Invalid 'count' or insufficient memory
1048 *
1049 * Notes: Returned memory area assumes that the structures are packed
1050 * densely, so this function may only be used by code that also
1051 * assumes no padding following structures.
1052 *
1053 * The returned structure must be freed by the caller using the
1054 * function kauth_filesec_free(), in case we decide to use an
1055 * allocation mechanism that is aware of the object size at some
1056 * point, since the object size is only available by introspecting
1057 * the object itself.
1058 */
1059kauth_filesec_t
1060kauth_filesec_alloc(int count)
1061{
1062 kauth_filesec_t fsp;
1063
1064 /* if the caller hasn't given us a valid size hint, assume the worst */
1065 if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES)) {
1066 return NULL;
1067 }
1068
1069 fsp = kalloc_data(KAUTH_FILESEC_SIZE(count), Z_WAITOK);
1070 if (fsp != NULL) {
1071 fsp->fsec_magic = KAUTH_FILESEC_MAGIC;
1072 fsp->fsec_owner = kauth_null_guid;
1073 fsp->fsec_group = kauth_null_guid;
1074 fsp->fsec_entrycount = KAUTH_FILESEC_NOACL;
1075 fsp->fsec_flags = 0;
1076 }
1077 return fsp;
1078}
1079
1080/*
1081 * Free a kauth_filesec_t that was previous allocated, either by a direct
1082 * call to kauth_filesec_alloc() or by calling a function that calls it.
1083 *
1084 * Parameters: fsp kauth_filesec_t to free
1085 *
1086 * Returns: (void)
1087 *
1088 * Notes: The kauth_filesec_t to be freed is assumed to be in host
1089 * byte order so that this function can introspect it in the
1090 * future to determine its size, if necesssary.
1091 */
1092void
1093kauth_filesec_free(kauth_filesec_t fsp)
1094{
1095#ifdef KAUTH_DEBUG_ENABLE
1096 if (fsp == KAUTH_FILESEC_NONE) {
1097 panic("freeing KAUTH_FILESEC_NONE");
1098 }
1099 if (fsp == KAUTH_FILESEC_WANTED) {
1100 panic("freeing KAUTH_FILESEC_WANTED");
1101 }
1102#endif
1103 kfree_data_addr(fsp);
1104}
1105
1106/*
1107 * Set the endianness of a filesec and an ACL; if 'acl' is NULL, use the
1108 * ACL interior to 'fsec' instead. If the endianness doesn't change, then
1109 * this function will have no effect.
1110 *
1111 * Parameters: kendian The endianness to set; this is either
1112 * KAUTH_ENDIAN_HOST or KAUTH_ENDIAN_DISK.
1113 * fsec The filesec to convert.
1114 * acl The ACL to convert (optional)
1115 *
1116 * Returns: (void)
1117 *
1118 * Notes: We use ntohl() because it has a transitive property on Intel
1119 * machines and no effect on PPC mancines. This guarantees us
1120 * that the swapping only occurs if the endiannes is wrong.
1121 */
1122void
1123kauth_filesec_acl_setendian(int kendian, kauth_filesec_t fsec, kauth_acl_t acl)
1124{
1125 uint32_t compare_magic = KAUTH_FILESEC_MAGIC;
1126 uint32_t invert_magic = ntohl(KAUTH_FILESEC_MAGIC);
1127 uint32_t compare_acl_entrycount;
1128 uint32_t i;
1129
1130 if (compare_magic == invert_magic) {
1131 return;
1132 }
1133
1134 /* If no ACL, use ACL interior to 'fsec' instead */
1135 if (acl == NULL) {
1136 acl = &fsec->fsec_acl;
1137 }
1138
1139 compare_acl_entrycount = acl->acl_entrycount;
1140
1141 /*
1142 * Only convert what needs to be converted, and only if the arguments
1143 * are valid. The following switch and tests effectively reject
1144 * conversions on invalid magic numbers as a desirable side effect.
1145 */
1146 switch (kendian) {
1147 case KAUTH_ENDIAN_HOST: /* not in host, convert to host */
1148 if (fsec->fsec_magic != invert_magic) {
1149 return;
1150 }
1151 /* acl_entrycount is byteswapped */
1152 compare_acl_entrycount = ntohl(acl->acl_entrycount);
1153 break;
1154 case KAUTH_ENDIAN_DISK: /* not in disk, convert to disk */
1155 if (fsec->fsec_magic != compare_magic) {
1156 return;
1157 }
1158 break;
1159 default: /* bad argument */
1160 return;
1161 }
1162
1163 /* We are go for conversion */
1164 fsec->fsec_magic = ntohl(fsec->fsec_magic);
1165 acl->acl_entrycount = ntohl(acl->acl_entrycount);
1166 if (compare_acl_entrycount != KAUTH_FILESEC_NOACL) {
1167 acl->acl_flags = ntohl(acl->acl_flags);
1168
1169 /* swap ACE rights and flags */
1170 for (i = 0; i < compare_acl_entrycount; i++) {
1171 acl->acl_ace[i].ace_flags = ntohl(acl->acl_ace[i].ace_flags);
1172 acl->acl_ace[i].ace_rights = ntohl(acl->acl_ace[i].ace_rights);
1173 }
1174 }
1175}
1176
1177/*
1178 * Allocate an ACL buffer.
1179 */
1180kauth_acl_t
1181kauth_acl_alloc(int count)
1182{
1183 /* if the caller hasn't given us a valid size hint, assume the worst */
1184 if ((count < 0) || (count > KAUTH_ACL_MAX_ENTRIES)) {
1185 return NULL;
1186 }
1187
1188 return kalloc_data(KAUTH_ACL_SIZE(count), Z_WAITOK | Z_ZERO);
1189}
1190
1191void
1192kauth_acl_free(kauth_acl_t aclp)
1193{
1194 kfree_data_addr(aclp);
1195}
1196
1197
1198/*
1199 * WARNING - caller must hold KAUTH_SCOPELOCK
1200 */
1201static int
1202kauth_add_callback_to_scope(kauth_scope_t sp, kauth_listener_t klp)
1203{
1204 int i;
1205
1206 for (i = 0; i < KAUTH_SCOPE_MAX_LISTENERS; i++) {
1207 if (sp->ks_listeners[i].kll_listenerp == NULL) {
1208 sp->ks_listeners[i].kll_callback = klp->kl_callback;
1209 sp->ks_listeners[i].kll_idata = klp->kl_idata;
1210 sp->ks_listeners[i].kll_listenerp = klp;
1211 sp->ks_flags |= KS_F_HAS_LISTENERS;
1212 return 0;
1213 }
1214 }
1215 return ENOSPC;
1216}
1217