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