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() */
36 bool is_regular_file(const char* path
) {
42 int result
= stat(path
, &s
);
44 return S_ISREG(s
.st_mode
);
51 bool is_directory(const char* path
) {
57 int result
= stat(path
, &s
);
59 return S_ISDIR(s
.st_mode
);
68 int acl_set_entry(acl_t
* aclp
,
70 /* Update or create the given entry. */
73 int gt_result
= acl_get_tag_type(entry
, &entry_tag
);
74 if (gt_result
== -1) {
75 perror("acl_set_entry (acl_get_tag_type)");
79 acl_permset_t entry_permset
;
80 int ps_result
= acl_get_permset(entry
, &entry_permset
);
81 if (ps_result
== -1) {
82 perror("acl_set_entry (acl_get_permset)");
86 acl_entry_t existing_entry
;
87 /* Loop through the given ACL looking for matching entries. */
88 int result
= acl_get_entry(*aclp
, ACL_FIRST_ENTRY
, &existing_entry
);
91 acl_tag_t existing_tag
= ACL_UNDEFINED_TAG
;
92 int tag_result
= acl_get_tag_type(existing_entry
, &existing_tag
);
94 if (tag_result
== -1) {
95 perror("set_acl_tag_permset (acl_get_tag_type)");
99 if (existing_tag
== entry_tag
) {
100 if (entry_tag
== ACL_USER_OBJ
||
101 entry_tag
== ACL_GROUP_OBJ
||
102 entry_tag
== ACL_OTHER
) {
103 /* Only update for these three since all other tags will have
105 acl_permset_t existing_permset
;
106 int gep_result
= acl_get_permset(existing_entry
, &existing_permset
);
107 if (gep_result
== -1) {
108 perror("acl_set_entry (acl_get_permset)");
112 int s_result
= acl_set_permset(existing_entry
, entry_permset
);
113 if (s_result
== -1) {
114 perror("acl_set_entry (acl_set_permset)");
123 result
= acl_get_entry(*aclp
, ACL_NEXT_ENTRY
, &existing_entry
);
126 /* This catches both the initial acl_get_entry and the ones at the
129 perror("acl_set_entry (acl_get_entry)");
133 /* If we've made it this far, we need to add a new entry to the
135 acl_entry_t new_entry
;
137 /* We allocate memory here that we should release! */
138 int c_result
= acl_create_entry(aclp
, &new_entry
);
139 if (c_result
== -1) {
140 perror("acl_set_entry (acl_create_entry)");
144 int st_result
= acl_set_tag_type(new_entry
, entry_tag
);
145 if (st_result
== -1) {
146 perror("acl_set_entry (acl_set_tag_type)");
150 int s_result
= acl_set_permset(new_entry
, entry_permset
);
151 if (s_result
== -1) {
152 perror("acl_set_entry (acl_set_permset)");
156 if (entry_tag
== ACL_USER
|| entry_tag
== ACL_GROUP
) {
157 /* We need to set the qualifier too. */
158 void* entry_qual
= acl_get_qualifier(entry
);
159 if (entry_qual
== (void*)NULL
) {
160 perror("acl_set_entry (acl_get_qualifier)");
164 int sq_result
= acl_set_qualifier(new_entry
, entry_qual
);
165 if (sq_result
== -1) {
166 perror("acl_set_entry (acl_set_qualifier)");
176 int acl_entry_count(acl_t
* acl
) {
177 /* Return the number of entries in ACL, or -1 on error. */
180 int result
= acl_get_entry(*acl
, ACL_FIRST_ENTRY
, &entry
);
182 while (result
== 1) {
184 result
= acl_get_entry(*acl
, ACL_NEXT_ENTRY
, &entry
);
188 perror("acl_is_minimal (acl_get_entry)");
196 int acl_is_minimal(acl_t
* acl
) {
197 /* An ACL is minimal if it has fewer than four entries. Return 0 for
198 false, 1 for true, and -1 on error. */
200 int ec
= acl_entry_count(acl
);
202 perror("acl_is_minimal (acl_entry_count)");
215 int any_can_execute(const char* path
) {
216 /* Returns 1 if any ACL entry has execute access, 0 if none do, and
218 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
220 if (acl
== (acl_t
)NULL
) {
224 /* Our return value. */
227 if (acl_is_minimal(&acl
)) {
228 mode_t mode
= get_mode(path
);
229 if (mode
& (S_IXUSR
| S_IXOTH
| S_IXGRP
)) {
240 int ge_result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
242 while (ge_result
== 1) {
243 acl_permset_t permset
;
245 int ps_result
= acl_get_permset(entry
, &permset
);
246 if (ps_result
== -1) {
247 perror("any_can_execute (acl_get_permset)");
252 int gp_result
= acl_get_perm(permset
, ACL_EXECUTE
);
253 if (gp_result
== -1) {
254 perror("any_can_execute (acl_get_perm)");
259 if (gp_result
== 1) {
264 ge_result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
267 if (ge_result
== -1) {
268 perror("any_can_execute (acl_get_entry)");
279 int inherit_default_acl(const char* path
, const char* parent
) {
280 /* Inherit the default ACL from parent to path. This overwrites any
281 existing default ACL. Returns 1 for success, 0 for failure, and
284 /* Our return value. */
292 if (!is_directory(path
) || !is_directory(parent
)) {
296 acl_t parent_acl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
297 if (parent_acl
== (acl_t
)NULL
) {
301 acl_t path_acl
= acl_dup(parent_acl
);
303 if (path_acl
== (acl_t
)NULL
) {
304 perror("inherit_default_acl (acl_dup)");
305 acl_free(parent_acl
);
309 int sf_result
= acl_set_file(path
, ACL_TYPE_DEFAULT
, path_acl
);
310 if (sf_result
== -1) {
311 perror("inherit_default_acl (acl_set_file)");
322 int wipe_acls(const char* path
) {
323 /* Remove ACL_USER, ACL_GROUP, and ACL_MASK entries from
324 path. Returns 1 for success, 0 for failure, and -1 on error. */
331 /* Finally, remove individual named/mask entries. */
332 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
333 if (acl
== (acl_t
)NULL
) {
334 perror("wipe_acls (acl_get_file)");
338 /* Our return value. */
342 int ge_result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
344 while (ge_result
== 1) {
345 int d_result
= acl_delete_entry(acl
, entry
);
346 if (d_result
== -1) {
347 perror("wipe_acls (acl_delete_entry)");
352 ge_result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
355 /* Catches the first acl_get_entry as well as the ones at the end of
357 if (ge_result
== -1) {
358 perror("reapply_default_acl_ng (acl_get_entry)");
363 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
364 if (sf_result
== -1) {
365 perror("wipe_acls (acl_set_file)");
376 int reapply_default_acl(const char* path
) {
377 /* Really reapply the default ACL by looping through it. Returns one
378 for success, zero for failure (i.e. no ACL), and -1 on unexpected
384 if (!is_regular_file(path
) && !is_directory(path
)) {
388 /* dirname mangles its argument */
389 char path_copy
[PATH_MAX
];
390 strncpy(path_copy
, path
, PATH_MAX
-1);
391 path_copy
[PATH_MAX
-1] = 0;
393 char* parent
= dirname(path_copy
);
394 if (!is_directory(parent
)) {
395 /* Make sure dirname() did what we think it did. */
399 int ace_result
= any_can_execute(path
);
400 if (ace_result
== -1) {
401 perror("reapply_default_acl_ng (any_can_execute)");
405 bool allow_exec
= (bool)ace_result
;
407 acl_t defacl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
409 if (defacl
== (acl_t
)NULL
) {
410 perror("reapply_default_acl_ng (acl_get_file)");
414 /* Our return value. */
417 int wipe_result
= wipe_acls(path
);
418 if (wipe_result
== -1) {
419 perror("reapply_default_acl_ng (wipe_acls)");
424 /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
425 ACL with this one. */
426 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
427 if (acl
== (acl_t
)NULL
) {
428 perror("reapply_default_acl_ng (acl_get_file)");
432 /* If it's a directory, inherit the parent's default. */
433 int inherit_result
= inherit_default_acl(path
, parent
);
434 if (inherit_result
== -1) {
435 perror("reapply_default_acl_ng (inherit_acls)");
441 int ge_result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
443 while (ge_result
== 1) {
444 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
445 int tag_result
= acl_get_tag_type(entry
, &tag
);
447 if (tag_result
== -1) {
448 perror("has_default_tag_acl (acl_get_tag_type)");
454 /* We've got an entry/tag from the default ACL. Get its permset. */
455 acl_permset_t permset
;
456 int ps_result
= acl_get_permset(entry
, &permset
);
457 if (ps_result
== -1) {
458 perror("reapply_default_acl_ng (acl_get_permset)");
463 /* If this is a default mask, fix it up. */
464 if (tag
== ACL_MASK
||
465 tag
== ACL_USER_OBJ
||
466 tag
== ACL_GROUP_OBJ
||
469 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
470 minimal ACLs) or acl_other entries, so if execute should be
471 masked, we have to do it manually. */
472 int d_result
= acl_delete_perm(permset
, ACL_EXECUTE
);
473 if (d_result
== -1) {
474 perror("reapply_default_acl_ng (acl_delete_perm)");
479 int sp_result
= acl_set_permset(entry
, permset
);
480 if (sp_result
== -1) {
481 perror("reapply_default_acl_ng (acl_set_permset)");
488 /* Finally, add the permset to the access ACL. */
489 int set_result
= acl_set_entry(&acl
, entry
);
490 if (set_result
== -1) {
491 perror("reapply_default_acl_ng (acl_set_entry)");
496 ge_result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
499 /* Catches the first acl_get_entry as well as the ones at the end of
501 if (ge_result
== -1) {
502 perror("reapply_default_acl_ng (acl_get_entry)");
507 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
508 if (sf_result
== -1) {
509 perror("reapply_default_acl_ng (acl_set_file)");
521 int main(int argc
, char* argv
[]) {
522 const char* target
= argv
[1];
524 bool result
= reapply_default_acl(target
);