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