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
;
136 int c_result
= acl_create_entry(aclp
, &new_entry
);
137 if (c_result
== -1) {
138 perror("acl_set_entry (acl_create_entry)");
142 int st_result
= acl_set_tag_type(new_entry
, entry_tag
);
143 if (st_result
== -1) {
144 perror("acl_set_entry (acl_set_tag_type)");
148 int s_result
= acl_set_permset(new_entry
, entry_permset
);
149 if (s_result
== -1) {
150 perror("acl_set_entry (acl_set_permset)");
154 if (entry_tag
== ACL_USER
|| entry_tag
== ACL_GROUP
) {
155 /* We need to set the qualifier too. */
156 void* entry_qual
= acl_get_qualifier(entry
);
157 if (entry_qual
== (void*)NULL
) {
158 perror("acl_set_entry (acl_get_qualifier)");
162 int sq_result
= acl_set_qualifier(new_entry
, entry_qual
);
163 if (sq_result
== -1) {
164 perror("acl_set_entry (acl_set_qualifier)");
174 int acl_entry_count(acl_t
* acl
) {
175 /* Return the number of entries in ACL, or -1 on error. */
178 int result
= acl_get_entry(*acl
, ACL_FIRST_ENTRY
, &entry
);
180 while (result
== 1) {
182 result
= acl_get_entry(*acl
, ACL_NEXT_ENTRY
, &entry
);
186 perror("acl_is_minimal (acl_get_entry)");
194 int acl_is_minimal(acl_t
* acl
) {
195 /* An ACL is minimal if it has fewer than four entries. Return 0 for
196 false, 1 for true, and -1 on error. */
198 int ec
= acl_entry_count(acl
);
200 perror("acl_is_minimal (acl_entry_count)");
213 int any_can_execute(const char* path
) {
214 /* Returns 1 if any ACL entry has execute access, 0 if none do, and
216 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
218 if (acl_is_minimal(&acl
)) {
219 mode_t mode
= get_mode(path
);
220 if (mode
& (S_IXUSR
| S_IXOTH
| S_IXGRP
)) {
228 if (acl
== (acl_t
)NULL
) {
233 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
235 while (result
== 1) {
236 acl_permset_t permset
;
238 int ps_result
= acl_get_permset(entry
, &permset
);
239 if (ps_result
== -1) {
240 perror("any_can_execute (acl_get_permset)");
244 int gp_result
= acl_get_perm(permset
, ACL_EXECUTE
);
245 if (gp_result
== -1) {
246 perror("any_can_execute (acl_get_perm)");
250 if (gp_result
== 1) {
254 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
258 perror("any_can_execute (acl_get_entry)");
266 int inherit_default_acl(const char* path
, const char* parent
) {
267 /* Inherit the default ACL from parent to path. This overwrites any
268 existing default ACL. Returns 1 for success, 0 for failure, and
276 if (!is_directory(path
) || !is_directory(parent
)) {
280 acl_t parent_acl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
281 acl_t path_acl
= acl_dup(parent_acl
);
283 if (path_acl
== (acl_t
)NULL
) {
284 perror("inherit_default_acl (acl_dup)");
288 int sf_result
= acl_set_file(path
, ACL_TYPE_DEFAULT
, path_acl
);
289 if (sf_result
== -1) {
290 perror("inherit_default_acl (acl_set_file)");
299 int wipe_acls(const char* path
) {
300 /* Remove ACL_USER, ACL_GROUP, and ACL_MASK entries from
301 path. Returns 1 for success, 0 for failure, and -1 on error. */
308 /* Finally, remove individual named/mask entries. */
309 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
310 if (acl
== (acl_t
)NULL
) {
311 perror("wipe_acls (acl_get_file)");
316 int result
= acl_get_entry(acl
, ACL_FIRST_ENTRY
, &entry
);
318 while (result
== 1) {
319 int d_result
= acl_delete_entry(acl
, entry
);
320 if (d_result
== -1) {
321 perror("wipe_acls (acl_delete_entry)");
325 result
= acl_get_entry(acl
, ACL_NEXT_ENTRY
, &entry
);
328 /* Catches the first acl_get_entry as well as the ones at the end of
331 perror("reapply_default_acl_ng (acl_get_entry)");
335 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
336 if (sf_result
== -1) {
337 perror("wipe_acls (acl_set_file)");
345 int reapply_default_acl(const char* path
) {
346 /* Really reapply the default ACL by looping through it. Returns one
347 for success, zero for failure (i.e. no ACL), and -1 on unexpected
353 if (!is_regular_file(path
) && !is_directory(path
)) {
357 /* dirname mangles its argument */
358 char path_copy
[PATH_MAX
];
359 strncpy(path_copy
, path
, PATH_MAX
-1);
360 path_copy
[PATH_MAX
-1] = 0;
362 char* parent
= dirname(path_copy
);
363 if (!is_directory(parent
)) {
364 /* Make sure dirname() did what we think it did. */
368 int ace_result
= any_can_execute(path
);
369 if (ace_result
== -1) {
370 perror("reapply_default_acl_ng (any_can_execute)");
374 bool allow_exec
= (bool)ace_result
;
376 acl_t defacl
= acl_get_file(parent
, ACL_TYPE_DEFAULT
);
378 if (defacl
== (acl_t
)NULL
) {
379 perror("reapply_default_acl_ng (acl_get_file)");
383 int wipe_result
= wipe_acls(path
);
384 if (wipe_result
== -1) {
385 perror("reapply_default_acl_ng (wipe_acls)");
389 /* Do this after wipe_acls(), otherwise we'll overwrite the wiped
390 ACL with this one. */
391 acl_t acl
= acl_get_file(path
, ACL_TYPE_ACCESS
);
392 if (acl
== (acl_t
)NULL
) {
393 perror("reapply_default_acl_ng (acl_get_file)");
397 /* If it's a directory, inherit the parent's default. */
398 int inherit_result
= inherit_default_acl(path
, parent
);
399 if (inherit_result
== -1) {
400 perror("reapply_default_acl_ng (inherit_acls)");
405 int result
= acl_get_entry(defacl
, ACL_FIRST_ENTRY
, &entry
);
407 while (result
== 1) {
408 acl_tag_t tag
= ACL_UNDEFINED_TAG
;
409 int tag_result
= acl_get_tag_type(entry
, &tag
);
411 if (tag_result
== -1) {
412 perror("has_default_tag_acl (acl_get_tag_type)");
417 /* We've got an entry/tag from the default ACL. Get its permset. */
418 acl_permset_t permset
;
419 int ps_result
= acl_get_permset(entry
, &permset
);
420 if (ps_result
== -1) {
421 perror("reapply_default_acl_ng (acl_get_permset)");
425 /* If this is a default mask, fix it up. */
426 if (tag
== ACL_MASK
||
427 tag
== ACL_USER_OBJ
||
428 tag
== ACL_GROUP_OBJ
||
431 /* The mask doesn't affect acl_user_obj, acl_group_obj (in
432 minimal ACLs) or acl_other entries, so if execute should be
433 masked, we have to do it manually. */
434 int d_result
= acl_delete_perm(permset
, ACL_EXECUTE
);
435 if (d_result
== -1) {
436 perror("reapply_default_acl_ng (acl_delete_perm)");
440 int sp_result
= acl_set_permset(entry
, permset
);
441 if (sp_result
== -1) {
442 perror("reapply_default_acl_ng (acl_set_permset)");
448 /* Finally, add the permset to the access ACL. */
449 int set_result
= acl_set_entry(&acl
, entry
);
450 if (set_result
== -1) {
451 perror("reapply_default_acl_ng (acl_set_entry)");
455 result
= acl_get_entry(defacl
, ACL_NEXT_ENTRY
, &entry
);
458 /* Catches the first acl_get_entry as well as the ones at the end of
461 perror("reapply_default_acl_ng (acl_get_entry)");
465 int sf_result
= acl_set_file(path
, ACL_TYPE_ACCESS
, acl
);
466 if (sf_result
== -1) {
467 perror("reapply_default_acl_ng (acl_set_file)");
476 int main(int argc
, char* argv
[]) {
477 const char* target
= argv
[1];
479 bool result
= reapply_default_acl(target
);