diff options
Diffstat (limited to 'usr/src/uts/common/os/sid.c')
-rw-r--r-- | usr/src/uts/common/os/sid.c | 395 |
1 files changed, 395 insertions, 0 deletions
diff --git a/usr/src/uts/common/os/sid.c b/usr/src/uts/common/os/sid.c new file mode 100644 index 0000000000..d5bef7def5 --- /dev/null +++ b/usr/src/uts/common/os/sid.c @@ -0,0 +1,395 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +/* + * Copyright 2007 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/* + * Sid manipulation (stubs). + */ + +#include <sys/atomic.h> +#include <sys/avl.h> +#include <sys/cmn_err.h> +#include <sys/kmem.h> +#include <sys/mutex.h> +#include <sys/sid.h> +#include <sys/sysmacros.h> +#include <sys/systm.h> + +static kmutex_t sid_lock; +static avl_tree_t sid_tree; +static boolean_t sid_inited = B_FALSE; + +static ksiddomain_t +*ksid_enterdomain(const char *dom) +{ + size_t len = strlen(dom) + 1; + ksiddomain_t *res; + + ASSERT(MUTEX_HELD(&sid_lock)); + res = kmem_alloc(sizeof (ksiddomain_t), KM_SLEEP); + res->kd_len = (uint_t)len; + res->kd_name = kmem_alloc(len, KM_SLEEP); + bcopy(dom, res->kd_name, len); + + res->kd_ref = 1; + + avl_add(&sid_tree, res); + + return (res); +} + +void +ksid_hold(ksid_t *ks) +{ + if (ks->ks_domain != NULL) + ksiddomain_hold(ks->ks_domain); +} + +void +ksid_rele(ksid_t *ks) +{ + if (ks->ks_domain != NULL) + ksiddomain_rele(ks->ks_domain); +} + +void +ksiddomain_hold(ksiddomain_t *kd) +{ + atomic_add_32(&kd->kd_ref, 1); +} + +void +ksiddomain_rele(ksiddomain_t *kd) +{ + if (atomic_add_32_nv(&kd->kd_ref, -1) == 0) { + /* + * The kd reference can only be incremented from 0 when + * the sid_lock is held; so we lock and then check need to + * check for 0 again. + */ + mutex_enter(&sid_lock); + if (kd->kd_ref == 0) { + avl_remove(&sid_tree, kd); + kmem_free(kd->kd_name, kd->kd_len); + kmem_free(kd, sizeof (*kd)); + } + mutex_exit(&sid_lock); + } +} + +void +ksidlist_hold(ksidlist_t *ksl) +{ + atomic_add_32(&ksl->ksl_ref, 1); +} + +void +ksidlist_rele(ksidlist_t *ksl) +{ + if (atomic_add_32_nv(&ksl->ksl_ref, -1) == 0) { + int i; + + for (i = 0; i < ksl->ksl_nsid; i++) + ksid_rele(&ksl->ksl_sids[i]); + + kmem_free(ksl, KSIDLIST_MEM(ksl->ksl_nsid)); + } +} + +static int +ksid_cmp(const void *a, const void *b) +{ + const ksiddomain_t *ap = a; + const ksiddomain_t *bp = b; + int res; + + res = strcmp(ap->kd_name, bp->kd_name); + if (res > 0) + return (1); + if (res != 0) + return (-1); + return (0); +} + +/* + * Lookup the named domain in the AVL tree. + * If no entry is found, add the domain to the AVL tree. + * The domain is returned held and needs to be released + * when done. + */ +ksiddomain_t +*ksid_lookupdomain(const char *dom) +{ + ksiddomain_t *res; + ksiddomain_t tmpl; + + mutex_enter(&sid_lock); + + if (!sid_inited) { + avl_create(&sid_tree, ksid_cmp, sizeof (ksiddomain_t), + offsetof(ksiddomain_t, kd_link)); + + res = ksid_enterdomain(dom); + sid_inited = B_TRUE; + mutex_exit(&sid_lock); + return (res); + } + + tmpl.kd_name = (char *)dom; + + res = avl_find(&sid_tree, &tmpl, NULL); + if (res == NULL) { + res = ksid_enterdomain(dom); + } else { + ksiddomain_hold(res); + } + + mutex_exit(&sid_lock); + return (res); +} + +const char * +ksid_getdomain(ksid_t *ks) +{ + return (ks->ks_domain->kd_name); +} + +uint_t +ksid_getrid(ksid_t *ks) +{ + return (ks->ks_rid); +} + +int +ksid_lookup(uid_t id, ksid_t *res) +{ + uid_t tmp; + + if (idmap_call_byid(id, res) == -1) + return (-1); + + tmp = idmap_call_bysid(res); + if (tmp != id) + cmn_err(CE_WARN, "The idmapper has gone bonkers"); + res->ks_id = id; + + return (0); +} + +credsid_t * +kcrsid_alloc(void) +{ + credsid_t *kcr = kmem_zalloc(sizeof (*kcr), KM_SLEEP); + kcr->kr_ref = 1; + return (kcr); +} + +/* + * Returns a credsid_t with a refcount of 1. + */ +static credsid_t * +kcrsid_dup(credsid_t *org) +{ + credsid_t *new; + ksid_index_t ki; + + if (org == NULL) + return (kcrsid_alloc()); + if (org->kr_ref == 1) + return (org); + new = kcrsid_alloc(); + + /* Copy, then update reference counts */ + *new = *org; + new->kr_ref = 1; + for (ki = 0; ki < KSID_COUNT; ki++) + ksid_hold(&new->kr_sidx[ki]); + + if (new->kr_sidlist != NULL) + ksidlist_hold(new->kr_sidlist); + + kcrsid_rele(org); + return (new); +} + +void +kcrsid_hold(credsid_t *kcr) +{ + atomic_add_32(&kcr->kr_ref, 1); +} + +void +kcrsid_rele(credsid_t *kcr) +{ + if (atomic_add_32_nv(&kcr->kr_ref, -1) == 0) { + ksid_index_t i; + + for (i = 0; i < KSID_COUNT; i++) + ksid_rele(&kcr->kr_sidx[i]); + + if (kcr->kr_sidlist != NULL) + ksidlist_rele(kcr->kr_sidlist); + + kmem_free(kcr, sizeof (*kcr)); + } +} + +/* + * Copy the SID credential into a previously allocated piece of memory. + */ +void +kcrsidcopy_to(const credsid_t *okcr, credsid_t *nkcr) +{ + int i; + + ASSERT(nkcr->kr_ref == 1); + + if (okcr == NULL) + return; + *nkcr = *okcr; + for (i = 0; i < KSID_COUNT; i++) + ksid_hold(&nkcr->kr_sidx[i]); + if (nkcr->kr_sidlist != NULL) + ksidlist_hold(nkcr->kr_sidlist); + nkcr->kr_ref = 1; +} + +static int +kcrsid_sidcount(const credsid_t *kcr) +{ + int cnt = 0; + int i; + + if (kcr == NULL) + return (0); + + for (i = 0; i < KSID_COUNT; i++) + if (kcr->kr_sidx[i].ks_domain != NULL) + cnt++; + + if (kcr->kr_sidlist != NULL) + cnt += kcr->kr_sidlist->ksl_nsid; + return (cnt); +} + +/* + * Argument needs to be a ksid_t with a properly held ks_domain reference. + */ +credsid_t * +kcrsid_setsid(credsid_t *okcr, ksid_t *ksp, ksid_index_t i) +{ + int ocnt = kcrsid_sidcount(okcr); + credsid_t *nkcr; + + /* + * Unset the particular ksid; if there are no other SIDs or if this + * is the last SID, remove the auxilary data structure. + */ + if (ksp == NULL) { + if (ocnt == 0 || + (ocnt == 1 && okcr->kr_sidx[i].ks_domain != NULL)) { + if (okcr != NULL) + kcrsid_rele(okcr); + return (NULL); + } + } + nkcr = kcrsid_dup(okcr); + ksid_rele(&nkcr->kr_sidx[i]); + if (ksp == NULL) + bzero(&nkcr->kr_sidx[i], sizeof (ksid_t)); + else + nkcr->kr_sidx[i] = *ksp; + + return (nkcr); +} + +/* + * Argument needs to be a ksidlist_t with properly held ks_domain references + * and a reference count taking the new reference into account. + */ +credsid_t * +kcrsid_setsidlist(credsid_t *okcr, ksidlist_t *ksl) +{ + int ocnt = kcrsid_sidcount(okcr); + credsid_t *nkcr; + + /* + * Unset the sidlist; if there are no further SIDs, remove the + * auxilary data structure. + */ + if (ksl == NULL) { + if (ocnt == 0 || (okcr->kr_sidlist != NULL && + ocnt == okcr->kr_sidlist->ksl_nsid)) { + if (okcr != NULL) + kcrsid_rele(okcr); + return (NULL); + } + } + nkcr = kcrsid_dup(okcr); + if (nkcr->kr_sidlist != NULL) + ksidlist_rele(nkcr->kr_sidlist); + + nkcr->kr_sidlist = ksl; + return (nkcr); +} + +ksidlist_t * +kcrsid_gidstosids(int ngrp, gid_t *grp) +{ + int i; + ksidlist_t *list; + int cnt; + + if (ngrp == 0) + return (NULL); + + cnt = 0; + list = kmem_zalloc(KSIDLIST_MEM(ngrp), KM_SLEEP); + + list->ksl_nsid = ngrp; + list->ksl_ref = 1; + + for (i = 0; i < ngrp; i++) { + if (grp[i] > MAXUID) { + list->ksl_neid++; + if (ksid_lookup(grp[i], &list->ksl_sids[i]) != 0) { + while (--i >= 0) + ksid_rele(&list->ksl_sids[i]); + cnt = 0; + break; + } + cnt++; + } else { + list->ksl_sids[i].ks_id = grp[i]; + } + } + if (cnt == 0) { + kmem_free(list, KSIDLIST_MEM(ngrp)); + return (NULL); + } + return (list); +} |