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 #define KSIDLIST_MEM(n) (sizeof (ksidlist_t) + ((n) - 1) * sizeof (ksid_t)) 45 46 static kmutex_t sid_lock; 47 static avl_tree_t sid_tree; 48 static boolean_t sid_inited = B_FALSE; 49 50 static ksiddomain_t 51 *ksid_enterdomain(const char *dom) 52 { 53 size_t len = strlen(dom) + 1; 54 ksiddomain_t *res; 55 56 ASSERT(MUTEX_HELD(&sid_lock)); 57 res = kmem_alloc(sizeof (ksiddomain_t), KM_SLEEP); 58 res->kd_len = (uint_t)len; 59 res->kd_name = kmem_alloc(len, KM_SLEEP); 60 bcopy(dom, res->kd_name, len); 61 62 res->kd_ref = 1; 63 64 avl_add(&sid_tree, res); 65 66 return (res); 67 } 68 69 void 70 ksid_hold(ksid_t *ks) 71 { 72 if (ks->ks_domain != NULL) 73 ksiddomain_hold(ks->ks_domain); 74 } 75 76 void 77 ksid_rele(ksid_t *ks) 78 { 79 if (ks->ks_domain != NULL) 80 ksiddomain_rele(ks->ks_domain); 81 } 82 83 void 84 ksiddomain_hold(ksiddomain_t *kd) 85 { 86 atomic_add_32(&kd->kd_ref, 1); 87 } 88 89 void 90 ksiddomain_rele(ksiddomain_t *kd) 91 { 92 if (atomic_add_32_nv(&kd->kd_ref, -1) == 0) { 93 /* 94 * The kd reference can only be incremented from 0 when 95 * the sid_lock is held; so we lock and then check need to 96 * check for 0 again. 97 */ 98 mutex_enter(&sid_lock); 99 if (kd->kd_ref == 0) { 100 avl_remove(&sid_tree, kd); 101 kmem_free(kd->kd_name, kd->kd_len); 102 kmem_free(kd, sizeof (*kd)); 103 } 104 mutex_exit(&sid_lock); 105 } 106 } 107 108 void 109 ksidlist_hold(ksidlist_t *ksl) 110 { 111 atomic_add_32(&ksl->ksl_ref, 1); 112 } 113 114 void 115 ksidlist_rele(ksidlist_t *ksl) 116 { 117 if (atomic_add_32_nv(&ksl->ksl_ref, -1) == 0) { 118 int i; 119 120 for (i = 0; i < ksl->ksl_nsid; i++) 121 ksid_rele(&ksl->ksl_sids[i]); 122 123 kmem_free(ksl, KSIDLIST_MEM(ksl->ksl_nsid)); 124 } 125 } 126 127 static int 128 ksid_cmp(const void *a, const void *b) 129 { 130 const ksiddomain_t *ap = a; 131 const ksiddomain_t *bp = b; 132 int res; 133 134 res = strcmp(ap->kd_name, bp->kd_name); 135 if (res > 0) 136 return (1); 137 if (res != 0) 138 return (-1); 139 return (0); 140 } 141 142 /* 143 * Lookup the named domain in the AVL tree. 144 * If no entry is found, add the domain to the AVL tree. 145 * The domain is returned held and needs to be released 146 * when done. 147 */ 148 ksiddomain_t 149 *ksid_lookupdomain(const char *dom) 150 { 151 ksiddomain_t *res; 152 ksiddomain_t tmpl; 153 154 mutex_enter(&sid_lock); 155 156 if (!sid_inited) { 157 avl_create(&sid_tree, ksid_cmp, sizeof (ksiddomain_t), 158 offsetof(ksiddomain_t, kd_link)); 159 160 res = ksid_enterdomain(dom); 161 sid_inited = B_TRUE; 162 mutex_exit(&sid_lock); 163 return (res); 164 } 165 166 tmpl.kd_name = (char *)dom; 167 168 res = avl_find(&sid_tree, &tmpl, NULL); 169 if (res == NULL) { 170 res = ksid_enterdomain(dom); 171 } else { 172 ksiddomain_hold(res); 173 } 174 175 mutex_exit(&sid_lock); 176 return (res); 177 } 178 179 const char * 180 ksid_getdomain(ksid_t *ks) 181 { 182 return (ks->ks_domain->kd_name); 183 } 184 185 uint_t 186 ksid_getrid(ksid_t *ks) 187 { 188 return (ks->ks_rid); 189 } 190 191 int 192 ksid_lookupbyuid(uid_t id, ksid_t *res) 193 { 194 const char *sid_prefix; 195 196 if (kidmap_getsidbyuid(id, &sid_prefix, &res->ks_rid) != IDMAP_SUCCESS) 197 return (-1); 198 199 res->ks_domain = ksid_lookupdomain(sid_prefix); 200 201 res->ks_id = id; 202 203 return (0); 204 } 205 206 int 207 ksid_lookupbygid(gid_t id, ksid_t *res) 208 { 209 const char *sid_prefix; 210 211 if (kidmap_getsidbygid(id, &sid_prefix, &res->ks_rid) != 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(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(grp[i], &list->ksl_sids[i]) != 0) { 398 while (--i >= 0) 399 ksid_rele(&list->ksl_sids[i]); 400 cnt = 0; 401 break; 402 } 403 cnt++; 404 } else { 405 list->ksl_sids[i].ks_id = grp[i]; 406 } 407 } 408 if (cnt == 0) { 409 kmem_free(list, KSIDLIST_MEM(ngrp)); 410 return (NULL); 411 } 412 return (list); 413 } 414