blob: 359d70b0e947d332ffd716479ac36f406ba39749 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
Christoph Hellwig5c8ebd52013-12-20 05:16:37 -08002 * Copyright (C) 2002,2003 by Andreas Gruenbacher <a.gruenbacher@computer.org>
Linus Torvalds1da177e2005-04-16 15:20:36 -07003 *
Christoph Hellwig5c8ebd52013-12-20 05:16:37 -08004 * Fixes from William Schumacher incorporated on 15 March 2001.
5 * (Reported by Charles Bertsch, <CBertsch@microtest.com>).
Linus Torvalds1da177e2005-04-16 15:20:36 -07006 */
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>
Arun Sharma600634972011-07-26 16:09:06 -070015#include <linux/atomic.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070016#include <linux/fs.h>
17#include <linux/sched.h>
18#include <linux/posix_acl.h>
Christoph Hellwig5c8ebd52013-12-20 05:16:37 -080019#include <linux/posix_acl_xattr.h>
Paul Gortmaker630d9c42011-11-16 23:57:37 -050020#include <linux/export.h>
Christoph Hellwig5c8ebd52013-12-20 05:16:37 -080021#include <linux/user_namespace.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070022
Chuck Leverf61f6da2011-01-21 03:05:38 +000023EXPORT_SYMBOL(posix_acl_init);
Linus Torvalds1da177e2005-04-16 15:20:36 -070024EXPORT_SYMBOL(posix_acl_alloc);
Linus Torvalds1da177e2005-04-16 15:20:36 -070025EXPORT_SYMBOL(posix_acl_valid);
26EXPORT_SYMBOL(posix_acl_equiv_mode);
27EXPORT_SYMBOL(posix_acl_from_mode);
Linus Torvalds1da177e2005-04-16 15:20:36 -070028
29/*
Chuck Leverf61f6da2011-01-21 03:05:38 +000030 * 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
39/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070040 * Allocate a new ACL with the specified number of entries.
41 */
42struct posix_acl *
Al Virodd0fc662005-10-07 07:46:04 +010043posix_acl_alloc(int count, gfp_t flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -070044{
45 const size_t size = sizeof(struct posix_acl) +
46 count * sizeof(struct posix_acl_entry);
47 struct posix_acl *acl = kmalloc(size, flags);
Chuck Leverf61f6da2011-01-21 03:05:38 +000048 if (acl)
49 posix_acl_init(acl, count);
Linus Torvalds1da177e2005-04-16 15:20:36 -070050 return acl;
51}
52
53/*
54 * Clone an ACL.
55 */
Al Viroedde8542011-07-23 03:27:37 -040056static struct posix_acl *
Al Virodd0fc662005-10-07 07:46:04 +010057posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -070058{
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);
Alexey Dobriyan52978be2006-09-30 23:27:21 -070064 clone = kmemdup(acl, size, flags);
65 if (clone)
Linus Torvalds1da177e2005-04-16 15:20:36 -070066 atomic_set(&clone->a_refcount, 1);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067 }
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;
Eric W. Biederman2f6f0652012-02-07 18:52:57 -080079 kuid_t prev_uid = INVALID_UID;
80 kgid_t prev_gid = INVALID_GID;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081 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) {
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 state = ACL_USER;
90 break;
91 }
92 return -EINVAL;
93
94 case ACL_USER:
95 if (state != ACL_USER)
96 return -EINVAL;
Eric W. Biederman2f6f0652012-02-07 18:52:57 -080097 if (!uid_valid(pa->e_uid))
Linus Torvalds1da177e2005-04-16 15:20:36 -070098 return -EINVAL;
Eric W. Biederman2f6f0652012-02-07 18:52:57 -080099 if (uid_valid(prev_uid) &&
100 uid_lte(pa->e_uid, prev_uid))
101 return -EINVAL;
102 prev_uid = pa->e_uid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700103 needs_mask = 1;
104 break;
105
106 case ACL_GROUP_OBJ:
107 if (state == ACL_USER) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700108 state = ACL_GROUP;
109 break;
110 }
111 return -EINVAL;
112
113 case ACL_GROUP:
114 if (state != ACL_GROUP)
115 return -EINVAL;
Eric W. Biederman2f6f0652012-02-07 18:52:57 -0800116 if (!gid_valid(pa->e_gid))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 return -EINVAL;
Eric W. Biederman2f6f0652012-02-07 18:52:57 -0800118 if (gid_valid(prev_gid) &&
119 gid_lte(pa->e_gid, prev_gid))
120 return -EINVAL;
121 prev_gid = pa->e_gid;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700122 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
Al Virod6952122011-07-23 18:56:36 -0400153posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154{
155 const struct posix_acl_entry *pa, *pe;
Al Virod6952122011-07-23 18:56:36 -0400156 umode_t mode = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 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 *
Al Viro3a5fba12011-07-23 19:01:48 -0400192posix_acl_from_mode(umode_t mode, gfp_t flags)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700193{
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;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
200
201 acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
203
204 acl->a_entries[2].e_tag = ACL_OTHER;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205 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
Andreas Gruenbacherd124b602011-10-23 23:13:32 +0530219 want &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;
220
Linus Torvalds1da177e2005-04-16 15:20:36 -0700221 FOREACH_ACL_ENTRY(pa, acl, pe) {
222 switch(pa->e_tag) {
223 case ACL_USER_OBJ:
224 /* (May have been checked already) */
Eric W. Biederman2f6f0652012-02-07 18:52:57 -0800225 if (uid_eq(inode->i_uid, current_fsuid()))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226 goto check_perm;
227 break;
228 case ACL_USER:
Eric W. Biederman2f6f0652012-02-07 18:52:57 -0800229 if (uid_eq(pa->e_uid, current_fsuid()))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230 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:
Eric W. Biederman2f6f0652012-02-07 18:52:57 -0800240 if (in_group_p(pa->e_gid)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700241 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 */
Al Virod3fb6122011-07-23 18:37:50 -0400282static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700283{
284 struct posix_acl_entry *pa, *pe;
285 struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
Al Virod3fb6122011-07-23 18:37:50 -0400286 umode_t mode = *mode_p;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700287 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 */
Al Viro86bc7042011-07-23 19:03:11 -0400339static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700340{
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}
Al Virobc26ab52011-07-23 00:18:02 -0400383
384int
Al Virod3fb6122011-07-23 18:37:50 -0400385posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
Al Viro826cae22011-07-23 03:10:32 -0400386{
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
402int
Al Viro86bc7042011-07-23 19:03:11 -0400403posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
Al Virobc26ab52011-07-23 00:18:02 -0400404{
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);
Christoph Hellwig5c8ebd52013-12-20 05:16:37 -0800419
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);