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