2 #include <libgen.h> /* dirname() */
3 #include <limits.h> /* PATH_MAX */
12 #include <acl/libacl.h> /* acl_get_perm, not portable */
13 #include <sys/types.h>
17 mode_t
get_mode(const char* path
) {
24 int result
= stat(path
, &s
);
30 /* errno will be set already by stat() */
35 bool mode_has_perm(mode_t mode
, int perm
) {
45 bool is_regular_file(const char* path
) {
51 int result
= stat(path
, &s
);
53 return S_ISREG(s
.st_mode
);
60 bool is_directory(const char* path
) {
66 int result
= stat(path
, &s
);
68 return S_ISDIR(s
.st_mode
);
76 int has_type_tag_acl(const char* path
,
78 acl_tag_t desired_tag
) {
79 /* Returns one if the given path has a default ACL for the supplied
80 tag, zero if it doesn't, and -1 on error. */
81 acl_t defacl
= acl_get_file(path
, type
);
83 if (defacl
== (acl_t
)NULL
) {
88 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
91 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
92 int tag_result
= acl_get_tag_type(entry
, &tag
);
94 if (tag_result
== -1) {
95 perror("has_default_tag_acl (acl_get_tag_type)");
99 if (tag
== desired_tag
) {
104 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
108 perror("has_default_tag_acl (acl_get_entry)");
115 int has_default_tag_acl(const char* path
, acl_tag_t desired_tag
) {
116 return has_type_tag_acl(path
, ACL_TYPE_DEFAULT
, desired_tag
);
119 int has_access_tag_acl(const char* path
, acl_tag_t desired_tag
) {
120 return has_type_tag_acl(path
, ACL_TYPE_ACCESS
, desired_tag
);
123 int has_default_user_obj_acl(const char* path
) {
124 return has_default_tag_acl(path
, ACL_USER_OBJ
);
127 int has_default_group_obj_acl(const char* path
) {
128 return has_default_tag_acl(path
, ACL_GROUP_OBJ
);
131 int has_default_other_acl(const char* path
) {
132 return has_default_tag_acl(path
, ACL_OTHER
);
135 int has_default_mask_acl(const char* path
) {
136 return has_default_tag_acl(path
, ACL_MASK
);
140 int get_type_tag_entry(const char* path
,
142 acl_tag_t desired_tag
,
143 acl_entry_t
* entry
) {
144 /* Returns one if successful, zero when the ACL doesn't exist, and
145 -1 on unexpected errors. */
146 acl_t acl
= acl_get_file(path
, type
);
148 if (acl
== (acl_t
)NULL
) {
149 /* Follow the acl_foo convention of -1 == error. */
153 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, entry
);
155 while (result
== 1) {
156 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
157 int tag_result
= acl_get_tag_type(*entry
, &tag
);
159 if (tag_result
== -1) {
160 perror("get_type_tag_entry (acl_get_tag_type)");
164 if (tag
== desired_tag
) {
165 /* We found the right tag, so return successfully. */
169 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, entry
);
172 /* This catches both the initial acl_get_entry and the ones at the
175 perror("get_type_tag_entry (acl_get_entry)");
182 int get_default_tag_entry(const char* path
,
183 acl_tag_t desired_tag
,
184 acl_entry_t
* entry
) {
185 return get_type_tag_entry(path
, ACL_TYPE_DEFAULT
, desired_tag
, entry
);
188 int get_access_tag_entry(const char* path
,
189 acl_tag_t desired_tag
,
190 acl_entry_t
* entry
) {
191 return get_type_tag_entry(path
, ACL_TYPE_ACCESS
, desired_tag
, entry
);
196 int get_type_tag_permset(const char* path
,
198 acl_tag_t desired_tag
,
199 acl_permset_t
* output_perms
) {
200 /* Returns one if successful, zero when the ACL doesn't exist, and
201 -1 on unexpected errors. */
202 acl_t defacl
= acl_get_file(path
, type
);
204 if (defacl
== (acl_t
)NULL
) {
205 /* Follow the acl_foo convention of -1 == error. */
210 int result
= get_type_tag_entry(path
, type
, desired_tag
, &entry
);
213 /* We found the right tag, now get the permset. */
214 int ps_result
= acl_get_permset(entry
, output_perms
);
215 if (ps_result
== -1) {
216 perror("get_type_tag_permset (acl_get_permset)");
220 if (ps_result
== 0) {
232 int get_default_tag_permset(const char* path
,
233 acl_tag_t desired_tag
,
234 acl_permset_t
* output_perms
) {
235 return get_type_tag_permset(path
,
241 int get_access_tag_permset(const char* path
,
242 acl_tag_t desired_tag
,
243 acl_permset_t
* output_perms
) {
244 return get_type_tag_permset(path
, ACL_TYPE_ACCESS
, desired_tag
, output_perms
);
247 int has_default_tag_perm(const char* path
,
250 /* Check path to see if tag has the given perm. Returns one if it
251 does, zero if it doesn't (or there's no ACL), and -1 on unexpected
254 if (!has_default_tag_acl(path
, tag
)) {
258 acl_permset_t permset
;
259 bool ps_result
= get_default_tag_permset(path
, tag
, &permset
);
261 if (ps_result
!= 1) {
262 /* Failure or error. */
266 int p_result
= acl_get_perm(permset
, perm
);
267 if (p_result
== -1) {
268 perror("has_default_tag_perm (acl_get_perm)");
274 int remove_access_tag_perm(const char* path
,
275 acl_tag_t desired_tag
,
277 /* Attempt to remove perm from tag. Returns one if successful, zero
278 if there was nothing to do, and -1 on errors. */
279 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
280 if (acl
== (acl_t
)NULL
) {
285 acl_permset_t permset
;
286 bool ps_result
= get_access_tag_permset(path
, desired_tag
, &permset
);
288 if (ps_result
!= 1) {
289 /* Failure or error. */
293 int d_result
= acl_delete_perm(permset
, perm
);
294 if (d_result
== -1) {
295 perror("remove_access_tag_perm (acl_delete_perm)");
299 /* We've only removed perm from the permset; now we have to replace
302 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
304 while (result
== 1) {
305 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
306 int tag_result
= acl_get_tag_type(entry
, &tag
);
308 if (tag_result
== -1) {
309 perror("remove_access_tag_perm (acl_get_tag_type)");
313 if (tag
== desired_tag
) {
314 /* We found the right tag. Update the permset. */
315 int s_result
= acl_set_permset(entry
, permset
);
316 if (s_result
== -1) {
317 perror("remove_access_tag_perm (acl_set_permset)");
321 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
322 if (sf_result
== -1) {
323 perror("remove_access_tag_perm (acl_set_file)");
330 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
333 /* This catches both the initial acl_get_entry and the ones at the
336 perror("remove_access_tag_perm (acl_get_entry)");
343 int remove_access_group_obj_execute(const char* path
) {
344 return remove_access_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
348 int has_default_user_obj_read(const char* path
) {
349 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_READ
);
352 int has_default_user_obj_write(const char* path
) {
353 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_WRITE
);
356 int has_default_user_obj_execute(const char* path
) {
357 return has_default_tag_perm(path
, ACL_USER_OBJ
, ACL_EXECUTE
);
360 int has_default_group_obj_read(const char* path
) {
361 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_READ
);
364 int has_default_group_obj_write(const char* path
) {
365 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_WRITE
);
368 int has_default_group_obj_execute(const char* path
) {
369 return has_default_tag_perm(path
, ACL_GROUP_OBJ
, ACL_EXECUTE
);
372 int has_default_other_read(const char* path
) {
373 return has_default_tag_perm(path
, ACL_OTHER
, ACL_READ
);
376 int has_default_other_write(const char* path
) {
377 return has_default_tag_perm(path
, ACL_OTHER
, ACL_WRITE
);
380 int has_default_other_execute(const char* path
) {
381 return has_default_tag_perm(path
, ACL_OTHER
, ACL_EXECUTE
);
384 int has_default_mask_read(const char* path
) {
385 return has_default_tag_perm(path
, ACL_MASK
, ACL_READ
);
388 int has_default_mask_write(const char* path
) {
389 return has_default_tag_perm(path
, ACL_MASK
, ACL_WRITE
);
392 int has_default_mask_execute(const char* path
) {
393 return has_default_tag_perm(path
, ACL_MASK
, ACL_EXECUTE
);
397 int reapply_default_acl(const char* path
) {
398 /* If this is a normal file or directory (i.e. that has just been
399 created), we proceed to find its parent directory which will have
402 Returns one for success, zero for failure (i.e. no ACL), and -1
403 on unexpected errors. */
408 if (!is_regular_file(path
) && !is_directory(path
)) {
412 /* dirname mangles its argument */
413 char path_copy
[PATH_MAX
];
414 strncpy(path_copy
, path
, PATH_MAX
-1);
415 path_copy
[PATH_MAX
-1] = 0;
417 char* parent
= dirname(path_copy
);
418 if (!is_directory(parent
)) {
419 /* Make sure dirname() did what we think it did. */
423 /* This is the original mode of path. We will simply add permissions
424 to it, and then later reapply the result via chmod. */
425 mode_t path_mode
= get_mode(path
);
427 if (has_default_mask_acl(parent
)) {
428 /* The parent has an extended ACL. Extended ACLs use the mask
431 /* For the group bits, we'll use the ACL's mask instead of the group
432 object bits. If the default ACL had a group entry, it should
433 already have propagated (but might be masked). */
434 if (has_default_mask_read(parent
)) {
435 path_mode
|= S_IRGRP
;
438 path_mode
&= ~S_IRGRP
;
441 if (has_default_mask_write(parent
)) {
442 path_mode
|= S_IWGRP
;
445 path_mode
&= ~S_IWGRP
;
448 if (!mode_has_perm(path_mode
, S_IXGRP
)) {
449 /* The group ACL entry should already have been inherited from the
450 default ACL. If the source was not group executable, we want to
451 modify the destination so that it is not group executable
452 either. In the presence of ACLs, the group permissions come not
453 from the mode bits, but from the group:: ACL entry. So, to do
454 this, we remove the group::x entry. */
455 remove_access_group_obj_execute(path
);
458 /* We need to determine whether or not to mask the execute
459 bit. This applies not only to the user/group/other entries, but
460 also to all other named entries. If the original file wasn't
461 executable, then the result probably should not be. To
462 determine whether or not "it was executable", we rely on the
463 user execute bits. Obviously this should be done before we
465 if (has_default_mask_execute(parent
)) {
466 if (mode_has_perm(path_mode
, S_IXUSR
)) {
467 /* This just adds the group execute bit, and doesn't actually
468 grant group execute permissions. */
469 path_mode
|= S_IXGRP
;
473 path_mode
&= ~S_IXGRP
;
478 /* It's a minimal ACL. We'll repeat for the group bits what we
479 already did for the owner/other bits. */
480 if (has_default_group_obj_acl(parent
)) {
481 if (has_default_group_obj_read(parent
)) {
482 path_mode
|= S_IRGRP
;
485 path_mode
&= ~S_IRGRP
;
489 if (has_default_group_obj_write(parent
)) {
490 path_mode
|= S_IWGRP
;
493 path_mode
&= ~S_IWGRP
;
496 /* We don't want to set the execute bit on via the ACL unless it
497 was on originally. */
498 if (!has_default_group_obj_execute(parent
)) {
499 path_mode
&= ~S_IXGRP
;
505 /* If parent has a default user ACL, apply it. */
506 if (has_default_user_obj_acl(parent
)) {
508 if (has_default_user_obj_read(parent
)) {
510 path_mode
|= S_IRUSR
;
513 /* Remove user read. */
514 path_mode
&= ~S_IRUSR
;
518 if (has_default_user_obj_write(parent
)) {
519 /* Add user write. */
520 path_mode
|= S_IWUSR
;
523 /* Remove user write. */
524 path_mode
&= ~S_IWUSR
;
528 /* We don't want to set the execute bit on via the ACL unless it
529 was on originally. */
530 if (!has_default_user_obj_execute(parent
)) {
531 /* Remove user execute. */
532 path_mode
&= ~S_IXUSR
;
537 /* Do the same thing with the other perms/ACL. */
538 if (has_default_other_acl(parent
)) {
540 if (has_default_other_read(parent
)) {
541 path_mode
|= S_IROTH
;
544 path_mode
&= ~S_IROTH
;
548 if (has_default_other_write(parent
)) {
549 path_mode
|= S_IWOTH
;
552 path_mode
&= ~S_IWOTH
;
556 /* We don't want to set the execute bit on via the ACL unless it
557 was on originally. */
558 if (!has_default_other_execute(parent
)) {
559 path_mode
&= ~S_IXOTH
;
563 int chmod_result
= chmod(path
, path_mode
);
564 if (chmod_result
== 0) {
576 int set_acl_entry(acl_t
* aclp
,
578 /* Update or create the given entry. */
581 int gt_result
= acl_get_tag_type(entry
, &entry_tag
);
582 if (gt_result
== -1) {
583 perror("set_acl_entry (acl_get_tag_type)");
587 acl_permset_t entry_permset
;
588 int ps_result
= acl_get_permset(entry
, &entry_permset
);
589 if (ps_result
== -1) {
590 perror("set_acl_entry (acl_get_permset)");
594 acl_entry_t existing_entry
;
595 /* Loop through the given ACL looking for matching entries. */
596 int result
= acl_get_entry(*aclp
, ACL_FIRST_ENTRY
, &existing_entry
);
598 while (result
== 1) {
599 acl_tag_t existing_tag
= ACL_UNDEFINED_TAG
;
600 int tag_result
= acl_get_tag_type(existing_entry
, &existing_tag
);
602 if (tag_result
== -1) {
603 perror("set_acl_tag_permset (acl_get_tag_type)");
607 if (existing_tag
== entry_tag
) {
608 bool update_it
= true;
610 if (entry_tag
== ACL_USER
|| entry_tag
== ACL_GROUP
) {
611 void* entry_qual
= acl_get_qualifier(entry
);
612 if (entry_qual
== (void*)NULL
) {
613 perror("set_acl_entry (acl_get_qualifier - entry_qual)");
617 void* existing_qual
= acl_get_qualifier(existing_entry
);
618 if (existing_qual
== (void*)NULL
) {
619 perror("set_acl_entry (acl_get_qualifier - existing_qual)");
623 if (entry_tag
== ACL_USER
) {
624 uid_t
* u1p
= (uid_t
*)entry_qual
;
625 uid_t
* u2p
= (uid_t
*)existing_qual
;
631 gid_t
* g1p
= (gid_t
*)entry_qual
;
632 gid_t
* g2p
= (gid_t
*)existing_qual
;
638 acl_free(entry_qual
);
639 acl_free(existing_qual
);
643 acl_permset_t existing_permset
;
644 int gep_result
= acl_get_permset(existing_entry
, &existing_permset
);
645 if (gep_result
== -1) {
646 perror("set_acl_entry (acl_get_permset)");
650 /* If an existing entry doesn't have its execute bits set, we
651 don't want to set them from a default ACL. Unless it's the
653 int gp_result
= acl_get_perm(existing_permset
, ACL_EXECUTE
);
654 if (gp_result
== -1) {
655 perror("set_acl_entry (acl_get_perm)");
659 if (gp_result
== 0 && existing_tag
!= ACL_MASK
) {
660 /* It doesn't already have execute perms */
661 int d_result
= acl_delete_perm(entry_permset
, ACL_EXECUTE
);
662 if (d_result
== -1) {
663 perror("set_acl_entry (acl_delete_perm)");
668 int s_result
= acl_set_permset(existing_entry
, entry_permset
);
669 if (s_result
== -1) {
670 perror("set_acl_entry (acl_set_permset)");
679 result
= acl_get_entry(*aclp
, ACL_NEXT_ENTRY
, &existing_entry
);
682 /* This catches both the initial acl_get_entry and the ones at the
685 perror("set_acl_entry (acl_get_entry)");
689 /* If we've made it this far, we need to add a new entry to the
691 acl_entry_t new_entry
;
692 int c_result
= acl_create_entry(aclp
, &new_entry
);
693 if (c_result
== -1) {
694 perror("set_acl_entry (acl_create_entry)");
698 int st_result
= acl_set_tag_type(new_entry
, entry_tag
);
699 if (st_result
== -1) {
700 perror("set_acl_entry (acl_set_tag_type)");
704 int s_result
= acl_set_permset(new_entry
, entry_permset
);
705 if (s_result
== -1) {
706 perror("set_acl_entry (acl_set_permset)");
710 if (entry_tag
== ACL_USER
|| entry_tag
== ACL_GROUP
) {
711 /* We need to set the qualifier too. */
712 void* entry_qual
= acl_get_qualifier(entry
);
713 if (entry_qual
== (void*)NULL
) {
714 perror("set_acl_entry (acl_get_qualifier)");
718 int sq_result
= acl_set_qualifier(new_entry
, entry_qual
);
719 if (sq_result
== -1) {
720 perror("set_acl_entry (acl_set_qualifier)");
730 int acl_is_minimal(acl_t
* acl
) {
731 /* An ACL is minimal if it has fewer than four entries. */
734 int result
= acl_get_entry(*acl
, ACL_FIRST_ENTRY
, &entry
);
736 while (result
== 1) {
738 result
= acl_get_entry(*acl
, ACL_NEXT_ENTRY
, &entry
);
742 perror("acl_is_minimal (acl_get_entry)");
746 if (entry_count
> 3) {
754 int any_can_execute(const char* path
) {
755 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
757 if (acl_is_minimal(&acl
)) {
758 mode_t mode
= get_mode(path
);
759 if (mode
& (S_IXUSR
| S_IXOTH
| S_IXGRP
)) {
767 if (acl
== (acl_t
)NULL
) {
772 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
774 while (result
== 1) {
775 acl_permset_t permset
;
777 int ps_result
= acl_get_permset(entry
, &permset
);
778 if (ps_result
== -1) {
779 perror("any_can_execute (acl_get_permset)");
783 int gp_result
= acl_get_perm(permset
, ACL_EXECUTE
);
784 if (gp_result
== -1) {
785 perror("any_can_execute (acl_get_perm)");
789 if (gp_result
== 1) {
793 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
797 perror("any_can_execute (acl_get_entry)");
805 int reapply_default_acl_ng(const char* path
) {
806 /* Really reapply the default ACL by looping through it. Returns one
807 for success, zero for failure (i.e. no ACL), and -1 on unexpected
813 if (!is_regular_file(path
) && !is_directory(path
)) {
817 /* dirname mangles its argument */
818 char path_copy
[PATH_MAX
];
819 strncpy(path_copy
, path
, PATH_MAX
-1);
820 path_copy
[PATH_MAX
-1] = 0;
822 char* parent
= dirname(path_copy
);
823 if (!is_directory(parent
)) {
824 /* Make sure dirname() did what we think it did. */
828 int ace_result
= any_can_execute(path
);
829 if (ace_result
== -1) {
830 perror("reapply_default_acl_ng (any_can_execute)");
834 bool allow_exec
= (bool)ace_result
;
836 acl_t defacl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
837 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
839 if (defacl
== (acl_t
)NULL
|| acl
== (acl_t
)NULL
) {
844 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
846 while (result
== 1) {
847 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
848 int tag_result
= acl_get_tag_type(entry
, &tag
);
850 if (tag_result
== -1) {
851 perror("has_default_tag_acl (acl_get_tag_type)");
856 /* We've got an entry/tag from the default ACL. Get its permset. */
857 acl_permset_t permset
;
858 int ps_result
= acl_get_permset(entry
, &permset
);
859 if (ps_result
== -1) {
860 perror("reapply_default_acl_ng (acl_get_permset)");
864 /* If this is a default mask, fix it up. */
865 if (tag
== ACL_MASK
||
866 tag
== ACL_USER_OBJ
||
867 tag
== ACL_GROUP_OBJ
||
870 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
871 minimal ACLs) or acl_other entries, so if execute should be
872 masked, we have to do it manually. */
873 int d_result
= acl_delete_perm(permset
, ACL_EXECUTE
);
874 if (d_result
== -1) {
875 perror("reapply_default_acl_ng (acl_delete_perm)");
879 int sp_result
= acl_set_permset(entry
, permset
);
880 if (sp_result
== -1) {
881 perror("reapply_default_acl_ng (acl_set_permset)");
887 /* Finally, add the permset to the access ACL. */
888 int set_result
= set_acl_entry(&acl
, entry
);
889 if (set_result
== -1) {
890 perror("reapply_default_acl_ng (set_acl_entry)");
894 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
897 /* Catches the first acl_get_entry as well as the ones at the end of
900 perror("reapply_default_acl_ng (acl_get_entry)");
904 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
905 if (sf_result
== -1) {
906 perror("reapply_default_acl_ng (acl_set_file)");
915 int main(int argc
, char* argv
[]) {
916 const char* target
= argv
[1];
918 bool result
= reapply_default_acl_ng(target
);