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