Home | History | Annotate | Download | only in os
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 /*
     28  * Sid manipulation (stubs).
     29  */
     30 
     31 #include <sys/atomic.h>
     32 #include <sys/avl.h>
     33 #include <sys/cmn_err.h>
     34 #include <sys/kmem.h>
     35 #include <sys/mutex.h>
     36 #include <sys/sid.h>
     37 #include <sys/sysmacros.h>
     38 #include <sys/systm.h>
     39 #include <sys/kidmap.h>
     40 #include <sys/idmap.h>
     41 
     42 static kmutex_t sid_lock;
     43 static avl_tree_t sid_tree;
     44 static boolean_t sid_inited = B_FALSE;
     45 
     46 static ksiddomain_t
     47 *ksid_enterdomain(const char *dom)
     48 {
     49 	size_t len = strlen(dom) + 1;
     50 	ksiddomain_t *res;
     51 
     52 	ASSERT(MUTEX_HELD(&sid_lock));
     53 	res = kmem_alloc(sizeof (ksiddomain_t), KM_SLEEP);
     54 	res->kd_len = (uint_t)len;
     55 	res->kd_name = kmem_alloc(len, KM_SLEEP);
     56 	bcopy(dom, res->kd_name, len);
     57 
     58 	res->kd_ref = 1;
     59 
     60 	avl_add(&sid_tree, res);
     61 
     62 	return (res);
     63 }
     64 
     65 void
     66 ksid_hold(ksid_t *ks)
     67 {
     68 	if (ks->ks_domain != NULL)
     69 		ksiddomain_hold(ks->ks_domain);
     70 }
     71 
     72 void
     73 ksid_rele(ksid_t *ks)
     74 {
     75 	if (ks->ks_domain != NULL)
     76 		ksiddomain_rele(ks->ks_domain);
     77 }
     78 
     79 void
     80 ksiddomain_hold(ksiddomain_t *kd)
     81 {
     82 	atomic_add_32(&kd->kd_ref, 1);
     83 }
     84 
     85 void
     86 ksiddomain_rele(ksiddomain_t *kd)
     87 {
     88 	if (atomic_add_32_nv(&kd->kd_ref, -1) == 0) {
     89 		/*
     90 		 * The kd reference can only be incremented from 0 when
     91 		 * the sid_lock is held; so we lock and then check need to
     92 		 * check for 0 again.
     93 		 */
     94 		mutex_enter(&sid_lock);
     95 		if (kd->kd_ref == 0) {
     96 			avl_remove(&sid_tree, kd);
     97 			kmem_free(kd->kd_name, kd->kd_len);
     98 			kmem_free(kd, sizeof (*kd));
     99 		}
    100 		mutex_exit(&sid_lock);
    101 	}
    102 }
    103 
    104 void
    105 ksidlist_hold(ksidlist_t *ksl)
    106 {
    107 	atomic_add_32(&ksl->ksl_ref, 1);
    108 }
    109 
    110 void
    111 ksidlist_rele(ksidlist_t *ksl)
    112 {
    113 	if (atomic_add_32_nv(&ksl->ksl_ref, -1) == 0) {
    114 		int i;
    115 
    116 		for (i = 0; i < ksl->ksl_nsid; i++)
    117 			ksid_rele(&ksl->ksl_sids[i]);
    118 
    119 		kmem_free(ksl, KSIDLIST_MEM(ksl->ksl_nsid));
    120 	}
    121 }
    122 
    123 static int
    124 ksid_cmp(const void *a, const void *b)
    125 {
    126 	const ksiddomain_t *ap = a;
    127 	const ksiddomain_t *bp = b;
    128 	int res;
    129 
    130 	res = strcmp(ap->kd_name, bp->kd_name);
    131 	if (res > 0)
    132 		return (1);
    133 	if (res != 0)
    134 		return (-1);
    135 	return (0);
    136 }
    137 
    138 /*
    139  * Lookup the named domain in the AVL tree.
    140  * If no entry is found, add the domain to the AVL tree.
    141  * The domain is returned held and needs to be released
    142  * when done.
    143  */
    144 ksiddomain_t
    145 *ksid_lookupdomain(const char *dom)
    146 {
    147 	ksiddomain_t *res;
    148 	ksiddomain_t tmpl;
    149 
    150 	mutex_enter(&sid_lock);
    151 
    152 	if (!sid_inited) {
    153 		avl_create(&sid_tree, ksid_cmp, sizeof (ksiddomain_t),
    154 		    offsetof(ksiddomain_t, kd_link));
    155 
    156 		res = ksid_enterdomain(dom);
    157 		sid_inited = B_TRUE;
    158 		mutex_exit(&sid_lock);
    159 		return (res);
    160 	}
    161 
    162 	tmpl.kd_name = (char *)dom;
    163 
    164 	res = avl_find(&sid_tree, &tmpl, NULL);
    165 	if (res == NULL) {
    166 		res = ksid_enterdomain(dom);
    167 	} else {
    168 		ksiddomain_hold(res);
    169 	}
    170 
    171 	mutex_exit(&sid_lock);
    172 	return (res);
    173 }
    174 
    175 const char *
    176 ksid_getdomain(ksid_t *ks)
    177 {
    178 	return (ks->ks_domain->kd_name);
    179 }
    180 
    181 uint_t
    182 ksid_getrid(ksid_t *ks)
    183 {
    184 	return (ks->ks_rid);
    185 }
    186 
    187 uid_t
    188 ksid_getid(ksid_t *ks)
    189 {
    190 	return (ks->ks_id);
    191 }
    192 
    193 int
    194 ksid_lookupbyuid(zone_t *zone, uid_t id, ksid_t *res)
    195 {
    196 	const char *sid_prefix;
    197 
    198 	if (kidmap_getsidbyuid(zone, id, &sid_prefix, &res->ks_rid)
    199 	    != IDMAP_SUCCESS)
    200 		return (-1);
    201 
    202 	res->ks_domain = ksid_lookupdomain(sid_prefix);
    203 
    204 	res->ks_id = id;
    205 
    206 	return (0);
    207 }
    208 
    209 int
    210 ksid_lookupbygid(zone_t *zone, gid_t id, ksid_t *res)
    211 {
    212 	const char *sid_prefix;
    213 
    214 	if (kidmap_getsidbygid(zone, id, &sid_prefix, &res->ks_rid)
    215 	    != IDMAP_SUCCESS)
    216 		return (-1);
    217 
    218 	res->ks_domain = ksid_lookupdomain(sid_prefix);
    219 
    220 	res->ks_id = id;
    221 
    222 	return (0);
    223 }
    224 
    225 credsid_t *
    226 kcrsid_alloc(void)
    227 {
    228 	credsid_t *kcr = kmem_zalloc(sizeof (*kcr), KM_SLEEP);
    229 	kcr->kr_ref = 1;
    230 	return (kcr);
    231 }
    232 
    233 /*
    234  * Returns a credsid_t with a refcount of 1.
    235  */
    236 static credsid_t *
    237 kcrsid_dup(credsid_t *org)
    238 {
    239 	credsid_t *new;
    240 	ksid_index_t ki;
    241 
    242 	if (org == NULL)
    243 		return (kcrsid_alloc());
    244 	if (org->kr_ref == 1)
    245 		return (org);
    246 	new = kcrsid_alloc();
    247 
    248 	/* Copy, then update reference counts */
    249 	*new = *org;
    250 	new->kr_ref = 1;
    251 	for (ki = 0; ki < KSID_COUNT; ki++)
    252 		ksid_hold(&new->kr_sidx[ki]);
    253 
    254 	if (new->kr_sidlist != NULL)
    255 		ksidlist_hold(new->kr_sidlist);
    256 
    257 	kcrsid_rele(org);
    258 	return (new);
    259 }
    260 
    261 void
    262 kcrsid_hold(credsid_t *kcr)
    263 {
    264 	atomic_add_32(&kcr->kr_ref, 1);
    265 }
    266 
    267 void
    268 kcrsid_rele(credsid_t *kcr)
    269 {
    270 	if (atomic_add_32_nv(&kcr->kr_ref, -1) == 0) {
    271 		ksid_index_t i;
    272 
    273 		for (i = 0; i < KSID_COUNT; i++)
    274 			ksid_rele(&kcr->kr_sidx[i]);
    275 
    276 		if (kcr->kr_sidlist != NULL)
    277 			ksidlist_rele(kcr->kr_sidlist);
    278 
    279 		kmem_free(kcr, sizeof (*kcr));
    280 	}
    281 }
    282 
    283 /*
    284  * Copy the SID credential into a previously allocated piece of memory.
    285  */
    286 void
    287 kcrsidcopy_to(const credsid_t *okcr, credsid_t *nkcr)
    288 {
    289 	int i;
    290 
    291 	ASSERT(nkcr->kr_ref == 1);
    292 
    293 	if (okcr == NULL)
    294 		return;
    295 	*nkcr = *okcr;
    296 	for (i = 0; i < KSID_COUNT; i++)
    297 		ksid_hold(&nkcr->kr_sidx[i]);
    298 	if (nkcr->kr_sidlist != NULL)
    299 		ksidlist_hold(nkcr->kr_sidlist);
    300 	nkcr->kr_ref = 1;
    301 }
    302 
    303 static int
    304 kcrsid_sidcount(const credsid_t *kcr)
    305 {
    306 	int cnt = 0;
    307 	int i;
    308 
    309 	if (kcr == NULL)
    310 		return (0);
    311 
    312 	for (i = 0; i < KSID_COUNT; i++)
    313 		if (kcr->kr_sidx[i].ks_domain != NULL)
    314 			cnt++;
    315 
    316 	if (kcr->kr_sidlist != NULL)
    317 		cnt += kcr->kr_sidlist->ksl_nsid;
    318 	return (cnt);
    319 }
    320 
    321 /*
    322  * Argument needs to be a ksid_t with a properly held ks_domain reference.
    323  */
    324 credsid_t *
    325 kcrsid_setsid(credsid_t *okcr, ksid_t *ksp, ksid_index_t i)
    326 {
    327 	int ocnt = kcrsid_sidcount(okcr);
    328 	credsid_t *nkcr;
    329 
    330 	/*
    331 	 * Unset the particular ksid; if there are no other SIDs or if this
    332 	 * is the last SID, remove the auxilary data structure.
    333 	 */
    334 	if (ksp == NULL) {
    335 		if (ocnt == 0 ||
    336 		    (ocnt == 1 && okcr->kr_sidx[i].ks_domain != NULL)) {
    337 			if (okcr != NULL)
    338 				kcrsid_rele(okcr);
    339 			return (NULL);
    340 		}
    341 	}
    342 	nkcr = kcrsid_dup(okcr);
    343 	ksid_rele(&nkcr->kr_sidx[i]);
    344 	if (ksp == NULL)
    345 		bzero(&nkcr->kr_sidx[i], sizeof (ksid_t));
    346 	else
    347 		nkcr->kr_sidx[i] = *ksp;
    348 
    349 	return (nkcr);
    350 }
    351 
    352 /*
    353  * Argument needs to be a ksidlist_t with properly held ks_domain references
    354  * and a reference count taking the new reference into account.
    355  */
    356 credsid_t *
    357 kcrsid_setsidlist(credsid_t *okcr, ksidlist_t *ksl)
    358 {
    359 	int ocnt = kcrsid_sidcount(okcr);
    360 	credsid_t *nkcr;
    361 
    362 	/*
    363 	 * Unset the sidlist; if there are no further SIDs, remove the
    364 	 * auxilary data structure.
    365 	 */
    366 	if (ksl == NULL) {
    367 		if (ocnt == 0 || (okcr->kr_sidlist != NULL &&
    368 		    ocnt == okcr->kr_sidlist->ksl_nsid)) {
    369 			if (okcr != NULL)
    370 				kcrsid_rele(okcr);
    371 			return (NULL);
    372 		}
    373 	}
    374 	nkcr = kcrsid_dup(okcr);
    375 	if (nkcr->kr_sidlist != NULL)
    376 		ksidlist_rele(nkcr->kr_sidlist);
    377 
    378 	nkcr->kr_sidlist = ksl;
    379 	return (nkcr);
    380 }
    381 
    382 ksidlist_t *
    383 kcrsid_gidstosids(zone_t *zone, int ngrp, gid_t *grp)
    384 {
    385 	int i;
    386 	ksidlist_t *list;
    387 	int cnt;
    388 
    389 	if (ngrp == 0)
    390 		return (NULL);
    391 
    392 	cnt = 0;
    393 	list = kmem_zalloc(KSIDLIST_MEM(ngrp), KM_SLEEP);
    394 
    395 	list->ksl_nsid = ngrp;
    396 	list->ksl_ref = 1;
    397 
    398 	for (i = 0; i < ngrp; i++) {
    399 		if (grp[i] > MAXUID) {
    400 			list->ksl_neid++;
    401 			if (ksid_lookupbygid(zone,
    402 			    grp[i], &list->ksl_sids[i]) != 0) {
    403 				while (--i >= 0)
    404 					ksid_rele(&list->ksl_sids[i]);
    405 				cnt = 0;
    406 				break;
    407 			}
    408 			cnt++;
    409 		} else {
    410 			list->ksl_sids[i].ks_id = grp[i];
    411 		}
    412 	}
    413 	if (cnt == 0) {
    414 		kmem_free(list, KSIDLIST_MEM(ngrp));
    415 		return (NULL);
    416 	}
    417 	return (list);
    418 }
    419