1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved.
  23  * Copyright (c) 2011, 2014 by Delphix. All rights reserved.
  24  */
  25 
  26 /*
  27  * DSL permissions are stored in a two level zap attribute
  28  * mechanism.   The first level identifies the "class" of
  29  * entry.  The class is identified by the first 2 letters of
  30  * the attribute.  The second letter "l" or "d" identifies whether
  31  * it is a local or descendent permission.  The first letter
  32  * identifies the type of entry.
  33  *
  34  * ul$<id>    identifies permissions granted locally for this userid.
  35  * ud$<id>    identifies permissions granted on descendent datasets for
  36  *            this userid.
  37  * Ul$<id>    identifies permission sets granted locally for this userid.
  38  * Ud$<id>    identifies permission sets granted on descendent datasets for
  39  *            this userid.
  40  * gl$<id>    identifies permissions granted locally for this groupid.
  41  * gd$<id>    identifies permissions granted on descendent datasets for
  42  *            this groupid.
  43  * Gl$<id>    identifies permission sets granted locally for this groupid.
  44  * Gd$<id>    identifies permission sets granted on descendent datasets for
  45  *            this groupid.
  46  * el$        identifies permissions granted locally for everyone.
  47  * ed$        identifies permissions granted on descendent datasets
  48  *            for everyone.
  49  * El$        identifies permission sets granted locally for everyone.
  50  * Ed$        identifies permission sets granted to descendent datasets for
  51  *            everyone.
  52  * c-$        identifies permission to create at dataset creation time.
  53  * C-$        identifies permission sets to grant locally at dataset creation
  54  *            time.
  55  * s-$@<name> permissions defined in specified set @<name>
  56  * S-$@<name> Sets defined in named set @<name>
  57  *
  58  * Each of the above entities points to another zap attribute that contains one
  59  * attribute for each allowed permission, such as create, destroy,...
  60  * All of the "upper" case class types will specify permission set names
  61  * rather than permissions.
  62  *
  63  * Basically it looks something like this:
  64  * ul$12 -> ZAP OBJ -> permissions...
  65  *
  66  * The ZAP OBJ is referred to as the jump object.
  67  */
  68 
  69 #include <sys/dmu.h>
  70 #include <sys/dmu_objset.h>
  71 #include <sys/dmu_tx.h>
  72 #include <sys/dsl_dataset.h>
  73 #include <sys/dsl_dir.h>
  74 #include <sys/dsl_prop.h>
  75 #include <sys/dsl_synctask.h>
  76 #include <sys/dsl_deleg.h>
  77 #include <sys/spa.h>
  78 #include <sys/zap.h>
  79 #include <sys/fs/zfs.h>
  80 #include <sys/cred.h>
  81 #include <sys/sunddi.h>
  82 
  83 #include "zfs_deleg.h"
  84 
  85 /*
  86  * Validate that user is allowed to delegate specified permissions.
  87  *
  88  * In order to delegate "create" you must have "create"
  89  * and "allow".
  90  */
  91 int
  92 dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr)
  93 {
  94         nvpair_t *whopair = NULL;
  95         int error;
  96 
  97         if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
  98                 return (error);
  99 
 100         while (whopair = nvlist_next_nvpair(nvp, whopair)) {
 101                 nvlist_t *perms;
 102                 nvpair_t *permpair = NULL;
 103 
 104                 VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
 105 
 106                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 107                         const char *perm = nvpair_name(permpair);
 108 
 109                         if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0)
 110                                 return (SET_ERROR(EPERM));
 111 
 112                         if ((error = dsl_deleg_access(ddname, perm, cr)) != 0)
 113                                 return (error);
 114                 }
 115         }
 116         return (0);
 117 }
 118 
 119 /*
 120  * Validate that user is allowed to unallow specified permissions.  They
 121  * must have the 'allow' permission, and even then can only unallow
 122  * perms for their uid.
 123  */
 124 int
 125 dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr)
 126 {
 127         nvpair_t *whopair = NULL;
 128         int error;
 129         char idstr[32];
 130 
 131         if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0)
 132                 return (error);
 133 
 134         (void) snprintf(idstr, sizeof (idstr), "%lld",
 135             (longlong_t)crgetuid(cr));
 136 
 137         while (whopair = nvlist_next_nvpair(nvp, whopair)) {
 138                 zfs_deleg_who_type_t type = nvpair_name(whopair)[0];
 139 
 140                 if (type != ZFS_DELEG_USER &&
 141                     type != ZFS_DELEG_USER_SETS)
 142                         return (SET_ERROR(EPERM));
 143 
 144                 if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0)
 145                         return (SET_ERROR(EPERM));
 146         }
 147         return (0);
 148 }
 149 
 150 typedef struct dsl_deleg_arg {
 151         const char *dda_name;
 152         nvlist_t *dda_nvlist;
 153 } dsl_deleg_arg_t;
 154 
 155 static void
 156 dsl_deleg_set_sync(void *arg, dmu_tx_t *tx)
 157 {
 158         dsl_deleg_arg_t *dda = arg;
 159         dsl_dir_t *dd;
 160         dsl_pool_t *dp = dmu_tx_pool(tx);
 161         objset_t *mos = dp->dp_meta_objset;
 162         nvpair_t *whopair = NULL;
 163         uint64_t zapobj;
 164 
 165         VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL));
 166 
 167         zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
 168         if (zapobj == 0) {
 169                 dmu_buf_will_dirty(dd->dd_dbuf, tx);
 170                 zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos,
 171                     DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 172         }
 173 
 174         while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) {
 175                 const char *whokey = nvpair_name(whopair);
 176                 nvlist_t *perms;
 177                 nvpair_t *permpair = NULL;
 178                 uint64_t jumpobj;
 179 
 180                 perms = fnvpair_value_nvlist(whopair);
 181 
 182                 if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) {
 183                         jumpobj = zap_create_link(mos, DMU_OT_DSL_PERMS,
 184                             zapobj, whokey, tx);
 185                 }
 186 
 187                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 188                         const char *perm = nvpair_name(permpair);
 189                         uint64_t n = 0;
 190 
 191                         VERIFY(zap_update(mos, jumpobj,
 192                             perm, 8, 1, &n, tx) == 0);
 193                         spa_history_log_internal_dd(dd, "permission update", tx,
 194                             "%s %s", whokey, perm);
 195                 }
 196         }
 197         dsl_dir_rele(dd, FTAG);
 198 }
 199 
 200 static void
 201 dsl_deleg_unset_sync(void *arg, dmu_tx_t *tx)
 202 {
 203         dsl_deleg_arg_t *dda = arg;
 204         dsl_dir_t *dd;
 205         dsl_pool_t *dp = dmu_tx_pool(tx);
 206         objset_t *mos = dp->dp_meta_objset;
 207         nvpair_t *whopair = NULL;
 208         uint64_t zapobj;
 209 
 210         VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL));
 211         zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
 212         if (zapobj == 0) {
 213                 dsl_dir_rele(dd, FTAG);
 214                 return;
 215         }
 216 
 217         while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) {
 218                 const char *whokey = nvpair_name(whopair);
 219                 nvlist_t *perms;
 220                 nvpair_t *permpair = NULL;
 221                 uint64_t jumpobj;
 222 
 223                 if (nvpair_value_nvlist(whopair, &perms) != 0) {
 224                         if (zap_lookup(mos, zapobj, whokey, 8,
 225                             1, &jumpobj) == 0) {
 226                                 (void) zap_remove(mos, zapobj, whokey, tx);
 227                                 VERIFY(0 == zap_destroy(mos, jumpobj, tx));
 228                         }
 229                         spa_history_log_internal_dd(dd, "permission who remove",
 230                             tx, "%s", whokey);
 231                         continue;
 232                 }
 233 
 234                 if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0)
 235                         continue;
 236 
 237                 while (permpair = nvlist_next_nvpair(perms, permpair)) {
 238                         const char *perm = nvpair_name(permpair);
 239                         uint64_t n = 0;
 240 
 241                         (void) zap_remove(mos, jumpobj, perm, tx);
 242                         if (zap_count(mos, jumpobj, &n) == 0 && n == 0) {
 243                                 (void) zap_remove(mos, zapobj,
 244                                     whokey, tx);
 245                                 VERIFY(0 == zap_destroy(mos,
 246                                     jumpobj, tx));
 247                         }
 248                         spa_history_log_internal_dd(dd, "permission remove", tx,
 249                             "%s %s", whokey, perm);
 250                 }
 251         }
 252         dsl_dir_rele(dd, FTAG);
 253 }
 254 
 255 static int
 256 dsl_deleg_check(void *arg, dmu_tx_t *tx)
 257 {
 258         dsl_deleg_arg_t *dda = arg;
 259         dsl_dir_t *dd;
 260         int error;
 261 
 262         if (spa_version(dmu_tx_pool(tx)->dp_spa) <
 263             SPA_VERSION_DELEGATED_PERMS) {
 264                 return (SET_ERROR(ENOTSUP));
 265         }
 266 
 267         error = dsl_dir_hold(dmu_tx_pool(tx), dda->dda_name, FTAG, &dd, NULL);
 268         if (error == 0)
 269                 dsl_dir_rele(dd, FTAG);
 270         return (error);
 271 }
 272 
 273 int
 274 dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
 275 {
 276         dsl_deleg_arg_t dda;
 277 
 278         /* nvp must already have been verified to be valid */
 279 
 280         dda.dda_name = ddname;
 281         dda.dda_nvlist = nvp;
 282 
 283         return (dsl_sync_task(ddname, dsl_deleg_check,
 284             unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync,
 285             &dda, fnvlist_num_pairs(nvp), ZFS_SPACE_CHECK_RESERVED));
 286 }
 287 
 288 /*
 289  * Find all 'allow' permissions from a given point and then continue
 290  * traversing up to the root.
 291  *
 292  * This function constructs an nvlist of nvlists.
 293  * each setpoint is an nvlist composed of an nvlist of an nvlist
 294  * of the individual * users/groups/everyone/create
 295  * permissions.
 296  *
 297  * The nvlist will look like this.
 298  *
 299  * { source fsname -> { whokeys { permissions,...}, ...}}
 300  *
 301  * The fsname nvpairs will be arranged in a bottom up order.  For example,
 302  * if we have the following structure a/b/c then the nvpairs for the fsnames
 303  * will be ordered a/b/c, a/b, a.
 304  */
 305 int
 306 dsl_deleg_get(const char *ddname, nvlist_t **nvp)
 307 {
 308         dsl_dir_t *dd, *startdd;
 309         dsl_pool_t *dp;
 310         int error;
 311         objset_t *mos;
 312 
 313         error = dsl_pool_hold(ddname, FTAG, &dp);
 314         if (error != 0)
 315                 return (error);
 316 
 317         error = dsl_dir_hold(dp, ddname, FTAG, &startdd, NULL);
 318         if (error != 0) {
 319                 dsl_pool_rele(dp, FTAG);
 320                 return (error);
 321         }
 322 
 323         dp = startdd->dd_pool;
 324         mos = dp->dp_meta_objset;
 325 
 326         VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
 327 
 328         for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
 329                 zap_cursor_t basezc;
 330                 zap_attribute_t baseza;
 331                 nvlist_t *sp_nvp;
 332                 uint64_t n;
 333                 char source[MAXNAMELEN];
 334 
 335                 if (dsl_dir_phys(dd)->dd_deleg_zapobj == 0 ||
 336                     zap_count(mos,
 337                     dsl_dir_phys(dd)->dd_deleg_zapobj, &n) != 0 || n == 0)
 338                         continue;
 339 
 340                 sp_nvp = fnvlist_alloc();
 341                 for (zap_cursor_init(&basezc, mos,
 342                     dsl_dir_phys(dd)->dd_deleg_zapobj);
 343                     zap_cursor_retrieve(&basezc, &baseza) == 0;
 344                     zap_cursor_advance(&basezc)) {
 345                         zap_cursor_t zc;
 346                         zap_attribute_t za;
 347                         nvlist_t *perms_nvp;
 348 
 349                         ASSERT(baseza.za_integer_length == 8);
 350                         ASSERT(baseza.za_num_integers == 1);
 351 
 352                         perms_nvp = fnvlist_alloc();
 353                         for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
 354                             zap_cursor_retrieve(&zc, &za) == 0;
 355                             zap_cursor_advance(&zc)) {
 356                                 fnvlist_add_boolean(perms_nvp, za.za_name);
 357                         }
 358                         zap_cursor_fini(&zc);
 359                         fnvlist_add_nvlist(sp_nvp, baseza.za_name, perms_nvp);
 360                         fnvlist_free(perms_nvp);
 361                 }
 362 
 363                 zap_cursor_fini(&basezc);
 364 
 365                 dsl_dir_name(dd, source);
 366                 fnvlist_add_nvlist(*nvp, source, sp_nvp);
 367                 nvlist_free(sp_nvp);
 368         }
 369 
 370         dsl_dir_rele(startdd, FTAG);
 371         dsl_pool_rele(dp, FTAG);
 372         return (0);
 373 }
 374 
 375 /*
 376  * Routines for dsl_deleg_access() -- access checking.
 377  */
 378 typedef struct perm_set {
 379         avl_node_t      p_node;
 380         boolean_t       p_matched;
 381         char            p_setname[ZFS_MAX_DELEG_NAME];
 382 } perm_set_t;
 383 
 384 static int
 385 perm_set_compare(const void *arg1, const void *arg2)
 386 {
 387         const perm_set_t *node1 = arg1;
 388         const perm_set_t *node2 = arg2;
 389         int val;
 390 
 391         val = strcmp(node1->p_setname, node2->p_setname);
 392         if (val == 0)
 393                 return (0);
 394         return (val > 0 ? 1 : -1);
 395 }
 396 
 397 /*
 398  * Determine whether a specified permission exists.
 399  *
 400  * First the base attribute has to be retrieved.  i.e. ul$12
 401  * Once the base object has been retrieved the actual permission
 402  * is lookup up in the zap object the base object points to.
 403  *
 404  * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
 405  * there is no perm in that jumpobj.
 406  */
 407 static int
 408 dsl_check_access(objset_t *mos, uint64_t zapobj,
 409     char type, char checkflag, void *valp, const char *perm)
 410 {
 411         int error;
 412         uint64_t jumpobj, zero;
 413         char whokey[ZFS_MAX_DELEG_NAME];
 414 
 415         zfs_deleg_whokey(whokey, type, checkflag, valp);
 416         error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
 417         if (error == 0) {
 418                 error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
 419                 if (error == ENOENT)
 420                         error = SET_ERROR(EPERM);
 421         }
 422         return (error);
 423 }
 424 
 425 /*
 426  * check a specified user/group for a requested permission
 427  */
 428 static int
 429 dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm,
 430     int checkflag, cred_t *cr)
 431 {
 432         const   gid_t *gids;
 433         int     ngids;
 434         int     i;
 435         uint64_t id;
 436 
 437         /* check for user */
 438         id = crgetuid(cr);
 439         if (dsl_check_access(mos, zapobj,
 440             ZFS_DELEG_USER, checkflag, &id, perm) == 0)
 441                 return (0);
 442 
 443         /* check for users primary group */
 444         id = crgetgid(cr);
 445         if (dsl_check_access(mos, zapobj,
 446             ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
 447                 return (0);
 448 
 449         /* check for everyone entry */
 450         id = -1;
 451         if (dsl_check_access(mos, zapobj,
 452             ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
 453                 return (0);
 454 
 455         /* check each supplemental group user is a member of */
 456         ngids = crgetngroups(cr);
 457         gids = crgetgroups(cr);
 458         for (i = 0; i != ngids; i++) {
 459                 id = gids[i];
 460                 if (dsl_check_access(mos, zapobj,
 461                     ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
 462                         return (0);
 463         }
 464 
 465         return (SET_ERROR(EPERM));
 466 }
 467 
 468 /*
 469  * Iterate over the sets specified in the specified zapobj
 470  * and load them into the permsets avl tree.
 471  */
 472 static int
 473 dsl_load_sets(objset_t *mos, uint64_t zapobj,
 474     char type, char checkflag, void *valp, avl_tree_t *avl)
 475 {
 476         zap_cursor_t zc;
 477         zap_attribute_t za;
 478         perm_set_t *permnode;
 479         avl_index_t idx;
 480         uint64_t jumpobj;
 481         int error;
 482         char whokey[ZFS_MAX_DELEG_NAME];
 483 
 484         zfs_deleg_whokey(whokey, type, checkflag, valp);
 485 
 486         error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
 487         if (error != 0)
 488                 return (error);
 489 
 490         for (zap_cursor_init(&zc, mos, jumpobj);
 491             zap_cursor_retrieve(&zc, &za) == 0;
 492             zap_cursor_advance(&zc)) {
 493                 permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
 494                 (void) strlcpy(permnode->p_setname, za.za_name,
 495                     sizeof (permnode->p_setname));
 496                 permnode->p_matched = B_FALSE;
 497 
 498                 if (avl_find(avl, permnode, &idx) == NULL) {
 499                         avl_insert(avl, permnode, idx);
 500                 } else {
 501                         kmem_free(permnode, sizeof (perm_set_t));
 502                 }
 503         }
 504         zap_cursor_fini(&zc);
 505         return (0);
 506 }
 507 
 508 /*
 509  * Load all permissions user based on cred belongs to.
 510  */
 511 static void
 512 dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
 513     char checkflag, cred_t *cr)
 514 {
 515         const   gid_t *gids;
 516         int     ngids, i;
 517         uint64_t id;
 518 
 519         id = crgetuid(cr);
 520         (void) dsl_load_sets(mos, zapobj,
 521             ZFS_DELEG_USER_SETS, checkflag, &id, avl);
 522 
 523         id = crgetgid(cr);
 524         (void) dsl_load_sets(mos, zapobj,
 525             ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
 526 
 527         (void) dsl_load_sets(mos, zapobj,
 528             ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
 529 
 530         ngids = crgetngroups(cr);
 531         gids = crgetgroups(cr);
 532         for (i = 0; i != ngids; i++) {
 533                 id = gids[i];
 534                 (void) dsl_load_sets(mos, zapobj,
 535                     ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
 536         }
 537 }
 538 
 539 /*
 540  * Check if user has requested permission.
 541  */
 542 int
 543 dsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr)
 544 {
 545         dsl_dir_t *dd;
 546         dsl_pool_t *dp;
 547         void *cookie;
 548         int     error;
 549         char    checkflag;
 550         objset_t *mos;
 551         avl_tree_t permsets;
 552         perm_set_t *setnode;
 553 
 554         dp = ds->ds_dir->dd_pool;
 555         mos = dp->dp_meta_objset;
 556 
 557         if (dsl_delegation_on(mos) == B_FALSE)
 558                 return (SET_ERROR(ECANCELED));
 559 
 560         if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
 561             SPA_VERSION_DELEGATED_PERMS)
 562                 return (SET_ERROR(EPERM));
 563 
 564         if (ds->ds_is_snapshot) {
 565                 /*
 566                  * Snapshots are treated as descendents only,
 567                  * local permissions do not apply.
 568                  */
 569                 checkflag = ZFS_DELEG_DESCENDENT;
 570         } else {
 571                 checkflag = ZFS_DELEG_LOCAL;
 572         }
 573 
 574         avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
 575             offsetof(perm_set_t, p_node));
 576 
 577         ASSERT(dsl_pool_config_held(dp));
 578         for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent,
 579             checkflag = ZFS_DELEG_DESCENDENT) {
 580                 uint64_t zapobj;
 581                 boolean_t expanded;
 582 
 583                 /*
 584                  * If not in global zone then make sure
 585                  * the zoned property is set
 586                  */
 587                 if (!INGLOBALZONE(curproc)) {
 588                         uint64_t zoned;
 589 
 590                         if (dsl_prop_get_dd(dd,
 591                             zfs_prop_to_name(ZFS_PROP_ZONED),
 592                             8, 1, &zoned, NULL, B_FALSE) != 0)
 593                                 break;
 594                         if (!zoned)
 595                                 break;
 596                 }
 597                 zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
 598 
 599                 if (zapobj == 0)
 600                         continue;
 601 
 602                 dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
 603 again:
 604                 expanded = B_FALSE;
 605                 for (setnode = avl_first(&permsets); setnode;
 606                     setnode = AVL_NEXT(&permsets, setnode)) {
 607                         if (setnode->p_matched == B_TRUE)
 608                                 continue;
 609 
 610                         /* See if this set directly grants this permission */
 611                         error = dsl_check_access(mos, zapobj,
 612                             ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
 613                         if (error == 0)
 614                                 goto success;
 615                         if (error == EPERM)
 616                                 setnode->p_matched = B_TRUE;
 617 
 618                         /* See if this set includes other sets */
 619                         error = dsl_load_sets(mos, zapobj,
 620                             ZFS_DELEG_NAMED_SET_SETS, 0,
 621                             setnode->p_setname, &permsets);
 622                         if (error == 0)
 623                                 setnode->p_matched = expanded = B_TRUE;
 624                 }
 625                 /*
 626                  * If we expanded any sets, that will define more sets,
 627                  * which we need to check.
 628                  */
 629                 if (expanded)
 630                         goto again;
 631 
 632                 error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
 633                 if (error == 0)
 634                         goto success;
 635         }
 636         error = SET_ERROR(EPERM);
 637 success:
 638 
 639         cookie = NULL;
 640         while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL)
 641                 kmem_free(setnode, sizeof (perm_set_t));
 642 
 643         return (error);
 644 }
 645 
 646 int
 647 dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr)
 648 {
 649         dsl_pool_t *dp;
 650         dsl_dataset_t *ds;
 651         int error;
 652 
 653         error = dsl_pool_hold(dsname, FTAG, &dp);
 654         if (error != 0)
 655                 return (error);
 656         error = dsl_dataset_hold(dp, dsname, FTAG, &ds);
 657         if (error == 0) {
 658                 error = dsl_deleg_access_impl(ds, perm, cr);
 659                 dsl_dataset_rele(ds, FTAG);
 660         }
 661         dsl_pool_rele(dp, FTAG);
 662 
 663         return (error);
 664 }
 665 
 666 /*
 667  * Other routines.
 668  */
 669 
 670 static void
 671 copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj,
 672     boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
 673 {
 674         objset_t *mos = dd->dd_pool->dp_meta_objset;
 675         uint64_t jumpobj, pjumpobj;
 676         uint64_t zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
 677         zap_cursor_t zc;
 678         zap_attribute_t za;
 679         char whokey[ZFS_MAX_DELEG_NAME];
 680 
 681         zfs_deleg_whokey(whokey,
 682             dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
 683             ZFS_DELEG_LOCAL, NULL);
 684         if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0)
 685                 return;
 686 
 687         if (zapobj == 0) {
 688                 dmu_buf_will_dirty(dd->dd_dbuf, tx);
 689                 zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos,
 690                     DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 691         }
 692 
 693         zfs_deleg_whokey(whokey,
 694             dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
 695             ZFS_DELEG_LOCAL, &uid);
 696         if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
 697                 jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
 698                 VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
 699         }
 700 
 701         for (zap_cursor_init(&zc, mos, pjumpobj);
 702             zap_cursor_retrieve(&zc, &za) == 0;
 703             zap_cursor_advance(&zc)) {
 704                 uint64_t zero = 0;
 705                 ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
 706 
 707                 VERIFY(zap_update(mos, jumpobj, za.za_name,
 708                     8, 1, &zero, tx) == 0);
 709         }
 710         zap_cursor_fini(&zc);
 711 }
 712 
 713 /*
 714  * set all create time permission on new dataset.
 715  */
 716 void
 717 dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
 718 {
 719         dsl_dir_t *dd;
 720         uint64_t uid = crgetuid(cr);
 721 
 722         if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
 723             SPA_VERSION_DELEGATED_PERMS)
 724                 return;
 725 
 726         for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
 727                 uint64_t pzapobj = dsl_dir_phys(dd)->dd_deleg_zapobj;
 728 
 729                 if (pzapobj == 0)
 730                         continue;
 731 
 732                 copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx);
 733                 copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx);
 734         }
 735 }
 736 
 737 int
 738 dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
 739 {
 740         zap_cursor_t zc;
 741         zap_attribute_t za;
 742 
 743         if (zapobj == 0)
 744                 return (0);
 745 
 746         for (zap_cursor_init(&zc, mos, zapobj);
 747             zap_cursor_retrieve(&zc, &za) == 0;
 748             zap_cursor_advance(&zc)) {
 749                 ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
 750                 VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
 751         }
 752         zap_cursor_fini(&zc);
 753         VERIFY(0 == zap_destroy(mos, zapobj, tx));
 754         return (0);
 755 }
 756 
 757 boolean_t
 758 dsl_delegation_on(objset_t *os)
 759 {
 760         return (!!spa_delegation(os->os_spa));
 761 }