1
by Amit Kucheria, Amit Kucheria
[ Amit Kucheria ] |
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 <asm/atomic.h> |
|
18 |
#include <linux/fs.h> |
|
19 |
#include <linux/sched.h> |
|
20 |
#include <linux/posix_acl.h> |
|
21 |
#include <linux/module.h> |
|
22 |
||
23 |
#include <linux/errno.h> |
|
24 |
||
25 |
EXPORT_SYMBOL(posix_acl_alloc); |
|
26 |
EXPORT_SYMBOL(posix_acl_clone); |
|
27 |
EXPORT_SYMBOL(posix_acl_valid); |
|
28 |
EXPORT_SYMBOL(posix_acl_equiv_mode); |
|
29 |
EXPORT_SYMBOL(posix_acl_from_mode); |
|
30 |
EXPORT_SYMBOL(posix_acl_create_masq); |
|
31 |
EXPORT_SYMBOL(posix_acl_chmod_masq); |
|
32 |
EXPORT_SYMBOL(posix_acl_permission); |
|
33 |
||
34 |
/*
|
|
35 |
* Allocate a new ACL with the specified number of entries.
|
|
36 |
*/
|
|
37 |
struct posix_acl * |
|
38 |
posix_acl_alloc(int count, gfp_t flags) |
|
39 |
{
|
|
40 |
const size_t size = sizeof(struct posix_acl) + |
|
41 |
count * sizeof(struct posix_acl_entry); |
|
42 |
struct posix_acl *acl = kmalloc(size, flags); |
|
43 |
if (acl) { |
|
44 |
atomic_set(&acl->a_refcount, 1); |
|
45 |
acl->a_count = count; |
|
46 |
}
|
|
47 |
return acl; |
|
48 |
}
|
|
49 |
||
50 |
/*
|
|
51 |
* Clone an ACL.
|
|
52 |
*/
|
|
53 |
struct posix_acl * |
|
54 |
posix_acl_clone(const struct posix_acl *acl, gfp_t flags) |
|
55 |
{
|
|
56 |
struct posix_acl *clone = NULL; |
|
57 |
||
58 |
if (acl) { |
|
59 |
int size = sizeof(struct posix_acl) + acl->a_count * |
|
60 |
sizeof(struct posix_acl_entry); |
|
61 |
clone = kmemdup(acl, size, flags); |
|
62 |
if (clone) |
|
63 |
atomic_set(&clone->a_refcount, 1); |
|
64 |
}
|
|
65 |
return clone; |
|
66 |
}
|
|
67 |
||
68 |
/*
|
|
69 |
* Check if an acl is valid. Returns 0 if it is, or -E... otherwise.
|
|
70 |
*/
|
|
71 |
int
|
|
72 |
posix_acl_valid(const struct posix_acl *acl) |
|
73 |
{
|
|
74 |
const struct posix_acl_entry *pa, *pe; |
|
75 |
int state = ACL_USER_OBJ; |
|
76 |
unsigned int id = 0; /* keep gcc happy */ |
|
77 |
int needs_mask = 0; |
|
78 |
||
79 |
FOREACH_ACL_ENTRY(pa, acl, pe) { |
|
80 |
if (pa->e_perm & ~(ACL_READ|ACL_WRITE|ACL_EXECUTE)) |
|
81 |
return -EINVAL; |
|
82 |
switch (pa->e_tag) { |
|
83 |
case ACL_USER_OBJ: |
|
84 |
if (state == ACL_USER_OBJ) { |
|
85 |
id = 0; |
|
86 |
state = ACL_USER; |
|
87 |
break; |
|
88 |
}
|
|
89 |
return -EINVAL; |
|
90 |
||
91 |
case ACL_USER: |
|
92 |
if (state != ACL_USER) |
|
93 |
return -EINVAL; |
|
94 |
if (pa->e_id == ACL_UNDEFINED_ID || |
|
95 |
pa->e_id < id) |
|
96 |
return -EINVAL; |
|
97 |
id = pa->e_id + 1; |
|
98 |
needs_mask = 1; |
|
99 |
break; |
|
100 |
||
101 |
case ACL_GROUP_OBJ: |
|
102 |
if (state == ACL_USER) { |
|
103 |
id = 0; |
|
104 |
state = ACL_GROUP; |
|
105 |
break; |
|
106 |
}
|
|
107 |
return -EINVAL; |
|
108 |
||
109 |
case ACL_GROUP: |
|
110 |
if (state != ACL_GROUP) |
|
111 |
return -EINVAL; |
|
112 |
if (pa->e_id == ACL_UNDEFINED_ID || |
|
113 |
pa->e_id < id) |
|
114 |
return -EINVAL; |
|
115 |
id = pa->e_id + 1; |
|
116 |
needs_mask = 1; |
|
117 |
break; |
|
118 |
||
119 |
case ACL_MASK: |
|
120 |
if (state != ACL_GROUP) |
|
121 |
return -EINVAL; |
|
122 |
state = ACL_OTHER; |
|
123 |
break; |
|
124 |
||
125 |
case ACL_OTHER: |
|
126 |
if (state == ACL_OTHER || |
|
127 |
(state == ACL_GROUP && !needs_mask)) { |
|
128 |
state = 0; |
|
129 |
break; |
|
130 |
}
|
|
131 |
return -EINVAL; |
|
132 |
||
133 |
default: |
|
134 |
return -EINVAL; |
|
135 |
}
|
|
136 |
}
|
|
137 |
if (state == 0) |
|
138 |
return 0; |
|
139 |
return -EINVAL; |
|
140 |
}
|
|
141 |
||
142 |
/*
|
|
143 |
* Returns 0 if the acl can be exactly represented in the traditional
|
|
144 |
* file mode permission bits, or else 1. Returns -E... on error.
|
|
145 |
*/
|
|
146 |
int
|
|
147 |
posix_acl_equiv_mode(const struct posix_acl *acl, mode_t *mode_p) |
|
148 |
{
|
|
149 |
const struct posix_acl_entry *pa, *pe; |
|
150 |
mode_t mode = 0; |
|
151 |
int not_equiv = 0; |
|
152 |
||
153 |
FOREACH_ACL_ENTRY(pa, acl, pe) { |
|
154 |
switch (pa->e_tag) { |
|
155 |
case ACL_USER_OBJ: |
|
156 |
mode |= (pa->e_perm & S_IRWXO) << 6; |
|
157 |
break; |
|
158 |
case ACL_GROUP_OBJ: |
|
159 |
mode |= (pa->e_perm & S_IRWXO) << 3; |
|
160 |
break; |
|
161 |
case ACL_OTHER: |
|
162 |
mode |= pa->e_perm & S_IRWXO; |
|
163 |
break; |
|
164 |
case ACL_MASK: |
|
165 |
mode = (mode & ~S_IRWXG) | |
|
166 |
((pa->e_perm & S_IRWXO) << 3); |
|
167 |
not_equiv = 1; |
|
168 |
break; |
|
169 |
case ACL_USER: |
|
170 |
case ACL_GROUP: |
|
171 |
not_equiv = 1; |
|
172 |
break; |
|
173 |
default: |
|
174 |
return -EINVAL; |
|
175 |
}
|
|
176 |
}
|
|
177 |
if (mode_p) |
|
178 |
*mode_p = (*mode_p & ~S_IRWXUGO) | mode; |
|
179 |
return not_equiv; |
|
180 |
}
|
|
181 |
||
182 |
/*
|
|
183 |
* Create an ACL representing the file mode permission bits of an inode.
|
|
184 |
*/
|
|
185 |
struct posix_acl * |
|
186 |
posix_acl_from_mode(mode_t mode, gfp_t flags) |
|
187 |
{
|
|
188 |
struct posix_acl *acl = posix_acl_alloc(3, flags); |
|
189 |
if (!acl) |
|
190 |
return ERR_PTR(-ENOMEM); |
|
191 |
||
192 |
acl->a_entries[0].e_tag = ACL_USER_OBJ; |
|
193 |
acl->a_entries[0].e_id = ACL_UNDEFINED_ID; |
|
194 |
acl->a_entries[0].e_perm = (mode & S_IRWXU) >> 6; |
|
195 |
||
196 |
acl->a_entries[1].e_tag = ACL_GROUP_OBJ; |
|
197 |
acl->a_entries[1].e_id = ACL_UNDEFINED_ID; |
|
198 |
acl->a_entries[1].e_perm = (mode & S_IRWXG) >> 3; |
|
199 |
||
200 |
acl->a_entries[2].e_tag = ACL_OTHER; |
|
201 |
acl->a_entries[2].e_id = ACL_UNDEFINED_ID; |
|
202 |
acl->a_entries[2].e_perm = (mode & S_IRWXO); |
|
203 |
return acl; |
|
204 |
}
|
|
205 |
||
206 |
/*
|
|
207 |
* Return 0 if current is granted want access to the inode
|
|
208 |
* by the acl. Returns -E... otherwise.
|
|
209 |
*/
|
|
210 |
int
|
|
211 |
posix_acl_permission(struct inode *inode, const struct posix_acl *acl, int want) |
|
212 |
{
|
|
213 |
const struct posix_acl_entry *pa, *pe, *mask_obj; |
|
214 |
int found = 0; |
|
215 |
||
216 |
FOREACH_ACL_ENTRY(pa, acl, pe) { |
|
217 |
switch(pa->e_tag) { |
|
218 |
case ACL_USER_OBJ: |
|
219 |
/* (May have been checked already) */
|
|
220 |
if (inode->i_uid == current_fsuid()) |
|
221 |
goto check_perm; |
|
222 |
break; |
|
223 |
case ACL_USER: |
|
224 |
if (pa->e_id == current_fsuid()) |
|
225 |
goto mask; |
|
226 |
break; |
|
227 |
case ACL_GROUP_OBJ: |
|
228 |
if (in_group_p(inode->i_gid)) { |
|
229 |
found = 1; |
|
230 |
if ((pa->e_perm & want) == want) |
|
231 |
goto mask; |
|
232 |
}
|
|
233 |
break; |
|
234 |
case ACL_GROUP: |
|
235 |
if (in_group_p(pa->e_id)) { |
|
236 |
found = 1; |
|
237 |
if ((pa->e_perm & want) == want) |
|
238 |
goto mask; |
|
239 |
}
|
|
240 |
break; |
|
241 |
case ACL_MASK: |
|
242 |
break; |
|
243 |
case ACL_OTHER: |
|
244 |
if (found) |
|
245 |
return -EACCES; |
|
246 |
else
|
|
247 |
goto check_perm; |
|
248 |
default: |
|
249 |
return -EIO; |
|
250 |
}
|
|
251 |
}
|
|
252 |
return -EIO; |
|
253 |
||
254 |
mask: |
|
255 |
for (mask_obj = pa+1; mask_obj != pe; mask_obj++) { |
|
256 |
if (mask_obj->e_tag == ACL_MASK) { |
|
257 |
if ((pa->e_perm & mask_obj->e_perm & want) == want) |
|
258 |
return 0; |
|
259 |
return -EACCES; |
|
260 |
}
|
|
261 |
}
|
|
262 |
||
263 |
check_perm: |
|
264 |
if ((pa->e_perm & want) == want) |
|
265 |
return 0; |
|
266 |
return -EACCES; |
|
267 |
}
|
|
268 |
||
269 |
/*
|
|
270 |
* Modify acl when creating a new inode. The caller must ensure the acl is
|
|
271 |
* only referenced once.
|
|
272 |
*
|
|
273 |
* mode_p initially must contain the mode parameter to the open() / creat()
|
|
274 |
* system calls. All permissions that are not granted by the acl are removed.
|
|
275 |
* The permissions in the acl are changed to reflect the mode_p parameter.
|
|
276 |
*/
|
|
277 |
int
|
|
278 |
posix_acl_create_masq(struct posix_acl *acl, mode_t *mode_p) |
|
279 |
{
|
|
280 |
struct posix_acl_entry *pa, *pe; |
|
281 |
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; |
|
282 |
mode_t mode = *mode_p; |
|
283 |
int not_equiv = 0; |
|
284 |
||
285 |
/* assert(atomic_read(acl->a_refcount) == 1); */
|
|
286 |
||
287 |
FOREACH_ACL_ENTRY(pa, acl, pe) { |
|
288 |
switch(pa->e_tag) { |
|
289 |
case ACL_USER_OBJ: |
|
290 |
pa->e_perm &= (mode >> 6) | ~S_IRWXO; |
|
291 |
mode &= (pa->e_perm << 6) | ~S_IRWXU; |
|
292 |
break; |
|
293 |
||
294 |
case ACL_USER: |
|
295 |
case ACL_GROUP: |
|
296 |
not_equiv = 1; |
|
297 |
break; |
|
298 |
||
299 |
case ACL_GROUP_OBJ: |
|
300 |
group_obj = pa; |
|
301 |
break; |
|
302 |
||
303 |
case ACL_OTHER: |
|
304 |
pa->e_perm &= mode | ~S_IRWXO; |
|
305 |
mode &= pa->e_perm | ~S_IRWXO; |
|
306 |
break; |
|
307 |
||
308 |
case ACL_MASK: |
|
309 |
mask_obj = pa; |
|
310 |
not_equiv = 1; |
|
311 |
break; |
|
312 |
||
313 |
default: |
|
314 |
return -EIO; |
|
315 |
}
|
|
316 |
}
|
|
317 |
||
318 |
if (mask_obj) { |
|
319 |
mask_obj->e_perm &= (mode >> 3) | ~S_IRWXO; |
|
320 |
mode &= (mask_obj->e_perm << 3) | ~S_IRWXG; |
|
321 |
} else { |
|
322 |
if (!group_obj) |
|
323 |
return -EIO; |
|
324 |
group_obj->e_perm &= (mode >> 3) | ~S_IRWXO; |
|
325 |
mode &= (group_obj->e_perm << 3) | ~S_IRWXG; |
|
326 |
}
|
|
327 |
||
328 |
*mode_p = (*mode_p & ~S_IRWXUGO) | mode; |
|
329 |
return not_equiv; |
|
330 |
}
|
|
331 |
||
332 |
/*
|
|
333 |
* Modify the ACL for the chmod syscall.
|
|
334 |
*/
|
|
335 |
int
|
|
336 |
posix_acl_chmod_masq(struct posix_acl *acl, mode_t mode) |
|
337 |
{
|
|
338 |
struct posix_acl_entry *group_obj = NULL, *mask_obj = NULL; |
|
339 |
struct posix_acl_entry *pa, *pe; |
|
340 |
||
341 |
/* assert(atomic_read(acl->a_refcount) == 1); */
|
|
342 |
||
343 |
FOREACH_ACL_ENTRY(pa, acl, pe) { |
|
344 |
switch(pa->e_tag) { |
|
345 |
case ACL_USER_OBJ: |
|
346 |
pa->e_perm = (mode & S_IRWXU) >> 6; |
|
347 |
break; |
|
348 |
||
349 |
case ACL_USER: |
|
350 |
case ACL_GROUP: |
|
351 |
break; |
|
352 |
||
353 |
case ACL_GROUP_OBJ: |
|
354 |
group_obj = pa; |
|
355 |
break; |
|
356 |
||
357 |
case ACL_MASK: |
|
358 |
mask_obj = pa; |
|
359 |
break; |
|
360 |
||
361 |
case ACL_OTHER: |
|
362 |
pa->e_perm = (mode & S_IRWXO); |
|
363 |
break; |
|
364 |
||
365 |
default: |
|
366 |
return -EIO; |
|
367 |
}
|
|
368 |
}
|
|
369 |
||
370 |
if (mask_obj) { |
|
371 |
mask_obj->e_perm = (mode & S_IRWXG) >> 3; |
|
372 |
} else { |
|
373 |
if (!group_obj) |
|
374 |
return -EIO; |
|
375 |
group_obj->e_perm = (mode & S_IRWXG) >> 3; |
|
376 |
}
|
|
377 |
||
378 |
return 0; |
|
379 |
}
|