Attacker might generate fake NS records pointing to victim's DNS zone.
If the zone contains wildcard the attacker might force us into packet
exchange with a (lame) DNS server on that IP address.
We now limit number of consecuctive failures and kill whole request if
limit is exceeded.
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 {
#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.
}
-/** 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;
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 */
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. */