From 3b6789d9406dc890f4c9386c227986739652f97d Mon Sep 17 00:00:00 2001 From: Michael Orlitzky Date: Tue, 14 Aug 2012 23:59:27 -0400 Subject: [PATCH] Complicate the shit out of everything to pass the new tests. --- src/aclq.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 344 insertions(+), 1 deletion(-) diff --git a/src/aclq.c b/src/aclq.c index 0d09c5c..19749cd 100644 --- a/src/aclq.c +++ b/src/aclq.c @@ -214,6 +214,7 @@ int get_type_tag_permset(const char* path, int ps_result = acl_get_permset(entry, output_perms); if (ps_result == -1) { perror("get_type_tag_permset (acl_get_permset)"); + return -1; } if (ps_result == 0) { @@ -569,10 +570,352 @@ int reapply_default_acl(const char* path) { } + + + +int set_acl_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("set_acl_entry (acl_get_tag_type)"); + return -1; + } + + acl_permset_t entry_permset; + int ps_result = acl_get_permset(entry, &entry_permset); + if (ps_result == -1) { + perror("set_acl_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 existing_tag = ACL_UNDEFINED_TAG; + int tag_result = acl_get_tag_type(existing_entry, &existing_tag); + + if (tag_result == -1) { + perror("set_acl_tag_permset (acl_get_tag_type)"); + return -1; + } + + if (existing_tag == entry_tag) { + bool update_it = true; + + if (entry_tag == ACL_USER || entry_tag == ACL_GROUP) { + void* entry_qual = acl_get_qualifier(entry); + if (entry_qual == (void*)NULL) { + perror("set_acl_entry (acl_get_qualifier - entry_qual)"); + return -1; + } + + void* existing_qual = acl_get_qualifier(existing_entry); + if (existing_qual == (void*)NULL) { + perror("set_acl_entry (acl_get_qualifier - existing_qual)"); + return -1; + } + + if (entry_tag == ACL_USER) { + uid_t* u1p = (uid_t *)entry_qual; + uid_t* u2p = (uid_t *)existing_qual; + if (*u1p != *u2p) { + update_it = false; + } + } + else { + gid_t* g1p = (gid_t *)entry_qual; + gid_t* g2p = (gid_t *)existing_qual; + if (*g1p != *g2p) { + update_it = false; + } + } + + acl_free(entry_qual); + acl_free(existing_qual); + } + + if (update_it) { + acl_permset_t existing_permset; + int gep_result = acl_get_permset(existing_entry, &existing_permset); + if (gep_result == -1) { + perror("set_acl_entry (acl_get_permset)"); + return -1; + } + + /* If an existing entry doesn't have its execute bits set, we + don't want to set them from a default ACL. Unless it's the + mask entry! */ + int gp_result = acl_get_perm(existing_permset, ACL_EXECUTE); + if (gp_result == -1) { + perror("set_acl_entry (acl_get_perm)"); + return -1; + } + + if (gp_result == 0 && existing_tag != ACL_MASK) { + /* It doesn't already have execute perms */ + int d_result = acl_delete_perm(entry_permset, ACL_EXECUTE); + if (d_result == -1) { + perror("set_acl_entry (acl_delete_perm)"); + return -1; + } + } + + int s_result = acl_set_permset(existing_entry, entry_permset); + if (s_result == -1) { + perror("set_acl_entry (acl_set_permset)"); + return -1; + } + + return 1; + } + + } + + 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("set_acl_entry (acl_get_entry)"); + return -1; + } + + /* If we've made it this far, we need to add a new entry to the + ACL. */ + acl_entry_t new_entry; + int c_result = acl_create_entry(aclp, &new_entry); + if (c_result == -1) { + perror("set_acl_entry (acl_create_entry)"); + return -1; + } + + int st_result = acl_set_tag_type(new_entry, entry_tag); + if (st_result == -1) { + perror("set_acl_entry (acl_set_tag_type)"); + return -1; + } + + int s_result = acl_set_permset(new_entry, entry_permset); + if (s_result == -1) { + perror("set_acl_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("set_acl_entry (acl_get_qualifier)"); + return -1; + } + + int sq_result = acl_set_qualifier(new_entry, entry_qual); + if (sq_result == -1) { + perror("set_acl_entry (acl_set_qualifier)"); + return -1; + } + } + + return 1; +} + + + +int acl_is_minimal(acl_t* acl) { + /* An ACL is minimal if it has fewer than four entries. */ + 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; + } + + if (entry_count > 3) { + return 1; + } + else { + return 0; + } +} + +int any_can_execute(const char* path) { + acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS); + + if (acl_is_minimal(&acl)) { + mode_t mode = get_mode(path); + if (mode & (S_IXUSR | S_IXOTH | S_IXGRP)) { + return 1; + } + else { + return 0; + } + } + + if (acl == (acl_t)NULL) { + return 0; + } + + acl_entry_t entry; + int result = acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); + + while (result == 1) { + acl_permset_t permset; + + int ps_result = acl_get_permset(entry, &permset); + if (ps_result == -1) { + perror("any_can_execute (acl_get_permset)"); + return -1; + } + + int gp_result = acl_get_perm(permset, ACL_EXECUTE); + if (gp_result == -1) { + perror("any_can_execute (acl_get_perm)"); + return -1; + } + + if (gp_result == 1) { + return 1; + } + + result = acl_get_entry(acl, ACL_NEXT_ENTRY, &entry); + } + + if (result == -1) { + perror("any_can_execute (acl_get_entry)"); + return -1; + } + + return 0; +} + + +int reapply_default_acl_ng(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); + acl_t acl = acl_get_file(path, ACL_TYPE_ACCESS); + + if (defacl == (acl_t)NULL || acl == (acl_t)NULL) { + return 0; + } + + acl_entry_t entry; + int result = acl_get_entry(defacl, ACL_FIRST_ENTRY, &entry); + + while (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)"); + return -1; + } + + + /* 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)"); + return -1; + } + + /* 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)"); + return -1; + } + + int sp_result = acl_set_permset(entry, permset); + if (sp_result == -1) { + perror("reapply_default_acl_ng (acl_set_permset)"); + return -1; + } + } + } + + /* Finally, add the permset to the access ACL. */ + int set_result = set_acl_entry(&acl, entry); + if (set_result == -1) { + perror("reapply_default_acl_ng (set_acl_entry)"); + return -1; + } + + 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 (result == -1) { + perror("reapply_default_acl_ng (acl_get_entry)"); + return -1; + } + + int sf_result = acl_set_file(path, ACL_TYPE_ACCESS, acl); + if (sf_result == -1) { + perror("reapply_default_acl_ng (acl_set_file)"); + return -1; + } + + return 1; +} + + + int main(int argc, char* argv[]) { const char* target = argv[1]; - bool result = reapply_default_acl(target); + bool result = reapply_default_acl_ng(target); if (result) { return EXIT_SUCCESS; -- 2.44.2