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.
This commit is contained in:
Daniel Salzman 2021-03-04 19:03:42 +01:00
parent 06a319cab0
commit 1f98f8dc18
4 changed files with 32 additions and 11 deletions

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2019 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
/* Copyright (C) 2021 CZ.NIC, z.s.p.o. <knot-dns@labs.nic.cz>
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.

View file

@ -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

View file

@ -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

View file

@ -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. '''