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