Print this page
    
Revert exi_zone to exi_zoneid, and install exi_ne backpointer
NFS Auth per-zone needs better cleanup
curzone reality check and teardown changes to use the RIGHT zone
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/nfs/nfs_auth.c
          +++ new/usr/src/uts/common/fs/nfs/nfs_auth.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) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
  24   24   * Copyright (c) 2015 by Delphix. All rights reserved.
  25   25   * Copyright 2018 Nexenta Systems, Inc. All rights reserved.
  26   26   */
  27   27  
  28   28  #include <sys/param.h>
  29   29  #include <sys/errno.h>
  30   30  #include <sys/vfs.h>
  31   31  #include <sys/vnode.h>
  32   32  #include <sys/cred.h>
  33   33  #include <sys/cmn_err.h>
  34   34  #include <sys/systm.h>
  35   35  #include <sys/kmem.h>
  36   36  #include <sys/pathname.h>
  37   37  #include <sys/utsname.h>
  38   38  #include <sys/debug.h>
  39   39  #include <sys/door.h>
  40   40  #include <sys/sdt.h>
  41   41  #include <sys/thread.h>
  42   42  #include <sys/avl.h>
  43   43  
  44   44  #include <rpc/types.h>
  45   45  #include <rpc/auth.h>
  46   46  #include <rpc/clnt.h>
  47   47  
  48   48  #include <nfs/nfs.h>
  49   49  #include <nfs/export.h>
  50   50  #include <nfs/nfs_clnt.h>
  51   51  #include <nfs/auth.h>
  52   52  
  53   53  static struct kmem_cache *exi_cache_handle;
  54   54  static void exi_cache_reclaim(void *);
  55   55  static void exi_cache_reclaim_zone(nfs_globals_t *);
  56   56  static void exi_cache_trim(struct exportinfo *exi);
  57   57  
  58   58  extern pri_t minclsyspri;
  59   59  
  60   60  /* NFS auth cache statistics */
  61   61  volatile uint_t nfsauth_cache_hit;
  62   62  volatile uint_t nfsauth_cache_miss;
  63   63  volatile uint_t nfsauth_cache_refresh;
  64   64  volatile uint_t nfsauth_cache_reclaim;
  65   65  volatile uint_t exi_cache_auth_reclaim_failed;
  66   66  volatile uint_t exi_cache_clnt_reclaim_failed;
  67   67  
  68   68  /*
  69   69   * The lifetime of an auth cache entry:
  70   70   * ------------------------------------
  71   71   *
  72   72   * An auth cache entry is created with both the auth_time
  73   73   * and auth_freshness times set to the current time.
  74   74   *
  75   75   * Upon every client access which results in a hit, the
  76   76   * auth_time will be updated.
  77   77   *
  78   78   * If a client access determines that the auth_freshness
  79   79   * indicates that the entry is STALE, then it will be
  80   80   * refreshed. Note that this will explicitly reset
  81   81   * auth_time.
  82   82   *
  83   83   * When the REFRESH successfully occurs, then the
  84   84   * auth_freshness is updated.
  85   85   *
  86   86   * There are two ways for an entry to leave the cache:
  87   87   *
  88   88   * 1) Purged by an action on the export (remove or changed)
  89   89   * 2) Memory backpressure from the kernel (check against NFSAUTH_CACHE_TRIM)
  90   90   *
  91   91   * For 2) we check the timeout value against auth_time.
  92   92   */
  93   93  
  94   94  /*
  95   95   * Number of seconds until we mark for refresh an auth cache entry.
  96   96   */
  97   97  #define NFSAUTH_CACHE_REFRESH 600
  98   98  
  99   99  /*
 100  100   * Number of idle seconds until we yield to backpressure
 101  101   * to trim a cache entry.
 102  102   */
 103  103  #define NFSAUTH_CACHE_TRIM 3600
 104  104  
 105  105  /*
 106  106   * While we could encapuslate the exi_list inside the
 107  107   * exi structure, we can't do that for the auth_list.
 108  108   * So, to keep things looking clean, we keep them both
 109  109   * in these external lists.
 110  110   */
 111  111  typedef struct refreshq_exi_node {
 112  112          struct exportinfo       *ren_exi;
 113  113          list_t                  ren_authlist;
 114  114          list_node_t             ren_node;
 115  115  } refreshq_exi_node_t;
 116  116  
 117  117  typedef struct refreshq_auth_node {
 118  118          struct auth_cache       *ran_auth;
 119  119          char                    *ran_netid;
 120  120          list_node_t             ran_node;
 121  121  } refreshq_auth_node_t;
 122  122  
 123  123  /*
 124  124   * Used to manipulate things on the refreshq_queue.  Note that the refresh
 125  125   * thread will effectively pop a node off of the queue, at which point it
 126  126   * will no longer need to hold the mutex.
 127  127   */
 128  128  static kmutex_t refreshq_lock;
 129  129  static list_t refreshq_queue;
 130  130  static kcondvar_t refreshq_cv;
 131  131  
 132  132  /*
 133  133   * If there is ever a problem with loading the module, then nfsauth_fini()
 134  134   * needs to be called to remove state.  In that event, since the refreshq
 135  135   * thread has been started, they need to work together to get rid of state.
 136  136   */
 137  137  typedef enum nfsauth_refreshq_thread_state {
 138  138          REFRESHQ_THREAD_RUNNING,
 139  139          REFRESHQ_THREAD_FINI_REQ,
 140  140          REFRESHQ_THREAD_HALTED,
 141  141          REFRESHQ_THREAD_NEED_CREATE
 142  142  } nfsauth_refreshq_thread_state_t;
 143  143  
 144  144  typedef struct nfsauth_globals {
 145  145          kmutex_t        mountd_lock;
 146  146          door_handle_t   mountd_dh;
 147  147  
 148  148          /*
 149  149           * Used to manipulate things on the refreshq_queue.  Note that the
 150  150           * refresh thread will effectively pop a node off of the queue,
 151  151           * at which point it will no longer need to hold the mutex.
 152  152           */
 153  153          kmutex_t        refreshq_lock;
 154  154          list_t          refreshq_queue;
 155  155          kcondvar_t      refreshq_cv;
 156  156  
 157  157          /*
 158  158           * A list_t would be overkill.  These are auth_cache entries which are
 159  159           * no longer linked to an exi.  It should be the case that all of their
 160  160           * states are NFS_AUTH_INVALID, i.e., the only way to be put on this
 161  161           * list is iff their state indicated that they had been placed on the
 162  162           * refreshq_queue.
 163  163           *
 164  164           * Note that while there is no link from the exi or back to the exi,
 165  165           * the exi can not go away until these entries are harvested.
 166  166           */
 167  167          struct auth_cache               *refreshq_dead_entries;
 168  168          nfsauth_refreshq_thread_state_t refreshq_thread_state;
 169  169  
 170  170  } nfsauth_globals_t;
 171  171  
 172  172  static void nfsauth_free_node(struct auth_cache *);
 173  173  static void nfsauth_refresh_thread(nfsauth_globals_t *);
 174  174  
 175  175  static int nfsauth_cache_compar(const void *, const void *);
 176  176  
 177  177  static nfsauth_globals_t *
 178  178  nfsauth_get_zg(void)
 179  179  {
 180  180          nfs_globals_t *ng = zone_getspecific(nfssrv_zone_key, curzone);
 181  181          nfsauth_globals_t *nag = ng->nfs_auth;
 182  182          ASSERT(nag != NULL);
 183  183          return (nag);
 184  184  }
 185  185  
 186  186  void
 187  187  mountd_args(uint_t did)
 188  188  {
 189  189          nfsauth_globals_t *nag;
 190  190  
 191  191          nag = nfsauth_get_zg();
 192  192          mutex_enter(&nag->mountd_lock);
 193  193          if (nag->mountd_dh != NULL)
 194  194                  door_ki_rele(nag->mountd_dh);
 195  195          nag->mountd_dh = door_ki_lookup(did);
 196  196          mutex_exit(&nag->mountd_lock);
 197  197  }
 198  198  
 199  199  void
 200  200  nfsauth_init(void)
 201  201  {
 202  202          exi_cache_handle = kmem_cache_create("exi_cache_handle",
 203  203              sizeof (struct auth_cache), 0, NULL, NULL,
 204  204              exi_cache_reclaim, NULL, NULL, 0);
 205  205  }
 206  206  
 207  207  void
 208  208  nfsauth_fini(void)
 209  209  {
 210  210          kmem_cache_destroy(exi_cache_handle);
 211  211  }
 212  212  
 213  213  void
 214  214  nfsauth_zone_init(nfs_globals_t *ng)
 215  215  {
 216  216          nfsauth_globals_t *nag;
 217  217  
 218  218          nag = kmem_zalloc(sizeof (*nag), KM_SLEEP);
 219  219  
 220  220          /*
 221  221           * mountd can be restarted by smf(5).  We need to make sure
 222  222           * the updated door handle will safely make it to mountd_dh.
 223  223           */
 224  224          mutex_init(&nag->mountd_lock, NULL, MUTEX_DEFAULT, NULL);
 225  225          mutex_init(&nag->refreshq_lock, NULL, MUTEX_DEFAULT, NULL);
 226  226          list_create(&nag->refreshq_queue, sizeof (refreshq_exi_node_t),
 227  227              offsetof(refreshq_exi_node_t, ren_node));
 228  228          cv_init(&nag->refreshq_cv, NULL, CV_DEFAULT, NULL);
 229  229          nag->refreshq_thread_state = REFRESHQ_THREAD_NEED_CREATE;
 230  230  
 231  231          ng->nfs_auth = nag;
 232  232  }
 233  233  
 234  234  void
 235  235  nfsauth_zone_shutdown(nfs_globals_t *ng)
 236  236  {
 237  237          refreshq_exi_node_t     *ren;
 238  238          nfsauth_globals_t       *nag = ng->nfs_auth;
 239  239  
 240  240          /* Prevent the nfsauth_refresh_thread from getting new work */
 241  241          mutex_enter(&nag->refreshq_lock);
 242  242          if (nag->refreshq_thread_state == REFRESHQ_THREAD_RUNNING) {
 243  243                  nag->refreshq_thread_state = REFRESHQ_THREAD_FINI_REQ;
 244  244                  cv_broadcast(&nag->refreshq_cv);
 245  245  
 246  246                  /* Wait for nfsauth_refresh_thread() to exit */
 247  247                  while (nag->refreshq_thread_state != REFRESHQ_THREAD_HALTED)
 248  248                          cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
 249  249          }
 250  250          mutex_exit(&nag->refreshq_lock);
 251  251  
 252  252          /*
 253  253           * Walk the exi_list and in turn, walk the auth_lists and free all
 254  254           * lists.  In addition, free INVALID auth_cache entries.
 255  255           */
 256  256          while ((ren = list_remove_head(&nag->refreshq_queue))) {
 257  257                  refreshq_auth_node_t *ran;
 258  258  
 259  259                  while ((ran = list_remove_head(&ren->ren_authlist)) != NULL) {
 260  260                          struct auth_cache *p = ran->ran_auth;
 261  261                          if (p->auth_state == NFS_AUTH_INVALID)
 262  262                                  nfsauth_free_node(p);
 263  263                          strfree(ran->ran_netid);
 264  264                          kmem_free(ran, sizeof (*ran));
 265  265                  }
 266  266  
 267  267                  list_destroy(&ren->ren_authlist);
 268  268                  exi_rele(ren->ren_exi);
 269  269                  kmem_free(ren, sizeof (*ren));
 270  270          }
 271  271  }
 272  272  
 273  273  void
  
    | 
      ↓ open down ↓ | 
    273 lines elided | 
    
      ↑ open up ↑ | 
  
 274  274  nfsauth_zone_fini(nfs_globals_t *ng)
 275  275  {
 276  276          nfsauth_globals_t *nag = ng->nfs_auth;
 277  277  
 278  278          ng->nfs_auth = NULL;
 279  279  
 280  280          list_destroy(&nag->refreshq_queue);
 281  281          cv_destroy(&nag->refreshq_cv);
 282  282          mutex_destroy(&nag->refreshq_lock);
 283  283          mutex_destroy(&nag->mountd_lock);
      284 +        /* Extra cleanup. */
      285 +        if (nag->mountd_dh != NULL)
      286 +                door_ki_rele(nag->mountd_dh);
 284  287          kmem_free(nag, sizeof (*nag));
 285  288  }
 286  289  
 287  290  /*
 288  291   * Convert the address in a netbuf to
 289  292   * a hash index for the auth_cache table.
 290  293   */
 291  294  static int
 292  295  hash(struct netbuf *a)
 293  296  {
 294  297          int i, h = 0;
 295  298  
 296  299          for (i = 0; i < a->len; i++)
 297  300                  h ^= a->buf[i];
 298  301  
 299  302          return (h & (AUTH_TABLESIZE - 1));
 300  303  }
 301  304  
 302  305  /*
 303  306   * Mask out the components of an
 304  307   * address that do not identify
 305  308   * a host. For socket addresses the
 306  309   * masking gets rid of the port number.
 307  310   */
 308  311  static void
 309  312  addrmask(struct netbuf *addr, struct netbuf *mask)
 310  313  {
 311  314          int i;
 312  315  
 313  316          for (i = 0; i < addr->len; i++)
 314  317                  addr->buf[i] &= mask->buf[i];
 315  318  }
 316  319  
 317  320  /*
 318  321   * nfsauth4_access is used for NFS V4 auth checking. Besides doing
 319  322   * the common nfsauth_access(), it will check if the client can
 320  323   * have a limited access to this vnode even if the security flavor
 321  324   * used does not meet the policy.
 322  325   */
 323  326  int
 324  327  nfsauth4_access(struct exportinfo *exi, vnode_t *vp, struct svc_req *req,
 325  328      cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
 326  329  {
 327  330          int access;
 328  331  
 329  332          access = nfsauth_access(exi, req, cr, uid, gid, ngids, gids);
 330  333  
 331  334          /*
 332  335           * There are cases that the server needs to allow the client
 333  336           * to have a limited view.
 334  337           *
 335  338           * e.g.
 336  339           * /export is shared as "sec=sys,rw=dfs-test-4,sec=krb5,rw"
 337  340           * /export/home is shared as "sec=sys,rw"
 338  341           *
 339  342           * When the client mounts /export with sec=sys, the client
 340  343           * would get a limited view with RO access on /export to see
 341  344           * "home" only because the client is allowed to access
 342  345           * /export/home with auth_sys.
 343  346           */
 344  347          if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
 345  348                  /*
 346  349                   * Allow ro permission with LIMITED view if there is a
 347  350                   * sub-dir exported under vp.
 348  351                   */
 349  352                  if (has_visible(exi, vp))
 350  353                          return (NFSAUTH_LIMITED);
 351  354          }
 352  355  
 353  356          return (access);
 354  357  }
 355  358  
 356  359  static void
 357  360  sys_log(const char *msg)
 358  361  {
 359  362          static time_t   tstamp = 0;
 360  363          time_t          now;
 361  364  
 362  365          /*
 363  366           * msg is shown (at most) once per minute
 364  367           */
 365  368          now = gethrestime_sec();
 366  369          if ((tstamp + 60) < now) {
 367  370                  tstamp = now;
 368  371                  cmn_err(CE_WARN, msg);
 369  372          }
 370  373  }
 371  374  
 372  375  /*
 373  376   * Callup to the mountd to get access information in the kernel.
 374  377   */
 375  378  static bool_t
 376  379  nfsauth_retrieve(nfsauth_globals_t *nag, struct exportinfo *exi,
 377  380      char *req_netid, int flavor, struct netbuf *addr, int *access,
 378  381      cred_t *clnt_cred, uid_t *srv_uid, gid_t *srv_gid, uint_t *srv_gids_cnt,
 379  382      gid_t **srv_gids)
 380  383  {
 381  384          varg_t                    varg = {0};
 382  385          nfsauth_res_t             res = {0};
 383  386          XDR                       xdrs;
 384  387          size_t                    absz;
 385  388          caddr_t                   abuf;
 386  389          int                       last = 0;
 387  390          door_arg_t                da;
 388  391          door_info_t               di;
 389  392          door_handle_t             dh;
 390  393          uint_t                    ntries = 0;
 391  394  
 392  395          /*
 393  396           * No entry in the cache for this client/flavor
 394  397           * so we need to call the nfsauth service in the
 395  398           * mount daemon.
 396  399           */
 397  400  
 398  401          varg.vers = V_PROTO;
 399  402          varg.arg_u.arg.cmd = NFSAUTH_ACCESS;
 400  403          varg.arg_u.arg.areq.req_client.n_len = addr->len;
 401  404          varg.arg_u.arg.areq.req_client.n_bytes = addr->buf;
 402  405          varg.arg_u.arg.areq.req_netid = req_netid;
 403  406          varg.arg_u.arg.areq.req_path = exi->exi_export.ex_path;
 404  407          varg.arg_u.arg.areq.req_flavor = flavor;
 405  408          varg.arg_u.arg.areq.req_clnt_uid = crgetuid(clnt_cred);
 406  409          varg.arg_u.arg.areq.req_clnt_gid = crgetgid(clnt_cred);
 407  410          varg.arg_u.arg.areq.req_clnt_gids.len = crgetngroups(clnt_cred);
 408  411          varg.arg_u.arg.areq.req_clnt_gids.val = (gid_t *)crgetgroups(clnt_cred);
 409  412  
 410  413          DTRACE_PROBE1(nfsserv__func__nfsauth__varg, varg_t *, &varg);
 411  414  
 412  415          /*
 413  416           * Setup the XDR stream for encoding the arguments. Notice that
 414  417           * in addition to the args having variable fields (req_netid and
 415  418           * req_path), the argument data structure is itself versioned,
 416  419           * so we need to make sure we can size the arguments buffer
 417  420           * appropriately to encode all the args. If we can't get sizing
 418  421           * info _or_ properly encode the arguments, there's really no
 419  422           * point in continuting, so we fail the request.
 420  423           */
 421  424          if ((absz = xdr_sizeof(xdr_varg, &varg)) == 0) {
 422  425                  *access = NFSAUTH_DENIED;
 423  426                  return (FALSE);
 424  427          }
 425  428  
 426  429          abuf = (caddr_t)kmem_alloc(absz, KM_SLEEP);
 427  430          xdrmem_create(&xdrs, abuf, absz, XDR_ENCODE);
 428  431          if (!xdr_varg(&xdrs, &varg)) {
 429  432                  XDR_DESTROY(&xdrs);
 430  433                  goto fail;
 431  434          }
 432  435          XDR_DESTROY(&xdrs);
 433  436  
 434  437          /*
 435  438           * Prepare the door arguments
 436  439           *
 437  440           * We don't know the size of the message the daemon
 438  441           * will pass back to us.  By setting rbuf to NULL,
 439  442           * we force the door code to allocate a buf of the
 440  443           * appropriate size.  We must set rsize > 0, however,
 441  444           * else the door code acts as if no response was
 442  445           * expected and doesn't pass the data to us.
 443  446           */
 444  447          da.data_ptr = (char *)abuf;
 445  448          da.data_size = absz;
 446  449          da.desc_ptr = NULL;
 447  450          da.desc_num = 0;
 448  451          da.rbuf = NULL;
 449  452          da.rsize = 1;
 450  453  
 451  454  retry:
 452  455          mutex_enter(&nag->mountd_lock);
 453  456          dh = nag->mountd_dh;
 454  457          if (dh != NULL)
 455  458                  door_ki_hold(dh);
 456  459          mutex_exit(&nag->mountd_lock);
 457  460  
 458  461          if (dh == NULL) {
 459  462                  /*
 460  463                   * The rendezvous point has not been established yet!
 461  464                   * This could mean that either mountd(1m) has not yet
 462  465                   * been started or that _this_ routine nuked the door
 463  466                   * handle after receiving an EINTR for a REVOKED door.
 464  467                   *
 465  468                   * Returning NFSAUTH_DROP will cause the NFS client
 466  469                   * to retransmit the request, so let's try to be more
 467  470                   * rescillient and attempt for ntries before we bail.
 468  471                   */
 469  472                  if (++ntries % NFSAUTH_DR_TRYCNT) {
 470  473                          delay(hz);
 471  474                          goto retry;
 472  475                  }
 473  476  
 474  477                  kmem_free(abuf, absz);
 475  478  
 476  479                  sys_log("nfsauth: mountd has not established door");
 477  480                  *access = NFSAUTH_DROP;
 478  481                  return (FALSE);
 479  482          }
 480  483  
 481  484          ntries = 0;
 482  485  
 483  486          /*
 484  487           * Now that we've got what we need, place the call.
 485  488           */
 486  489          switch (door_ki_upcall_limited(dh, &da, NULL, SIZE_MAX, 0)) {
 487  490          case 0:                         /* Success */
 488  491                  door_ki_rele(dh);
 489  492  
 490  493                  if (da.data_ptr == NULL && da.data_size == 0) {
 491  494                          /*
 492  495                           * The door_return that contained the data
 493  496                           * failed! We're here because of the 2nd
 494  497                           * door_return (w/o data) such that we can
 495  498                           * get control of the thread (and exit
 496  499                           * gracefully).
 497  500                           */
 498  501                          DTRACE_PROBE1(nfsserv__func__nfsauth__door__nil,
 499  502                              door_arg_t *, &da);
 500  503                          goto fail;
 501  504                  }
 502  505  
 503  506                  break;
 504  507  
 505  508          case EAGAIN:
 506  509                  /*
 507  510                   * Server out of resources; back off for a bit
 508  511                   */
 509  512                  door_ki_rele(dh);
 510  513                  delay(hz);
 511  514                  goto retry;
 512  515                  /* NOTREACHED */
 513  516  
 514  517          case EINTR:
 515  518                  if (!door_ki_info(dh, &di)) {
 516  519                          door_ki_rele(dh);
 517  520  
 518  521                          if (di.di_attributes & DOOR_REVOKED) {
 519  522                                  /*
 520  523                                   * The server barfed and revoked
 521  524                                   * the (existing) door on us; we
 522  525                                   * want to wait to give smf(5) a
 523  526                                   * chance to restart mountd(1m)
 524  527                                   * and establish a new door handle.
 525  528                                   */
 526  529                                  mutex_enter(&nag->mountd_lock);
 527  530                                  if (dh == nag->mountd_dh) {
 528  531                                          door_ki_rele(nag->mountd_dh);
 529  532                                          nag->mountd_dh = NULL;
 530  533                                  }
 531  534                                  mutex_exit(&nag->mountd_lock);
 532  535                                  delay(hz);
 533  536                                  goto retry;
 534  537                          }
 535  538                          /*
 536  539                           * If the door was _not_ revoked on us,
 537  540                           * then more than likely we took an INTR,
 538  541                           * so we need to fail the operation.
 539  542                           */
 540  543                          goto fail;
 541  544                  }
 542  545                  /*
 543  546                   * The only failure that can occur from getting
 544  547                   * the door info is EINVAL, so we let the code
 545  548                   * below handle it.
 546  549                   */
 547  550                  /* FALLTHROUGH */
 548  551  
 549  552          case EBADF:
 550  553          case EINVAL:
 551  554          default:
 552  555                  /*
 553  556                   * If we have a stale door handle, give smf a last
 554  557                   * chance to start it by sleeping for a little bit.
 555  558                   * If we're still hosed, we'll fail the call.
 556  559                   *
 557  560                   * Since we're going to reacquire the door handle
 558  561                   * upon the retry, we opt to sleep for a bit and
 559  562                   * _not_ to clear mountd_dh. If mountd restarted
 560  563                   * and was able to set mountd_dh, we should see
 561  564                   * the new instance; if not, we won't get caught
 562  565                   * up in the retry/DELAY loop.
 563  566                   */
 564  567                  door_ki_rele(dh);
 565  568                  if (!last) {
 566  569                          delay(hz);
 567  570                          last++;
 568  571                          goto retry;
 569  572                  }
 570  573                  sys_log("nfsauth: stale mountd door handle");
 571  574                  goto fail;
 572  575          }
 573  576  
 574  577          ASSERT(da.rbuf != NULL);
 575  578  
 576  579          /*
 577  580           * No door errors encountered; setup the XDR stream for decoding
 578  581           * the results. If we fail to decode the results, we've got no
 579  582           * other recourse than to fail the request.
 580  583           */
 581  584          xdrmem_create(&xdrs, da.rbuf, da.rsize, XDR_DECODE);
 582  585          if (!xdr_nfsauth_res(&xdrs, &res)) {
 583  586                  xdr_free(xdr_nfsauth_res, (char *)&res);
 584  587                  XDR_DESTROY(&xdrs);
 585  588                  kmem_free(da.rbuf, da.rsize);
 586  589                  goto fail;
 587  590          }
 588  591          XDR_DESTROY(&xdrs);
 589  592          kmem_free(da.rbuf, da.rsize);
 590  593  
 591  594          DTRACE_PROBE1(nfsserv__func__nfsauth__results, nfsauth_res_t *, &res);
 592  595          switch (res.stat) {
 593  596                  case NFSAUTH_DR_OKAY:
 594  597                          *access = res.ares.auth_perm;
 595  598                          *srv_uid = res.ares.auth_srv_uid;
 596  599                          *srv_gid = res.ares.auth_srv_gid;
 597  600                          *srv_gids_cnt = res.ares.auth_srv_gids.len;
 598  601                          *srv_gids = kmem_alloc(*srv_gids_cnt * sizeof (gid_t),
 599  602                              KM_SLEEP);
 600  603                          bcopy(res.ares.auth_srv_gids.val, *srv_gids,
 601  604                              *srv_gids_cnt * sizeof (gid_t));
 602  605                          break;
 603  606  
 604  607                  case NFSAUTH_DR_EFAIL:
 605  608                  case NFSAUTH_DR_DECERR:
 606  609                  case NFSAUTH_DR_BADCMD:
 607  610                  default:
 608  611                          xdr_free(xdr_nfsauth_res, (char *)&res);
 609  612  fail:
 610  613                          *access = NFSAUTH_DENIED;
 611  614                          kmem_free(abuf, absz);
 612  615                          return (FALSE);
 613  616                          /* NOTREACHED */
 614  617          }
 615  618  
 616  619          xdr_free(xdr_nfsauth_res, (char *)&res);
 617  620          kmem_free(abuf, absz);
 618  621  
 619  622          return (TRUE);
 620  623  }
 621  624  
 622  625  static void
 623  626  nfsauth_refresh_thread(nfsauth_globals_t *nag)
 624  627  {
 625  628          refreshq_exi_node_t     *ren;
 626  629          refreshq_auth_node_t    *ran;
 627  630  
 628  631          struct exportinfo       *exi;
 629  632  
 630  633          int                     access;
 631  634          bool_t                  retrieval;
 632  635  
 633  636          callb_cpr_t             cprinfo;
 634  637  
 635  638          CALLB_CPR_INIT(&cprinfo, &nag->refreshq_lock, callb_generic_cpr,
 636  639              "nfsauth_refresh");
 637  640  
 638  641          for (;;) {
 639  642                  mutex_enter(&nag->refreshq_lock);
 640  643                  if (nag->refreshq_thread_state != REFRESHQ_THREAD_RUNNING) {
 641  644                          /* Keep the hold on the lock! */
 642  645                          break;
 643  646                  }
 644  647  
 645  648                  ren = list_remove_head(&nag->refreshq_queue);
 646  649                  if (ren == NULL) {
 647  650                          CALLB_CPR_SAFE_BEGIN(&cprinfo);
 648  651                          cv_wait(&nag->refreshq_cv, &nag->refreshq_lock);
 649  652                          CALLB_CPR_SAFE_END(&cprinfo, &nag->refreshq_lock);
 650  653                          mutex_exit(&nag->refreshq_lock);
 651  654                          continue;
 652  655                  }
 653  656                  mutex_exit(&nag->refreshq_lock);
 654  657  
 655  658                  exi = ren->ren_exi;
 656  659                  ASSERT(exi != NULL);
 657  660  
 658  661                  /*
 659  662                   * Since the ren was removed from the refreshq_queue above,
 660  663                   * this is the only thread aware about the ren existence, so we
 661  664                   * have the exclusive ownership of it and we do not need to
 662  665                   * protect it by any lock.
 663  666                   */
 664  667                  while ((ran = list_remove_head(&ren->ren_authlist))) {
 665  668                          uid_t uid;
 666  669                          gid_t gid;
 667  670                          uint_t ngids;
 668  671                          gid_t *gids;
 669  672                          struct auth_cache *p = ran->ran_auth;
 670  673                          char *netid = ran->ran_netid;
 671  674  
 672  675                          ASSERT(p != NULL);
 673  676                          ASSERT(netid != NULL);
 674  677  
 675  678                          kmem_free(ran, sizeof (refreshq_auth_node_t));
 676  679  
 677  680                          mutex_enter(&p->auth_lock);
 678  681  
 679  682                          /*
 680  683                           * Once the entry goes INVALID, it can not change
 681  684                           * state.
 682  685                           *
 683  686                           * No need to refresh entries also in a case we are
 684  687                           * just shutting down.
 685  688                           *
 686  689                           * In general, there is no need to hold the
 687  690                           * refreshq_lock to test the refreshq_thread_state.  We
 688  691                           * do hold it at other places because there is some
 689  692                           * related thread synchronization (or some other tasks)
 690  693                           * close to the refreshq_thread_state check.
 691  694                           *
 692  695                           * The check for the refreshq_thread_state value here
 693  696                           * is purely advisory to allow the faster
 694  697                           * nfsauth_refresh_thread() shutdown.  In a case we
 695  698                           * will miss such advisory, nothing catastrophic
 696  699                           * happens: we will just spin longer here before the
 697  700                           * shutdown.
 698  701                           */
 699  702                          if (p->auth_state == NFS_AUTH_INVALID ||
 700  703                              nag->refreshq_thread_state !=
 701  704                              REFRESHQ_THREAD_RUNNING) {
 702  705                                  mutex_exit(&p->auth_lock);
 703  706  
 704  707                                  if (p->auth_state == NFS_AUTH_INVALID)
 705  708                                          nfsauth_free_node(p);
 706  709  
 707  710                                  strfree(netid);
 708  711  
 709  712                                  continue;
 710  713                          }
 711  714  
 712  715                          /*
 713  716                           * Make sure the state is valid.  Note that once we
 714  717                           * change the state to NFS_AUTH_REFRESHING, no other
 715  718                           * thread will be able to work on this entry.
 716  719                           */
 717  720                          ASSERT(p->auth_state == NFS_AUTH_STALE);
 718  721  
 719  722                          p->auth_state = NFS_AUTH_REFRESHING;
 720  723                          mutex_exit(&p->auth_lock);
 721  724  
 722  725                          DTRACE_PROBE2(nfsauth__debug__cache__refresh,
 723  726                              struct exportinfo *, exi,
 724  727                              struct auth_cache *, p);
 725  728  
 726  729                          /*
 727  730                           * The first caching of the access rights
 728  731                           * is done with the netid pulled out of the
 729  732                           * request from the client. All subsequent
 730  733                           * users of the cache may or may not have
 731  734                           * the same netid. It doesn't matter. So
 732  735                           * when we refresh, we simply use the netid
 733  736                           * of the request which triggered the
 734  737                           * refresh attempt.
 735  738                           */
 736  739                          retrieval = nfsauth_retrieve(nag, exi, netid,
 737  740                              p->auth_flavor, &p->auth_clnt->authc_addr, &access,
 738  741                              p->auth_clnt_cred, &uid, &gid, &ngids, &gids);
 739  742  
 740  743                          /*
 741  744                           * This can only be set in one other place
 742  745                           * and the state has to be NFS_AUTH_FRESH.
 743  746                           */
 744  747                          strfree(netid);
 745  748  
 746  749                          mutex_enter(&p->auth_lock);
 747  750                          if (p->auth_state == NFS_AUTH_INVALID) {
 748  751                                  mutex_exit(&p->auth_lock);
 749  752                                  nfsauth_free_node(p);
 750  753                                  if (retrieval == TRUE)
 751  754                                          kmem_free(gids, ngids * sizeof (gid_t));
 752  755                          } else {
 753  756                                  /*
 754  757                                   * If we got an error, do not reset the
 755  758                                   * time. This will cause the next access
 756  759                                   * check for the client to reschedule this
 757  760                                   * node.
 758  761                                   */
 759  762                                  if (retrieval == TRUE) {
 760  763                                          p->auth_access = access;
 761  764  
 762  765                                          p->auth_srv_uid = uid;
 763  766                                          p->auth_srv_gid = gid;
 764  767                                          kmem_free(p->auth_srv_gids,
 765  768                                              p->auth_srv_ngids * sizeof (gid_t));
 766  769                                          p->auth_srv_ngids = ngids;
 767  770                                          p->auth_srv_gids = gids;
 768  771  
 769  772                                          p->auth_freshness = gethrestime_sec();
 770  773                                  }
 771  774                                  p->auth_state = NFS_AUTH_FRESH;
 772  775  
 773  776                                  cv_broadcast(&p->auth_cv);
 774  777                                  mutex_exit(&p->auth_lock);
 775  778                          }
 776  779                  }
 777  780  
 778  781                  list_destroy(&ren->ren_authlist);
 779  782                  exi_rele(ren->ren_exi);
 780  783                  kmem_free(ren, sizeof (refreshq_exi_node_t));
 781  784          }
 782  785  
 783  786          nag->refreshq_thread_state = REFRESHQ_THREAD_HALTED;
 784  787          cv_broadcast(&nag->refreshq_cv);
 785  788          CALLB_CPR_EXIT(&cprinfo);
 786  789          DTRACE_PROBE(nfsauth__nfsauth__refresh__thread__exit);
 787  790          zthread_exit();
 788  791  }
 789  792  
 790  793  int
 791  794  nfsauth_cache_clnt_compar(const void *v1, const void *v2)
 792  795  {
 793  796          int c;
 794  797  
 795  798          const struct auth_cache_clnt *a1 = (const struct auth_cache_clnt *)v1;
 796  799          const struct auth_cache_clnt *a2 = (const struct auth_cache_clnt *)v2;
 797  800  
 798  801          if (a1->authc_addr.len < a2->authc_addr.len)
 799  802                  return (-1);
 800  803          if (a1->authc_addr.len > a2->authc_addr.len)
 801  804                  return (1);
 802  805  
 803  806          c = memcmp(a1->authc_addr.buf, a2->authc_addr.buf, a1->authc_addr.len);
 804  807          if (c < 0)
 805  808                  return (-1);
 806  809          if (c > 0)
 807  810                  return (1);
 808  811  
 809  812          return (0);
 810  813  }
 811  814  
 812  815  static int
 813  816  nfsauth_cache_compar(const void *v1, const void *v2)
 814  817  {
 815  818          int c;
 816  819  
 817  820          const struct auth_cache *a1 = (const struct auth_cache *)v1;
 818  821          const struct auth_cache *a2 = (const struct auth_cache *)v2;
 819  822  
 820  823          if (a1->auth_flavor < a2->auth_flavor)
 821  824                  return (-1);
 822  825          if (a1->auth_flavor > a2->auth_flavor)
 823  826                  return (1);
 824  827  
 825  828          if (crgetuid(a1->auth_clnt_cred) < crgetuid(a2->auth_clnt_cred))
 826  829                  return (-1);
 827  830          if (crgetuid(a1->auth_clnt_cred) > crgetuid(a2->auth_clnt_cred))
 828  831                  return (1);
 829  832  
 830  833          if (crgetgid(a1->auth_clnt_cred) < crgetgid(a2->auth_clnt_cred))
 831  834                  return (-1);
 832  835          if (crgetgid(a1->auth_clnt_cred) > crgetgid(a2->auth_clnt_cred))
 833  836                  return (1);
 834  837  
 835  838          if (crgetngroups(a1->auth_clnt_cred) < crgetngroups(a2->auth_clnt_cred))
 836  839                  return (-1);
 837  840          if (crgetngroups(a1->auth_clnt_cred) > crgetngroups(a2->auth_clnt_cred))
 838  841                  return (1);
 839  842  
 840  843          c = memcmp(crgetgroups(a1->auth_clnt_cred),
 841  844              crgetgroups(a2->auth_clnt_cred), crgetngroups(a1->auth_clnt_cred));
 842  845          if (c < 0)
 843  846                  return (-1);
 844  847          if (c > 0)
 845  848                  return (1);
 846  849  
 847  850          return (0);
 848  851  }
 849  852  
 850  853  /*
 851  854   * Get the access information from the cache or callup to the mountd
 852  855   * to get and cache the access information in the kernel.
 853  856   */
 854  857  static int
 855  858  nfsauth_cache_get(struct exportinfo *exi, struct svc_req *req, int flavor,
 856  859      cred_t *cr, uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
 857  860  {
 858  861          nfsauth_globals_t       *nag;
 859  862          struct netbuf           *taddrmask;
 860  863          struct netbuf           addr;   /* temporary copy of client's address */
 861  864          const struct netbuf     *claddr;
 862  865          avl_tree_t              *tree;
 863  866          struct auth_cache       ac;     /* used as a template for avl_find() */
 864  867          struct auth_cache_clnt  *c;
 865  868          struct auth_cache_clnt  acc;    /* used as a template for avl_find() */
 866  869          struct auth_cache       *p = NULL;
 867  870          int                     access;
  
    | 
      ↓ open down ↓ | 
    574 lines elided | 
    
      ↑ open up ↑ | 
  
 868  871  
 869  872          uid_t                   tmpuid;
 870  873          gid_t                   tmpgid;
 871  874          uint_t                  tmpngids;
 872  875          gid_t                   *tmpgids;
 873  876  
 874  877          avl_index_t             where;  /* used for avl_find()/avl_insert() */
 875  878  
 876  879          ASSERT(cr != NULL);
 877  880  
      881 +        ASSERT3P(curzone->zone_id, ==, exi->exi_zoneid);
 878  882          nag = nfsauth_get_zg();
 879  883  
 880  884          /*
 881  885           * Now check whether this client already
 882  886           * has an entry for this flavor in the cache
 883  887           * for this export.
 884  888           * Get the caller's address, mask off the
 885  889           * parts of the address that do not identify
 886  890           * the host (port number, etc), and then hash
 887  891           * it to find the chain of cache entries.
 888  892           */
 889  893  
 890  894          claddr = svc_getrpccaller(req->rq_xprt);
 891  895          addr = *claddr;
 892  896          addr.buf = kmem_alloc(addr.maxlen, KM_SLEEP);
 893  897          bcopy(claddr->buf, addr.buf, claddr->len);
 894  898  
 895  899          SVC_GETADDRMASK(req->rq_xprt, SVC_TATTR_ADDRMASK, (void **)&taddrmask);
 896  900          ASSERT(taddrmask != NULL);
 897  901          addrmask(&addr, taddrmask);
 898  902  
 899  903          ac.auth_flavor = flavor;
 900  904          ac.auth_clnt_cred = crdup(cr);
 901  905  
 902  906          acc.authc_addr = addr;
 903  907  
 904  908          tree = exi->exi_cache[hash(&addr)];
 905  909  
 906  910          rw_enter(&exi->exi_cache_lock, RW_READER);
 907  911          c = (struct auth_cache_clnt *)avl_find(tree, &acc, NULL);
 908  912  
 909  913          if (c == NULL) {
 910  914                  struct auth_cache_clnt *nc;
 911  915  
 912  916                  rw_exit(&exi->exi_cache_lock);
 913  917  
 914  918                  nc = kmem_alloc(sizeof (*nc), KM_NOSLEEP | KM_NORMALPRI);
 915  919                  if (nc == NULL)
 916  920                          goto retrieve;
 917  921  
 918  922                  /*
 919  923                   * Initialize the new auth_cache_clnt
 920  924                   */
 921  925                  nc->authc_addr = addr;
 922  926                  nc->authc_addr.buf = kmem_alloc(addr.maxlen,
 923  927                      KM_NOSLEEP | KM_NORMALPRI);
 924  928                  if (addr.maxlen != 0 && nc->authc_addr.buf == NULL) {
 925  929                          kmem_free(nc, sizeof (*nc));
 926  930                          goto retrieve;
 927  931                  }
 928  932                  bcopy(addr.buf, nc->authc_addr.buf, addr.len);
 929  933                  rw_init(&nc->authc_lock, NULL, RW_DEFAULT, NULL);
 930  934                  avl_create(&nc->authc_tree, nfsauth_cache_compar,
 931  935                      sizeof (struct auth_cache),
 932  936                      offsetof(struct auth_cache, auth_link));
 933  937  
 934  938                  rw_enter(&exi->exi_cache_lock, RW_WRITER);
 935  939                  c = (struct auth_cache_clnt *)avl_find(tree, &acc, &where);
 936  940                  if (c == NULL) {
 937  941                          avl_insert(tree, nc, where);
 938  942                          rw_downgrade(&exi->exi_cache_lock);
 939  943                          c = nc;
 940  944                  } else {
 941  945                          rw_downgrade(&exi->exi_cache_lock);
 942  946  
 943  947                          avl_destroy(&nc->authc_tree);
 944  948                          rw_destroy(&nc->authc_lock);
 945  949                          kmem_free(nc->authc_addr.buf, nc->authc_addr.maxlen);
 946  950                          kmem_free(nc, sizeof (*nc));
 947  951                  }
 948  952          }
 949  953  
 950  954          ASSERT(c != NULL);
 951  955  
 952  956          rw_enter(&c->authc_lock, RW_READER);
 953  957          p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, NULL);
 954  958  
 955  959          if (p == NULL) {
 956  960                  struct auth_cache *np;
 957  961  
 958  962                  rw_exit(&c->authc_lock);
 959  963  
 960  964                  np = kmem_cache_alloc(exi_cache_handle,
 961  965                      KM_NOSLEEP | KM_NORMALPRI);
 962  966                  if (np == NULL) {
 963  967                          rw_exit(&exi->exi_cache_lock);
 964  968                          goto retrieve;
 965  969                  }
 966  970  
 967  971                  /*
 968  972                   * Initialize the new auth_cache
 969  973                   */
 970  974                  np->auth_clnt = c;
 971  975                  np->auth_flavor = flavor;
 972  976                  np->auth_clnt_cred = ac.auth_clnt_cred;
 973  977                  np->auth_srv_ngids = 0;
 974  978                  np->auth_srv_gids = NULL;
 975  979                  np->auth_time = np->auth_freshness = gethrestime_sec();
 976  980                  np->auth_state = NFS_AUTH_NEW;
 977  981                  mutex_init(&np->auth_lock, NULL, MUTEX_DEFAULT, NULL);
 978  982                  cv_init(&np->auth_cv, NULL, CV_DEFAULT, NULL);
 979  983  
 980  984                  rw_enter(&c->authc_lock, RW_WRITER);
 981  985                  rw_exit(&exi->exi_cache_lock);
 982  986  
 983  987                  p = (struct auth_cache *)avl_find(&c->authc_tree, &ac, &where);
 984  988                  if (p == NULL) {
 985  989                          avl_insert(&c->authc_tree, np, where);
 986  990                          rw_downgrade(&c->authc_lock);
 987  991                          p = np;
 988  992                  } else {
 989  993                          rw_downgrade(&c->authc_lock);
 990  994  
 991  995                          cv_destroy(&np->auth_cv);
 992  996                          mutex_destroy(&np->auth_lock);
 993  997                          crfree(ac.auth_clnt_cred);
 994  998                          kmem_cache_free(exi_cache_handle, np);
 995  999                  }
 996 1000          } else {
 997 1001                  rw_exit(&exi->exi_cache_lock);
 998 1002                  crfree(ac.auth_clnt_cred);
 999 1003          }
1000 1004  
1001 1005          mutex_enter(&p->auth_lock);
1002 1006          rw_exit(&c->authc_lock);
1003 1007  
1004 1008          /*
1005 1009           * If the entry is in the WAITING state then some other thread is just
1006 1010           * retrieving the required info.  The entry was either NEW, or the list
1007 1011           * of client's supplemental groups is going to be changed (either by
1008 1012           * this thread, or by some other thread).  We need to wait until the
1009 1013           * nfsauth_retrieve() is done.
1010 1014           */
1011 1015          while (p->auth_state == NFS_AUTH_WAITING)
1012 1016                  cv_wait(&p->auth_cv, &p->auth_lock);
1013 1017  
1014 1018          /*
1015 1019           * Here the entry cannot be in WAITING or INVALID state.
1016 1020           */
1017 1021          ASSERT(p->auth_state != NFS_AUTH_WAITING);
1018 1022          ASSERT(p->auth_state != NFS_AUTH_INVALID);
1019 1023  
1020 1024          /*
1021 1025           * If the cache entry is not valid yet, we need to retrieve the
1022 1026           * info ourselves.
1023 1027           */
1024 1028          if (p->auth_state == NFS_AUTH_NEW) {
1025 1029                  bool_t res;
1026 1030                  /*
1027 1031                   * NFS_AUTH_NEW is the default output auth_state value in a
1028 1032                   * case we failed somewhere below.
1029 1033                   */
1030 1034                  auth_state_t state = NFS_AUTH_NEW;
1031 1035  
1032 1036                  p->auth_state = NFS_AUTH_WAITING;
1033 1037                  mutex_exit(&p->auth_lock);
1034 1038                  kmem_free(addr.buf, addr.maxlen);
1035 1039                  addr = p->auth_clnt->authc_addr;
1036 1040  
1037 1041                  atomic_inc_uint(&nfsauth_cache_miss);
1038 1042  
1039 1043                  res = nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt),
1040 1044                      flavor, &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids,
1041 1045                      &tmpgids);
1042 1046  
1043 1047                  p->auth_access = access;
1044 1048                  p->auth_time = p->auth_freshness = gethrestime_sec();
1045 1049  
1046 1050                  if (res == TRUE) {
1047 1051                          if (uid != NULL)
1048 1052                                  *uid = tmpuid;
1049 1053                          if (gid != NULL)
1050 1054                                  *gid = tmpgid;
1051 1055                          if (ngids != NULL && gids != NULL) {
1052 1056                                  *ngids = tmpngids;
1053 1057                                  *gids = tmpgids;
1054 1058  
1055 1059                                  /*
1056 1060                                   * We need a copy of gids for the
1057 1061                                   * auth_cache entry
1058 1062                                   */
1059 1063                                  tmpgids = kmem_alloc(tmpngids * sizeof (gid_t),
1060 1064                                      KM_NOSLEEP | KM_NORMALPRI);
1061 1065                                  if (tmpgids != NULL)
1062 1066                                          bcopy(*gids, tmpgids,
1063 1067                                              tmpngids * sizeof (gid_t));
1064 1068                          }
1065 1069  
1066 1070                          if (tmpgids != NULL || tmpngids == 0) {
1067 1071                                  p->auth_srv_uid = tmpuid;
1068 1072                                  p->auth_srv_gid = tmpgid;
1069 1073                                  p->auth_srv_ngids = tmpngids;
1070 1074                                  p->auth_srv_gids = tmpgids;
1071 1075  
1072 1076                                  state = NFS_AUTH_FRESH;
1073 1077                          }
1074 1078                  }
1075 1079  
1076 1080                  /*
1077 1081                   * Set the auth_state and notify waiters.
1078 1082                   */
1079 1083                  mutex_enter(&p->auth_lock);
1080 1084                  p->auth_state = state;
1081 1085                  cv_broadcast(&p->auth_cv);
1082 1086                  mutex_exit(&p->auth_lock);
1083 1087          } else {
1084 1088                  uint_t nach;
1085 1089                  time_t refresh;
1086 1090  
1087 1091                  refresh = gethrestime_sec() - p->auth_freshness;
1088 1092  
1089 1093                  p->auth_time = gethrestime_sec();
1090 1094  
1091 1095                  if (uid != NULL)
1092 1096                          *uid = p->auth_srv_uid;
1093 1097                  if (gid != NULL)
1094 1098                          *gid = p->auth_srv_gid;
1095 1099                  if (ngids != NULL && gids != NULL) {
1096 1100                          *ngids = p->auth_srv_ngids;
1097 1101                          *gids = kmem_alloc(*ngids * sizeof (gid_t), KM_SLEEP);
1098 1102                          bcopy(p->auth_srv_gids, *gids, *ngids * sizeof (gid_t));
1099 1103                  }
1100 1104  
1101 1105                  access = p->auth_access;
1102 1106  
1103 1107                  if ((refresh > NFSAUTH_CACHE_REFRESH) &&
1104 1108                      p->auth_state == NFS_AUTH_FRESH) {
1105 1109                          refreshq_auth_node_t *ran;
1106 1110                          uint_t nacr;
1107 1111  
1108 1112                          p->auth_state = NFS_AUTH_STALE;
1109 1113                          mutex_exit(&p->auth_lock);
1110 1114  
1111 1115                          nacr = atomic_inc_uint_nv(&nfsauth_cache_refresh);
1112 1116                          DTRACE_PROBE3(nfsauth__debug__cache__stale,
1113 1117                              struct exportinfo *, exi,
1114 1118                              struct auth_cache *, p,
1115 1119                              uint_t, nacr);
1116 1120  
1117 1121                          ran = kmem_alloc(sizeof (refreshq_auth_node_t),
1118 1122                              KM_SLEEP);
1119 1123                          ran->ran_auth = p;
1120 1124                          ran->ran_netid = strdup(svc_getnetid(req->rq_xprt));
1121 1125  
1122 1126                          mutex_enter(&nag->refreshq_lock);
1123 1127  
1124 1128                          if (nag->refreshq_thread_state ==
1125 1129                              REFRESHQ_THREAD_NEED_CREATE) {
1126 1130                                  /* Launch nfsauth refresh thread */
1127 1131                                  nag->refreshq_thread_state =
1128 1132                                      REFRESHQ_THREAD_RUNNING;
1129 1133                                  (void) zthread_create(NULL, 0,
1130 1134                                      nfsauth_refresh_thread, nag, 0,
1131 1135                                      minclsyspri);
1132 1136                          }
1133 1137  
1134 1138                          /*
1135 1139                           * We should not add a work queue item if the thread
1136 1140                           * is not accepting them.
1137 1141                           */
1138 1142                          if (nag->refreshq_thread_state ==
1139 1143                              REFRESHQ_THREAD_RUNNING) {
1140 1144                                  refreshq_exi_node_t *ren;
1141 1145  
1142 1146                                  /*
1143 1147                                   * Is there an existing exi_list?
1144 1148                                   */
1145 1149                                  for (ren = list_head(&nag->refreshq_queue);
1146 1150                                      ren != NULL;
1147 1151                                      ren = list_next(&nag->refreshq_queue,
1148 1152                                      ren)) {
1149 1153                                          if (ren->ren_exi == exi) {
1150 1154                                                  list_insert_tail(
1151 1155                                                      &ren->ren_authlist, ran);
1152 1156                                                  break;
1153 1157                                          }
1154 1158                                  }
1155 1159  
1156 1160                                  if (ren == NULL) {
1157 1161                                          ren = kmem_alloc(
1158 1162                                              sizeof (refreshq_exi_node_t),
1159 1163                                              KM_SLEEP);
1160 1164  
1161 1165                                          exi_hold(exi);
1162 1166                                          ren->ren_exi = exi;
1163 1167  
1164 1168                                          list_create(&ren->ren_authlist,
1165 1169                                              sizeof (refreshq_auth_node_t),
1166 1170                                              offsetof(refreshq_auth_node_t,
1167 1171                                              ran_node));
1168 1172  
1169 1173                                          list_insert_tail(&ren->ren_authlist,
1170 1174                                              ran);
1171 1175                                          list_insert_tail(&nag->refreshq_queue,
1172 1176                                              ren);
1173 1177                                  }
1174 1178  
1175 1179                                  cv_broadcast(&nag->refreshq_cv);
1176 1180                          } else {
1177 1181                                  strfree(ran->ran_netid);
1178 1182                                  kmem_free(ran, sizeof (refreshq_auth_node_t));
1179 1183                          }
1180 1184  
1181 1185                          mutex_exit(&nag->refreshq_lock);
1182 1186                  } else {
1183 1187                          mutex_exit(&p->auth_lock);
1184 1188                  }
1185 1189  
1186 1190                  nach = atomic_inc_uint_nv(&nfsauth_cache_hit);
1187 1191                  DTRACE_PROBE2(nfsauth__debug__cache__hit,
1188 1192                      uint_t, nach,
1189 1193                      time_t, refresh);
1190 1194  
1191 1195                  kmem_free(addr.buf, addr.maxlen);
1192 1196          }
1193 1197  
1194 1198          return (access);
1195 1199  
1196 1200  retrieve:
1197 1201          crfree(ac.auth_clnt_cred);
1198 1202  
1199 1203          /*
1200 1204           * Retrieve the required data without caching.
1201 1205           */
1202 1206  
1203 1207          ASSERT(p == NULL);
1204 1208  
1205 1209          atomic_inc_uint(&nfsauth_cache_miss);
1206 1210  
1207 1211          if (nfsauth_retrieve(nag, exi, svc_getnetid(req->rq_xprt), flavor,
1208 1212              &addr, &access, cr, &tmpuid, &tmpgid, &tmpngids, &tmpgids)) {
1209 1213                  if (uid != NULL)
1210 1214                          *uid = tmpuid;
1211 1215                  if (gid != NULL)
1212 1216                          *gid = tmpgid;
1213 1217                  if (ngids != NULL && gids != NULL) {
1214 1218                          *ngids = tmpngids;
1215 1219                          *gids = tmpgids;
1216 1220                  } else {
1217 1221                          kmem_free(tmpgids, tmpngids * sizeof (gid_t));
1218 1222                  }
1219 1223          }
1220 1224  
1221 1225          kmem_free(addr.buf, addr.maxlen);
1222 1226  
1223 1227          return (access);
1224 1228  }
1225 1229  
1226 1230  /*
1227 1231   * Check if the requesting client has access to the filesystem with
1228 1232   * a given nfs flavor number which is an explicitly shared flavor.
1229 1233   */
1230 1234  int
1231 1235  nfsauth4_secinfo_access(struct exportinfo *exi, struct svc_req *req,
1232 1236      int flavor, int perm, cred_t *cr)
1233 1237  {
1234 1238          int access;
1235 1239  
1236 1240          if (! (perm & M_4SEC_EXPORTED)) {
1237 1241                  return (NFSAUTH_DENIED);
1238 1242          }
1239 1243  
1240 1244          /*
1241 1245           * Optimize if there are no lists
1242 1246           */
1243 1247          if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0) {
1244 1248                  perm &= ~M_4SEC_EXPORTED;
1245 1249                  if (perm == M_RO)
1246 1250                          return (NFSAUTH_RO);
1247 1251                  if (perm == M_RW)
1248 1252                          return (NFSAUTH_RW);
1249 1253          }
1250 1254  
1251 1255          access = nfsauth_cache_get(exi, req, flavor, cr, NULL, NULL, NULL,
1252 1256              NULL);
1253 1257  
1254 1258          return (access);
1255 1259  }
1256 1260  
1257 1261  int
1258 1262  nfsauth_access(struct exportinfo *exi, struct svc_req *req, cred_t *cr,
1259 1263      uid_t *uid, gid_t *gid, uint_t *ngids, gid_t **gids)
1260 1264  {
1261 1265          int access, mapaccess;
1262 1266          struct secinfo *sp;
1263 1267          int i, flavor, perm;
1264 1268          int authnone_entry = -1;
1265 1269  
1266 1270          /*
1267 1271           * By default root is mapped to anonymous user.
1268 1272           * This might get overriden later in nfsauth_cache_get().
1269 1273           */
1270 1274          if (crgetuid(cr) == 0) {
1271 1275                  if (uid != NULL)
1272 1276                          *uid = exi->exi_export.ex_anon;
1273 1277                  if (gid != NULL)
1274 1278                          *gid = exi->exi_export.ex_anon;
1275 1279          } else {
1276 1280                  if (uid != NULL)
1277 1281                          *uid = crgetuid(cr);
1278 1282                  if (gid != NULL)
1279 1283                          *gid = crgetgid(cr);
1280 1284          }
1281 1285  
1282 1286          if (ngids != NULL)
1283 1287                  *ngids = 0;
1284 1288          if (gids != NULL)
1285 1289                  *gids = NULL;
1286 1290  
1287 1291          /*
1288 1292           *  Get the nfs flavor number from xprt.
1289 1293           */
1290 1294          flavor = (int)(uintptr_t)req->rq_xprt->xp_cookie;
1291 1295  
1292 1296          /*
1293 1297           * First check the access restrictions on the filesystem.  If
1294 1298           * there are no lists associated with this flavor then there's no
1295 1299           * need to make an expensive call to the nfsauth service or to
1296 1300           * cache anything.
1297 1301           */
1298 1302  
1299 1303          sp = exi->exi_export.ex_secinfo;
1300 1304          for (i = 0; i < exi->exi_export.ex_seccnt; i++) {
1301 1305                  if (flavor != sp[i].s_secinfo.sc_nfsnum) {
1302 1306                          if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE)
1303 1307                                  authnone_entry = i;
1304 1308                          continue;
1305 1309                  }
1306 1310                  break;
1307 1311          }
1308 1312  
1309 1313          mapaccess = 0;
1310 1314  
1311 1315          if (i >= exi->exi_export.ex_seccnt) {
1312 1316                  /*
1313 1317                   * Flavor not found, but use AUTH_NONE if it exists
1314 1318                   */
1315 1319                  if (authnone_entry == -1)
1316 1320                          return (NFSAUTH_DENIED);
1317 1321                  flavor = AUTH_NONE;
1318 1322                  mapaccess = NFSAUTH_MAPNONE;
1319 1323                  i = authnone_entry;
1320 1324          }
1321 1325  
1322 1326          /*
1323 1327           * If the flavor is in the ex_secinfo list, but not an explicitly
1324 1328           * shared flavor by the user, it is a result of the nfsv4 server
1325 1329           * namespace setup. We will grant an RO permission similar for
1326 1330           * a pseudo node except that this node is a shared one.
1327 1331           *
1328 1332           * e.g. flavor in (flavor) indicates that it is not explictly
1329 1333           *      shared by the user:
1330 1334           *
1331 1335           *              /       (sys, krb5)
1332 1336           *              |
1333 1337           *              export  #share -o sec=sys (krb5)
1334 1338           *              |
1335 1339           *              secure  #share -o sec=krb5
1336 1340           *
1337 1341           *      In this case, when a krb5 request coming in to access
1338 1342           *      /export, RO permission is granted.
1339 1343           */
1340 1344          if (!(sp[i].s_flags & M_4SEC_EXPORTED))
1341 1345                  return (mapaccess | NFSAUTH_RO);
1342 1346  
1343 1347          /*
1344 1348           * Optimize if there are no lists.
1345 1349           * We cannot optimize for AUTH_SYS with NGRPS (16) supplemental groups.
1346 1350           */
1347 1351          perm = sp[i].s_flags;
1348 1352          if ((perm & (M_ROOT | M_NONE | M_MAP)) == 0 && (ngroups_max <= NGRPS ||
1349 1353              flavor != AUTH_SYS || crgetngroups(cr) < NGRPS)) {
1350 1354                  perm &= ~M_4SEC_EXPORTED;
1351 1355                  if (perm == M_RO)
1352 1356                          return (mapaccess | NFSAUTH_RO);
1353 1357                  if (perm == M_RW)
1354 1358                          return (mapaccess | NFSAUTH_RW);
1355 1359          }
1356 1360  
1357 1361          access = nfsauth_cache_get(exi, req, flavor, cr, uid, gid, ngids, gids);
1358 1362  
1359 1363          /*
1360 1364           * For both NFSAUTH_DENIED and NFSAUTH_WRONGSEC we do not care about
1361 1365           * the supplemental groups.
1362 1366           */
1363 1367          if (access & NFSAUTH_DENIED || access & NFSAUTH_WRONGSEC) {
1364 1368                  if (ngids != NULL && gids != NULL) {
1365 1369                          kmem_free(*gids, *ngids * sizeof (gid_t));
1366 1370                          *ngids = 0;
1367 1371                          *gids = NULL;
1368 1372                  }
1369 1373          }
1370 1374  
1371 1375          /*
1372 1376           * Client's security flavor doesn't match with "ro" or
1373 1377           * "rw" list. Try again using AUTH_NONE if present.
1374 1378           */
1375 1379          if ((access & NFSAUTH_WRONGSEC) && (flavor != AUTH_NONE)) {
1376 1380                  /*
1377 1381                   * Have we already encountered AUTH_NONE ?
1378 1382                   */
1379 1383                  if (authnone_entry != -1) {
1380 1384                          mapaccess = NFSAUTH_MAPNONE;
1381 1385                          access = nfsauth_cache_get(exi, req, AUTH_NONE, cr,
1382 1386                              NULL, NULL, NULL, NULL);
1383 1387                  } else {
1384 1388                          /*
1385 1389                           * Check for AUTH_NONE presence.
1386 1390                           */
1387 1391                          for (; i < exi->exi_export.ex_seccnt; i++) {
1388 1392                                  if (sp[i].s_secinfo.sc_nfsnum == AUTH_NONE) {
1389 1393                                          mapaccess = NFSAUTH_MAPNONE;
1390 1394                                          access = nfsauth_cache_get(exi, req,
1391 1395                                              AUTH_NONE, cr, NULL, NULL, NULL,
1392 1396                                              NULL);
1393 1397                                          break;
1394 1398                                  }
1395 1399                          }
1396 1400                  }
1397 1401          }
1398 1402  
1399 1403          if (access & NFSAUTH_DENIED)
1400 1404                  access = NFSAUTH_DENIED;
1401 1405  
1402 1406          return (access | mapaccess);
1403 1407  }
1404 1408  
1405 1409  static void
1406 1410  nfsauth_free_clnt_node(struct auth_cache_clnt *p)
1407 1411  {
1408 1412          void *cookie = NULL;
1409 1413          struct auth_cache *node;
1410 1414  
1411 1415          while ((node = avl_destroy_nodes(&p->authc_tree, &cookie)) != NULL)
1412 1416                  nfsauth_free_node(node);
1413 1417          avl_destroy(&p->authc_tree);
1414 1418  
1415 1419          kmem_free(p->authc_addr.buf, p->authc_addr.maxlen);
1416 1420          rw_destroy(&p->authc_lock);
1417 1421  
1418 1422          kmem_free(p, sizeof (*p));
1419 1423  }
1420 1424  
1421 1425  static void
1422 1426  nfsauth_free_node(struct auth_cache *p)
1423 1427  {
1424 1428          crfree(p->auth_clnt_cred);
1425 1429          kmem_free(p->auth_srv_gids, p->auth_srv_ngids * sizeof (gid_t));
1426 1430          mutex_destroy(&p->auth_lock);
1427 1431          cv_destroy(&p->auth_cv);
1428 1432          kmem_cache_free(exi_cache_handle, p);
1429 1433  }
1430 1434  
1431 1435  /*
1432 1436   * Free the nfsauth cache for a given export
1433 1437   */
1434 1438  void
1435 1439  nfsauth_cache_free(struct exportinfo *exi)
1436 1440  {
1437 1441          int i;
1438 1442  
1439 1443          /*
1440 1444           * The only way we got here was with an exi_rele, which means that no
1441 1445           * auth cache entry is being refreshed.
1442 1446           */
1443 1447  
1444 1448          for (i = 0; i < AUTH_TABLESIZE; i++) {
1445 1449                  avl_tree_t *tree = exi->exi_cache[i];
1446 1450                  void *cookie = NULL;
1447 1451                  struct auth_cache_clnt *node;
1448 1452  
1449 1453                  while ((node = avl_destroy_nodes(tree, &cookie)) != NULL)
1450 1454                          nfsauth_free_clnt_node(node);
1451 1455          }
1452 1456  }
1453 1457  
1454 1458  /*
1455 1459   * Called by the kernel memory allocator when memory is low.
1456 1460   * Free unused cache entries. If that's not enough, the VM system
1457 1461   * will call again for some more.
1458 1462   *
1459 1463   * This needs to operate on all zones, so we take a reader lock
1460 1464   * on the list of zones and walk the list.  This is OK here
1461 1465   * becuase exi_cache_trim doesn't block or cause new objects
1462 1466   * to be allocated (basically just frees lots of stuff).
1463 1467   * Use care if nfssrv_globals_rwl is taken as reader in any
1464 1468   * other cases because it will block nfs_server_zone_init
1465 1469   * and nfs_server_zone_fini, which enter as writer.
1466 1470   */
1467 1471  /*ARGSUSED*/
1468 1472  void
1469 1473  exi_cache_reclaim(void *cdrarg)
1470 1474  {
1471 1475          nfs_globals_t *ng;
1472 1476  
1473 1477          rw_enter(&nfssrv_globals_rwl, RW_READER);
1474 1478  
1475 1479          ng = list_head(&nfssrv_globals_list);
1476 1480          while (ng != NULL) {
1477 1481                  exi_cache_reclaim_zone(ng);
1478 1482                  ng = list_next(&nfssrv_globals_list, ng);
1479 1483          }
1480 1484  
1481 1485          rw_exit(&nfssrv_globals_rwl);
1482 1486  }
1483 1487  
1484 1488  static void
1485 1489  exi_cache_reclaim_zone(nfs_globals_t *ng)
1486 1490  {
1487 1491          int i;
1488 1492          struct exportinfo *exi;
1489 1493          nfs_export_t *ne = ng->nfs_export;
1490 1494  
1491 1495          rw_enter(&ne->exported_lock, RW_READER);
1492 1496  
1493 1497          for (i = 0; i < EXPTABLESIZE; i++) {
1494 1498                  for (exi = ne->exptable[i]; exi; exi = exi->fid_hash.next)
1495 1499                          exi_cache_trim(exi);
1496 1500          }
1497 1501  
1498 1502          rw_exit(&ne->exported_lock);
1499 1503  
1500 1504          atomic_inc_uint(&nfsauth_cache_reclaim);
1501 1505  }
1502 1506  
1503 1507  static void
1504 1508  exi_cache_trim(struct exportinfo *exi)
1505 1509  {
1506 1510          struct auth_cache_clnt *c;
1507 1511          struct auth_cache_clnt *nextc;
1508 1512          struct auth_cache *p;
1509 1513          struct auth_cache *next;
1510 1514          int i;
1511 1515          time_t stale_time;
1512 1516          avl_tree_t *tree;
1513 1517  
1514 1518          for (i = 0; i < AUTH_TABLESIZE; i++) {
1515 1519                  tree = exi->exi_cache[i];
1516 1520                  stale_time = gethrestime_sec() - NFSAUTH_CACHE_TRIM;
1517 1521                  rw_enter(&exi->exi_cache_lock, RW_READER);
1518 1522  
1519 1523                  /*
1520 1524                   * Free entries that have not been
1521 1525                   * used for NFSAUTH_CACHE_TRIM seconds.
1522 1526                   */
1523 1527                  for (c = avl_first(tree); c != NULL; c = AVL_NEXT(tree, c)) {
1524 1528                          /*
1525 1529                           * We are being called by the kmem subsystem to reclaim
1526 1530                           * memory so don't block if we can't get the lock.
1527 1531                           */
1528 1532                          if (rw_tryenter(&c->authc_lock, RW_WRITER) == 0) {
1529 1533                                  exi_cache_auth_reclaim_failed++;
1530 1534                                  rw_exit(&exi->exi_cache_lock);
1531 1535                                  return;
1532 1536                          }
1533 1537  
1534 1538                          for (p = avl_first(&c->authc_tree); p != NULL;
1535 1539                              p = next) {
1536 1540                                  next = AVL_NEXT(&c->authc_tree, p);
1537 1541  
1538 1542                                  ASSERT(p->auth_state != NFS_AUTH_INVALID);
1539 1543  
1540 1544                                  mutex_enter(&p->auth_lock);
1541 1545  
1542 1546                                  /*
1543 1547                                   * We won't trim recently used and/or WAITING
1544 1548                                   * entries.
1545 1549                                   */
1546 1550                                  if (p->auth_time > stale_time ||
1547 1551                                      p->auth_state == NFS_AUTH_WAITING) {
1548 1552                                          mutex_exit(&p->auth_lock);
1549 1553                                          continue;
1550 1554                                  }
1551 1555  
1552 1556                                  DTRACE_PROBE1(nfsauth__debug__trim__state,
1553 1557                                      auth_state_t, p->auth_state);
1554 1558  
1555 1559                                  /*
1556 1560                                   * STALE and REFRESHING entries needs to be
1557 1561                                   * marked INVALID only because they are
1558 1562                                   * referenced by some other structures or
1559 1563                                   * threads.  They will be freed later.
1560 1564                                   */
1561 1565                                  if (p->auth_state == NFS_AUTH_STALE ||
1562 1566                                      p->auth_state == NFS_AUTH_REFRESHING) {
1563 1567                                          p->auth_state = NFS_AUTH_INVALID;
1564 1568                                          mutex_exit(&p->auth_lock);
1565 1569  
1566 1570                                          avl_remove(&c->authc_tree, p);
1567 1571                                  } else {
1568 1572                                          mutex_exit(&p->auth_lock);
1569 1573  
1570 1574                                          avl_remove(&c->authc_tree, p);
1571 1575                                          nfsauth_free_node(p);
1572 1576                                  }
1573 1577                          }
1574 1578                          rw_exit(&c->authc_lock);
1575 1579                  }
1576 1580  
1577 1581                  if (rw_tryupgrade(&exi->exi_cache_lock) == 0) {
1578 1582                          rw_exit(&exi->exi_cache_lock);
1579 1583                          exi_cache_clnt_reclaim_failed++;
1580 1584                          continue;
1581 1585                  }
1582 1586  
1583 1587                  for (c = avl_first(tree); c != NULL; c = nextc) {
1584 1588                          nextc = AVL_NEXT(tree, c);
1585 1589  
1586 1590                          if (avl_is_empty(&c->authc_tree) == B_FALSE)
1587 1591                                  continue;
1588 1592  
1589 1593                          avl_remove(tree, c);
1590 1594  
1591 1595                          nfsauth_free_clnt_node(c);
1592 1596                  }
1593 1597  
1594 1598                  rw_exit(&exi->exi_cache_lock);
1595 1599          }
1596 1600  }
  
    | 
      ↓ open down ↓ | 
    709 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX