file_cmds-116.9.tar.gz
[apple/file_cmds.git] / chmod / chmod_acl.c
1 /*
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. All advertising materials mentioning features or use of this software
14 * must display the following acknowledgement:
15 * This product includes software developed by the University of
16 * California, Berkeley and its contributors.
17 * 4. Neither the name of the University nor the names of its contributors
18 * may be used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31 * SUCH DAMAGE.
32 */
33 #include <err.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <sys/types.h>
38 #include <unistd.h>
39 #include <errno.h>
40
41 #include <membership.h>
42 #include "chmod_acl.h"
43
44 extern void usage(void);
45
46 #ifdef __APPLE__
47 static struct {
48 acl_perm_t perm;
49 char *name;
50 int flags;
51 #define ACL_PERM_DIR (1<<0)
52 #define ACL_PERM_FILE (1<<1)
53 } acl_perms[] = {
54 {ACL_READ_DATA, "read", ACL_PERM_FILE},
55 {ACL_LIST_DIRECTORY, "list", ACL_PERM_DIR},
56 {ACL_WRITE_DATA, "write", ACL_PERM_FILE},
57 {ACL_ADD_FILE, "add_file", ACL_PERM_DIR},
58 {ACL_EXECUTE, "execute", ACL_PERM_FILE},
59 {ACL_SEARCH, "search", ACL_PERM_DIR},
60 {ACL_DELETE, "delete", ACL_PERM_FILE | ACL_PERM_DIR},
61 {ACL_APPEND_DATA, "append", ACL_PERM_FILE},
62 {ACL_ADD_SUBDIRECTORY, "add_subdirectory", ACL_PERM_DIR},
63 {ACL_DELETE_CHILD, "delete_child", ACL_PERM_DIR},
64 {ACL_READ_ATTRIBUTES, "readattr", ACL_PERM_FILE | ACL_PERM_DIR},
65 {ACL_WRITE_ATTRIBUTES, "writeattr", ACL_PERM_FILE | ACL_PERM_DIR},
66 {ACL_READ_EXTATTRIBUTES, "readextattr", ACL_PERM_FILE | ACL_PERM_DIR},
67 {ACL_WRITE_EXTATTRIBUTES, "writeextattr", ACL_PERM_FILE | ACL_PERM_DIR},
68 {ACL_READ_SECURITY, "readsecurity", ACL_PERM_FILE | ACL_PERM_DIR},
69 {ACL_WRITE_SECURITY, "writesecurity", ACL_PERM_FILE | ACL_PERM_DIR},
70 {ACL_CHANGE_OWNER, "chown", ACL_PERM_FILE | ACL_PERM_DIR},
71 {0, NULL, 0}
72 };
73
74 static struct {
75 acl_flag_t flag;
76 char *name;
77 int flags;
78 } acl_flags[] = {
79 {ACL_ENTRY_INHERITED, "inherited", ACL_PERM_FILE | ACL_PERM_DIR},
80 {ACL_ENTRY_FILE_INHERIT, "file_inherit", ACL_PERM_DIR},
81 {ACL_ENTRY_DIRECTORY_INHERIT, "directory_inherit", ACL_PERM_DIR},
82 {ACL_ENTRY_LIMIT_INHERIT, "limit_inherit", ACL_PERM_FILE | ACL_PERM_DIR},
83 {ACL_ENTRY_ONLY_INHERIT, "only_inherit", ACL_PERM_DIR},
84 {0, NULL, 0}
85 };
86
87 /* TBD - Many of these routines could potentially be considered for
88 * inclusion in a library. If that is done, either avoid use of "err"
89 * and implement a better fall-through strategy in case of errors,
90 * or use err_set_exit() and make various structures globals.
91 */
92
93 /* Perform a name to uuid mapping - calls through to memberd */
94
95 uuid_t *
96 name_to_uuid(char *tok) {
97 struct passwd *tpass = NULL;
98 struct group *tgrp = NULL;
99 uuid_t *entryg = NULL;
100 char is_uid = 1;
101
102 if ((entryg = (uuid_t *) calloc(1,sizeof(uuid_t))) == NULL)
103 err(1, "Unable to allocate a uuid");
104
105 tpass = getpwnam(tok);
106
107 if (tpass == NULL) {
108 tgrp = getgrnam(tok);
109 if (tgrp == NULL) {
110 errno = EINVAL;
111 err(1, "Unable to translate %s to a UID/GID", tok);
112 }
113 is_uid = 0;
114 }
115
116 if (tpass) {
117 if (0 != mbr_uid_to_uuid(tpass->pw_uid, entryg)) {
118 errno = EINVAL;
119 err(1, "mbr_uid_to_uuid(): Unable to translate");
120 }
121 }
122 else {
123 if (0 != mbr_gid_to_uuid(tgrp->gr_gid, entryg)) {
124 errno = EINVAL;
125 err(1, "mbr_gid_to_uuid(): Unable to translate");
126 }
127 }
128 return entryg;
129 }
130
131 /* Convert an acl entry in string form to an acl_entry_t */
132 int
133 parse_entry(char *entrybuf, acl_entry_t newent) {
134 char *tok;
135 char *pebuf;
136 uuid_t *entryg;
137
138 acl_tag_t tag;
139 acl_permset_t perms;
140 acl_flagset_t flags;
141 unsigned permcount = 0;
142 unsigned pindex = 0;
143
144 acl_get_permset(newent, &perms);
145 acl_get_flagset_np(newent, &flags);
146
147 pebuf = entrybuf;
148
149 tok = strsep(&pebuf, " ");
150
151 if ((tok == NULL) || *tok == '\0') {
152 errno = EINVAL;
153 err(1, "Invalid entry format");
154 }
155
156 /* parse the name into a qualifier */
157 entryg = name_to_uuid(tok);
158
159 tok = strsep(&pebuf, " ");
160 if ((tok == NULL) || *tok == '\0') {
161 errno = EINVAL;
162 err(1, "Invalid entry format");
163 }
164
165 /* is the verb 'allow' or 'deny'? */
166 if (!strcmp(tok, "allow")) {
167 tag = ACL_EXTENDED_ALLOW;
168 } else if (!strcmp(tok, "deny")) {
169 tag = ACL_EXTENDED_DENY;
170 } else {
171 errno = EINVAL;
172 err(1, "Unknown tag type '%s'", tok);
173 }
174
175 /* parse permissions */
176 for (; (tok = strsep(&pebuf, ",")) != NULL;) {
177 if (*tok != '\0') {
178 /* is it a permission? */
179 for (pindex = 0; acl_perms[pindex].name != NULL; pindex++) {
180 if (!strcmp(acl_perms[pindex].name, tok)) {
181 /* got one */
182 acl_add_perm(perms, acl_perms[pindex].perm);
183 permcount++;
184 goto found;
185 }
186 }
187 /* is it a flag? */
188 for (pindex = 0; acl_flags[pindex].name != NULL; pindex++) {
189 if (!strcmp(acl_flags[pindex].name, tok)) {
190 /* got one */
191 acl_add_flag_np(flags, acl_flags[pindex].flag);
192 permcount++;
193 goto found;
194 }
195 }
196 errno = EINVAL;
197 err(1,"Invalid permission type %s", tok);
198 found:
199 continue;
200 }
201 }
202 if (0 == permcount) {
203 errno = EINVAL;
204 err(1, "No permissions specified");
205 }
206 acl_set_tag_type(newent, tag);
207 acl_set_qualifier(newent, entryg);
208 acl_set_permset(newent, perms);
209 acl_set_flagset_np(newent, flags);
210 free(entryg);
211
212 return(0);
213 }
214
215 /* Convert one or more acl entries in string form to an acl_t */
216 acl_t
217 parse_acl_entries(const char *input) {
218 acl_t acl_input;
219 acl_entry_t newent;
220 char *inbuf;
221 char *oinbuf;
222
223 char **bufp, *entryv[ACL_MAX_ENTRIES];
224 #if 0
225 /* XXX acl_from_text(), when implemented, will presumably use the canonical
226 * text representation format, which is what chmod should be using
227 * We may need to add an entry number to the input
228 */
229 /* Translate the user supplied ACL entry */
230 /* acl_input = acl_from_text(input); */
231 #else
232 inbuf = malloc(MAX_ACL_TEXT_SIZE);
233
234 if (inbuf == NULL)
235 err(1, "malloc() failed");
236 strncpy(inbuf, input, MAX_ACL_TEXT_SIZE);
237 inbuf[MAX_ACL_TEXT_SIZE - 1] = '\0';
238
239 if ((acl_input = acl_init(1)) == NULL)
240 err(1, "acl_init() failed");
241
242 oinbuf = inbuf;
243
244 for (bufp = entryv; (*bufp = strsep(&oinbuf, "\n")) != NULL;)
245 if (**bufp != '\0') {
246 if (0 != acl_create_entry(&acl_input, &newent))
247 err(1, "acl_create_entry() failed");
248 if (0 != parse_entry(*bufp, newent)) {
249 errno = EINVAL;
250 err(1, "Failed parsing entry %s", *bufp);
251 }
252 if (++bufp >= &entryv[ACL_MAX_ENTRIES - 1]) {
253 errno = ERANGE;
254 err(1, "Too many entries");
255 }
256 }
257
258 free(inbuf);
259 return acl_input;
260 #endif /* #if 0 */
261 }
262
263 /* XXX No Libc support for inherited entries and generation determination yet */
264 unsigned
265 get_inheritance_level(acl_entry_t entry) {
266 /* XXX to be implemented */
267 return 1;
268 }
269
270 /* Determine a "score" for an acl entry. The entry scores higher if it's
271 * tagged ACL_EXTENDED_DENY, and non-inherited entries are ranked higher
272 * than inherited entries.
273 */
274
275 int
276 score_acl_entry(acl_entry_t entry) {
277
278 acl_tag_t tag;
279 acl_flagset_t flags;
280 acl_permset_t perms;
281
282 int score = 0;
283
284 if (entry == NULL)
285 return (MINIMUM_TIER);
286
287 if (acl_get_tag_type(entry, &tag) != 0) {
288 err(1, "Malformed ACL entry, no tag present");
289 }
290 if (acl_get_flagset_np(entry, &flags) != 0){
291 err(1, "Unable to obtain flagset");
292 }
293 if (acl_get_permset(entry, &perms) != 0)
294 err(1, "Malformed ACL entry, no permset present");
295
296 switch(tag) {
297 case ACL_EXTENDED_ALLOW:
298 break;
299 case ACL_EXTENDED_DENY:
300 score++;
301 break;
302 default:
303 errno = EINVAL;
304 err(1, "Unknown tag type present in ACL entry");
305 /* NOTREACHED */
306 }
307
308 if (acl_get_flag_np(flags, ACL_ENTRY_INHERITED))
309 score += get_inheritance_level(entry) * INHERITANCE_TIER;
310
311 return score;
312 }
313
314 int
315 compare_acl_qualifiers(uuid_t *qa, uuid_t *qb) {
316 return bcmp(qa, qb, sizeof(uuid_t));
317 }
318
319 /* Compare two ACL permsets.
320 * Returns :
321 * MATCH_SUBSET if bperms is a subset of aperms
322 * MATCH_SUPERSET if bperms is a superset of aperms
323 * MATCH_PARTIAL if the two permsets have a common subset
324 * MATCH_EXACT if the two permsets are identical
325 * MATCH_NONE if they are disjoint
326 */
327
328 int
329 compare_acl_permsets(acl_permset_t aperms, acl_permset_t bperms)
330 {
331 int i;
332 /* TBD Implement other match levels as needed */
333 for (i = 0; acl_perms[i].name != NULL; i++) {
334 if (acl_get_perm_np(aperms, acl_perms[i].perm) !=
335 acl_get_perm_np(bperms, acl_perms[i].perm))
336 return MATCH_NONE;
337 }
338 return MATCH_EXACT;
339 }
340
341 int
342 compare_acl_flagsets(acl_flagset_t aflags, acl_flagset_t bflags)
343 {
344 int i;
345 /* TBD Implement other match levels as needed */
346 for (i = 0; acl_flags[i].name != NULL; i++) {
347 if (acl_get_flag_np(aflags, acl_flags[i].flag) !=
348 acl_get_flag_np(bflags, acl_flags[i].flag))
349 return MATCH_NONE;
350 }
351 return MATCH_EXACT;
352 }
353
354 /* Compares two ACL entries for equality */
355 int
356 compare_acl_entries(acl_entry_t a, acl_entry_t b)
357 {
358 acl_tag_t atag, btag;
359 acl_permset_t aperms, bperms;
360 acl_flagset_t aflags, bflags;
361 int pcmp = 0, fcmp = 0;
362
363 if (0 != compare_acl_qualifiers(acl_get_qualifier(a),
364 acl_get_qualifier(b)))
365 return MATCH_NONE;
366
367 if (0 != acl_get_tag_type(a, &atag))
368 err(1, "No tag type present in entry");
369 if (0!= acl_get_tag_type(b, &btag))
370 err(1, "No tag type present in entry");
371
372 if (atag != btag)
373 return MATCH_NONE;
374
375 if ((acl_get_permset(a, &aperms) != 0) ||
376 (acl_get_flagset_np(a, &aflags) != 0) ||
377 (acl_get_permset(b, &bperms) != 0) ||
378 (acl_get_flagset_np(b, &bflags) != 0))
379 err(1, "error fetching permissions");
380
381 pcmp = compare_acl_permsets(aperms, bperms);
382 fcmp = compare_acl_flagsets(aflags, bflags);
383
384 if ((pcmp == MATCH_NONE) || (fcmp == MATCH_NONE))
385 return(MATCH_PARTIAL);
386 else
387 return(MATCH_EXACT);
388 }
389
390 /* Verify that an ACL is in canonical order. Currently, the canonical
391 * form is:
392 * local deny
393 * local allow
394 * inherited deny (parent)
395 * inherited allow (parent)
396 * inherited deny (grandparent)
397 * inherited allow (grandparent)
398 * ...
399 */
400 unsigned int
401 is_canonical(acl_t acl) {
402
403 unsigned aindex;
404 acl_entry_t entry;
405 int score = 0, next_score = 0;
406
407 /* XXX - is a zero entry ACL in canonical form? */
408 if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry))
409 return 1;
410
411 score = score_acl_entry(entry);
412
413 for (aindex = 0; acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) == 0;
414 aindex++) {
415 if (score < (next_score = score_acl_entry(entry)))
416 return 0;
417 score = next_score;
418 }
419 return 1;
420 }
421
422
423 /* Iterate through an ACL, and find the canonical position for the
424 * specified entry
425 */
426 unsigned int
427 find_canonical_position(acl_t acl, acl_entry_t modifier) {
428
429 acl_entry_t entry;
430 int mscore = 0;
431 unsigned mpos = 0;
432
433 /* Check if there's an entry with the same qualifier
434 * and tag type; if not, find the appropriate slot
435 * for the score.
436 */
437
438 if (0 != acl_get_entry(acl, ACL_FIRST_ENTRY, &entry))
439 return 0;
440
441 mscore = score_acl_entry(modifier);
442
443 while (mscore < score_acl_entry(entry)) {
444
445 mpos++;
446
447 if (0 != acl_get_entry(acl, ACL_NEXT_ENTRY, &entry))
448 break;
449
450 }
451 return mpos;
452 }
453
454 int canonicalize_acl_entries(acl_t acl);
455
456 /* For a given acl_entry_t "modifier", find the first exact or
457 * partially matching entry from the specified acl_t acl
458 */
459
460 int
461 find_matching_entry (acl_t acl, acl_entry_t modifier, acl_entry_t *rentryp,
462 unsigned match_inherited) {
463
464 acl_entry_t entry = NULL;
465
466 unsigned aindex;
467 int cmp, fcmp = MATCH_NONE;
468
469 for (aindex = 0;
470 acl_get_entry(acl, entry == NULL ? ACL_FIRST_ENTRY :
471 ACL_NEXT_ENTRY, &entry) == 0;
472 aindex++) {
473 cmp = compare_acl_entries(entry, modifier);
474 if ((cmp == MATCH_EXACT) || (cmp == MATCH_PARTIAL)) {
475 if (match_inherited) {
476 acl_flagset_t eflags, mflags;
477
478 if (0 != acl_get_flagset_np(modifier, &mflags))
479 err(1, "Unable to get flagset");
480
481 if (0 != acl_get_flagset_np(entry, &eflags))
482 err(1, "Unable to get flagset");
483
484 if (acl_get_flag_np(mflags, ACL_ENTRY_INHERITED) ==
485 acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) {
486 *rentryp = entry;
487 fcmp = cmp;
488 }
489 }
490 else {
491 *rentryp = entry;
492 fcmp = cmp;
493 }
494 }
495 if (fcmp == MATCH_EXACT)
496 break;
497 }
498 return fcmp;
499 }
500
501 /* Remove all perms specified in modifier from rentry*/
502 int
503 subtract_from_entry(acl_entry_t rentry, acl_entry_t modifier)
504 {
505 acl_permset_t rperms, mperms;
506 acl_flagset_t rflags, mflags;
507 int i;
508
509 if ((acl_get_permset(rentry, &rperms) != 0) ||
510 (acl_get_flagset_np(rentry, &rflags) != 0) ||
511 (acl_get_permset(modifier, &mperms) != 0) ||
512 (acl_get_flagset_np(modifier, &mflags) != 0))
513 err(1, "error computing ACL modification");
514
515 for (i = 0; acl_perms[i].name != NULL; i++) {
516 if (acl_get_perm_np(mperms, acl_perms[i].perm))
517 acl_delete_perm(rperms, acl_perms[i].perm);
518 }
519 for (i = 0; acl_flags[i].name != NULL; i++) {
520 if (acl_get_flag_np(mflags, acl_flags[i].flag))
521 acl_delete_flag_np(rflags, acl_flags[i].flag);
522 }
523 acl_set_permset(rentry, rperms);
524 acl_set_flagset_np(rentry, rflags);
525 return 0;
526 }
527 /* Add the perms specified in modifier to rentry */
528 int
529 merge_entry_perms(acl_entry_t rentry, acl_entry_t modifier)
530 {
531 acl_permset_t rperms, mperms;
532 acl_flagset_t rflags, mflags;
533 int i;
534
535 if ((acl_get_permset(rentry, &rperms) != 0) ||
536 (acl_get_flagset_np(rentry, &rflags) != 0) ||
537 (acl_get_permset(modifier, &mperms) != 0) ||
538 (acl_get_flagset_np(modifier, &mflags) != 0))
539 err(1, "error computing ACL modification");
540
541 for (i = 0; acl_perms[i].name != NULL; i++) {
542 if (acl_get_perm_np(mperms, acl_perms[i].perm))
543 acl_add_perm(rperms, acl_perms[i].perm);
544 }
545 for (i = 0; acl_flags[i].name != NULL; i++) {
546 if (acl_get_flag_np(mflags, acl_flags[i].flag))
547 acl_add_flag_np(rflags, acl_flags[i].flag);
548 }
549 acl_set_permset(rentry, rperms);
550 acl_set_flagset_np(rentry, rflags);
551 return 0;
552 }
553
554 int
555 modify_acl(acl_t *oaclp, acl_entry_t modifier, unsigned int optflags,
556 int position, int inheritance_level,
557 unsigned flag_new_acl) {
558
559 unsigned cpos = 0;
560 acl_entry_t newent = NULL;
561 int dmatch = 0;
562 acl_entry_t rentry = NULL;
563 unsigned retval = 0;
564 acl_t oacl = *oaclp;
565
566 /* Add the inherited flag if requested by the user*/
567 if (modifier && (optflags & ACL_INHERIT_FLAG)) {
568 acl_flagset_t mflags;
569
570 acl_get_flagset_np(modifier, &mflags);
571 acl_add_flag_np(mflags, ACL_ENTRY_INHERITED);
572 acl_set_flagset_np(modifier, mflags);
573 }
574
575 if (optflags & ACL_SET_FLAG) {
576 if (position != -1) {
577 if (0 != acl_create_entry_np(&oacl, &newent, position))
578 err(1, "acl_create_entry() failed");
579 acl_copy_entry(newent, modifier);
580 }
581 else {
582 /* If an entry exists, add the new permissions to it, else add an
583 * entry in the canonical position.
584 */
585
586 /* First, check for a matching entry - if one exists, merge flags */
587 dmatch = find_matching_entry(oacl, modifier, &rentry, 1);
588
589 if (dmatch != MATCH_NONE) {
590 if (dmatch == MATCH_EXACT)
591 /* Nothing to be done */
592 goto ma_exit;
593
594 if (dmatch == MATCH_PARTIAL) {
595 merge_entry_perms(rentry, modifier);
596 goto ma_exit;
597 }
598 }
599 /* Insert the entry in canonical order */
600 cpos = find_canonical_position(oacl, modifier);
601 if (0!= acl_create_entry_np(&oacl, &newent, cpos))
602 err(1, "acl_create_entry() failed");
603 acl_copy_entry(newent, modifier);
604 }
605 }
606 else
607 if (optflags & ACL_DELETE_FLAG) {
608
609 if (flag_new_acl) {
610 errno = EINVAL;
611 err(1, "No ACL present");
612 }
613 if (position != -1 ) {
614 if (0 != acl_get_entry(oacl, position, &rentry))
615 err(1, "Invalid entry number");
616 acl_delete_entry(oacl, rentry);
617 }
618 else {
619 unsigned match_found = 0, aindex;
620 for (aindex = 0;
621 acl_get_entry(oacl, rentry == NULL ?
622 ACL_FIRST_ENTRY :
623 ACL_NEXT_ENTRY, &rentry)
624 == 0; aindex++) {
625 unsigned cmp;
626 cmp = compare_acl_entries(rentry,
627 modifier);
628 if ((cmp == MATCH_EXACT) ||
629 (cmp == MATCH_PARTIAL)) {
630 match_found++;
631 if (cmp == MATCH_EXACT)
632 acl_delete_entry(oacl, rentry);
633 else
634 /* In the event of a partial match, remove the specified perms from the
635 * entry */
636 subtract_from_entry(rentry, modifier);
637 }
638 }
639 if (0 == match_found) {
640 errno = EINVAL;
641 warn("Entry not found when attempting delete");
642 retval = 1;
643 }
644 }
645 }
646 else
647 if (optflags & ACL_REWRITE_FLAG) {
648 acl_entry_t rentry;
649
650 if (-1 == position) {
651 usage();
652 }
653 if (0 == flag_new_acl) {
654 if (0 != acl_get_entry(oacl, position,
655 &rentry))
656 err(1, "Invalid entry number");
657
658 if (0 != acl_delete_entry(oacl, rentry))
659 err(1, "Unable to delete entry");
660 }
661 if (0!= acl_create_entry_np(&oacl, &newent, position))
662 err(1, "acl_create_entry() failed");
663 acl_copy_entry(newent, modifier);
664 }
665 ma_exit:
666 *oaclp = oacl;
667 return retval;
668 }
669
670 int
671 modify_file_acl(unsigned int optflags, const char *path, acl_t modifier, int position, int inheritance_level) {
672
673 acl_t oacl = NULL;
674 unsigned aindex = 0, flag_new_acl = 0;
675 acl_entry_t newent = NULL;
676 acl_entry_t entry = NULL;
677 unsigned retval = 0 ;
678
679 extern int fflag;
680
681 /* XXX acl_get_file() returns a zero entry ACL if an ACL was previously
682 * associated with the file, and has had its entries removed.
683 * However, POSIX 1003.1e states that a zero entry ACL should be
684 * returned if the caller asks for ACL_TYPE_DEFAULT, and no ACL is
685 * associated with the path; it
686 * does not specifically state that a request for ACL_TYPE_EXTENDED
687 * should not return a zero entry ACL, however.
688 */
689
690 /* Determine if we've been given a zero entry ACL, or create an ACL if
691 * none exists. There are some issues to consider here: Should we create
692 * a zero-entry ACL for a delete or check canonicity operation?
693 */
694
695 if (path == NULL)
696 usage();
697
698 if (optflags & ACL_FROM_STDIN)
699 oacl = acl_dup(modifier);
700 else {
701 oacl = acl_get_file(path, ACL_TYPE_EXTENDED);
702
703 if ((oacl == NULL) ||
704 (acl_get_entry(oacl,ACL_FIRST_ENTRY, &newent) != 0)) {
705 if ((oacl = acl_init(1)) == NULL)
706 err(1, "acl_init() failed");
707 flag_new_acl = 1;
708 position = 0;
709 }
710
711 if ((0 == flag_new_acl) && (optflags & (ACL_REMOVE_INHERIT_FLAG |
712 ACL_REMOVE_INHERITED_ENTRIES))) {
713 acl_t facl = NULL;
714 if ((facl = acl_init(1)) == NULL)
715 err(1, "acl_init() failed");
716 for (aindex = 0;
717 acl_get_entry(oacl,
718 (entry == NULL ? ACL_FIRST_ENTRY :
719 ACL_NEXT_ENTRY), &entry) == 0;
720 aindex++) {
721 acl_flagset_t eflags;
722 acl_entry_t fent = NULL;
723 if (acl_get_flagset_np(entry, &eflags) != 0) {
724 err(1, "Unable to obtain flagset");
725 }
726
727 if (acl_get_flag_np(eflags, ACL_ENTRY_INHERITED)) {
728 if (optflags & ACL_REMOVE_INHERIT_FLAG) {
729 acl_delete_flag_np(eflags, ACL_ENTRY_INHERITED);
730 acl_set_flagset_np(entry, eflags);
731 acl_create_entry(&facl, &fent);
732 acl_copy_entry(fent, entry);
733 }
734 }
735 else {
736 acl_create_entry(&facl, &fent);
737 acl_copy_entry(fent, entry);
738 }
739 }
740 if (oacl)
741 acl_free(oacl);
742 oacl = facl;
743 }
744 else
745 if (optflags & ACL_CHECK_CANONICITY) {
746 if (flag_new_acl) {
747 errno = EINVAL;
748 warn("No ACL currently associated with file %s", path);
749 }
750 return(is_canonical(oacl));
751 }
752 else
753 if ((optflags & ACL_SET_FLAG) && (position == -1) &&
754 (!is_canonical(oacl))) {
755 errno = EINVAL;
756 warn("The specified file %s does not have an ACL in canonical order, please specify a position with +a# ", path);
757 retval = 1;
758 }
759 else
760 if (((optflags & ACL_DELETE_FLAG) && (position != -1))
761 || (optflags & ACL_CHECK_CANONICITY)) {
762 retval = modify_acl(&oacl, NULL, optflags, position,
763 inheritance_level, flag_new_acl);
764 }
765 else
766 for (aindex = 0;
767 acl_get_entry(modifier,
768 (entry == NULL ? ACL_FIRST_ENTRY :
769 ACL_NEXT_ENTRY), &entry) == 0;
770 aindex++) {
771
772 retval += modify_acl(&oacl, entry, optflags,
773 position, inheritance_level,
774 flag_new_acl);
775 }
776 }
777
778 /* XXX Potential race here, since someone else could've modified or
779 * read the ACL on this file (with the intention of modifying it) in
780 * the interval from acl_get_file() to acl_set_file(); we can
781 * minimize one aspect of this window by comparing the original acl
782 * to a fresh one from acl_get_file() but we could consider a
783 * "changeset" mechanism, common locking strategy, or kernel
784 * supplied reservation mechanism to prevent this race.
785 */
786 if (!(optflags & ACL_CHECK_CANONICITY) &&
787 (0 != acl_set_file(path, ACL_TYPE_EXTENDED, oacl))){
788 if (!fflag)
789 warn("Failed to set ACL on file %s", path);
790 retval = 1;
791 }
792
793 if (oacl)
794 acl_free(oacl);
795
796 return retval;
797 }
798
799 #endif /*__APPLE__*/