X-Git-Url: https://gitweb.michael.orlitzky.com/?a=blobdiff_plain;f=src%2Faclq.c;h=9217bc240da90bbc481a51f66889e16b3325452a;hb=b7804ac18110deba71f84682869bd43a12fd1daf;hp=4f9ad721d2b9c5ac5bb68d428e61b01228a30336;hpb=c86a532bb3c052c423a1e7ffddaa253c73e7267d;p=apply-default-acl.git diff --git a/src/aclq.c b/src/aclq.c index 4f9ad72..9217bc2 100644 --- a/src/aclq.c +++ b/src/aclq.c @@ -1,228 +1,532 @@ #include +#include /* dirname() */ #include /* PATH_MAX */ #include #include #include +#include +#include +#include /* ACLs */ +#include /* acl_get_perm, not portable */ #include #include -bool has_default_acl(const char* path) { - /* Return true if the given path has a default ACL, false - otherwise. */ - acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT); - - if (defacl == (acl_t)NULL) { +mode_t get_mode(const char* path) { + if (path == NULL) { + errno = ENOENT; + return -1; + } + + struct stat s; + int result = stat(path, &s); + + if (result == 0) { + return s.st_mode; + } + else { + /* errno will be set already by stat() */ + return result; + } +} + + +bool is_regular_file(const char* path) { + if (path == NULL) { return false; } - /* Used to store the entry if it exists, even though we don't care - what it is. */ - acl_entry_t dummy; - - int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &dummy); - - if (result == 1) { - /* There's a first entry in the default ACL. */ - return true; + struct stat s; + int result = stat(path, &s); + if (result == 0) { + return S_ISREG(s.st_mode); } - else if (result == 0) { + else { return false; } +} + +bool is_directory(const char* path) { + if (path == NULL) { + return false; + } + + struct stat s; + int result = stat(path, &s); + if (result == 0) { + return S_ISDIR(s.st_mode); + } else { - perror("has_default_acl"); return false; } } -bool has_default_tag_acl(const char* path, acl_tag_t tag_type) { - /* Return true if the given path has a default ACL for the supplied - tag, false otherwise. */ - acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT); - - if (defacl == (acl_t)NULL) { - return false; +int acl_set_entry(acl_t* aclp, + acl_entry_t entry) { + /* Update or create the given entry. */ + + acl_tag_t entry_tag; + int gt_result = acl_get_tag_type(entry, &entry_tag); + if (gt_result == -1) { + perror("acl_set_entry (acl_get_tag_type)"); + return -1; } - acl_entry_t entry; - int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry); + acl_permset_t entry_permset; + int ps_result = acl_get_permset(entry, &entry_permset); + if (ps_result == -1) { + perror("acl_set_entry (acl_get_permset)"); + return -1; + } + + acl_entry_t existing_entry; + /* Loop through the given ACL looking for matching entries. */ + int result = acl_get_entry(*aclp, ACL_FIRST_ENTRY, &existing_entry); while (result == 1) { - acl_tag_t tag = ACL_UNDEFINED_TAG; - int tag_result = acl_get_tag_type(entry, &tag); - + acl_tag_t existing_tag = ACL_UNDEFINED_TAG; + int tag_result = acl_get_tag_type(existing_entry, &existing_tag); + if (tag_result == -1) { - perror("has_default_tag_acl - acl_get_tag_type"); - return false; + perror("set_acl_tag_permset (acl_get_tag_type)"); + return -1; } - else { - if (tag == tag_type) { - return true; + + if (existing_tag == entry_tag) { + if (entry_tag == ACL_USER_OBJ || + entry_tag == ACL_GROUP_OBJ || + entry_tag == ACL_OTHER) { + /* Only update for these three since all other tags will have + been wiped. */ + acl_permset_t existing_permset; + int gep_result = acl_get_permset(existing_entry, &existing_permset); + if (gep_result == -1) { + perror("acl_set_entry (acl_get_permset)"); + return -1; + } + + int s_result = acl_set_permset(existing_entry, entry_permset); + if (s_result == -1) { + perror("acl_set_entry (acl_set_permset)"); + return -1; + } + + return 1; } + } - - result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry); + + result = acl_get_entry(*aclp, ACL_NEXT_ENTRY, &existing_entry); + } + + /* This catches both the initial acl_get_entry and the ones at the + end of the loop. */ + if (result == -1) { + perror("acl_set_entry (acl_get_entry)"); + return -1; } - - return false; -} + /* If we've made it this far, we need to add a new entry to the + ACL. */ + acl_entry_t new_entry; -bool has_default_user_obj_acl(const char* path) { - return has_default_tag_acl(path, ACL_USER_OBJ); -} + /* We allocate memory here that we should release! */ + int c_result = acl_create_entry(aclp, &new_entry); + if (c_result == -1) { + perror("acl_set_entry (acl_create_entry)"); + return -1; + } + + int st_result = acl_set_tag_type(new_entry, entry_tag); + if (st_result == -1) { + perror("acl_set_entry (acl_set_tag_type)"); + return -1; + } + + int s_result = acl_set_permset(new_entry, entry_permset); + if (s_result == -1) { + perror("acl_set_entry (acl_set_permset)"); + return -1; + } + + if (entry_tag == ACL_USER || entry_tag == ACL_GROUP) { + /* We need to set the qualifier too. */ + void* entry_qual = acl_get_qualifier(entry); + if (entry_qual == (void*)NULL) { + perror("acl_set_entry (acl_get_qualifier)"); + return -1; + } + + int sq_result = acl_set_qualifier(new_entry, entry_qual); + if (sq_result == -1) { + perror("acl_set_entry (acl_set_qualifier)"); + return -1; + } + } -bool has_default_group_obj_acl(const char* path) { - return has_default_tag_acl(path, ACL_GROUP_OBJ); + return 1; } -bool has_default_other_obj_acl(const char* path) { - return has_default_tag_acl(path, ACL_OTHER); + + +int acl_entry_count(acl_t* acl) { + /* Return the number of entries in ACL, or -1 on error. */ + acl_entry_t entry; + int entry_count = 0; + int result = acl_get_entry(*acl, ACL_FIRST_ENTRY, &entry); + + while (result == 1) { + entry_count++; + result = acl_get_entry(*acl, ACL_NEXT_ENTRY, &entry); + } + + if (result == -1) { + perror("acl_is_minimal (acl_get_entry)"); + return -1; + } + + return entry_count; } -int get_default_tag_permset(const char* path, - acl_tag_t tag_type, - acl_permset_t* output_perms) { - /* Returns 0 if successful or -1 on error in accordance with - acl_get_permset. */ - acl_t defacl = acl_get_file(path, ACL_TYPE_DEFAULT); - - if (defacl == (acl_t)NULL) { - /* Follow the acl_foo convention of -1 == error. */ - errno = EINVAL; +int acl_is_minimal(acl_t* acl) { + /* An ACL is minimal if it has fewer than four entries. Return 0 for + false, 1 for true, and -1 on error. */ + + int ec = acl_entry_count(acl); + if (ec == -1) { + perror("acl_is_minimal (acl_entry_count)"); return -1; } - acl_entry_t entry; - int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry); + if (ec < 4) { + return 1; + } + else { + return 0; + } +} - while (result == 1) { - acl_tag_t tag = ACL_UNDEFINED_TAG; - int tag_result = acl_get_tag_type(entry, &tag); - - if (tag_result == -1) { - perror("get_default_tag_permset"); - return -1; + +int any_can_execute(const char* path) { + /* Returns 1 if any ACL entry has execute access, 0 if none do, and + -1 on error. */ + acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS); + + if (acl == (acl_t)NULL) { + return 0; + } + + /* Our return value. */ + int result = 0; + + if (acl_is_minimal(&acl)) { + mode_t mode = get_mode(path); + if (mode & (S_IXUSR | S_IXOTH | S_IXGRP)) { + result = 1; + goto cleanup; } else { - if (tag == tag_type) { - /* We found the right tag, now get the permset. */ - return acl_get_permset(entry, output_perms); - } + result = 0; + goto cleanup; } - - result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry); } - errno = EINVAL; - return -1; -} + acl_entry_t entry; + int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); -int get_default_user_obj_permset(const char* path, - acl_permset_t* output_perms) { - return get_default_tag_permset(path, ACL_USER_OBJ, output_perms); -} + while (ge_result == 1) { + acl_permset_t permset; -int get_default_group_obj_permset(const char* path, - acl_permset_t* output_perms) { - return get_default_tag_permset(path, ACL_GROUP_OBJ, output_perms); -} + int ps_result = acl_get_permset(entry, &permset); + if (ps_result == -1) { + perror("any_can_execute (acl_get_permset)"); + result = -1; + goto cleanup; + } + + int gp_result = acl_get_perm(permset, ACL_EXECUTE); + if (gp_result == -1) { + perror("any_can_execute (acl_get_perm)"); + result = -1; + goto cleanup; + } + + if (gp_result == 1) { + result = 1; + goto cleanup; + } + + ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); + } -int get_default_other_obj_permset(const char* path, - acl_permset_t* output_perms) { - return get_default_tag_permset(path, ACL_OTHER, output_perms); + if (ge_result == -1) { + perror("any_can_execute (acl_get_entry)"); + result = -1; + goto cleanup; + } + + cleanup: + acl_free(acl); + return result; } +int inherit_default_acl(const char* path, const char* parent) { + /* Inherit the default ACL from parent to path. This overwrites any + existing default ACL. Returns 1 for success, 0 for failure, and + -1 on error. */ -bool has_default_tag_perm(const char* path, acl_perm_t, perm) { - acl_permset_t permset; - int ps_result = get_default_tag_permset(path, tag, &permset); + /* Our return value. */ + int result = 1; - if (ps_result == -1) { - perror("has_default_tag_perm - get_default_tag_permset"); - return false; + if (path == NULL) { + errno = ENOENT; + return -1; } - int p_result = acl_get_perm(permset, perm); - if (p_result == 1) { - return true; + if (!is_directory(path) || !is_directory(parent)) { + return 0; } - else if (p_result == 0) { - return false; + + acl_t parent_acl = acl_get_file(parent, ACL_TYPE_DEFAULT); + if (parent_acl == (acl_t)NULL) { + return 0; } - else { - /* p_result == -1 */ - perror("has_default_tag_perm - get_default_tag_permset"); - return false; + + acl_t path_acl = acl_dup(parent_acl); + + if (path_acl == (acl_t)NULL) { + perror("inherit_default_acl (acl_dup)"); + acl_free(parent_acl); + return -1; + } + + int sf_result = acl_set_file(path, ACL_TYPE_DEFAULT, path_acl); + if (sf_result == -1) { + perror("inherit_default_acl (acl_set_file)"); + result = -1; + goto cleanup; } -} -bool has_default_user_obj_read(const char* path) { - return has_default_tag_perm(ACL_USER_OBJ, ACL_READ); + cleanup: + acl_free(path_acl); + return result; } -bool has_default_user_obj_write(const char* path) { - return has_default_tag_perm(ACL_USER_OBJ, ACL_WRITE); + +int wipe_acls(const char* path) { + /* Remove ACL_USER, ACL_GROUP, and ACL_MASK entries from + path. Returns 1 for success, 0 for failure, and -1 on error. */ + + if (path == NULL) { + errno = ENOENT; + return -1; + } + + /* Finally, remove individual named/mask entries. */ + acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS); + if (acl == (acl_t)NULL) { + perror("wipe_acls (acl_get_file)"); + return -1; + } + + /* Our return value. */ + int result = 1; + + acl_entry_t entry; + int ge_result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); + + while (ge_result == 1) { + int d_result = acl_delete_entry(acl, entry); + if (d_result == -1) { + perror("wipe_acls (acl_delete_entry)"); + result = -1; + goto cleanup; + } + + ge_result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); + } + + /* Catches the first acl_get_entry as well as the ones at the end of + the loop. */ + if (ge_result == -1) { + perror("reapply_default_acl_ng (acl_get_entry)"); + result = -1; + goto cleanup; + } + + int sf_result = acl_set_file(path, ACL_TYPE_ACCESS, acl); + if (sf_result == -1) { + perror("wipe_acls (acl_set_file)"); + result = -1; + goto cleanup; + } + + cleanup: + acl_free(acl); + return result; } -bool has_default_user_obj_execute(const char* path) { - return has_default_tag_perm(ACL_USER_OBJ, ACL_EXECUTE); + +int reapply_default_acl(const char* path) { + /* Really reapply the default ACL by looping through it. Returns one + for success, zero for failure (i.e. no ACL), and -1 on unexpected + errors. */ + if (path == NULL) { + return 0; + } + + if (!is_regular_file(path) && !is_directory(path)) { + return 0; + } + + /* dirname mangles its argument */ + char path_copy[PATH_MAX]; + strncpy(path_copy, path, PATH_MAX-1); + path_copy[PATH_MAX-1] = 0; + + char* parent = dirname(path_copy); + if (!is_directory(parent)) { + /* Make sure dirname() did what we think it did. */ + return 0; + } + + int ace_result = any_can_execute(path); + if (ace_result == -1) { + perror("reapply_default_acl_ng (any_can_execute)"); + return -1; + } + + bool allow_exec = (bool)ace_result; + + acl_t defacl = acl_get_file(parent, ACL_TYPE_DEFAULT); + + if (defacl == (acl_t)NULL) { + perror("reapply_default_acl_ng (acl_get_file)"); + return -1; + } + + /* Our return value. */ + int result = 1; + + int wipe_result = wipe_acls(path); + if (wipe_result == -1) { + perror("reapply_default_acl_ng (wipe_acls)"); + result = -1; + goto cleanup; + } + + /* Do this after wipe_acls(), otherwise we'll overwrite the wiped + ACL with this one. */ + acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS); + if (acl == (acl_t)NULL) { + perror("reapply_default_acl_ng (acl_get_file)"); + return -1; + } + + /* If it's a directory, inherit the parent's default. */ + int inherit_result = inherit_default_acl(path, parent); + if (inherit_result == -1) { + perror("reapply_default_acl_ng (inherit_acls)"); + result = -1; + goto cleanup; + } + + acl_entry_t entry; + int ge_result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry); + + while (ge_result == 1) { + acl_tag_t tag = ACL_UNDEFINED_TAG; + int tag_result = acl_get_tag_type(entry, &tag); + + if (tag_result == -1) { + perror("has_default_tag_acl (acl_get_tag_type)"); + result = -1; + goto cleanup; + } + + + /* We've got an entry/tag from the default ACL. Get its permset. */ + acl_permset_t permset; + int ps_result = acl_get_permset(entry, &permset); + if (ps_result == -1) { + perror("reapply_default_acl_ng (acl_get_permset)"); + result = -1; + goto cleanup; + } + + /* If this is a default mask, fix it up. */ + if (tag == ACL_MASK || + tag == ACL_USER_OBJ || + tag == ACL_GROUP_OBJ || + tag == ACL_OTHER) { + if (!allow_exec) { + /* The mask doesn't affect acl_user_obj, acl_group_obj (in + minimal ACLs) or acl_other entries, so if execute should be + masked, we have to do it manually. */ + int d_result = acl_delete_perm(permset, ACL_EXECUTE); + if (d_result == -1) { + perror("reapply_default_acl_ng (acl_delete_perm)"); + result = -1; + goto cleanup; + } + + int sp_result = acl_set_permset(entry, permset); + if (sp_result == -1) { + perror("reapply_default_acl_ng (acl_set_permset)"); + result = -1; + goto cleanup; + } + } + } + + /* Finally, add the permset to the access ACL. */ + int set_result = acl_set_entry(&acl, entry); + if (set_result == -1) { + perror("reapply_default_acl_ng (acl_set_entry)"); + result = -1; + goto cleanup; + } + + ge_result = acl_get_entry(defacl, ACL_NEXT_ENTRY, &entry); + } + + /* Catches the first acl_get_entry as well as the ones at the end of + the loop. */ + if (ge_result == -1) { + perror("reapply_default_acl_ng (acl_get_entry)"); + result = -1; + goto cleanup; + } + + int sf_result = acl_set_file(path, ACL_TYPE_ACCESS, acl); + if (sf_result == -1) { + perror("reapply_default_acl_ng (acl_set_file)"); + result = -1; + goto cleanup; + } + + cleanup: + acl_free(defacl); + return result; } + + int main(int argc, char* argv[]) { const char* target = argv[1]; - printf("Target: %s\n", target); - - if (has_default_acl(target)) { - printf("Target has a default ACL.\n"); - } - else { - printf("Target does not have a default ACL.\n"); - } - if (has_default_user_obj_acl(target)) { - printf("Target has a default owner ACL.\n"); - acl_permset_t owner_perms; - get_default_user_obj_permset(target, &owner_perms); - if (acl_get_perm(owner_perms, ACL_READ) == 1) { - printf("User: read\n"); - } - if (acl_get_perm(owner_perms, ACL_WRITE) == 1) { - printf("User: write\n"); - } - if (acl_get_perm(owner_perms, ACL_EXECUTE) == 1) { - printf("User: execute\n"); - } - } - else { - printf("Target does not have a default owner ACL.\n"); - } - - if (has_default_group_obj_acl(target)) { - printf("Target has a default group ACL.\n"); - } - else { - printf("Target does not have a default group ACL.\n"); - } + bool result = reapply_default_acl(target); - if (has_default_other_obj_acl(target)) { - printf("Target has a default other ACL.\n"); + if (result) { + return EXIT_SUCCESS; } else { - printf("Target does not have a default other ACL.\n"); + return EXIT_FAILURE; } - /* - acl_permset_t group_perms; - get_default_group_obj_permset(); - - acl_permset_t other_perms; - get_default_other_obj_permset(); - */ - return EXIT_SUCCESS; }