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 * Copyright 2020 Tintri by DDN, Inc. All rights reserved. 29 */ 30 31 /* 32 * Sid manipulation (stubs). 33 */ 34 35 #include <sys/atomic.h> 36 #include <sys/avl.h> 37 #include <sys/cmn_err.h> 38 #include <sys/kmem.h> 39 #include <sys/mutex.h> 40 #include <sys/sid.h> 41 #include <sys/sysmacros.h> 42 #include <sys/systm.h> 43 44 #ifdef _KERNEL 45 #include <sys/kidmap.h> 46 #endif 47 48 #include <sys/idmap.h> 49 #include <util/qsort.h> 50 51 static kmutex_t sid_lock; 52 static avl_tree_t sid_tree; 53 static boolean_t sid_inited = B_FALSE; 54 55 static ksiddomain_t 56 *ksid_enterdomain(const char *dom) 57 { 58 size_t len = strlen(dom) + 1; 59 ksiddomain_t *res; 60 61 ASSERT(MUTEX_HELD(&sid_lock)); 62 res = kmem_alloc(sizeof (ksiddomain_t), KM_SLEEP); 63 res->kd_len = (uint_t)len; 64 res->kd_name = kmem_alloc(len, KM_SLEEP); 65 bcopy(dom, res->kd_name, len); 66 67 res->kd_ref = 1; 68 69 avl_add(&sid_tree, res); 70 71 return (res); 72 } 73 74 void 75 ksid_hold(ksid_t *ks) 76 { 77 if (ks->ks_domain != NULL) 78 ksiddomain_hold(ks->ks_domain); 79 } 80 81 void 82 ksid_rele(ksid_t *ks) 83 { 84 if (ks->ks_domain != NULL) 85 ksiddomain_rele(ks->ks_domain); 86 } 87 88 void 89 ksiddomain_hold(ksiddomain_t *kd) 90 { 91 atomic_inc_32(&kd->kd_ref); 92 } 93 94 void 95 ksiddomain_rele(ksiddomain_t *kd) 96 { 97 if (atomic_dec_32_nv(&kd->kd_ref) == 0) { 98 /* 99 * The kd reference can only be incremented from 0 when 100 * the sid_lock is held; so we lock and then check need to 101 * check for 0 again. 102 */ 103 mutex_enter(&sid_lock); 104 if (kd->kd_ref == 0) { 105 avl_remove(&sid_tree, kd); 106 kmem_free(kd->kd_name, kd->kd_len); 107 kmem_free(kd, sizeof (*kd)); 108 } 109 mutex_exit(&sid_lock); 110 } 111 } 112 113 void 114 ksidlist_hold(ksidlist_t *ksl) 115 { 116 atomic_inc_32(&ksl->ksl_ref); 117 } 118 119 void 120 ksidlist_rele(ksidlist_t *ksl) 121 { 122 if (atomic_dec_32_nv(&ksl->ksl_ref) == 0) { 123 int i; 124 125 if (ksl->ksl_sorted != NULL) 126 kmem_free(ksl->ksl_sorted, 127 ksl->ksl_nsid * sizeof (ksid_t *)); 128 for (i = 0; i < ksl->ksl_nsid; i++) 129 ksid_rele(&ksl->ksl_sids[i]); 130 131 kmem_free(ksl, KSIDLIST_MEM(ksl->ksl_nsid)); 132 } 133 } 134 135 /* 136 * Linear search is more efficient for 'small' arrays. 137 * What's considered 'small' varies by system. 138 * supgroupmember() uses 16; we make ours a variable for testing. 139 */ 140 int ksl_bin_search_cutoff = 16; 141 142 boolean_t 143 ksidlist_has_sid(ksidlist_t *ksl, const char *domain, uint32_t rid) 144 { 145 int64_t hi, lo, m; 146 int cmp; 147 ksid_t *sids = ksl->ksl_sids; /* sorted by SID */ 148 149 lo = 0; 150 hi = ksl->ksl_nsid - 1; 151 152 if (hi < ksl_bin_search_cutoff) { 153 for (; lo <= hi; lo++) { 154 if (rid == sids[lo].ks_rid && 155 strcmp(domain, ksid_getdomain(&sids[lo])) == 0) 156 return (B_TRUE); 157 } 158 return (B_FALSE); 159 } 160 161 do { 162 /* This is an overflow-safe version of m = (lo + hi) / 2 */ 163 m = (int64_t)((uint64_t)(lo + hi) >> 1); 164 165 cmp = AVL_CMP(rid, sids[m].ks_rid); 166 if (cmp == 0) 167 cmp = strcmp(domain, ksid_getdomain(&sids[m])); 168 169 if (cmp > 0) 170 lo = m + 1; 171 else if (cmp < 0) 172 hi = m - 1; 173 else 174 return (B_TRUE); 175 176 } while (lo <= hi); 177 178 return (B_FALSE); 179 } 180 181 boolean_t 182 ksidlist_has_pid(ksidlist_t *ksl, uint32_t pid) 183 { 184 int64_t hi, lo, m; 185 int cmp; 186 ksid_t **sidsp = ksl->ksl_sorted; /* sorted by posix ID */ 187 188 lo = 0; 189 hi = ksl->ksl_nsid - 1; 190 191 if (hi < ksl_bin_search_cutoff) { 192 for (; lo <= hi; lo++) { 193 if (pid == ksl->ksl_sids[lo].ks_id) 194 return (B_TRUE); 195 } 196 return (B_FALSE); 197 } 198 199 do { 200 /* This is an overflow-safe version of m = (lo + hi) / 2 */ 201 m = (int64_t)((uint64_t)(lo + hi) >> 1); 202 203 cmp = AVL_CMP(pid, sidsp[m]->ks_id); 204 205 if (cmp > 0) 206 lo = m + 1; 207 else if (cmp < 0) 208 hi = m - 1; 209 else 210 return (B_TRUE); 211 212 } while (lo <= hi); 213 214 return (B_FALSE); 215 } 216 217 static int 218 ksid_cmp(const void *a, const void *b) 219 { 220 const ksiddomain_t *ap = a; 221 const ksiddomain_t *bp = b; 222 int res; 223 224 res = strcmp(ap->kd_name, bp->kd_name); 225 226 return (AVL_ISIGN(res)); 227 } 228 229 /* 230 * Lookup the named domain in the AVL tree. 231 * If no entry is found, add the domain to the AVL tree. 232 * The domain is returned held and needs to be released 233 * when done. 234 */ 235 ksiddomain_t 236 *ksid_lookupdomain(const char *dom) 237 { 238 ksiddomain_t *res; 239 ksiddomain_t tmpl; 240 241 mutex_enter(&sid_lock); 242 243 if (!sid_inited) { 244 avl_create(&sid_tree, ksid_cmp, sizeof (ksiddomain_t), 245 offsetof(ksiddomain_t, kd_link)); 246 247 res = ksid_enterdomain(dom); 248 sid_inited = B_TRUE; 249 mutex_exit(&sid_lock); 250 return (res); 251 } 252 253 tmpl.kd_name = (char *)dom; 254 255 res = avl_find(&sid_tree, &tmpl, NULL); 256 if (res == NULL) { 257 res = ksid_enterdomain(dom); 258 } else { 259 ksiddomain_hold(res); 260 } 261 262 mutex_exit(&sid_lock); 263 return (res); 264 } 265 266 const char * 267 ksid_getdomain(ksid_t *ks) 268 { 269 return (ks->ks_domain->kd_name); 270 } 271 272 uint_t 273 ksid_getrid(ksid_t *ks) 274 { 275 return (ks->ks_rid); 276 } 277 278 uid_t 279 ksid_getid(ksid_t *ks) 280 { 281 return (ks->ks_id); 282 } 283 284 #ifdef _KERNEL 285 int 286 ksid_lookupbyuid(zone_t *zone, uid_t id, ksid_t *res) 287 { 288 const char *sid_prefix; 289 290 if (kidmap_getsidbyuid(zone, id, &sid_prefix, &res->ks_rid) 291 != IDMAP_SUCCESS) 292 return (-1); 293 294 res->ks_domain = ksid_lookupdomain(sid_prefix); 295 296 res->ks_id = id; 297 298 return (0); 299 } 300 301 int 302 ksid_lookupbygid(zone_t *zone, gid_t id, ksid_t *res) 303 { 304 const char *sid_prefix; 305 306 if (kidmap_getsidbygid(zone, id, &sid_prefix, &res->ks_rid) 307 != IDMAP_SUCCESS) 308 return (-1); 309 310 res->ks_domain = ksid_lookupdomain(sid_prefix); 311 312 res->ks_id = id; 313 314 return (0); 315 } 316 #endif 317 318 credsid_t * 319 kcrsid_alloc(void) 320 { 321 credsid_t *kcr = kmem_zalloc(sizeof (*kcr), KM_SLEEP); 322 kcr->kr_ref = 1; 323 return (kcr); 324 } 325 326 /* 327 * Returns a credsid_t with a refcount of 1. 328 */ 329 static credsid_t * 330 kcrsid_dup(credsid_t *org) 331 { 332 credsid_t *new; 333 ksid_index_t ki; 334 335 if (org == NULL) 336 return (kcrsid_alloc()); 337 if (org->kr_ref == 1) 338 return (org); 339 new = kcrsid_alloc(); 340 341 /* Copy, then update reference counts */ 342 *new = *org; 343 new->kr_ref = 1; 344 for (ki = 0; ki < KSID_COUNT; ki++) 345 ksid_hold(&new->kr_sidx[ki]); 346 347 if (new->kr_sidlist != NULL) 348 ksidlist_hold(new->kr_sidlist); 349 350 kcrsid_rele(org); 351 return (new); 352 } 353 354 void 355 kcrsid_hold(credsid_t *kcr) 356 { 357 atomic_inc_32(&kcr->kr_ref); 358 } 359 360 void 361 kcrsid_rele(credsid_t *kcr) 362 { 363 if (atomic_dec_32_nv(&kcr->kr_ref) == 0) { 364 ksid_index_t i; 365 366 for (i = 0; i < KSID_COUNT; i++) 367 ksid_rele(&kcr->kr_sidx[i]); 368 369 if (kcr->kr_sidlist != NULL) 370 ksidlist_rele(kcr->kr_sidlist); 371 372 kmem_free(kcr, sizeof (*kcr)); 373 } 374 } 375 376 /* 377 * Copy the SID credential into a previously allocated piece of memory. 378 */ 379 void 380 kcrsidcopy_to(const credsid_t *okcr, credsid_t *nkcr) 381 { 382 int i; 383 384 ASSERT(nkcr->kr_ref == 1); 385 386 if (okcr == NULL) 387 return; 388 *nkcr = *okcr; 389 for (i = 0; i < KSID_COUNT; i++) 390 ksid_hold(&nkcr->kr_sidx[i]); 391 if (nkcr->kr_sidlist != NULL) 392 ksidlist_hold(nkcr->kr_sidlist); 393 nkcr->kr_ref = 1; 394 } 395 396 static int 397 kcrsid_sidcount(const credsid_t *kcr) 398 { 399 int cnt = 0; 400 int i; 401 402 if (kcr == NULL) 403 return (0); 404 405 for (i = 0; i < KSID_COUNT; i++) 406 if (kcr->kr_sidx[i].ks_domain != NULL) 407 cnt++; 408 409 if (kcr->kr_sidlist != NULL) 410 cnt += kcr->kr_sidlist->ksl_nsid; 411 return (cnt); 412 } 413 414 /* 415 * Argument needs to be a ksid_t with a properly held ks_domain reference. 416 */ 417 credsid_t * 418 kcrsid_setsid(credsid_t *okcr, ksid_t *ksp, ksid_index_t i) 419 { 420 int ocnt = kcrsid_sidcount(okcr); 421 credsid_t *nkcr; 422 423 /* 424 * Unset the particular ksid; if there are no other SIDs or if this 425 * is the last SID, remove the auxilary data structure. 426 */ 427 if (ksp == NULL) { 428 if (ocnt == 0 || 429 (ocnt == 1 && okcr->kr_sidx[i].ks_domain != NULL)) { 430 if (okcr != NULL) 431 kcrsid_rele(okcr); 432 return (NULL); 433 } 434 } 435 nkcr = kcrsid_dup(okcr); 436 ksid_rele(&nkcr->kr_sidx[i]); 437 if (ksp == NULL) 438 bzero(&nkcr->kr_sidx[i], sizeof (ksid_t)); 439 else 440 nkcr->kr_sidx[i] = *ksp; 441 442 return (nkcr); 443 } 444 445 static int 446 ksid_sid_cmp(const void *arg1, const void *arg2) 447 { 448 ksid_t *sid1 = (ksid_t *)arg1; 449 ksid_t *sid2 = (ksid_t *)arg2; 450 int cmp = AVL_CMP(sid1->ks_rid, sid2->ks_rid); 451 452 if (cmp == 0) 453 cmp = AVL_ISIGN(strcmp(ksid_getdomain(sid1), 454 ksid_getdomain(sid2))); 455 456 return (cmp); 457 } 458 459 static int 460 ksid_id_cmp(const void *arg1, const void *arg2) 461 { 462 ksid_t *sid1 = *(ksid_t **)arg1; 463 ksid_t *sid2 = *(ksid_t **)arg2; 464 465 return (AVL_CMP(sid1->ks_id, sid2->ks_id)); 466 } 467 468 /* 469 * Argument needs to be a ksidlist_t with properly held ks_domain references 470 * and a reference count taking the new reference into account. 471 */ 472 credsid_t * 473 kcrsid_setsidlist(credsid_t *okcr, ksidlist_t *ksl) 474 { 475 int ocnt = kcrsid_sidcount(okcr); 476 credsid_t *nkcr; 477 int i; 478 479 /* 480 * Unset the sidlist; if there are no further SIDs, remove the 481 * auxilary data structure. 482 */ 483 if (ksl == NULL) { 484 if (ocnt == 0 || (okcr->kr_sidlist != NULL && 485 ocnt == okcr->kr_sidlist->ksl_nsid)) { 486 if (okcr != NULL) 487 kcrsid_rele(okcr); 488 return (NULL); 489 } 490 } 491 nkcr = kcrsid_dup(okcr); 492 if (nkcr->kr_sidlist != NULL) 493 ksidlist_rele(nkcr->kr_sidlist); 494 495 /* sort the lists so that we can do binary search */ 496 nkcr->kr_sidlist = ksl; 497 498 if (ksl->ksl_sorted == NULL) { 499 qsort(ksl->ksl_sids, ksl->ksl_nsid, sizeof (ksid_t), 500 ksid_sid_cmp); 501 502 ksl->ksl_sorted = kmem_alloc(ksl->ksl_nsid * sizeof (ksid_t *), 503 KM_SLEEP); 504 for (i = 0; i < ksl->ksl_nsid; i++) 505 ksl->ksl_sorted[i] = &ksl->ksl_sids[i]; 506 qsort(ksl->ksl_sorted, ksl->ksl_nsid, sizeof (ksid_t *), 507 ksid_id_cmp); 508 } 509 510 return (nkcr); 511 } 512 513 ksidlist_t * 514 kcrsid_gidstosids(zone_t *zone, int ngrp, gid_t *grp) 515 { 516 int i; 517 ksidlist_t *list; 518 int cnt; 519 520 if (ngrp == 0) 521 return (NULL); 522 523 cnt = 0; 524 list = kmem_zalloc(KSIDLIST_MEM(ngrp), KM_SLEEP); 525 526 list->ksl_nsid = ngrp; 527 list->ksl_ref = 1; 528 529 for (i = 0; i < ngrp; i++) { 530 if (grp[i] > MAXUID) { 531 list->ksl_neid++; 532 #ifdef _KERNEL 533 if (ksid_lookupbygid(zone, 534 grp[i], &list->ksl_sids[i]) != 0) { 535 while (--i >= 0) 536 ksid_rele(&list->ksl_sids[i]); 537 cnt = 0; 538 break; 539 } 540 #endif 541 cnt++; 542 } else { 543 list->ksl_sids[i].ks_id = grp[i]; 544 } 545 } 546 if (cnt == 0) { 547 kmem_free(list, KSIDLIST_MEM(ngrp)); 548 return (NULL); 549 } 550 return (list); 551 } 552