1 /*
2  * GPL HEADER START
3  *
4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License version 2 only,
8  * as published by the Free Software Foundation.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License version 2 for more details (a copy is included
14  * in the LICENSE file that accompanied this code).
15  *
16  * You should have received a copy of the GNU General Public License
17  * version 2 along with this program; If not, see
18  * http://www.sun.com/software/products/lustre/docs/GPLv2.pdf
19  *
20  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
21  * CA 95054 USA or visit www.sun.com if you need additional information or
22  * have any questions.
23  *
24  * GPL HEADER END
25  */
26 /*
27  * Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
28  * Use is subject to license terms.
29  *
30  * Copyright (c) 2012, Intel Corporation.
31  */
32 /*
33  * This file is part of Lustre, http://www.lustre.org/
34  * Lustre is a trademark of Sun Microsystems, Inc.
35  *
36  * lustre/obdclass/acl.c
37  *
38  * Lustre Access Control List.
39  *
40  * Author: Fan Yong <fanyong@clusterfs.com>
41  */
42 
43 #define DEBUG_SUBSYSTEM S_SEC
44 #include "../include/lu_object.h"
45 #include "../include/lustre_acl.h"
46 #include "../include/lustre_eacl.h"
47 #include "../include/obd_support.h"
48 
49 #ifdef CONFIG_FS_POSIX_ACL
50 
51 #define CFS_ACL_XATTR_VERSION POSIX_ACL_XATTR_VERSION
52 
53 enum {
54 	ES_UNK  = 0,    /* unknown stat */
55 	ES_UNC  = 1,    /* ACL entry is not changed */
56 	ES_MOD  = 2,    /* ACL entry is modified */
57 	ES_ADD  = 3,    /* ACL entry is added */
58 	ES_DEL  = 4     /* ACL entry is deleted */
59 };
60 
lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry * d,ext_acl_xattr_entry * s)61 static inline void lustre_ext_acl_le_to_cpu(ext_acl_xattr_entry *d,
62 					    ext_acl_xattr_entry *s)
63 {
64 	d->e_tag	= le16_to_cpu(s->e_tag);
65 	d->e_perm       = le16_to_cpu(s->e_perm);
66 	d->e_id	 = le32_to_cpu(s->e_id);
67 	d->e_stat       = le32_to_cpu(s->e_stat);
68 }
69 
lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry * d,ext_acl_xattr_entry * s)70 static inline void lustre_ext_acl_cpu_to_le(ext_acl_xattr_entry *d,
71 					    ext_acl_xattr_entry *s)
72 {
73 	d->e_tag	= cpu_to_le16(s->e_tag);
74 	d->e_perm       = cpu_to_le16(s->e_perm);
75 	d->e_id	 = cpu_to_le32(s->e_id);
76 	d->e_stat       = cpu_to_le32(s->e_stat);
77 }
78 
lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry * d,posix_acl_xattr_entry * s)79 static inline void lustre_posix_acl_le_to_cpu(posix_acl_xattr_entry *d,
80 					      posix_acl_xattr_entry *s)
81 {
82 	d->e_tag	= le16_to_cpu(s->e_tag);
83 	d->e_perm       = le16_to_cpu(s->e_perm);
84 	d->e_id	 = le32_to_cpu(s->e_id);
85 }
86 
lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry * d,posix_acl_xattr_entry * s)87 static inline void lustre_posix_acl_cpu_to_le(posix_acl_xattr_entry *d,
88 					      posix_acl_xattr_entry *s)
89 {
90 	d->e_tag	= cpu_to_le16(s->e_tag);
91 	d->e_perm       = cpu_to_le16(s->e_perm);
92 	d->e_id	 = cpu_to_le32(s->e_id);
93 }
94 
95 
96 /* if "new_count == 0", then "new = {a_version, NULL}", NOT NULL. */
lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header ** header,int old_count,int new_count)97 static int lustre_posix_acl_xattr_reduce_space(posix_acl_xattr_header **header,
98 					       int old_count, int new_count)
99 {
100 	int old_size = CFS_ACL_XATTR_SIZE(old_count, posix_acl_xattr);
101 	int new_size = CFS_ACL_XATTR_SIZE(new_count, posix_acl_xattr);
102 	posix_acl_xattr_header *new;
103 
104 	if (unlikely(old_count <= new_count))
105 		return old_size;
106 
107 	OBD_ALLOC(new, new_size);
108 	if (unlikely(new == NULL))
109 		return -ENOMEM;
110 
111 	memcpy(new, *header, new_size);
112 	OBD_FREE(*header, old_size);
113 	*header = new;
114 	return new_size;
115 }
116 
117 /* if "new_count == 0", then "new = {0, NULL}", NOT NULL. */
lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header ** header,int old_count)118 static int lustre_ext_acl_xattr_reduce_space(ext_acl_xattr_header **header,
119 					     int old_count)
120 {
121 	int ext_count = le32_to_cpu((*header)->a_count);
122 	int ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
123 	int old_size = CFS_ACL_XATTR_SIZE(old_count, ext_acl_xattr);
124 	ext_acl_xattr_header *new;
125 
126 	if (unlikely(old_count <= ext_count))
127 		return 0;
128 
129 	OBD_ALLOC(new, ext_size);
130 	if (unlikely(new == NULL))
131 		return -ENOMEM;
132 
133 	memcpy(new, *header, ext_size);
134 	OBD_FREE(*header, old_size);
135 	*header = new;
136 	return 0;
137 }
138 
139 /*
140  * Generate new extended ACL based on the posix ACL.
141  */
142 ext_acl_xattr_header *
lustre_posix_acl_xattr_2ext(posix_acl_xattr_header * header,int size)143 lustre_posix_acl_xattr_2ext(posix_acl_xattr_header *header, int size)
144 {
145 	int count, i, esize;
146 	ext_acl_xattr_header *new;
147 
148 	if (unlikely(size < 0))
149 		return ERR_PTR(-EINVAL);
150 	else if (!size)
151 		count = 0;
152 	else
153 		count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
154 	esize = CFS_ACL_XATTR_SIZE(count, ext_acl_xattr);
155 	OBD_ALLOC(new, esize);
156 	if (unlikely(new == NULL))
157 		return ERR_PTR(-ENOMEM);
158 
159 	new->a_count = cpu_to_le32(count);
160 	for (i = 0; i < count; i++) {
161 		new->a_entries[i].e_tag  = header->a_entries[i].e_tag;
162 		new->a_entries[i].e_perm = header->a_entries[i].e_perm;
163 		new->a_entries[i].e_id   = header->a_entries[i].e_id;
164 		new->a_entries[i].e_stat = cpu_to_le32(ES_UNK);
165 	}
166 
167 	return new;
168 }
169 EXPORT_SYMBOL(lustre_posix_acl_xattr_2ext);
170 
171 /*
172  * Filter out the "nobody" entries in the posix ACL.
173  */
lustre_posix_acl_xattr_filter(posix_acl_xattr_header * header,size_t size,posix_acl_xattr_header ** out)174 int lustre_posix_acl_xattr_filter(posix_acl_xattr_header *header, size_t size,
175 				  posix_acl_xattr_header **out)
176 {
177 	int count, i, j, rc = 0;
178 	__u32 id;
179 	posix_acl_xattr_header *new;
180 
181 	if (!size)
182 		return 0;
183 	if (size < sizeof(*new))
184 		return -EINVAL;
185 
186 	OBD_ALLOC(new, size);
187 	if (unlikely(new == NULL))
188 		return -ENOMEM;
189 
190 	new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
191 	count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
192 	for (i = 0, j = 0; i < count; i++) {
193 		id = le32_to_cpu(header->a_entries[i].e_id);
194 		switch (le16_to_cpu(header->a_entries[i].e_tag)) {
195 		case ACL_USER_OBJ:
196 		case ACL_GROUP_OBJ:
197 		case ACL_MASK:
198 		case ACL_OTHER:
199 			if (id != ACL_UNDEFINED_ID) {
200 				rc = -EIO;
201 				goto _out;
202 			}
203 
204 			memcpy(&new->a_entries[j++], &header->a_entries[i],
205 			       sizeof(posix_acl_xattr_entry));
206 			break;
207 		case ACL_USER:
208 			if (id != NOBODY_UID)
209 				memcpy(&new->a_entries[j++],
210 				       &header->a_entries[i],
211 				       sizeof(posix_acl_xattr_entry));
212 			break;
213 		case ACL_GROUP:
214 			if (id != NOBODY_GID)
215 				memcpy(&new->a_entries[j++],
216 				       &header->a_entries[i],
217 				       sizeof(posix_acl_xattr_entry));
218 			break;
219 		default:
220 			rc = -EIO;
221 			goto _out;
222 		}
223 	}
224 
225 	/* free unused space. */
226 	rc = lustre_posix_acl_xattr_reduce_space(&new, count, j);
227 	if (rc >= 0) {
228 		size = rc;
229 		*out = new;
230 		rc = 0;
231 	}
232 
233 _out:
234 	if (rc) {
235 		OBD_FREE(new, size);
236 		size = rc;
237 	}
238 	return size;
239 }
240 EXPORT_SYMBOL(lustre_posix_acl_xattr_filter);
241 
242 /*
243  * Release the posix ACL space.
244  */
lustre_posix_acl_xattr_free(posix_acl_xattr_header * header,int size)245 void lustre_posix_acl_xattr_free(posix_acl_xattr_header *header, int size)
246 {
247 	OBD_FREE(header, size);
248 }
249 EXPORT_SYMBOL(lustre_posix_acl_xattr_free);
250 
251 /*
252  * Release the extended ACL space.
253  */
lustre_ext_acl_xattr_free(ext_acl_xattr_header * header)254 void lustre_ext_acl_xattr_free(ext_acl_xattr_header *header)
255 {
256 	OBD_FREE(header, CFS_ACL_XATTR_SIZE(le32_to_cpu(header->a_count), \
257 					    ext_acl_xattr));
258 }
259 EXPORT_SYMBOL(lustre_ext_acl_xattr_free);
260 
261 static ext_acl_xattr_entry *
lustre_ext_acl_xattr_search(ext_acl_xattr_header * header,posix_acl_xattr_entry * entry,int * pos)262 lustre_ext_acl_xattr_search(ext_acl_xattr_header *header,
263 			    posix_acl_xattr_entry *entry, int *pos)
264 {
265 	int once, start, end, i, j, count = le32_to_cpu(header->a_count);
266 
267 	once = 0;
268 	start = *pos;
269 	end = count;
270 
271 again:
272 	for (i = start; i < end; i++) {
273 		if (header->a_entries[i].e_tag == entry->e_tag &&
274 		    header->a_entries[i].e_id == entry->e_id) {
275 			j = i;
276 			if (++i >= count)
277 				i = 0;
278 			*pos = i;
279 			return &header->a_entries[j];
280 		}
281 	}
282 
283 	if (!once) {
284 		once = 1;
285 		start = 0;
286 		end = *pos;
287 		goto again;
288 	}
289 
290 	return NULL;
291 }
292 
293 /*
294  * Merge the posix ACL and the extended ACL into new posix ACL.
295  */
lustre_acl_xattr_merge2posix(posix_acl_xattr_header * posix_header,int size,ext_acl_xattr_header * ext_header,posix_acl_xattr_header ** out)296 int lustre_acl_xattr_merge2posix(posix_acl_xattr_header *posix_header, int size,
297 				 ext_acl_xattr_header *ext_header,
298 				 posix_acl_xattr_header **out)
299 {
300 	int posix_count, posix_size, i, j;
301 	int ext_count = le32_to_cpu(ext_header->a_count), pos = 0, rc = 0;
302 	posix_acl_xattr_entry pe = {ACL_MASK, 0, ACL_UNDEFINED_ID};
303 	posix_acl_xattr_header *new;
304 	ext_acl_xattr_entry *ee, ae;
305 
306 	lustre_posix_acl_cpu_to_le(&pe, &pe);
307 	ee = lustre_ext_acl_xattr_search(ext_header, &pe, &pos);
308 	if (ee == NULL || le32_to_cpu(ee->e_stat) == ES_DEL) {
309 		/* there are only base ACL entries at most. */
310 		posix_count = 3;
311 		posix_size = CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
312 		OBD_ALLOC(new, posix_size);
313 		if (unlikely(new == NULL))
314 			return -ENOMEM;
315 
316 		new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
317 		for (i = 0, j = 0; i < ext_count; i++) {
318 			lustre_ext_acl_le_to_cpu(&ae,
319 						 &ext_header->a_entries[i]);
320 			switch (ae.e_tag) {
321 			case ACL_USER_OBJ:
322 			case ACL_GROUP_OBJ:
323 			case ACL_OTHER:
324 				if (ae.e_id != ACL_UNDEFINED_ID) {
325 					rc = -EIO;
326 					goto _out;
327 				}
328 
329 				if (ae.e_stat != ES_DEL) {
330 					new->a_entries[j].e_tag =
331 						ext_header->a_entries[i].e_tag;
332 					new->a_entries[j].e_perm =
333 						ext_header->a_entries[i].e_perm;
334 					new->a_entries[j++].e_id =
335 						ext_header->a_entries[i].e_id;
336 				}
337 				break;
338 			case ACL_MASK:
339 			case ACL_USER:
340 			case ACL_GROUP:
341 				if (ae.e_stat == ES_DEL)
342 					break;
343 			default:
344 				rc = -EIO;
345 				goto _out;
346 			}
347 		}
348 	} else {
349 		/* maybe there are valid ACL_USER or ACL_GROUP entries in the
350 		 * original server-side ACL, they are regarded as ES_UNC stat.*/
351 		int ori_posix_count;
352 
353 		if (unlikely(size < 0))
354 			return -EINVAL;
355 		else if (!size)
356 			ori_posix_count = 0;
357 		else
358 			ori_posix_count =
359 				CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
360 		posix_count = ori_posix_count + ext_count;
361 		posix_size =
362 			CFS_ACL_XATTR_SIZE(posix_count, posix_acl_xattr);
363 		OBD_ALLOC(new, posix_size);
364 		if (unlikely(new == NULL))
365 			return -ENOMEM;
366 
367 		new->a_version = cpu_to_le32(CFS_ACL_XATTR_VERSION);
368 		/* 1. process the unchanged ACL entries
369 		 *    in the original server-side ACL. */
370 		pos = 0;
371 		for (i = 0, j = 0; i < ori_posix_count; i++) {
372 			ee = lustre_ext_acl_xattr_search(ext_header,
373 					&posix_header->a_entries[i], &pos);
374 			if (ee == NULL)
375 				memcpy(&new->a_entries[j++],
376 				       &posix_header->a_entries[i],
377 				       sizeof(posix_acl_xattr_entry));
378 		}
379 
380 		/* 2. process the non-deleted entries
381 		 *    from client-side extended ACL. */
382 		for (i = 0; i < ext_count; i++) {
383 			if (le16_to_cpu(ext_header->a_entries[i].e_stat) !=
384 			    ES_DEL) {
385 				new->a_entries[j].e_tag =
386 						ext_header->a_entries[i].e_tag;
387 				new->a_entries[j].e_perm =
388 						ext_header->a_entries[i].e_perm;
389 				new->a_entries[j++].e_id =
390 						ext_header->a_entries[i].e_id;
391 			}
392 		}
393 	}
394 
395 	/* free unused space. */
396 	rc = lustre_posix_acl_xattr_reduce_space(&new, posix_count, j);
397 	if (rc >= 0) {
398 		posix_size = rc;
399 		*out = new;
400 		rc = 0;
401 	}
402 
403 _out:
404 	if (rc) {
405 		OBD_FREE(new, posix_size);
406 		posix_size = rc;
407 	}
408 	return posix_size;
409 }
410 EXPORT_SYMBOL(lustre_acl_xattr_merge2posix);
411 
412 /*
413  * Merge the posix ACL and the extended ACL into new extended ACL.
414  */
415 ext_acl_xattr_header *
lustre_acl_xattr_merge2ext(posix_acl_xattr_header * posix_header,int size,ext_acl_xattr_header * ext_header)416 lustre_acl_xattr_merge2ext(posix_acl_xattr_header *posix_header, int size,
417 			   ext_acl_xattr_header *ext_header)
418 {
419 	int ori_ext_count, posix_count, ext_count, ext_size;
420 	int i, j, pos = 0, rc = 0;
421 	posix_acl_xattr_entry pae;
422 	ext_acl_xattr_header *new;
423 	ext_acl_xattr_entry *ee, eae;
424 
425 	if (unlikely(size < 0))
426 		return ERR_PTR(-EINVAL);
427 	else if (!size)
428 		posix_count = 0;
429 	else
430 		posix_count = CFS_ACL_XATTR_COUNT(size, posix_acl_xattr);
431 	ori_ext_count = le32_to_cpu(ext_header->a_count);
432 	ext_count = posix_count + ori_ext_count;
433 	ext_size = CFS_ACL_XATTR_SIZE(ext_count, ext_acl_xattr);
434 
435 	OBD_ALLOC(new, ext_size);
436 	if (unlikely(new == NULL))
437 		return ERR_PTR(-ENOMEM);
438 
439 	for (i = 0, j = 0; i < posix_count; i++) {
440 		lustre_posix_acl_le_to_cpu(&pae, &posix_header->a_entries[i]);
441 		switch (pae.e_tag) {
442 		case ACL_USER_OBJ:
443 		case ACL_GROUP_OBJ:
444 		case ACL_MASK:
445 		case ACL_OTHER:
446 			if (pae.e_id != ACL_UNDEFINED_ID) {
447 				rc = -EIO;
448 				goto out;
449 		}
450 		case ACL_USER:
451 			/* ignore "nobody" entry. */
452 			if (pae.e_id == NOBODY_UID)
453 				break;
454 
455 			new->a_entries[j].e_tag =
456 					posix_header->a_entries[i].e_tag;
457 			new->a_entries[j].e_perm =
458 					posix_header->a_entries[i].e_perm;
459 			new->a_entries[j].e_id =
460 					posix_header->a_entries[i].e_id;
461 			ee = lustre_ext_acl_xattr_search(ext_header,
462 					&posix_header->a_entries[i], &pos);
463 			if (ee) {
464 				if (posix_header->a_entries[i].e_perm !=
465 								ee->e_perm)
466 					/* entry modified. */
467 					ee->e_stat =
468 					new->a_entries[j++].e_stat =
469 							cpu_to_le32(ES_MOD);
470 				else
471 					/* entry unchanged. */
472 					ee->e_stat =
473 					new->a_entries[j++].e_stat =
474 							cpu_to_le32(ES_UNC);
475 			} else {
476 				/* new entry. */
477 				new->a_entries[j++].e_stat =
478 							cpu_to_le32(ES_ADD);
479 			}
480 			break;
481 		case ACL_GROUP:
482 			/* ignore "nobody" entry. */
483 			if (pae.e_id == NOBODY_GID)
484 				break;
485 			new->a_entries[j].e_tag =
486 					posix_header->a_entries[i].e_tag;
487 			new->a_entries[j].e_perm =
488 					posix_header->a_entries[i].e_perm;
489 			new->a_entries[j].e_id =
490 					posix_header->a_entries[i].e_id;
491 			ee = lustre_ext_acl_xattr_search(ext_header,
492 					&posix_header->a_entries[i], &pos);
493 			if (ee) {
494 				if (posix_header->a_entries[i].e_perm !=
495 								ee->e_perm)
496 					/* entry modified. */
497 					ee->e_stat =
498 					new->a_entries[j++].e_stat =
499 							cpu_to_le32(ES_MOD);
500 				else
501 					/* entry unchanged. */
502 					ee->e_stat =
503 					new->a_entries[j++].e_stat =
504 							cpu_to_le32(ES_UNC);
505 			} else {
506 				/* new entry. */
507 				new->a_entries[j++].e_stat =
508 							cpu_to_le32(ES_ADD);
509 			}
510 			break;
511 		default:
512 			rc = -EIO;
513 			goto out;
514 		}
515 	}
516 
517 	/* process deleted entries. */
518 	for (i = 0; i < ori_ext_count; i++) {
519 		lustre_ext_acl_le_to_cpu(&eae, &ext_header->a_entries[i]);
520 		if (eae.e_stat == ES_UNK) {
521 			/* ignore "nobody" entry. */
522 			if ((eae.e_tag == ACL_USER && eae.e_id == NOBODY_UID) ||
523 			    (eae.e_tag == ACL_GROUP && eae.e_id == NOBODY_GID))
524 				continue;
525 
526 			new->a_entries[j].e_tag =
527 						ext_header->a_entries[i].e_tag;
528 			new->a_entries[j].e_perm =
529 						ext_header->a_entries[i].e_perm;
530 			new->a_entries[j].e_id = ext_header->a_entries[i].e_id;
531 			new->a_entries[j++].e_stat = cpu_to_le32(ES_DEL);
532 		}
533 	}
534 
535 	new->a_count = cpu_to_le32(j);
536 	/* free unused space. */
537 	rc = lustre_ext_acl_xattr_reduce_space(&new, ext_count);
538 
539 out:
540 	if (rc) {
541 		OBD_FREE(new, ext_size);
542 		new = ERR_PTR(rc);
543 	}
544 	return new;
545 }
546 EXPORT_SYMBOL(lustre_acl_xattr_merge2ext);
547 
548 #endif
549