diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua index 853d23d7..3f5cb779 100644 --- a/daemon/lua/kres-gen.lua +++ b/daemon/lua/kres-gen.lua @@ -193,6 +193,8 @@ struct kr_request { int vars_ref; knot_mm_t pool; unsigned int uid; + unsigned int count_no_nsaddr; + unsigned int count_fail_row; }; enum kr_rank {KR_RANK_INITIAL, KR_RANK_OMIT, KR_RANK_TRY, KR_RANK_INDET = 4, KR_RANK_BOGUS, KR_RANK_MISMATCH, KR_RANK_MISSING, KR_RANK_INSECURE, KR_RANK_AUTH = 16, KR_RANK_SECURE = 32}; struct kr_cdb_stats { diff --git a/lib/defines.h b/lib/defines.h index b4ed5b58..be2d3a94 100644 --- a/lib/defines.h +++ b/lib/defines.h @@ -52,6 +52,8 @@ static inline int KR_COLD kr_error(int x) { #define KR_CNAME_CHAIN_LIMIT 13 /* Built-in maximum CNAME chain length */ #define KR_TIMEOUT_LIMIT 4 /* Maximum number of retries after timeout. */ #define KR_QUERY_NSRETRY_LIMIT 4 /* Maximum number of retries per query. */ +#define KR_COUNT_NO_NSADDR_LIMIT 5 +#define KR_CONSUME_FAIL_ROW_LIMIT 3 /* Maximum number of KR_STATE_FAIL in a row. */ /* * Defines. diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c index 3d2a93cb..780e9d56 100644 --- a/lib/layer/iterate.c +++ b/lib/layer/iterate.c @@ -869,7 +869,8 @@ static int process_stub(knot_pkt_t *pkt, struct kr_request *req) } -/** Error handling, RFC1034 5.3.3, 4d. */ +/** Error handling, RFC1034 5.3.3, 4d. + * NOTE: returing this does not prevent further queries (by itself). */ static int resolve_error(knot_pkt_t *pkt, struct kr_request *req) { return KR_STATE_FAIL; diff --git a/lib/resolve.c b/lib/resolve.c index fd67a3a6..3d7aacd6 100644 --- a/lib/resolve.c +++ b/lib/resolve.c @@ -299,10 +299,10 @@ static int ns_fetch_cut(struct kr_query *qry, const knot_dname_t *requested_name return KR_STATE_PRODUCE; } -static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param) +static int ns_resolve_addr(struct kr_query *qry, struct kr_request *req) { - struct kr_rplan *rplan = ¶m->rplan; - struct kr_context *ctx = param->ctx; + struct kr_rplan *rplan = &req->rplan; + struct kr_context *ctx = req->ctx; /* Start NS queries from root, to avoid certain cases @@ -333,7 +333,9 @@ static int ns_resolve_addr(struct kr_query *qry, struct kr_request *param) return kr_error(EAGAIN); } /* No IPv4 nor IPv6, flag server as unusable. */ - VERBOSE_MSG(qry, "=> unresolvable NS address, bailing out\n"); + ++req->count_no_nsaddr; + VERBOSE_MSG(qry, "=> unresolvable NS address, bailing out (counter: %u)\n", + req->count_no_nsaddr); qry->ns.reputation |= KR_NS_NOIP4 | KR_NS_NOIP6; kr_nsrep_update_rep(&qry->ns, qry->ns.reputation, ctx->cache_rep); invalidate_ns(rplan, qry); @@ -937,6 +939,23 @@ int kr_resolve_consume(struct kr_request *request, const struct sockaddr *src, k qry->flags.RESOLVED = false; } + /* For multiple errors in a row; invalidate_ns() is not enough. */ + if (!qry->flags.CACHED) { + if (request->state & KR_STATE_FAIL) { + if (++request->count_fail_row > KR_CONSUME_FAIL_ROW_LIMIT) { + if (VERBOSE_STATUS || kr_log_rtrace_enabled(request)) { + kr_log_req(request, 0, 2, "resl", + "=> too many failures in a row, " + "bail out (mitigation for NXNSAttack " + "CVE-2020-12667)"); + } + return KR_STATE_FAIL; + } + } else { + request->count_fail_row = 0; + } + } + /* Pop query if resolved. */ if (request->state == KR_STATE_YIELD) { return KR_STATE_PRODUCE; /* Requery */ @@ -1396,6 +1415,11 @@ int kr_resolve_produce(struct kr_request *request, struct sockaddr **dst, int *t ns_election: + if (unlikely(request->count_no_nsaddr >= KR_COUNT_NO_NSADDR_LIMIT)) { + VERBOSE_MSG(qry, "=> too many unresolvable NSs, bail out " + "(mitigation for NXNSAttack CVE-2020-12667)\n"); + return KR_STATE_FAIL; + } /* If the query has already selected a NS and is waiting for IPv4/IPv6 record, * elect best address only, otherwise elect a completely new NS. */ diff --git a/lib/resolve.h b/lib/resolve.h index 1b339ff3..3a55fefe 100644 --- a/lib/resolve.h +++ b/lib/resolve.h @@ -227,6 +227,8 @@ struct kr_request { int vars_ref; /**< Reference to per-request variable table. LUA_NOREF if not set. */ knot_mm_t pool; unsigned int uid; /** for logging purposes only */ + unsigned int count_no_nsaddr; + unsigned int count_fail_row; }; /** Initializer for an array of *_selected. */