14543Smarks /* 24543Smarks * CDDL HEADER START 34543Smarks * 44543Smarks * The contents of this file are subject to the terms of the 54543Smarks * Common Development and Distribution License (the "License"). 64543Smarks * You may not use this file except in compliance with the License. 74543Smarks * 84543Smarks * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94543Smarks * or http://www.opensolaris.org/os/licensing. 104543Smarks * See the License for the specific language governing permissions 114543Smarks * and limitations under the License. 124543Smarks * 134543Smarks * When distributing Covered Code, include this CDDL HEADER in each 144543Smarks * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154543Smarks * If applicable, add the following below this CDDL HEADER, with the 164543Smarks * fields enclosed by brackets "[]" replaced with your own identifying 174543Smarks * information: Portions Copyright [yyyy] [name of copyright owner] 184543Smarks * 194543Smarks * CDDL HEADER END 204543Smarks */ 214543Smarks /* 224543Smarks * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 234543Smarks * Use is subject to license terms. 244543Smarks */ 254543Smarks 264543Smarks /* 274543Smarks * DSL permissions are stored in a two level zap attribute 284543Smarks * mechanism. The first level identifies the "class" of 294543Smarks * entry. The class is identified by the first 2 letters of 304543Smarks * the attribute. The second letter "l" or "d" identifies whether 314543Smarks * it is a local or descendent permission. The first letter 324543Smarks * identifies the type of entry. 334543Smarks * 345331Samw * ul$<id> identifies permissions granted locally for this userid. 354543Smarks * ud$<id> identifies permissions granted on descendent datasets for 364543Smarks * this userid. 374543Smarks * Ul$<id> identifies permission sets granted locally for this userid. 384543Smarks * Ud$<id> identifies permission sets granted on descendent datasets for 394543Smarks * this userid. 404543Smarks * gl$<id> identifies permissions granted locally for this groupid. 414543Smarks * gd$<id> identifies permissions granted on descendent datasets for 424543Smarks * this groupid. 434543Smarks * Gl$<id> identifies permission sets granted locally for this groupid. 444543Smarks * Gd$<id> identifies permission sets granted on descendent datasets for 454543Smarks * this groupid. 464543Smarks * el$ identifies permissions granted locally for everyone. 474543Smarks * ed$ identifies permissions granted on descendent datasets 484543Smarks * for everyone. 494543Smarks * El$ identifies permission sets granted locally for everyone. 504543Smarks * Ed$ identifies permission sets granted to descendent datasets for 514543Smarks * everyone. 524543Smarks * c-$ identifies permission to create at dataset creation time. 534543Smarks * C-$ identifies permission sets to grant locally at dataset creation 544543Smarks * time. 554543Smarks * s-$@<name> permissions defined in specified set @<name> 564543Smarks * S-$@<name> Sets defined in named set @<name> 574543Smarks * 585331Samw * Each of the above entities points to another zap attribute that contains one 594543Smarks * attribute for each allowed permission, such as create, destroy,... 604543Smarks * All of the "upper" case class types will specify permission set names 614543Smarks * rather than permissions. 624543Smarks * 634543Smarks * Basically it looks something like this: 644543Smarks * ul$12 -> ZAP OBJ -> permissions... 654543Smarks * 664543Smarks * The ZAP OBJ is referred to as the jump object. 674543Smarks */ 684543Smarks 694543Smarks #pragma ident "%Z%%M% %I% %E% SMI" 704543Smarks 714543Smarks #include <sys/dmu.h> 724543Smarks #include <sys/dmu_objset.h> 734543Smarks #include <sys/dmu_tx.h> 744543Smarks #include <sys/dsl_dataset.h> 754543Smarks #include <sys/dsl_dir.h> 764543Smarks #include <sys/dsl_prop.h> 774543Smarks #include <sys/dsl_synctask.h> 784543Smarks #include <sys/dsl_deleg.h> 794543Smarks #include <sys/spa.h> 804543Smarks #include <sys/spa_impl.h> 814543Smarks #include <sys/zio_checksum.h> /* for the default checksum value */ 824543Smarks #include <sys/zap.h> 834543Smarks #include <sys/fs/zfs.h> 844543Smarks #include <sys/cred.h> 854543Smarks #include <sys/sunddi.h> 864543Smarks 874543Smarks #include "zfs_deleg.h" 884543Smarks 894543Smarks /* 904543Smarks * Validate that user is allowed to delegate specified permissions. 914543Smarks * 924787Sahrens * In order to delegate "create" you must have "create" 934543Smarks * and "allow". 944543Smarks */ 954543Smarks int 964543Smarks dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr) 974543Smarks { 984543Smarks nvpair_t *whopair = NULL; 994787Sahrens int error; 1004543Smarks 1014787Sahrens if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0) 1024543Smarks return (error); 1034543Smarks 1044543Smarks while (whopair = nvlist_next_nvpair(nvp, whopair)) { 1054543Smarks nvlist_t *perms; 1064543Smarks nvpair_t *permpair = NULL; 1074543Smarks 1084543Smarks VERIFY(nvpair_value_nvlist(whopair, &perms) == 0); 1094543Smarks 1104543Smarks while (permpair = nvlist_next_nvpair(perms, permpair)) { 1114543Smarks const char *perm = nvpair_name(permpair); 1124543Smarks 1134543Smarks if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0) 1144543Smarks return (EPERM); 1154543Smarks 1164787Sahrens if ((error = dsl_deleg_access(ddname, perm, cr)) != 0) 1174543Smarks return (error); 1184543Smarks } 1194543Smarks } 1204787Sahrens return (0); 1214543Smarks } 1224543Smarks 1234543Smarks /* 1244543Smarks * Validate that user is allowed to unallow specified permissions. They 1254543Smarks * must have the 'allow' permission, and even then can only unallow 1264543Smarks * perms for their uid. 1274543Smarks */ 1284543Smarks int 1294543Smarks dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr) 1304543Smarks { 1314543Smarks nvpair_t *whopair = NULL; 1324543Smarks int error; 1334787Sahrens char idstr[32]; 1344543Smarks 1354787Sahrens if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0) 1364543Smarks return (error); 1374543Smarks 1384787Sahrens (void) snprintf(idstr, sizeof (idstr), "%lld", 1394787Sahrens (longlong_t)crgetuid(cr)); 1404787Sahrens 1414543Smarks while (whopair = nvlist_next_nvpair(nvp, whopair)) { 1424543Smarks zfs_deleg_who_type_t type = nvpair_name(whopair)[0]; 1434543Smarks 1444543Smarks if (type != ZFS_DELEG_USER && 1454543Smarks type != ZFS_DELEG_USER_SETS) 1464543Smarks return (EPERM); 1474543Smarks 1484543Smarks if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0) 1494543Smarks return (EPERM); 1504543Smarks } 1514543Smarks return (0); 1524543Smarks } 1534543Smarks 1544543Smarks static void 1554543Smarks dsl_deleg_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 1564543Smarks { 1574543Smarks dsl_dir_t *dd = arg1; 158*5367Sahrens nvlist_t *nvp = arg2; 1594543Smarks objset_t *mos = dd->dd_pool->dp_meta_objset; 1604543Smarks nvpair_t *whopair = NULL; 1614543Smarks uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj; 1624543Smarks 1634543Smarks if (zapobj == 0) { 1644543Smarks dmu_buf_will_dirty(dd->dd_dbuf, tx); 1654543Smarks zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos, 1664543Smarks DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 1674543Smarks } 1684543Smarks 169*5367Sahrens while (whopair = nvlist_next_nvpair(nvp, whopair)) { 170*5367Sahrens const char *whokey = nvpair_name(whopair); 171*5367Sahrens nvlist_t *perms; 172*5367Sahrens nvpair_t *permpair = NULL; 173*5367Sahrens uint64_t jumpobj; 174*5367Sahrens 175*5367Sahrens VERIFY(nvpair_value_nvlist(whopair, &perms) == 0); 176*5367Sahrens 177*5367Sahrens if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) { 178*5367Sahrens jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, 179*5367Sahrens DMU_OT_NONE, 0, tx); 180*5367Sahrens VERIFY(zap_update(mos, zapobj, 181*5367Sahrens whokey, 8, 1, &jumpobj, tx) == 0); 182*5367Sahrens } 183*5367Sahrens 184*5367Sahrens while (permpair = nvlist_next_nvpair(perms, permpair)) { 185*5367Sahrens const char *perm = nvpair_name(permpair); 186*5367Sahrens uint64_t n = 0; 187*5367Sahrens 188*5367Sahrens VERIFY(zap_update(mos, jumpobj, 189*5367Sahrens perm, 8, 1, &n, tx) == 0); 190*5367Sahrens spa_history_internal_log(LOG_DS_PERM_UPDATE, 191*5367Sahrens dd->dd_pool->dp_spa, tx, cr, 192*5367Sahrens "%s %s dataset = %llu", whokey, perm, 193*5367Sahrens dd->dd_phys->dd_head_dataset_obj); 194*5367Sahrens } 195*5367Sahrens } 196*5367Sahrens } 197*5367Sahrens 198*5367Sahrens static void 199*5367Sahrens dsl_deleg_unset_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx) 200*5367Sahrens { 201*5367Sahrens dsl_dir_t *dd = arg1; 202*5367Sahrens nvlist_t *nvp = arg2; 203*5367Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 204*5367Sahrens nvpair_t *whopair = NULL; 205*5367Sahrens uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj; 206*5367Sahrens 207*5367Sahrens if (zapobj == 0) 208*5367Sahrens return; 209*5367Sahrens 210*5367Sahrens while (whopair = nvlist_next_nvpair(nvp, whopair)) { 2114543Smarks const char *whokey = nvpair_name(whopair); 2124543Smarks nvlist_t *perms; 2134543Smarks nvpair_t *permpair = NULL; 2144543Smarks uint64_t jumpobj; 2154543Smarks 2164543Smarks if (nvpair_value_nvlist(whopair, &perms) != 0) { 2174543Smarks if (zap_lookup(mos, zapobj, whokey, 8, 2184543Smarks 1, &jumpobj) == 0) { 2194543Smarks (void) zap_remove(mos, zapobj, whokey, tx); 2204543Smarks VERIFY(0 == zap_destroy(mos, jumpobj, tx)); 2214543Smarks } 2224543Smarks spa_history_internal_log(LOG_DS_PERM_WHO_REMOVE, 2234543Smarks dd->dd_pool->dp_spa, tx, cr, 2244543Smarks "%s dataset = %llu", whokey, 2254543Smarks dd->dd_phys->dd_head_dataset_obj); 2264543Smarks continue; 2274543Smarks } 2284543Smarks 229*5367Sahrens if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) 230*5367Sahrens continue; 2314543Smarks 2324543Smarks while (permpair = nvlist_next_nvpair(perms, permpair)) { 2334543Smarks const char *perm = nvpair_name(permpair); 2344543Smarks uint64_t n = 0; 2354543Smarks 236*5367Sahrens (void) zap_remove(mos, jumpobj, perm, tx); 237*5367Sahrens if (zap_count(mos, jumpobj, &n) == 0 && n == 0) { 238*5367Sahrens (void) zap_remove(mos, zapobj, 239*5367Sahrens whokey, tx); 240*5367Sahrens VERIFY(0 == zap_destroy(mos, 241*5367Sahrens jumpobj, tx)); 2424543Smarks } 243*5367Sahrens spa_history_internal_log(LOG_DS_PERM_REMOVE, 2444543Smarks dd->dd_pool->dp_spa, tx, cr, 2454543Smarks "%s %s dataset = %llu", whokey, perm, 2464543Smarks dd->dd_phys->dd_head_dataset_obj); 2474543Smarks } 2484543Smarks } 2494543Smarks } 2504543Smarks 2514543Smarks int 2524543Smarks dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset) 2534543Smarks { 2544543Smarks dsl_dir_t *dd; 2554543Smarks int error; 2564543Smarks nvpair_t *whopair = NULL; 2574543Smarks int blocks_modified = 0; 2584543Smarks 2594543Smarks error = dsl_dir_open(ddname, FTAG, &dd, NULL); 2604543Smarks if (error) 2614543Smarks return (error); 2624543Smarks 2634543Smarks if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) < 2645094Slling SPA_VERSION_DELEGATED_PERMS) { 2654543Smarks dsl_dir_close(dd, FTAG); 2664543Smarks return (ENOTSUP); 2674543Smarks } 2684543Smarks 2694543Smarks while (whopair = nvlist_next_nvpair(nvp, whopair)) 2704543Smarks blocks_modified++; 2714543Smarks 272*5367Sahrens error = dsl_sync_task_do(dd->dd_pool, NULL, 273*5367Sahrens unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync, 274*5367Sahrens dd, nvp, blocks_modified); 2754543Smarks dsl_dir_close(dd, FTAG); 2764543Smarks 2774543Smarks return (error); 2784543Smarks } 2794543Smarks 2804543Smarks /* 2814543Smarks * Find all 'allow' permissions from a given point and then continue 2824543Smarks * traversing up to the root. 2834543Smarks * 2844543Smarks * This function constructs an nvlist of nvlists. 2854543Smarks * each setpoint is an nvlist composed of an nvlist of an nvlist 2864543Smarks * of the individual * users/groups/everyone/create 2874543Smarks * permissions. 2884543Smarks * 2894543Smarks * The nvlist will look like this. 2904543Smarks * 2914543Smarks * { source fsname -> { whokeys { permissions,...}, ...}} 2924543Smarks * 2934543Smarks * The fsname nvpairs will be arranged in a bottom up order. For example, 2944543Smarks * if we have the following structure a/b/c then the nvpairs for the fsnames 2954543Smarks * will be ordered a/b/c, a/b, a. 2964543Smarks */ 2974543Smarks int 2984543Smarks dsl_deleg_get(const char *ddname, nvlist_t **nvp) 2994543Smarks { 3004543Smarks dsl_dir_t *dd, *startdd; 3014543Smarks dsl_pool_t *dp; 3024543Smarks int error; 3034543Smarks objset_t *mos; 3044543Smarks 3054543Smarks error = dsl_dir_open(ddname, FTAG, &startdd, NULL); 3064543Smarks if (error) 3074543Smarks return (error); 3084543Smarks 3094543Smarks dp = startdd->dd_pool; 3104543Smarks mos = dp->dp_meta_objset; 3114543Smarks 3124543Smarks VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 3134543Smarks 3144543Smarks rw_enter(&dp->dp_config_rwlock, RW_READER); 3154543Smarks for (dd = startdd; dd != NULL; dd = dd->dd_parent) { 3164543Smarks zap_cursor_t basezc; 3174543Smarks zap_attribute_t baseza; 3184543Smarks nvlist_t *sp_nvp; 3194543Smarks uint64_t n; 3204543Smarks char source[MAXNAMELEN]; 3214543Smarks 3224543Smarks if (dd->dd_phys->dd_deleg_zapobj && 3234543Smarks (zap_count(mos, dd->dd_phys->dd_deleg_zapobj, 3244543Smarks &n) == 0) && n) { 3254543Smarks VERIFY(nvlist_alloc(&sp_nvp, 3264543Smarks NV_UNIQUE_NAME, KM_SLEEP) == 0); 3274543Smarks } else { 3284543Smarks continue; 3294543Smarks } 3304543Smarks 3314543Smarks for (zap_cursor_init(&basezc, mos, 3324543Smarks dd->dd_phys->dd_deleg_zapobj); 3334543Smarks zap_cursor_retrieve(&basezc, &baseza) == 0; 3344543Smarks zap_cursor_advance(&basezc)) { 3354543Smarks zap_cursor_t zc; 3364543Smarks zap_attribute_t za; 3374543Smarks nvlist_t *perms_nvp; 3384543Smarks 3394543Smarks ASSERT(baseza.za_integer_length == 8); 3404543Smarks ASSERT(baseza.za_num_integers == 1); 3414543Smarks 3424543Smarks VERIFY(nvlist_alloc(&perms_nvp, 3434543Smarks NV_UNIQUE_NAME, KM_SLEEP) == 0); 3444543Smarks for (zap_cursor_init(&zc, mos, baseza.za_first_integer); 3454543Smarks zap_cursor_retrieve(&zc, &za) == 0; 3464543Smarks zap_cursor_advance(&zc)) { 3474543Smarks VERIFY(nvlist_add_boolean(perms_nvp, 3484543Smarks za.za_name) == 0); 3494543Smarks } 3504543Smarks zap_cursor_fini(&zc); 3514543Smarks VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name, 3524543Smarks perms_nvp) == 0); 3534543Smarks nvlist_free(perms_nvp); 3544543Smarks } 3554543Smarks 3564543Smarks zap_cursor_fini(&basezc); 3574543Smarks 3584543Smarks dsl_dir_name(dd, source); 3594543Smarks VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0); 3604543Smarks nvlist_free(sp_nvp); 3614543Smarks } 3624543Smarks rw_exit(&dp->dp_config_rwlock); 3634543Smarks 3644543Smarks dsl_dir_close(startdd, FTAG); 3654543Smarks return (0); 3664543Smarks } 3674543Smarks 3684543Smarks /* 3694543Smarks * Routines for dsl_deleg_access() -- access checking. 3704543Smarks */ 3714543Smarks typedef struct perm_set { 3724543Smarks avl_node_t p_node; 3734787Sahrens boolean_t p_matched; 3744543Smarks char p_setname[ZFS_MAX_DELEG_NAME]; 3754543Smarks } perm_set_t; 3764543Smarks 3774543Smarks static int 3784543Smarks perm_set_compare(const void *arg1, const void *arg2) 3794543Smarks { 3804543Smarks const perm_set_t *node1 = arg1; 3814543Smarks const perm_set_t *node2 = arg2; 3824543Smarks int val; 3834543Smarks 3844543Smarks val = strcmp(node1->p_setname, node2->p_setname); 3854543Smarks if (val == 0) 3864543Smarks return (0); 3874543Smarks return (val > 0 ? 1 : -1); 3884543Smarks } 3894543Smarks 3904543Smarks /* 3914543Smarks * Determine whether a specified permission exists. 3924543Smarks * 3934543Smarks * First the base attribute has to be retrieved. i.e. ul$12 3944543Smarks * Once the base object has been retrieved the actual permission 3954543Smarks * is lookup up in the zap object the base object points to. 3964543Smarks * 3974543Smarks * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if 3984543Smarks * there is no perm in that jumpobj. 3994543Smarks */ 4004543Smarks static int 4014543Smarks dsl_check_access(objset_t *mos, uint64_t zapobj, 4024543Smarks char type, char checkflag, void *valp, const char *perm) 4034543Smarks { 4044543Smarks int error; 4054543Smarks uint64_t jumpobj, zero; 4064543Smarks char whokey[ZFS_MAX_DELEG_NAME]; 4074543Smarks 4084543Smarks zfs_deleg_whokey(whokey, type, checkflag, valp); 4094543Smarks error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 4104543Smarks if (error == 0) { 4114543Smarks error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero); 4124543Smarks if (error == ENOENT) 4134543Smarks error = EPERM; 4144543Smarks } 4154543Smarks return (error); 4164543Smarks } 4174543Smarks 4184543Smarks /* 4194543Smarks * check a specified user/group for a requested permission 4204543Smarks */ 4214543Smarks static int 4224787Sahrens dsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm, 4234543Smarks int checkflag, cred_t *cr) 4244543Smarks { 4254543Smarks const gid_t *gids; 4264543Smarks int ngids; 4274543Smarks int i; 4284543Smarks uint64_t id; 4294543Smarks 4304543Smarks /* check for user */ 4314543Smarks id = crgetuid(cr); 4324787Sahrens if (dsl_check_access(mos, zapobj, 4334543Smarks ZFS_DELEG_USER, checkflag, &id, perm) == 0) 4344543Smarks return (0); 4354543Smarks 4364543Smarks /* check for users primary group */ 4374543Smarks id = crgetgid(cr); 4384787Sahrens if (dsl_check_access(mos, zapobj, 4394543Smarks ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 4404543Smarks return (0); 4414543Smarks 4424543Smarks /* check for everyone entry */ 4434543Smarks id = -1; 4444787Sahrens if (dsl_check_access(mos, zapobj, 4454543Smarks ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0) 4464543Smarks return (0); 4474543Smarks 4484543Smarks /* check each supplemental group user is a member of */ 4494543Smarks ngids = crgetngroups(cr); 4504543Smarks gids = crgetgroups(cr); 4514543Smarks for (i = 0; i != ngids; i++) { 4524543Smarks id = gids[i]; 4534787Sahrens if (dsl_check_access(mos, zapobj, 4544543Smarks ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 4554543Smarks return (0); 4564543Smarks } 4574543Smarks 4584543Smarks return (EPERM); 4594543Smarks } 4604543Smarks 4614543Smarks /* 4624543Smarks * Iterate over the sets specified in the specified zapobj 4634543Smarks * and load them into the permsets avl tree. 4644543Smarks */ 4654543Smarks static int 4664543Smarks dsl_load_sets(objset_t *mos, uint64_t zapobj, 4674543Smarks char type, char checkflag, void *valp, avl_tree_t *avl) 4684543Smarks { 4694543Smarks zap_cursor_t zc; 4704543Smarks zap_attribute_t za; 4714543Smarks perm_set_t *permnode; 4724543Smarks avl_index_t idx; 4734543Smarks uint64_t jumpobj; 4744543Smarks int error; 4754543Smarks char whokey[ZFS_MAX_DELEG_NAME]; 4764543Smarks 4774543Smarks zfs_deleg_whokey(whokey, type, checkflag, valp); 4784543Smarks 4794543Smarks error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 4804543Smarks if (error != 0) 4814543Smarks return (error); 4824543Smarks 4834543Smarks for (zap_cursor_init(&zc, mos, jumpobj); 4844543Smarks zap_cursor_retrieve(&zc, &za) == 0; 4854543Smarks zap_cursor_advance(&zc)) { 4864543Smarks permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP); 4874543Smarks (void) strlcpy(permnode->p_setname, za.za_name, 4884543Smarks sizeof (permnode->p_setname)); 4894543Smarks permnode->p_matched = B_FALSE; 4904543Smarks 4914543Smarks if (avl_find(avl, permnode, &idx) == NULL) { 4924543Smarks avl_insert(avl, permnode, idx); 4934543Smarks } else { 4944543Smarks kmem_free(permnode, sizeof (perm_set_t)); 4954543Smarks } 4964543Smarks } 4974543Smarks zap_cursor_fini(&zc); 4984543Smarks return (0); 4994543Smarks } 5004543Smarks 5014543Smarks /* 5024543Smarks * Load all permissions user based on cred belongs to. 5034543Smarks */ 5044543Smarks static void 5054543Smarks dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl, 5064543Smarks char checkflag, cred_t *cr) 5074543Smarks { 5084543Smarks const gid_t *gids; 5094543Smarks int ngids, i; 5104543Smarks uint64_t id; 5114543Smarks 5124543Smarks id = crgetuid(cr); 5134543Smarks (void) dsl_load_sets(mos, zapobj, 5144543Smarks ZFS_DELEG_USER_SETS, checkflag, &id, avl); 5154543Smarks 5164543Smarks id = crgetgid(cr); 5174543Smarks (void) dsl_load_sets(mos, zapobj, 5184543Smarks ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 5194543Smarks 5204543Smarks (void) dsl_load_sets(mos, zapobj, 5214543Smarks ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl); 5224543Smarks 5234543Smarks ngids = crgetngroups(cr); 5244543Smarks gids = crgetgroups(cr); 5254543Smarks for (i = 0; i != ngids; i++) { 5264543Smarks id = gids[i]; 5274543Smarks (void) dsl_load_sets(mos, zapobj, 5284543Smarks ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 5294543Smarks } 5304543Smarks } 5314543Smarks 5324543Smarks /* 5334543Smarks * Check if user has requested permission. 5344543Smarks */ 5354543Smarks int 5364543Smarks dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr) 5374543Smarks { 5384543Smarks dsl_dir_t *dd, *startdd; 5394543Smarks dsl_pool_t *dp; 5404543Smarks void *cookie; 5414543Smarks int error; 5424543Smarks char checkflag = ZFS_DELEG_LOCAL; 5434543Smarks const char *tail; 5444543Smarks objset_t *mos; 5454543Smarks avl_tree_t permsets; 5464543Smarks perm_set_t *setnode; 5474543Smarks 5484543Smarks /* 5494543Smarks * Use tail so that zfs_ioctl() code doesn't have 5504543Smarks * to always to to figure out parent name in order 5514543Smarks * to do access check. for example renaming a snapshot 5524543Smarks */ 5534543Smarks error = dsl_dir_open(ddname, FTAG, &startdd, &tail); 5544543Smarks if (error) 5554543Smarks return (error); 5564543Smarks 5574543Smarks if (tail && tail[0] != '@') { 5584543Smarks dsl_dir_close(startdd, FTAG); 5594543Smarks return (ENOENT); 5604543Smarks } 5614543Smarks dp = startdd->dd_pool; 5624543Smarks mos = dp->dp_meta_objset; 5634543Smarks 5644543Smarks if (dsl_delegation_on(mos) == B_FALSE) { 5654543Smarks dsl_dir_close(startdd, FTAG); 5664543Smarks return (ECANCELED); 5674543Smarks } 5684543Smarks 5694543Smarks if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) < 5705094Slling SPA_VERSION_DELEGATED_PERMS) { 5714543Smarks dsl_dir_close(startdd, FTAG); 5724543Smarks return (EPERM); 5734543Smarks } 5744543Smarks 5754543Smarks avl_create(&permsets, perm_set_compare, sizeof (perm_set_t), 5764543Smarks offsetof(perm_set_t, p_node)); 5774543Smarks 5784543Smarks rw_enter(&dp->dp_config_rwlock, RW_READER); 5794543Smarks for (dd = startdd; dd != NULL; dd = dd->dd_parent, 5804543Smarks checkflag = ZFS_DELEG_DESCENDENT) { 5814543Smarks uint64_t zapobj; 5824543Smarks boolean_t expanded; 5834543Smarks 5844543Smarks /* 5854543Smarks * If not in global zone then make sure 5864543Smarks * the zoned property is set 5874543Smarks */ 5884543Smarks if (!INGLOBALZONE(curproc)) { 5894543Smarks uint64_t zoned; 5904543Smarks 5914543Smarks if (dsl_prop_get_ds_locked(dd, 5924543Smarks zfs_prop_to_name(ZFS_PROP_ZONED), 5934543Smarks 8, 1, &zoned, NULL) != 0) 5944543Smarks break; 5954543Smarks if (!zoned) 5964543Smarks break; 5974543Smarks } 5984543Smarks zapobj = dd->dd_phys->dd_deleg_zapobj; 5994543Smarks 6004543Smarks if (zapobj == 0) 6014543Smarks continue; 6024543Smarks 6034543Smarks dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr); 6044543Smarks again: 6054543Smarks expanded = B_FALSE; 6064543Smarks for (setnode = avl_first(&permsets); setnode; 6074543Smarks setnode = AVL_NEXT(&permsets, setnode)) { 6084543Smarks if (setnode->p_matched == B_TRUE) 6094543Smarks continue; 6104543Smarks 6114543Smarks /* See if this set directly grants this permission */ 6124543Smarks error = dsl_check_access(mos, zapobj, 6134543Smarks ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm); 6144543Smarks if (error == 0) 6154543Smarks goto success; 6164543Smarks if (error == EPERM) 6174543Smarks setnode->p_matched = B_TRUE; 6184543Smarks 6194543Smarks /* See if this set includes other sets */ 6204543Smarks error = dsl_load_sets(mos, zapobj, 6214543Smarks ZFS_DELEG_NAMED_SET_SETS, 0, 6224543Smarks setnode->p_setname, &permsets); 6234543Smarks if (error == 0) 6244543Smarks setnode->p_matched = expanded = B_TRUE; 6254543Smarks } 6264543Smarks /* 6274543Smarks * If we expanded any sets, that will define more sets, 6284543Smarks * which we need to check. 6294543Smarks */ 6304543Smarks if (expanded) 6314543Smarks goto again; 6324543Smarks 6334543Smarks error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr); 6344543Smarks if (error == 0) 6354543Smarks goto success; 6364543Smarks } 6374543Smarks error = EPERM; 6384543Smarks success: 6394543Smarks rw_exit(&dp->dp_config_rwlock); 6404543Smarks dsl_dir_close(startdd, FTAG); 6414543Smarks 6424543Smarks cookie = NULL; 6434787Sahrens while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) 6444543Smarks kmem_free(setnode, sizeof (perm_set_t)); 6454543Smarks 6464543Smarks return (error); 6474543Smarks } 6484543Smarks 6494543Smarks /* 6504543Smarks * Other routines. 6514543Smarks */ 6524543Smarks 6534543Smarks static void 6544787Sahrens copy_create_perms(dsl_dir_t *dd, uint64_t pzapobj, 6554543Smarks boolean_t dosets, uint64_t uid, dmu_tx_t *tx) 6564543Smarks { 6574787Sahrens objset_t *mos = dd->dd_pool->dp_meta_objset; 6584543Smarks uint64_t jumpobj, pjumpobj; 6594543Smarks uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj; 6604543Smarks zap_cursor_t zc; 6614543Smarks zap_attribute_t za; 6624543Smarks char whokey[ZFS_MAX_DELEG_NAME]; 6634543Smarks 6644543Smarks zfs_deleg_whokey(whokey, 6654543Smarks dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE, 6664543Smarks ZFS_DELEG_LOCAL, NULL); 6674787Sahrens if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0) 6684543Smarks return; 6694543Smarks 6704543Smarks if (zapobj == 0) { 6714543Smarks dmu_buf_will_dirty(dd->dd_dbuf, tx); 6724543Smarks zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos, 6734543Smarks DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 6744543Smarks } 6754543Smarks 6764787Sahrens zfs_deleg_whokey(whokey, 6774787Sahrens dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER, 6784787Sahrens ZFS_DELEG_LOCAL, &uid); 6794543Smarks if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) { 6804543Smarks jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 6814543Smarks VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0); 6824543Smarks } 6834543Smarks 6844543Smarks for (zap_cursor_init(&zc, mos, pjumpobj); 6854543Smarks zap_cursor_retrieve(&zc, &za) == 0; 6864543Smarks zap_cursor_advance(&zc)) { 6874787Sahrens uint64_t zero = 0; 6884543Smarks ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 6894543Smarks 6904543Smarks VERIFY(zap_update(mos, jumpobj, za.za_name, 6914543Smarks 8, 1, &zero, tx) == 0); 6924543Smarks } 6934543Smarks zap_cursor_fini(&zc); 6944543Smarks } 6954543Smarks 6964543Smarks /* 6974543Smarks * set all create time permission on new dataset. 6984543Smarks */ 6994543Smarks void 7004543Smarks dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr) 7014543Smarks { 7024543Smarks dsl_dir_t *dd; 7034787Sahrens uint64_t uid = crgetuid(cr); 7044543Smarks 7054543Smarks if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) < 7065094Slling SPA_VERSION_DELEGATED_PERMS) 7074543Smarks return; 7084543Smarks 7094543Smarks for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) { 7104787Sahrens uint64_t pzapobj = dd->dd_phys->dd_deleg_zapobj; 7114543Smarks 7124787Sahrens if (pzapobj == 0) 7134543Smarks continue; 7144543Smarks 7154787Sahrens copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx); 7164787Sahrens copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx); 7174543Smarks } 7184543Smarks } 7194543Smarks 7204543Smarks int 7214543Smarks dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx) 7224543Smarks { 7234543Smarks zap_cursor_t zc; 7244543Smarks zap_attribute_t za; 7254543Smarks 7264543Smarks if (zapobj == 0) 7274543Smarks return (0); 7284543Smarks 7294543Smarks for (zap_cursor_init(&zc, mos, zapobj); 7304543Smarks zap_cursor_retrieve(&zc, &za) == 0; 7314543Smarks zap_cursor_advance(&zc)) { 7324543Smarks ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 7334543Smarks VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx)); 7344543Smarks } 7354543Smarks zap_cursor_fini(&zc); 7364543Smarks VERIFY(0 == zap_destroy(mos, zapobj, tx)); 7374543Smarks return (0); 7384543Smarks } 7394543Smarks 7404543Smarks boolean_t 7414543Smarks dsl_delegation_on(objset_t *os) 7424543Smarks { 7434543Smarks return (os->os->os_spa->spa_delegation); 7444543Smarks } 745