Branch data Line data Source code
1 : : /*
2 : : * linux/fs/posix_acl.c
3 : : *
4 : : * Copyright (C) 2002 by Andreas Gruenbacher <a.gruenbacher@computer.org>
5 : : *
6 : : * Fixes from William Schumacher incorporated on 15 March 2001.
7 : : * (Reported by Charles Bertsch, <CBertsch@microtest.com>).
8 : : */
9 : :
10 : : /*
11 : : * This file contains generic functions for manipulating
12 : : * POSIX 1003.1e draft standard 17 ACLs.
13 : : */
14 : :
15 : : #include <linux/kernel.h>
16 : : #include <linux/slab.h>
17 : : #include <linux/atomic.h>
18 : : #include <linux/fs.h>
19 : : #include <linux/sched.h>
20 : : #include <linux/posix_acl.h>
21 : : #include <linux/export.h>
22 : :
23 : : #include <linux/errno.h>
24 : :
25 : : EXPORT_SYMBOL(posix_acl_init);
26 : : EXPORT_SYMBOL(posix_acl_alloc);
27 : : EXPORT_SYMBOL(posix_acl_valid);
28 : : EXPORT_SYMBOL(posix_acl_equiv_mode);
29 : : EXPORT_SYMBOL(posix_acl_from_mode);
30 : :
31 : : /*
32 : : * Init a fresh posix_acl
33 : : */
34 : : void
35 : 0 : posix_acl_init(struct posix_acl *acl, int count)
36 : : {
37 : 0 : atomic_set(&acl->a_refcount, 1);
38 : 0 : acl->a_count = count;
39 : 0 : }
40 : :
41 : : /*
42 : : * Allocate a new ACL with the specified number of entries.
43 : : */
44 : : struct posix_acl *
45 : 0 : posix_acl_alloc(int count, gfp_t flags)
46 : : {
47 : 0 : const size_t size = sizeof(struct posix_acl) +
48 : 0 : count * sizeof(struct posix_acl_entry);
49 : : struct posix_acl *acl = kmalloc(size, flags);
50 [ # # ]: 0 : if (acl)
51 : : posix_acl_init(acl, count);
52 : 0 : return acl;
53 : : }
54 : :
55 : : /*
56 : : * Clone an ACL.
57 : : */
58 : : static struct posix_acl *
59 : 0 : posix_acl_clone(const struct posix_acl *acl, gfp_t flags)
60 : : {
61 : : struct posix_acl *clone = NULL;
62 : :
63 [ # # ]: 0 : if (acl) {
64 : 0 : int size = sizeof(struct posix_acl) + acl->a_count *
65 : : sizeof(struct posix_acl_entry);
66 : 0 : clone = kmemdup(acl, size, flags);
67 [ # # ]: 0 : if (clone)
68 : 0 : atomic_set(&clone->a_refcount, 1);
69 : : }
70 : 0 : return clone;
71 : : }
72 : :
73 : : /*
74 : : * Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
75 : : */
76 : : int
77 : 0 : posix_acl_valid(const struct posix_acl *acl)
78 : : {
79 : : const struct posix_acl_entry *pa, *pe;
80 : : int state = ACL_USER_OBJ;
81 : : kuid_t prev_uid = INVALID_UID;
82 : : kgid_t prev_gid = INVALID_GID;
83 : : int needs_mask = 0;
84 : :
85 [ # # ]: 0 : FOREACH_ACL_ENTRY(pa, acl, pe) {
86 [ # # ]: 0 : if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE))
87 : : return -EINVAL;
88 [ # # # # : 0 : switch (pa->e_tag) {
# # # ]
89 : : case ACL_USER_OBJ:
90 [ # # ]: 0 : if (state == ACL_USER_OBJ) {
91 : : state = ACL_USER;
92 : : break;
93 : : }
94 : : return -EINVAL;
95 : :
96 : : case ACL_USER:
97 [ # # ]: 0 : if (state != ACL_USER)
98 : : return -EINVAL;
99 [ # # ]: 0 : if (!uid_valid(pa->e_uid))
100 : : return -EINVAL;
101 [ # # ][ # # ]: 0 : if (uid_valid(prev_uid) &&
102 : : uid_lte(pa->e_uid, prev_uid))
103 : : return -EINVAL;
104 : : prev_uid = pa->e_uid;
105 : : needs_mask = 1;
106 : : break;
107 : :
108 : : case ACL_GROUP_OBJ:
109 [ # # ]: 0 : if (state == ACL_USER) {
110 : : state = ACL_GROUP;
111 : : break;
112 : : }
113 : : return -EINVAL;
114 : :
115 : : case ACL_GROUP:
116 [ # # ]: 0 : if (state != ACL_GROUP)
117 : : return -EINVAL;
118 [ # # ]: 0 : if (!gid_valid(pa->e_gid))
119 : : return -EINVAL;
120 [ # # ][ # # ]: 0 : if (gid_valid(prev_gid) &&
121 : : gid_lte(pa->e_gid, prev_gid))
122 : : return -EINVAL;
123 : : prev_gid = pa->e_gid;
124 : : needs_mask = 1;
125 : : break;
126 : :
127 : : case ACL_MASK:
128 [ # # ]: 0 : if (state != ACL_GROUP)
129 : : return -EINVAL;
130 : : state = ACL_OTHER;
131 : : break;
132 : :
133 : : case ACL_OTHER:
134 [ # # ][ # # ]: 0 : if (state == ACL_OTHER ||
135 : 0 : (state == ACL_GROUP && !needs_mask)) {
136 : : state = 0;
137 : : break;
138 : : }
139 : : return -EINVAL;
140 : :
141 : : default:
142 : : return -EINVAL;
143 : : }
144 : : }
145 [ # # ]: 0 : if (state == 0)
146 : : return 0;
147 : 0 : return -EINVAL;
148 : : }
149 : :
150 : : /*
151 : : * Returns 0 if the acl can be exactly represented in the traditional
152 : : * file mode permission bits, or else 1. Returns -E... on error.
153 : : */
154 : : int
155 : 0 : posix_acl_equiv_mode(const struct posix_acl *acl, umode_t *mode_p)
156 : : {
157 : : const struct posix_acl_entry *pa, *pe;
158 : : umode_t mode = 0;
159 : : int not_equiv = 0;
160 : :
161 [ # # ]: 0 : FOREACH_ACL_ENTRY(pa, acl, pe) {
162 [ # # # # : 0 : switch (pa->e_tag) {
# # ]
163 : : case ACL_USER_OBJ:
164 : 0 : mode |= (pa->e_perm & S_IRWXO) << 6;
165 : 0 : break;
166 : : case ACL_GROUP_OBJ:
167 : 0 : mode |= (pa->e_perm & S_IRWXO) << 3;
168 : 0 : break;
169 : : case ACL_OTHER:
170 : 0 : mode |= pa->e_perm & S_IRWXO;
171 : 0 : break;
172 : : case ACL_MASK:
173 : 0 : mode = (mode & ~S_IRWXG) |
174 : 0 : ((pa->e_perm & S_IRWXO) << 3);
175 : : not_equiv = 1;
176 : 0 : break;
177 : : case ACL_USER:
178 : : case ACL_GROUP:
179 : : not_equiv = 1;
180 : : break;
181 : : default:
182 : : return -EINVAL;
183 : : }
184 : : }
185 [ # # ]: 0 : if (mode_p)
186 : 0 : *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
187 : 0 : return not_equiv;
188 : : }
189 : :
190 : : /*
191 : : * Create an ACL representing the file mode permission bits of an inode.
192 : : */
193 : : struct posix_acl *
194 : 0 : posix_acl_from_mode(umode_t mode, gfp_t flags)
195 : : {
196 : 0 : struct posix_acl *acl = posix_acl_alloc(3, flags);
197 [ # # ]: 0 : if (!acl)
198 : : return ERR_PTR(-ENOMEM);
199 : :
200 : 0 : acl->a_entries[0].e_tag = ACL_USER_OBJ;
201 : 0 : acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6;
202 : :
203 : 0 : acl->a_entries[1].e_tag = ACL_GROUP_OBJ;
204 : 0 : acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3;
205 : :
206 : 0 : acl->a_entries[2].e_tag = ACL_OTHER;
207 : 0 : acl->a_entries[2].e_perm = (mode & S_IRWXO);
208 : 0 : return acl;
209 : : }
210 : :
211 : : /*
212 : : * Return 0 if current is granted want access to the inode
213 : : * by the acl. Returns -E... otherwise.
214 : : */
215 : : int
216 : 0 : posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want)
217 : : {
218 : : const struct posix_acl_entry *pa, *pe, *mask_obj;
219 : : int found = 0;
220 : :
221 : 0 : want &= MAY_READ | MAY_WRITE | MAY_EXEC | MAY_NOT_BLOCK;
222 : :
223 [ # # ]: 0 : FOREACH_ACL_ENTRY(pa, acl, pe) {
224 [ # # # # : 0 : switch(pa->e_tag) {
# # # ]
225 : : case ACL_USER_OBJ:
226 : : /* (May have been checked already) */
227 [ # # ]: 0 : if (uid_eq(inode->i_uid, current_fsuid()))
228 : : goto check_perm;
229 : : break;
230 : : case ACL_USER:
231 [ # # ]: 0 : if (uid_eq(pa->e_uid, current_fsuid()))
232 : : goto mask;
233 : : break;
234 : : case ACL_GROUP_OBJ:
235 [ # # ]: 0 : if (in_group_p(inode->i_gid)) {
236 : : found = 1;
237 [ # # ]: 0 : if ((pa->e_perm & want) == want)
238 : : goto mask;
239 : : }
240 : : break;
241 : : case ACL_GROUP:
242 [ # # ]: 0 : if (in_group_p(pa->e_gid)) {
243 : : found = 1;
244 [ # # ]: 0 : if ((pa->e_perm & want) == want)
245 : : goto mask;
246 : : }
247 : : break;
248 : : case ACL_MASK:
249 : : break;
250 : : case ACL_OTHER:
251 [ # # ]: 0 : if (found)
252 : : return -EACCES;
253 : : else
254 : : goto check_perm;
255 : : default:
256 : : return -EIO;
257 : : }
258 : : }
259 : : return -EIO;
260 : :
261 : : mask:
262 [ # # ]: 0 : for (mask_obj = pa+1; mask_obj != pe; mask_obj++) {
263 [ # # ]: 0 : if (mask_obj->e_tag == ACL_MASK) {
264 [ # # ]: 0 : if ((pa->e_perm & mask_obj->e_perm & want) == want)
265 : : return 0;
266 : 0 : return -EACCES;
267 : : }
268 : : }
269 : :
270 : : check_perm:
271 [ # # ]: 0 : if ((pa->e_perm & want) == want)
272 : : return 0;
273 : 0 : return -EACCES;
274 : : }
275 : :
276 : : /*
277 : : * Modify acl when creating a new inode. The caller must ensure the acl is
278 : : * only referenced once.
279 : : *
280 : : * mode_p initially must contain the mode parameter to the open() / creat()
281 : : * system calls. All permissions that are not granted by the acl are removed.
282 : : * The permissions in the acl are changed to reflect the mode_p parameter.
283 : : */
284 : 0 : static int posix_acl_create_masq(struct posix_acl *acl, umode_t *mode_p)
285 : : {
286 : : struct posix_acl_entry *pa, *pe;
287 : : struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
288 : 0 : umode_t mode = *mode_p;
289 : : int not_equiv = 0;
290 : :
291 : : /* assert(atomic_read(acl->a_refcount) == 1); */
292 : :
293 [ # # ]: 0 : FOREACH_ACL_ENTRY(pa, acl, pe) {
294 [ # # # # : 0 : switch(pa->e_tag) {
# # ]
295 : : case ACL_USER_OBJ:
296 : 0 : pa->e_perm &= (mode >> 6) | ~S_IRWXO;
297 : 0 : mode &= (pa->e_perm << 6) | ~S_IRWXU;
298 : 0 : break;
299 : :
300 : : case ACL_USER:
301 : : case ACL_GROUP:
302 : : not_equiv = 1;
303 : : break;
304 : :
305 : : case ACL_GROUP_OBJ:
306 : : group_obj = pa;
307 : 0 : break;
308 : :
309 : : case ACL_OTHER:
310 : 0 : pa->e_perm &= mode | ~S_IRWXO;
311 : 0 : mode &= pa->e_perm | ~S_IRWXO;
312 : 0 : break;
313 : :
314 : : case ACL_MASK:
315 : : mask_obj = pa;
316 : : not_equiv = 1;
317 : 0 : break;
318 : :
319 : : default:
320 : : return -EIO;
321 : : }
322 : : }
323 : :
324 [ # # ]: 0 : if (mask_obj) {
325 : 0 : mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
326 : 0 : mode &= (mask_obj->e_perm << 3) | ~S_IRWXG;
327 : : } else {
328 [ # # ]: 0 : if (!group_obj)
329 : : return -EIO;
330 : 0 : group_obj->e_perm &= (mode >> 3) | ~S_IRWXO;
331 : 0 : mode &= (group_obj->e_perm << 3) | ~S_IRWXG;
332 : : }
333 : :
334 : 0 : *mode_p = (*mode_p & ~S_IRWXUGO) | mode;
335 : 0 : return not_equiv;
336 : : }
337 : :
338 : : /*
339 : : * Modify the ACL for the chmod syscall.
340 : : */
341 : 0 : static int posix_acl_chmod_masq(struct posix_acl *acl, umode_t mode)
342 : : {
343 : : struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL;
344 : : struct posix_acl_entry *pa, *pe;
345 : :
346 : : /* assert(atomic_read(acl->a_refcount) == 1); */
347 : :
348 [ # # ]: 0 : FOREACH_ACL_ENTRY(pa, acl, pe) {
349 [ # # # # : 0 : switch(pa->e_tag) {
# # ]
350 : : case ACL_USER_OBJ:
351 : 0 : pa->e_perm = (mode & S_IRWXU) >> 6;
352 : 0 : break;
353 : :
354 : : case ACL_USER:
355 : : case ACL_GROUP:
356 : : break;
357 : :
358 : : case ACL_GROUP_OBJ:
359 : : group_obj = pa;
360 : 0 : break;
361 : :
362 : : case ACL_MASK:
363 : : mask_obj = pa;
364 : 0 : break;
365 : :
366 : : case ACL_OTHER:
367 : 0 : pa->e_perm = (mode & S_IRWXO);
368 : 0 : break;
369 : :
370 : : default:
371 : : return -EIO;
372 : : }
373 : : }
374 : :
375 [ # # ]: 0 : if (mask_obj) {
376 : 0 : mask_obj->e_perm = (mode & S_IRWXG) >> 3;
377 : : } else {
378 [ # # ]: 0 : if (!group_obj)
379 : : return -EIO;
380 : 0 : group_obj->e_perm = (mode & S_IRWXG) >> 3;
381 : : }
382 : :
383 : : return 0;
384 : : }
385 : :
386 : : int
387 : 0 : posix_acl_create(struct posix_acl **acl, gfp_t gfp, umode_t *mode_p)
388 : : {
389 : 0 : struct posix_acl *clone = posix_acl_clone(*acl, gfp);
390 : : int err = -ENOMEM;
391 [ # # ]: 0 : if (clone) {
392 : 0 : err = posix_acl_create_masq(clone, mode_p);
393 [ # # ]: 0 : if (err < 0) {
394 : : posix_acl_release(clone);
395 : : clone = NULL;
396 : : }
397 : : }
398 : 0 : posix_acl_release(*acl);
399 : 0 : *acl = clone;
400 : 0 : return err;
401 : : }
402 : : EXPORT_SYMBOL(posix_acl_create);
403 : :
404 : : int
405 : 0 : posix_acl_chmod(struct posix_acl **acl, gfp_t gfp, umode_t mode)
406 : : {
407 : 0 : struct posix_acl *clone = posix_acl_clone(*acl, gfp);
408 : : int err = -ENOMEM;
409 [ # # ]: 0 : if (clone) {
410 : 0 : err = posix_acl_chmod_masq(clone, mode);
411 [ # # ]: 0 : if (err) {
412 : : posix_acl_release(clone);
413 : : clone = NULL;
414 : : }
415 : : }
416 : 0 : posix_acl_release(*acl);
417 : 0 : *acl = clone;
418 : 0 : return err;
419 : : }
420 : : EXPORT_SYMBOL(posix_acl_chmod);
|