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 2007 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(uid_t id, ksid_t *res) 191 { 192 const char *sid_prefix; 193 194 if (kidmap_getsidbyuid(id, &sid_prefix, &res->ks_rid) != IDMAP_SUCCESS) 195 return (-1); 196 197 res->ks_domain = ksid_lookupdomain(sid_prefix); 198 199 res->ks_id = id; 200 201 return (0); 202 } 203 204 int 205 ksid_lookupbygid(gid_t id, ksid_t *res) 206 { 207 const char *sid_prefix; 208 209 if (kidmap_getsidbygid(id, &sid_prefix, &res->ks_rid) != IDMAP_SUCCESS) 210 return (-1); 211 212 res->ks_domain = ksid_lookupdomain(sid_prefix); 213 214 res->ks_id = id; 215 216 return (0); 217 } 218 219 credsid_t * 220 kcrsid_alloc(void) 221 { 222 credsid_t *kcr = kmem_zalloc(sizeof (*kcr), KM_SLEEP); 223 kcr->kr_ref = 1; 224 return (kcr); 225 } 226 227 /* 228 * Returns a credsid_t with a refcount of 1. 229 */ 230 static credsid_t * 231 kcrsid_dup(credsid_t *org) 232 { 233 credsid_t *new; 234 ksid_index_t ki; 235 236 if (org == NULL) 237 return (kcrsid_alloc()); 238 if (org->kr_ref == 1) 239 return (org); 240 new = kcrsid_alloc(); 241 242 /* Copy, then update reference counts */ 243 *new = *org; 244 new->kr_ref = 1; 245 for (ki = 0; ki < KSID_COUNT; ki++) 246 ksid_hold(&new->kr_sidx[ki]); 247 248 if (new->kr_sidlist != NULL) 249 ksidlist_hold(new->kr_sidlist); 250 251 kcrsid_rele(org); 252 return (new); 253 } 254 255 void 256 kcrsid_hold(credsid_t *kcr) 257 { 258 atomic_add_32(&kcr->kr_ref, 1); 259 } 260 261 void 262 kcrsid_rele(credsid_t *kcr) 263 { 264 if (atomic_add_32_nv(&kcr->kr_ref, -1) == 0) { 265 ksid_index_t i; 266 267 for (i = 0; i < KSID_COUNT; i++) 268 ksid_rele(&kcr->kr_sidx[i]); 269 270 if (kcr->kr_sidlist != NULL) 271 ksidlist_rele(kcr->kr_sidlist); 272 273 kmem_free(kcr, sizeof (*kcr)); 274 } 275 } 276 277 /* 278 * Copy the SID credential into a previously allocated piece of memory. 279 */ 280 void 281 kcrsidcopy_to(const credsid_t *okcr, credsid_t *nkcr) 282 { 283 int i; 284 285 ASSERT(nkcr->kr_ref == 1); 286 287 if (okcr == NULL) 288 return; 289 *nkcr = *okcr; 290 for (i = 0; i < KSID_COUNT; i++) 291 ksid_hold(&nkcr->kr_sidx[i]); 292 if (nkcr->kr_sidlist != NULL) 293 ksidlist_hold(nkcr->kr_sidlist); 294 nkcr->kr_ref = 1; 295 } 296 297 static int 298 kcrsid_sidcount(const credsid_t *kcr) 299 { 300 int cnt = 0; 301 int i; 302 303 if (kcr == NULL) 304 return (0); 305 306 for (i = 0; i < KSID_COUNT; i++) 307 if (kcr->kr_sidx[i].ks_domain != NULL) 308 cnt++; 309 310 if (kcr->kr_sidlist != NULL) 311 cnt += kcr->kr_sidlist->ksl_nsid; 312 return (cnt); 313 } 314 315 /* 316 * Argument needs to be a ksid_t with a properly held ks_domain reference. 317 */ 318 credsid_t * 319 kcrsid_setsid(credsid_t *okcr, ksid_t *ksp, ksid_index_t i) 320 { 321 int ocnt = kcrsid_sidcount(okcr); 322 credsid_t *nkcr; 323 324 /* 325 * Unset the particular ksid; if there are no other SIDs or if this 326 * is the last SID, remove the auxilary data structure. 327 */ 328 if (ksp == NULL) { 329 if (ocnt == 0 || 330 (ocnt == 1 && okcr->kr_sidx[i].ks_domain != NULL)) { 331 if (okcr != NULL) 332 kcrsid_rele(okcr); 333 return (NULL); 334 } 335 } 336 nkcr = kcrsid_dup(okcr); 337 ksid_rele(&nkcr->kr_sidx[i]); 338 if (ksp == NULL) 339 bzero(&nkcr->kr_sidx[i], sizeof (ksid_t)); 340 else 341 nkcr->kr_sidx[i] = *ksp; 342 343 return (nkcr); 344 } 345 346 /* 347 * Argument needs to be a ksidlist_t with properly held ks_domain references 348 * and a reference count taking the new reference into account. 349 */ 350 credsid_t * 351 kcrsid_setsidlist(credsid_t *okcr, ksidlist_t *ksl) 352 { 353 int ocnt = kcrsid_sidcount(okcr); 354 credsid_t *nkcr; 355 356 /* 357 * Unset the sidlist; if there are no further SIDs, remove the 358 * auxilary data structure. 359 */ 360 if (ksl == NULL) { 361 if (ocnt == 0 || (okcr->kr_sidlist != NULL && 362 ocnt == okcr->kr_sidlist->ksl_nsid)) { 363 if (okcr != NULL) 364 kcrsid_rele(okcr); 365 return (NULL); 366 } 367 } 368 nkcr = kcrsid_dup(okcr); 369 if (nkcr->kr_sidlist != NULL) 370 ksidlist_rele(nkcr->kr_sidlist); 371 372 nkcr->kr_sidlist = ksl; 373 return (nkcr); 374 } 375 376 ksidlist_t * 377 kcrsid_gidstosids(int ngrp, gid_t *grp) 378 { 379 int i; 380 ksidlist_t *list; 381 int cnt; 382 383 if (ngrp == 0) 384 return (NULL); 385 386 cnt = 0; 387 list = kmem_zalloc(KSIDLIST_MEM(ngrp), KM_SLEEP); 388 389 list->ksl_nsid = ngrp; 390 list->ksl_ref = 1; 391 392 for (i = 0; i < ngrp; i++) { 393 if (grp[i] > MAXUID) { 394 list->ksl_neid++; 395 if (ksid_lookupbygid(grp[i], &list->ksl_sids[i]) != 0) { 396 while (--i >= 0) 397 ksid_rele(&list->ksl_sids[i]); 398 cnt = 0; 399 break; 400 } 401 cnt++; 402 } else { 403 list->ksl_sids[i].ks_id = grp[i]; 404 } 405 } 406 if (cnt == 0) { 407 kmem_free(list, KSIDLIST_MEM(ngrp)); 408 return (NULL); 409 } 410 return (list); 411 } 412