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) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright 2015, Joyent, Inc.
  25  */
  26 
  27 #include <sys/atomic.h>
  28 #include <sys/door.h>
  29 #include <sys/proc.h>
  30 #include <sys/cred_impl.h>
  31 #include <sys/policy.h>
  32 #include <sys/priv.h>
  33 #include <sys/klpd.h>
  34 #include <sys/errno.h>
  35 #include <sys/kmem.h>
  36 #include <sys/project.h>
  37 #include <sys/systm.h>
  38 #include <sys/sysmacros.h>
  39 #include <sys/pathname.h>
  40 #include <sys/varargs.h>
  41 #include <sys/zone.h>
  42 #include <netinet/in.h>
  43 
  44 #define ROUNDUP(a, n) (((a) + ((n) - 1)) & ~((n) - 1))
  45 
  46 static kmutex_t klpd_mutex;
  47 
  48 typedef struct klpd_reg {
  49         struct klpd_reg *klpd_next;
  50         struct klpd_reg **klpd_refp;
  51         door_handle_t   klpd_door;
  52         pid_t           klpd_door_pid;
  53         priv_set_t      klpd_pset;
  54         cred_t          *klpd_cred;
  55         int             klpd_indel;             /* Disabled */
  56         uint32_t        klpd_ref;
  57 } klpd_reg_t;
  58 
  59 
  60 /*
  61  * This data structure hangs off the credential of a process; the
  62  * credential is finalized and cannot be changed; but this structure
  63  * can be changed when a new door server for the particular group
  64  * needs to be registered.  It is refcounted and shared between
  65  * processes with common ancestry.
  66  *
  67  * The reference count is atomically updated.
  68  *
  69  * But the registration probably needs to be updated under a lock.
  70  */
  71 typedef struct credklpd {
  72         kmutex_t        crkl_lock;
  73         klpd_reg_t      *crkl_reg;
  74         uint32_t        crkl_ref;
  75 } credklpd_t;
  76 
  77 klpd_reg_t *klpd_list;
  78 
  79 static void klpd_unlink(klpd_reg_t *);
  80 static int klpd_unreg_dh(door_handle_t);
  81 
  82 static credklpd_t *crklpd_alloc(void);
  83 
  84 void crklpd_setreg(credklpd_t *, klpd_reg_t *);
  85 
  86 extern size_t max_vnode_path;
  87 
  88 void
  89 klpd_rele(klpd_reg_t *p)
  90 {
  91         if (atomic_dec_32_nv(&p->klpd_ref) == 0) {
  92                 if (p->klpd_refp != NULL)
  93                         klpd_unlink(p);
  94                 if (p->klpd_cred != NULL)
  95                         crfree(p->klpd_cred);
  96                 door_ki_rele(p->klpd_door);
  97                 kmem_free(p, sizeof (*p));
  98         }
  99 }
 100 
 101 /*
 102  * In order to be able to walk the lists, we can't unlink the entry
 103  * until the reference count drops to 0.  If we remove it too soon,
 104  * list walkers will terminate when they happen to call a now orphaned
 105  * entry.
 106  */
 107 static klpd_reg_t *
 108 klpd_rele_next(klpd_reg_t *p)
 109 {
 110         klpd_reg_t *r = p->klpd_next;
 111 
 112         klpd_rele(p);
 113         return (r);
 114 }
 115 
 116 
 117 static void
 118 klpd_hold(klpd_reg_t *p)
 119 {
 120         atomic_inc_32(&p->klpd_ref);
 121 }
 122 
 123 /*
 124  * Remove registration from where it is registered.  Returns next in list.
 125  */
 126 static void
 127 klpd_unlink(klpd_reg_t *p)
 128 {
 129         ASSERT(p->klpd_refp == NULL || *p->klpd_refp == p);
 130 
 131         if (p->klpd_refp != NULL)
 132                 *p->klpd_refp = p->klpd_next;
 133 
 134         if (p->klpd_next != NULL)
 135                 p->klpd_next->klpd_refp = p->klpd_refp;
 136         p->klpd_refp = NULL;
 137 }
 138 
 139 /*
 140  * Remove all elements of the klpd list and decrement their refcnts.
 141  * The lock guarding the list should be held; this function is
 142  * called when we are sure we want to destroy the list completely
 143  * list but not so sure that the reference counts of all elements have
 144  * dropped back to 1.
 145  */
 146 void
 147 klpd_freelist(klpd_reg_t **pp)
 148 {
 149         klpd_reg_t *p;
 150 
 151         while ((p = *pp) != NULL) {
 152                 klpd_unlink(p);
 153                 klpd_rele(p);
 154         }
 155 }
 156 
 157 /*
 158  * Link new entry in list.  The Boolean argument specifies whether this
 159  * list can contain only a single item or multiple items.
 160  * Returns the entry which needs to be released if single is B_TRUE.
 161  */
 162 static klpd_reg_t *
 163 klpd_link(klpd_reg_t *p, klpd_reg_t **listp, boolean_t single)
 164 {
 165         klpd_reg_t *old = *listp;
 166 
 167         ASSERT(p->klpd_ref == 1);
 168 
 169         ASSERT(old == NULL || *old->klpd_refp == old);
 170         p->klpd_refp = listp;
 171         p->klpd_next = single ? NULL : old;
 172         *listp = p;
 173         if (old != NULL) {
 174                 if (single) {
 175                         ASSERT(old->klpd_next == NULL);
 176                         old->klpd_refp = NULL;
 177                         return (old);
 178                 } else
 179                         old->klpd_refp = &p->klpd_next;
 180         }
 181         return (NULL);
 182 }
 183 
 184 /*
 185  * The typical call consists of:
 186  *      - priv_set_t
 187  *      - some integer data (type, value)
 188  * for now, it's just one bit.
 189  */
 190 static klpd_head_t *
 191 klpd_marshall(klpd_reg_t *p, const priv_set_t *rq, va_list ap)
 192 {
 193         char    *tmp;
 194         uint_t  type;
 195         vnode_t *vp;
 196         size_t  len = sizeof (priv_set_t) + sizeof (klpd_head_t);
 197         size_t  plen, clen;
 198         int     proto;
 199 
 200         klpd_arg_t *kap = NULL;
 201         klpd_head_t *khp;
 202 
 203         type = va_arg(ap, uint_t);
 204         switch (type) {
 205         case KLPDARG_NOMORE:
 206                 khp = kmem_zalloc(len, KM_SLEEP);
 207                 khp->klh_argoff = 0;
 208                 break;
 209         case KLPDARG_VNODE:
 210                 len += offsetof(klpd_arg_t, kla_str);
 211                 vp = va_arg(ap, vnode_t *);
 212                 if (vp == NULL)
 213                         return (NULL);
 214 
 215                 tmp = va_arg(ap, char *);
 216 
 217                 if (tmp != NULL && *tmp != '\0')
 218                         clen = strlen(tmp) + 1;
 219                 else
 220                         clen = 0;
 221 
 222                 len += ROUNDUP(MAXPATHLEN, sizeof (uint_t));
 223                 khp = kmem_zalloc(len, KM_SLEEP);
 224 
 225                 khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
 226                 kap = KLH_ARG(khp);
 227 
 228                 if (vnodetopath(crgetzone(p->klpd_cred)->zone_rootvp,
 229                     vp, kap->kla_str, MAXPATHLEN, p->klpd_cred) != 0) {
 230                         kmem_free(khp, len);
 231                         return (NULL);
 232                 }
 233                 if (clen != 0) {
 234                         plen = strlen(kap->kla_str);
 235                         if (plen + clen + 1 >= MAXPATHLEN) {
 236                                 kmem_free(khp, len);
 237                                 return (NULL);
 238                         }
 239                         /* Don't make root into a double "/" */
 240                         if (plen <= 2)
 241                                 plen = 0;
 242                         kap->kla_str[plen] = '/';
 243                         bcopy(tmp, &kap->kla_str[plen + 1], clen);
 244                 }
 245                 break;
 246         case KLPDARG_PORT:
 247                 proto = va_arg(ap, int);
 248                 switch (proto) {
 249                 case IPPROTO_TCP:       type = KLPDARG_TCPPORT;
 250                                         break;
 251                 case IPPROTO_UDP:       type = KLPDARG_UDPPORT;
 252                                         break;
 253                 case IPPROTO_SCTP:      type = KLPDARG_SCTPPORT;
 254                                         break;
 255                 case PROTO_SDP:         type = KLPDARG_SDPPORT;
 256                                         break;
 257                 }
 258                 /* FALLTHROUGH */
 259         case KLPDARG_INT:
 260         case KLPDARG_TCPPORT:
 261         case KLPDARG_UDPPORT:
 262         case KLPDARG_SCTPPORT:
 263         case KLPDARG_SDPPORT:
 264                 len += sizeof (*kap);
 265                 khp = kmem_zalloc(len, KM_SLEEP);
 266                 khp->klh_argoff = sizeof (klpd_head_t) + sizeof (priv_set_t);
 267                 kap = KLH_ARG(khp);
 268                 kap->kla_int = va_arg(ap, int);
 269                 break;
 270         default:
 271                 return (NULL);
 272         }
 273         khp->klh_vers = KLPDCALL_VERS;
 274         khp->klh_len = len;
 275         khp->klh_privoff = sizeof (*khp);
 276         *KLH_PRIVSET(khp) = *rq;
 277         if (kap != NULL) {
 278                 kap->kla_type = type;
 279                 kap->kla_dlen = len - khp->klh_argoff;
 280         }
 281         return (khp);
 282 }
 283 
 284 static int
 285 klpd_do_call(klpd_reg_t *p, const priv_set_t *req, va_list ap)
 286 {
 287         door_arg_t da;
 288         int res;
 289         int dres;
 290         klpd_head_t *klh;
 291 
 292         if (p->klpd_door_pid == curproc->p_pid)
 293                 return (-1);
 294 
 295         klh = klpd_marshall(p, req, ap);
 296 
 297         if (klh == NULL)
 298                 return (-1);
 299 
 300         da.data_ptr = (char *)klh;
 301         da.data_size = klh->klh_len;
 302         da.desc_ptr = NULL;
 303         da.desc_num = 0;
 304         da.rbuf = (char *)&res;
 305         da.rsize = sizeof (res);
 306 
 307         while ((dres = door_ki_upcall_limited(p->klpd_door, &da, NULL,
 308             SIZE_MAX, 0)) != 0) {
 309                 switch (dres) {
 310                 case EAGAIN:
 311                         delay(1);
 312                         continue;
 313                 case EINVAL:
 314                 case EBADF:
 315                         /* Bad door, don't call it again. */
 316                         (void) klpd_unreg_dh(p->klpd_door);
 317                         /* FALLTHROUGH */
 318                 case EINTR:
 319                         /* Pending signal, nothing we can do. */
 320                         /* FALLTHROUGH */
 321                 default:
 322                         kmem_free(klh, klh->klh_len);
 323                         return (-1);
 324                 }
 325         }
 326         kmem_free(klh, klh->klh_len);
 327         /* Bogus return value, must be a failure */
 328         if (da.rbuf != (char *)&res) {
 329                 kmem_free(da.rbuf, da.rsize);
 330                 return (-1);
 331         }
 332         return (res);
 333 }
 334 
 335 uint32_t klpd_bad_locks;
 336 
 337 int
 338 klpd_call(const cred_t *cr, const priv_set_t *req, va_list ap)
 339 {
 340         klpd_reg_t *p;
 341         int rv = -1;
 342         credklpd_t *ckp;
 343         zone_t *ckzone;
 344 
 345         /*
 346          * These locks must not be held when this code is called;
 347          * callbacks to userland with these locks held will result
 348          * in issues.  That said, the code at the call sides was
 349          * restructured not to call with any of the locks held and
 350          * no policies operate by default on most processes.
 351          */
 352         if (mutex_owned(&pidlock) || mutex_owned(&curproc->p_lock) ||
 353             mutex_owned(&curproc->p_crlock)) {
 354                 atomic_inc_32(&klpd_bad_locks);
 355                 return (-1);
 356         }
 357 
 358         /*
 359          * Enforce the limit set for the call process (still).
 360          */
 361         if (!priv_issubset(req, &CR_LPRIV(cr)))
 362                 return (-1);
 363 
 364         /* Try 1: get the credential specific klpd */
 365         if ((ckp = crgetcrklpd(cr)) != NULL) {
 366                 mutex_enter(&ckp->crkl_lock);
 367                 if ((p = ckp->crkl_reg) != NULL &&
 368                     p->klpd_indel == 0 &&
 369                     priv_issubset(req, &p->klpd_pset)) {
 370                         klpd_hold(p);
 371                         mutex_exit(&ckp->crkl_lock);
 372                         rv = klpd_do_call(p, req, ap);
 373                         mutex_enter(&ckp->crkl_lock);
 374                         klpd_rele(p);
 375                         mutex_exit(&ckp->crkl_lock);
 376                         if (rv != -1)
 377                                 return (rv == 0 ? 0 : -1);
 378                 } else {
 379                         mutex_exit(&ckp->crkl_lock);
 380                 }
 381         }
 382 
 383         /* Try 2: get the project specific klpd */
 384         mutex_enter(&klpd_mutex);
 385 
 386         if ((p = curproj->kpj_klpd) != NULL) {
 387                 klpd_hold(p);
 388                 mutex_exit(&klpd_mutex);
 389                 if (p->klpd_indel == 0 &&
 390                     priv_issubset(req, &p->klpd_pset)) {
 391                         rv = klpd_do_call(p, req, ap);
 392                 }
 393                 mutex_enter(&klpd_mutex);
 394                 klpd_rele(p);
 395                 mutex_exit(&klpd_mutex);
 396 
 397                 if (rv != -1)
 398                         return (rv == 0 ? 0 : -1);
 399         } else {
 400                 mutex_exit(&klpd_mutex);
 401         }
 402 
 403         /* Try 3: get the global klpd list */
 404         ckzone = crgetzone(cr);
 405         mutex_enter(&klpd_mutex);
 406 
 407         for (p = klpd_list; p != NULL; ) {
 408                 zone_t *kkzone = crgetzone(p->klpd_cred);
 409                 if ((kkzone == &zone0 || kkzone == ckzone) &&
 410                     p->klpd_indel == 0 &&
 411                     priv_issubset(req, &p->klpd_pset)) {
 412                         klpd_hold(p);
 413                         mutex_exit(&klpd_mutex);
 414                         rv = klpd_do_call(p, req, ap);
 415                         mutex_enter(&klpd_mutex);
 416 
 417                         p = klpd_rele_next(p);
 418 
 419                         if (rv != -1)
 420                                 break;
 421                 } else {
 422                         p = p->klpd_next;
 423                 }
 424         }
 425         mutex_exit(&klpd_mutex);
 426         return (rv == 0 ? 0 : -1);
 427 }
 428 
 429 /*
 430  * Register the klpd.
 431  * If the pid_t passed in is positive, update the registration for
 432  * the specific process; that is only possible if the process already
 433  * has a registration on it.  This change of registration will affect
 434  * all processes which share common ancestry.
 435  *
 436  * MY_PID (pid 0) can be used to create or change the context for
 437  * the current process, typically done after fork().
 438  *
 439  * A negative value can be used to register a klpd globally.
 440  *
 441  * The per-credential klpd needs to be cleaned up when entering
 442  * a zone or unsetting the flag.
 443  */
 444 int
 445 klpd_reg(int did, idtype_t type, id_t id, priv_set_t *psetbuf)
 446 {
 447         cred_t *cr = CRED();
 448         door_handle_t dh;
 449         klpd_reg_t *kpd;
 450         priv_set_t pset;
 451         door_info_t di;
 452         credklpd_t *ckp = NULL;
 453         pid_t pid = -1;
 454         projid_t proj = -1;
 455         kproject_t *kpp = NULL;
 456 
 457         if (CR_FLAGS(cr) & PRIV_XPOLICY)
 458                 return (set_errno(EINVAL));
 459 
 460         if (copyin(psetbuf, &pset, sizeof (priv_set_t)))
 461                 return (set_errno(EFAULT));
 462 
 463         if (!priv_issubset(&pset, &CR_OEPRIV(cr)))
 464                 return (set_errno(EPERM));
 465 
 466         switch (type) {
 467         case P_PID:
 468                 pid = (pid_t)id;
 469                 if (pid == P_MYPID)
 470                         pid = curproc->p_pid;
 471                 if (pid == curproc->p_pid)
 472                         ckp = crklpd_alloc();
 473                 break;
 474         case P_PROJID:
 475                 proj = (projid_t)id;
 476                 kpp = project_hold_by_id(proj, crgetzone(cr),
 477                     PROJECT_HOLD_FIND);
 478                 if (kpp == NULL)
 479                         return (set_errno(ESRCH));
 480                 break;
 481         default:
 482                 return (set_errno(ENOTSUP));
 483         }
 484 
 485 
 486         /*
 487          * Verify the door passed in; it must be a door and we won't
 488          * allow processes to be called on their own behalf.
 489          */
 490         dh = door_ki_lookup(did);
 491         if (dh == NULL || door_ki_info(dh, &di) != 0) {
 492                 if (ckp != NULL)
 493                         crklpd_rele(ckp);
 494                 if (kpp != NULL)
 495                         project_rele(kpp);
 496                 return (set_errno(EBADF));
 497         }
 498         if (type == P_PID && pid == di.di_target) {
 499                 if (ckp != NULL)
 500                         crklpd_rele(ckp);
 501                 ASSERT(kpp == NULL);
 502                 return (set_errno(EINVAL));
 503         }
 504 
 505         kpd = kmem_zalloc(sizeof (*kpd), KM_SLEEP);
 506         crhold(kpd->klpd_cred = cr);
 507         kpd->klpd_door = dh;
 508         kpd->klpd_door_pid = di.di_target;
 509         kpd->klpd_ref = 1;
 510         kpd->klpd_pset = pset;
 511 
 512         if (kpp != NULL) {
 513                 mutex_enter(&klpd_mutex);
 514                 kpd = klpd_link(kpd, &kpp->kpj_klpd, B_TRUE);
 515                 mutex_exit(&klpd_mutex);
 516                 if (kpd != NULL)
 517                         klpd_rele(kpd);
 518                 project_rele(kpp);
 519         } else if ((int)pid < 0) {
 520                 /* Global daemon */
 521                 mutex_enter(&klpd_mutex);
 522                 (void) klpd_link(kpd, &klpd_list, B_FALSE);
 523                 mutex_exit(&klpd_mutex);
 524         } else if (pid == curproc->p_pid) {
 525                 proc_t *p = curproc;
 526                 cred_t *newcr = cralloc();
 527 
 528                 /* No need to lock, sole reference to ckp */
 529                 kpd = klpd_link(kpd, &ckp->crkl_reg, B_TRUE);
 530 
 531                 if (kpd != NULL)
 532                         klpd_rele(kpd);
 533 
 534                 mutex_enter(&p->p_crlock);
 535                 cr = p->p_cred;
 536                 crdup_to(cr, newcr);
 537                 crsetcrklpd(newcr, ckp);
 538                 p->p_cred = newcr;   /* Already held for p_cred */
 539 
 540                 crhold(newcr);          /* Hold once for the current thread */
 541                 mutex_exit(&p->p_crlock);
 542                 crfree(cr);             /* One for the p_cred */
 543                 crset(p, newcr);
 544         } else {
 545                 proc_t *p;
 546                 cred_t *pcr;
 547                 mutex_enter(&pidlock);
 548                 p = prfind(pid);
 549                 if (p == NULL || !prochasprocperm(p, curproc, CRED())) {
 550                         mutex_exit(&pidlock);
 551                         klpd_rele(kpd);
 552                         return (set_errno(p == NULL ? ESRCH : EPERM));
 553                 }
 554                 mutex_enter(&p->p_crlock);
 555                 crhold(pcr = p->p_cred);
 556                 mutex_exit(&pidlock);
 557                 mutex_exit(&p->p_crlock);
 558                 /*
 559                  * We're going to update the credential's ckp in place;
 560                  * this requires that it exists.
 561                  */
 562                 ckp = crgetcrklpd(pcr);
 563                 if (ckp == NULL) {
 564                         crfree(pcr);
 565                         klpd_rele(kpd);
 566                         return (set_errno(EINVAL));
 567                 }
 568                 crklpd_setreg(ckp, kpd);
 569                 crfree(pcr);
 570         }
 571 
 572         return (0);
 573 }
 574 
 575 static int
 576 klpd_unreg_dh(door_handle_t dh)
 577 {
 578         klpd_reg_t *p;
 579 
 580         mutex_enter(&klpd_mutex);
 581         for (p = klpd_list; p != NULL; p = p->klpd_next) {
 582                 if (p->klpd_door == dh)
 583                         break;
 584         }
 585         if (p == NULL) {
 586                 mutex_exit(&klpd_mutex);
 587                 return (EINVAL);
 588         }
 589         if (p->klpd_indel != 0) {
 590                 mutex_exit(&klpd_mutex);
 591                 return (EAGAIN);
 592         }
 593         p->klpd_indel = 1;
 594         klpd_rele(p);
 595         mutex_exit(&klpd_mutex);
 596         return (0);
 597 }
 598 
 599 int
 600 klpd_unreg(int did, idtype_t type, id_t id)
 601 {
 602         door_handle_t dh;
 603         int res = 0;
 604         proc_t *p;
 605         pid_t pid;
 606         projid_t proj;
 607         kproject_t *kpp = NULL;
 608         credklpd_t *ckp;
 609 
 610         switch (type) {
 611         case P_PID:
 612                 pid = (pid_t)id;
 613                 break;
 614         case P_PROJID:
 615                 proj = (projid_t)id;
 616                 kpp = project_hold_by_id(proj, crgetzone(CRED()),
 617                     PROJECT_HOLD_FIND);
 618                 if (kpp == NULL)
 619                         return (set_errno(ESRCH));
 620                 break;
 621         default:
 622                 return (set_errno(ENOTSUP));
 623         }
 624 
 625         dh = door_ki_lookup(did);
 626         if (dh == NULL) {
 627                 if (kpp != NULL)
 628                         project_rele(kpp);
 629                 return (set_errno(EINVAL));
 630         }
 631 
 632         if (kpp != NULL) {
 633                 mutex_enter(&klpd_mutex);
 634                 if (kpp->kpj_klpd == NULL)
 635                         res = ESRCH;
 636                 else
 637                         klpd_freelist(&kpp->kpj_klpd);
 638                 mutex_exit(&klpd_mutex);
 639                 project_rele(kpp);
 640                 goto out;
 641         } else if ((int)pid > 0) {
 642                 mutex_enter(&pidlock);
 643                 p = prfind(pid);
 644                 if (p == NULL) {
 645                         mutex_exit(&pidlock);
 646                         door_ki_rele(dh);
 647                         return (set_errno(ESRCH));
 648                 }
 649                 mutex_enter(&p->p_crlock);
 650                 mutex_exit(&pidlock);
 651         } else if (pid == 0) {
 652                 p = curproc;
 653                 mutex_enter(&p->p_crlock);
 654         } else {
 655                 res = klpd_unreg_dh(dh);
 656                 goto out;
 657         }
 658 
 659         ckp = crgetcrklpd(p->p_cred);
 660         if (ckp != NULL) {
 661                 crklpd_setreg(ckp, NULL);
 662         } else {
 663                 res = ESRCH;
 664         }
 665         mutex_exit(&p->p_crlock);
 666 
 667 out:
 668         door_ki_rele(dh);
 669 
 670         if (res != 0)
 671                 return (set_errno(res));
 672         return (0);
 673 }
 674 
 675 void
 676 crklpd_hold(credklpd_t *crkpd)
 677 {
 678         atomic_inc_32(&crkpd->crkl_ref);
 679 }
 680 
 681 void
 682 crklpd_rele(credklpd_t *crkpd)
 683 {
 684         if (atomic_dec_32_nv(&crkpd->crkl_ref) == 0) {
 685                 if (crkpd->crkl_reg != NULL)
 686                         klpd_rele(crkpd->crkl_reg);
 687                 mutex_destroy(&crkpd->crkl_lock);
 688                 kmem_free(crkpd, sizeof (*crkpd));
 689         }
 690 }
 691 
 692 static credklpd_t *
 693 crklpd_alloc(void)
 694 {
 695         credklpd_t *res = kmem_alloc(sizeof (*res), KM_SLEEP);
 696 
 697         mutex_init(&res->crkl_lock, NULL, MUTEX_DEFAULT, NULL);
 698         res->crkl_ref = 1;
 699         res->crkl_reg = NULL;
 700 
 701         return (res);
 702 }
 703 
 704 void
 705 crklpd_setreg(credklpd_t *crk, klpd_reg_t *new)
 706 {
 707         klpd_reg_t *old;
 708 
 709         mutex_enter(&crk->crkl_lock);
 710         if (new == NULL) {
 711                 old = crk->crkl_reg;
 712                 if (old != NULL)
 713                         klpd_unlink(old);
 714         } else {
 715                 old = klpd_link(new, &crk->crkl_reg, B_TRUE);
 716         }
 717         mutex_exit(&crk->crkl_lock);
 718 
 719         if (old != NULL)
 720                 klpd_rele(old);
 721 }
 722 
 723 /* Allocate and register the pfexec specific callback */
 724 int
 725 pfexec_reg(int did)
 726 {
 727         door_handle_t dh;
 728         int err = secpolicy_pfexec_register(CRED());
 729         klpd_reg_t *pfx;
 730         door_info_t di;
 731         zone_t *myzone = crgetzone(CRED());
 732 
 733         if (err != 0)
 734                 return (set_errno(err));
 735 
 736         dh = door_ki_lookup(did);
 737         if (dh == NULL || door_ki_info(dh, &di) != 0)
 738                 return (set_errno(EBADF));
 739 
 740         pfx = kmem_zalloc(sizeof (*pfx), KM_SLEEP);
 741 
 742         pfx->klpd_door = dh;
 743         pfx->klpd_door_pid = di.di_target;
 744         pfx->klpd_ref = 1;
 745         pfx->klpd_cred = NULL;
 746         mutex_enter(&myzone->zone_lock);
 747         pfx = klpd_link(pfx, &myzone->zone_pfexecd, B_TRUE);
 748         mutex_exit(&myzone->zone_lock);
 749         if (pfx != NULL)
 750                 klpd_rele(pfx);
 751 
 752         return (0);
 753 }
 754 
 755 int
 756 pfexec_unreg(int did)
 757 {
 758         door_handle_t dh;
 759         int err = 0;
 760         zone_t *myzone = crgetzone(CRED());
 761         klpd_reg_t *pfd;
 762 
 763         dh = door_ki_lookup(did);
 764         if (dh == NULL)
 765                 return (set_errno(EBADF));
 766 
 767         mutex_enter(&myzone->zone_lock);
 768         pfd = myzone->zone_pfexecd;
 769         if (pfd != NULL && pfd->klpd_door == dh) {
 770                 klpd_unlink(pfd);
 771         } else {
 772                 pfd = NULL;
 773                 err = EINVAL;
 774         }
 775         mutex_exit(&myzone->zone_lock);
 776         door_ki_rele(dh);
 777         /*
 778          * crfree() cannot be called with zone_lock held; it is called
 779          * indirectly through closing the door handle
 780          */
 781         if (pfd != NULL)
 782                 klpd_rele(pfd);
 783         if (err != 0)
 784                 return (set_errno(err));
 785         return (0);
 786 }
 787 
 788 static int
 789 get_path(char *buf, const char *path, int len)
 790 {
 791         size_t lc;
 792         char *s;
 793 
 794         if (len < 0)
 795                 len = strlen(path);
 796 
 797         if (*path == '/' && len < MAXPATHLEN) {
 798                 (void) strcpy(buf, path);
 799                 return (0);
 800         }
 801         /*
 802          * Build the pathname using the current directory + resolve pathname.
 803          * The resolve pathname either starts with a normal component and
 804          * we can just concatenate them or it starts with one
 805          * or more ".." component and we can remove those; the
 806          * last one cannot be a ".." and the current directory has
 807          * more components than the number of ".." in the resolved pathname.
 808          */
 809         if (dogetcwd(buf, MAXPATHLEN) != 0)
 810                 return (-1);
 811 
 812         lc = strlen(buf);
 813 
 814         while (len > 3 && strncmp("../", path, 3) == 0) {
 815                 len -= 3;
 816                 path += 3;
 817 
 818                 s = strrchr(buf, '/');
 819                 if (s == NULL || s == buf)
 820                         return (-1);
 821 
 822                 *s = '\0';
 823                 lc = s - buf;
 824         }
 825         /* Add a "/" and a NUL */
 826         if (lc < 2 || lc + len + 2 >= MAXPATHLEN)
 827                 return (-1);
 828 
 829         buf[lc] = '/';
 830         (void) strcpy(buf + lc + 1, path);
 831 
 832         return (0);
 833 }
 834 
 835 /*
 836  * Perform the pfexec upcall.
 837  *
 838  * The pfexec upcall is different from the klpd_upcall in that a failure
 839  * will lead to a denial of execution.
 840  */
 841 int
 842 pfexec_call(const cred_t *cr, struct pathname *rpnp, cred_t **pfcr,
 843     boolean_t *scrub)
 844 {
 845         klpd_reg_t *pfd;
 846         pfexec_arg_t *pap;
 847         pfexec_reply_t pr, *prp;
 848         door_arg_t da;
 849         int dres;
 850         cred_t *ncr = NULL;
 851         int err = EACCES;
 852         priv_set_t *iset;
 853         priv_set_t *lset;
 854         zone_t *myzone = crgetzone(CRED());
 855         size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
 856 
 857         /* Find registration */
 858         mutex_enter(&myzone->zone_lock);
 859         if ((pfd = myzone->zone_pfexecd) != NULL)
 860                 klpd_hold(pfd);
 861         mutex_exit(&myzone->zone_lock);
 862 
 863         if (pfd == NULL)
 864                 return (0);
 865 
 866         if (pfd->klpd_door_pid == curproc->p_pid) {
 867                 klpd_rele(pfd);
 868                 return (0);
 869         }
 870 
 871         pap = kmem_zalloc(pasize, KM_SLEEP);
 872 
 873         if (get_path(pap->pfa_path, rpnp->pn_path, rpnp->pn_pathlen) == -1)
 874                 goto out1;
 875 
 876         pap->pfa_vers = PFEXEC_ARG_VERS;
 877         pap->pfa_call = PFEXEC_EXEC_ATTRS;
 878         pap->pfa_len = pasize;
 879         pap->pfa_uid = crgetruid(cr);
 880 
 881         da.data_ptr = (char *)pap;
 882         da.data_size = pap->pfa_len;
 883         da.desc_ptr = NULL;
 884         da.desc_num = 0;
 885         da.rbuf = (char *)≺
 886         da.rsize = sizeof (pr);
 887 
 888         while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
 889                 switch (dres) {
 890                 case EAGAIN:
 891                         delay(1);
 892                         continue;
 893                 case EINVAL:
 894                 case EBADF:
 895                         /* FALLTHROUGH */
 896                 case EINTR:
 897                         /* FALLTHROUGH */
 898                 default:
 899                         goto out;
 900                 }
 901         }
 902 
 903         prp = (pfexec_reply_t *)da.rbuf;
 904         /*
 905          * Check the size of the result and the alignment of the
 906          * privilege sets.
 907          */
 908         if (da.rsize < sizeof (pr) ||
 909             prp->pfr_ioff > da.rsize - sizeof (priv_set_t) ||
 910             prp->pfr_loff > da.rsize - sizeof (priv_set_t) ||
 911             (prp->pfr_loff & (sizeof (priv_chunk_t) - 1)) != 0 ||
 912             (prp->pfr_ioff & (sizeof (priv_chunk_t) - 1)) != 0)
 913                 goto out;
 914 
 915         /*
 916          * Get results:
 917          *      allow/allow with additional credentials/disallow[*]
 918          *
 919          *      euid, uid, egid, gid, privs, and limitprivs
 920          * We now have somewhat more flexibility we could even set E and P
 921          * judiciously but that would break some currently valid assumptions
 922          *      [*] Disallow is not readily supported by always including
 923          *      the Basic Solaris User profile in all user's profiles.
 924          */
 925 
 926         if (!prp->pfr_allowed) {
 927                 err = EACCES;
 928                 goto out;
 929         }
 930         if (!prp->pfr_setcred) {
 931                 err = 0;
 932                 goto out;
 933         }
 934         ncr = crdup((cred_t *)cr);
 935 
 936         /*
 937          * Generate the new credential set scrubenv if ruid != euid (or set)
 938          * the "I'm set-uid flag" but that is not inherited so scrubbing
 939          * the environment is a requirement.
 940          */
 941         /* Set uids or gids, note that -1 will do the right thing */
 942         if (crsetresuid(ncr, prp->pfr_ruid, prp->pfr_euid, prp->pfr_euid) != 0)
 943                 goto out;
 944         if (crsetresgid(ncr, prp->pfr_rgid, prp->pfr_egid, prp->pfr_egid) != 0)
 945                 goto out;
 946 
 947         *scrub = prp->pfr_scrubenv;
 948 
 949         if (prp->pfr_clearflag)
 950                 CR_FLAGS(ncr) &= ~PRIV_PFEXEC;
 951 
 952         /* We cannot exceed our Limit set, no matter what */
 953         iset = PFEXEC_REPLY_IPRIV(prp);
 954 
 955         if (iset != NULL) {
 956                 if (!priv_issubset(iset, &CR_LPRIV(ncr)))
 957                         goto out;
 958                 priv_union(iset, &CR_IPRIV(ncr));
 959         }
 960 
 961         /* Nor can we increate our Limit set itself */
 962         lset = PFEXEC_REPLY_LPRIV(prp);
 963 
 964         if (lset != NULL) {
 965                 if (!priv_issubset(lset, &CR_LPRIV(ncr)))
 966                         goto out;
 967                 CR_LPRIV(ncr) = *lset;
 968         }
 969 
 970         /* Exec will do the standard set operations */
 971 
 972         err = 0;
 973 out:
 974         if (da.rbuf != (char *)&pr)
 975                 kmem_free(da.rbuf, da.rsize);
 976 out1:
 977         kmem_free(pap, pasize);
 978         klpd_rele(pfd);
 979         if (ncr != NULL) {
 980                 if (err == 0)
 981                         *pfcr = ncr;
 982                 else
 983                         crfree(ncr);
 984         }
 985         return (err);
 986 }
 987 
 988 int
 989 get_forced_privs(const cred_t *cr, const char *respn, priv_set_t *set)
 990 {
 991         klpd_reg_t *pfd;
 992         pfexec_arg_t *pap;
 993         door_arg_t da;
 994         int dres;
 995         int err = -1;
 996         priv_set_t *fset, pmem;
 997         cred_t *zkcr;
 998         zone_t *myzone = crgetzone(cr);
 999         size_t pasize = PFEXEC_ARG_SIZE(MAXPATHLEN);
1000 
1001         mutex_enter(&myzone->zone_lock);
1002         if ((pfd = myzone->zone_pfexecd) != NULL)
1003                 klpd_hold(pfd);
1004         mutex_exit(&myzone->zone_lock);
1005 
1006         if (pfd == NULL)
1007                 return (-1);
1008 
1009         if (pfd->klpd_door_pid == curproc->p_pid) {
1010                 klpd_rele(pfd);
1011                 return (0);
1012         }
1013 
1014         pap = kmem_zalloc(pasize, KM_SLEEP);
1015 
1016         if (get_path(pap->pfa_path, respn, -1) == -1)
1017                 goto out1;
1018 
1019         pap->pfa_vers = PFEXEC_ARG_VERS;
1020         pap->pfa_call = PFEXEC_FORCED_PRIVS;
1021         pap->pfa_len = pasize;
1022         pap->pfa_uid = (uid_t)-1;                    /* Not relevant */
1023 
1024         da.data_ptr = (char *)pap;
1025         da.data_size = pap->pfa_len;
1026         da.desc_ptr = NULL;
1027         da.desc_num = 0;
1028         da.rbuf = (char *)&pmem;
1029         da.rsize = sizeof (pmem);
1030 
1031         while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1032                 switch (dres) {
1033                 case EAGAIN:
1034                         delay(1);
1035                         continue;
1036                 case EINVAL:
1037                 case EBADF:
1038                 case EINTR:
1039                 default:
1040                         goto out;
1041                 }
1042         }
1043 
1044         /*
1045          * Check the size of the result, it's a privilege set.
1046          */
1047         if (da.rsize != sizeof (priv_set_t))
1048                 goto out;
1049 
1050         fset = (priv_set_t *)da.rbuf;
1051 
1052         /*
1053          * We restrict the forced privileges with whatever is available in
1054          * the current zone.
1055          */
1056         zkcr = zone_kcred();
1057         priv_intersect(&CR_LPRIV(zkcr), fset);
1058 
1059         /*
1060          * But we fail if the forced privileges are not found in the current
1061          * Limit set.
1062          */
1063         if (!priv_issubset(fset, &CR_LPRIV(cr))) {
1064                 err = EACCES;
1065         } else if (!priv_isemptyset(fset)) {
1066                 err = 0;
1067                 *set = *fset;
1068         }
1069 out:
1070         if (da.rbuf != (char *)&pmem)
1071                 kmem_free(da.rbuf, da.rsize);
1072 out1:
1073         kmem_free(pap, pasize);
1074         klpd_rele(pfd);
1075         return (err);
1076 }
1077 
1078 int
1079 check_user_privs(const cred_t *cr, const priv_set_t *set)
1080 {
1081         klpd_reg_t *pfd;
1082         pfexec_arg_t *pap;
1083         door_arg_t da;
1084         int dres;
1085         int err = -1;
1086         zone_t *myzone = crgetzone(cr);
1087         size_t pasize = PFEXEC_ARG_SIZE(sizeof (priv_set_t));
1088         uint32_t res;
1089 
1090         mutex_enter(&myzone->zone_lock);
1091         if ((pfd = myzone->zone_pfexecd) != NULL)
1092                 klpd_hold(pfd);
1093         mutex_exit(&myzone->zone_lock);
1094 
1095         if (pfd == NULL)
1096                 return (-1);
1097 
1098         if (pfd->klpd_door_pid == curproc->p_pid) {
1099                 klpd_rele(pfd);
1100                 return (0);
1101         }
1102 
1103         pap = kmem_zalloc(pasize, KM_SLEEP);
1104 
1105         *(priv_set_t *)&pap->pfa_buf = *set;
1106 
1107         pap->pfa_vers = PFEXEC_ARG_VERS;
1108         pap->pfa_call = PFEXEC_USER_PRIVS;
1109         pap->pfa_len = pasize;
1110         pap->pfa_uid = crgetruid(cr);
1111 
1112         da.data_ptr = (char *)pap;
1113         da.data_size = pap->pfa_len;
1114         da.desc_ptr = NULL;
1115         da.desc_num = 0;
1116         da.rbuf = (char *)&res;
1117         da.rsize = sizeof (res);
1118 
1119         while ((dres = door_ki_upcall(pfd->klpd_door, &da)) != 0) {
1120                 switch (dres) {
1121                 case EAGAIN:
1122                         delay(1);
1123                         continue;
1124                 case EINVAL:
1125                 case EBADF:
1126                 case EINTR:
1127                 default:
1128                         goto out;
1129                 }
1130         }
1131 
1132         /*
1133          * Check the size of the result.
1134          */
1135         if (da.rsize != sizeof (res))
1136                 goto out;
1137 
1138         if (*(uint32_t *)da.rbuf == 1)
1139                 err = 0;
1140 out:
1141         if (da.rbuf != (char *)&res)
1142                 kmem_free(da.rbuf, da.rsize);
1143 out1:
1144         kmem_free(pap, pasize);
1145         klpd_rele(pfd);
1146         return (err);
1147 }