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  * Copyright 2010 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * Device policy implementation.
  28  *
  29  * Maintains the device policy table and defines the lookup functions.
  30  *
  31  * The table contains one entry for each major device number; each
  32  * major bucket has a list of minor number specific entries.  First
  33  * match gets it.  Not even simple minor names are expanded as that
  34  * would cause the device to be loaded.  Non-wildcard entries are expanded
  35  * on first match. Wildcard entries are matched each open but the actual
  36  * policy is cached with the common snode, so the matching code will
  37  * probably be called infrequently.  The trivial wildcard ``*'' does
  38  * not cause expensive string expansions and matches.
  39  *
  40  * When the policy is updated, the the generation count is increased;
  41  * whenever a cached policy is used, the generation count is compared;
  42  * if there's no match, the device policy is refreshed.
  43  *
  44  * The special policy "nullpolicy" is used to mean "no checking beyond DAC
  45  * needed".  It too will change when the policy is rev'ed to make sure
  46  * that devices with nullpolicy are also refreshed.
  47  *
  48  * The special policy "dfltpolicy" is used for those devices with no
  49  * matching policy.  On boot, it is "all privileges required".
  50  * This restriction on boot functions as a fail-safe; if no device policy
  51  * is loaded a "no restriction policy" would lead to security problems that
  52  * are not immediately noticable.
  53  */
  54 
  55 #include <sys/priv_impl.h>
  56 #include <sys/policy.h>
  57 #include <sys/atomic.h>
  58 #include <sys/autoconf.h>
  59 #include <sys/sysmacros.h>
  60 #include <sys/systm.h>
  61 #include <sys/vnode.h>
  62 #include <sys/devpolicy.h>
  63 #include <sys/priv.h>
  64 #include <sys/kmem.h>
  65 #include <sys/ksynch.h>
  66 #include <sys/errno.h>
  67 #include <sys/sunddi.h>
  68 #include <c2/audit.h>
  69 #include <sys/fs/dv_node.h>
  70 
  71 /*
  72  * Internal data structures definitions.
  73  */
  74 
  75 typedef struct devplcyent devplcyent_t;
  76 
  77 /*
  78  * The device policy entry; if there is an expression string, the
  79  * minor numbers are not relevant.  This is indicated by dpe_len > 0.
  80  */
  81 struct devplcyent {
  82         devplcyent_t    *dpe_next;      /* next entry in this list */
  83         devplcy_t       *dpe_plcy;      /* policy for this entry */
  84         char            *dpe_expr;      /* expression matching minor mode */
  85         int             dpe_len;        /* size of allocated mem for expr */
  86         uint32_t        dpe_flags;      /* flags */
  87         minor_t         dpe_lomin;      /* expanded: low minor number */
  88         minor_t         dpe_himin;      /* expanded: high minor number */
  89         vtype_t         dpe_spec;       /* expanded: VBLK or VCHR */
  90 };
  91 
  92 #define DPE_WILDC       0x01            /* Expression has wildcard */
  93 #define DPE_ALLMINOR    0x02            /* Matches all minor numbers */
  94 #define DPE_EXPANDED    0x04            /* Minor numbers expanded */
  95 
  96 typedef struct tableent {
  97         devplcyent_t    *t_ent;         /* list of policies by minor */
  98         major_t         t_major;        /* device major number */
  99 } tableent_t;
 100 
 101 /*
 102  * The data store.
 103  */
 104 
 105 static int ntabent;             /* # of major numbers */
 106 static int totitems;            /* Number of entries in all buckets + dflt */
 107 static tableent_t *devpolicy;   /* The device policy itself */
 108 
 109 static krwlock_t policyrw;      /* protects the table */
 110 static kmutex_t policymutex;    /* allows only one concurrent devpolicy_load */
 111 
 112 devplcy_t *nullpolicy;          /* public because it's used for shortcuts */
 113 static devplcy_t *dfltpolicy;
 114 static devplcy_t *netpolicy;
 115 
 116 /*
 117  * Device policy generation count; only device policies matching the
 118  * generation count are still valid.
 119  */
 120 volatile uint32_t devplcy_gen;
 121 
 122 /*
 123  * Tunable: maximum number of device policy entries to load in
 124  * a system call.  (Protects KM_SLEEP call)
 125  */
 126 int maxdevpolicy = MAXDEVPOLICY;
 127 
 128 /*
 129  * Initialize the device policy code
 130  */
 131 void
 132 devpolicy_init(void)
 133 {
 134         rw_init(&policyrw, NULL, RW_DRIVER, NULL);
 135         mutex_init(&policymutex, NULL, MUTEX_DRIVER, NULL);
 136 
 137         /* The mutex is held here in order to satisfy the ASSERT in dpget() */
 138         mutex_enter(&policymutex);
 139 
 140         nullpolicy = dpget();
 141         dfltpolicy = dpget();
 142         netpolicy = dpget();
 143 
 144         /*
 145          * Initially, we refuse access to all devices except
 146          * to processes with all privileges.
 147          */
 148         priv_fillset(&dfltpolicy->dp_rdp);
 149         priv_fillset(&dfltpolicy->dp_wrp);
 150 
 151         totitems = 1;
 152 
 153         devplcy_gen++;
 154         mutex_exit(&policymutex);
 155 
 156         /* initialize default network privilege */
 157         priv_emptyset(&netpolicy->dp_rdp);
 158         priv_emptyset(&netpolicy->dp_wrp);
 159         priv_addset(&netpolicy->dp_rdp, PRIV_NET_RAWACCESS);
 160         priv_addset(&netpolicy->dp_wrp, PRIV_NET_RAWACCESS);
 161 }
 162 
 163 /*
 164  * Devpolicy reference counting/allocation routines.
 165  * cf. crget()/crhold()/crfree().
 166  */
 167 devplcy_t *
 168 dpget(void)
 169 {
 170         devplcy_t *dp = kmem_zalloc(sizeof (*dp), KM_SLEEP);
 171 
 172         ASSERT(MUTEX_HELD(&policymutex));
 173 
 174         dp->dp_ref = 1;
 175         /* New ones belong to the next generation */
 176         dp->dp_gen = devplcy_gen + 1;
 177         return (dp);
 178 }
 179 
 180 void
 181 dphold(devplcy_t *dp)
 182 {
 183         ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0);
 184         atomic_inc_32(&dp->dp_ref);
 185 }
 186 
 187 void
 188 dpfree(devplcy_t *dp)
 189 {
 190         ASSERT(dp->dp_ref != 0xdeadbeef && dp->dp_ref != 0);
 191         if (atomic_dec_32_nv(&dp->dp_ref) == 0)
 192                 kmem_free(dp, sizeof (*dp));
 193 }
 194 
 195 /*
 196  * Find the policy that matches this device.
 197  */
 198 static devplcy_t *
 199 match_policy(devplcyent_t *de, dev_t dev, vtype_t spec)
 200 {
 201         char *mname = NULL;
 202         minor_t min = getminor(dev);
 203 
 204         for (; de != NULL; de = de->dpe_next) {
 205                 if (de->dpe_flags & DPE_ALLMINOR)
 206                         break;
 207 
 208                 if (de->dpe_flags & DPE_EXPANDED) {
 209                         if (min >= de->dpe_lomin && min <= de->dpe_himin &&
 210                             spec == de->dpe_spec) {
 211                                 break;
 212                         } else {
 213                                 continue;
 214                         }
 215                 }
 216 
 217                 /*
 218                  * We now need the minor name to match string or
 219                  * simle regexp.  Could we use csp->s_dip and not
 220                  * allocate a string here?
 221                  */
 222                 if (mname == NULL &&
 223                     ddi_lyr_get_minor_name(dev, spec, &mname) != DDI_SUCCESS)
 224                         /* mname can be set after the function fails */
 225                         return (dfltpolicy);
 226 
 227                 /* Simple wildcard, with only one ``*'' */
 228                 if (de->dpe_flags & DPE_WILDC) {
 229                         int plen = de->dpe_len - 1;
 230                         int slen = strlen(mname);
 231                         char *pp = de->dpe_expr;
 232                         char *sp = mname;
 233 
 234                         /* string must be at least as long as pattern w/o '*' */
 235                         if (slen < plen - 1)
 236                                 continue;
 237 
 238                         /* skip prefix */
 239                         while (*pp == *sp && *pp != '\0') {
 240                                 pp++;
 241                                 sp++;
 242                         }
 243                         /* matched single '*' */
 244                         if (*pp == '\0')
 245                                 if (*sp == '\0')
 246                                         break;
 247                                 else
 248                                         continue;
 249                         if (*pp != '*')
 250                                 continue;
 251 
 252                         pp++;
 253                         /*
 254                          * skip characters matched by '*': difference of
 255                          * length of s and length of pattern sans '*'
 256                          */
 257                         sp += slen - (plen - 1);
 258                         if (strcmp(pp, sp) == 0)        /* match! */
 259                                 break;
 260 
 261                 } else if (strcmp(de->dpe_expr, mname) == 0) {
 262                         /* Store minor number, if no contention */
 263                         if (rw_tryupgrade(&policyrw)) {
 264                                 de->dpe_lomin = de->dpe_himin = min;
 265                                 de->dpe_spec = spec;
 266                                 de->dpe_flags |= DPE_EXPANDED;
 267                         }
 268                         break;
 269                 }
 270 
 271         }
 272 
 273         if (mname != NULL)
 274                 kmem_free(mname, strlen(mname) + 1);
 275 
 276         return (de != NULL ? de->dpe_plcy : dfltpolicy);
 277 }
 278 
 279 static int
 280 devpolicyent_bymajor(major_t maj)
 281 {
 282         int lo, hi;
 283 
 284         ASSERT(RW_LOCK_HELD(&policyrw));
 285 
 286         lo = 0;
 287         hi = ntabent - 1;
 288 
 289         /* Binary search for major number */
 290         while (lo <= hi) {
 291                 int mid = (lo + hi) / 2;
 292 
 293                 if (devpolicy[mid].t_major == maj)
 294                         return (mid);
 295                 else if (maj < devpolicy[mid].t_major)
 296                         hi = mid - 1;
 297                 else
 298                         lo = mid + 1;
 299         }
 300         return (-1);
 301 }
 302 
 303 /*
 304  * Returns held device policy for the specific device node.
 305  * Note devfs_devpolicy returns with a hold on the policy.
 306  */
 307 devplcy_t *
 308 devpolicy_find(vnode_t *vp)
 309 {
 310         dev_t dev = vp->v_rdev;
 311         vtype_t spec = vp->v_type;
 312         major_t maj = getmajor(dev);
 313         int i;
 314         devplcy_t *res;
 315 
 316         if (maj == clone_major)
 317                 maj = getminor(dev);
 318 
 319         rw_enter(&policyrw, RW_READER);
 320 
 321         i = devpolicyent_bymajor(maj);
 322 
 323         if (i != -1) {
 324                 res = match_policy(devpolicy[i].t_ent, dev, spec);
 325                 dphold(res);
 326         } else if (devfs_devpolicy(vp, &res) != 0) {
 327                 res = NETWORK_DRV(maj) ? netpolicy : dfltpolicy;
 328                 dphold(res);
 329         }
 330 
 331         rw_exit(&policyrw);
 332 
 333         return (res);
 334 }
 335 
 336 static devplcyent_t *
 337 parse_policy(devplcysys_t *ds, devplcy_t *nullp, devplcy_t *defp)
 338 {
 339         devplcyent_t *de = kmem_zalloc(sizeof (*de), KM_SLEEP);
 340         devplcy_t *np;
 341 
 342         if (priv_isemptyset(&ds->dps_rdp) && priv_isemptyset(&ds->dps_wrp))
 343                 dphold(np = nullp);
 344         else if (defp != nullp &&
 345             priv_isequalset(&ds->dps_rdp, &defp->dp_rdp) &&
 346             priv_isequalset(&ds->dps_wrp, &defp->dp_wrp))
 347                 dphold(np = defp);
 348         else {
 349                 np = dpget();
 350                 np->dp_rdp = ds->dps_rdp;
 351                 np->dp_wrp = ds->dps_wrp;
 352         }
 353 
 354         if (ds->dps_minornm[0] != '\0') {
 355                 de->dpe_len = strlen(ds->dps_minornm) + 1;
 356 
 357                 if (strchr(ds->dps_minornm, '*') != NULL) {
 358                         if (de->dpe_len == 2) {              /* "*\0" */
 359                                 de->dpe_flags = DPE_ALLMINOR;
 360                                 de->dpe_len = 0;
 361                         } else
 362                                 de->dpe_flags = DPE_WILDC;
 363                 }
 364                 if (de->dpe_len != 0) {
 365                         de->dpe_expr = kmem_alloc(de->dpe_len, KM_SLEEP);
 366                         (void) strcpy(de->dpe_expr, ds->dps_minornm);
 367                 }
 368         } else {
 369                 de->dpe_lomin = ds->dps_lomin;
 370                 de->dpe_himin = ds->dps_himin;
 371                 de->dpe_flags = DPE_EXPANDED;
 372                 de->dpe_spec = ds->dps_isblock ? VBLK : VCHR;
 373         }
 374         de->dpe_plcy = np;
 375 
 376         ASSERT((de->dpe_flags & (DPE_ALLMINOR|DPE_EXPANDED)) ||
 377             de->dpe_expr != NULL);
 378 
 379         return (de);
 380 }
 381 
 382 static void
 383 freechain(devplcyent_t *de)
 384 {
 385         devplcyent_t *dn;
 386 
 387         do {
 388                 dn = de->dpe_next;
 389                 dpfree(de->dpe_plcy);
 390                 if (de->dpe_len != 0)
 391                         kmem_free(de->dpe_expr, de->dpe_len);
 392                 kmem_free(de, sizeof (*de));
 393                 de = dn;
 394         } while (de != NULL);
 395 }
 396 
 397 /*
 398  * Load the device policy.
 399  * The device policy currently makes nu distinction between the
 400  * block and characters devices; that is generally not a problem
 401  * as the names of those devices cannot clash.
 402  */
 403 int
 404 devpolicy_load(int nitems, size_t sz, devplcysys_t *uitmp)
 405 {
 406         int i, j;
 407         int nmaj = 0;
 408         major_t lastmajor;
 409         devplcysys_t *items;
 410         size_t mem;
 411         major_t curmaj;
 412         devplcyent_t **last, *de;
 413 
 414         tableent_t *newpolicy, *oldpolicy;
 415         devplcy_t *newnull, *newdflt, *oldnull, *olddflt;
 416         int oldcnt;
 417         int lastlen;
 418         int lastwild;
 419 
 420 #ifdef lint
 421         /* Lint can't figure out that the "i == 1" test protects all */
 422         lastlen = 0;
 423         lastwild = 0;
 424         lastmajor = 0;
 425 #endif
 426         /*
 427          * The application must agree with the kernel on the size of each
 428          * item; it must not exceed the maximum number and must be
 429          * at least 1 item in size.
 430          */
 431         if (sz != sizeof (devplcysys_t) || nitems > maxdevpolicy || nitems < 1)
 432                 return (EINVAL);
 433 
 434         mem = nitems * sz;
 435 
 436         items = kmem_alloc(mem, KM_SLEEP);
 437 
 438         if (copyin(uitmp, items, mem)) {
 439                 kmem_free(items, mem);
 440                 return (EFAULT);
 441         }
 442 
 443         /* Check for default policy, it must exist and be sorted first */
 444         if (items[0].dps_maj != DEVPOLICY_DFLT_MAJ) {
 445                 kmem_free(items, mem);
 446                 return (EINVAL);
 447         }
 448 
 449         /*
 450          * Application must deliver entries sorted.
 451          * Sorted meaning here:
 452          *      In major number order
 453          *      For each major number, we first need to have the explicit
 454          *      entries, then the wild card entries, longest first.
 455          */
 456         for (i = 1; i < nitems; i++) {
 457                 int len, wild;
 458                 char *tmp;
 459 
 460                 curmaj = items[i].dps_maj;
 461                 len = strlen(items[i].dps_minornm);
 462                 wild = len > 0 &&
 463                     (tmp = strchr(items[i].dps_minornm, '*')) != NULL;
 464 
 465                 /* Another default major, string too long or too many ``*'' */
 466                 if (curmaj == DEVPOLICY_DFLT_MAJ ||
 467                     len >= sizeof (items[i].dps_minornm) ||
 468                     wild && strchr(tmp + 1, '*') != NULL) {
 469                         kmem_free(items, mem);
 470                         return (EINVAL);
 471                 }
 472                 if (i == 1 || lastmajor < curmaj) {
 473                         lastmajor = curmaj;
 474                         nmaj++;
 475                 } else if (lastmajor > curmaj || lastwild > wild ||
 476                     lastwild && lastlen < len) {
 477                         kmem_free(items, mem);
 478                         return (EINVAL);
 479                 }
 480                 lastlen = len;
 481                 lastwild = wild;
 482         }
 483 
 484         if (AU_AUDITING())
 485                 audit_devpolicy(nitems, items);
 486 
 487         /*
 488          * Parse the policy.  We create an array for all major numbers
 489          * and in each major number bucket we'll have a linked list of
 490          * entries.  Each item may contain either a lo,hi minor pair
 491          * or a string/wild card matching a minor node.
 492          */
 493         if (nmaj > 0)
 494                 newpolicy = kmem_zalloc(nmaj * sizeof (tableent_t), KM_SLEEP);
 495 
 496         /*
 497          * We want to lock out concurrent updates but we don't want to
 498          * lock out device opens while we still need to allocate memory.
 499          * As soon as we allocate new devplcy_t's we commit to the next
 500          * generation number, so we must lock out other updates from here.
 501          */
 502         mutex_enter(&policymutex);
 503 
 504         /* New default and NULL policy */
 505         newnull = dpget();
 506 
 507         if (priv_isemptyset(&items[0].dps_rdp) &&
 508             priv_isemptyset(&items[0].dps_wrp)) {
 509                 newdflt = newnull;
 510                 dphold(newdflt);
 511         } else {
 512                 newdflt = dpget();
 513                 newdflt->dp_rdp = items[0].dps_rdp;
 514                 newdflt->dp_wrp = items[0].dps_wrp;
 515         }
 516 
 517         j = -1;
 518 
 519         /* Userland made sure sorting was ok */
 520         for (i = 1; i < nitems; i++) {
 521                 de = parse_policy(&items[i], newnull, newdflt);
 522 
 523                 if (j == -1 || curmaj != items[i].dps_maj) {
 524                         j++;
 525                         newpolicy[j].t_major = curmaj = items[i].dps_maj;
 526                         last = &newpolicy[j].t_ent;
 527                 }
 528                 *last = de;
 529                 last = &de->dpe_next;
 530         }
 531 
 532         /* Done parsing, throw away input */
 533         kmem_free(items, mem);
 534 
 535         /* Lock out all devpolicy_find()s */
 536         rw_enter(&policyrw, RW_WRITER);
 537 
 538         /* Install the new global data */
 539         oldnull = nullpolicy;
 540         nullpolicy = newnull;
 541 
 542         olddflt = dfltpolicy;
 543         dfltpolicy = newdflt;
 544 
 545         oldcnt = ntabent;
 546         ntabent = nmaj;
 547 
 548         totitems = nitems;
 549 
 550         oldpolicy = devpolicy;
 551         devpolicy = newpolicy;
 552 
 553         /* Force all calls by devpolicy_find() */
 554         devplcy_gen++;
 555 
 556         /* Reenable policy finds */
 557         rw_exit(&policyrw);
 558         mutex_exit(&policymutex);
 559 
 560         /* Free old stuff */
 561         if (oldcnt != 0) {
 562                 for (i = 0; i < oldcnt; i++)
 563                         freechain(oldpolicy[i].t_ent);
 564                 kmem_free(oldpolicy, oldcnt * sizeof (*oldpolicy));
 565         }
 566 
 567         dpfree(oldnull);
 568         dpfree(olddflt);
 569 
 570         return (0);
 571 }
 572 
 573 /*
 574  * Get device policy: argument one is a pointer to an integer holding
 575  * the number of items allocated for the 3rd argument; the size argument
 576  * is a revision check between kernel and userland.
 577  */
 578 int
 579 devpolicy_get(int *nitemp, size_t sz, devplcysys_t *uitmp)
 580 {
 581         int i;
 582         devplcyent_t *de;
 583         devplcysys_t *itmp;
 584         int ind;
 585         int nitems;
 586         int err = 0;
 587         size_t alloced;
 588 
 589         if (sz != sizeof (devplcysys_t))
 590                 return (EINVAL);
 591 
 592         if (copyin(nitemp, &nitems, sizeof (nitems)))
 593                 return (EFAULT);
 594 
 595         rw_enter(&policyrw, RW_READER);
 596 
 597         if (copyout(&totitems, nitemp, sizeof (totitems)))
 598                 err = EFAULT;
 599         else if (nitems < totitems)
 600                 err = ENOMEM;
 601 
 602         if (err != 0) {
 603                 rw_exit(&policyrw);
 604                 return (err);
 605         }
 606 
 607         alloced = totitems * sizeof (devplcysys_t);
 608         itmp = kmem_zalloc(alloced, KM_SLEEP);
 609 
 610         itmp[0].dps_rdp = dfltpolicy->dp_rdp;
 611         itmp[0].dps_wrp = dfltpolicy->dp_wrp;
 612         itmp[0].dps_maj = DEVPOLICY_DFLT_MAJ;
 613 
 614         ind = 1;
 615 
 616         for (i = 0; i < ntabent; i++) {
 617                 for (de = devpolicy[i].t_ent; de != NULL; de = de->dpe_next) {
 618                         itmp[ind].dps_maj = devpolicy[i].t_major;
 619                         itmp[ind].dps_rdp = de->dpe_plcy->dp_rdp;
 620                         itmp[ind].dps_wrp = de->dpe_plcy->dp_wrp;
 621                         if (de->dpe_len)
 622                                 (void) strcpy(itmp[ind].dps_minornm,
 623                                     de->dpe_expr);
 624                         else if (de->dpe_flags & DPE_ALLMINOR)
 625                                 (void) strcpy(itmp[ind].dps_minornm, "*");
 626                         else {
 627                                 itmp[ind].dps_lomin = de->dpe_lomin;
 628                                 itmp[ind].dps_himin = de->dpe_himin;
 629                                 itmp[ind].dps_isblock = de->dpe_spec == VBLK;
 630                         }
 631                         ind++;
 632                 }
 633         }
 634 
 635         rw_exit(&policyrw);
 636 
 637         if (copyout(itmp, uitmp, alloced))
 638                 err = EFAULT;
 639 
 640         kmem_free(itmp, alloced);
 641         return (err);
 642 }
 643 
 644 /*
 645  * Get device policy by device name.
 646  * This is the implementation of MODGETDEVPOLICYBYNAME
 647  */
 648 int
 649 devpolicy_getbyname(size_t sz, devplcysys_t *uitmp, char *devname)
 650 {
 651         devplcysys_t itm;
 652         devplcy_t *plcy;
 653         vtype_t spec;
 654         vnode_t *vp;
 655 
 656         if (sz != sizeof (devplcysys_t))
 657                 return (EINVAL);
 658 
 659         if (lookupname(devname, UIO_USERSPACE, FOLLOW,
 660             NULLVPP, &vp) != 0)
 661                 return (EINVAL);
 662 
 663         spec = vp->v_type;
 664         if (spec != VBLK && spec != VCHR) {
 665                 VN_RELE(vp);
 666                 return (EINVAL);
 667         }
 668 
 669         plcy = devpolicy_find(vp);
 670         VN_RELE(vp);
 671 
 672         bzero(&itm, sizeof (itm));
 673 
 674         /* These are the only values of interest */
 675         itm.dps_rdp = plcy->dp_rdp;
 676         itm.dps_wrp = plcy->dp_wrp;
 677 
 678         dpfree(plcy);
 679 
 680         if (copyout(&itm, uitmp, sz))
 681                 return (EFAULT);
 682         else
 683                 return (0);
 684 }
 685 
 686 static void
 687 priv_str_to_set(const char *priv_name, priv_set_t *priv_set)
 688 {
 689         if (priv_name == NULL || strcmp(priv_name, "none") == 0) {
 690                 priv_emptyset(priv_set);
 691         } else if (strcmp(priv_name, "all") == 0) {
 692                 priv_fillset(priv_set);
 693         } else {
 694                 int priv;
 695                 priv = priv_getbyname(priv_name, PRIV_ALLOC);
 696                 if (priv < 0) {
 697                         cmn_err(CE_WARN, "fail to allocate privilege: %s",
 698                             priv_name);
 699                         return;
 700                 }
 701                 priv_emptyset(priv_set);
 702                 priv_addset(priv_set, priv);
 703         }
 704 }
 705 
 706 /*
 707  * Return device privileges by privilege name
 708  * Called by ddi_create_priv_minor_node()
 709  */
 710 devplcy_t *
 711 devpolicy_priv_by_name(const char *read_priv, const char *write_priv)
 712 {
 713         devplcy_t *dp;
 714         mutex_enter(&policymutex);
 715         dp = dpget();
 716         mutex_exit(&policymutex);
 717         priv_str_to_set(read_priv, &dp->dp_rdp);
 718         priv_str_to_set(write_priv, &dp->dp_wrp);
 719 
 720         return (dp);
 721 }