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