fs: merge xattr_acl.c into posix_acl.c
[deliverable/linux.git] / fs / posix_acl.c
CommitLineData
1da177e4 1/*
5c8ebd57 2 * Copyright (C) 2002,2003 by Andreas Gruenbacher <a.gruenbacher@computer.org>
1da177e4 3 *
5c8ebd57
CH
4 * Fixes from William Schumacher incorporated on 15 March 2001.
5 * (Reported by Charles Bertsch, <CBertsch@microtest.com>).
1da177e4
LT
6 */
7
8/*
9 * This file contains generic functions for manipulating
10 * POSIX 1003.1e draft standard 17 ACLs.
11 */
12
13#include <linux/kernel.h>
14#include <linux/slab.h>
60063497 15#include <linux/atomic.h>
1da177e4
LT
16#include <linux/fs.h>
17#include <linux/sched.h>
18#include <linux/posix_acl.h>
5c8ebd57 19#include <linux/posix_acl_xattr.h>
630d9c47 20#include <linux/export.h>
5c8ebd57 21#include <linux/user_namespace.h>
1da177e4 22
f61f6da0 23EXPORT_SYMBOL(posix_acl_init);
1da177e4 24EXPORT_SYMBOL(posix_acl_alloc);
1da177e4
LT
25EXPORT_SYMBOL(posix_acl_valid);
26EXPORT_SYMBOL(posix_acl_equiv_mode);
27EXPORT_SYMBOL(posix_acl_from_mode);
1da177e4 28
f61f6da0
CL
29/*
30 * Init a fresh posix_acl
31 */
32void
33posix_acl_init(struct posix_acl *acl, int count)
34{
35 atomic_set(&acl->a_refcount, 1);
36 acl->a_count = count;
37}
38
1da177e4
LT
39/*
40 * Allocate a new ACL with the specified number of entries.
41 */
42struct posix_acl *
dd0fc66f 43posix_acl_alloc(int count, gfp_t flags)
1da177e4
LT
44{
45 const size_t size = sizeof(struct posix_acl) +
46 count * sizeof(struct posix_acl_entry);
47 struct posix_acl *acl = kmalloc(size, flags);
f61f6da0
CL
48 if (acl)
49 posix_acl_init(acl, count);
1da177e4
LT
50 return acl;
51}
52
53/*
54 * Clone an ACL.
55 */
edde854e 56static struct posix_acl *
dd0fc66f 57posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
1da177e4
LT
58{
59 struct posix_acl *clone = NULL;
60
61 if (acl) {
62 int size = sizeof(struct posix_acl) + acl->a_count *
63 sizeof(struct posix_acl_entry);
52978be6
AD
64 clone = kmemdup(acl, size, flags);
65 if (clone)
1da177e4 66 atomic_set(&clone->a_refcount, 1);
1da177e4
LT
67 }
68 return clone;
69}
70
71/*
72 * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
73 */
74int
75posix_acl_valid(const struct posix_acl *acl)
76{
77 const struct posix_acl_entry *pa, *pe;
78 int state = ACL_USER_OBJ;
2f6f0654
EB
79 kuid_t prev_uid = INVALID_UID;
80 kgid_t prev_gid = INVALID_GID;
1da177e4
LT
81 int needs_mask = 0;
82
83 FOREACH_ACL_ENTRY(pa, acl, pe) {
84 if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
85 return -EINVAL;
86 switch (pa->e_tag) {
87 case ACL_USER_OBJ:
88 if (state == ACL_USER_OBJ) {
1da177e4
LT
89 state = ACL_USER;
90 break;
91 }
92 return -EINVAL;
93
94 case ACL_USER:
95 if (state != ACL_USER)
96 return -EINVAL;
2f6f0654 97 if (!uid_valid(pa->e_uid))
1da177e4 98 return -EINVAL;
2f6f0654
EB
99 if (uid_valid(prev_uid) &&
100 uid_lte(pa->e_uid, prev_uid))
101 return -EINVAL;
102 prev_uid = pa->e_uid;
1da177e4
LT
103 needs_mask = 1;
104 break;
105
106 case ACL_GROUP_OBJ:
107 if (state == ACL_USER) {
1da177e4
LT
108 state = ACL_GROUP;
109 break;
110 }
111 return -EINVAL;
112
113 case ACL_GROUP:
114 if (state != ACL_GROUP)
115 return -EINVAL;
2f6f0654
EB
116 if (!gid_valid(pa->e_gid))
117 return -EINVAL;
118 if (gid_valid(prev_gid) &&
119 gid_lte(pa->e_gid, prev_gid))
1da177e4 120 return -EINVAL;
2f6f0654 121 prev_gid = pa->e_gid;
1da177e4
LT
122 needs_mask = 1;
123 break;
124
125 case ACL_MASK:
126 if (state != ACL_GROUP)
127 return -EINVAL;
128 state = ACL_OTHER;
129 break;
130
131 case ACL_OTHER:
132 if (state == ACL_OTHER ||
133 (state == ACL_GROUP && !needs_mask)) {
134 state = 0;
135 break;
136 }
137 return -EINVAL;
138
139 default:
140 return -EINVAL;
141 }
142 }
143 if (state == 0)
144 return 0;
145 return -EINVAL;
146}
147
148/*
149 * Returns 0 if the acl can be exactly represented in the traditional
150 * file mode permission bits, or else 1. Returns -E... on error.
151 */
152int
d6952123 153posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
1da177e4
LT
154{
155 const struct posix_acl_entry *pa, *pe;
d6952123 156 umode_t mode = 0;
1da177e4
LT
157 int not_equiv = 0;
158
159 FOREACH_ACL_ENTRY(pa, acl, pe) {
160 switch (pa->e_tag) {
161 case ACL_USER_OBJ:
162 mode |= (pa->e_perm & S_IRWXO) << 6;
163 break;
164 case ACL_GROUP_OBJ:
165 mode |= (pa->e_perm & S_IRWXO) << 3;
166 break;
167 case ACL_OTHER:
168 mode |= pa->e_perm & S_IRWXO;
169 break;
170 case ACL_MASK:
171 mode = (mode & ~S_IRWXG) |
172 ((pa->e_perm & S_IRWXO) << 3);
173 not_equiv = 1;
174 break;
175 case ACL_USER:
176 case ACL_GROUP:
177 not_equiv = 1;
178 break;
179 default:
180 return -EINVAL;
181 }
182 }
183 if (mode_p)
184 *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
185 return not_equiv;
186}
187
188/*
189 * Create an ACL representing the file mode permission bits of an inode.
190 */
191struct posix_acl *
3a5fba19 192posix_acl_from_mode(umode_t mode, gfp_t flags)
1da177e4
LT
193{
194 struct posix_acl *acl = posix_acl_alloc(3, flags);
195 if (!acl)
196 return ERR_PTR(-ENOMEM);
197
198 acl->a_entries[0].e_tag = ACL_USER_OBJ;
1da177e4
LT
199 acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
200
201 acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
1da177e4
LT
202 acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
203
204 acl->a_entries[2].e_tag = ACL_OTHER;
1da177e4
LT
205 acl->a_entries[2].e_perm = (mode & S_IRWXO);
206 return acl;
207}
208
209/*
210 * Return 0 if current is granted want access to the inode
211 * by the acl. Returns -E... otherwise.
212 */
213int
214posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
215{
216 const struct posix_acl_entry *pa, *pe, *mask_obj;
217 int found = 0;
218
d124b60a
AG
219 want &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;
220
1da177e4
LT
221 FOREACH_ACL_ENTRY(pa, acl, pe) {
222 switch(pa->e_tag) {
223 case ACL_USER_OBJ:
224 /* (May have been checked already) */
2f6f0654 225 if (uid_eq(inode->i_uid, current_fsuid()))
1da177e4
LT
226 goto check_perm;
227 break;
228 case ACL_USER:
2f6f0654 229 if (uid_eq(pa->e_uid, current_fsuid()))
1da177e4
LT
230 goto mask;
231 break;
232 case ACL_GROUP_OBJ:
233 if (in_group_p(inode->i_gid)) {
234 found = 1;
235 if ((pa->e_perm & want) == want)
236 goto mask;
237 }
238 break;
239 case ACL_GROUP:
2f6f0654 240 if (in_group_p(pa->e_gid)) {
1da177e4
LT
241 found = 1;
242 if ((pa->e_perm & want) == want)
243 goto mask;
244 }
245 break;
246 case ACL_MASK:
247 break;
248 case ACL_OTHER:
249 if (found)
250 return -EACCES;
251 else
252 goto check_perm;
253 default:
254 return -EIO;
255 }
256 }
257 return -EIO;
258
259mask:
260 for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
261 if (mask_obj->e_tag == ACL_MASK) {
262 if ((pa->e_perm & mask_obj->e_perm & want) == want)
263 return 0;
264 return -EACCES;
265 }
266 }
267
268check_perm:
269 if ((pa->e_perm & want) == want)
270 return 0;
271 return -EACCES;
272}
273
274/*
275 * Modify acl when creating a new inode. The caller must ensure the acl is
276 * only referenced once.
277 *
278 * mode_p initially must contain the mode parameter to the open() / creat()
279 * system calls. All permissions that are not granted by the acl are removed.
280 * The permissions in the acl are changed to reflect the mode_p parameter.
281 */
d3fb6120 282static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
1da177e4
LT
283{
284 struct posix_acl_entry *pa, *pe;
285 struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
d3fb6120 286 umode_t mode = *mode_p;
1da177e4
LT
287 int not_equiv = 0;
288
289 /* assert(atomic_read(acl->a_refcount) == 1); */
290
291 FOREACH_ACL_ENTRY(pa, acl, pe) {
292 switch(pa->e_tag) {
293 case ACL_USER_OBJ:
294 pa->e_perm &= (mode >> 6) | ~S_IRWXO;
295 mode &= (pa->e_perm << 6) | ~S_IRWXU;
296 break;
297
298 case ACL_USER:
299 case ACL_GROUP:
300 not_equiv = 1;
301 break;
302
303 case ACL_GROUP_OBJ:
304 group_obj = pa;
305 break;
306
307 case ACL_OTHER:
308 pa->e_perm &= mode | ~S_IRWXO;
309 mode &= pa->e_perm | ~S_IRWXO;
310 break;
311
312 case ACL_MASK:
313 mask_obj = pa;
314 not_equiv = 1;
315 break;
316
317 default:
318 return -EIO;
319 }
320 }
321
322 if (mask_obj) {
323 mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
324 mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
325 } else {
326 if (!group_obj)
327 return -EIO;
328 group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
329 mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
330 }
331
332 *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
333 return not_equiv;
334}
335
336/*
337 * Modify the ACL for the chmod syscall.
338 */
86bc704d 339static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
1da177e4
LT
340{
341 struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
342 struct posix_acl_entry *pa, *pe;
343
344 /* assert(atomic_read(acl->a_refcount) == 1); */
345
346 FOREACH_ACL_ENTRY(pa, acl, pe) {
347 switch(pa->e_tag) {
348 case ACL_USER_OBJ:
349 pa->e_perm = (mode & S_IRWXU) >> 6;
350 break;
351
352 case ACL_USER:
353 case ACL_GROUP:
354 break;
355
356 case ACL_GROUP_OBJ:
357 group_obj = pa;
358 break;
359
360 case ACL_MASK:
361 mask_obj = pa;
362 break;
363
364 case ACL_OTHER:
365 pa->e_perm = (mode & S_IRWXO);
366 break;
367
368 default:
369 return -EIO;
370 }
371 }
372
373 if (mask_obj) {
374 mask_obj->e_perm = (mode & S_IRWXG) >> 3;
375 } else {
376 if (!group_obj)
377 return -EIO;
378 group_obj->e_perm = (mode & S_IRWXG) >> 3;
379 }
380
381 return 0;
382}
bc26ab5f 383
826cae2f 384int
d3fb6120 385posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
826cae2f
AV
386{
387 struct posix_acl *clone = posix_acl_clone(*acl, gfp);
388 int err = -ENOMEM;
389 if (clone) {
390 err = posix_acl_create_masq(clone, mode_p);
391 if (err < 0) {
392 posix_acl_release(clone);
393 clone = NULL;
394 }
395 }
396 posix_acl_release(*acl);
397 *acl = clone;
398 return err;
399}
400EXPORT_SYMBOL(posix_acl_create);
401
bc26ab5f 402int
86bc704d 403posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
bc26ab5f
AV
404{
405 struct posix_acl *clone = posix_acl_clone(*acl, gfp);
406 int err = -ENOMEM;
407 if (clone) {
408 err = posix_acl_chmod_masq(clone, mode);
409 if (err) {
410 posix_acl_release(clone);
411 clone = NULL;
412 }
413 }
414 posix_acl_release(*acl);
415 *acl = clone;
416 return err;
417}
418EXPORT_SYMBOL(posix_acl_chmod);
5c8ebd57
CH
419
420/*
421 * Fix up the uids and gids in posix acl extended attributes in place.
422 */
423static void posix_acl_fix_xattr_userns(
424 struct user_namespace *to, struct user_namespace *from,
425 void *value, size_t size)
426{
427 posix_acl_xattr_header *header = (posix_acl_xattr_header *)value;
428 posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
429 int count;
430 kuid_t uid;
431 kgid_t gid;
432
433 if (!value)
434 return;
435 if (size < sizeof(posix_acl_xattr_header))
436 return;
437 if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
438 return;
439
440 count = posix_acl_xattr_count(size);
441 if (count < 0)
442 return;
443 if (count == 0)
444 return;
445
446 for (end = entry + count; entry != end; entry++) {
447 switch(le16_to_cpu(entry->e_tag)) {
448 case ACL_USER:
449 uid = make_kuid(from, le32_to_cpu(entry->e_id));
450 entry->e_id = cpu_to_le32(from_kuid(to, uid));
451 break;
452 case ACL_GROUP:
453 gid = make_kgid(from, le32_to_cpu(entry->e_id));
454 entry->e_id = cpu_to_le32(from_kgid(to, gid));
455 break;
456 default:
457 break;
458 }
459 }
460}
461
462void posix_acl_fix_xattr_from_user(void *value, size_t size)
463{
464 struct user_namespace *user_ns = current_user_ns();
465 if (user_ns == &init_user_ns)
466 return;
467 posix_acl_fix_xattr_userns(&init_user_ns, user_ns, value, size);
468}
469
470void posix_acl_fix_xattr_to_user(void *value, size_t size)
471{
472 struct user_namespace *user_ns = current_user_ns();
473 if (user_ns == &init_user_ns)
474 return;
475 posix_acl_fix_xattr_userns(user_ns, &init_user_ns, value, size);
476}
477
478/*
479 * Convert from extended attribute to in-memory representation.
480 */
481struct posix_acl *
482posix_acl_from_xattr(struct user_namespace *user_ns,
483 const void *value, size_t size)
484{
485 posix_acl_xattr_header *header = (posix_acl_xattr_header *)value;
486 posix_acl_xattr_entry *entry = (posix_acl_xattr_entry *)(header+1), *end;
487 int count;
488 struct posix_acl *acl;
489 struct posix_acl_entry *acl_e;
490
491 if (!value)
492 return NULL;
493 if (size < sizeof(posix_acl_xattr_header))
494 return ERR_PTR(-EINVAL);
495 if (header->a_version != cpu_to_le32(POSIX_ACL_XATTR_VERSION))
496 return ERR_PTR(-EOPNOTSUPP);
497
498 count = posix_acl_xattr_count(size);
499 if (count < 0)
500 return ERR_PTR(-EINVAL);
501 if (count == 0)
502 return NULL;
503
504 acl = posix_acl_alloc(count, GFP_NOFS);
505 if (!acl)
506 return ERR_PTR(-ENOMEM);
507 acl_e = acl->a_entries;
508
509 for (end = entry + count; entry != end; acl_e++, entry++) {
510 acl_e->e_tag = le16_to_cpu(entry->e_tag);
511 acl_e->e_perm = le16_to_cpu(entry->e_perm);
512
513 switch(acl_e->e_tag) {
514 case ACL_USER_OBJ:
515 case ACL_GROUP_OBJ:
516 case ACL_MASK:
517 case ACL_OTHER:
518 break;
519
520 case ACL_USER:
521 acl_e->e_uid =
522 make_kuid(user_ns,
523 le32_to_cpu(entry->e_id));
524 if (!uid_valid(acl_e->e_uid))
525 goto fail;
526 break;
527 case ACL_GROUP:
528 acl_e->e_gid =
529 make_kgid(user_ns,
530 le32_to_cpu(entry->e_id));
531 if (!gid_valid(acl_e->e_gid))
532 goto fail;
533 break;
534
535 default:
536 goto fail;
537 }
538 }
539 return acl;
540
541fail:
542 posix_acl_release(acl);
543 return ERR_PTR(-EINVAL);
544}
545EXPORT_SYMBOL (posix_acl_from_xattr);
546
547/*
548 * Convert from in-memory to extended attribute representation.
549 */
550int
551posix_acl_to_xattr(struct user_namespace *user_ns, const struct posix_acl *acl,
552 void *buffer, size_t size)
553{
554 posix_acl_xattr_header *ext_acl = (posix_acl_xattr_header *)buffer;
555 posix_acl_xattr_entry *ext_entry = ext_acl->a_entries;
556 int real_size, n;
557
558 real_size = posix_acl_xattr_size(acl->a_count);
559 if (!buffer)
560 return real_size;
561 if (real_size > size)
562 return -ERANGE;
563
564 ext_acl->a_version = cpu_to_le32(POSIX_ACL_XATTR_VERSION);
565
566 for (n=0; n < acl->a_count; n++, ext_entry++) {
567 const struct posix_acl_entry *acl_e = &acl->a_entries[n];
568 ext_entry->e_tag = cpu_to_le16(acl_e->e_tag);
569 ext_entry->e_perm = cpu_to_le16(acl_e->e_perm);
570 switch(acl_e->e_tag) {
571 case ACL_USER:
572 ext_entry->e_id =
573 cpu_to_le32(from_kuid(user_ns, acl_e->e_uid));
574 break;
575 case ACL_GROUP:
576 ext_entry->e_id =
577 cpu_to_le32(from_kgid(user_ns, acl_e->e_gid));
578 break;
579 default:
580 ext_entry->e_id = cpu_to_le32(ACL_UNDEFINED_ID);
581 break;
582 }
583 }
584 return real_size;
585}
586EXPORT_SYMBOL (posix_acl_to_xattr);
This page took 0.675105 seconds and 5 git commands to generate.