Commit c2035b1f authored by Marek Vavruša's avatar Marek Vavruša

lib/zonecut: filter private addresses from internet

zonecut should be able to hold these for testing reasons (like private
root or zone cut), but it should filter out data from the internet
a new flag: QUERY_ALLOW_LOCAL allows for being more permissive, and
letting name server query local or private address ranges
parent 5bf7293a
......@@ -147,10 +147,34 @@ static void follow_cname_chain(const knot_dname_t **cname, const knot_rrset_t *r
}
}
/** @internal Filter ANY or loopback addresses. */
static bool is_valid_addr(const uint8_t *addr, size_t len)
{
if (len == sizeof(struct in_addr)) {
/* Filter ANY and 127.0.0.0/8 */
uint32_t ip_host = ntohl(*(const uint32_t *)(addr));
if (ip_host == 0 || (ip_host & 0xff000000) == 0x7f000000) {
return false;
}
} else if (len == sizeof(struct in6_addr)) {
struct in6_addr ip6_mask;
memset(&ip6_mask, 0, sizeof(ip6_mask));
/* All except last byte are zeroed, last byte defines ANY/::1 */
if (memcmp(addr, ip6_mask.s6_addr, sizeof(ip6_mask.s6_addr) - 1) == 0) {
return (addr[len - 1] > 1);
}
}
return true;
}
static int update_nsaddr(const knot_rrset_t *rr, struct kr_query *query)
{
if (rr->type == KNOT_RRTYPE_A || rr->type == KNOT_RRTYPE_AAAA) {
const knot_rdata_t *rdata = rr->rrs.data;
if (!(query->flags & QUERY_ALLOW_LOCAL) &&
!is_valid_addr(knot_rdata_data(rdata), knot_rdata_rdlen(rdata))) {
return KNOT_STATE_CONSUME; /* Ignore invalid addresses */
}
int ret = kr_zonecut_add(&query->zone_cut, rr->owner, rdata);
if (ret != 0) {
return KNOT_STATE_FAIL;
......
......@@ -36,7 +36,8 @@
X(AWAIT_CUT , 1 << 6) /**< Query is waiting for zone cut lookup */ \
X(SAFEMODE , 1 << 7) /**< Don't use fancy stuff (EDNS...) */ \
X(CACHED , 1 << 8) /**< Query response is cached. */ \
X(EXPIRING , 1 << 9) /**< Query response is cached, but expiring. */
X(EXPIRING , 1 << 9) /**< Query response is cached, but expiring. */ \
X(ALLOW_LOCAL, 1 << 10) /**< Allow queries to local or private address ranges. */
/** Query flags */
enum kr_query_flag {
......
......@@ -143,29 +143,7 @@ int kr_zonecut_copy(struct kr_zonecut *dst, const struct kr_zonecut *src)
return map_walk((map_t *)&src->nsset, copy_addr_set, dst);
}
/** @internal Filter ANY or loopback addresses. */
static bool is_valid_addr(uint8_t *addr, size_t len)
{
if (len == sizeof(struct in_addr)) {
/* Filter ANY and 127.0.0.0/8 */
/* temporary workaround for local testing
uint32_t ip_host = ntohl(*(uint32_t *)(addr));
if (ip_host == 0 || (ip_host & 0xff000000) == 0x7f000000) {
return false;
}
*/
} else if (len == sizeof(struct in6_addr)) {
struct in6_addr ip6_mask;
memset(&ip6_mask, 0, sizeof(ip6_mask));
/* All except last byte are zeroed, last byte defines ANY/::1 */
if (memcmp(addr, ip6_mask.s6_addr, sizeof(ip6_mask.s6_addr) - 1) == 0) {
return (addr[len - 1] > 1);
}
}
return true;
}
int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
{
......@@ -186,13 +164,9 @@ int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
if (rdata == NULL) {
return kr_ok();
}
/* Check for invalid */
/* Check for duplicates */
uint16_t rdlen = knot_rdata_rdlen(rdata);
uint8_t *raw_addr = knot_rdata_data(rdata);
if (!is_valid_addr(raw_addr, rdlen)) {
return kr_error(EILSEQ);
}
/* Check for duplicates */
if (pack_obj_find(pack, raw_addr, rdlen)) {
return kr_ok();
}
......@@ -201,7 +175,7 @@ int kr_zonecut_add(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rd
if (ret != 0) {
return kr_error(ENOMEM);
}
return pack_obj_push(pack, knot_rdata_data(rdata), rdlen);
return pack_obj_push(pack, raw_addr, rdlen);
}
int kr_zonecut_del(struct kr_zonecut *cut, const knot_dname_t *ns, const knot_rdata_t *rdata)
......
......@@ -205,6 +205,7 @@ def play_object(path):
os.write(fd, "modules = {'hints'}\n")
os.write(fd, "hints.root({['k.root-servers.net'] = '%s'})\n" % selfaddr)
os.write(fd, "option('NO_MINIMIZE', true)\n")
os.write(fd, "option('ALLOW_LOCAL', true)\n") # Permit queries to localhost
for k,v in config:
# Enable selectively for some tests
if k == 'query-minimization' and str2bool(v):
......
......@@ -35,40 +35,6 @@ static void test_zonecut_params(void **state)
assert_int_not_equal(kr_zonecut_find_cached(NULL, NULL, NULL, NULL, 0), 0);
}
#define TEST_IP(cut, ip, expect) { \
knot_rdata_t rdata[knot_rdata_array_size(sizeof(ip))]; \
knot_rdata_init(rdata, sizeof(ip), (uint8_t *)&ip, 0); \
assert_int_equal(kr_zonecut_add(&(cut), (const uint8_t *)"\x02cz", rdata), (expect)); \
} while (0)
static void test_zonecut_filter(void **state)
{
struct kr_zonecut cut;
kr_zonecut_init(&cut, (const uint8_t *)"", NULL);
/* IPv4 */
uint32_t ip4 = 0; /* 0.0.0.0 */
TEST_IP(cut, ip4, kr_error(EILSEQ));
ip4 = htonl(0x7f000002); /* 127.0.0.2 */
TEST_IP(cut, ip4, kr_error(EILSEQ));
ip4 = htonl(0x7fffffff); /* 127.255.255.255 */
TEST_IP(cut, ip4, kr_error(EILSEQ));
ip4 = htonl(0xff000001); /* 255.0.0.1 */
TEST_IP(cut, ip4, 0);
/* IPv6 */
struct in6_addr ip6;
memset(&ip6, 0, sizeof(ip6)); /* :: */
TEST_IP(cut, ip6.s6_addr, kr_error(EILSEQ));
ip6.s6_addr[15] = 0x01; /* ::1 */
TEST_IP(cut, ip6.s6_addr, kr_error(EILSEQ));
ip6.s6_addr[15] = 0x02; /* ::2 */
TEST_IP(cut, ip6.s6_addr, 0);
kr_zonecut_deinit(&cut);
}
#undef TEST_IP
static void test_zonecut_copy(void **state)
{
const knot_dname_t *root = (const uint8_t *)"";
......@@ -92,7 +58,6 @@ int main(void)
{
const UnitTest tests[] = {
unit_test(test_zonecut_params),
unit_test(test_zonecut_filter),
unit_test(test_zonecut_copy)
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment