From 1f98f8dc18dc86a4e2dec3f2f8f2aab5086958f9 Mon Sep 17 00:00:00 2001 From: Daniel Salzman Date: Thu, 4 Mar 2021 19:03:42 +0100 Subject: [PATCH] nameserver: decrease maximum CNAME/DNAME chain length from 20 to 5 The reason for this change is to reduce possible amplification attacks and other negative effects of long processing loops. Also most resolvers don't accept multiple jumps without requerying, so this change should be harmless. --- src/knot/nameserver/internet.h | 4 +-- tests-extra/data/flags.zone | 6 +++- tests-extra/tests/basic/cname-follow/test.py | 2 +- tests-extra/tests/basic/query/test.py | 31 +++++++++++++++----- 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/src/knot/nameserver/internet.h b/src/knot/nameserver/internet.h index 420256746..ea6d93a45 100644 --- a/src/knot/nameserver/internet.h +++ b/src/knot/nameserver/internet.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 CZ.NIC, z.s.p.o. +/* Copyright (C) 2021 CZ.NIC, z.s.p.o. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -21,7 +21,7 @@ #include "knot/nameserver/process_query.h" /*! \brief Don't follow CNAME/DNAME chain beyond this depth. */ -#define CNAME_CHAIN_MAX 20 +#define CNAME_CHAIN_MAX 5 /*! * \brief Answer query from an IN class zone. diff --git a/tests-extra/data/flags.zone b/tests-extra/data/flags.zone index 0014ccf88..a24380318 100644 --- a/tests-extra/data/flags.zone +++ b/tests-extra/data/flags.zone @@ -70,7 +70,11 @@ d.dname-tree CNAME cname-wildcard e.dname-tree CNAME e.dname f.dname-tree DNAME f.f.dname-tree -dname-loop DNAME loop.dname-loop +dname-outloop DNAME loop.dname-outloop + +loop.dname-inloop A 1.2.3.4 +loop.dname-inloop DNAME dname-inloop + dname-out DNAME outside.zone. dname-dangl DNAME dangling dname.below.sub DNAME dname-tree diff --git a/tests-extra/tests/basic/cname-follow/test.py b/tests-extra/tests/basic/cname-follow/test.py index 04c6b0ad3..68f7a927c 100644 --- a/tests-extra/tests/basic/cname-follow/test.py +++ b/tests-extra/tests/basic/cname-follow/test.py @@ -54,7 +54,7 @@ resp.check_count(3, rtype="CNAME") resp = knot.dig("chain.follow", "AAAA", udp=False) resp.check(rcode="NOERROR") -resp.check_count(20, rtype="CNAME") +resp.check_count(5, rtype="CNAME") resp.check_count(0, rtype="AAAA") # query for RRSIG diff --git a/tests-extra/tests/basic/query/test.py b/tests-extra/tests/basic/query/test.py index 92fa8c191..04a6bb91b 100644 --- a/tests-extra/tests/basic/query/test.py +++ b/tests-extra/tests/basic/query/test.py @@ -131,7 +131,7 @@ resp.cmp(bind) # Long CNAME loop (Bind truncates the loop at 17 records) resp = knot.dig("ab.flags", "A", udp=True) resp.check(rcode="NOERROR") -compare(resp.count(rtype="CNAME", section="answer"), 19, "Count of CNAME records in loop.") +compare(resp.count(rtype="CNAME", section="answer"), 5, "Count of CNAME records in loop.") ''' CNAME in MX EXCHANGE. ''' @@ -212,15 +212,32 @@ resp.check_record(name="dname.flags.", rtype="DNAME", ttl=3600, rdata=" resp.check_record(name="x.f.dname.flags.", rtype="CNAME", ttl=3600, rdata="x.f.dname-tree.flags.") resp.check_record(name="f.dname-tree.flags.", rtype="DNAME", ttl=3600, rdata="f.f.dname-tree.flags.") resp.check_record(name="x.f.dname-tree.flags.", rtype="CNAME", ttl=3600, rdata="x.f.f.dname-tree.flags.") -resp.check_counts(22, 0, 0) -# resp.cmp(bind) BIND responds partially unrolled CNAME loop +resp.check_counts(7, 0, 0) +# resp.cmp(bind) BIND responds deeply unrolled CNAME loop # Infinite DNAME loop -resp = knot.dig("end.dname-loop.flags", "A", udp=False) +resp = knot.dig("end.dname-outloop.flags", "A", udp=True) resp.check(rcode="NOERROR") -resp.check_record(name="dname-loop.flags.", rtype="DNAME", ttl=3600, rdata="loop.dname-loop.flags.") -compare(resp.count(rtype="CNAME", section="answer"), 20, "Count of synthesized CNAME records in loop.") -resp.check_record(name="end.loop.loop.loop.dname-loop.flags.", rtype="CNAME", ttl=3600, rdata="end.loop.loop.loop.loop.dname-loop.flags.") +resp.check_record(name="dname-outloop.flags.", rtype="DNAME", ttl=3600, rdata="loop.dname-outloop.flags.") +compare(resp.count(rtype="CNAME", section="answer"), 5, "Count of synthesized CNAME records in loop.") +resp.check_record(name="end.loop.loop.loop.loop.dname-outloop.flags.", rtype="CNAME", ttl=3600, + rdata="end.loop.loop.loop.loop.loop.dname-outloop.flags.") +resp.check_counts(6, 0, 0) + +# Recursive DNAME loop - full +resp = knot.dig("loop.loop.loop.loop.loop.loop.dname-inloop.flags", "A", udp=True) +resp.check(rcode="NOERROR") +resp.check_record(name="loop.dname-inloop.flags.", rtype="DNAME", ttl=3600, rdata="dname-inloop.flags.") +compare(resp.count(rtype="CNAME", section="answer"), 5, "Count of synthesized CNAME records in loop.") +resp.check_record(name="loop.dname-inloop.flags.", rtype="A", ttl=3600, rdata="1.2.3.4") +resp.check_counts(7, 0, 0) + +# Recursive DNAME loop - incomplete +resp = knot.dig("loop.loop.loop.loop.loop.loop.loop.dname-inloop.flags", "A", udp=True) +resp.check(rcode="NOERROR") +resp.check_record(name="loop.dname-inloop.flags.", rtype="DNAME", ttl=3600, rdata="dname-inloop.flags.") +compare(resp.count(rtype="CNAME", section="answer"), 5, "Count of synthesized CNAME records in loop.") +resp.check_counts(6, 0, 0) ''' Wildcard answers. '''