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