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 2015 Gary Mills
  24  * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
  25  */
  26 
  27 #pragma weak _getprivimplinfo   = getprivimplinfo
  28 #pragma weak _priv_addset       = priv_addset
  29 #pragma weak _priv_allocset     = priv_allocset
  30 #pragma weak _priv_copyset      = priv_copyset
  31 #pragma weak _priv_delset       = priv_delset
  32 #pragma weak _priv_emptyset     = priv_emptyset
  33 #pragma weak _priv_basicset     = priv_basicset
  34 #pragma weak _priv_fillset      = priv_fillset
  35 #pragma weak _priv_freeset      = priv_freeset
  36 #pragma weak _priv_getbyname    = priv_getbyname
  37 #pragma weak _priv_getbynum     = priv_getbynum
  38 #pragma weak _priv_getsetbyname = priv_getsetbyname
  39 #pragma weak _priv_getsetbynum  = priv_getsetbynum
  40 #pragma weak _priv_ineffect     = priv_ineffect
  41 #pragma weak _priv_intersect    = priv_intersect
  42 #pragma weak _priv_inverse      = priv_inverse
  43 #pragma weak _priv_isemptyset   = priv_isemptyset
  44 #pragma weak _priv_isequalset   = priv_isequalset
  45 #pragma weak _priv_isfullset    = priv_isfullset
  46 #pragma weak _priv_ismember     = priv_ismember
  47 #pragma weak _priv_issubset     = priv_issubset
  48 #pragma weak _priv_set          = priv_set
  49 #pragma weak _priv_union        = priv_union
  50 
  51 #include "lint.h"
  52 
  53 #define _STRUCTURED_PROC        1
  54 
  55 #include "priv_private.h"
  56 #include "mtlib.h"
  57 #include "libc.h"
  58 #include <errno.h>
  59 #include <stdarg.h>
  60 #include <stdlib.h>
  61 #include <unistd.h>
  62 #include <strings.h>
  63 #include <synch.h>
  64 #include <alloca.h>
  65 #include <atomic.h>
  66 #include <sys/ucred.h>
  67 #include <sys/procfs.h>
  68 #include <sys/param.h>
  69 #include <sys/corectl.h>
  70 #include <priv_utils.h>
  71 #include <zone.h>
  72 
  73 /* Include each string only once - until the compiler/linker are fixed */
  74 static const char *permitted    = PRIV_PERMITTED;
  75 static const char *effective    = PRIV_EFFECTIVE;
  76 static const char *limit        = PRIV_LIMIT;
  77 static const char *inheritable  = PRIV_INHERITABLE;
  78 /*
  79  * Data independent privilege set operations.
  80  *
  81  * Only a few functions are provided that do not default to
  82  * the system implementation of privileges.  A limited set of
  83  * interfaces is provided that accepts a priv_data_t *
  84  * argument; this set of interfaces is a private interface between libc
  85  * and libproc.  It is delivered in order to interpret privilege sets
  86  * in debuggers in a implementation independent way.  As such, we
  87  * don't need to provide the bulk of the interfaces, only a few
  88  * boolean tests (isfull, isempty) the name<->num mappings and
  89  * set pretty print functions.   The boolean tests are only needed for
  90  * the latter, so those aren't provided externally.
  91  *
  92  * Additionally, we provide the function that maps the kernel implementation
  93  * structure into a libc private data structure.
  94  */
  95 
  96 priv_data_t *privdata;
  97 
  98 static mutex_t pd_lock = DEFAULTMUTEX;
  99 
 100 static int
 101 parseninfo(priv_info_names_t *na, char ***buf, int *cp)
 102 {
 103         char *q;
 104         int i;
 105 
 106         *buf = libc_malloc(sizeof (char *) * na->cnt);
 107 
 108         if (*buf == NULL)
 109                 return (-1);
 110 
 111         q = na->names;
 112 
 113         for (i = 0; i < na->cnt; i++) {
 114                 int l = strlen(q);
 115 
 116                 (*buf)[i] = q;
 117                 q += l + 1;
 118         }
 119         *cp = na->cnt;
 120         return (0);
 121 }
 122 
 123 struct strint {
 124         char *name;
 125         int rank;
 126 };
 127 
 128 static int
 129 strintcmp(const void *a, const void *b)
 130 {
 131         const struct strint *ap = a;
 132         const struct strint *bp = b;
 133 
 134         return (strcasecmp(ap->name, bp->name));
 135 }
 136 
 137 priv_data_t *
 138 __priv_parse_info(priv_impl_info_t *ip)
 139 {
 140         priv_data_t *tmp;
 141         char *x;
 142         size_t size = PRIV_IMPL_INFO_SIZE(ip);
 143         int i;
 144 
 145         tmp = libc_malloc(sizeof (*tmp));
 146 
 147         if (tmp == NULL)
 148                 return (NULL);
 149 
 150         (void) memset(tmp, 0, sizeof (*tmp));
 151 
 152         tmp->pd_pinfo = ip;
 153         tmp->pd_setsize = sizeof (priv_chunk_t) * ip->priv_setsize;
 154         tmp->pd_ucredsize = UCRED_SIZE(ip);
 155 
 156         x = (char *)ip;
 157         x += ip->priv_headersize;
 158 
 159         while (x < ((char *)ip) + size) {
 160                 /* LINTED: alignment */
 161                 priv_info_names_t *na = (priv_info_names_t *)x;
 162                 /* LINTED: alignment */
 163                 priv_info_set_t *st = (priv_info_set_t *)x;
 164                 struct strint *tmparr;
 165 
 166                 switch (na->info.priv_info_type) {
 167                 case PRIV_INFO_SETNAMES:
 168                         if (parseninfo(na, &tmp->pd_setnames, &tmp->pd_nsets))
 169                                 goto out;
 170                         break;
 171                 case PRIV_INFO_PRIVNAMES:
 172                         if (parseninfo(na, &tmp->pd_privnames, &tmp->pd_nprivs))
 173                                 goto out;
 174                         /*
 175                          * We compute a sorted index which allows us
 176                          * to present a sorted list of privileges
 177                          * without actually having to sort it each time.
 178                          */
 179                         tmp->pd_setsort = libc_malloc(tmp->pd_nprivs *
 180                             sizeof (int));
 181                         if (tmp->pd_setsort == NULL)
 182                                 goto out;
 183 
 184                         tmparr = libc_malloc(tmp->pd_nprivs *
 185                             sizeof (struct strint));
 186 
 187                         if (tmparr == NULL)
 188                                 goto out;
 189 
 190                         for (i = 0; i < tmp->pd_nprivs; i++) {
 191                                 tmparr[i].rank = i;
 192                                 tmparr[i].name = tmp->pd_privnames[i];
 193                         }
 194                         qsort(tmparr, tmp->pd_nprivs, sizeof (struct strint),
 195                             strintcmp);
 196                         for (i = 0; i < tmp->pd_nprivs; i++)
 197                                 tmp->pd_setsort[i] = tmparr[i].rank;
 198                         libc_free(tmparr);
 199                         break;
 200                 case PRIV_INFO_BASICPRIVS:
 201                         tmp->pd_basicset = (priv_set_t *)&st->set[0];
 202                         break;
 203                 default:
 204                         /* unknown, ignore */
 205                         break;
 206                 }
 207                 x += na->info.priv_info_size;
 208         }
 209         return (tmp);
 210 out:
 211         libc_free(tmp->pd_setnames);
 212         libc_free(tmp->pd_privnames);
 213         libc_free(tmp->pd_setsort);
 214         libc_free(tmp);
 215         return (NULL);
 216 }
 217 
 218 /*
 219  * Caller must have allocated d->pd_pinfo and should free it,
 220  * if necessary.
 221  */
 222 void
 223 __priv_free_info(priv_data_t *d)
 224 {
 225         libc_free(d->pd_setnames);
 226         libc_free(d->pd_privnames);
 227         libc_free(d->pd_setsort);
 228         libc_free(d);
 229 }
 230 
 231 /*
 232  * Return with the pd_lock held and data loaded or indicate failure.
 233  */
 234 int
 235 lock_data(void)
 236 {
 237         if (__priv_getdata() == NULL)
 238                 return (-1);
 239 
 240         lmutex_lock(&pd_lock);
 241         return (0);
 242 }
 243 
 244 boolean_t
 245 refresh_data(void)
 246 {
 247         priv_impl_info_t *ip, ii;
 248         priv_data_t *tmp;
 249         char *p0, *q0;
 250         int oldn, newn;
 251         int i;
 252 
 253         if (getprivinfo(&ii, sizeof (ii)) != 0 ||
 254             ii.priv_max == privdata->pd_nprivs)
 255                 return (B_FALSE);
 256 
 257         ip = alloca(PRIV_IMPL_INFO_SIZE(&ii));
 258 
 259         (void) getprivinfo(ip, PRIV_IMPL_INFO_SIZE(&ii));
 260 
 261         /* Parse the info; then copy the additional bits */
 262         tmp = __priv_parse_info(ip);
 263         if (tmp == NULL)
 264                 return (B_FALSE);
 265 
 266         oldn = privdata->pd_nprivs;
 267         p0 = privdata->pd_privnames[0];
 268 
 269         newn = tmp->pd_nprivs;
 270         q0 = tmp->pd_privnames[0];
 271 
 272         /* copy the extra information to the old datastructure */
 273         (void) memcpy((char *)privdata->pd_pinfo + sizeof (priv_impl_info_t),
 274             (char *)ip + sizeof (priv_impl_info_t),
 275             PRIV_IMPL_INFO_SIZE(ip) - sizeof (priv_impl_info_t));
 276 
 277         /* Copy the first oldn pointers */
 278         (void) memcpy(tmp->pd_privnames, privdata->pd_privnames,
 279             oldn * sizeof (char *));
 280 
 281         /* Adjust the rest */
 282         for (i = oldn; i < newn; i++)
 283                 tmp->pd_privnames[i] += p0 - q0;
 284 
 285         /* Install the larger arrays */
 286         libc_free(privdata->pd_privnames);
 287         privdata->pd_privnames = tmp->pd_privnames;
 288         tmp->pd_privnames = NULL;
 289 
 290         libc_free(privdata->pd_setsort);
 291         privdata->pd_setsort = tmp->pd_setsort;
 292         tmp->pd_setsort = NULL;
 293 
 294         /* Copy the rest of the data */
 295         *privdata->pd_pinfo = *ip;
 296 
 297         privdata->pd_nprivs = newn;
 298 
 299         __priv_free_info(tmp);
 300         return (B_TRUE);
 301 }
 302 
 303 void
 304 unlock_data(void)
 305 {
 306         lmutex_unlock(&pd_lock);
 307 }
 308 
 309 static priv_set_t *__priv_allocset(priv_data_t *);
 310 
 311 priv_data_t *
 312 __priv_getdata(void)
 313 {
 314         if (privdata == NULL) {
 315                 lmutex_lock(&pd_lock);
 316                 if (privdata == NULL) {
 317                         priv_data_t *tmp;
 318                         priv_impl_info_t *ip;
 319                         size_t size = sizeof (priv_impl_info_t) + 2048;
 320                         size_t realsize;
 321                         priv_impl_info_t *aip = alloca(size);
 322 
 323                         if (getprivinfo(aip, size) != 0)
 324                                 goto out;
 325 
 326                         realsize = PRIV_IMPL_INFO_SIZE(aip);
 327 
 328                         ip = libc_malloc(realsize);
 329 
 330                         if (ip == NULL)
 331                                 goto out;
 332 
 333                         if (realsize <= size) {
 334                                 (void) memcpy(ip, aip, realsize);
 335                         } else if (getprivinfo(ip, realsize) != 0) {
 336                                 libc_free(ip);
 337                                 goto out;
 338                         }
 339 
 340                         if ((tmp = __priv_parse_info(ip)) == NULL) {
 341                                 libc_free(ip);
 342                                 goto out;
 343                         }
 344 
 345                         /* Allocate the zoneset just once, here */
 346                         tmp->pd_zoneset = __priv_allocset(tmp);
 347                         if (tmp->pd_zoneset == NULL)
 348                                 goto clean;
 349 
 350                         if (zone_getattr(getzoneid(), ZONE_ATTR_PRIVSET,
 351                             tmp->pd_zoneset, tmp->pd_setsize)
 352                             == tmp->pd_setsize) {
 353                                 membar_producer();
 354                                 privdata = tmp;
 355                                 goto out;
 356                         }
 357 
 358                         priv_freeset(tmp->pd_zoneset);
 359 clean:
 360                         __priv_free_info(tmp);
 361                         libc_free(ip);
 362                 }
 363 out:
 364                 lmutex_unlock(&pd_lock);
 365         }
 366         membar_consumer();
 367         return (privdata);
 368 }
 369 
 370 const priv_impl_info_t *
 371 getprivimplinfo(void)
 372 {
 373         priv_data_t *d;
 374 
 375         LOADPRIVDATA(d);
 376 
 377         return (d->pd_pinfo);
 378 }
 379 
 380 static priv_set_t *
 381 priv_vlist(va_list ap)
 382 {
 383         priv_set_t *pset = priv_allocset();
 384         const char *priv;
 385 
 386         if (pset == NULL)
 387                 return (NULL);
 388 
 389         priv_emptyset(pset);
 390 
 391         while ((priv = va_arg(ap, const char *)) != NULL) {
 392                 if (priv_addset(pset, priv) < 0) {
 393                         priv_freeset(pset);
 394                         return (NULL);
 395                 }
 396         }
 397         return (pset);
 398 }
 399 
 400 /*
 401  * priv_set(op, set, priv_id1, priv_id2, ..., NULL)
 402  *
 403  * Library routine to enable a user process to set a specific
 404  * privilege set appropriately using a single call.  User is
 405  * required to terminate the list of privileges with NULL.
 406  */
 407 int
 408 priv_set(priv_op_t op, priv_ptype_t setname, ...)
 409 {
 410         va_list ap;
 411         priv_set_t *pset;
 412         int ret;
 413 
 414         va_start(ap, setname);
 415 
 416         pset = priv_vlist(ap);
 417 
 418         va_end(ap);
 419 
 420         if (pset == NULL)
 421                 return (-1);
 422 
 423         /* All sets */
 424         if (setname == NULL) {
 425                 priv_data_t *d;
 426                 int set;
 427 
 428                 LOADPRIVDATA(d);
 429 
 430                 for (set = 0; set < d->pd_nsets; set++)
 431                         if ((ret = syscall(SYS_privsys, PRIVSYS_SETPPRIV, op,
 432                             set, (void *)pset, d->pd_setsize)) != 0)
 433                                 break;
 434         } else {
 435                 ret = setppriv(op, setname, pset);
 436         }
 437 
 438         priv_freeset(pset);
 439         return (ret);
 440 }
 441 
 442 /*
 443  * priv_ineffect(privilege).
 444  * tests the existence of a privilege against the effective set.
 445  */
 446 boolean_t
 447 priv_ineffect(const char *priv)
 448 {
 449         priv_set_t *curset;
 450         boolean_t res;
 451 
 452         curset = priv_allocset();
 453 
 454         if (curset == NULL)
 455                 return (B_FALSE);
 456 
 457         if (getppriv(effective, curset) != 0 ||
 458             !priv_ismember(curset, priv))
 459                 res = B_FALSE;
 460         else
 461                 res = B_TRUE;
 462 
 463         priv_freeset(curset);
 464 
 465         return (res);
 466 }
 467 
 468 /*
 469  * The routine __init_daemon_priv() is private to Solaris and is
 470  * used by daemons to limit the privileges they can use and
 471  * to set the uid they run under.
 472  */
 473 
 474 static const char root_cp[] = "/core.%f.%t";
 475 static const char daemon_cp[] = "/var/tmp/core.%f.%t";
 476 
 477 int
 478 __init_daemon_priv(int flags, uid_t uid, gid_t gid, ...)
 479 {
 480         priv_set_t *nset;
 481         priv_set_t *perm = NULL;
 482         va_list pa;
 483         priv_data_t *d;
 484         int ret = -1;
 485         char buf[1024];
 486 
 487         LOADPRIVDATA(d);
 488 
 489         va_start(pa, gid);
 490 
 491         nset = priv_vlist(pa);
 492 
 493         va_end(pa);
 494 
 495         if (nset == NULL)
 496                 return (-1);
 497 
 498         /* Always add the basic set */
 499         if (d->pd_basicset != NULL)
 500                 priv_union(d->pd_basicset, nset);
 501 
 502         /*
 503          * This is not a significant failure: it allows us to start programs
 504          * with sufficient privileges and with the proper uid.   We don't
 505          * care enough about the extra groups in that case.
 506          */
 507         if (flags & PU_RESETGROUPS)
 508                 (void) setgroups(0, NULL);
 509 
 510         if (gid != (gid_t)-1 && setgid(gid) != 0)
 511                 goto end;
 512 
 513         perm = priv_allocset();
 514         if (perm == NULL)
 515                 goto end;
 516 
 517         /* E = P */
 518         (void) getppriv(permitted, perm);
 519         (void) setppriv(PRIV_SET, effective, perm);
 520 
 521         /* Now reset suid and euid */
 522         if (uid != (uid_t)-1 && setreuid(uid, uid) != 0)
 523                 goto end;
 524 
 525         /* Check for the limit privs */
 526         if ((flags & PU_LIMITPRIVS) &&
 527             setppriv(PRIV_SET, limit, nset) != 0)
 528                 goto end;
 529 
 530         if (flags & PU_CLEARLIMITSET) {
 531                 priv_emptyset(perm);
 532                 if (setppriv(PRIV_SET, limit, perm) != 0)
 533                         goto end;
 534         }
 535 
 536         /* Remove the privileges from all the other sets */
 537         if (setppriv(PRIV_SET, permitted, nset) != 0)
 538                 goto end;
 539 
 540         if (!(flags & PU_INHERITPRIVS))
 541                 priv_emptyset(nset);
 542 
 543         ret = setppriv(PRIV_SET, inheritable, nset);
 544 end:
 545         priv_freeset(nset);
 546         priv_freeset(perm);
 547 
 548         if (core_get_process_path(buf, sizeof (buf), getpid()) == 0 &&
 549             strcmp(buf, "core") == 0) {
 550 
 551                 if ((uid == (uid_t)-1 ? geteuid() : uid) == 0) {
 552                         (void) core_set_process_path(root_cp, sizeof (root_cp),
 553                             getpid());
 554                 } else {
 555                         (void) core_set_process_path(daemon_cp,
 556                             sizeof (daemon_cp), getpid());
 557                 }
 558         }
 559         (void) setpflags(__PROC_PROTECT, 0);
 560 
 561         return (ret);
 562 }
 563 
 564 /*
 565  * The routine __fini_daemon_priv() is private to Solaris and is
 566  * used by daemons to clear remaining unwanted privileges and
 567  * reenable core dumps.
 568  */
 569 void
 570 __fini_daemon_priv(const char *priv, ...)
 571 {
 572         priv_set_t *nset;
 573         va_list pa;
 574 
 575         if (priv != NULL) {
 576 
 577                 va_start(pa, priv);
 578                 nset = priv_vlist(pa);
 579                 va_end(pa);
 580 
 581                 if (nset == NULL)
 582                         return;
 583 
 584                 (void) priv_addset(nset, priv);
 585                 (void) setppriv(PRIV_OFF, permitted, nset);
 586                 priv_freeset(nset);
 587         }
 588 
 589         (void) setpflags(__PROC_PROTECT, 0);
 590 }
 591 
 592 /*
 593  * The routine __init_suid_priv() is private to Solaris and is
 594  * used by set-uid root programs to limit the privileges acquired
 595  * to those actually needed.
 596  */
 597 
 598 static priv_set_t *bracketpriv;
 599 
 600 int
 601 __init_suid_priv(int flags, ...)
 602 {
 603         priv_set_t *nset = NULL;
 604         priv_set_t *tmpset = NULL;
 605         va_list pa;
 606         int r = -1;
 607         uid_t ruid, euid;
 608 
 609         euid = geteuid();
 610 
 611         /* If we're not set-uid root, don't reset the uid */
 612         if (euid == 0) {
 613                 ruid = getuid();
 614                 /* If we're running as root, keep everything */
 615                 if (ruid == 0)
 616                         return (0);
 617         }
 618 
 619         /* Can call this only once */
 620         if (bracketpriv != NULL)
 621                 return (-1);
 622 
 623         va_start(pa, flags);
 624 
 625         nset = priv_vlist(pa);
 626 
 627         va_end(pa);
 628 
 629         if (nset == NULL)
 630                 goto end;
 631 
 632         tmpset = priv_allocset();
 633 
 634         if (tmpset == NULL)
 635                 goto end;
 636 
 637         /* We cannot grow our privileges beyond P, so start there */
 638         (void) getppriv(permitted, tmpset);
 639 
 640         /* Is the privilege we need even in P? */
 641         if (!priv_issubset(nset, tmpset))
 642                 goto end;
 643 
 644         bracketpriv = priv_allocset();
 645         if (bracketpriv == NULL)
 646                 goto end;
 647 
 648         priv_copyset(nset, bracketpriv);
 649 
 650         /* Always add the basic set */
 651         priv_union(priv_basic(), nset);
 652 
 653         /* But don't add what we don't have */
 654         priv_intersect(tmpset, nset);
 655 
 656         (void) getppriv(inheritable, tmpset);
 657 
 658         /* And stir in the inheritable privileges */
 659         priv_union(tmpset, nset);
 660 
 661         if ((r = setppriv(PRIV_SET, effective, tmpset)) != 0)
 662                 goto end;
 663 
 664         if ((r = setppriv(PRIV_SET, permitted, nset)) != 0)
 665                 goto end;
 666 
 667         if (flags & PU_CLEARLIMITSET)
 668                 priv_emptyset(nset);
 669 
 670         if ((flags & (PU_LIMITPRIVS|PU_CLEARLIMITSET)) != 0 &&
 671             (r = setppriv(PRIV_SET, limit, nset)) != 0)
 672                 goto end;
 673 
 674         if (euid == 0)
 675                 r = setreuid(ruid, ruid);
 676 
 677 end:
 678         priv_freeset(tmpset);
 679         priv_freeset(nset);
 680         if (r != 0) {
 681                 /* Fail without leaving uid 0 around */
 682                 if (euid == 0)
 683                         (void) setreuid(ruid, ruid);
 684                 priv_freeset(bracketpriv);
 685                 bracketpriv = NULL;
 686         }
 687 
 688         return (r);
 689 }
 690 
 691 /*
 692  * Toggle privileges on/off in the effective set.
 693  */
 694 int
 695 __priv_bracket(priv_op_t op)
 696 {
 697         /* We're running fully privileged or didn't check errors first time */
 698         if (bracketpriv == NULL)
 699                 return (0);
 700 
 701         /* Only PRIV_ON and PRIV_OFF are valid */
 702         if (op == PRIV_SET)
 703                 return (-1);
 704 
 705         return (setppriv(op, effective, bracketpriv));
 706 }
 707 
 708 /*
 709  * Remove privileges from E & P.
 710  */
 711 void
 712 __priv_relinquish(void)
 713 {
 714         if (bracketpriv != NULL) {
 715                 (void) setppriv(PRIV_OFF, permitted, bracketpriv);
 716                 priv_freeset(bracketpriv);
 717                 bracketpriv = NULL;
 718         }
 719 }
 720 
 721 /*
 722  * Use binary search on the ordered list.
 723  */
 724 int
 725 __priv_getbyname(const priv_data_t *d, const char *name)
 726 {
 727         char *const *list;
 728         const int *order;
 729         int lo = 0;
 730         int hi;
 731 
 732         if (d == NULL)
 733                 return (-1);
 734 
 735         list = d->pd_privnames;
 736         order = d->pd_setsort;
 737         hi = d->pd_nprivs - 1;
 738 
 739         if (strncasecmp(name, "priv_", 5) == 0)
 740                 name += 5;
 741 
 742         do {
 743                 int mid = (lo + hi) / 2;
 744                 int res = strcasecmp(name, list[order[mid]]);
 745 
 746                 if (res == 0)
 747                         return (order[mid]);
 748                 else if (res < 0)
 749                         hi = mid - 1;
 750                 else
 751                         lo = mid + 1;
 752         } while (lo <= hi);
 753 
 754         errno = EINVAL;
 755         return (-1);
 756 }
 757 
 758 int
 759 priv_getbyname(const char *name)
 760 {
 761         WITHPRIVLOCKED(int, -1, __priv_getbyname(GETPRIVDATA(), name))
 762 }
 763 
 764 int
 765 __priv_getsetbyname(const priv_data_t *d, const char *name)
 766 {
 767         int i;
 768         int n = d->pd_nsets;
 769         char *const *list = d->pd_setnames;
 770 
 771         if (strncasecmp(name, "priv_", 5) == 0)
 772                 name += 5;
 773 
 774         for (i = 0; i < n; i++) {
 775                 if (strcasecmp(list[i], name) == 0)
 776                         return (i);
 777         }
 778 
 779         errno = EINVAL;
 780         return (-1);
 781 }
 782 
 783 int
 784 priv_getsetbyname(const char *name)
 785 {
 786         /* Not locked: sets don't change */
 787         return (__priv_getsetbyname(GETPRIVDATA(), name));
 788 }
 789 
 790 static const char *
 791 priv_bynum(int i, int n, char **list)
 792 {
 793         if (i < 0 || i >= n)
 794                 return (NULL);
 795 
 796         return (list[i]);
 797 }
 798 
 799 const char *
 800 __priv_getbynum(const priv_data_t *d, int num)
 801 {
 802         if (d == NULL)
 803                 return (NULL);
 804         return (priv_bynum(num, d->pd_nprivs, d->pd_privnames));
 805 }
 806 
 807 const char *
 808 priv_getbynum(int num)
 809 {
 810         WITHPRIVLOCKED(const char *, NULL, __priv_getbynum(GETPRIVDATA(), num))
 811 }
 812 
 813 const char *
 814 __priv_getsetbynum(const priv_data_t *d, int num)
 815 {
 816         if (d == NULL)
 817                 return (NULL);
 818         return (priv_bynum(num, d->pd_nsets, d->pd_setnames));
 819 }
 820 
 821 const char *
 822 priv_getsetbynum(int num)
 823 {
 824         return (__priv_getsetbynum(GETPRIVDATA(), num));
 825 }
 826 
 827 
 828 /*
 829  * Privilege manipulation functions
 830  *
 831  * Without knowing the details of the privilege set implementation,
 832  * opaque pointers can be used to manipulate sets at will.
 833  */
 834 
 835 static priv_set_t *
 836 __priv_allocset(priv_data_t *d)
 837 {
 838         if (d == NULL)
 839                 return (NULL);
 840 
 841         return (libc_malloc(d->pd_setsize));
 842 }
 843 
 844 priv_set_t *
 845 priv_allocset(void)
 846 {
 847         return (__priv_allocset(GETPRIVDATA()));
 848 }
 849 
 850 void
 851 priv_freeset(priv_set_t *p)
 852 {
 853         int er = errno;
 854 
 855         libc_free(p);
 856         errno = er;
 857 }
 858 
 859 void
 860 __priv_emptyset(priv_data_t *d, priv_set_t *set)
 861 {
 862         (void) memset(set, 0, d->pd_setsize);
 863 }
 864 
 865 void
 866 priv_emptyset(priv_set_t *set)
 867 {
 868         __priv_emptyset(GETPRIVDATA(), set);
 869 }
 870 
 871 void
 872 priv_basicset(priv_set_t *set)
 873 {
 874         priv_copyset(priv_basic(), set);
 875 }
 876 
 877 void
 878 __priv_fillset(priv_data_t *d, priv_set_t *set)
 879 {
 880         (void) memset(set, ~0, d->pd_setsize);
 881 }
 882 
 883 void
 884 priv_fillset(priv_set_t *set)
 885 {
 886         __priv_fillset(GETPRIVDATA(), set);
 887 }
 888 
 889 
 890 #define PRIV_TEST_BODY_D(d, test) \
 891         int i; \
 892 \
 893         for (i = d->pd_pinfo->priv_setsize; i-- > 0; ) \
 894                 if (!(test)) \
 895                         return (B_FALSE); \
 896 \
 897         return (B_TRUE)
 898 
 899 boolean_t
 900 priv_isequalset(const priv_set_t *a, const priv_set_t *b)
 901 {
 902         priv_data_t *d;
 903 
 904         LOADPRIVDATA(d);
 905 
 906         return ((boolean_t)(memcmp(a, b, d->pd_setsize) == 0));
 907 }
 908 
 909 boolean_t
 910 __priv_isemptyset(priv_data_t *d, const priv_set_t *set)
 911 {
 912         PRIV_TEST_BODY_D(d, ((priv_chunk_t *)set)[i] == 0);
 913 }
 914 
 915 boolean_t
 916 priv_isemptyset(const priv_set_t *set)
 917 {
 918         return (__priv_isemptyset(GETPRIVDATA(), set));
 919 }
 920 
 921 boolean_t
 922 __priv_isfullset(priv_data_t *d, const priv_set_t *set)
 923 {
 924         PRIV_TEST_BODY_D(d, ((priv_chunk_t *)set)[i] == ~(priv_chunk_t)0);
 925 }
 926 
 927 boolean_t
 928 priv_isfullset(const priv_set_t *set)
 929 {
 930         return (__priv_isfullset(GETPRIVDATA(), set));
 931 }
 932 
 933 /*
 934  * Return true if a is a subset of b
 935  */
 936 boolean_t
 937 __priv_issubset(priv_data_t *d, const priv_set_t *a, const priv_set_t *b)
 938 {
 939         PRIV_TEST_BODY_D(d, (((priv_chunk_t *)a)[i] | ((priv_chunk_t *)b)[i]) ==
 940             ((priv_chunk_t *)b)[i]);
 941 }
 942 
 943 boolean_t
 944 priv_issubset(const priv_set_t *a, const priv_set_t *b)
 945 {
 946         return (__priv_issubset(GETPRIVDATA(), a, b));
 947 }
 948 
 949 #define PRIV_CHANGE_BODY(a, op, b) \
 950         int i; \
 951         priv_data_t *d; \
 952 \
 953         LOADPRIVDATA(d); \
 954 \
 955         for (i = 0; i < d->pd_pinfo->priv_setsize; i++) \
 956                 ((priv_chunk_t *)a)[i] op \
 957                         ((priv_chunk_t *)b)[i]
 958 
 959 /* B = A ^ B */
 960 void
 961 priv_intersect(const priv_set_t *a, priv_set_t *b)
 962 {
 963         /* CSTYLED */
 964         PRIV_CHANGE_BODY(b, &=, a);
 965 }
 966 
 967 /* B = A */
 968 void
 969 priv_copyset(const priv_set_t *a, priv_set_t *b)
 970 {
 971         /* CSTYLED */
 972         PRIV_CHANGE_BODY(b, =, a);
 973 }
 974 
 975 /* B = A v B */
 976 void
 977 priv_union(const priv_set_t *a, priv_set_t *b)
 978 {
 979         /* CSTYLED */
 980         PRIV_CHANGE_BODY(b, |=, a);
 981 }
 982 
 983 /* A = ! A */
 984 void
 985 priv_inverse(priv_set_t *a)
 986 {
 987         PRIV_CHANGE_BODY(a, = ~, a);
 988 }
 989 
 990 /*
 991  * Manipulating single privileges.
 992  */
 993 
 994 int
 995 priv_addset(priv_set_t *a, const char *p)
 996 {
 997         int priv = priv_getbyname(p);
 998 
 999         if (priv < 0)
1000                 return (-1);
1001 
1002         PRIV_ADDSET(a, priv);
1003 
1004         return (0);
1005 }
1006 
1007 int
1008 priv_delset(priv_set_t *a, const char *p)
1009 {
1010         int priv = priv_getbyname(p);
1011 
1012         if (priv < 0)
1013                 return (-1);
1014 
1015         PRIV_DELSET(a, priv);
1016         return (0);
1017 }
1018 
1019 boolean_t
1020 priv_ismember(const priv_set_t *a, const char *p)
1021 {
1022         int priv = priv_getbyname(p);
1023 
1024         if (priv < 0)
1025                 return (B_FALSE);
1026 
1027         return ((boolean_t)PRIV_ISMEMBER(a, priv));
1028 }