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 }