int pr_netaddr_fnmatch(pr_netaddr_t *na, const char *pattern, int flags) { /* Note: I'm still not sure why proftpd bundles an fnmatch(3) * implementation rather than using the system library's implementation. * Needs looking into. * * The FNM_CASEFOLD flag is a GNU extension; perhaps the bundled * implementation was added to make that flag available on other platforms. */ int match_flags = PR_FNM_NOESCAPE|PR_FNM_CASEFOLD; if (!na || !pattern) { errno = EINVAL; return -1; } if (flags & PR_NETADDR_MATCH_DNS) { const char *dnsstr = pr_netaddr_get_dnsstr(na); if (pr_fnmatch(pattern, dnsstr, match_flags) == 0) { pr_trace_msg(trace_channel, 6, "DNS name '%s' matches pattern '%s'", dnsstr, pattern); return TRUE; } } if (flags & PR_NETADDR_MATCH_IP) { const char *ipstr = pr_netaddr_get_ipstr(na); if (pr_fnmatch(pattern, ipstr, match_flags) == 0) { pr_trace_msg(trace_channel, 6, "DNS name '%s' matches pattern '%s'", ipstr, pattern); return TRUE; } } pr_trace_msg(trace_channel, 4, "addr %s does not match pattern '%s'", pr_netaddr_get_ipstr(na), pattern); return FALSE; }
END_TEST START_TEST (netacl_match_test) { pr_netacl_t *acl; pr_netaddr_t *addr; char *acl_str; int have_localdomain = FALSE, res; res = pr_netacl_match(NULL, NULL); fail_unless(res == -2, "Failed to handle NULL arguments"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); acl_str = "all"; acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, NULL); fail_unless(res == -2, "Failed to handle NULL addr"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); addr = pr_netaddr_get_addr(p, "localhost", NULL); fail_unless(addr != NULL, "Failed to get addr for '%s': %s", "localhost", strerror(errno)); /* It's possible that the DNS name for 'localhost' that is used will * actually be 'localhost.localdomain', depending on the contents of * the host's /etc/hosts file. */ if (strcmp(pr_netaddr_get_dnsstr(addr), "localhost.localdomain") == 0) { have_localdomain = TRUE; } res = pr_netacl_match(NULL, addr); fail_unless(res == -2, "Failed to handle NULL ACL"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); res = pr_netacl_match(acl, addr); fail_unless(res == 1, "Failed to positively match ACL to addr: %s", strerror(errno)); acl_str = "none"; acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == -1, "Failed to negatively match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "127.0.0.1"); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == 1, "Failed to positively match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "!127.0.0.1"); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == -1, "Failed to negatively match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "192.168.0.1"); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == 0, "Failed to match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "!192.168.0.1"); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == 1, "Failed to positively match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "127.0.0.0/24"); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == 1, "Failed to positively match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "!127.0.0.0/24"); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == -1, "Failed to negatively match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "127.0.0."); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == 1, "Failed to positively match ACL to addr: %s", strerror(errno)); acl_str = pstrdup(p, "!127.0.0."); acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == -1, "Failed to negatively match ACL to addr: %s", strerror(errno)); if (!have_localdomain) { acl_str = pstrdup(p, "localhost"); } else { acl_str = pstrdup(p, "localhost.localdomain"); } acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == 1, "Failed to positively match ACL to addr: %s", strerror(errno)); if (!have_localdomain) { acl_str = pstrdup(p, "!localhost"); } else { acl_str = pstrdup(p, "!localhost.localdomain"); } acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == -1, "Failed to negatively match ACL to addr: %s", strerror(errno)); if (!have_localdomain) { acl_str = pstrdup(p, "loc*st"); } else { acl_str = pstrdup(p, "loc*st.loc*in"); } acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == 1, "Failed to positively match ACL to addr: %s", strerror(errno)); if (!have_localdomain) { acl_str = pstrdup(p, "!loc*st"); } else { acl_str = pstrdup(p, "!loc*st.loc*in"); } acl = pr_netacl_create(p, acl_str); fail_unless(acl != NULL, "Failed to handle ACL string '%s': %s", acl_str, strerror(errno)); res = pr_netacl_match(acl, addr); fail_unless(res == -1, "Failed to negatively match ACL to addr: %s", strerror(errno)); }
pr_class_t *pr_class_match_addr(pr_netaddr_t *addr) { pr_class_t *cls; pool *tmp_pool; if (!addr) { errno = EINVAL; return NULL; } tmp_pool = make_sub_pool(permanent_pool); for (cls = class_list; cls; cls = cls->cls_next) { array_header *acl_list = cls->cls_acls; pr_netacl_t **acls = acl_list->elts; register int i; int next_class = FALSE; /* For each ACL rule in this class, compare the rule against the given * address. The address matches the given class depending on the * Satisfy setting: if "any", the class matches if any rule matches; * if "all", the class matches only if _all_ rules match. */ for (i = 0; i < acl_list->nelts; i++) { int res; if (next_class) break; if (acls[i] == NULL) continue; switch (cls->cls_satisfy) { case PR_CLASS_SATISFY_ANY: pr_trace_msg(trace_channel, 6, "checking addr '%s' (%s) against class '%s' rule: %s " "(requires any ACL matching)", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), cls->cls_name, pr_netacl_get_str(tmp_pool, acls[i])); res = pr_netacl_match(acls[i], addr); if (res == 1) { destroy_pool(tmp_pool); return cls; } break; case PR_CLASS_SATISFY_ALL: pr_trace_msg(trace_channel, 6, "checking addr '%s' (%s) against class '%s' ACL: %s " "(requires all ACLs matching)", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), cls->cls_name, pr_netacl_get_str(tmp_pool, acls[i])); res = pr_netacl_match(acls[i], addr); if (res <= 0) next_class = TRUE; break; } } /* If this is a "Satisfy all" class, and all rules have matched * (positively or negatively), then it matches the address. */ if (next_class == FALSE && cls->cls_satisfy == PR_CLASS_SATISFY_ALL && i == acl_list->nelts) { destroy_pool(tmp_pool); return cls; } } destroy_pool(tmp_pool); errno = ENOENT; return NULL; }
END_TEST #ifdef PR_USE_IPV6 START_TEST (netaddr_get_dnsstr_ipv6_test) { pr_netaddr_t *addr; const char *ip, *res; ip = "::1"; res = pr_netaddr_get_dnsstr(NULL); fail_unless(res == NULL, "Failed to handle null argument"); fail_unless(errno == EINVAL, "Failed to set errno to EINVAL"); addr = pr_netaddr_get_addr(p, ip, NULL); fail_unless(addr != NULL, "Failed to get addr for '%s': %s", ip, strerror(errno)); pr_netaddr_set_reverse_dns(FALSE); res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); fail_unless(strcmp(res, ip) == 0, "Expected '%s', got '%s'", ip, res); pr_netaddr_set_reverse_dns(TRUE); /* Even though we should expect a DNS name, not an IP address, the * previous call to pr_netaddr_get_dnsstr() cached the IP address. */ res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); fail_unless(strcmp(res, ip) == 0, "Expected '%s', got '%s'", ip, res); pr_netaddr_clear(addr); /* Clearing the address doesn't work, since that removes even the address * info, in addition to the cached strings. */ res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); fail_unless(strcmp(res, "") == 0, "Expected '%s', got '%s'", "", res); /* We need to clear the netaddr internal cache as well. */ pr_netaddr_clear_ipcache(ip); addr = pr_netaddr_get_addr(p, ip, NULL); fail_unless(addr != NULL, "Failed to get addr for '%s': %s", ip, strerror(errno)); mark_point(); fail_unless(addr->na_have_dnsstr == 0, "addr already has cached DNS str"); mark_point(); res = pr_netaddr_get_dnsstr(addr); fail_unless(res != NULL, "Failed to get DNS str for addr: %s", strerror(errno)); mark_point(); /* Depending on the contents of /etc/hosts, resolving ::1 could * return either "localhost" or "localhost.localdomain". Perhaps even * other variations, although these should be the most common. */ fail_unless(strcmp(res, "localhost") == 0 || strcmp(res, "localhost.localdomain") == 0 || strcmp(res, "localhost6") == 0 || strcmp(res, "localhost6.localdomain") == 0 || strcmp(res, "ip6-localhost") == 0 || strcmp(res, "ip6-loopback") == 0 || strcmp(res, ip) == 0, "Expected '%s', got '%s'", "localhost, localhost.localdomain et al", res); }
/* Returns 1 if there was a positive match, -1 if there was a negative * match, -2 if there was an error, and zero if there was no match at all. */ int pr_netacl_match(const pr_netacl_t *acl, const pr_netaddr_t *addr) { pool *tmp_pool; if (acl == NULL || addr == NULL) { errno = EINVAL; return -2; } tmp_pool = make_sub_pool(permanent_pool); switch (acl->type) { case PR_NETACL_TYPE_ALL: pr_trace_msg(trace_channel, 10, "addr '%s' matched rule 'ALL' ('%s')", pr_netaddr_get_ipstr(addr), pr_netacl_get_str(tmp_pool, acl)); destroy_pool(tmp_pool); return 1; case PR_NETACL_TYPE_NONE: pr_trace_msg(trace_channel, 10, "addr '%s' matched rule 'NONE'", pr_netaddr_get_ipstr(addr)); destroy_pool(tmp_pool); return -1; case PR_NETACL_TYPE_IPMASK: pr_trace_msg(trace_channel, 10, "checking addr '%s' against IP mask rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); if (pr_netaddr_ncmp(addr, acl->addr, acl->masklen) == 0) { pr_trace_msg(trace_channel, 10, "addr '%s' matched IP mask rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_IPMATCH: pr_trace_msg(trace_channel, 10, "checking addr '%s' against IP address rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); if (pr_netaddr_cmp(addr, acl->addr) == 0) { pr_trace_msg(trace_channel, 10, "addr '%s' matched IP address rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_DNSMATCH: pr_trace_msg(trace_channel, 10, "checking addr '%s' against DNS name rule '%s'", pr_netaddr_get_dnsstr(addr), acl->pattern); if (strcmp(pr_netaddr_get_dnsstr(addr), acl->pattern) == 0) { pr_trace_msg(trace_channel, 10, "addr '%s' (%s) matched DNS name rule '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_IPGLOB: pr_trace_msg(trace_channel, 10, "checking addr '%s' against IP glob rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); if (pr_netaddr_fnmatch(addr, acl->pattern, PR_NETADDR_MATCH_IP) == TRUE) { pr_trace_msg(trace_channel, 10, "addr '%s' matched IP glob rule '%s'", pr_netaddr_get_ipstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } break; case PR_NETACL_TYPE_DNSGLOB: if (ServerUseReverseDNS) { pr_trace_msg(trace_channel, 10, "checking addr '%s' against DNS glob rule '%s'", pr_netaddr_get_dnsstr(addr), acl->pattern); if (pr_netaddr_fnmatch(addr, acl->pattern, PR_NETADDR_MATCH_DNS) == TRUE) { pr_trace_msg(trace_channel, 10, "addr '%s' (%s) matched DNS glob rule '%s'", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), acl->aclstr); destroy_pool(tmp_pool); if (acl->negated) return -1; return 1; } else { if (acl->negated) { destroy_pool(tmp_pool); return 1; } } } else { pr_trace_msg(trace_channel, 10, "skipping comparing addr '%s' (%s) against DNS glob rule '%s' " "because UseReverseDNS is off", pr_netaddr_get_ipstr(addr), pr_netaddr_get_dnsstr(addr), acl->aclstr); } break; } destroy_pool(tmp_pool); return 0; }