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