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