xref: /openbmc/linux/fs/posix_acl.c (revision b6dcefde)
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 }
380