xref: /onnv-gate/usr/src/uts/common/fs/zfs/dsl_deleg.c (revision 4543:12bb2876a62e)
1*4543Smarks /*
2*4543Smarks  * CDDL HEADER START
3*4543Smarks  *
4*4543Smarks  * The contents of this file are subject to the terms of the
5*4543Smarks  * Common Development and Distribution License (the "License").
6*4543Smarks  * You may not use this file except in compliance with the License.
7*4543Smarks  *
8*4543Smarks  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*4543Smarks  * or http://www.opensolaris.org/os/licensing.
10*4543Smarks  * See the License for the specific language governing permissions
11*4543Smarks  * and limitations under the License.
12*4543Smarks  *
13*4543Smarks  * When distributing Covered Code, include this CDDL HEADER in each
14*4543Smarks  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*4543Smarks  * If applicable, add the following below this CDDL HEADER, with the
16*4543Smarks  * fields enclosed by brackets "[]" replaced with your own identifying
17*4543Smarks  * information: Portions Copyright [yyyy] [name of copyright owner]
18*4543Smarks  *
19*4543Smarks  * CDDL HEADER END
20*4543Smarks  */
21*4543Smarks /*
22*4543Smarks  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
23*4543Smarks  * Use is subject to license terms.
24*4543Smarks  */
25*4543Smarks 
26*4543Smarks /*
27*4543Smarks  * DSL permissions are stored in a two level zap attribute
28*4543Smarks  * mechanism.   The first level identifies the "class" of
29*4543Smarks  * entry.  The class is identified by the first 2 letters of
30*4543Smarks  * the attribute.  The second letter "l" or "d" identifies whether
31*4543Smarks  * it is a local or descendent permission.  The first letter
32*4543Smarks  * identifies the type of entry.
33*4543Smarks  *
34*4543Smarks  * ul$<id>    identifies permssions granted locally for this userid.
35*4543Smarks  * ud$<id>    identifies permissions granted on descendent datasets for
36*4543Smarks  *            this userid.
37*4543Smarks  * Ul$<id>    identifies permission sets granted locally for this userid.
38*4543Smarks  * Ud$<id>    identifies permission sets granted on descendent datasets for
39*4543Smarks  *            this userid.
40*4543Smarks  * gl$<id>    identifies permissions granted locally for this groupid.
41*4543Smarks  * gd$<id>    identifies permissions granted on descendent datasets for
42*4543Smarks  *            this groupid.
43*4543Smarks  * Gl$<id>    identifies permission sets granted locally for this groupid.
44*4543Smarks  * Gd$<id>    identifies permission sets granted on descendent datasets for
45*4543Smarks  *            this groupid.
46*4543Smarks  * el$        identifies permissions granted locally for everyone.
47*4543Smarks  * ed$        identifies permissions granted on descendent datasets
48*4543Smarks  *            for everyone.
49*4543Smarks  * El$        identifies permission sets granted locally for everyone.
50*4543Smarks  * Ed$        identifies permission sets granted to descendent datasets for
51*4543Smarks  *            everyone.
52*4543Smarks  * c-$        identifies permission to create at dataset creation time.
53*4543Smarks  * C-$        identifies permission sets to grant locally at dataset creation
54*4543Smarks  *            time.
55*4543Smarks  * s-$@<name> permissions defined in specified set @<name>
56*4543Smarks  * S-$@<name> Sets defined in named set @<name>
57*4543Smarks  *
58*4543Smarks  * Each of the above entiies points to another zap attribute that contains one
59*4543Smarks  * attribute for each allowed permission, such as create, destroy,...
60*4543Smarks  * All of the "upper" case class types will specify permission set names
61*4543Smarks  * rather than permissions.
62*4543Smarks  *
63*4543Smarks  * Basically it looks something like this:
64*4543Smarks  * ul$12 -> ZAP OBJ -> permissions...
65*4543Smarks  *
66*4543Smarks  * The ZAP OBJ is referred to as the jump object.
67*4543Smarks  */
68*4543Smarks 
69*4543Smarks #pragma ident	"%Z%%M%	%I%	%E% SMI"
70*4543Smarks 
71*4543Smarks #include <sys/dmu.h>
72*4543Smarks #include <sys/dmu_objset.h>
73*4543Smarks #include <sys/dmu_tx.h>
74*4543Smarks #include <sys/dsl_dataset.h>
75*4543Smarks #include <sys/dsl_dir.h>
76*4543Smarks #include <sys/dsl_prop.h>
77*4543Smarks #include <sys/dsl_synctask.h>
78*4543Smarks #include <sys/dsl_deleg.h>
79*4543Smarks #include <sys/spa.h>
80*4543Smarks #include <sys/spa_impl.h>
81*4543Smarks #include <sys/zio_checksum.h> /* for the default checksum value */
82*4543Smarks #include <sys/zap.h>
83*4543Smarks #include <sys/fs/zfs.h>
84*4543Smarks #include <sys/cred.h>
85*4543Smarks #include <sys/sunddi.h>
86*4543Smarks 
87*4543Smarks #include "zfs_deleg.h"
88*4543Smarks 
89*4543Smarks /*
90*4543Smarks  * Validate that user is allowed to delegate specified permissions.
91*4543Smarks  *
92*4543Smarks  * In order to delegate "create" you must have create"
93*4543Smarks  * and "allow".
94*4543Smarks  */
95*4543Smarks int
96*4543Smarks dsl_deleg_can_allow(char *ddname, nvlist_t *nvp, cred_t *cr)
97*4543Smarks {
98*4543Smarks 	nvpair_t *whopair = NULL;
99*4543Smarks 	int error = 0;
100*4543Smarks 
101*4543Smarks 	if ((error = dsl_deleg_access(ddname,
102*4543Smarks 	    ZFS_DELEG_PERM_ALLOW, cr)) != 0)
103*4543Smarks 		return (error);
104*4543Smarks 
105*4543Smarks 	while (whopair = nvlist_next_nvpair(nvp, whopair)) {
106*4543Smarks 		nvlist_t *perms;
107*4543Smarks 		nvpair_t *permpair = NULL;
108*4543Smarks 
109*4543Smarks 		VERIFY(nvpair_value_nvlist(whopair, &perms) == 0);
110*4543Smarks 
111*4543Smarks 		while (permpair = nvlist_next_nvpair(perms, permpair)) {
112*4543Smarks 			const char *perm = nvpair_name(permpair);
113*4543Smarks 
114*4543Smarks 			if (strcmp(perm, ZFS_DELEG_PERM_ALLOW) == 0)
115*4543Smarks 				return (EPERM);
116*4543Smarks 
117*4543Smarks 			if ((error = dsl_deleg_access(ddname,
118*4543Smarks 			    perm, cr)) != 0)
119*4543Smarks 				return (error);
120*4543Smarks 		}
121*4543Smarks 	}
122*4543Smarks 	return (error);
123*4543Smarks }
124*4543Smarks 
125*4543Smarks /*
126*4543Smarks  * Validate that user is allowed to unallow specified permissions.  They
127*4543Smarks  * must have the 'allow' permission, and even then can only unallow
128*4543Smarks  * perms for their uid.
129*4543Smarks  */
130*4543Smarks int
131*4543Smarks dsl_deleg_can_unallow(char *ddname, nvlist_t *nvp, cred_t *cr)
132*4543Smarks {
133*4543Smarks 	nvpair_t *whopair = NULL;
134*4543Smarks 	int error;
135*4543Smarks 
136*4543Smarks 	if ((error = dsl_deleg_access(ddname,
137*4543Smarks 	    ZFS_DELEG_PERM_ALLOW, cr)) != 0)
138*4543Smarks 		return (error);
139*4543Smarks 
140*4543Smarks 	while (whopair = nvlist_next_nvpair(nvp, whopair)) {
141*4543Smarks 		zfs_deleg_who_type_t type = nvpair_name(whopair)[0];
142*4543Smarks 		char idstr[32];
143*4543Smarks 
144*4543Smarks 		if (type != ZFS_DELEG_USER &&
145*4543Smarks 		    type != ZFS_DELEG_USER_SETS)
146*4543Smarks 			return (EPERM);
147*4543Smarks 
148*4543Smarks 		(void) snprintf(idstr, sizeof (idstr), "%lld",
149*4543Smarks 		    (longlong_t)crgetuid(cr));
150*4543Smarks 		if (strcmp(idstr, &nvpair_name(whopair)[3]) != 0)
151*4543Smarks 			return (EPERM);
152*4543Smarks 
153*4543Smarks 		continue;
154*4543Smarks 	}
155*4543Smarks 	return (0);
156*4543Smarks }
157*4543Smarks 
158*4543Smarks typedef struct {
159*4543Smarks 	nvlist_t *p_nvp;
160*4543Smarks 	boolean_t p_unset;
161*4543Smarks } perm_args_t;
162*4543Smarks 
163*4543Smarks static void
164*4543Smarks dsl_deleg_set_sync(void *arg1, void *arg2, cred_t *cr, dmu_tx_t *tx)
165*4543Smarks {
166*4543Smarks 	dsl_dir_t *dd = arg1;
167*4543Smarks 	perm_args_t *pa = arg2;
168*4543Smarks 	objset_t *mos = dd->dd_pool->dp_meta_objset;
169*4543Smarks 	nvpair_t *whopair = NULL;
170*4543Smarks 	uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
171*4543Smarks 
172*4543Smarks 	if (zapobj == 0) {
173*4543Smarks 		if (pa->p_unset)
174*4543Smarks 			return;
175*4543Smarks 		dmu_buf_will_dirty(dd->dd_dbuf, tx);
176*4543Smarks 		zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
177*4543Smarks 		    DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
178*4543Smarks 	}
179*4543Smarks 
180*4543Smarks 	while (whopair = nvlist_next_nvpair(pa->p_nvp, whopair)) {
181*4543Smarks 		const char *whokey = nvpair_name(whopair);
182*4543Smarks 		nvlist_t *perms;
183*4543Smarks 		nvpair_t *permpair = NULL;
184*4543Smarks 		uint64_t jumpobj;
185*4543Smarks 
186*4543Smarks 		if (nvpair_value_nvlist(whopair, &perms) != 0) {
187*4543Smarks 			if (zap_lookup(mos, zapobj, whokey, 8,
188*4543Smarks 			    1, &jumpobj) == 0) {
189*4543Smarks 				(void) zap_remove(mos, zapobj, whokey, tx);
190*4543Smarks 				VERIFY(0 == zap_destroy(mos, jumpobj, tx));
191*4543Smarks 			}
192*4543Smarks 			spa_history_internal_log(LOG_DS_PERM_WHO_REMOVE,
193*4543Smarks 			    dd->dd_pool->dp_spa, tx, cr,
194*4543Smarks 			    "%s dataset = %llu", whokey,
195*4543Smarks 			    dd->dd_phys->dd_head_dataset_obj);
196*4543Smarks 			continue;
197*4543Smarks 		}
198*4543Smarks 
199*4543Smarks 		if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) != 0) {
200*4543Smarks 			/*
201*4543Smarks 			 * If object doesn't exist and we are removing
202*4543Smarks 			 * it, then just continue to next item in nvlist
203*4543Smarks 			 */
204*4543Smarks 			if (pa->p_unset == 1)
205*4543Smarks 				continue;
206*4543Smarks 			jumpobj = zap_create(mos, DMU_OT_DSL_PERMS,
207*4543Smarks 			    DMU_OT_NONE, 0, tx);
208*4543Smarks 			VERIFY(zap_update(mos, zapobj,
209*4543Smarks 			    whokey, 8, 1, &jumpobj, tx) == 0);
210*4543Smarks 		}
211*4543Smarks 
212*4543Smarks 		while (permpair = nvlist_next_nvpair(perms, permpair)) {
213*4543Smarks 			const char *perm = nvpair_name(permpair);
214*4543Smarks 			uint64_t n = 0;
215*4543Smarks 
216*4543Smarks 			if (pa->p_unset) {
217*4543Smarks 				(void) zap_remove(mos, jumpobj, perm, tx);
218*4543Smarks 				if (zap_count(mos, jumpobj, &n) == 0 && !n) {
219*4543Smarks 					(void) zap_remove(mos, zapobj,
220*4543Smarks 					    whokey, tx);
221*4543Smarks 					VERIFY(0 == zap_destroy(mos,
222*4543Smarks 					    jumpobj, tx));
223*4543Smarks 				}
224*4543Smarks 			} else {
225*4543Smarks 				VERIFY(zap_update(mos, jumpobj,
226*4543Smarks 				    perm, 8, 1, &n, tx) == 0);
227*4543Smarks 			}
228*4543Smarks 			spa_history_internal_log((pa->p_unset == B_FALSE) ?
229*4543Smarks 			    LOG_DS_PERM_UPDATE : LOG_DS_PERM_REMOVE,
230*4543Smarks 			    dd->dd_pool->dp_spa, tx, cr,
231*4543Smarks 			    "%s %s dataset = %llu", whokey, perm,
232*4543Smarks 			    dd->dd_phys->dd_head_dataset_obj);
233*4543Smarks 		}
234*4543Smarks 	}
235*4543Smarks }
236*4543Smarks 
237*4543Smarks int
238*4543Smarks dsl_deleg_set(const char *ddname, nvlist_t *nvp, boolean_t unset)
239*4543Smarks {
240*4543Smarks 	dsl_dir_t *dd;
241*4543Smarks 	int error;
242*4543Smarks 	perm_args_t pa;
243*4543Smarks 	nvpair_t *whopair = NULL;
244*4543Smarks 	int blocks_modified = 0;
245*4543Smarks 
246*4543Smarks 	error = dsl_dir_open(ddname, FTAG, &dd, NULL);
247*4543Smarks 	if (error)
248*4543Smarks 		return (error);
249*4543Smarks 
250*4543Smarks 	if (spa_version(dmu_objset_spa(dd->dd_pool->dp_meta_objset)) <
251*4543Smarks 	    ZFS_VERSION_DELEGATED_PERMS) {
252*4543Smarks 		dsl_dir_close(dd, FTAG);
253*4543Smarks 		return (ENOTSUP);
254*4543Smarks 	}
255*4543Smarks 
256*4543Smarks 	while (whopair = nvlist_next_nvpair(nvp, whopair))
257*4543Smarks 		blocks_modified++;
258*4543Smarks 
259*4543Smarks 	pa.p_nvp = nvp;
260*4543Smarks 	pa.p_unset = unset;
261*4543Smarks 
262*4543Smarks 	error = dsl_sync_task_do(dd->dd_pool, NULL, dsl_deleg_set_sync,
263*4543Smarks 	    dd, &pa, blocks_modified);
264*4543Smarks 	dsl_dir_close(dd, FTAG);
265*4543Smarks 
266*4543Smarks 	return (error);
267*4543Smarks }
268*4543Smarks 
269*4543Smarks /*
270*4543Smarks  * Find all 'allow' permissions from a given point and then continue
271*4543Smarks  * traversing up to the root.
272*4543Smarks  *
273*4543Smarks  * This function constructs an nvlist of nvlists.
274*4543Smarks  * each setpoint is an nvlist composed of an nvlist of an nvlist
275*4543Smarks  * of the individual * users/groups/everyone/create
276*4543Smarks  * permissions.
277*4543Smarks  *
278*4543Smarks  * The nvlist will look like this.
279*4543Smarks  *
280*4543Smarks  * { source fsname -> { whokeys { permissions,...}, ...}}
281*4543Smarks  *
282*4543Smarks  * The fsname nvpairs will be arranged in a bottom up order.  For example,
283*4543Smarks  * if we have the following structure a/b/c then the nvpairs for the fsnames
284*4543Smarks  * will be ordered a/b/c, a/b, a.
285*4543Smarks  */
286*4543Smarks int
287*4543Smarks dsl_deleg_get(const char *ddname, nvlist_t **nvp)
288*4543Smarks {
289*4543Smarks 	dsl_dir_t *dd, *startdd;
290*4543Smarks 	dsl_pool_t *dp;
291*4543Smarks 	int error;
292*4543Smarks 	objset_t *mos;
293*4543Smarks 
294*4543Smarks 	error = dsl_dir_open(ddname, FTAG, &startdd, NULL);
295*4543Smarks 	if (error)
296*4543Smarks 		return (error);
297*4543Smarks 
298*4543Smarks 	dp = startdd->dd_pool;
299*4543Smarks 	mos = dp->dp_meta_objset;
300*4543Smarks 
301*4543Smarks 	VERIFY(nvlist_alloc(nvp, NV_UNIQUE_NAME, KM_SLEEP) == 0);
302*4543Smarks 
303*4543Smarks 	rw_enter(&dp->dp_config_rwlock, RW_READER);
304*4543Smarks 	for (dd = startdd; dd != NULL; dd = dd->dd_parent) {
305*4543Smarks 		zap_cursor_t basezc;
306*4543Smarks 		zap_attribute_t baseza;
307*4543Smarks 		nvlist_t *sp_nvp;
308*4543Smarks 		uint64_t n;
309*4543Smarks 		char source[MAXNAMELEN];
310*4543Smarks 
311*4543Smarks 		if (dd->dd_phys->dd_deleg_zapobj &&
312*4543Smarks 		    (zap_count(mos, dd->dd_phys->dd_deleg_zapobj,
313*4543Smarks 		    &n) == 0) && n) {
314*4543Smarks 			VERIFY(nvlist_alloc(&sp_nvp,
315*4543Smarks 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
316*4543Smarks 		} else {
317*4543Smarks 			continue;
318*4543Smarks 		}
319*4543Smarks 
320*4543Smarks 		for (zap_cursor_init(&basezc, mos,
321*4543Smarks 		    dd->dd_phys->dd_deleg_zapobj);
322*4543Smarks 		    zap_cursor_retrieve(&basezc, &baseza) == 0;
323*4543Smarks 		    zap_cursor_advance(&basezc)) {
324*4543Smarks 			zap_cursor_t zc;
325*4543Smarks 			zap_attribute_t za;
326*4543Smarks 			nvlist_t *perms_nvp;
327*4543Smarks 
328*4543Smarks 			ASSERT(baseza.za_integer_length == 8);
329*4543Smarks 			ASSERT(baseza.za_num_integers == 1);
330*4543Smarks 
331*4543Smarks 			VERIFY(nvlist_alloc(&perms_nvp,
332*4543Smarks 			    NV_UNIQUE_NAME, KM_SLEEP) == 0);
333*4543Smarks 			for (zap_cursor_init(&zc, mos, baseza.za_first_integer);
334*4543Smarks 			    zap_cursor_retrieve(&zc, &za) == 0;
335*4543Smarks 			    zap_cursor_advance(&zc)) {
336*4543Smarks 				VERIFY(nvlist_add_boolean(perms_nvp,
337*4543Smarks 				    za.za_name) == 0);
338*4543Smarks 			}
339*4543Smarks 			zap_cursor_fini(&zc);
340*4543Smarks 			VERIFY(nvlist_add_nvlist(sp_nvp, baseza.za_name,
341*4543Smarks 			    perms_nvp) == 0);
342*4543Smarks 			nvlist_free(perms_nvp);
343*4543Smarks 		}
344*4543Smarks 
345*4543Smarks 		zap_cursor_fini(&basezc);
346*4543Smarks 
347*4543Smarks 		dsl_dir_name(dd, source);
348*4543Smarks 		VERIFY(nvlist_add_nvlist(*nvp, source, sp_nvp) == 0);
349*4543Smarks 		nvlist_free(sp_nvp);
350*4543Smarks 	}
351*4543Smarks 	rw_exit(&dp->dp_config_rwlock);
352*4543Smarks 
353*4543Smarks 	dsl_dir_close(startdd, FTAG);
354*4543Smarks 	return (0);
355*4543Smarks }
356*4543Smarks 
357*4543Smarks /*
358*4543Smarks  * Routines for dsl_deleg_access() -- access checking.
359*4543Smarks  */
360*4543Smarks typedef struct perm_set {
361*4543Smarks 	avl_node_t	p_node;
362*4543Smarks 	char		p_setname[ZFS_MAX_DELEG_NAME];
363*4543Smarks 	boolean_t	p_matched;
364*4543Smarks } perm_set_t;
365*4543Smarks 
366*4543Smarks static int
367*4543Smarks perm_set_compare(const void *arg1, const void *arg2)
368*4543Smarks {
369*4543Smarks 	const perm_set_t *node1 = arg1;
370*4543Smarks 	const perm_set_t *node2 = arg2;
371*4543Smarks 	int val;
372*4543Smarks 
373*4543Smarks 	val = strcmp(node1->p_setname, node2->p_setname);
374*4543Smarks 	if (val == 0)
375*4543Smarks 		return (0);
376*4543Smarks 	return (val > 0 ? 1 : -1);
377*4543Smarks }
378*4543Smarks 
379*4543Smarks /*
380*4543Smarks  * Determine whether a specified permission exists.
381*4543Smarks  *
382*4543Smarks  * First the base attribute has to be retrieved.  i.e. ul$12
383*4543Smarks  * Once the base object has been retrieved the actual permission
384*4543Smarks  * is lookup up in the zap object the base object points to.
385*4543Smarks  *
386*4543Smarks  * Return 0 if permission exists, ENOENT if there is no whokey, EPERM if
387*4543Smarks  * there is no perm in that jumpobj.
388*4543Smarks  */
389*4543Smarks static int
390*4543Smarks dsl_check_access(objset_t *mos, uint64_t zapobj,
391*4543Smarks     char type, char checkflag, void *valp, const char *perm)
392*4543Smarks {
393*4543Smarks 	int error;
394*4543Smarks 	uint64_t jumpobj, zero;
395*4543Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
396*4543Smarks 
397*4543Smarks 	zfs_deleg_whokey(whokey, type, checkflag, valp);
398*4543Smarks 	error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
399*4543Smarks 	if (error == 0) {
400*4543Smarks 		error = zap_lookup(mos, jumpobj, perm, 8, 1, &zero);
401*4543Smarks 		if (error == ENOENT)
402*4543Smarks 			error = EPERM;
403*4543Smarks 	}
404*4543Smarks 	return (error);
405*4543Smarks }
406*4543Smarks 
407*4543Smarks /*
408*4543Smarks  * check a specified user/group for a requested permission
409*4543Smarks  */
410*4543Smarks static int
411*4543Smarks dsl_check_user_access(objset_t *os, uint64_t zapobj, const char *perm,
412*4543Smarks     int checkflag, cred_t *cr)
413*4543Smarks {
414*4543Smarks 	const	gid_t *gids;
415*4543Smarks 	int	ngids;
416*4543Smarks 	int	i;
417*4543Smarks 	uint64_t id;
418*4543Smarks 
419*4543Smarks 	/* check for user */
420*4543Smarks 	id = crgetuid(cr);
421*4543Smarks 	if (dsl_check_access(os, zapobj,
422*4543Smarks 	    ZFS_DELEG_USER, checkflag, &id, perm) == 0)
423*4543Smarks 		return (0);
424*4543Smarks 
425*4543Smarks 	/* check for users primary group */
426*4543Smarks 	id = crgetgid(cr);
427*4543Smarks 	if (dsl_check_access(os, zapobj,
428*4543Smarks 	    ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
429*4543Smarks 		return (0);
430*4543Smarks 
431*4543Smarks 	/* check for everyone entry */
432*4543Smarks 	id = -1;
433*4543Smarks 	if (dsl_check_access(os, zapobj,
434*4543Smarks 	    ZFS_DELEG_EVERYONE, checkflag, &id, perm) == 0)
435*4543Smarks 		return (0);
436*4543Smarks 
437*4543Smarks 	/* check each supplemental group user is a member of */
438*4543Smarks 	ngids = crgetngroups(cr);
439*4543Smarks 	gids = crgetgroups(cr);
440*4543Smarks 	for (i = 0; i != ngids; i++) {
441*4543Smarks 		id = gids[i];
442*4543Smarks 		if (dsl_check_access(os, zapobj,
443*4543Smarks 		    ZFS_DELEG_GROUP, checkflag, &id, perm) == 0)
444*4543Smarks 			return (0);
445*4543Smarks 	}
446*4543Smarks 
447*4543Smarks 	return (EPERM);
448*4543Smarks }
449*4543Smarks 
450*4543Smarks /*
451*4543Smarks  * Iterate over the sets specified in the specified zapobj
452*4543Smarks  * and load them into the permsets avl tree.
453*4543Smarks  */
454*4543Smarks static int
455*4543Smarks dsl_load_sets(objset_t *mos, uint64_t zapobj,
456*4543Smarks     char type, char checkflag, void *valp, avl_tree_t *avl)
457*4543Smarks {
458*4543Smarks 	zap_cursor_t zc;
459*4543Smarks 	zap_attribute_t za;
460*4543Smarks 	perm_set_t *permnode;
461*4543Smarks 	avl_index_t idx;
462*4543Smarks 	uint64_t jumpobj;
463*4543Smarks 	int error;
464*4543Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
465*4543Smarks 
466*4543Smarks 	zfs_deleg_whokey(whokey, type, checkflag, valp);
467*4543Smarks 
468*4543Smarks 	error = zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj);
469*4543Smarks 	if (error != 0)
470*4543Smarks 		return (error);
471*4543Smarks 
472*4543Smarks 	for (zap_cursor_init(&zc, mos, jumpobj);
473*4543Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
474*4543Smarks 	    zap_cursor_advance(&zc)) {
475*4543Smarks 		permnode = kmem_alloc(sizeof (perm_set_t), KM_SLEEP);
476*4543Smarks 		(void) strlcpy(permnode->p_setname, za.za_name,
477*4543Smarks 		    sizeof (permnode->p_setname));
478*4543Smarks 		permnode->p_matched = B_FALSE;
479*4543Smarks 
480*4543Smarks 		if (avl_find(avl, permnode, &idx) == NULL) {
481*4543Smarks 			avl_insert(avl, permnode, idx);
482*4543Smarks 		} else {
483*4543Smarks 			kmem_free(permnode, sizeof (perm_set_t));
484*4543Smarks 		}
485*4543Smarks 	}
486*4543Smarks 	zap_cursor_fini(&zc);
487*4543Smarks 	return (0);
488*4543Smarks }
489*4543Smarks 
490*4543Smarks /*
491*4543Smarks  * Load all permissions user based on cred belongs to.
492*4543Smarks  */
493*4543Smarks static void
494*4543Smarks dsl_load_user_sets(objset_t *mos, uint64_t zapobj, avl_tree_t *avl,
495*4543Smarks     char checkflag, cred_t *cr)
496*4543Smarks {
497*4543Smarks 	const	gid_t *gids;
498*4543Smarks 	int	ngids, i;
499*4543Smarks 	uint64_t id;
500*4543Smarks 
501*4543Smarks 	id = crgetuid(cr);
502*4543Smarks 	(void) dsl_load_sets(mos, zapobj,
503*4543Smarks 	    ZFS_DELEG_USER_SETS, checkflag, &id, avl);
504*4543Smarks 
505*4543Smarks 	id = crgetgid(cr);
506*4543Smarks 	(void) dsl_load_sets(mos, zapobj,
507*4543Smarks 	    ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
508*4543Smarks 
509*4543Smarks 	(void) dsl_load_sets(mos, zapobj,
510*4543Smarks 	    ZFS_DELEG_EVERYONE_SETS, checkflag, NULL, avl);
511*4543Smarks 
512*4543Smarks 	ngids = crgetngroups(cr);
513*4543Smarks 	gids = crgetgroups(cr);
514*4543Smarks 	for (i = 0; i != ngids; i++) {
515*4543Smarks 		id = gids[i];
516*4543Smarks 		(void) dsl_load_sets(mos, zapobj,
517*4543Smarks 		    ZFS_DELEG_GROUP_SETS, checkflag, &id, avl);
518*4543Smarks 	}
519*4543Smarks }
520*4543Smarks 
521*4543Smarks /*
522*4543Smarks  * Check if user has requested permission.
523*4543Smarks  */
524*4543Smarks int
525*4543Smarks dsl_deleg_access(const char *ddname, const char *perm, cred_t *cr)
526*4543Smarks {
527*4543Smarks 	dsl_dir_t *dd, *startdd;
528*4543Smarks 	dsl_pool_t *dp;
529*4543Smarks 	void *cookie;
530*4543Smarks 	int	error;
531*4543Smarks 	char	checkflag = ZFS_DELEG_LOCAL;
532*4543Smarks 	const char *tail;
533*4543Smarks 	objset_t *mos;
534*4543Smarks 	avl_tree_t permsets;
535*4543Smarks 	perm_set_t *setnode;
536*4543Smarks 
537*4543Smarks 	/*
538*4543Smarks 	 * Use tail so that zfs_ioctl() code doesn't have
539*4543Smarks 	 * to always to to figure out parent name in order
540*4543Smarks 	 * to do access check.  for example renaming a snapshot
541*4543Smarks 	 */
542*4543Smarks 	error = dsl_dir_open(ddname, FTAG, &startdd, &tail);
543*4543Smarks 	if (error)
544*4543Smarks 		return (error);
545*4543Smarks 
546*4543Smarks 	if (tail && tail[0] != '@') {
547*4543Smarks 		dsl_dir_close(startdd, FTAG);
548*4543Smarks 		return (ENOENT);
549*4543Smarks 	}
550*4543Smarks 	dp = startdd->dd_pool;
551*4543Smarks 	mos = dp->dp_meta_objset;
552*4543Smarks 
553*4543Smarks 	if (dsl_delegation_on(mos) == B_FALSE) {
554*4543Smarks 		dsl_dir_close(startdd, FTAG);
555*4543Smarks 		return (ECANCELED);
556*4543Smarks 	}
557*4543Smarks 
558*4543Smarks 	if (spa_version(dmu_objset_spa(dp->dp_meta_objset)) <
559*4543Smarks 	    ZFS_VERSION_DELEGATED_PERMS) {
560*4543Smarks 		dsl_dir_close(startdd, FTAG);
561*4543Smarks 		return (EPERM);
562*4543Smarks 	}
563*4543Smarks 
564*4543Smarks 	avl_create(&permsets, perm_set_compare, sizeof (perm_set_t),
565*4543Smarks 	    offsetof(perm_set_t, p_node));
566*4543Smarks 
567*4543Smarks 	rw_enter(&dp->dp_config_rwlock, RW_READER);
568*4543Smarks 	for (dd = startdd; dd != NULL; dd = dd->dd_parent,
569*4543Smarks 	    checkflag = ZFS_DELEG_DESCENDENT) {
570*4543Smarks 		uint64_t zapobj;
571*4543Smarks 		boolean_t expanded;
572*4543Smarks 
573*4543Smarks 		/*
574*4543Smarks 		 * If not in global zone then make sure
575*4543Smarks 		 * the zoned property is set
576*4543Smarks 		 */
577*4543Smarks 		if (!INGLOBALZONE(curproc)) {
578*4543Smarks 			uint64_t zoned;
579*4543Smarks 
580*4543Smarks 			if (dsl_prop_get_ds_locked(dd,
581*4543Smarks 			    zfs_prop_to_name(ZFS_PROP_ZONED),
582*4543Smarks 			    8, 1, &zoned, NULL) != 0)
583*4543Smarks 				break;
584*4543Smarks 
585*4543Smarks 			/*
586*4543Smarks 			 * if zoned property isn't set then break
587*4543Smarks 			 * out and return EPERM.
588*4543Smarks 			 */
589*4543Smarks 			if (!zoned)
590*4543Smarks 				break;
591*4543Smarks 		}
592*4543Smarks 		zapobj = dd->dd_phys->dd_deleg_zapobj;
593*4543Smarks 
594*4543Smarks 		if (zapobj == 0)
595*4543Smarks 			continue;
596*4543Smarks 
597*4543Smarks 		dsl_load_user_sets(mos, zapobj, &permsets, checkflag, cr);
598*4543Smarks 		setnode = avl_first(&permsets);
599*4543Smarks again:
600*4543Smarks 		expanded = B_FALSE;
601*4543Smarks 		for (setnode = avl_first(&permsets); setnode;
602*4543Smarks 		    setnode = AVL_NEXT(&permsets, setnode)) {
603*4543Smarks 
604*4543Smarks 			if (setnode->p_matched == B_TRUE)
605*4543Smarks 				continue;
606*4543Smarks 
607*4543Smarks 			/* See if this set directly grants this permission */
608*4543Smarks 			error = dsl_check_access(mos, zapobj,
609*4543Smarks 			    ZFS_DELEG_NAMED_SET, 0, setnode->p_setname, perm);
610*4543Smarks 			if (error == 0)
611*4543Smarks 				goto success;
612*4543Smarks 			if (error == EPERM)
613*4543Smarks 				setnode->p_matched = B_TRUE;
614*4543Smarks 
615*4543Smarks 			/* See if this set includes other sets */
616*4543Smarks 			error = dsl_load_sets(mos, zapobj,
617*4543Smarks 			    ZFS_DELEG_NAMED_SET_SETS, 0,
618*4543Smarks 			    setnode->p_setname, &permsets);
619*4543Smarks 			if (error == 0)
620*4543Smarks 				setnode->p_matched = expanded = B_TRUE;
621*4543Smarks 		}
622*4543Smarks 		/*
623*4543Smarks 		 * If we expanded any sets, that will define more sets,
624*4543Smarks 		 * which we need to check.
625*4543Smarks 		 */
626*4543Smarks 		if (expanded)
627*4543Smarks 			goto again;
628*4543Smarks 
629*4543Smarks 		error = dsl_check_user_access(mos, zapobj, perm, checkflag, cr);
630*4543Smarks 		if (error == 0)
631*4543Smarks 			goto success;
632*4543Smarks 	}
633*4543Smarks 	error = EPERM;
634*4543Smarks success:
635*4543Smarks 	rw_exit(&dp->dp_config_rwlock);
636*4543Smarks 	dsl_dir_close(startdd, FTAG);
637*4543Smarks 
638*4543Smarks 	cookie = NULL;
639*4543Smarks 	while ((setnode = avl_destroy_nodes(&permsets, &cookie)) != NULL) {
640*4543Smarks 		/* These sets were used but never defined! */
641*4543Smarks 		kmem_free(setnode, sizeof (perm_set_t));
642*4543Smarks 	}
643*4543Smarks 
644*4543Smarks 	return (error);
645*4543Smarks }
646*4543Smarks 
647*4543Smarks /*
648*4543Smarks  * Other routines.
649*4543Smarks  */
650*4543Smarks 
651*4543Smarks static void
652*4543Smarks copy_create_perms(objset_t *mos, uint64_t pzapobj, dsl_dir_t *dd,
653*4543Smarks     boolean_t dosets, uint64_t uid, dmu_tx_t *tx)
654*4543Smarks {
655*4543Smarks 	int error;
656*4543Smarks 	uint64_t jumpobj, pjumpobj;
657*4543Smarks 	uint64_t zero = 0;
658*4543Smarks 	uint64_t zapobj = dd->dd_phys->dd_deleg_zapobj;
659*4543Smarks 	zap_cursor_t zc;
660*4543Smarks 	zap_attribute_t za;
661*4543Smarks 	char whokey[ZFS_MAX_DELEG_NAME];
662*4543Smarks 
663*4543Smarks 	zfs_deleg_whokey(whokey,
664*4543Smarks 	    dosets ? ZFS_DELEG_CREATE_SETS : ZFS_DELEG_CREATE,
665*4543Smarks 	    ZFS_DELEG_LOCAL, NULL);
666*4543Smarks 	error = zap_lookup(mos, pzapobj, whokey, 8, 1, &pjumpobj);
667*4543Smarks 	if (error != 0)
668*4543Smarks 		return;
669*4543Smarks 
670*4543Smarks 	zfs_deleg_whokey(whokey,
671*4543Smarks 	    dosets ? ZFS_DELEG_USER_SETS : ZFS_DELEG_USER,
672*4543Smarks 	    ZFS_DELEG_LOCAL, &uid);
673*4543Smarks 
674*4543Smarks 	if (zapobj == 0) {
675*4543Smarks 		dmu_buf_will_dirty(dd->dd_dbuf, tx);
676*4543Smarks 		zapobj = dd->dd_phys->dd_deleg_zapobj = zap_create(mos,
677*4543Smarks 		    DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
678*4543Smarks 	}
679*4543Smarks 
680*4543Smarks 	if (zap_lookup(mos, zapobj, whokey, 8, 1, &jumpobj) == ENOENT) {
681*4543Smarks 		jumpobj = zap_create(mos, DMU_OT_DSL_PERMS, DMU_OT_NONE, 0, tx);
682*4543Smarks 		VERIFY(zap_add(mos, zapobj, whokey, 8, 1, &jumpobj, tx) == 0);
683*4543Smarks 	}
684*4543Smarks 
685*4543Smarks 	for (zap_cursor_init(&zc, mos, pjumpobj);
686*4543Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
687*4543Smarks 	    zap_cursor_advance(&zc)) {
688*4543Smarks 		ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
689*4543Smarks 
690*4543Smarks 		VERIFY(zap_update(mos, jumpobj, za.za_name,
691*4543Smarks 		    8, 1, &zero, tx) == 0);
692*4543Smarks 	}
693*4543Smarks 	zap_cursor_fini(&zc);
694*4543Smarks }
695*4543Smarks 
696*4543Smarks /*
697*4543Smarks  * set all create time permission on new dataset.
698*4543Smarks  */
699*4543Smarks void
700*4543Smarks dsl_deleg_set_create_perms(dsl_dir_t *sdd, dmu_tx_t *tx, cred_t *cr)
701*4543Smarks {
702*4543Smarks 	dsl_dir_t *dd;
703*4543Smarks 	objset_t *mos = sdd->dd_pool->dp_meta_objset;
704*4543Smarks 
705*4543Smarks 	if (spa_version(dmu_objset_spa(sdd->dd_pool->dp_meta_objset)) <
706*4543Smarks 	    ZFS_VERSION_DELEGATED_PERMS)
707*4543Smarks 		return;
708*4543Smarks 
709*4543Smarks 	for (dd = sdd->dd_parent; dd != NULL; dd = dd->dd_parent) {
710*4543Smarks 		uint64_t pobj = dd->dd_phys->dd_deleg_zapobj;
711*4543Smarks 
712*4543Smarks 		if (pobj == 0)
713*4543Smarks 			continue;
714*4543Smarks 
715*4543Smarks 		copy_create_perms(mos, pobj, sdd, B_FALSE, crgetuid(cr), tx);
716*4543Smarks 		copy_create_perms(mos, pobj, sdd, B_TRUE, crgetuid(cr), tx);
717*4543Smarks 	}
718*4543Smarks }
719*4543Smarks 
720*4543Smarks int
721*4543Smarks dsl_deleg_destroy(objset_t *mos, uint64_t zapobj, dmu_tx_t *tx)
722*4543Smarks {
723*4543Smarks 	zap_cursor_t zc;
724*4543Smarks 	zap_attribute_t za;
725*4543Smarks 
726*4543Smarks 	if (zapobj == 0)
727*4543Smarks 		return (0);
728*4543Smarks 
729*4543Smarks 	for (zap_cursor_init(&zc, mos, zapobj);
730*4543Smarks 	    zap_cursor_retrieve(&zc, &za) == 0;
731*4543Smarks 	    zap_cursor_advance(&zc)) {
732*4543Smarks 		ASSERT(za.za_integer_length == 8 && za.za_num_integers == 1);
733*4543Smarks 		VERIFY(0 == zap_destroy(mos, za.za_first_integer, tx));
734*4543Smarks 	}
735*4543Smarks 	zap_cursor_fini(&zc);
736*4543Smarks 	VERIFY(0 == zap_destroy(mos, zapobj, tx));
737*4543Smarks 	return (0);
738*4543Smarks }
739*4543Smarks 
740*4543Smarks boolean_t
741*4543Smarks dsl_delegation_on(objset_t *os)
742*4543Smarks {
743*4543Smarks 	return (os->os->os_spa->spa_delegation);
744*4543Smarks }
745