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. 23307108Smav * 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 167275782Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 168185029Spjd if (zapobj == 0) { 169185029Spjd dmu_buf_will_dirty(dd->dd_dbuf, tx); 170275782Sdelphij 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)); 211275782Sdelphij 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, 285268473Sdelphij &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; 333307108Smav char source[ZFS_MAX_DATASET_NAME_LEN]; 334185029Spjd 335275782Sdelphij if (dsl_dir_phys(dd)->dd_deleg_zapobj == 0 || 336275782Sdelphij zap_count(mos, 337275782Sdelphij 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, 342275782Sdelphij 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{ 387339158Smav const perm_set_t *node1 = (const perm_set_t *)arg1; 388339158Smav const perm_set_t *node2 = (const perm_set_t *)arg2; 389185029Spjd int val; 390185029Spjd 391185029Spjd val = strcmp(node1->p_setname, node2->p_setname); 392339158Smav 393339158Smav return (AVL_ISIGN(val)); 394185029Spjd} 395185029Spjd 396185029Spjd/* 397185029Spjd * Determine whether a specified permission exists. 398185029Spjd * 399185029Spjd * First the base attribute has to be retrieved. i.e. ul$12 400185029Spjd * Once the base object has been retrieved the actual permission 401185029Spjd * is lookup up in the zap object the base object points to. 402185029Spjd * 403185029Spjd * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if 404185029Spjd * there is no perm in that jumpobj. 405185029Spjd */ 406185029Spjdstatic int 407185029Spjddsl_check_access(objset_t *mos, uint64_t zapobj, 408185029Spjd char type, char checkflag, void *valp, const char *perm) 409185029Spjd{ 410185029Spjd int error; 411185029Spjd uint64_t jumpobj, zero; 412185029Spjd char whokey[ZFS_MAX_DELEG_NAME]; 413185029Spjd 414185029Spjd zfs_deleg_whokey(whokey, type, checkflag, valp); 415185029Spjd error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 416185029Spjd if (error == 0) { 417185029Spjd error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero); 418185029Spjd if (error == ENOENT) 419249195Smm error = SET_ERROR(EPERM); 420185029Spjd } 421185029Spjd return (error); 422185029Spjd} 423185029Spjd 424185029Spjd/* 425185029Spjd * check a specified user/group for a requested permission 426185029Spjd */ 427185029Spjdstatic int 428185029Spjddsl_check_user_access(objset_t *mos, uint64_t zapobj, const char *perm, 429185029Spjd int checkflag, cred_t *cr) 430185029Spjd{ 431185029Spjd const gid_t *gids; 432185029Spjd int ngids; 433185029Spjd int i; 434185029Spjd uint64_t id; 435185029Spjd 436185029Spjd /* check for user */ 437185029Spjd id = crgetuid(cr); 438185029Spjd if (dsl_check_access(mos, zapobj, 439185029Spjd ZFS_DELEG_USER, checkflag, &id, perm) == 0) 440185029Spjd return (0); 441185029Spjd 442185029Spjd /* check for users primary group */ 443185029Spjd id = crgetgid(cr); 444185029Spjd if (dsl_check_access(mos, zapobj, 445185029Spjd ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 446185029Spjd return (0); 447185029Spjd 448185029Spjd /* check for everyone entry */ 449185029Spjd id = -1; 450185029Spjd if (dsl_check_access(mos, zapobj, 451185029Spjd ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0) 452185029Spjd return (0); 453185029Spjd 454185029Spjd /* check each supplemental group user is a member of */ 455185029Spjd ngids = crgetngroups(cr); 456185029Spjd gids = crgetgroups(cr); 457185029Spjd for (i = 0; i != ngids; i++) { 458185029Spjd id = gids[i]; 459185029Spjd if (dsl_check_access(mos, zapobj, 460185029Spjd ZFS_DELEG_GROUP, checkflag, &id, perm) == 0) 461185029Spjd return (0); 462185029Spjd } 463185029Spjd 464249195Smm return (SET_ERROR(EPERM)); 465185029Spjd} 466185029Spjd 467185029Spjd/* 468185029Spjd * Iterate over the sets specified in the specified zapobj 469185029Spjd * and load them into the permsets avl tree. 470185029Spjd */ 471185029Spjdstatic int 472185029Spjddsl_load_sets(objset_t *mos, uint64_t zapobj, 473185029Spjd char type, char checkflag, void *valp, avl_tree_t *avl) 474185029Spjd{ 475185029Spjd zap_cursor_t zc; 476185029Spjd zap_attribute_t za; 477185029Spjd perm_set_t *permnode; 478185029Spjd avl_index_t idx; 479185029Spjd uint64_t jumpobj; 480185029Spjd int error; 481185029Spjd char whokey[ZFS_MAX_DELEG_NAME]; 482185029Spjd 483185029Spjd zfs_deleg_whokey(whokey, type, checkflag, valp); 484185029Spjd 485185029Spjd error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj); 486185029Spjd if (error != 0) 487185029Spjd return (error); 488185029Spjd 489185029Spjd for (zap_cursor_init(&zc, mos, jumpobj); 490185029Spjd zap_cursor_retrieve(&zc, &za) == 0; 491185029Spjd zap_cursor_advance(&zc)) { 492185029Spjd permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP); 493185029Spjd (void) strlcpy(permnode->p_setname, za.za_name, 494185029Spjd sizeof (permnode->p_setname)); 495185029Spjd permnode->p_matched = B_FALSE; 496185029Spjd 497185029Spjd if (avl_find(avl, permnode, &idx) == NULL) { 498185029Spjd avl_insert(avl, permnode, idx); 499185029Spjd } else { 500185029Spjd kmem_free(permnode, sizeof (perm_set_t)); 501185029Spjd } 502185029Spjd } 503185029Spjd zap_cursor_fini(&zc); 504185029Spjd return (0); 505185029Spjd} 506185029Spjd 507185029Spjd/* 508185029Spjd * Load all permissions user based on cred belongs to. 509185029Spjd */ 510185029Spjdstatic void 511185029Spjddsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl, 512185029Spjd char checkflag, cred_t *cr) 513185029Spjd{ 514185029Spjd const gid_t *gids; 515185029Spjd int ngids, i; 516185029Spjd uint64_t id; 517185029Spjd 518185029Spjd id = crgetuid(cr); 519185029Spjd (void) dsl_load_sets(mos, zapobj, 520185029Spjd ZFS_DELEG_USER_SETS, checkflag, &id, avl); 521185029Spjd 522185029Spjd id = crgetgid(cr); 523185029Spjd (void) dsl_load_sets(mos, zapobj, 524185029Spjd ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 525185029Spjd 526185029Spjd (void) dsl_load_sets(mos, zapobj, 527185029Spjd ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl); 528185029Spjd 529185029Spjd ngids = crgetngroups(cr); 530185029Spjd gids = crgetgroups(cr); 531185029Spjd for (i = 0; i != ngids; i++) { 532185029Spjd id = gids[i]; 533185029Spjd (void) dsl_load_sets(mos, zapobj, 534185029Spjd ZFS_DELEG_GROUP_SETS, checkflag, &id, avl); 535185029Spjd } 536185029Spjd} 537185029Spjd 538185029Spjd/* 539248571Smm * Check if user has requested permission. 540185029Spjd */ 541185029Spjdint 542248571Smmdsl_deleg_access_impl(dsl_dataset_t *ds, const char *perm, cred_t *cr) 543185029Spjd{ 544185029Spjd dsl_dir_t *dd; 545185029Spjd dsl_pool_t *dp; 546185029Spjd void *cookie; 547185029Spjd int error; 548210457Smm char checkflag; 549185029Spjd objset_t *mos; 550185029Spjd avl_tree_t permsets; 551185029Spjd perm_set_t *setnode; 552185029Spjd 553185029Spjd dp = ds->ds_dir->dd_pool; 554185029Spjd mos = dp->dp_meta_objset; 555185029Spjd 556219089Spjd if (dsl_delegation_on(mos) == B_FALSE) 557249195Smm return (SET_ERROR(ECANCELED)); 558185029Spjd 559185029Spjd if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) < 560219089Spjd SPA_VERSION_DELEGATED_PERMS) 561249195Smm return (SET_ERROR(EPERM)); 562185029Spjd 563286575Smav if (ds->ds_is_snapshot) { 564210457Smm /* 565210457Smm * Snapshots are treated as descendents only, 566210457Smm * local permissions do not apply. 567210457Smm */ 568210457Smm checkflag = ZFS_DELEG_DESCENDENT; 569210457Smm } else { 570210457Smm checkflag = ZFS_DELEG_LOCAL; 571210457Smm } 572210457Smm 573185029Spjd avl_create(&permsets, perm_set_compare, sizeof (perm_set_t), 574185029Spjd offsetof(perm_set_t, p_node)); 575185029Spjd 576248571Smm ASSERT(dsl_pool_config_held(dp)); 577185029Spjd for (dd = ds->ds_dir; dd != NULL; dd = dd->dd_parent, 578185029Spjd checkflag = ZFS_DELEG_DESCENDENT) { 579185029Spjd uint64_t zapobj; 580185029Spjd boolean_t expanded; 581185029Spjd 582185029Spjd /* 583185029Spjd * If not in global zone then make sure 584185029Spjd * the zoned property is set 585185029Spjd */ 586185029Spjd if (!INGLOBALZONE(curthread)) { 587185029Spjd uint64_t zoned; 588185029Spjd 589185029Spjd if (dsl_prop_get_dd(dd, 590185029Spjd zfs_prop_to_name(ZFS_PROP_ZONED), 591219089Spjd 8, 1, &zoned, NULL, B_FALSE) != 0) 592185029Spjd break; 593185029Spjd if (!zoned) 594185029Spjd break; 595185029Spjd } 596275782Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 597185029Spjd 598185029Spjd if (zapobj == 0) 599185029Spjd continue; 600185029Spjd 601185029Spjd dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr); 602185029Spjdagain: 603185029Spjd expanded = B_FALSE; 604185029Spjd for (setnode = avl_first(&permsets); setnode; 605185029Spjd setnode = AVL_NEXT(&permsets, setnode)) { 606185029Spjd if (setnode->p_matched == B_TRUE) 607185029Spjd continue; 608185029Spjd 609185029Spjd /* See if this set directly grants this permission */ 610185029Spjd error = dsl_check_access(mos, zapobj, 611185029Spjd ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm); 612185029Spjd if (error == 0) 613185029Spjd goto success; 614185029Spjd if (error == EPERM) 615185029Spjd setnode->p_matched = B_TRUE; 616185029Spjd 617185029Spjd /* See if this set includes other sets */ 618185029Spjd error = dsl_load_sets(mos, zapobj, 619185029Spjd ZFS_DELEG_NAMED_SET_SETS, 0, 620185029Spjd setnode->p_setname, &permsets); 621185029Spjd if (error == 0) 622185029Spjd setnode->p_matched = expanded = B_TRUE; 623185029Spjd } 624185029Spjd /* 625185029Spjd * If we expanded any sets, that will define more sets, 626185029Spjd * which we need to check. 627185029Spjd */ 628185029Spjd if (expanded) 629185029Spjd goto again; 630185029Spjd 631185029Spjd error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr); 632185029Spjd if (error == 0) 633185029Spjd goto success; 634185029Spjd } 635249195Smm error = SET_ERROR(EPERM); 636185029Spjdsuccess: 637185029Spjd 638185029Spjd cookie = NULL; 639185029Spjd while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) 640185029Spjd kmem_free(setnode, sizeof (perm_set_t)); 641185029Spjd 642185029Spjd return (error); 643185029Spjd} 644185029Spjd 645219089Spjdint 646219089Spjddsl_deleg_access(const char *dsname, const char *perm, cred_t *cr) 647219089Spjd{ 648248571Smm dsl_pool_t *dp; 649219089Spjd dsl_dataset_t *ds; 650219089Spjd int error; 651219089Spjd 652248571Smm error = dsl_pool_hold(dsname, FTAG, &dp); 653248571Smm if (error != 0) 654219089Spjd return (error); 655248571Smm error = dsl_dataset_hold(dp, dsname, FTAG, &ds); 656248571Smm if (error == 0) { 657248571Smm error = dsl_deleg_access_impl(ds, perm, cr); 658248571Smm dsl_dataset_rele(ds, FTAG); 659248571Smm } 660248571Smm dsl_pool_rele(dp, FTAG); 661219089Spjd 662219089Spjd return (error); 663219089Spjd} 664219089Spjd 665185029Spjd/* 666185029Spjd * Other routines. 667185029Spjd */ 668185029Spjd 669185029Spjdstatic void 670185029Spjdcopy_create_perms(dsl_dir_t *dd, uint64_t pzapobj, 671185029Spjd boolean_t dosets, uint64_t uid, dmu_tx_t *tx) 672185029Spjd{ 673185029Spjd objset_t *mos = dd->dd_pool->dp_meta_objset; 674185029Spjd uint64_t jumpobj, pjumpobj; 675275782Sdelphij uint64_t zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 676185029Spjd zap_cursor_t zc; 677185029Spjd zap_attribute_t za; 678185029Spjd char whokey[ZFS_MAX_DELEG_NAME]; 679185029Spjd 680185029Spjd zfs_deleg_whokey(whokey, 681185029Spjd dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE, 682185029Spjd ZFS_DELEG_LOCAL, NULL); 683185029Spjd if (zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj) != 0) 684185029Spjd return; 685185029Spjd 686185029Spjd if (zapobj == 0) { 687185029Spjd dmu_buf_will_dirty(dd->dd_dbuf, tx); 688275782Sdelphij zapobj = dsl_dir_phys(dd)->dd_deleg_zapobj = zap_create(mos, 689185029Spjd DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 690185029Spjd } 691185029Spjd 692185029Spjd zfs_deleg_whokey(whokey, 693185029Spjd dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER, 694185029Spjd ZFS_DELEG_LOCAL, &uid); 695185029Spjd if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) { 696185029Spjd jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx); 697185029Spjd VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0); 698185029Spjd } 699185029Spjd 700185029Spjd for (zap_cursor_init(&zc, mos, pjumpobj); 701185029Spjd zap_cursor_retrieve(&zc, &za) == 0; 702185029Spjd zap_cursor_advance(&zc)) { 703185029Spjd uint64_t zero = 0; 704185029Spjd ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 705185029Spjd 706185029Spjd VERIFY(zap_update(mos, jumpobj, za.za_name, 707185029Spjd 8, 1, &zero, tx) == 0); 708185029Spjd } 709185029Spjd zap_cursor_fini(&zc); 710185029Spjd} 711185029Spjd 712185029Spjd/* 713185029Spjd * set all create time permission on new dataset. 714185029Spjd */ 715185029Spjdvoid 716185029Spjddsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr) 717185029Spjd{ 718185029Spjd dsl_dir_t *dd; 719185029Spjd uint64_t uid = crgetuid(cr); 720185029Spjd 721185029Spjd if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) < 722185029Spjd SPA_VERSION_DELEGATED_PERMS) 723185029Spjd return; 724185029Spjd 725185029Spjd for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) { 726275782Sdelphij uint64_t pzapobj = dsl_dir_phys(dd)->dd_deleg_zapobj; 727185029Spjd 728185029Spjd if (pzapobj == 0) 729185029Spjd continue; 730185029Spjd 731185029Spjd copy_create_perms(sdd, pzapobj, B_FALSE, uid, tx); 732185029Spjd copy_create_perms(sdd, pzapobj, B_TRUE, uid, tx); 733185029Spjd } 734185029Spjd} 735185029Spjd 736185029Spjdint 737185029Spjddsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx) 738185029Spjd{ 739185029Spjd zap_cursor_t zc; 740185029Spjd zap_attribute_t za; 741185029Spjd 742185029Spjd if (zapobj == 0) 743185029Spjd return (0); 744185029Spjd 745185029Spjd for (zap_cursor_init(&zc, mos, zapobj); 746185029Spjd zap_cursor_retrieve(&zc, &za) == 0; 747185029Spjd zap_cursor_advance(&zc)) { 748185029Spjd ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1); 749185029Spjd VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx)); 750185029Spjd } 751185029Spjd zap_cursor_fini(&zc); 752185029Spjd VERIFY(0 == zap_destroy(mos, zapobj, tx)); 753185029Spjd return (0); 754185029Spjd} 755185029Spjd 756185029Spjdboolean_t 757185029Spjddsl_delegation_on(objset_t *os) 758185029Spjd{ 759219089Spjd return (!!spa_delegation(os->os_spa)); 760185029Spjd} 761