From 62001974db6f114da2cde037a87ee8eb250b88da Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marek=20Vavru=C5=A1a?= <marek.vavrusa@nic.cz>
Date: Thu, 22 Oct 2015 15:13:01 +0200
Subject: [PATCH] iterator+rrcache: do not follow CNAME chains (if not
 DNSSEC-secured)

[1] shows an attack using spoofed CNAME targets to replace legitimate
entries in resolver cache by speeding up once-per-TTL attack opportunity

as a defense, the resolver almost always requeries CNAME targets and
doesn't store them in cache. the only exception is when the CNAME target
is within current authority, and the answer is DNSSEC-secured

thanks to Toshinori Maeno (@beyondDNS) for pointing this out [2]

[1]: https://tools.ietf.org/id/draft-weaver-dnsext-comprehensive-
resolver-00.html
[2]: https://moin.qmail.jp/DNS/KnotResolver/CNAMEpatch
---
 lib/layer/iterate.c | 4 ++--
 lib/layer/rrcache.c | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c
index da59fc382..f2d7f36fc 100644
--- a/lib/layer/iterate.c
+++ b/lib/layer/iterate.c
@@ -363,10 +363,10 @@ static int process_answer(knot_pkt_t *pkt, struct kr_request *req)
 		if (state == KNOT_STATE_FAIL) {
 			return state;
 		}
-		/* Follow chain only within current cut. */
+		/* Follow chain only within current cut (if secure). */
 		if (follow_chain) {
 			follow_cname_chain(&cname, rr, query);
-			if (!knot_dname_in(query->zone_cut.name, cname)) {
+			if (!(query->flags & QUERY_DNSSEC_WANT) || !knot_dname_in(query->zone_cut.name, cname)) {
 				follow_chain = false; 
 			}
 		}
diff --git a/lib/layer/rrcache.c b/lib/layer/rrcache.c
index 623f6fe24..dfd2cec41 100644
--- a/lib/layer/rrcache.c
+++ b/lib/layer/rrcache.c
@@ -260,8 +260,8 @@ static int stash_answer(struct kr_query *qry, knot_pkt_t *pkt, map_t *stash, mm_
 			continue;
 		}
 		kr_rrmap_add(stash, rr, KR_RANK_AUTH, pool);
-		/* Follow CNAME chain in current cut. */
-		if (rr->type == KNOT_RRTYPE_CNAME) {
+		/* Follow CNAME chain in current cut (if SECURE). */
+		if ((qry->flags & QUERY_DNSSEC_WANT) && rr->type == KNOT_RRTYPE_CNAME) {
 			const knot_dname_t *next_cname = knot_cname_name(&rr->rrs);
 			if (knot_dname_in(qry->zone_cut.name, next_cname)) {
 				cname = next_cname;
-- 
GitLab