/** * Run this function on every resource (file, class, var etc) access to * grant/deny rights. Currently it checks if: * 1. #ipaddr matches the subnet expression in {admit,deny}_ips * 2. #hostname matches the subdomain expression in {admit,deny}_hostnames * 3. #key is found exactly as it is in {admit,deny}_keys * * @return Default is false, i.e. deny. If a match is found in #acl->admit.* * then return true, unless a match is also found in #acl->deny.* in * which case return false. * * @TODO preprocess our global ACL the moment a client connects, and store in * ServerConnectionState a list of objects that he can access. That way * only his relevant resources will be stored in e.g. {admit,deny}_paths * lists, and running through these two lists on every file request will * be much faster. */ bool access_CheckResource(const struct resource_acl *acl, const char *ipaddr, const char *hostname, const char *key) { /* Only hostname may be NULL in case of resolution failure. */ assert(ipaddr != NULL); assert(key != NULL); size_t pos = (size_t) -1; bool access = false; /* DENY by default */ /* First we check for admission, secondly for denial, so that denial takes * precedence. */ const char *rule; if (acl->admit.ips) { /* Still using legacy code here, doing linear search over all IPs in * textual representation... too CPU intensive! TODO store the ACL as * one list of struct sockaddr_storage, together with CIDR notation * subnet length. */ bool found_rule = false; for (int i = 0; i < StrList_Len(acl->admit.ips); i++) { if (FuzzySetMatch(StrList_At(acl->admit.ips, i), MapAddress(ipaddr)) == 0 || /* Legacy regex matching, TODO DEPRECATE */ StringMatchFull(StrList_At(acl->admit.ips, i), MapAddress(ipaddr))) { found_rule = true; rule = StrList_At(acl->admit.ips, i); break; } } if (found_rule) { Log(LOG_LEVEL_DEBUG, "access_Check: admit IP: %s", rule); access = true; } } if (!access && acl->admit.keys != NULL) { bool ret = StrList_BinarySearch(acl->admit.keys, key, &pos); if (ret) { rule = acl->admit.keys->list[pos]->str; Log(LOG_LEVEL_DEBUG, "access_Check: admit key: %s", rule); access = true; } } if (!access && acl->admit.hostnames != NULL && hostname != NULL) { size_t hostname_len = strlen(hostname); size_t pos = StrList_SearchForPrefix(acl->admit.hostnames, hostname, hostname_len, false); /* === Legacy regex matching, slow, TODO DEPRECATE === */ bool regex_match = false; if (pos == (size_t) -1) { for (int i = 0; i < StrList_Len(acl->admit.hostnames); i++) { if (StringMatchFull(StrList_At(acl->admit.hostnames, i), hostname)) { pos = i; break; } } } /* === === */ if (pos != (size_t) -1) { rule = acl->admit.hostnames->list[pos]->str; size_t rule_len = acl->admit.hostnames->list[pos]->len; /* The rule in the access list has to be an exact match, or be a * subdomain match (i.e. the rule begins with '.') or a regex. */ if (rule_len == hostname_len || rule[0] == '.' || regex_match) { Log(LOG_LEVEL_DEBUG, "access_Check: admit hostname: %s", rule); access = true; } } } /* If access has been granted, we might need to deny it based on ACL. */ if (access && acl->deny.ips != NULL) { bool found_rule = false; for (int i = 0; i < StrList_Len(acl->deny.ips); i++) { if (FuzzySetMatch(StrList_At(acl->deny.ips, i), MapAddress(ipaddr)) == 0 || /* Legacy regex matching, TODO DEPRECATE */ StringMatchFull(StrList_At(acl->deny.ips, i), MapAddress(ipaddr))) { found_rule = true; rule = acl->deny.ips->list[i]->str; break; } } if (found_rule) { Log(LOG_LEVEL_DEBUG, "access_Check: deny IP: %s", rule); access = false; } } if (access && acl->deny.keys != NULL) { bool ret = StrList_BinarySearch(acl->deny.keys, key, &pos); if (ret) { rule = StrList_At(acl->deny.keys, pos); Log(LOG_LEVEL_DEBUG, "access_Check: deny key: %s", rule); access = false; } } if (access && acl->deny.hostnames != NULL && hostname != NULL) { size_t hostname_len = strlen(hostname); size_t pos = StrList_SearchForPrefix(acl->deny.hostnames, hostname, hostname_len, false); /* === Legacy regex matching, slow, TODO DEPRECATE === */ bool regex_match = false; if (pos == (size_t) -1) { for (int i = 0; i < StrList_Len(acl->deny.hostnames); i++) { if (StringMatchFull(StrList_At(acl->deny.hostnames, i), hostname)) { pos = i; break; } } } /* === === */ if (pos != (size_t) -1) { rule = acl->deny.hostnames->list[pos]->str; size_t rule_len = acl->deny.hostnames->list[pos]->len; /* The rule in the access list has to be an exact match, or be a * subdomain match (i.e. the rule begins with '.') or a regex. */ if (rule_len == hostname_len || rule[0] == '.' || regex_match) { Log(LOG_LEVEL_DEBUG, "access_Check: deny hostname: %s", rule); access = false; } } } return access; }
static void test_StrList_SearchForSuffix() { /* REMINDER: HOSTNAME_STRINGS[] = { "*", ".*", ".", "com", "cfengine.com", ".allowed.cfengine.com", "www.cfengine.com", ".no" }; */ size_t ret, ret2, ret3, ret4, ret5, ret6, ret7, ret8, ret9, ret10, ret11; ret = StrList_SearchForPrefix(HOSTNAME_SL, "cfengine.com", 0, false); ret2 = StrList_SearchForPrefix(HOSTNAME_SL, "google.com", 0, false); ret3 = StrList_SearchForPrefix(HOSTNAME_SL, "yr.no", 0, false); ret4 = StrList_SearchForPrefix(HOSTNAME_SL, "ntua.gr", 0, false); ret5 = StrList_SearchForPrefix(HOSTNAME_SL, "disallowed.cfengine.com", 0, false); ret6 = StrList_SearchForPrefix(HOSTNAME_SL, "allowed.cfengine.com", 0, false); ret7 = StrList_SearchForPrefix(HOSTNAME_SL, "blah.allowed.cfengine.com", 0, false); ret8 = StrList_SearchForPrefix(HOSTNAME_SL, "www.cfengine.com", 0, false); ret9 = StrList_SearchForPrefix(HOSTNAME_SL, "www1.cfengine.com", 0, false); ret10 = StrList_SearchForPrefix(HOSTNAME_SL, "1www.cfengine.com", 0, false); ret11 = StrList_SearchForPrefix(HOSTNAME_SL, "no", 0, false); /* SearchForPrefix() does not guarantee which one of all the matches it * will return if there are many matches. */ /* E.g. the first search might return "cfengine.com" or "com" entry. */ LargestIntegralType set[] = {3, 4}; assert_in_set(ret, set, 2); assert_string_equal(StrList_At(HOSTNAME_SL, ret2), "com"); assert_string_equal(StrList_At(HOSTNAME_SL, ret3), ".no"); assert_int_equal(ret4, (size_t) -1); assert_in_set(ret5, set, 2); assert_in_set(ret6, set, 2); LargestIntegralType set2[] = {3, 4, 5}; assert_in_set(ret7, set2, 3); assert_in_set(ret8, set, 2); assert_in_set(ret9, set, 2); LargestIntegralType set3[] = {3, 4, 6}; assert_in_set(ret10, set3, 3); assert_int_equal(ret11, (size_t) -1); }