Print this page
    
NEX-19350 User ACE incorrectly grants access to matching group IDs
Reviewed by: Rick McNeal <rick.mcneal@nexenta.com>
Reviewed by: Joyce McIntosh <joyce.mcintosh@nexenta.com>
Reviewed by: Evan Layton <evan.layton@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs (part 2)
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs (part 2)
Reviewed by: Evan Layton <evan.layton@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
NEX-15035 Allow user ACE in ACL to match SID in token extra SIDs
Reviewed by: Roman Strashkin <roman.strashkin@nexenta.com>
Reviewed by: Sanjay Nadkarni <sanjay.nadkarni@nexenta.com>
    
      
        | Split | 
	Close | 
      
      | Expand all | 
      | Collapse all | 
    
    
          --- old/usr/src/uts/common/fs/zfs/zfs_fuid.c
          +++ new/usr/src/uts/common/fs/zfs/zfs_fuid.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   *
  
    | 
      ↓ open down ↓ | 
    12 lines elided | 
    
      ↑ open up ↑ | 
  
  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   * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
       23 + * Copyright 2018 Nexenta Systems, Inc.  All rights reserved.
  23   24   */
  24   25  
  25   26  #include <sys/zfs_context.h>
  26   27  #include <sys/dmu.h>
  27   28  #include <sys/avl.h>
  28   29  #include <sys/zap.h>
  29   30  #include <sys/refcount.h>
  30   31  #include <sys/nvpair.h>
  31   32  #ifdef _KERNEL
  32   33  #include <sys/kidmap.h>
  33   34  #include <sys/sid.h>
  34   35  #include <sys/zfs_vfsops.h>
  35   36  #include <sys/zfs_znode.h>
  36   37  #endif
  37   38  #include <sys/zfs_fuid.h>
  38   39  
  39   40  /*
  40   41   * FUID Domain table(s).
  41   42   *
  42   43   * The FUID table is stored as a packed nvlist of an array
  43   44   * of nvlists which contain an index, domain string and offset
  44   45   *
  45   46   * During file system initialization the nvlist(s) are read and
  46   47   * two AVL trees are created.  One tree is keyed by the index number
  47   48   * and the other by the domain string.  Nodes are never removed from
  48   49   * trees, but new entries may be added.  If a new entry is added then
  49   50   * the zfsvfs->z_fuid_dirty flag is set to true and the caller will then
  50   51   * be responsible for calling zfs_fuid_sync() to sync the changes to disk.
  51   52   *
  52   53   */
  53   54  
  54   55  #define FUID_IDX        "fuid_idx"
  55   56  #define FUID_DOMAIN     "fuid_domain"
  56   57  #define FUID_OFFSET     "fuid_offset"
  57   58  #define FUID_NVP_ARRAY  "fuid_nvlist"
  58   59  
  59   60  typedef struct fuid_domain {
  60   61          avl_node_t      f_domnode;
  61   62          avl_node_t      f_idxnode;
  62   63          ksiddomain_t    *f_ksid;
  63   64          uint64_t        f_idx;
  64   65  } fuid_domain_t;
  65   66  
  66   67  static char *nulldomain = "";
  67   68  
  68   69  /*
  69   70   * Compare two indexes.
  70   71   */
  71   72  static int
  72   73  idx_compare(const void *arg1, const void *arg2)
  73   74  {
  74   75          const fuid_domain_t *node1 = arg1;
  75   76          const fuid_domain_t *node2 = arg2;
  76   77  
  77   78          if (node1->f_idx < node2->f_idx)
  78   79                  return (-1);
  79   80          else if (node1->f_idx > node2->f_idx)
  80   81                  return (1);
  81   82          return (0);
  82   83  }
  83   84  
  84   85  /*
  85   86   * Compare two domain strings.
  86   87   */
  87   88  static int
  88   89  domain_compare(const void *arg1, const void *arg2)
  89   90  {
  90   91          const fuid_domain_t *node1 = arg1;
  91   92          const fuid_domain_t *node2 = arg2;
  92   93          int val;
  93   94  
  94   95          val = strcmp(node1->f_ksid->kd_name, node2->f_ksid->kd_name);
  95   96          if (val == 0)
  96   97                  return (0);
  97   98          return (val > 0 ? 1 : -1);
  98   99  }
  99  100  
 100  101  void
 101  102  zfs_fuid_avl_tree_create(avl_tree_t *idx_tree, avl_tree_t *domain_tree)
 102  103  {
 103  104          avl_create(idx_tree, idx_compare,
 104  105              sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_idxnode));
 105  106          avl_create(domain_tree, domain_compare,
 106  107              sizeof (fuid_domain_t), offsetof(fuid_domain_t, f_domnode));
 107  108  }
 108  109  
 109  110  /*
 110  111   * load initial fuid domain and idx trees.  This function is used by
 111  112   * both the kernel and zdb.
 112  113   */
 113  114  uint64_t
 114  115  zfs_fuid_table_load(objset_t *os, uint64_t fuid_obj, avl_tree_t *idx_tree,
 115  116      avl_tree_t *domain_tree)
 116  117  {
 117  118          dmu_buf_t *db;
 118  119          uint64_t fuid_size;
 119  120  
 120  121          ASSERT(fuid_obj != 0);
 121  122          VERIFY(0 == dmu_bonus_hold(os, fuid_obj,
 122  123              FTAG, &db));
 123  124          fuid_size = *(uint64_t *)db->db_data;
 124  125          dmu_buf_rele(db, FTAG);
 125  126  
 126  127          if (fuid_size)  {
 127  128                  nvlist_t **fuidnvp;
 128  129                  nvlist_t *nvp = NULL;
 129  130                  uint_t count;
 130  131                  char *packed;
 131  132                  int i;
 132  133  
 133  134                  packed = kmem_alloc(fuid_size, KM_SLEEP);
 134  135                  VERIFY(dmu_read(os, fuid_obj, 0,
 135  136                      fuid_size, packed, DMU_READ_PREFETCH) == 0);
 136  137                  VERIFY(nvlist_unpack(packed, fuid_size,
 137  138                      &nvp, 0) == 0);
 138  139                  VERIFY(nvlist_lookup_nvlist_array(nvp, FUID_NVP_ARRAY,
 139  140                      &fuidnvp, &count) == 0);
 140  141  
 141  142                  for (i = 0; i != count; i++) {
 142  143                          fuid_domain_t *domnode;
 143  144                          char *domain;
 144  145                          uint64_t idx;
 145  146  
 146  147                          VERIFY(nvlist_lookup_string(fuidnvp[i], FUID_DOMAIN,
 147  148                              &domain) == 0);
 148  149                          VERIFY(nvlist_lookup_uint64(fuidnvp[i], FUID_IDX,
 149  150                              &idx) == 0);
 150  151  
 151  152                          domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP);
 152  153  
 153  154                          domnode->f_idx = idx;
 154  155                          domnode->f_ksid = ksid_lookupdomain(domain);
 155  156                          avl_add(idx_tree, domnode);
 156  157                          avl_add(domain_tree, domnode);
 157  158                  }
 158  159                  nvlist_free(nvp);
 159  160                  kmem_free(packed, fuid_size);
 160  161          }
 161  162          return (fuid_size);
 162  163  }
 163  164  
 164  165  void
 165  166  zfs_fuid_table_destroy(avl_tree_t *idx_tree, avl_tree_t *domain_tree)
 166  167  {
 167  168          fuid_domain_t *domnode;
 168  169          void *cookie;
 169  170  
 170  171          cookie = NULL;
 171  172          while (domnode = avl_destroy_nodes(domain_tree, &cookie))
 172  173                  ksiddomain_rele(domnode->f_ksid);
 173  174  
 174  175          avl_destroy(domain_tree);
 175  176          cookie = NULL;
 176  177          while (domnode = avl_destroy_nodes(idx_tree, &cookie))
 177  178                  kmem_free(domnode, sizeof (fuid_domain_t));
 178  179          avl_destroy(idx_tree);
 179  180  }
 180  181  
 181  182  char *
 182  183  zfs_fuid_idx_domain(avl_tree_t *idx_tree, uint32_t idx)
 183  184  {
 184  185          fuid_domain_t searchnode, *findnode;
 185  186          avl_index_t loc;
 186  187  
 187  188          searchnode.f_idx = idx;
 188  189  
 189  190          findnode = avl_find(idx_tree, &searchnode, &loc);
 190  191  
 191  192          return (findnode ? findnode->f_ksid->kd_name : nulldomain);
 192  193  }
 193  194  
 194  195  #ifdef _KERNEL
 195  196  /*
 196  197   * Load the fuid table(s) into memory.
 197  198   */
 198  199  static void
 199  200  zfs_fuid_init(zfsvfs_t *zfsvfs)
 200  201  {
 201  202          rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER);
 202  203  
 203  204          if (zfsvfs->z_fuid_loaded) {
 204  205                  rw_exit(&zfsvfs->z_fuid_lock);
 205  206                  return;
 206  207          }
 207  208  
 208  209          zfs_fuid_avl_tree_create(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain);
 209  210  
 210  211          (void) zap_lookup(zfsvfs->z_os, MASTER_NODE_OBJ,
 211  212              ZFS_FUID_TABLES, 8, 1, &zfsvfs->z_fuid_obj);
 212  213          if (zfsvfs->z_fuid_obj != 0) {
 213  214                  zfsvfs->z_fuid_size = zfs_fuid_table_load(zfsvfs->z_os,
 214  215                      zfsvfs->z_fuid_obj, &zfsvfs->z_fuid_idx,
 215  216                      &zfsvfs->z_fuid_domain);
 216  217          }
 217  218  
 218  219          zfsvfs->z_fuid_loaded = B_TRUE;
 219  220          rw_exit(&zfsvfs->z_fuid_lock);
 220  221  }
 221  222  
 222  223  /*
 223  224   * sync out AVL trees to persistent storage.
 224  225   */
 225  226  void
 226  227  zfs_fuid_sync(zfsvfs_t *zfsvfs, dmu_tx_t *tx)
 227  228  {
 228  229          nvlist_t *nvp;
 229  230          nvlist_t **fuids;
 230  231          size_t nvsize = 0;
 231  232          char *packed;
 232  233          dmu_buf_t *db;
 233  234          fuid_domain_t *domnode;
 234  235          int numnodes;
 235  236          int i;
 236  237  
 237  238          if (!zfsvfs->z_fuid_dirty) {
 238  239                  return;
 239  240          }
 240  241  
 241  242          rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER);
 242  243  
 243  244          /*
 244  245           * First see if table needs to be created?
 245  246           */
 246  247          if (zfsvfs->z_fuid_obj == 0) {
 247  248                  zfsvfs->z_fuid_obj = dmu_object_alloc(zfsvfs->z_os,
 248  249                      DMU_OT_FUID, 1 << 14, DMU_OT_FUID_SIZE,
 249  250                      sizeof (uint64_t), tx);
 250  251                  VERIFY(zap_add(zfsvfs->z_os, MASTER_NODE_OBJ,
 251  252                      ZFS_FUID_TABLES, sizeof (uint64_t), 1,
 252  253                      &zfsvfs->z_fuid_obj, tx) == 0);
 253  254          }
 254  255  
 255  256          VERIFY(nvlist_alloc(&nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 256  257  
 257  258          numnodes = avl_numnodes(&zfsvfs->z_fuid_idx);
 258  259          fuids = kmem_alloc(numnodes * sizeof (void *), KM_SLEEP);
 259  260          for (i = 0, domnode = avl_first(&zfsvfs->z_fuid_domain); domnode; i++,
 260  261              domnode = AVL_NEXT(&zfsvfs->z_fuid_domain, domnode)) {
 261  262                  VERIFY(nvlist_alloc(&fuids[i], NV_UNIQUE_NAME, KM_SLEEP) == 0);
 262  263                  VERIFY(nvlist_add_uint64(fuids[i], FUID_IDX,
 263  264                      domnode->f_idx) == 0);
 264  265                  VERIFY(nvlist_add_uint64(fuids[i], FUID_OFFSET, 0) == 0);
 265  266                  VERIFY(nvlist_add_string(fuids[i], FUID_DOMAIN,
 266  267                      domnode->f_ksid->kd_name) == 0);
 267  268          }
 268  269          VERIFY(nvlist_add_nvlist_array(nvp, FUID_NVP_ARRAY,
 269  270              fuids, numnodes) == 0);
 270  271          for (i = 0; i != numnodes; i++)
 271  272                  nvlist_free(fuids[i]);
 272  273          kmem_free(fuids, numnodes * sizeof (void *));
 273  274          VERIFY(nvlist_size(nvp, &nvsize, NV_ENCODE_XDR) == 0);
 274  275          packed = kmem_alloc(nvsize, KM_SLEEP);
 275  276          VERIFY(nvlist_pack(nvp, &packed, &nvsize,
 276  277              NV_ENCODE_XDR, KM_SLEEP) == 0);
 277  278          nvlist_free(nvp);
 278  279          zfsvfs->z_fuid_size = nvsize;
 279  280          dmu_write(zfsvfs->z_os, zfsvfs->z_fuid_obj, 0,
 280  281              zfsvfs->z_fuid_size, packed, tx);
 281  282          kmem_free(packed, zfsvfs->z_fuid_size);
 282  283          VERIFY(0 == dmu_bonus_hold(zfsvfs->z_os, zfsvfs->z_fuid_obj,
 283  284              FTAG, &db));
 284  285          dmu_buf_will_dirty(db, tx);
 285  286          *(uint64_t *)db->db_data = zfsvfs->z_fuid_size;
 286  287          dmu_buf_rele(db, FTAG);
 287  288  
 288  289          zfsvfs->z_fuid_dirty = B_FALSE;
 289  290          rw_exit(&zfsvfs->z_fuid_lock);
 290  291  }
 291  292  
 292  293  /*
 293  294   * Query domain table for a given domain.
 294  295   *
 295  296   * If domain isn't found and addok is set, it is added to AVL trees and
 296  297   * the zfsvfs->z_fuid_dirty flag will be set to TRUE.  It will then be
 297  298   * necessary for the caller or another thread to detect the dirty table
 298  299   * and sync out the changes.
 299  300   */
 300  301  int
 301  302  zfs_fuid_find_by_domain(zfsvfs_t *zfsvfs, const char *domain,
 302  303      char **retdomain, boolean_t addok)
 303  304  {
 304  305          fuid_domain_t searchnode, *findnode;
 305  306          avl_index_t loc;
 306  307          krw_t rw = RW_READER;
 307  308  
 308  309          /*
 309  310           * If the dummy "nobody" domain then return an index of 0
 310  311           * to cause the created FUID to be a standard POSIX id
 311  312           * for the user nobody.
 312  313           */
 313  314          if (domain[0] == '\0') {
 314  315                  if (retdomain)
 315  316                          *retdomain = nulldomain;
 316  317                  return (0);
 317  318          }
 318  319  
 319  320          searchnode.f_ksid = ksid_lookupdomain(domain);
 320  321          if (retdomain)
 321  322                  *retdomain = searchnode.f_ksid->kd_name;
 322  323          if (!zfsvfs->z_fuid_loaded)
 323  324                  zfs_fuid_init(zfsvfs);
 324  325  
 325  326  retry:
 326  327          rw_enter(&zfsvfs->z_fuid_lock, rw);
 327  328          findnode = avl_find(&zfsvfs->z_fuid_domain, &searchnode, &loc);
 328  329  
 329  330          if (findnode) {
 330  331                  rw_exit(&zfsvfs->z_fuid_lock);
 331  332                  ksiddomain_rele(searchnode.f_ksid);
 332  333                  return (findnode->f_idx);
 333  334          } else if (addok) {
 334  335                  fuid_domain_t *domnode;
 335  336                  uint64_t retidx;
 336  337  
 337  338                  if (rw == RW_READER && !rw_tryupgrade(&zfsvfs->z_fuid_lock)) {
 338  339                          rw_exit(&zfsvfs->z_fuid_lock);
 339  340                          rw = RW_WRITER;
 340  341                          goto retry;
 341  342                  }
 342  343  
 343  344                  domnode = kmem_alloc(sizeof (fuid_domain_t), KM_SLEEP);
 344  345                  domnode->f_ksid = searchnode.f_ksid;
 345  346  
 346  347                  retidx = domnode->f_idx = avl_numnodes(&zfsvfs->z_fuid_idx) + 1;
 347  348  
 348  349                  avl_add(&zfsvfs->z_fuid_domain, domnode);
 349  350                  avl_add(&zfsvfs->z_fuid_idx, domnode);
 350  351                  zfsvfs->z_fuid_dirty = B_TRUE;
 351  352                  rw_exit(&zfsvfs->z_fuid_lock);
 352  353                  return (retidx);
 353  354          } else {
 354  355                  rw_exit(&zfsvfs->z_fuid_lock);
 355  356                  return (-1);
 356  357          }
 357  358  }
 358  359  
 359  360  /*
 360  361   * Query domain table by index, returning domain string
 361  362   *
 362  363   * Returns a pointer from an avl node of the domain string.
 363  364   *
 364  365   */
 365  366  const char *
 366  367  zfs_fuid_find_by_idx(zfsvfs_t *zfsvfs, uint32_t idx)
 367  368  {
 368  369          char *domain;
 369  370  
 370  371          if (idx == 0 || !zfsvfs->z_use_fuids)
 371  372                  return (NULL);
 372  373  
 373  374          if (!zfsvfs->z_fuid_loaded)
 374  375                  zfs_fuid_init(zfsvfs);
 375  376  
 376  377          rw_enter(&zfsvfs->z_fuid_lock, RW_READER);
 377  378  
 378  379          if (zfsvfs->z_fuid_obj || zfsvfs->z_fuid_dirty)
 379  380                  domain = zfs_fuid_idx_domain(&zfsvfs->z_fuid_idx, idx);
 380  381          else
 381  382                  domain = nulldomain;
 382  383          rw_exit(&zfsvfs->z_fuid_lock);
 383  384  
 384  385          ASSERT(domain);
 385  386          return (domain);
 386  387  }
 387  388  
 388  389  void
 389  390  zfs_fuid_map_ids(znode_t *zp, cred_t *cr, uid_t *uidp, uid_t *gidp)
 390  391  {
 391  392          *uidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_uid, cr, ZFS_OWNER);
 392  393          *gidp = zfs_fuid_map_id(zp->z_zfsvfs, zp->z_gid, cr, ZFS_GROUP);
 393  394  }
 394  395  
 395  396  uid_t
 396  397  zfs_fuid_map_id(zfsvfs_t *zfsvfs, uint64_t fuid,
 397  398      cred_t *cr, zfs_fuid_type_t type)
 398  399  {
 399  400          uint32_t index = FUID_INDEX(fuid);
 400  401          const char *domain;
 401  402          uid_t id;
 402  403  
 403  404          if (index == 0)
 404  405                  return (fuid);
 405  406  
 406  407          domain = zfs_fuid_find_by_idx(zfsvfs, index);
 407  408          ASSERT(domain != NULL);
 408  409  
 409  410          if (type == ZFS_OWNER || type == ZFS_ACE_USER) {
 410  411                  (void) kidmap_getuidbysid(crgetzone(cr), domain,
 411  412                      FUID_RID(fuid), &id);
 412  413          } else {
 413  414                  (void) kidmap_getgidbysid(crgetzone(cr), domain,
 414  415                      FUID_RID(fuid), &id);
 415  416          }
 416  417          return (id);
 417  418  }
 418  419  
 419  420  /*
 420  421   * Add a FUID node to the list of fuid's being created for this
 421  422   * ACL
 422  423   *
 423  424   * If ACL has multiple domains, then keep only one copy of each unique
 424  425   * domain.
 425  426   */
 426  427  void
 427  428  zfs_fuid_node_add(zfs_fuid_info_t **fuidpp, const char *domain, uint32_t rid,
 428  429      uint64_t idx, uint64_t id, zfs_fuid_type_t type)
 429  430  {
 430  431          zfs_fuid_t *fuid;
 431  432          zfs_fuid_domain_t *fuid_domain;
 432  433          zfs_fuid_info_t *fuidp;
 433  434          uint64_t fuididx;
 434  435          boolean_t found = B_FALSE;
 435  436  
 436  437          if (*fuidpp == NULL)
 437  438                  *fuidpp = zfs_fuid_info_alloc();
 438  439  
 439  440          fuidp = *fuidpp;
 440  441          /*
 441  442           * First find fuid domain index in linked list
 442  443           *
 443  444           * If one isn't found then create an entry.
 444  445           */
 445  446  
 446  447          for (fuididx = 1, fuid_domain = list_head(&fuidp->z_domains);
 447  448              fuid_domain; fuid_domain = list_next(&fuidp->z_domains,
 448  449              fuid_domain), fuididx++) {
 449  450                  if (idx == fuid_domain->z_domidx) {
 450  451                          found = B_TRUE;
 451  452                          break;
 452  453                  }
 453  454          }
 454  455  
 455  456          if (!found) {
 456  457                  fuid_domain = kmem_alloc(sizeof (zfs_fuid_domain_t), KM_SLEEP);
 457  458                  fuid_domain->z_domain = domain;
 458  459                  fuid_domain->z_domidx = idx;
 459  460                  list_insert_tail(&fuidp->z_domains, fuid_domain);
 460  461                  fuidp->z_domain_str_sz += strlen(domain) + 1;
 461  462                  fuidp->z_domain_cnt++;
 462  463          }
 463  464  
 464  465          if (type == ZFS_ACE_USER || type == ZFS_ACE_GROUP) {
 465  466  
 466  467                  /*
 467  468                   * Now allocate fuid entry and add it on the end of the list
 468  469                   */
 469  470  
 470  471                  fuid = kmem_alloc(sizeof (zfs_fuid_t), KM_SLEEP);
 471  472                  fuid->z_id = id;
 472  473                  fuid->z_domidx = idx;
 473  474                  fuid->z_logfuid = FUID_ENCODE(fuididx, rid);
 474  475  
 475  476                  list_insert_tail(&fuidp->z_fuids, fuid);
 476  477                  fuidp->z_fuid_cnt++;
 477  478          } else {
 478  479                  if (type == ZFS_OWNER)
 479  480                          fuidp->z_fuid_owner = FUID_ENCODE(fuididx, rid);
 480  481                  else
 481  482                          fuidp->z_fuid_group = FUID_ENCODE(fuididx, rid);
 482  483          }
 483  484  }
 484  485  
 485  486  /*
 486  487   * Create a file system FUID, based on information in the users cred
 487  488   *
 488  489   * If cred contains KSID_OWNER then it should be used to determine
 489  490   * the uid otherwise cred's uid will be used. By default cred's gid
  
    | 
      ↓ open down ↓ | 
    457 lines elided | 
    
      ↑ open up ↑ | 
  
 490  491   * is used unless it's an ephemeral ID in which case KSID_GROUP will
 491  492   * be used if it exists.
 492  493   */
 493  494  uint64_t
 494  495  zfs_fuid_create_cred(zfsvfs_t *zfsvfs, zfs_fuid_type_t type,
 495  496      cred_t *cr, zfs_fuid_info_t **fuidp)
 496  497  {
 497  498          uint64_t        idx;
 498  499          ksid_t          *ksid;
 499  500          uint32_t        rid;
 500      -        char            *kdomain;
      501 +        char            *kdomain;
 501  502          const char      *domain;
 502  503          uid_t           id;
 503  504  
 504  505          VERIFY(type == ZFS_OWNER || type == ZFS_GROUP);
 505  506  
 506  507          ksid = crgetsid(cr, (type == ZFS_OWNER) ? KSID_OWNER : KSID_GROUP);
 507  508  
 508  509          if (!zfsvfs->z_use_fuids || (ksid == NULL)) {
 509  510                  id = (type == ZFS_OWNER) ? crgetuid(cr) : crgetgid(cr);
 510  511  
 511  512                  if (IS_EPHEMERAL(id))
 512  513                          return ((type == ZFS_OWNER) ? UID_NOBODY : GID_NOBODY);
 513  514  
 514  515                  return ((uint64_t)id);
 515  516          }
 516  517  
 517  518          /*
 518  519           * ksid is present and FUID is supported
 519  520           */
 520  521          id = (type == ZFS_OWNER) ? ksid_getid(ksid) : crgetgid(cr);
 521  522  
 522  523          if (!IS_EPHEMERAL(id))
 523  524                  return ((uint64_t)id);
 524  525  
 525  526          if (type == ZFS_GROUP)
 526  527                  id = ksid_getid(ksid);
 527  528  
 528  529          rid = ksid_getrid(ksid);
 529  530          domain = ksid_getdomain(ksid);
 530  531  
 531  532          idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE);
 532  533  
 533  534          zfs_fuid_node_add(fuidp, kdomain, rid, idx, id, type);
 534  535  
 535  536          return (FUID_ENCODE(idx, rid));
 536  537  }
 537  538  
 538  539  /*
 539  540   * Create a file system FUID for an ACL ace
 540  541   * or a chown/chgrp of the file.
 541  542   * This is similar to zfs_fuid_create_cred, except that
 542  543   * we can't find the domain + rid information in the
 543  544   * cred.  Instead we have to query Winchester for the
 544  545   * domain and rid.
 545  546   *
 546  547   * During replay operations the domain+rid information is
 547  548   * found in the zfs_fuid_info_t that the replay code has
 548  549   * attached to the zfsvfs of the file system.
 549  550   */
 550  551  uint64_t
 551  552  zfs_fuid_create(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr,
 552  553      zfs_fuid_type_t type, zfs_fuid_info_t **fuidpp)
 553  554  {
 554  555          const char *domain;
 555  556          char *kdomain;
 556  557          uint32_t fuid_idx = FUID_INDEX(id);
 557  558          uint32_t rid;
 558  559          idmap_stat status;
 559  560          uint64_t idx = 0;
 560  561          zfs_fuid_t *zfuid = NULL;
 561  562          zfs_fuid_info_t *fuidp = NULL;
 562  563  
 563  564          /*
 564  565           * If POSIX ID, or entry is already a FUID then
 565  566           * just return the id
 566  567           *
 567  568           * We may also be handed an already FUID'ized id via
 568  569           * chmod.
 569  570           */
 570  571  
 571  572          if (!zfsvfs->z_use_fuids || !IS_EPHEMERAL(id) || fuid_idx != 0)
 572  573                  return (id);
 573  574  
 574  575          if (zfsvfs->z_replay) {
 575  576                  fuidp = zfsvfs->z_fuid_replay;
 576  577  
 577  578                  /*
 578  579                   * If we are passed an ephemeral id, but no
 579  580                   * fuid_info was logged then return NOBODY.
 580  581                   * This is most likely a result of idmap service
 581  582                   * not being available.
 582  583                   */
 583  584                  if (fuidp == NULL)
 584  585                          return (UID_NOBODY);
 585  586  
 586  587                  VERIFY3U(type, >=, ZFS_OWNER);
 587  588                  VERIFY3U(type, <=, ZFS_ACE_GROUP);
 588  589  
 589  590                  switch (type) {
 590  591                  case ZFS_ACE_USER:
 591  592                  case ZFS_ACE_GROUP:
 592  593                          zfuid = list_head(&fuidp->z_fuids);
 593  594                          rid = FUID_RID(zfuid->z_logfuid);
 594  595                          idx = FUID_INDEX(zfuid->z_logfuid);
 595  596                          break;
 596  597                  case ZFS_OWNER:
 597  598                          rid = FUID_RID(fuidp->z_fuid_owner);
 598  599                          idx = FUID_INDEX(fuidp->z_fuid_owner);
 599  600                          break;
 600  601                  case ZFS_GROUP:
 601  602                          rid = FUID_RID(fuidp->z_fuid_group);
 602  603                          idx = FUID_INDEX(fuidp->z_fuid_group);
 603  604                          break;
 604  605                  };
 605  606                  domain = fuidp->z_domain_table[idx - 1];
 606  607          } else {
 607  608                  if (type == ZFS_OWNER || type == ZFS_ACE_USER)
 608  609                          status = kidmap_getsidbyuid(crgetzone(cr), id,
 609  610                              &domain, &rid);
 610  611                  else
 611  612                          status = kidmap_getsidbygid(crgetzone(cr), id,
 612  613                              &domain, &rid);
 613  614  
 614  615                  if (status != 0) {
 615  616                          /*
 616  617                           * When returning nobody we will need to
 617  618                           * make a dummy fuid table entry for logging
 618  619                           * purposes.
 619  620                           */
 620  621                          rid = UID_NOBODY;
 621  622                          domain = nulldomain;
 622  623                  }
 623  624          }
 624  625  
 625  626          idx = zfs_fuid_find_by_domain(zfsvfs, domain, &kdomain, B_TRUE);
 626  627  
 627  628          if (!zfsvfs->z_replay)
 628  629                  zfs_fuid_node_add(fuidpp, kdomain,
 629  630                      rid, idx, id, type);
 630  631          else if (zfuid != NULL) {
 631  632                  list_remove(&fuidp->z_fuids, zfuid);
 632  633                  kmem_free(zfuid, sizeof (zfs_fuid_t));
 633  634          }
 634  635          return (FUID_ENCODE(idx, rid));
 635  636  }
 636  637  
 637  638  void
 638  639  zfs_fuid_destroy(zfsvfs_t *zfsvfs)
 639  640  {
 640  641          rw_enter(&zfsvfs->z_fuid_lock, RW_WRITER);
 641  642          if (!zfsvfs->z_fuid_loaded) {
 642  643                  rw_exit(&zfsvfs->z_fuid_lock);
 643  644                  return;
 644  645          }
 645  646          zfs_fuid_table_destroy(&zfsvfs->z_fuid_idx, &zfsvfs->z_fuid_domain);
 646  647          rw_exit(&zfsvfs->z_fuid_lock);
 647  648  }
 648  649  
 649  650  /*
 650  651   * Allocate zfs_fuid_info for tracking FUIDs created during
 651  652   * zfs_mknode, VOP_SETATTR() or VOP_SETSECATTR()
 652  653   */
 653  654  zfs_fuid_info_t *
 654  655  zfs_fuid_info_alloc(void)
 655  656  {
 656  657          zfs_fuid_info_t *fuidp;
 657  658  
 658  659          fuidp = kmem_zalloc(sizeof (zfs_fuid_info_t), KM_SLEEP);
 659  660          list_create(&fuidp->z_domains, sizeof (zfs_fuid_domain_t),
 660  661              offsetof(zfs_fuid_domain_t, z_next));
 661  662          list_create(&fuidp->z_fuids, sizeof (zfs_fuid_t),
 662  663              offsetof(zfs_fuid_t, z_next));
 663  664          return (fuidp);
 664  665  }
 665  666  
 666  667  /*
 667  668   * Release all memory associated with zfs_fuid_info_t
 668  669   */
 669  670  void
 670  671  zfs_fuid_info_free(zfs_fuid_info_t *fuidp)
 671  672  {
 672  673          zfs_fuid_t *zfuid;
 673  674          zfs_fuid_domain_t *zdomain;
 674  675  
 675  676          while ((zfuid = list_head(&fuidp->z_fuids)) != NULL) {
 676  677                  list_remove(&fuidp->z_fuids, zfuid);
 677  678                  kmem_free(zfuid, sizeof (zfs_fuid_t));
 678  679          }
 679  680  
 680  681          if (fuidp->z_domain_table != NULL)
 681  682                  kmem_free(fuidp->z_domain_table,
 682  683                      (sizeof (char **)) * fuidp->z_domain_cnt);
  
    | 
      ↓ open down ↓ | 
    172 lines elided | 
    
      ↑ open up ↑ | 
  
 683  684  
 684  685          while ((zdomain = list_head(&fuidp->z_domains)) != NULL) {
 685  686                  list_remove(&fuidp->z_domains, zdomain);
 686  687                  kmem_free(zdomain, sizeof (zfs_fuid_domain_t));
 687  688          }
 688  689  
 689  690          kmem_free(fuidp, sizeof (zfs_fuid_info_t));
 690  691  }
 691  692  
 692  693  /*
      694 + * Check to see if user ID is in the list of SIDs in CR.
      695 + */
      696 +boolean_t
      697 +zfs_user_in_cred(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr)
      698 +{
      699 +        ksid_t          *ksid = crgetsid(cr, KSID_USER);
      700 +        ksidlist_t      *ksidlist = crgetsidlist(cr);
      701 +        uid_t           uid;
      702 +
      703 +        /* Check for match with cred->cr_uid */
      704 +        uid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_ACE_USER);
      705 +        if (uid != IDMAP_WK_CREATOR_OWNER_UID &&
      706 +            uid == crgetuid(cr))
      707 +                return (B_TRUE);
      708 +
      709 +        /* Check for any match in the ksidlist */
      710 +        if (ksid && ksidlist) {
      711 +                int             i;
      712 +                ksid_t          *ksid_vec;
      713 +                uint32_t        idx = FUID_INDEX(id);
      714 +                uint32_t        rid = FUID_RID(id);
      715 +                const char      *domain;
      716 +
      717 +                if (idx == 0) {
      718 +                        /*
      719 +                         * The ID passed in has idx zero, which means
      720 +                         * it's just a Unix UID.  That can never match
      721 +                         * anything in ksid_vec[] because those all
      722 +                         * have ksid->ks_id set to a Group ID.
      723 +                         */
      724 +                        return (B_FALSE);
      725 +                }
      726 +
      727 +                domain = zfs_fuid_find_by_idx(zfsvfs, idx);
      728 +                ASSERT(domain != NULL);
      729 +
      730 +                if (strcmp(domain, IDMAP_WK_CREATOR_SID_AUTHORITY) == 0)
      731 +                        return (B_FALSE);
      732 +
      733 +                ksid_vec = ksidlist->ksl_sids;
      734 +                for (i = 0; i != ksidlist->ksl_nsid; i++) {
      735 +                        if ((strcmp(domain,
      736 +                            ksid_vec[i].ks_domain->kd_name) == 0) &&
      737 +                            rid == ksid_vec[i].ks_rid)
      738 +                                return (B_TRUE);
      739 +                }
      740 +        }
      741 +        return (B_FALSE);
      742 +}
      743 +
      744 +/*
 693  745   * Check to see if id is a groupmember.  If cred
 694  746   * has ksid info then sidlist is checked first
 695  747   * and if still not found then POSIX groups are checked
 696  748   *
 697  749   * Will use a straight FUID compare when possible.
 698  750   */
 699  751  boolean_t
 700  752  zfs_groupmember(zfsvfs_t *zfsvfs, uint64_t id, cred_t *cr)
 701  753  {
 702  754          ksid_t          *ksid = crgetsid(cr, KSID_GROUP);
 703  755          ksidlist_t      *ksidlist = crgetsidlist(cr);
 704  756          uid_t           gid;
 705  757  
 706  758          if (ksid && ksidlist) {
 707      -                int             i;
      759 +                int             i;
 708  760                  ksid_t          *ksid_groups;
 709  761                  uint32_t        idx = FUID_INDEX(id);
 710  762                  uint32_t        rid = FUID_RID(id);
 711  763  
 712  764                  ksid_groups = ksidlist->ksl_sids;
 713  765  
 714  766                  for (i = 0; i != ksidlist->ksl_nsid; i++) {
 715  767                          if (idx == 0) {
 716  768                                  if (id != IDMAP_WK_CREATOR_GROUP_GID &&
 717  769                                      id == ksid_groups[i].ks_id) {
 718  770                                          return (B_TRUE);
 719  771                                  }
 720  772                          } else {
 721  773                                  const char *domain;
 722  774  
 723  775                                  domain = zfs_fuid_find_by_idx(zfsvfs, idx);
 724  776                                  ASSERT(domain != NULL);
 725  777  
 726  778                                  if (strcmp(domain,
 727  779                                      IDMAP_WK_CREATOR_SID_AUTHORITY) == 0)
 728  780                                          return (B_FALSE);
 729  781  
 730  782                                  if ((strcmp(domain,
 731  783                                      ksid_groups[i].ks_domain->kd_name) == 0) &&
 732  784                                      rid == ksid_groups[i].ks_rid)
 733  785                                          return (B_TRUE);
 734  786                          }
 735  787                  }
 736  788          }
 737  789  
 738  790          /*
 739  791           * Not found in ksidlist, check posix groups
 740  792           */
 741  793          gid = zfs_fuid_map_id(zfsvfs, id, cr, ZFS_GROUP);
 742  794          return (groupmember(gid, cr));
 743  795  }
 744  796  
 745  797  void
 746  798  zfs_fuid_txhold(zfsvfs_t *zfsvfs, dmu_tx_t *tx)
 747  799  {
 748  800          if (zfsvfs->z_fuid_obj == 0) {
 749  801                  dmu_tx_hold_bonus(tx, DMU_NEW_OBJECT);
 750  802                  dmu_tx_hold_write(tx, DMU_NEW_OBJECT, 0,
 751  803                      FUID_SIZE_ESTIMATE(zfsvfs));
 752  804                  dmu_tx_hold_zap(tx, MASTER_NODE_OBJ, FALSE, NULL);
 753  805          } else {
 754  806                  dmu_tx_hold_bonus(tx, zfsvfs->z_fuid_obj);
 755  807                  dmu_tx_hold_write(tx, zfsvfs->z_fuid_obj, 0,
 756  808                      FUID_SIZE_ESTIMATE(zfsvfs));
 757  809          }
 758  810  }
 759  811  #endif
  
    | 
      ↓ open down ↓ | 
    42 lines elided | 
    
      ↑ open up ↑ | 
  
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX