1185029Spjd/* 2185029Spjd * CDDL HEADER START 3185029Spjd * 4185029Spjd * The contents of this file are subject to the terms of the 5185029Spjd * Common Development and Distribution License (the "License"). 6185029Spjd * You may not use this file except in compliance with the License. 7185029Spjd * 8185029Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9185029Spjd * or http://www.opensolaris.org/os/licensing. 10185029Spjd * See the License for the specific language governing permissions 11185029Spjd * and limitations under the License. 12185029Spjd * 13185029Spjd * When distributing Covered Code, include this CDDL HEADER in each 14185029Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15185029Spjd * If applicable, add the following below this CDDL HEADER, with the 16185029Spjd * fields enclosed by brackets "[]" replaced with your own identifying 17185029Spjd * information: Portions Copyright [yyyy] [name of copyright owner] 18185029Spjd * 19185029Spjd * CDDL HEADER END 20185029Spjd */ 21185029Spjd/* 22219089Spjd * Copyright (c) 2007, 2010, Oracle and/or its affiliates. All rights reserved. 23307122Smav * Copyright (c) 2011, 2015 by Delphix. All rights reserved. 24185029Spjd */ 25185029Spjd 26185029Spjd/* 27185029Spjd * DSL permissions are stored in a two level zap attribute 28185029Spjd * mechanism. The first level identifies the "class" of 29185029Spjd * entry. The class is identified by the first 2 letters of 30185029Spjd * the attribute. The second letter "l" or "d" identifies whether 31185029Spjd * it is a local or descendent permission. The first letter 32185029Spjd * identifies the type of entry. 33185029Spjd * 34185029Spjd * ul$<id> identifies permissions granted locally for this userid. 35185029Spjd * ud$<id> identifies permissions granted on descendent datasets for 36185029Spjd * this userid. 37185029Spjd * Ul$<id> identifies permission sets granted locally for this userid. 38185029Spjd * Ud$<id> identifies permission sets granted on descendent datasets for 39185029Spjd * this userid. 40185029Spjd * gl$<id> identifies permissions granted locally for this groupid. 41185029Spjd * gd$<id> identifies permissions granted on descendent datasets for 42185029Spjd * this groupid. 43185029Spjd * Gl$<id> identifies permission sets granted locally for this groupid. 44185029Spjd * Gd$<id> identifies permission sets granted on descendent datasets for 45185029Spjd * this groupid. 46185029Spjd * el$ identifies permissions granted locally for everyone. 47185029Spjd * ed$ identifies permissions granted on descendent datasets 48185029Spjd * for everyone. 49185029Spjd * El$ identifies permission sets granted locally for everyone. 50185029Spjd * Ed$ identifies permission sets granted to descendent datasets for 51185029Spjd * everyone. 52185029Spjd * c-$ identifies permission to create at dataset creation time. 53185029Spjd * C-$ identifies permission sets to grant locally at dataset creation 54185029Spjd * time. 55185029Spjd * s-$@<name> permissions defined in specified set @<name> 56185029Spjd * S-$@<name> Sets defined in named set @<name> 57185029Spjd * 58185029Spjd * Each of the above entities points to another zap attribute that contains one 59185029Spjd * attribute for each allowed permission, such as create, destroy,... 60185029Spjd * All of the "upper" case class types will specify permission set names 61185029Spjd * rather than permissions. 62185029Spjd * 63185029Spjd * Basically it looks something like this: 64185029Spjd * ul$12 -> ZAP OBJ -> permissions... 65185029Spjd * 66185029Spjd * The ZAP OBJ is referred to as the jump object. 67185029Spjd */ 68185029Spjd 69185029Spjd#include <sys/dmu.h> 70185029Spjd#include <sys/dmu_objset.h> 71185029Spjd#include <sys/dmu_tx.h> 72185029Spjd#include <sys/dsl_dataset.h> 73185029Spjd#include <sys/dsl_dir.h> 74185029Spjd#include <sys/dsl_prop.h> 75185029Spjd#include <sys/dsl_synctask.h> 76185029Spjd#include <sys/dsl_deleg.h> 77185029Spjd#include <sys/spa.h> 78185029Spjd#include <sys/zap.h> 79185029Spjd#include <sys/fs/zfs.h> 80185029Spjd#include <sys/cred.h> 81185029Spjd#include <sys/sunddi.h> 82185029Spjd 83185029Spjd#include "zfs_deleg.h" 84185029Spjd 85185029Spjd/* 86185029Spjd * Validate that user is allowed to delegate specified permissions. 87185029Spjd * 88185029Spjd * In order to delegate "create" you must have "create" 89185029Spjd * and "allow". 90185029Spjd */ 91185029Spjdint 92185029Spjddsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr) 93185029Spjd{ 94185029Spjd nvpair_t *whopair = NULL; 95185029Spjd int error; 96185029Spjd 97185029Spjd if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0) 98185029Spjd return (error); 99185029Spjd 100185029Spjd while (whopair = nvlist_next_nvpair(nvp, whopair)) { 101185029Spjd nvlist_t *perms; 102185029Spjd nvpair_t *permpair = NULL; 103185029Spjd 104185029Spjd VERIFY(nvpair_value_nvlist(whopair, &perms) == 0); 105185029Spjd 106185029Spjd while (permpair = nvlist_next_nvpair(perms, permpair)) { 107185029Spjd const char *perm = nvpair_name(permpair); 108185029Spjd 109185029Spjd if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0) 110249195Smm return (SET_ERROR(EPERM)); 111185029Spjd 112185029Spjd if ((error = dsl_deleg_access(ddname, perm, cr)) != 0) 113185029Spjd return (error); 114185029Spjd } 115185029Spjd } 116185029Spjd return (0); 117185029Spjd} 118185029Spjd 119185029Spjd/* 120185029Spjd * Validate that user is allowed to unallow specified permissions. They 121185029Spjd * must have the 'allow' permission, and even then can only unallow 122185029Spjd * perms for their uid. 123185029Spjd */ 124185029Spjdint 125185029Spjddsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr) 126185029Spjd{ 127185029Spjd nvpair_t *whopair = NULL; 128185029Spjd int error; 129185029Spjd char idstr[32]; 130185029Spjd 131185029Spjd if ((error = dsl_deleg_access(ddname, ZFS_DELEG_PERM_ALLOW, cr)) != 0) 132185029Spjd return (error); 133185029Spjd 134185029Spjd (void) snprintf(idstr, sizeof (idstr), "%lld", 135185029Spjd (longlong_t)crgetuid(cr)); 136185029Spjd 137185029Spjd while (whopair = nvlist_next_nvpair(nvp, whopair)) { 138185029Spjd zfs_deleg_who_type_t type = nvpair_name(whopair)[0]; 139185029Spjd 140185029Spjd if (type != ZFS_DELEG_USER && 141185029Spjd type != ZFS_DELEG_USER_SETS) 142249195Smm return (SET_ERROR(EPERM)); 143185029Spjd 144185029Spjd if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0) 145249195Smm return (SET_ERROR(EPERM)); 146185029Spjd } 147185029Spjd return (0); 148185029Spjd} 149185029Spjd 150248571Smmtypedef struct dsl_deleg_arg { 151248571Smm const char *dda_name; 152248571Smm nvlist_t *dda_nvlist; 153248571Smm} dsl_deleg_arg_t; 154248571Smm 155185029Spjdstatic void 156248571Smmdsl_deleg_set_sync(void *arg, dmu_tx_t *tx) 157185029Spjd{ 158248571Smm dsl_deleg_arg_t *dda = arg; 159248571Smm dsl_dir_t *dd; 160248571Smm dsl_pool_t *dp = dmu_tx_pool(tx); 161248571Smm objset_t *mos = dp->dp_meta_objset; 162185029Spjd nvpair_t *whopair = NULL; 163248571Smm uint64_t zapobj; 164185029Spjd 165248571Smm VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL)); 166248571Smm 167277585Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 168185029Spjd if (zapobj == 0) { 169185029Spjd dmu_buf_will_dirty(dd->dd_dbuf, tx); 170277585Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos, 171185029Spjd DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 172185029Spjd } 173185029Spjd 174248571Smm while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) { 175185029Spjd const char *whokey = nvpair_name(whopair); 176185029Spjd nvlist_t *perms; 177185029Spjd nvpair_t *permpair = NULL; 178185029Spjd uint64_t jumpobj; 179185029Spjd 180248571Smm perms = fnvpair_value_nvlist(whopair); 181185029Spjd 182185029Spjd if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) { 183236884Smm jumpobj = zap_create_link(mos, DMU_OT_DSL_PERMS, 184236884Smm zapobj, whokey, tx); 185185029Spjd } 186185029Spjd 187185029Spjd while (permpair = nvlist_next_nvpair(perms, permpair)) { 188185029Spjd const char *perm = nvpair_name(permpair); 189185029Spjd uint64_t n = 0; 190185029Spjd 191185029Spjd VERIFY(zap_update(mos, jumpobj, 192185029Spjd perm, 8, 1, &n, tx) == 0); 193248571Smm spa_history_log_internal_dd(dd, "permission update", tx, 194248571Smm "%s %s", whokey, perm); 195185029Spjd } 196185029Spjd } 197248571Smm dsl_dir_rele(dd, FTAG); 198185029Spjd} 199185029Spjd 200185029Spjdstatic void 201248571Smmdsl_deleg_unset_sync(void *arg, dmu_tx_t *tx) 202185029Spjd{ 203248571Smm dsl_deleg_arg_t *dda = arg; 204248571Smm dsl_dir_t *dd; 205248571Smm dsl_pool_t *dp = dmu_tx_pool(tx); 206248571Smm objset_t *mos = dp->dp_meta_objset; 207185029Spjd nvpair_t *whopair = NULL; 208248571Smm uint64_t zapobj; 209185029Spjd 210248571Smm VERIFY0(dsl_dir_hold(dp, dda->dda_name, FTAG, &dd, NULL)); 211277585Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 212248571Smm if (zapobj == 0) { 213248571Smm dsl_dir_rele(dd, FTAG); 214185029Spjd return; 215248571Smm } 216185029Spjd 217248571Smm while (whopair = nvlist_next_nvpair(dda->dda_nvlist, whopair)) { 218185029Spjd const char *whokey = nvpair_name(whopair); 219185029Spjd nvlist_t *perms; 220185029Spjd nvpair_t *permpair = NULL; 221185029Spjd uint64_t jumpobj; 222185029Spjd 223185029Spjd if (nvpair_value_nvlist(whopair, &perms) != 0) { 224185029Spjd if (zap_lookup(mos, zapobj, whokey, 8, 225185029Spjd 1, &jumpobj) == 0) { 226185029Spjd (void) zap_remove(mos, zapobj, whokey, tx); 227185029Spjd VERIFY(0 == zap_destroy(mos, jumpobj, tx)); 228185029Spjd } 229248571Smm spa_history_log_internal_dd(dd, "permission who remove", 230248571Smm tx, "%s", whokey); 231185029Spjd continue; 232185029Spjd } 233185029Spjd 234185029Spjd if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) 235185029Spjd continue; 236185029Spjd 237185029Spjd while (permpair = nvlist_next_nvpair(perms, permpair)) { 238185029Spjd const char *perm = nvpair_name(permpair); 239185029Spjd uint64_t n = 0; 240185029Spjd 241185029Spjd (void) zap_remove(mos, jumpobj, perm, tx); 242185029Spjd if (zap_count(mos, jumpobj, &n) == 0 && n == 0) { 243185029Spjd (void) zap_remove(mos, zapobj, 244185029Spjd whokey, tx); 245185029Spjd VERIFY(0 == zap_destroy(mos, 246185029Spjd jumpobj, tx)); 247185029Spjd } 248248571Smm spa_history_log_internal_dd(dd, "permission remove", tx, 249248571Smm "%s %s", whokey, perm); 250185029Spjd } 251185029Spjd } 252248571Smm dsl_dir_rele(dd, FTAG); 253185029Spjd} 254185029Spjd 255248571Smmstatic int 256248571Smmdsl_deleg_check(void *arg, dmu_tx_t *tx) 257185029Spjd{ 258248571Smm dsl_deleg_arg_t *dda = arg; 259185029Spjd dsl_dir_t *dd; 260185029Spjd int error; 261185029Spjd 262248571Smm if (spa_version(dmu_tx_pool(tx)->dp_spa) < 263185029Spjd SPA_VERSION_DELEGATED_PERMS) { 264249195Smm return (SET_ERROR(ENOTSUP)); 265185029Spjd } 266185029Spjd 267248571Smm error = dsl_dir_hold(dmu_tx_pool(tx), dda->dda_name, FTAG, &dd, NULL); 268248571Smm if (error == 0) 269248571Smm dsl_dir_rele(dd, FTAG); 270248571Smm return (error); 271248571Smm} 272185029Spjd 273248571Smmint 274248571Smmdsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset) 275248571Smm{ 276248571Smm dsl_deleg_arg_t dda; 277248571Smm 278248571Smm /* nvp must already have been verified to be valid */ 279248571Smm 280248571Smm dda.dda_name = ddname; 281248571Smm dda.dda_nvlist = nvp; 282248571Smm 283248571Smm return (dsl_sync_task(ddname, dsl_deleg_check, 284185029Spjd unset ? dsl_deleg_unset_sync : dsl_deleg_set_sync, 285269006Sdelphij &dda, fnvlist_num_pairs(nvp), ZFS_SPACE_CHECK_RESERVED)); 286185029Spjd} 287185029Spjd 288185029Spjd/* 289185029Spjd * Find all 'allow' permissions from a given point and then continue 290185029Spjd * traversing up to the root. 291185029Spjd * 292185029Spjd * This function constructs an nvlist of nvlists. 293185029Spjd * each setpoint is an nvlist composed of an nvlist of an nvlist 294185029Spjd * of the individual * users/groups/everyone/create 295185029Spjd * permissions. 296185029Spjd * 297185029Spjd * The nvlist will look like this. 298185029Spjd * 299185029Spjd * { source fsname -> { whokeys { permissions,...}, ...}} 300185029Spjd * 301185029Spjd * The fsname nvpairs will be arranged in a bottom up order. For example, 302185029Spjd * if we have the following structure a/b/c then the nvpairs for the fsnames 303185029Spjd * will be ordered a/b/c, a/b, a. 304185029Spjd */ 305185029Spjdint 306185029Spjddsl_deleg_get(const char *ddname, nvlist_t **nvp) 307185029Spjd{ 308185029Spjd dsl_dir_t *dd, *startdd; 309185029Spjd dsl_pool_t *dp; 310185029Spjd int error; 311185029Spjd objset_t *mos; 312185029Spjd 313248571Smm error = dsl_pool_hold(ddname, FTAG, &dp); 314248571Smm if (error != 0) 315185029Spjd return (error); 316185029Spjd 317248571Smm error = dsl_dir_hold(dp, ddname, FTAG, &startdd, NULL); 318248571Smm if (error != 0) { 319248571Smm dsl_pool_rele(dp, FTAG); 320248571Smm return (error); 321248571Smm } 322248571Smm 323185029Spjd dp = startdd->dd_pool; 324185029Spjd mos = dp->dp_meta_objset; 325185029Spjd 326185029Spjd VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0); 327185029Spjd 328185029Spjd for (dd = startdd; dd != NULL; dd = dd->dd_parent) { 329185029Spjd zap_cursor_t basezc; 330185029Spjd zap_attribute_t baseza; 331185029Spjd nvlist_t *sp_nvp; 332185029Spjd uint64_t n; 333307122Smav char source[ZFS_MAX_DATASET_NAME_LEN]; 334185029Spjd 335277585Sdelphij if (dsl_dir_phys(dd)->dd_deleg_zapobj == 0 || 336277585Sdelphij zap_count(mos, 337277585Sdelphij dsl_dir_phys(dd)->dd_deleg_zapobj, &n) != 0 || n == 0) 338185029Spjd continue; 339185029Spjd 340248571Smm sp_nvp = fnvlist_alloc(); 341185029Spjd for (zap_cursor_init(&basezc, mos, 342277585Sdelphij dsl_dir_phys(dd)->dd_deleg_zapobj); 343185029Spjd zap_cursor_retrieve(&basezc, &baseza) == 0; 344185029Spjd zap_cursor_advance(&basezc)) { 345185029Spjd zap_cursor_t zc; 346185029Spjd zap_attribute_t za; 347185029Spjd nvlist_t *perms_nvp; 348185029Spjd 349185029Spjd ASSERT(baseza.za_integer_length == 8); 350185029Spjd ASSERT(baseza.za_num_integers == 1); 351185029Spjd 352248571Smm perms_nvp = fnvlist_alloc(); 353185029Spjd for (zap_cursor_init(&zc, mos, baseza.za_first_integer); 354185029Spjd zap_cursor_retrieve(&zc, &za) == 0; 355185029Spjd zap_cursor_advance(&zc)) { 356248571Smm fnvlist_add_boolean(perms_nvp, za.za_name); 357185029Spjd } 358185029Spjd zap_cursor_fini(&zc); 359248571Smm fnvlist_add_nvlist(sp_nvp, baseza.za_name, perms_nvp); 360248571Smm fnvlist_free(perms_nvp); 361185029Spjd } 362185029Spjd 363185029Spjd zap_cursor_fini(&basezc); 364185029Spjd 365185029Spjd dsl_dir_name(dd, source); 366248571Smm fnvlist_add_nvlist(*nvp, source, sp_nvp); 367185029Spjd nvlist_free(sp_nvp); 368185029Spjd } 369185029Spjd 370248571Smm dsl_dir_rele(startdd, FTAG); 371248571Smm dsl_pool_rele(dp, FTAG); 372185029Spjd return (0); 373185029Spjd} 374185029Spjd 375185029Spjd/* 376185029Spjd * Routines for dsl_deleg_access() -- access checking. 377185029Spjd */ 378185029Spjdtypedef struct perm_set { 379185029Spjd avl_node_t p_node; 380185029Spjd boolean_t p_matched; 381185029Spjd char p_setname[ZFS_MAX_DELEG_NAME]; 382185029Spjd} perm_set_t; 383185029Spjd 384185029Spjdstatic int 385185029Spjdperm_set_compare(const void *arg1, const void *arg2) 386185029Spjd{ 387185029Spjd const perm_set_t *node1 = arg1; 388185029Spjd const perm_set_t *node2 = arg2; 389185029Spjd int val; 390185029Spjd 391185029Spjd val = strcmp(node1->p_setname, node2->p_setname); 392185029Spjd if (val == 0) 393185029Spjd return (0); 394185029Spjd return (val > 0 ? 1 : -1); 395185029Spjd} 396185029Spjd 397185029Spjd/* 398185029Spjd * Determine whether a specified permission exists. 399185029Spjd * 400185029Spjd * First the base attribute has to be retrieved. i.e. ul$12 401185029Spjd * Once the base object has been retrieved the actual permission 402185029Spjd * is lookup up in the zap object the base object points to. 403185029Spjd * 404185029Spjd * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if 405185029Spjd * there is no perm in that jumpobj. 406185029Spjd */ 407185029Spjdstatic int 408185029Spjddsl_check_access(objset_t *mos, uint64_t zapobj, 409185029Spjd char type, char checkflag, void *valp, const char *perm) 410185029Spjd{ 411185029Spjd int error; 412185029Spjd uint64_t jumpobj, zero; 413185029Spjd char whokey[ZFS_MAX_DELEG_NAME]; 414185029Spjd 415185029Spjd zfs_deleg_whokey(whokey, type, checkflag, valp); 416185029Spjd error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 417185029Spjd if (error == 0) { 418185029Spjd error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero); 419185029Spjd if (error == ENOENT) 420249195Smm error = SET_ERROR(EPERM); 421185029Spjd } 422185029Spjd return (error); 423185029Spjd} 424185029Spjd 425185029Spjd/* 426185029Spjd * check a specified user/group for a requested permission 427185029Spjd */ 428185029Spjdstatic int 429185029Spjddsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm, 430185029Spjd int checkflag, cred_t *cr) 431185029Spjd{ 432185029Spjd const gid_t *gids; 433185029Spjd int ngids; 434185029Spjd int i; 435185029Spjd uint64_t id; 436185029Spjd 437185029Spjd /* check for user */ 438185029Spjd id = crgetuid(cr); 439185029Spjd if (dsl_check_access(mos, zapobj, 440185029Spjd ZFS_DELEG_USER, checkflag, &id, perm) == 0) 441185029Spjd return (0); 442185029Spjd 443185029Spjd /* check for users primary group */ 444185029Spjd id = crgetgid(cr); 445185029Spjd if (dsl_check_access(mos, zapobj, 446185029Spjd ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 447185029Spjd return (0); 448185029Spjd 449185029Spjd /* check for everyone entry */ 450185029Spjd id = -1; 451185029Spjd if (dsl_check_access(mos, zapobj, 452185029Spjd ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0) 453185029Spjd return (0); 454185029Spjd 455185029Spjd /* check each supplemental group user is a member of */ 456185029Spjd ngids = crgetngroups(cr); 457185029Spjd gids = crgetgroups(cr); 458185029Spjd for (i = 0; i != ngids; i++) { 459185029Spjd id = gids[i]; 460185029Spjd if (dsl_check_access(mos, zapobj, 461185029Spjd ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 462185029Spjd return (0); 463185029Spjd } 464185029Spjd 465249195Smm return (SET_ERROR(EPERM)); 466185029Spjd} 467185029Spjd 468185029Spjd/* 469185029Spjd * Iterate over the sets specified in the specified zapobj 470185029Spjd * and load them into the permsets avl tree. 471185029Spjd */ 472185029Spjdstatic int 473185029Spjddsl_load_sets(objset_t *mos, uint64_t zapobj, 474185029Spjd char type, char checkflag, void *valp, avl_tree_t *avl) 475185029Spjd{ 476185029Spjd zap_cursor_t zc; 477185029Spjd zap_attribute_t za; 478185029Spjd perm_set_t *permnode; 479185029Spjd avl_index_t idx; 480185029Spjd uint64_t jumpobj; 481185029Spjd int error; 482185029Spjd char whokey[ZFS_MAX_DELEG_NAME]; 483185029Spjd 484185029Spjd zfs_deleg_whokey(whokey, type, checkflag, valp); 485185029Spjd 486185029Spjd error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 487185029Spjd if (error != 0) 488185029Spjd return (error); 489185029Spjd 490185029Spjd for (zap_cursor_init(&zc, mos, jumpobj); 491185029Spjd zap_cursor_retrieve(&zc, &za) == 0; 492185029Spjd zap_cursor_advance(&zc)) { 493185029Spjd permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP); 494185029Spjd (void) strlcpy(permnode->p_setname, za.za_name, 495185029Spjd sizeof (permnode->p_setname)); 496185029Spjd permnode->p_matched = B_FALSE; 497185029Spjd 498185029Spjd if (avl_find(avl, permnode, &idx) == NULL) { 499185029Spjd avl_insert(avl, permnode, idx); 500185029Spjd } else { 501185029Spjd kmem_free(permnode, sizeof (perm_set_t)); 502185029Spjd } 503185029Spjd } 504185029Spjd zap_cursor_fini(&zc); 505185029Spjd return (0); 506185029Spjd} 507185029Spjd 508185029Spjd/* 509185029Spjd * Load all permissions user based on cred belongs to. 510185029Spjd */ 511185029Spjdstatic void 512185029Spjddsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl, 513185029Spjd char checkflag, cred_t *cr) 514185029Spjd{ 515185029Spjd const gid_t *gids; 516185029Spjd int ngids, i; 517185029Spjd uint64_t id; 518185029Spjd 519185029Spjd id = crgetuid(cr); 520185029Spjd (void) dsl_load_sets(mos, zapobj, 521185029Spjd ZFS_DELEG_USER_SETS, checkflag, &id, avl); 522185029Spjd 523185029Spjd id = crgetgid(cr); 524185029Spjd (void) dsl_load_sets(mos, zapobj, 525185029Spjd ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 526185029Spjd 527185029Spjd (void) dsl_load_sets(mos, zapobj, 528185029Spjd ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl); 529185029Spjd 530185029Spjd ngids = crgetngroups(cr); 531185029Spjd gids = crgetgroups(cr); 532185029Spjd for (i = 0; i != ngids; i++) { 533185029Spjd id = gids[i]; 534185029Spjd (void) dsl_load_sets(mos, zapobj, 535185029Spjd ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 536185029Spjd } 537185029Spjd} 538185029Spjd 539185029Spjd/* 540248571Smm * Check if user has requested permission. 541185029Spjd */ 542185029Spjdint 543248571Smmdsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr) 544185029Spjd{ 545185029Spjd dsl_dir_t *dd; 546185029Spjd dsl_pool_t *dp; 547185029Spjd void *cookie; 548185029Spjd int error; 549210457Smm char checkflag; 550185029Spjd objset_t *mos; 551185029Spjd avl_tree_t permsets; 552185029Spjd perm_set_t *setnode; 553185029Spjd 554185029Spjd dp = ds->ds_dir->dd_pool; 555185029Spjd mos = dp->dp_meta_objset; 556185029Spjd 557219089Spjd if (dsl_delegation_on(mos) == B_FALSE) 558249195Smm return (SET_ERROR(ECANCELED)); 559185029Spjd 560185029Spjd if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) < 561219089Spjd SPA_VERSION_DELEGATED_PERMS) 562249195Smm return (SET_ERROR(EPERM)); 563185029Spjd 564288549Smav if (ds->ds_is_snapshot) { 565210457Smm /* 566210457Smm * Snapshots are treated as descendents only, 567210457Smm * local permissions do not apply. 568210457Smm */ 569210457Smm checkflag = ZFS_DELEG_DESCENDENT; 570210457Smm } else { 571210457Smm checkflag = ZFS_DELEG_LOCAL; 572210457Smm } 573210457Smm 574185029Spjd avl_create(&permsets, perm_set_compare, sizeof (perm_set_t), 575185029Spjd offsetof(perm_set_t, p_node)); 576185029Spjd 577248571Smm ASSERT(dsl_pool_config_held(dp)); 578185029Spjd for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent, 579185029Spjd checkflag = ZFS_DELEG_DESCENDENT) { 580185029Spjd uint64_t zapobj; 581185029Spjd boolean_t expanded; 582185029Spjd 583185029Spjd /* 584185029Spjd * If not in global zone then make sure 585185029Spjd * the zoned property is set 586185029Spjd */ 587185029Spjd if (!INGLOBALZONE(curthread)) { 588185029Spjd uint64_t zoned; 589185029Spjd 590185029Spjd if (dsl_prop_get_dd(dd, 591185029Spjd zfs_prop_to_name(ZFS_PROP_ZONED), 592219089Spjd 8, 1, &zoned, NULL, B_FALSE) != 0) 593185029Spjd break; 594185029Spjd if (!zoned) 595185029Spjd break; 596185029Spjd } 597277585Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 598185029Spjd 599185029Spjd if (zapobj == 0) 600185029Spjd continue; 601185029Spjd 602185029Spjd dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr); 603185029Spjdagain: 604185029Spjd expanded = B_FALSE; 605185029Spjd for (setnode = avl_first(&permsets); setnode; 606185029Spjd setnode = AVL_NEXT(&permsets, setnode)) { 607185029Spjd if (setnode->p_matched == B_TRUE) 608185029Spjd continue; 609185029Spjd 610185029Spjd /* See if this set directly grants this permission */ 611185029Spjd error = dsl_check_access(mos, zapobj, 612185029Spjd ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm); 613185029Spjd if (error == 0) 614185029Spjd goto success; 615185029Spjd if (error == EPERM) 616185029Spjd setnode->p_matched = B_TRUE; 617185029Spjd 618185029Spjd /* See if this set includes other sets */ 619185029Spjd error = dsl_load_sets(mos, zapobj, 620185029Spjd ZFS_DELEG_NAMED_SET_SETS, 0, 621185029Spjd setnode->p_setname, &permsets); 622185029Spjd if (error == 0) 623185029Spjd setnode->p_matched = expanded = B_TRUE; 624185029Spjd } 625185029Spjd /* 626185029Spjd * If we expanded any sets, that will define more sets, 627185029Spjd * which we need to check. 628185029Spjd */ 629185029Spjd if (expanded) 630185029Spjd goto again; 631185029Spjd 632185029Spjd error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr); 633185029Spjd if (error == 0) 634185029Spjd goto success; 635185029Spjd } 636249195Smm error = SET_ERROR(EPERM); 637185029Spjdsuccess: 638185029Spjd 639185029Spjd cookie = NULL; 640185029Spjd while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) 641185029Spjd kmem_free(setnode, sizeof (perm_set_t)); 642185029Spjd 643185029Spjd return (error); 644185029Spjd} 645185029Spjd 646219089Spjdint 647219089Spjddsl_deleg_access(const char *dsname, const char *perm, cred_t *cr) 648219089Spjd{ 649248571Smm dsl_pool_t *dp; 650219089Spjd dsl_dataset_t *ds; 651219089Spjd int error; 652219089Spjd 653248571Smm error = dsl_pool_hold(dsname, FTAG, &dp); 654248571Smm if (error != 0) 655219089Spjd return (error); 656248571Smm error = dsl_dataset_hold(dp, dsname, FTAG, &ds); 657248571Smm if (error == 0) { 658248571Smm error = dsl_deleg_access_impl(ds, perm, cr); 659248571Smm dsl_dataset_rele(ds, FTAG); 660248571Smm } 661248571Smm dsl_pool_rele(dp, FTAG); 662219089Spjd 663219089Spjd return (error); 664219089Spjd} 665219089Spjd 666185029Spjd/* 667185029Spjd * Other routines. 668185029Spjd */ 669185029Spjd 670185029Spjdstatic void 671185029Spjdcopy_create_perms(dsl_dir_t *dd, uint64_t pzapobj, 672185029Spjd boolean_t dosets, uint64_t uid, dmu_tx_t *tx) 673185029Spjd{ 674185029Spjd objset_t *mos = dd->dd_pool->dp_meta_objset; 675185029Spjd uint64_t jumpobj, pjumpobj; 676277585Sdelphij uint64_t zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 677185029Spjd zap_cursor_t zc; 678185029Spjd zap_attribute_t za; 679185029Spjd char whokey[ZFS_MAX_DELEG_NAME]; 680185029Spjd 681185029Spjd zfs_deleg_whokey(whokey, 682185029Spjd dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE, 683185029Spjd ZFS_DELEG_LOCAL, NULL); 684185029Spjd if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0) 685185029Spjd return; 686185029Spjd 687185029Spjd if (zapobj == 0) { 688185029Spjd dmu_buf_will_dirty(dd->dd_dbuf, tx); 689277585Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos, 690185029Spjd DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 691185029Spjd } 692185029Spjd 693185029Spjd zfs_deleg_whokey(whokey, 694185029Spjd dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER, 695185029Spjd ZFS_DELEG_LOCAL, &uid); 696185029Spjd if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) { 697185029Spjd jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 698185029Spjd VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0); 699185029Spjd } 700185029Spjd 701185029Spjd for (zap_cursor_init(&zc, mos, pjumpobj); 702185029Spjd zap_cursor_retrieve(&zc, &za) == 0; 703185029Spjd zap_cursor_advance(&zc)) { 704185029Spjd uint64_t zero = 0; 705185029Spjd ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 706185029Spjd 707185029Spjd VERIFY(zap_update(mos, jumpobj, za.za_name, 708185029Spjd 8, 1, &zero, tx) == 0); 709185029Spjd } 710185029Spjd zap_cursor_fini(&zc); 711185029Spjd} 712185029Spjd 713185029Spjd/* 714185029Spjd * set all create time permission on new dataset. 715185029Spjd */ 716185029Spjdvoid 717185029Spjddsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr) 718185029Spjd{ 719185029Spjd dsl_dir_t *dd; 720185029Spjd uint64_t uid = crgetuid(cr); 721185029Spjd 722185029Spjd if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) < 723185029Spjd SPA_VERSION_DELEGATED_PERMS) 724185029Spjd return; 725185029Spjd 726185029Spjd for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) { 727277585Sdelphij uint64_t pzapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 728185029Spjd 729185029Spjd if (pzapobj == 0) 730185029Spjd continue; 731185029Spjd 732185029Spjd copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx); 733185029Spjd copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx); 734185029Spjd } 735185029Spjd} 736185029Spjd 737185029Spjdint 738185029Spjddsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx) 739185029Spjd{ 740185029Spjd zap_cursor_t zc; 741185029Spjd zap_attribute_t za; 742185029Spjd 743185029Spjd if (zapobj == 0) 744185029Spjd return (0); 745185029Spjd 746185029Spjd for (zap_cursor_init(&zc, mos, zapobj); 747185029Spjd zap_cursor_retrieve(&zc, &za) == 0; 748185029Spjd zap_cursor_advance(&zc)) { 749185029Spjd ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 750185029Spjd VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx)); 751185029Spjd } 752185029Spjd zap_cursor_fini(&zc); 753185029Spjd VERIFY(0 == zap_destroy(mos, zapobj, tx)); 754185029Spjd return (0); 755185029Spjd} 756185029Spjd 757185029Spjdboolean_t 758185029Spjddsl_delegation_on(objset_t *os) 759185029Spjd{ 760219089Spjd return (!!spa_delegation(os->os_spa)); 761185029Spjd} 762