Commit bef03dcf authored by Vladimír Čunát's avatar Vladimír Čunát Committed by Petr Špaček

lib/cache: fix CVE-2019-10191

Don't stash a packet with mismatching QNAME+QTYPE.
When receiving an NXDOMAIN or NODATA packet in an insecure zone,
it would get cached with KR_RANK_INSECURE regardless of mismatch
in QNAME.  If the 0x20 pattern was preserved in the fake QNAME,
such packet would then be used to answer queries with matching QNAME,
even if there's no proof that this QNAME is insecure.
parent a6ecb58f
Knot Resolver 4.x.y (2019-0m-dd)
================================
Security
--------
- fix CVE-2019-10191: caching negative answer with forged QNAME+QTYPE
Improvements
------------
- DNS-over-HTTPS: answers include `access-control-allow-origin: *` (!823)
......
......@@ -124,6 +124,7 @@ struct kr_qflags {
_Bool DNS64_MARK : 1;
_Bool CACHE_TRIED : 1;
_Bool NO_NS_FOUND : 1;
_Bool PKT_IS_SANE : 1;
};
typedef struct {
knot_rrset_t **at;
......
......@@ -414,7 +414,7 @@ int cache_stash(kr_layer_t *ctx, knot_pkt_t *pkt)
/* LATER(optim.): typically we also have corresponding NS record in the list,
* so we might save a cache operation. */
if (check_dname_for_lf(knot_pkt_qname(pkt), qry)) {
if (qry->flags.PKT_IS_SANE && check_dname_for_lf(knot_pkt_qname(pkt), qry)) {
stash_pkt(pkt, qry, req, needs_pkt);
}
......
......@@ -82,6 +82,8 @@ static bool is_paired_to_query(const knot_pkt_t *answer, struct kr_query *query)
uint16_t qtype = query->stype;
const knot_dname_t *qname = minimized_qname(query, &qtype);
/* ID should already match, thanks to session_tasklist_del_msgid()
* in worker_submit(), but it won't hurt to check again. */
return query->id == knot_wire_get_id(answer->wire) &&
knot_wire_get_qdcount(answer->wire) > 0 &&
query->sclass == knot_pkt_qclass(answer) &&
......@@ -1015,6 +1017,7 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
if (!query) {
return ctx->state;
}
query->flags.PKT_IS_SANE = false;
WITH_VERBOSE(query) {
if (query->flags.TRACE) {
......@@ -1058,6 +1061,10 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt)
return KR_STATE_CONSUME;
}
/* If exiting above here, there's no sense to put it into packet cache.
* The most important part is to check for spoofing: is_paired_to_query() */
query->flags.PKT_IS_SANE = true;
#ifndef NOVERBOSELOG
const knot_lookup_t *rcode = knot_lookup_by_id(knot_rcode_names, knot_wire_get_rcode(pkt->wire));
#endif
......
......@@ -64,6 +64,8 @@ struct kr_qflags {
bool DNS64_MARK : 1; /**< Internal mark for dns64 module. */
bool CACHE_TRIED : 1; /**< Internal to cache module. */
bool NO_NS_FOUND : 1; /**< No valid NS found during last PRODUCE stage. */
bool PKT_IS_SANE : 1; /**< Set by iterator in consume phase to indicate whether
* some basic aspects of the packet are OK, e.g. QNAME. */
};
/** Combine flags together. This means set union for simple flags. */
......
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