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_inc_32(&kd->kd_ref); 83 } 84 85 void 86 ksiddomain_rele(ksiddomain_t *kd) 87 { 88 if (atomic_dec_32_nv(&kd->kd_ref) == 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_inc_32(&ksl->ksl_ref); 108 } 109 110 void 111 ksidlist_rele(ksidlist_t *ksl) 112 { 113 if (atomic_dec_32_nv(&ksl->ksl_ref) == 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_inc_32(&kcr->kr_ref); 265 } 266 267 void 268 kcrsid_rele(credsid_t *kcr) 269 { 270 if (atomic_dec_32_nv(&kcr->kr_ref) == 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