1ecd6cf80Smarks /* 2ecd6cf80Smarks * CDDL HEADER START 3ecd6cf80Smarks * 4ecd6cf80Smarks * The contents of this file are subject to the terms of the 5ecd6cf80Smarks * Common Development and Distribution License (the "License"). 6ecd6cf80Smarks * You may not use this file except in compliance with the License. 7ecd6cf80Smarks * 8ecd6cf80Smarks * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9ecd6cf80Smarks * or http://www.opensolaris.org/os/licensing. 10ecd6cf80Smarks * See the License for the specific language governing permissions 11ecd6cf80Smarks * and limitations under the License. 12ecd6cf80Smarks * 13ecd6cf80Smarks * When distributing Covered Code, include this CDDL HEADER in each 14ecd6cf80Smarks * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15ecd6cf80Smarks * If applicable, add the following below this CDDL HEADER, with the 16ecd6cf80Smarks * fields enclosed by brackets "[]" replaced with your own identifying 17ecd6cf80Smarks * information: Portions Copyright [yyyy] [name of copyright owner] 18ecd6cf80Smarks * 19ecd6cf80Smarks * CDDL HEADER END 20ecd6cf80Smarks */ 21ecd6cf80Smarks /* 2299d5e173STim Haley * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23*40a5c998SMatthew Ahrens * Copyright (c) 2011, 2015 by Delphix. All rights reserved. 24ecd6cf80Smarks */ 25ecd6cf80Smarks 26ecd6cf80Smarks /* 27ecd6cf80Smarks * DSL permissions are stored in a two level zap attribute 28ecd6cf80Smarks * mechanism. The first level identifies the "class" of 29ecd6cf80Smarks * entry. The class is identified by the first 2 letters of 30ecd6cf80Smarks * the attribute. The second letter "l" or "d" identifies whether 31ecd6cf80Smarks * it is a local or descendent permission. The first letter 32ecd6cf80Smarks * identifies the type of entry. 33ecd6cf80Smarks * 34da6c28aaSamw * ul$<id> identifies permissions granted locally for this userid. 35ecd6cf80Smarks * ud$<id> identifies permissions granted on descendent datasets for 36ecd6cf80Smarks * this userid. 37ecd6cf80Smarks * Ul$<id> identifies permission sets granted locally for this userid. 38ecd6cf80Smarks * Ud$<id> identifies permission sets granted on descendent datasets for 39ecd6cf80Smarks * this userid. 40ecd6cf80Smarks * gl$<id> identifies permissions granted locally for this groupid. 41ecd6cf80Smarks * gd$<id> identifies permissions granted on descendent datasets for 42ecd6cf80Smarks * this groupid. 43ecd6cf80Smarks * Gl$<id> identifies permission sets granted locally for this groupid. 44ecd6cf80Smarks * Gd$<id> identifies permission sets granted on descendent datasets for 45ecd6cf80Smarks * this groupid. 46ecd6cf80Smarks * el$ identifies permissions granted locally for everyone. 47ecd6cf80Smarks * ed$ identifies permissions granted on descendent datasets 48ecd6cf80Smarks * for everyone. 49ecd6cf80Smarks * El$ identifies permission sets granted locally for everyone. 50ecd6cf80Smarks * Ed$ identifies permission sets granted to descendent datasets for 51ecd6cf80Smarks * everyone. 52ecd6cf80Smarks * c-$ identifies permission to create at dataset creation time. 53ecd6cf80Smarks * C-$ identifies permission sets to grant locally at dataset creation 54ecd6cf80Smarks * time. 55ecd6cf80Smarks * s-$@<name> permissions defined in specified set @<name> 56ecd6cf80Smarks * S-$@<name> Sets defined in named set @<name> 57ecd6cf80Smarks * 58da6c28aaSamw * Each of the above entities points to another zap attribute that contains one 59ecd6cf80Smarks * attribute for each allowed permission, such as create, destroy,... 60ecd6cf80Smarks * All of the "upper" case class types will specify permission set names 61ecd6cf80Smarks * rather than permissions. 62ecd6cf80Smarks * 63ecd6cf80Smarks * Basically it looks something like this: 64ecd6cf80Smarks * ul$12 -> ZAP OBJ -> permissions... 65ecd6cf80Smarks * 66ecd6cf80Smarks * The ZAP OBJ is referred to as the jump object. 67ecd6cf80Smarks */ 68ecd6cf80Smarks 69ecd6cf80Smarks #include <sys/dmu.h> 70ecd6cf80Smarks #include <sys/dmu_objset.h> 71ecd6cf80Smarks #include <sys/dmu_tx.h> 72ecd6cf80Smarks #include <sys/dsl_dataset.h> 73ecd6cf80Smarks #include <sys/dsl_dir.h> 74ecd6cf80Smarks #include <sys/dsl_prop.h> 75ecd6cf80Smarks #include <sys/dsl_synctask.h> 76ecd6cf80Smarks #include <sys/dsl_deleg.h> 77ecd6cf80Smarks #include <sys/spa.h> 78ecd6cf80Smarks #include <sys/zap.h> 79ecd6cf80Smarks #include <sys/fs/zfs.h> 80ecd6cf80Smarks #include <sys/cred.h> 81ecd6cf80Smarks #include <sys/sunddi.h> 82ecd6cf80Smarks 83ecd6cf80Smarks #include "zfs_deleg.h" 84ecd6cf80Smarks 85ecd6cf80Smarks /* 86ecd6cf80Smarks * Validate that user is allowed to delegate specified permissions. 87ecd6cf80Smarks * 8891ebeef5Sahrens * In order to delegate "create" you must have "create" 89ecd6cf80Smarks * and "allow". 90ecd6cf80Smarks */ 91ecd6cf80Smarks int 92ecd6cf80Smarks dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr) 93ecd6cf80Smarks { 94ecd6cf80Smarks nvpair_t *whopair = NULL; 9591ebeef5Sahrens int error; 96ecd6cf80Smarks 9791ebeef5Sahrens if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0) 98ecd6cf80Smarks return (error); 99ecd6cf80Smarks 100ecd6cf80Smarks while (whopair = nvlist_next_nvpair(nvp, whopair)) { 101ecd6cf80Smarks nvlist_t *perms; 102ecd6cf80Smarks nvpair_t *permpair = NULL; 103ecd6cf80Smarks 104ecd6cf80Smarks VERIFY(nvpair_value_nvlist(whopair, &perms) == 0); 105ecd6cf80Smarks 106ecd6cf80Smarks while (permpair = nvlist_next_nvpair(perms, permpair)) { 107ecd6cf80Smarks const char *perm = nvpair_name(permpair); 108ecd6cf80Smarks 109ecd6cf80Smarks if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0) 110be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 111ecd6cf80Smarks 11291ebeef5Sahrens if ((error = dsl_deleg_access(ddname, perm, cr)) != 0) 113ecd6cf80Smarks return (error); 114ecd6cf80Smarks } 115ecd6cf80Smarks } 11691ebeef5Sahrens return (0); 117ecd6cf80Smarks } 118ecd6cf80Smarks 119ecd6cf80Smarks /* 120ecd6cf80Smarks * Validate that user is allowed to unallow specified permissions. They 121ecd6cf80Smarks * must have the 'allow' permission, and even then can only unallow 122ecd6cf80Smarks * perms for their uid. 123ecd6cf80Smarks */ 124ecd6cf80Smarks int 125ecd6cf80Smarks dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr) 126ecd6cf80Smarks { 127ecd6cf80Smarks nvpair_t *whopair = NULL; 128ecd6cf80Smarks int error; 12991ebeef5Sahrens char idstr[32]; 130ecd6cf80Smarks 13191ebeef5Sahrens if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0) 132ecd6cf80Smarks return (error); 133ecd6cf80Smarks 13491ebeef5Sahrens (void) snprintf(idstr, sizeof (idstr), "%lld", 13591ebeef5Sahrens (longlong_t)crgetuid(cr)); 13691ebeef5Sahrens 137ecd6cf80Smarks while (whopair = nvlist_next_nvpair(nvp, whopair)) { 138ecd6cf80Smarks zfs_deleg_who_type_t type = nvpair_name(whopair)[0]; 139ecd6cf80Smarks 140ecd6cf80Smarks if (type != ZFS_DELEG_USER && 141ecd6cf80Smarks type != ZFS_DELEG_USER_SETS) 142be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 143ecd6cf80Smarks 144ecd6cf80Smarks if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0) 145be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 146ecd6cf80Smarks } 147ecd6cf80Smarks return (0); 148ecd6cf80Smarks } 149ecd6cf80Smarks 1503b2aab18SMatthew Ahrens typedef struct dsl_deleg_arg { 1513b2aab18SMatthew Ahrens const char *dda_name; 1523b2aab18SMatthew Ahrens nvlist_t *dda_nvlist; 1533b2aab18SMatthew Ahrens } dsl_deleg_arg_t; 154ecd6cf80Smarks 1553b2aab18SMatthew Ahrens static void 1563b2aab18SMatthew Ahrens dsl_deleg_set_sync(void *arg, dmu_tx_t *tx) 1573b2aab18SMatthew Ahrens { 1583b2aab18SMatthew Ahrens dsl_deleg_arg_t *dda = arg; 1593b2aab18SMatthew Ahrens dsl_dir_t *dd; 1603b2aab18SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 1613b2aab18SMatthew Ahrens objset_t *mos = dp->dp_meta_objset; 1623b2aab18SMatthew Ahrens nvpair_t *whopair = NULL; 1633b2aab18SMatthew Ahrens uint64_t zapobj; 1643b2aab18SMatthew Ahrens 1653b2aab18SMatthew Ahrens VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL)); 1663b2aab18SMatthew Ahrens 167c1379625SJustin T. Gibbs zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 168ecd6cf80Smarks if (zapobj == 0) { 169ecd6cf80Smarks dmu_buf_will_dirty(dd->dd_dbuf, tx); 170c1379625SJustin T. Gibbs zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos, 171ecd6cf80Smarks DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 172ecd6cf80Smarks } 173ecd6cf80Smarks 1743b2aab18SMatthew Ahrens while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) { 1753cb34c60Sahrens const char *whokey = nvpair_name(whopair); 1763cb34c60Sahrens nvlist_t *perms; 1773cb34c60Sahrens nvpair_t *permpair = NULL; 1783cb34c60Sahrens uint64_t jumpobj; 1793cb34c60Sahrens 1803b2aab18SMatthew Ahrens perms = fnvpair_value_nvlist(whopair); 1813cb34c60Sahrens 1823cb34c60Sahrens if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) { 183ad135b5dSChristopher Siden jumpobj = zap_create_link(mos, DMU_OT_DSL_PERMS, 184ad135b5dSChristopher Siden zapobj, whokey, tx); 1853cb34c60Sahrens } 1863cb34c60Sahrens 1873cb34c60Sahrens while (permpair = nvlist_next_nvpair(perms, permpair)) { 1883cb34c60Sahrens const char *perm = nvpair_name(permpair); 1893cb34c60Sahrens uint64_t n = 0; 1903cb34c60Sahrens 1913cb34c60Sahrens VERIFY(zap_update(mos, jumpobj, 1923cb34c60Sahrens perm, 8, 1, &n, tx) == 0); 1934445fffbSMatthew Ahrens spa_history_log_internal_dd(dd, "permission update", tx, 1944445fffbSMatthew Ahrens "%s %s", whokey, perm); 1953cb34c60Sahrens } 1963cb34c60Sahrens } 1973b2aab18SMatthew Ahrens dsl_dir_rele(dd, FTAG); 1983cb34c60Sahrens } 1993cb34c60Sahrens 2003cb34c60Sahrens static void 2013b2aab18SMatthew Ahrens dsl_deleg_unset_sync(void *arg, dmu_tx_t *tx) 2023cb34c60Sahrens { 2033b2aab18SMatthew Ahrens dsl_deleg_arg_t *dda = arg; 2043b2aab18SMatthew Ahrens dsl_dir_t *dd; 2053b2aab18SMatthew Ahrens dsl_pool_t *dp = dmu_tx_pool(tx); 2063b2aab18SMatthew Ahrens objset_t *mos = dp->dp_meta_objset; 2073cb34c60Sahrens nvpair_t *whopair = NULL; 2083b2aab18SMatthew Ahrens uint64_t zapobj; 2093cb34c60Sahrens 2103b2aab18SMatthew Ahrens VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL)); 211c1379625SJustin T. Gibbs zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 2123b2aab18SMatthew Ahrens if (zapobj == 0) { 2133b2aab18SMatthew Ahrens dsl_dir_rele(dd, FTAG); 2143cb34c60Sahrens return; 2153b2aab18SMatthew Ahrens } 2163cb34c60Sahrens 2173b2aab18SMatthew Ahrens while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) { 218ecd6cf80Smarks const char *whokey = nvpair_name(whopair); 219ecd6cf80Smarks nvlist_t *perms; 220ecd6cf80Smarks nvpair_t *permpair = NULL; 221ecd6cf80Smarks uint64_t jumpobj; 222ecd6cf80Smarks 223ecd6cf80Smarks if (nvpair_value_nvlist(whopair, &perms) != 0) { 224ecd6cf80Smarks if (zap_lookup(mos, zapobj, whokey, 8, 225ecd6cf80Smarks 1, &jumpobj) == 0) { 226ecd6cf80Smarks (void) zap_remove(mos, zapobj, whokey, tx); 227ecd6cf80Smarks VERIFY(0 == zap_destroy(mos, jumpobj, tx)); 228ecd6cf80Smarks } 2294445fffbSMatthew Ahrens spa_history_log_internal_dd(dd, "permission who remove", 2304445fffbSMatthew Ahrens tx, "%s", whokey); 231ecd6cf80Smarks continue; 232ecd6cf80Smarks } 233ecd6cf80Smarks 2343cb34c60Sahrens if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) 235ecd6cf80Smarks continue; 236ecd6cf80Smarks 237ecd6cf80Smarks while (permpair = nvlist_next_nvpair(perms, permpair)) { 238ecd6cf80Smarks const char *perm = nvpair_name(permpair); 239ecd6cf80Smarks uint64_t n = 0; 240ecd6cf80Smarks 241ecd6cf80Smarks (void) zap_remove(mos, jumpobj, perm, tx); 2423cb34c60Sahrens if (zap_count(mos, jumpobj, &n) == 0 && n == 0) { 243ecd6cf80Smarks (void) zap_remove(mos, zapobj, 244ecd6cf80Smarks whokey, tx); 245ecd6cf80Smarks VERIFY(0 == zap_destroy(mos, 246ecd6cf80Smarks jumpobj, tx)); 247ecd6cf80Smarks } 2484445fffbSMatthew Ahrens spa_history_log_internal_dd(dd, "permission remove", tx, 2494445fffbSMatthew Ahrens "%s %s", whokey, perm); 250ecd6cf80Smarks } 251ecd6cf80Smarks } 2523b2aab18SMatthew Ahrens dsl_dir_rele(dd, FTAG); 2533b2aab18SMatthew Ahrens } 2543b2aab18SMatthew Ahrens 2553b2aab18SMatthew Ahrens static int 2563b2aab18SMatthew Ahrens dsl_deleg_check(void *arg, dmu_tx_t *tx) 2573b2aab18SMatthew Ahrens { 2583b2aab18SMatthew Ahrens dsl_deleg_arg_t *dda = arg; 2593b2aab18SMatthew Ahrens dsl_dir_t *dd; 2603b2aab18SMatthew Ahrens int error; 2613b2aab18SMatthew Ahrens 2623b2aab18SMatthew Ahrens if (spa_version(dmu_tx_pool(tx)->dp_spa) < 2633b2aab18SMatthew Ahrens SPA_VERSION_DELEGATED_PERMS) { 264be6fd75aSMatthew Ahrens return (SET_ERROR(ENOTSUP)); 2653b2aab18SMatthew Ahrens } 2663b2aab18SMatthew Ahrens 2673b2aab18SMatthew Ahrens error = dsl_dir_hold(dmu_tx_pool(tx), dda->dda_name, FTAG, &dd, NULL); 2683b2aab18SMatthew Ahrens if (error == 0) 2693b2aab18SMatthew Ahrens dsl_dir_rele(dd, FTAG); 2703b2aab18SMatthew Ahrens return (error); 271ecd6cf80Smarks } 272ecd6cf80Smarks 273ecd6cf80Smarks int 274ecd6cf80Smarks dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset) 275ecd6cf80Smarks { 2763b2aab18SMatthew Ahrens dsl_deleg_arg_t dda; 277ecd6cf80Smarks 2783b2aab18SMatthew Ahrens /* nvp must already have been verified to be valid */ 279ecd6cf80Smarks 2803b2aab18SMatthew Ahrens dda.dda_name = ddname; 2813b2aab18SMatthew Ahrens dda.dda_nvlist = nvp; 282ecd6cf80Smarks 2833b2aab18SMatthew Ahrens return (dsl_sync_task(ddname, dsl_deleg_check, 2843cb34c60Sahrens unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync, 2857d46dc6cSMatthew Ahrens &dda, fnvlist_num_pairs(nvp), ZFS_SPACE_CHECK_RESERVED)); 286ecd6cf80Smarks } 287ecd6cf80Smarks 288ecd6cf80Smarks /* 289ecd6cf80Smarks * Find all 'allow' permissions from a given point and then continue 290ecd6cf80Smarks * traversing up to the root. 291ecd6cf80Smarks * 292ecd6cf80Smarks * This function constructs an nvlist of nvlists. 293ecd6cf80Smarks * each setpoint is an nvlist composed of an nvlist of an nvlist 294ecd6cf80Smarks * of the individual * users/groups/everyone/create 295ecd6cf80Smarks * permissions. 296ecd6cf80Smarks * 297ecd6cf80Smarks * The nvlist will look like this. 298ecd6cf80Smarks * 299ecd6cf80Smarks * { source fsname -> { whokeys { permissions,...}, ...}} 300ecd6cf80Smarks * 301ecd6cf80Smarks * The fsname nvpairs will be arranged in a bottom up order. For example, 302ecd6cf80Smarks * if we have the following structure a/b/c then the nvpairs for the fsnames 303ecd6cf80Smarks * will be ordered a/b/c, a/b, a. 304ecd6cf80Smarks */ 305ecd6cf80Smarks int 306ecd6cf80Smarks dsl_deleg_get(const char *ddname, nvlist_t **nvp) 307ecd6cf80Smarks { 308ecd6cf80Smarks dsl_dir_t *dd, *startdd; 309ecd6cf80Smarks dsl_pool_t *dp; 310ecd6cf80Smarks int error; 311ecd6cf80Smarks objset_t *mos; 312ecd6cf80Smarks 3133b2aab18SMatthew Ahrens error = dsl_pool_hold(ddname, FTAG, &dp); 3143b2aab18SMatthew Ahrens if (error != 0) 315ecd6cf80Smarks return (error); 316ecd6cf80Smarks 3173b2aab18SMatthew Ahrens error = dsl_dir_hold(dp, ddname, FTAG, &startdd, NULL); 3183b2aab18SMatthew Ahrens if (error != 0) { 3193b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 3203b2aab18SMatthew Ahrens return (error); 3213b2aab18SMatthew Ahrens } 3223b2aab18SMatthew Ahrens 323ecd6cf80Smarks dp = startdd->dd_pool; 324ecd6cf80Smarks mos = dp->dp_meta_objset; 325ecd6cf80Smarks 326ecd6cf80Smarks VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 327ecd6cf80Smarks 328ecd6cf80Smarks for (dd = startdd; dd != NULL; dd = dd->dd_parent) { 329ecd6cf80Smarks zap_cursor_t basezc; 330ecd6cf80Smarks zap_attribute_t baseza; 331ecd6cf80Smarks nvlist_t *sp_nvp; 332ecd6cf80Smarks uint64_t n; 333*40a5c998SMatthew Ahrens char source[ZFS_MAX_DATASET_NAME_LEN]; 334ecd6cf80Smarks 335c1379625SJustin T. Gibbs if (dsl_dir_phys(dd)->dd_deleg_zapobj == 0 || 336c1379625SJustin T. Gibbs zap_count(mos, 337c1379625SJustin T. Gibbs dsl_dir_phys(dd)->dd_deleg_zapobj, &n) != 0 || n == 0) 338ecd6cf80Smarks continue; 339ecd6cf80Smarks 3403b2aab18SMatthew Ahrens sp_nvp = fnvlist_alloc(); 341ecd6cf80Smarks for (zap_cursor_init(&basezc, mos, 342c1379625SJustin T. Gibbs dsl_dir_phys(dd)->dd_deleg_zapobj); 343ecd6cf80Smarks zap_cursor_retrieve(&basezc, &baseza) == 0; 344ecd6cf80Smarks zap_cursor_advance(&basezc)) { 345ecd6cf80Smarks zap_cursor_t zc; 346ecd6cf80Smarks zap_attribute_t za; 347ecd6cf80Smarks nvlist_t *perms_nvp; 348ecd6cf80Smarks 349ecd6cf80Smarks ASSERT(baseza.za_integer_length == 8); 350ecd6cf80Smarks ASSERT(baseza.za_num_integers == 1); 351ecd6cf80Smarks 3523b2aab18SMatthew Ahrens perms_nvp = fnvlist_alloc(); 353ecd6cf80Smarks for (zap_cursor_init(&zc, mos, baseza.za_first_integer); 354ecd6cf80Smarks zap_cursor_retrieve(&zc, &za) == 0; 355ecd6cf80Smarks zap_cursor_advance(&zc)) { 3563b2aab18SMatthew Ahrens fnvlist_add_boolean(perms_nvp, za.za_name); 357ecd6cf80Smarks } 358ecd6cf80Smarks zap_cursor_fini(&zc); 3593b2aab18SMatthew Ahrens fnvlist_add_nvlist(sp_nvp, baseza.za_name, perms_nvp); 3603b2aab18SMatthew Ahrens fnvlist_free(perms_nvp); 361ecd6cf80Smarks } 362ecd6cf80Smarks 363ecd6cf80Smarks zap_cursor_fini(&basezc); 364ecd6cf80Smarks 365ecd6cf80Smarks dsl_dir_name(dd, source); 3663b2aab18SMatthew Ahrens fnvlist_add_nvlist(*nvp, source, sp_nvp); 367ecd6cf80Smarks nvlist_free(sp_nvp); 368ecd6cf80Smarks } 369ecd6cf80Smarks 3703b2aab18SMatthew Ahrens dsl_dir_rele(startdd, FTAG); 3713b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 372ecd6cf80Smarks return (0); 373ecd6cf80Smarks } 374ecd6cf80Smarks 375ecd6cf80Smarks /* 376ecd6cf80Smarks * Routines for dsl_deleg_access() -- access checking. 377ecd6cf80Smarks */ 378ecd6cf80Smarks typedef struct perm_set { 379ecd6cf80Smarks avl_node_t p_node; 380ecd6cf80Smarks boolean_t p_matched; 38191ebeef5Sahrens char p_setname[ZFS_MAX_DELEG_NAME]; 382ecd6cf80Smarks } perm_set_t; 383ecd6cf80Smarks 384ecd6cf80Smarks static int 385ecd6cf80Smarks perm_set_compare(const void *arg1, const void *arg2) 386ecd6cf80Smarks { 387ecd6cf80Smarks const perm_set_t *node1 = arg1; 388ecd6cf80Smarks const perm_set_t *node2 = arg2; 389ecd6cf80Smarks int val; 390ecd6cf80Smarks 391ecd6cf80Smarks val = strcmp(node1->p_setname, node2->p_setname); 392ecd6cf80Smarks if (val == 0) 393ecd6cf80Smarks return (0); 394ecd6cf80Smarks return (val > 0 ? 1 : -1); 395ecd6cf80Smarks } 396ecd6cf80Smarks 397ecd6cf80Smarks /* 398ecd6cf80Smarks * Determine whether a specified permission exists. 399ecd6cf80Smarks * 400ecd6cf80Smarks * First the base attribute has to be retrieved. i.e. ul$12 401ecd6cf80Smarks * Once the base object has been retrieved the actual permission 402ecd6cf80Smarks * is lookup up in the zap object the base object points to. 403ecd6cf80Smarks * 404ecd6cf80Smarks * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if 405ecd6cf80Smarks * there is no perm in that jumpobj. 406ecd6cf80Smarks */ 407ecd6cf80Smarks static int 408ecd6cf80Smarks dsl_check_access(objset_t *mos, uint64_t zapobj, 409ecd6cf80Smarks char type, char checkflag, void *valp, const char *perm) 410ecd6cf80Smarks { 411ecd6cf80Smarks int error; 412ecd6cf80Smarks uint64_t jumpobj, zero; 413ecd6cf80Smarks char whokey[ZFS_MAX_DELEG_NAME]; 414ecd6cf80Smarks 415ecd6cf80Smarks zfs_deleg_whokey(whokey, type, checkflag, valp); 416ecd6cf80Smarks error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 417ecd6cf80Smarks if (error == 0) { 418ecd6cf80Smarks error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero); 419ecd6cf80Smarks if (error == ENOENT) 420be6fd75aSMatthew Ahrens error = SET_ERROR(EPERM); 421ecd6cf80Smarks } 422ecd6cf80Smarks return (error); 423ecd6cf80Smarks } 424ecd6cf80Smarks 425ecd6cf80Smarks /* 426ecd6cf80Smarks * check a specified user/group for a requested permission 427ecd6cf80Smarks */ 428ecd6cf80Smarks static int 42991ebeef5Sahrens dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm, 430ecd6cf80Smarks int checkflag, cred_t *cr) 431ecd6cf80Smarks { 432ecd6cf80Smarks const gid_t *gids; 433ecd6cf80Smarks int ngids; 434ecd6cf80Smarks int i; 435ecd6cf80Smarks uint64_t id; 436ecd6cf80Smarks 437ecd6cf80Smarks /* check for user */ 438ecd6cf80Smarks id = crgetuid(cr); 43991ebeef5Sahrens if (dsl_check_access(mos, zapobj, 440ecd6cf80Smarks ZFS_DELEG_USER, checkflag, &id, perm) == 0) 441ecd6cf80Smarks return (0); 442ecd6cf80Smarks 443ecd6cf80Smarks /* check for users primary group */ 444ecd6cf80Smarks id = crgetgid(cr); 44591ebeef5Sahrens if (dsl_check_access(mos, zapobj, 446ecd6cf80Smarks ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 447ecd6cf80Smarks return (0); 448ecd6cf80Smarks 449ecd6cf80Smarks /* check for everyone entry */ 450ecd6cf80Smarks id = -1; 45191ebeef5Sahrens if (dsl_check_access(mos, zapobj, 452ecd6cf80Smarks ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0) 453ecd6cf80Smarks return (0); 454ecd6cf80Smarks 455ecd6cf80Smarks /* check each supplemental group user is a member of */ 456ecd6cf80Smarks ngids = crgetngroups(cr); 457ecd6cf80Smarks gids = crgetgroups(cr); 458ecd6cf80Smarks for (i = 0; i != ngids; i++) { 459ecd6cf80Smarks id = gids[i]; 46091ebeef5Sahrens if (dsl_check_access(mos, zapobj, 461ecd6cf80Smarks ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 462ecd6cf80Smarks return (0); 463ecd6cf80Smarks } 464ecd6cf80Smarks 465be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 466ecd6cf80Smarks } 467ecd6cf80Smarks 468ecd6cf80Smarks /* 469ecd6cf80Smarks * Iterate over the sets specified in the specified zapobj 470ecd6cf80Smarks * and load them into the permsets avl tree. 471ecd6cf80Smarks */ 472ecd6cf80Smarks static int 473ecd6cf80Smarks dsl_load_sets(objset_t *mos, uint64_t zapobj, 474ecd6cf80Smarks char type, char checkflag, void *valp, avl_tree_t *avl) 475ecd6cf80Smarks { 476ecd6cf80Smarks zap_cursor_t zc; 477ecd6cf80Smarks zap_attribute_t za; 478ecd6cf80Smarks perm_set_t *permnode; 479ecd6cf80Smarks avl_index_t idx; 480ecd6cf80Smarks uint64_t jumpobj; 481ecd6cf80Smarks int error; 482ecd6cf80Smarks char whokey[ZFS_MAX_DELEG_NAME]; 483ecd6cf80Smarks 484ecd6cf80Smarks zfs_deleg_whokey(whokey, type, checkflag, valp); 485ecd6cf80Smarks 486ecd6cf80Smarks error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 487ecd6cf80Smarks if (error != 0) 488ecd6cf80Smarks return (error); 489ecd6cf80Smarks 490ecd6cf80Smarks for (zap_cursor_init(&zc, mos, jumpobj); 491ecd6cf80Smarks zap_cursor_retrieve(&zc, &za) == 0; 492ecd6cf80Smarks zap_cursor_advance(&zc)) { 493ecd6cf80Smarks permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP); 494ecd6cf80Smarks (void) strlcpy(permnode->p_setname, za.za_name, 495ecd6cf80Smarks sizeof (permnode->p_setname)); 496ecd6cf80Smarks permnode->p_matched = B_FALSE; 497ecd6cf80Smarks 498ecd6cf80Smarks if (avl_find(avl, permnode, &idx) == NULL) { 499ecd6cf80Smarks avl_insert(avl, permnode, idx); 500ecd6cf80Smarks } else { 501ecd6cf80Smarks kmem_free(permnode, sizeof (perm_set_t)); 502ecd6cf80Smarks } 503ecd6cf80Smarks } 504ecd6cf80Smarks zap_cursor_fini(&zc); 505ecd6cf80Smarks return (0); 506ecd6cf80Smarks } 507ecd6cf80Smarks 508ecd6cf80Smarks /* 509ecd6cf80Smarks * Load all permissions user based on cred belongs to. 510ecd6cf80Smarks */ 511ecd6cf80Smarks static void 512ecd6cf80Smarks dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl, 513ecd6cf80Smarks char checkflag, cred_t *cr) 514ecd6cf80Smarks { 515ecd6cf80Smarks const gid_t *gids; 516ecd6cf80Smarks int ngids, i; 517ecd6cf80Smarks uint64_t id; 518ecd6cf80Smarks 519ecd6cf80Smarks id = crgetuid(cr); 520ecd6cf80Smarks (void) dsl_load_sets(mos, zapobj, 521ecd6cf80Smarks ZFS_DELEG_USER_SETS, checkflag, &id, avl); 522ecd6cf80Smarks 523ecd6cf80Smarks id = crgetgid(cr); 524ecd6cf80Smarks (void) dsl_load_sets(mos, zapobj, 525ecd6cf80Smarks ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 526ecd6cf80Smarks 527ecd6cf80Smarks (void) dsl_load_sets(mos, zapobj, 528ecd6cf80Smarks ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl); 529ecd6cf80Smarks 530ecd6cf80Smarks ngids = crgetngroups(cr); 531ecd6cf80Smarks gids = crgetgroups(cr); 532ecd6cf80Smarks for (i = 0; i != ngids; i++) { 533ecd6cf80Smarks id = gids[i]; 534ecd6cf80Smarks (void) dsl_load_sets(mos, zapobj, 535ecd6cf80Smarks ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 536ecd6cf80Smarks } 537ecd6cf80Smarks } 538ecd6cf80Smarks 539ecd6cf80Smarks /* 5404445fffbSMatthew Ahrens * Check if user has requested permission. 541ecd6cf80Smarks */ 542ecd6cf80Smarks int 5434445fffbSMatthew Ahrens dsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr) 544ecd6cf80Smarks { 545bb0ade09Sahrens dsl_dir_t *dd; 546ecd6cf80Smarks dsl_pool_t *dp; 547ecd6cf80Smarks void *cookie; 548ecd6cf80Smarks int error; 5498b33e213SWilliam Gorrell char checkflag; 550ecd6cf80Smarks objset_t *mos; 551ecd6cf80Smarks avl_tree_t permsets; 552ecd6cf80Smarks perm_set_t *setnode; 553ecd6cf80Smarks 554bb0ade09Sahrens dp = ds->ds_dir->dd_pool; 555ecd6cf80Smarks mos = dp->dp_meta_objset; 556ecd6cf80Smarks 557a7f53a56SChris Kirby if (dsl_delegation_on(mos) == B_FALSE) 558be6fd75aSMatthew Ahrens return (SET_ERROR(ECANCELED)); 559ecd6cf80Smarks 560ecd6cf80Smarks if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) < 561a7f53a56SChris Kirby SPA_VERSION_DELEGATED_PERMS) 562be6fd75aSMatthew Ahrens return (SET_ERROR(EPERM)); 563ecd6cf80Smarks 564bc9014e6SJustin Gibbs if (ds->ds_is_snapshot) { 5658b33e213SWilliam Gorrell /* 5668b33e213SWilliam Gorrell * Snapshots are treated as descendents only, 5678b33e213SWilliam Gorrell * local permissions do not apply. 5688b33e213SWilliam Gorrell */ 5698b33e213SWilliam Gorrell checkflag = ZFS_DELEG_DESCENDENT; 5708b33e213SWilliam Gorrell } else { 5718b33e213SWilliam Gorrell checkflag = ZFS_DELEG_LOCAL; 5728b33e213SWilliam Gorrell } 5738b33e213SWilliam Gorrell 574ecd6cf80Smarks avl_create(&permsets, perm_set_compare, sizeof (perm_set_t), 575ecd6cf80Smarks offsetof(perm_set_t, p_node)); 576ecd6cf80Smarks 5773b2aab18SMatthew Ahrens ASSERT(dsl_pool_config_held(dp)); 578bb0ade09Sahrens for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent, 579ecd6cf80Smarks checkflag = ZFS_DELEG_DESCENDENT) { 580ecd6cf80Smarks uint64_t zapobj; 581ecd6cf80Smarks boolean_t expanded; 582ecd6cf80Smarks 583ecd6cf80Smarks /* 584ecd6cf80Smarks * If not in global zone then make sure 585ecd6cf80Smarks * the zoned property is set 586ecd6cf80Smarks */ 587ecd6cf80Smarks if (!INGLOBALZONE(curproc)) { 588ecd6cf80Smarks uint64_t zoned; 589ecd6cf80Smarks 590bb0ade09Sahrens if (dsl_prop_get_dd(dd, 591ecd6cf80Smarks zfs_prop_to_name(ZFS_PROP_ZONED), 59292241e0bSTom Erickson 8, 1, &zoned, NULL, B_FALSE) != 0) 593ecd6cf80Smarks break; 594ecd6cf80Smarks if (!zoned) 595ecd6cf80Smarks break; 596ecd6cf80Smarks } 597c1379625SJustin T. Gibbs zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 598ecd6cf80Smarks 599ecd6cf80Smarks if (zapobj == 0) 600ecd6cf80Smarks continue; 601ecd6cf80Smarks 602ecd6cf80Smarks dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr); 603ecd6cf80Smarks again: 604ecd6cf80Smarks expanded = B_FALSE; 605ecd6cf80Smarks for (setnode = avl_first(&permsets); setnode; 606ecd6cf80Smarks setnode = AVL_NEXT(&permsets, setnode)) { 607ecd6cf80Smarks if (setnode->p_matched == B_TRUE) 608ecd6cf80Smarks continue; 609ecd6cf80Smarks 610ecd6cf80Smarks /* See if this set directly grants this permission */ 611ecd6cf80Smarks error = dsl_check_access(mos, zapobj, 612ecd6cf80Smarks ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm); 613ecd6cf80Smarks if (error == 0) 614ecd6cf80Smarks goto success; 615ecd6cf80Smarks if (error == EPERM) 616ecd6cf80Smarks setnode->p_matched = B_TRUE; 617ecd6cf80Smarks 618ecd6cf80Smarks /* See if this set includes other sets */ 619ecd6cf80Smarks error = dsl_load_sets(mos, zapobj, 620ecd6cf80Smarks ZFS_DELEG_NAMED_SET_SETS, 0, 621ecd6cf80Smarks setnode->p_setname, &permsets); 622ecd6cf80Smarks if (error == 0) 623ecd6cf80Smarks setnode->p_matched = expanded = B_TRUE; 624ecd6cf80Smarks } 625ecd6cf80Smarks /* 626ecd6cf80Smarks * If we expanded any sets, that will define more sets, 627ecd6cf80Smarks * which we need to check. 628ecd6cf80Smarks */ 629ecd6cf80Smarks if (expanded) 630ecd6cf80Smarks goto again; 631ecd6cf80Smarks 632ecd6cf80Smarks error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr); 633ecd6cf80Smarks if (error == 0) 634ecd6cf80Smarks goto success; 635ecd6cf80Smarks } 636be6fd75aSMatthew Ahrens error = SET_ERROR(EPERM); 637ecd6cf80Smarks success: 638ecd6cf80Smarks 639ecd6cf80Smarks cookie = NULL; 64091ebeef5Sahrens while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) 641ecd6cf80Smarks kmem_free(setnode, sizeof (perm_set_t)); 642ecd6cf80Smarks 643ecd6cf80Smarks return (error); 644ecd6cf80Smarks } 645ecd6cf80Smarks 646a7f53a56SChris Kirby int 647a7f53a56SChris Kirby dsl_deleg_access(const char *dsname, const char *perm, cred_t *cr) 648a7f53a56SChris Kirby { 6493b2aab18SMatthew Ahrens dsl_pool_t *dp; 650a7f53a56SChris Kirby dsl_dataset_t *ds; 651a7f53a56SChris Kirby int error; 652a7f53a56SChris Kirby 6533b2aab18SMatthew Ahrens error = dsl_pool_hold(dsname, FTAG, &dp); 6543b2aab18SMatthew Ahrens if (error != 0) 655a7f53a56SChris Kirby return (error); 6563b2aab18SMatthew Ahrens error = dsl_dataset_hold(dp, dsname, FTAG, &ds); 6573b2aab18SMatthew Ahrens if (error == 0) { 6584445fffbSMatthew Ahrens error = dsl_deleg_access_impl(ds, perm, cr); 659a7f53a56SChris Kirby dsl_dataset_rele(ds, FTAG); 6603b2aab18SMatthew Ahrens } 6613b2aab18SMatthew Ahrens dsl_pool_rele(dp, FTAG); 662a7f53a56SChris Kirby 663a7f53a56SChris Kirby return (error); 664a7f53a56SChris Kirby } 665a7f53a56SChris Kirby 666ecd6cf80Smarks /* 667ecd6cf80Smarks * Other routines. 668ecd6cf80Smarks */ 669ecd6cf80Smarks 670ecd6cf80Smarks static void 67191ebeef5Sahrens copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj, 672ecd6cf80Smarks boolean_t dosets, uint64_t uid, dmu_tx_t *tx) 673ecd6cf80Smarks { 67491ebeef5Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 675ecd6cf80Smarks uint64_t jumpobj, pjumpobj; 676c1379625SJustin T. Gibbs uint64_t zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 677ecd6cf80Smarks zap_cursor_t zc; 678ecd6cf80Smarks zap_attribute_t za; 679ecd6cf80Smarks char whokey[ZFS_MAX_DELEG_NAME]; 680ecd6cf80Smarks 681ecd6cf80Smarks zfs_deleg_whokey(whokey, 682ecd6cf80Smarks dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE, 683ecd6cf80Smarks ZFS_DELEG_LOCAL, NULL); 68491ebeef5Sahrens if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0) 685ecd6cf80Smarks return; 686ecd6cf80Smarks 687ecd6cf80Smarks if (zapobj == 0) { 688ecd6cf80Smarks dmu_buf_will_dirty(dd->dd_dbuf, tx); 689c1379625SJustin T. Gibbs zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos, 690ecd6cf80Smarks DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 691ecd6cf80Smarks } 692ecd6cf80Smarks 69391ebeef5Sahrens zfs_deleg_whokey(whokey, 69491ebeef5Sahrens dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER, 69591ebeef5Sahrens ZFS_DELEG_LOCAL, &uid); 696ecd6cf80Smarks if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) { 697ecd6cf80Smarks jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 698ecd6cf80Smarks VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0); 699ecd6cf80Smarks } 700ecd6cf80Smarks 701ecd6cf80Smarks for (zap_cursor_init(&zc, mos, pjumpobj); 702ecd6cf80Smarks zap_cursor_retrieve(&zc, &za) == 0; 703ecd6cf80Smarks zap_cursor_advance(&zc)) { 70491ebeef5Sahrens uint64_t zero = 0; 705ecd6cf80Smarks ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 706ecd6cf80Smarks 707ecd6cf80Smarks VERIFY(zap_update(mos, jumpobj, za.za_name, 708ecd6cf80Smarks 8, 1, &zero, tx) == 0); 709ecd6cf80Smarks } 710ecd6cf80Smarks zap_cursor_fini(&zc); 711ecd6cf80Smarks } 712ecd6cf80Smarks 713ecd6cf80Smarks /* 714ecd6cf80Smarks * set all create time permission on new dataset. 715ecd6cf80Smarks */ 716ecd6cf80Smarks void 717ecd6cf80Smarks dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr) 718ecd6cf80Smarks { 719ecd6cf80Smarks dsl_dir_t *dd; 72091ebeef5Sahrens uint64_t uid = crgetuid(cr); 721ecd6cf80Smarks 722ecd6cf80Smarks if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) < 723990b4856Slling SPA_VERSION_DELEGATED_PERMS) 724ecd6cf80Smarks return; 725ecd6cf80Smarks 726ecd6cf80Smarks for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) { 727c1379625SJustin T. Gibbs uint64_t pzapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 728ecd6cf80Smarks 72991ebeef5Sahrens if (pzapobj == 0) 730ecd6cf80Smarks continue; 731ecd6cf80Smarks 73291ebeef5Sahrens copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx); 73391ebeef5Sahrens copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx); 734ecd6cf80Smarks } 735ecd6cf80Smarks } 736ecd6cf80Smarks 737ecd6cf80Smarks int 738ecd6cf80Smarks dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx) 739ecd6cf80Smarks { 740ecd6cf80Smarks zap_cursor_t zc; 741ecd6cf80Smarks zap_attribute_t za; 742ecd6cf80Smarks 743ecd6cf80Smarks if (zapobj == 0) 744ecd6cf80Smarks return (0); 745ecd6cf80Smarks 746ecd6cf80Smarks for (zap_cursor_init(&zc, mos, zapobj); 747ecd6cf80Smarks zap_cursor_retrieve(&zc, &za) == 0; 748ecd6cf80Smarks zap_cursor_advance(&zc)) { 749ecd6cf80Smarks ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 750ecd6cf80Smarks VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx)); 751ecd6cf80Smarks } 752ecd6cf80Smarks zap_cursor_fini(&zc); 753ecd6cf80Smarks VERIFY(0 == zap_destroy(mos, zapobj, tx)); 754ecd6cf80Smarks return (0); 755ecd6cf80Smarks } 756ecd6cf80Smarks 757ecd6cf80Smarks boolean_t 758ecd6cf80Smarks dsl_delegation_on(objset_t *os) 759ecd6cf80Smarks { 760b24ab676SJeff Bonwick return (!!spa_delegation(os->os_spa)); 761ecd6cf80Smarks } 762