summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/os/sid.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/uts/common/os/sid.c')
-rw-r--r--usr/src/uts/common/os/sid.c395
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);
+}