Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files. =================================================================== RCS file: /ftp/cvs/cvsroot/src/sys/kern/kern_auth.c,v rcsdiff: /ftp/cvs/cvsroot/src/sys/kern/kern_auth.c,v: warning: Unknown phrases like `commitid ...;' are present. retrieving revision 1.18 retrieving revision 1.18.2.7 diff -u -p -r1.18 -r1.18.2.7 --- src/sys/kern/kern_auth.c 2006/09/02 20:10:24 1.18 +++ src/sys/kern/kern_auth.c 2007/02/09 21:03:53 1.18.2.7 @@ -1,4 +1,4 @@ -/* $NetBSD: kern_auth.c,v 1.18 2006/09/02 20:10:24 elad Exp $ */ +/* $NetBSD: kern_auth.c,v 1.18.2.7 2007/02/09 21:03:53 ad Exp $ */ /*- * Copyright (c) 2005, 2006 Elad Efrat @@ -12,10 +12,7 @@ * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * This product includes software developed by Elad Efrat. - * 4. The name of the author may not be used to endorse or promote products + * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR @@ -30,36 +27,27 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* - * Todo: - * - Garbage collection to pool_put() unused scopes/listeners. - */ +#include +__KERNEL_RCSID(0, "$NetBSD: kern_auth.c,v 1.18.2.7 2007/02/09 21:03:53 ad Exp $"); + +#define _KAUTH_PRIVATE -#include #include #include -#include #include #include #include #include -#include -#include +#include +#include +#include -/* - * Credentials. +/* + * Secmodel-specific credentials. */ -struct kauth_cred { - struct simplelock cr_lock; /* lock on cr_refcnt */ - u_int cr_refcnt; /* reference count */ - uid_t cr_uid; /* user id */ - uid_t cr_euid; /* effective user id */ - uid_t cr_svuid; /* saved effective user id */ - gid_t cr_gid; /* group id */ - gid_t cr_egid; /* effective group id */ - gid_t cr_svgid; /* saved effective group id */ - u_int cr_ngroups; /* number of groups */ - gid_t cr_groups[NGROUPS]; /* group memberships */ +struct kauth_key { + const char *ks_secmodel; /* secmodel */ + specificdata_key_t ks_key; /* key */ }; /* @@ -83,20 +71,27 @@ struct kauth_scope { SIMPLEQ_ENTRY(kauth_scope) next_scope; /* scope list */ }; -static POOL_INIT(kauth_scope_pool, sizeof(struct kauth_scope), 0, 0, 0, - "kauth_scopepl", &pool_allocator_nointr); -static POOL_INIT(kauth_listener_pool, sizeof(struct kauth_listener), 0, 0, 0, - "kauth_listenerpl", &pool_allocator_nointr); +static int kauth_cred_hook(kauth_cred_t, kauth_action_t, void *, void *); + static POOL_INIT(kauth_cred_pool, sizeof(struct kauth_cred), 0, 0, 0, - "kauth_credpl", &pool_allocator_nointr); + "kauthcredpl", &pool_allocator_nointr); /* List of scopes and its lock. */ static SIMPLEQ_HEAD(, kauth_scope) scope_list; -static struct simplelock scopes_lock; /* Built-in scopes: generic, process. */ static kauth_scope_t kauth_builtin_scope_generic; +static kauth_scope_t kauth_builtin_scope_system; static kauth_scope_t kauth_builtin_scope_process; +static kauth_scope_t kauth_builtin_scope_network; +static kauth_scope_t kauth_builtin_scope_machdep; +static kauth_scope_t kauth_builtin_scope_device; +static kauth_scope_t kauth_builtin_scope_cred; + +static unsigned int nsecmodels = 0; + +static specificdata_domain_t kauth_domain; +krwlock_t kauth_lock; /* Allocate new, empty kauth credentials. */ kauth_cred_t @@ -106,8 +101,10 @@ kauth_cred_alloc(void) cred = pool_get(&kauth_cred_pool, PR_WAITOK); memset(cred, 0, sizeof(*cred)); - simple_lock_init(&cred->cr_lock); + mutex_init(&cred->cr_lock, MUTEX_DEFAULT, IPL_NONE); cred->cr_refcnt = 1; + specificdata_init(kauth_domain, &cred->cr_sd); + kauth_cred_hook(cred, KAUTH_CRED_INIT, NULL, NULL); return (cred); } @@ -119,9 +116,9 @@ kauth_cred_hold(kauth_cred_t cred) KASSERT(cred != NULL); KASSERT(cred->cr_refcnt > 0); - simple_lock(&cred->cr_lock); + mutex_enter(&cred->cr_lock); cred->cr_refcnt++; - simple_unlock(&cred->cr_lock); + mutex_exit(&cred->cr_lock); } /* Decrease reference count to cred. If reached zero, free it. */ @@ -133,12 +130,16 @@ kauth_cred_free(kauth_cred_t cred) KASSERT(cred != NULL); KASSERT(cred->cr_refcnt > 0); - simple_lock(&cred->cr_lock); + mutex_enter(&cred->cr_lock); refcnt = --cred->cr_refcnt; - simple_unlock(&cred->cr_lock); + mutex_exit(&cred->cr_lock); - if (refcnt == 0) + if (refcnt == 0) { + kauth_cred_hook(cred, KAUTH_CRED_FREE, NULL, NULL); + specificdata_fini(kauth_domain, &cred->cr_sd); + mutex_destroy(&cred->cr_lock); pool_put(&kauth_cred_pool, cred); + } } void @@ -156,6 +157,8 @@ kauth_cred_clone(kauth_cred_t from, kaut to->cr_svgid = from->cr_svgid; to->cr_ngroups = from->cr_ngroups; memcpy(to->cr_groups, from->cr_groups, sizeof(to->cr_groups)); + + kauth_cred_hook(from, KAUTH_CRED_COPY, to, NULL); } /* @@ -201,6 +204,20 @@ kauth_cred_copy(kauth_cred_t cred) return (new_cred); } +void +kauth_proc_fork(struct proc *parent, struct proc *child) +{ + + mutex_enter(&parent->p_mutex); + kauth_cred_hold(parent->p_cred); + child->p_cred = parent->p_cred; + mutex_exit(&parent->p_mutex); + + /* XXX: relies on parent process stalling during fork() */ + kauth_cred_hook(parent->p_cred, KAUTH_CRED_FORK, parent, + child); +} + uid_t kauth_cred_getuid(kauth_cred_t cred) { @@ -373,21 +390,67 @@ kauth_cred_getgroups(kauth_cred_t cred, return (0); } +int +kauth_register_key(const char *secmodel, kauth_key_t *result) +{ + kauth_key_t k; + specificdata_key_t key; + int error; + + KASSERT(result != NULL); + + error = specificdata_key_create(kauth_domain, &key, NULL); + if (error) + return (error); + + k = kmem_alloc(sizeof(*k), KM_SLEEP); + k->ks_secmodel = secmodel; + k->ks_key = key; + + *result = k; + + return (0); +} + +int +kauth_deregister_key(kauth_key_t key) +{ + KASSERT(key != NULL); + + specificdata_key_delete(kauth_domain, key->ks_key); + kmem_free(key, sizeof(*key)); + + return (0); +} + +void * +kauth_cred_getdata(kauth_cred_t cred, kauth_key_t key) +{ + KASSERT(cred != NULL); + KASSERT(key != NULL); + + return (specificdata_getspecific(kauth_domain, &cred->cr_sd, + key->ks_key)); +} + +void +kauth_cred_setdata(kauth_cred_t cred, kauth_key_t key, void *data) +{ + KASSERT(cred != NULL); + KASSERT(key != NULL); + + specificdata_setspecific(kauth_domain, &cred->cr_sd, key->ks_key, data); +} + /* - * Match uids in two credentials. Checks if cred1 can access stuff owned by - * cred2. - * XXX: root bypasses this! + * Match uids in two credentials. */ -static int +int kauth_cred_uidmatch(kauth_cred_t cred1, kauth_cred_t cred2) { KASSERT(cred1 != NULL); KASSERT(cred2 != NULL); - /* Are we root? */ - if (cred1->cr_euid == 0) - return (1); - if (cred1->cr_uid == cred2->cr_uid || cred1->cr_euid == cred2->cr_uid || cred1->cr_uid == cred2->cr_euid || @@ -407,14 +470,14 @@ kauth_cred_getrefcnt(kauth_cred_t cred) /* * Convert userland credentials (struct uucred) to kauth_cred_t. - * XXX: For NFS code. + * XXX: For NFS & puffs */ -void -kauth_cred_uucvt(kauth_cred_t cred, const struct uucred *uuc) -{ +void +kauth_uucred_to_cred(kauth_cred_t cred, const struct uucred *uuc) +{ KASSERT(cred != NULL); KASSERT(uuc != NULL); - + cred->cr_refcnt = 1; cred->cr_uid = uuc->cr_uid; cred->cr_euid = uuc->cr_uid; @@ -428,6 +491,24 @@ kauth_cred_uucvt(kauth_cred_t cred, cons } /* + * Convert kauth_cred_t to userland credentials (struct uucred). + * XXX: For NFS & puffs + */ +void +kauth_cred_to_uucred(struct uucred *uuc, const kauth_cred_t cred) +{ + KASSERT(cred != NULL); + KASSERT(uuc != NULL); + int ng; + + ng = min(cred->cr_ngroups, NGROUPS); + uuc->cr_uid = cred->cr_euid; + uuc->cr_gid = cred->cr_egid; + uuc->cr_ngroups = ng; + kauth_cred_getgroups(cred, uuc->cr_groups, ng); +} + +/* * Compare kauth_cred_t and uucred credentials. * XXX: Modelled after crcmp() for NFS. */ @@ -511,7 +592,7 @@ kauth_ifindscope(const char *id) { kauth_scope_t scope; - /* XXX: assert lock on scope list? */ + KASSERT(rw_lock_held(&kauth_lock)); scope = NULL; SIMPLEQ_FOREACH(scope, &scope_list, next_scope) { @@ -534,25 +615,36 @@ kauth_register_scope(const char *id, kau void *cookie) { kauth_scope_t scope; - kauth_listener_t listener; + kauth_listener_t listener = NULL; /* XXX gcc */ /* Sanitize input */ if (id == NULL) return (NULL); /* Allocate space for a new scope and listener. */ - scope = pool_get(&kauth_scope_pool, PR_WAITOK); - listener = pool_get(&kauth_listener_pool, PR_WAITOK); + scope = kmem_alloc(sizeof(*scope), KM_SLEEP); + if (scope == NULL) + return NULL; + if (callback != NULL) { + listener = kmem_alloc(sizeof(*listener), KM_SLEEP); + if (listener == NULL) { + kmem_free(scope, sizeof(*scope)); + return (NULL); + } + } - /* Acquire scope list lock. */ - simple_lock(&scopes_lock); + /* + * Acquire scope list lock. + */ + rw_enter(&kauth_lock, RW_WRITER); /* Check we don't already have a scope with the same id */ if (kauth_ifindscope(id) != NULL) { - simple_unlock(&scopes_lock); + rw_exit(&kauth_lock); - pool_put(&kauth_scope_pool, scope); - pool_put(&kauth_listener_pool, listener); + kmem_free(scope, sizeof(*scope)); + if (callback != NULL) + kmem_free(listener, sizeof(*listener)); return (NULL); } @@ -575,7 +667,7 @@ kauth_register_scope(const char *id, kau /* Insert scope to scopes list */ SIMPLEQ_INSERT_TAIL(&scope_list, scope, next_scope); - simple_unlock(&scopes_lock); + rw_exit(&kauth_lock); return (scope); } @@ -584,21 +676,46 @@ kauth_register_scope(const char *id, kau * Initialize the kernel authorization subsystem. * * Initialize the scopes list lock. - * Register built-in scopes: generic, process. + * Create specificdata domain. + * Register the credentials scope, used in kauth(9) internally. + * Register built-in scopes: generic, system, process, network, machdep, device. */ void kauth_init(void) { SIMPLEQ_INIT(&scope_list); - simple_lock_init(&scopes_lock); + rw_init(&kauth_lock); + + /* Create specificdata domain. */ + kauth_domain = specificdata_domain_create(); + + /* Register credentials scope. */ + kauth_builtin_scope_cred = + kauth_register_scope(KAUTH_SCOPE_CRED, NULL, NULL); /* Register generic scope. */ kauth_builtin_scope_generic = kauth_register_scope(KAUTH_SCOPE_GENERIC, - kauth_authorize_cb_generic, NULL); + NULL, NULL); + + /* Register system scope. */ + kauth_builtin_scope_system = kauth_register_scope(KAUTH_SCOPE_SYSTEM, + NULL, NULL); /* Register process scope. */ kauth_builtin_scope_process = kauth_register_scope(KAUTH_SCOPE_PROCESS, - kauth_authorize_cb_process, NULL); + NULL, NULL); + + /* Register network scope. */ + kauth_builtin_scope_network = kauth_register_scope(KAUTH_SCOPE_NETWORK, + NULL, NULL); + + /* Register machdep scope. */ + kauth_builtin_scope_machdep = kauth_register_scope(KAUTH_SCOPE_MACHDEP, + NULL, NULL); + + /* Register device scope. */ + kauth_builtin_scope_device = kauth_register_scope(KAUTH_SCOPE_DEVICE, + NULL, NULL); } /* @@ -613,6 +730,7 @@ kauth_deregister_scope(kauth_scope_t sco if (scope != NULL) { /* Remove scope from list */ SIMPLEQ_REMOVE(&scope_list, scope, kauth_scope, next_scope); + kmem_free(scope, sizeof(*scope)); } } @@ -625,20 +743,28 @@ kauth_deregister_scope(kauth_scope_t sco */ kauth_listener_t kauth_listen_scope(const char *id, kauth_scope_callback_t callback, - void *cookie) + void *cookie) { kauth_scope_t scope; kauth_listener_t listener; - /* Find scope struct */ - simple_lock(&scopes_lock); + listener = kmem_alloc(sizeof(*listener), KM_SLEEP); + if (listener == NULL) + return (NULL); + + rw_enter(&kauth_lock, RW_WRITER); + + /* + * Find scope struct. + */ scope = kauth_ifindscope(id); - simple_unlock(&scopes_lock); - if (scope == NULL) + if (scope == NULL) { + rw_exit(&kauth_lock); + kmem_free(listener, sizeof(*listener)); return (NULL); + } /* Allocate listener */ - listener = pool_get(&kauth_listener_pool, PR_WAITOK); /* Initialize listener with parameters */ listener->func = callback; @@ -651,6 +777,8 @@ kauth_listen_scope(const char *id, kauth scope->nlisteners++; listener->scope = scope; + rw_exit(&kauth_lock); + return (listener); } @@ -662,10 +790,14 @@ kauth_listen_scope(const char *id, kauth void kauth_unlisten_scope(kauth_listener_t listener) { + if (listener != NULL) { + rw_enter(&kauth_lock, RW_WRITER); SIMPLEQ_REMOVE(&listener->scope->listenq, listener, kauth_listener, listener_next); listener->scope->nlisteners--; + rw_exit(&kauth_lock); + kmem_free(listener, sizeof(*listener)); } } @@ -691,82 +823,41 @@ kauth_authorize_action(kauth_scope_t sco simple_lock_only_held(NULL, "kauth_authorize_action"); #endif - /* Sanitize input */ - if (scope == NULL || cred == NULL) - return (EFAULT); - if (!action) - return (EINVAL); + KASSERT(cred != NULL); + KASSERT(action != 0); /* Short-circuit requests coming from the kernel. */ if (cred == NOCRED || cred == FSCRED) return (0); - /* Short-circuit requests when there are no listeners. */ - if (SIMPLEQ_EMPTY(&scope->listenq)) - return (0); + KASSERT(scope != NULL); - /* - * Each scope is associated with at least one listener. We need to - * traverse that list of listeners, as long as they return either - * KAUTH_REQUEST_DEFER or KAUTH_REQUEST_ALLOW. - */ fail = 0; allow = 0; + + /* rw_enter(&kauth_lock, RW_READER); XXX not yet */ SIMPLEQ_FOREACH(listener, &scope->listenq, listener_next) { error = listener->func(cred, action, scope->cookie, arg0, - arg1, arg2, arg3); + arg1, arg2, arg3); if (error == KAUTH_RESULT_ALLOW) allow = 1; else if (error == KAUTH_RESULT_DENY) fail = 1; } + /* rw_exit(&kauth_lock); */ - return ((allow && !fail) ? 0 : EPERM); -}; + if (fail) + return (EPERM); -/* - * Generic scope default callback. - */ -int -kauth_authorize_cb_generic(kauth_cred_t cred, kauth_action_t action, - void *cookie, void *arg0, void *arg1, void *arg2, - void *arg3) -{ - int error; - - error = KAUTH_RESULT_DEFER; + if (allow) + return (0); - switch (action) { - case KAUTH_GENERIC_ISSUSER: - /* Check if credential belongs to superuser. */ - if (cred->cr_euid == 0) { - u_short *acflag = (u_short *)arg0; - - if (acflag != NULL) - *acflag |= ASU; - - error = KAUTH_RESULT_ALLOW; - } else - error = KAUTH_RESULT_DENY; - break; - - case KAUTH_GENERIC_CANSEE: - if (!security_curtain) { - error = KAUTH_RESULT_ALLOW; - } else { - kauth_cred_t cred2 = arg0; - - if (kauth_cred_uidmatch(cred, cred2)) - error = KAUTH_RESULT_ALLOW; - else - error = KAUTH_RESULT_DENY; - } - break; - } + if (!nsecmodels) + return (0); - return (error); -} + return (EPERM); +}; /* * Generic scope authorization wrapper. @@ -779,55 +870,14 @@ kauth_authorize_generic(kauth_cred_t cre } /* - * Process scope default callback. + * System scope authorization wrapper. */ int -kauth_authorize_cb_process(kauth_cred_t cred, kauth_action_t action, - void *cookie, void *arg0, void *arg1, void *arg2, - void *arg3) +kauth_authorize_system(kauth_cred_t cred, kauth_action_t action, + enum kauth_system_req req, void *arg1, void *arg2, void *arg3) { - struct proc *p; - int error; - - error = KAUTH_RESULT_DEFER; - - p = arg0; - - switch (action) { - case KAUTH_PROCESS_CANSIGNAL: { - int signum; - - signum = (int)(unsigned long)arg1; - - if (kauth_cred_uidmatch(cred, p->p_cred) || - (signum == SIGCONT && (curproc->p_session == p->p_session))) - error = KAUTH_RESULT_ALLOW; - else - error = KAUTH_RESULT_DEFER; - break; - } - - case KAUTH_PROCESS_CANPTRACE: - if (kauth_cred_uidmatch(cred, p->p_cred)) - error = KAUTH_RESULT_ALLOW; - else - error = KAUTH_RESULT_DENY; - break; - - case KAUTH_PROCESS_CANSEE: - if (!security_curtain) { - error = KAUTH_RESULT_ALLOW; - } else { - if (kauth_cred_uidmatch(cred, p->p_cred)) - error = KAUTH_RESULT_ALLOW; - else - error = KAUTH_RESULT_DENY; - /* arg2 - type of information [XXX NOTIMPL] */ - } - break; - } - - return (error); + return (kauth_authorize_action(kauth_builtin_scope_system, cred, + action, (void *)req, arg1, arg2, arg3)); } /* @@ -840,3 +890,92 @@ kauth_authorize_process(kauth_cred_t cre return (kauth_authorize_action(kauth_builtin_scope_process, cred, action, p, arg1, arg2, arg3)); } + +/* + * Network scope authorization wrapper. + */ +int +kauth_authorize_network(kauth_cred_t cred, kauth_action_t action, + enum kauth_network_req req, void *arg1, void *arg2, void *arg3) +{ + return (kauth_authorize_action(kauth_builtin_scope_network, cred, + action, (void *)req, arg1, arg2, arg3)); +} + +int +kauth_authorize_machdep(kauth_cred_t cred, kauth_action_t action, + void *arg0, void *arg1, void *arg2, void *arg3) +{ + return (kauth_authorize_action(kauth_builtin_scope_machdep, cred, + action, arg0, arg1, arg2, arg3)); +} + +int +kauth_authorize_device(kauth_cred_t cred, kauth_action_t action, + void *arg0, void *arg1, void *arg2, void *arg3) +{ + return (kauth_authorize_action(kauth_builtin_scope_device, cred, + action, arg0, arg1, arg2, arg3)); +} + +int +kauth_authorize_device_tty(kauth_cred_t cred, kauth_action_t action, + struct tty *tty) +{ + return (kauth_authorize_action(kauth_builtin_scope_device, cred, + action, tty, NULL, NULL, NULL)); +} + +int +kauth_authorize_device_spec(kauth_cred_t cred, enum kauth_device_req req, + struct vnode *vp) +{ + return (kauth_authorize_action(kauth_builtin_scope_device, cred, + KAUTH_DEVICE_RAWIO_SPEC, (void *)req, vp, NULL, NULL)); +} + +int +kauth_authorize_device_passthru(kauth_cred_t cred, dev_t dev, u_long bits, + void *data) +{ + return (kauth_authorize_action(kauth_builtin_scope_device, cred, + KAUTH_DEVICE_RAWIO_PASSTHRU, (void *)bits, (void *)(u_long)dev, + data, NULL)); +} + +static int +kauth_cred_hook(kauth_cred_t cred, kauth_action_t action, void *arg0, + void *arg1) +{ + int r; + + r = kauth_authorize_action(kauth_builtin_scope_cred, cred, action, + arg0, arg1, NULL, NULL); + +#ifdef DIAGNOSTIC + if (!SIMPLEQ_EMPTY(&kauth_builtin_scope_cred->listenq)) + KASSERT(r == 0); +#endif /* DIAGNOSTIC */ + + return (r); +} + +void +secmodel_register(void) +{ + KASSERT(nsecmodels + 1 != 0); + + rw_enter(&kauth_lock, RW_WRITER); + nsecmodels++; + rw_exit(&kauth_lock); +} + +void +secmodel_deregister(void) +{ + KASSERT(nsecmodels != 0); + + rw_enter(&kauth_lock, RW_WRITER); + nsecmodels--; + rw_exit(&kauth_lock); +}