Version in base suite: 5.6.0-1 Base version: knot-resolver_5.6.0-1 Target version: knot-resolver_5.6.0-1+deb12u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/k/knot-resolver/knot-resolver_5.6.0-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/k/knot-resolver/knot-resolver_5.6.0-1+deb12u1.dsc changelog | 8 gbp.conf | 2 patches/0001-validator-lower-the-NSEC3-iteration-limit-150-50.patch | 32 + patches/0002-validator-similarly-also-limit-excessive-NSEC3-salt-.patch | 143 ++++++ patches/0003-lib-cache-limit-the-amount-of-work-on-SHA1.patch | 60 ++ patches/0004-validator-limit-the-amount-of-work-on-SHA1-in-NSEC3-.patch | 31 + patches/0005-validator-refuse-to-validate-answers-with-more-than-.patch | 37 + patches/0006-validator-compatibility-with-older-libknot-versions.patch | 22 patches/0007-lib-cache-bump-CACHE_VERSION.patch | 25 + patches/0008-lib-dnssec-kr_rrset_validate_with_key-deduplicate-cl.patch | 43 + patches/0009-lib-resolve-kr_request_set_extended_error-tweak-prio.patch | 25 + patches/0010-mitigate-KeyTrap-DoS-CVE-2023-50387.patch | 233 ++++++++++ patches/0011-mitigate-KeyTrap-DoS-CVE-2023-50387.patch | 80 +++ patches/0012-lib-dnssec-fix-imprecise-assertion.patch | 22 patches/0013-daemon-more-avoidance-of-excessive-TCP-reconnections.patch | 153 ++++++ patches/series | 13 salsa-ci.yml | 3 17 files changed, 931 insertions(+), 1 deletion(-) diff -Nru knot-resolver-5.6.0/debian/changelog knot-resolver-5.6.0/debian/changelog --- knot-resolver-5.6.0/debian/changelog 2023-01-26 20:41:02.000000000 +0000 +++ knot-resolver-5.6.0/debian/changelog 2024-02-23 12:16:49.000000000 +0000 @@ -1,3 +1,11 @@ +knot-resolver (5.6.0-1+deb12u1) bookworm-security; urgency=medium + + * Backport upstream fixes for CVE-2023-50868 "NSEC3" + * Backport upstream fixes for CVE-2023-50387 "KeyTrap" + * Backport upstream fix for CVE-2023-46317 + + -- Jakub Ružička Fri, 23 Feb 2024 13:16:49 +0100 + knot-resolver (5.6.0-1) unstable; urgency=medium * New upstream release diff -Nru knot-resolver-5.6.0/debian/gbp.conf knot-resolver-5.6.0/debian/gbp.conf --- knot-resolver-5.6.0/debian/gbp.conf 2023-01-26 20:24:03.000000000 +0000 +++ knot-resolver-5.6.0/debian/gbp.conf 2024-02-22 16:34:42.000000000 +0000 @@ -1,5 +1,5 @@ [DEFAULT] -debian-branch = debian/master +debian-branch = debian/bookworm debian-tag = debian/%(version)s upstream-branch = upstream upstream-tag = upstream/%(version)s diff -Nru knot-resolver-5.6.0/debian/patches/0001-validator-lower-the-NSEC3-iteration-limit-150-50.patch knot-resolver-5.6.0/debian/patches/0001-validator-lower-the-NSEC3-iteration-limit-150-50.patch --- knot-resolver-5.6.0/debian/patches/0001-validator-lower-the-NSEC3-iteration-limit-150-50.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0001-validator-lower-the-NSEC3-iteration-limit-150-50.patch 2024-02-23 12:14:42.000000000 +0000 @@ -0,0 +1,32 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Tue, 2 Jan 2024 10:05:28 +0100 +Subject: validator: lower the NSEC3 iteration limit (150 -> 50) + +Also done by BIND9 >= 9.19.19: +https://gitlab.isc.org/isc-projects/bind9/-/merge_requests/8515 + +The latest real-life measurements show that values above 50 are rare: +https://chat.dns-oarc.net/community/pl/aadp9wwrp7g7ux1b8chbzebmze +--- + lib/dnssec/nsec3.h | 7 ++----- + 1 file changed, 2 insertions(+), 5 deletions(-) + +diff --git a/lib/dnssec/nsec3.h b/lib/dnssec/nsec3.h +index eb0bd39..723dc4a 100644 +--- a/lib/dnssec/nsec3.h ++++ b/lib/dnssec/nsec3.h +@@ -11,12 +11,9 @@ + * ...so we avoid doing all the work. The value is a current compromise; + * zones shooting over get downgraded to insecure status. + * +- * Original restriction wasn't that strict: +- https://datatracker.ietf.org/doc/html/rfc5155#section-10.3 +- * but there is discussion about officially lowering the limits: +- https://tools.ietf.org/id/draft-hardaker-dnsop-nsec3-guidance-02.html#section-2.3 ++ https://datatracker.ietf.org/doc/html/rfc9276#name-recommendation-for-validati + */ +-#define KR_NSEC3_MAX_ITERATIONS 150 ++#define KR_NSEC3_MAX_ITERATIONS 50 + + /** + * Name error response check (RFC5155 7.2.2). diff -Nru knot-resolver-5.6.0/debian/patches/0002-validator-similarly-also-limit-excessive-NSEC3-salt-.patch knot-resolver-5.6.0/debian/patches/0002-validator-similarly-also-limit-excessive-NSEC3-salt-.patch --- knot-resolver-5.6.0/debian/patches/0002-validator-similarly-also-limit-excessive-NSEC3-salt-.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0002-validator-similarly-also-limit-excessive-NSEC3-salt-.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,143 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Tue, 2 Jan 2024 11:18:31 +0100 +Subject: validator: similarly also limit excessive NSEC3 salt length + +Limit combination of iterations and salt length, based on estimated +expense of the computation. Note that the result only differs for +salt length > 44 which is rather nonsensical and very rare: +https://chat.dns-oarc.net/community/pl/h58qx9sjkbgt9dajb7x988p78a +--- + lib/cache/api.c | 2 +- + lib/cache/nsec3.c | 2 +- + lib/dnssec/nsec3.c | 4 ++-- + lib/dnssec/nsec3.h | 32 ++++++++++++++++++++++++++++---- + lib/layer/validate.c | 7 ++++--- + 5 files changed, 36 insertions(+), 11 deletions(-) + +diff --git a/lib/cache/api.c b/lib/cache/api.c +index 116d775..bb627ea 100644 +--- a/lib/cache/api.c ++++ b/lib/cache/api.c +@@ -500,7 +500,7 @@ static ssize_t stash_rrset(struct kr_cache *cache, const struct kr_query *qry, + return kr_ok(); + } + if (rr->type == KNOT_RRTYPE_NSEC3 && rr->rrs.count +- && knot_nsec3_iters(rr->rrs.rdata) > KR_NSEC3_MAX_ITERATIONS) { ++ && kr_nsec3_limited_rdata(rr->rrs.rdata)) { + /* This shouldn't happen often, thanks to downgrades during validation. */ + VERBOSE_MSG(qry, "=> skipping NSEC3 with too many iterations\n"); + return kr_ok(); +diff --git a/lib/cache/nsec3.c b/lib/cache/nsec3.c +index 0b70775..9832630 100644 +--- a/lib/cache/nsec3.c ++++ b/lib/cache/nsec3.c +@@ -84,7 +84,7 @@ static knot_db_val_t key_NSEC3_name(struct key *k, const knot_dname_t *name, + .data = (uint8_t *)/*const-cast*/name, + }; + +- if (kr_fails_assert(nsec_p->libknot.iterations <= KR_NSEC3_MAX_ITERATIONS)) { ++ if (kr_fails_assert(!kr_nsec3_limited_params(&nsec_p->libknot))) { + /* This is mainly defensive; it shouldn't happen thanks to downgrades. */ + return VAL_EMPTY; + } +diff --git a/lib/dnssec/nsec3.c b/lib/dnssec/nsec3.c +index 037d5bd..e4d314b 100644 +--- a/lib/dnssec/nsec3.c ++++ b/lib/dnssec/nsec3.c +@@ -71,7 +71,7 @@ static int hash_name(dnssec_binary_t *hash, const dnssec_nsec3_params_t *params, + return kr_error(EINVAL); + if (!name) + return kr_error(EINVAL); +- if (kr_fails_assert(params->iterations <= KR_NSEC3_MAX_ITERATIONS)) { ++ if (kr_fails_assert(!kr_nsec3_limited_params(params))) { + /* This if is mainly defensive; it shouldn't happen. */ + return kr_error(EINVAL); + } +@@ -565,7 +565,7 @@ int kr_nsec3_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_ + const knot_rrset_t *rrset = knot_pkt_rr(sec, i); + if (rrset->type != KNOT_RRTYPE_NSEC3) + continue; +- if (knot_nsec3_iters(rrset->rrs.rdata) > KR_NSEC3_MAX_ITERATIONS) { ++ if (kr_nsec3_limited_rdata(rrset->rrs.rdata)) { + /* Avoid hashing with too many iterations. + * If we get here, the `sname` wildcard probably ends up bogus, + * but it gets downgraded to KR_RANK_INSECURE when validator +diff --git a/lib/dnssec/nsec3.h b/lib/dnssec/nsec3.h +index 723dc4a..76ef2e9 100644 +--- a/lib/dnssec/nsec3.h ++++ b/lib/dnssec/nsec3.h +@@ -5,15 +5,39 @@ + #pragma once + + #include ++#include ++#include ++ ++ ++static inline unsigned int kr_nsec3_price(unsigned int iterations, unsigned int salt_len) ++{ ++ // SHA1 works on 64-byte chunks. ++ // On iterating we hash the salt + 20 bytes of the previous hash. ++ int chunks_per_iter = (20 + salt_len - 1) / 64 + 1; ++ return (iterations + 1) * chunks_per_iter; ++} + + /** High numbers in NSEC3 iterations don't really help security + * +- * ...so we avoid doing all the work. The value is a current compromise; +- * zones shooting over get downgraded to insecure status. ++ * ...so we avoid doing all the work. The limit is a current compromise; ++ * answers using NSEC3 over kr_nsec3_limited* get downgraded to insecure status. + * + https://datatracker.ietf.org/doc/html/rfc9276#name-recommendation-for-validati + */ +-#define KR_NSEC3_MAX_ITERATIONS 50 ++static inline bool kr_nsec3_limited(unsigned int iterations, unsigned int salt_len) ++{ ++ const int MAX_ITERATIONS = 50; // limit with short salt length ++ return kr_nsec3_price(iterations, salt_len) > MAX_ITERATIONS + 1; ++} ++static inline bool kr_nsec3_limited_rdata(const knot_rdata_t *rd) ++{ ++ return kr_nsec3_limited(knot_nsec3_iters(rd), knot_nsec3_salt_len(rd)); ++} ++static inline bool kr_nsec3_limited_params(const dnssec_nsec3_params_t *params) ++{ ++ return kr_nsec3_limited(params->iterations, params->salt.size); ++} ++ + + /** + * Name error response check (RFC5155 7.2.2). +@@ -36,7 +60,7 @@ int kr_nsec3_name_error_response_check(const knot_pkt_t *pkt, knot_section_t sec + * KNOT_ERANGE - NSEC3 RR that covers a wildcard + * has been found, but has opt-out flag set; + * otherwise - error. +- * Records over KR_NSEC3_MAX_ITERATIONS are skipped, so you probably get kr_error(ENOENT). ++ * Too expensive NSEC3 records are skipped, so you probably get kr_error(ENOENT). + */ + int kr_nsec3_wildcard_answer_response_check(const knot_pkt_t *pkt, knot_section_t section_id, + const knot_dname_t *sname, int trim_to_next); +diff --git a/lib/layer/validate.c b/lib/layer/validate.c +index 93f1d4f..5fea99d 100644 +--- a/lib/layer/validate.c ++++ b/lib/layer/validate.c +@@ -128,14 +128,15 @@ static bool maybe_downgrade_nsec3(const ranked_rr_array_entry_t *e, struct kr_qu + const knot_rdataset_t *rrs = &e->rr->rrs; + knot_rdata_t *rd = rrs->rdata; + for (int j = 0; j < rrs->count; ++j, rd = knot_rdataset_next(rd)) { +- if (knot_nsec3_iters(rd) > KR_NSEC3_MAX_ITERATIONS) ++ if (kr_nsec3_limited_rdata(rd)) + goto do_downgrade; + } + return false; + + do_downgrade: // we do this deep inside calls because of having signer name available +- VERBOSE_MSG(qry, "<= DNSSEC downgraded due to NSEC3 iterations %d > %d\n", +- (int)knot_nsec3_iters(rd), (int)KR_NSEC3_MAX_ITERATIONS); ++ VERBOSE_MSG(qry, ++ "<= DNSSEC downgraded due to expensive NSEC3: %d iterations, %d salt length\n", ++ (int)knot_nsec3_iters(rd), (int)knot_nsec3_salt_len(rd)); + qry->flags.DNSSEC_WANT = false; + qry->flags.DNSSEC_INSECURE = true; + rank_records(qry, true, KR_RANK_INSECURE, vctx->zone_name); diff -Nru knot-resolver-5.6.0/debian/patches/0003-lib-cache-limit-the-amount-of-work-on-SHA1.patch knot-resolver-5.6.0/debian/patches/0003-lib-cache-limit-the-amount-of-work-on-SHA1.patch --- knot-resolver-5.6.0/debian/patches/0003-lib-cache-limit-the-amount-of-work-on-SHA1.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0003-lib-cache-limit-the-amount-of-work-on-SHA1.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,60 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Sun, 11 Feb 2024 10:00:32 +0100 +Subject: lib/cache: limit the amount of work on SHA1 + +That's when searching NSEC3 aggressive cache. +--- + lib/cache/nsec3.c | 14 ++++++++++++++ + lib/dnssec/nsec3.h | 12 ++++++++++++ + 2 files changed, 26 insertions(+) + +diff --git a/lib/cache/nsec3.c b/lib/cache/nsec3.c +index 9832630..2716456 100644 +--- a/lib/cache/nsec3.c ++++ b/lib/cache/nsec3.c +@@ -272,8 +272,22 @@ int nsec3_encloser(struct key *k, struct answer *ans, + const int zname_labels = knot_dname_labels(k->zname, NULL); + int last_nxproven_labels = -1; + const knot_dname_t *name = qry->sname; ++ ++ /* Avoid doing too much work on SHA1; we might consider that a part of mitigating ++ * CVE-2023-50868: NSEC3 closest encloser proof can exhaust CPU ++ * As currently the code iterates from the longest name, we limit that. ++ * Note that we don't want to limit too much, as the alternative usually includes ++ * sending more queries upstream, which would come with nontrivial work, too. ++ */ ++ const int max_labels = zname_labels + kr_nsec3_max_depth(&ans->nsec_p.libknot); ++ if (sname_labels > max_labels) ++ VERBOSE_MSG(qry, "=> NSEC3 hashing partly skipped due to too long SNAME (CVE-2023-50868)\n"); ++ + for (int name_labels = sname_labels; name_labels >= zname_labels; + --name_labels, name += 1 + name[0]) { ++ if (name_labels > max_labels) ++ continue; // avoid the hashing ++ + /* Find a previous-or-equal NSEC3 in cache covering the name, + * checking TTL etc. */ + const knot_db_val_t key = key_NSEC3_name(k, name, false, &ans->nsec_p); +diff --git a/lib/dnssec/nsec3.h b/lib/dnssec/nsec3.h +index 76ef2e9..a28d3c7 100644 +--- a/lib/dnssec/nsec3.h ++++ b/lib/dnssec/nsec3.h +@@ -38,6 +38,18 @@ static inline bool kr_nsec3_limited_params(const dnssec_nsec3_params_t *params) + return kr_nsec3_limited(params->iterations, params->salt.size); + } + ++/** Return limit on NSEC3 depth. The point is to avoid doing too much work on SHA1. ++ * ++ * CVE-2023-50868: NSEC3 closest encloser proof can exhaust CPU ++ * ++ * 128 is chosen so that zones with good NSEC3 parameters (giving _price() == 1) ++ * won't be limited in any way. Performance doesn't seem too bad with that either. ++ */ ++static inline int kr_nsec3_max_depth(const dnssec_nsec3_params_t *params) ++{ ++ return 128 / kr_nsec3_price(params->iterations, params->salt.size); ++} ++ + + /** + * Name error response check (RFC5155 7.2.2). diff -Nru knot-resolver-5.6.0/debian/patches/0004-validator-limit-the-amount-of-work-on-SHA1-in-NSEC3-.patch knot-resolver-5.6.0/debian/patches/0004-validator-limit-the-amount-of-work-on-SHA1-in-NSEC3-.patch --- knot-resolver-5.6.0/debian/patches/0004-validator-limit-the-amount-of-work-on-SHA1-in-NSEC3-.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0004-validator-limit-the-amount-of-work-on-SHA1-in-NSEC3-.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,31 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Mon, 12 Feb 2024 11:16:37 +0100 +Subject: validator: limit the amount of work on SHA1 in NSEC3 proofs + +--- + lib/dnssec/nsec3.c | 12 ++++++++++++ + 1 file changed, 12 insertions(+) + +diff --git a/lib/dnssec/nsec3.c b/lib/dnssec/nsec3.c +index e4d314b..4199f25 100644 +--- a/lib/dnssec/nsec3.c ++++ b/lib/dnssec/nsec3.c +@@ -146,6 +146,18 @@ static int closest_encloser_match(int *flags, const knot_rrset_t *nsec3, + const knot_dname_t *encloser = knot_wire_next_label(name, NULL); + *skipped = 1; + ++ /* Avoid doing too much work on SHA1, mitigating: ++ * CVE-2023-50868: NSEC3 closest encloser proof can exhaust CPU ++ * We log nothing here; it wouldn't be easy from this place ++ * and huge SNAME should be suspicious on its own. ++ */ ++ const int max_labels = knot_dname_labels(nsec3->owner, NULL) - 1 ++ + kr_nsec3_max_depth(¶ms); ++ for (int l = knot_dname_labels(encloser, NULL); l > max_labels; --l) { ++ encloser = knot_wire_next_label(encloser, NULL); ++ ++(*skipped); ++ } ++ + while(encloser) { + ret = hash_name(&name_hash, ¶ms, encloser); + if (ret != 0) diff -Nru knot-resolver-5.6.0/debian/patches/0005-validator-refuse-to-validate-answers-with-more-than-.patch knot-resolver-5.6.0/debian/patches/0005-validator-refuse-to-validate-answers-with-more-than-.patch --- knot-resolver-5.6.0/debian/patches/0005-validator-refuse-to-validate-answers-with-more-than-.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0005-validator-refuse-to-validate-answers-with-more-than-.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,37 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Mon, 12 Feb 2024 11:16:47 +0100 +Subject: validator: refuse to validate answers with more than 8 NSEC3 records + +--- + lib/layer/validate.c | 18 ++++++++++++++++++ + 1 file changed, 18 insertions(+) + +diff --git a/lib/layer/validate.c b/lib/layer/validate.c +index 5fea99d..95a7624 100644 +--- a/lib/layer/validate.c ++++ b/lib/layer/validate.c +@@ -1120,6 +1120,24 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt) + } + } + ++ /* Check for too many NSEC3 records. That's an issue, as some parts of validation ++ * are quadratic in their count, doing nontrivial computations inside. ++ * Also there seems to be no use in sending many NSEC3 records. */ ++ if (!qry->flags.CACHED) { ++ const knot_pktsection_t *sec = knot_pkt_section(pkt, KNOT_AUTHORITY); ++ int count = 0; ++ for (int i = 0; i < sec->count; ++i) ++ count += (knot_pkt_rr(sec, i)->type == KNOT_RRTYPE_NSEC3); ++ if (count > 8) { ++ VERBOSE_MSG(qry, "<= too many NSEC3 records in AUTHORITY (%d)\n", count); ++ kr_request_set_extended_error(req, KNOT_EDNS_EDE_NSEC3_ITERS, ++ /* It's not about iteration values per se, but close enough. */ ++ "DYRH: too many NSEC3 records"); ++ qry->flags.DNSSEC_BOGUS = true; ++ return KR_STATE_FAIL; ++ } ++ } ++ + if (knot_wire_get_aa(pkt->wire) && qtype == KNOT_RRTYPE_DNSKEY) { + const knot_rrset_t *ds = qry->zone_cut.trust_anchor; + if (ds && !kr_ds_algo_support(ds)) { diff -Nru knot-resolver-5.6.0/debian/patches/0006-validator-compatibility-with-older-libknot-versions.patch knot-resolver-5.6.0/debian/patches/0006-validator-compatibility-with-older-libknot-versions.patch --- knot-resolver-5.6.0/debian/patches/0006-validator-compatibility-with-older-libknot-versions.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0006-validator-compatibility-with-older-libknot-versions.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,22 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Mon, 12 Feb 2024 11:30:50 +0100 +Subject: validator: compatibility with older libknot versions + +The value is in IANA registry, so it's very constant anyway. +--- + lib/layer/validate.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/layer/validate.c b/lib/layer/validate.c +index 95a7624..6c0d7ad 100644 +--- a/lib/layer/validate.c ++++ b/lib/layer/validate.c +@@ -1130,7 +1130,7 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt) + count += (knot_pkt_rr(sec, i)->type == KNOT_RRTYPE_NSEC3); + if (count > 8) { + VERBOSE_MSG(qry, "<= too many NSEC3 records in AUTHORITY (%d)\n", count); +- kr_request_set_extended_error(req, KNOT_EDNS_EDE_NSEC3_ITERS, ++ kr_request_set_extended_error(req, 27/*KNOT_EDNS_EDE_NSEC3_ITERS*/, + /* It's not about iteration values per se, but close enough. */ + "DYRH: too many NSEC3 records"); + qry->flags.DNSSEC_BOGUS = true; diff -Nru knot-resolver-5.6.0/debian/patches/0007-lib-cache-bump-CACHE_VERSION.patch knot-resolver-5.6.0/debian/patches/0007-lib-cache-bump-CACHE_VERSION.patch --- knot-resolver-5.6.0/debian/patches/0007-lib-cache-bump-CACHE_VERSION.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0007-lib-cache-bump-CACHE_VERSION.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,25 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Fri, 23 Feb 2024 10:07:35 +0100 +Subject: lib/cache: bump CACHE_VERSION + +Ideally we would've done that at once with increasing NSEC3 strictness, +i.e. in 5.7.1 + 6.0.6, as otherwise we could run into some recoverable +assertions until the records got removed or expired. +We at least do the bump now. +--- + lib/cache/api.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/cache/api.c b/lib/cache/api.c +index bb627ea..f71a8d0 100644 +--- a/lib/cache/api.c ++++ b/lib/cache/api.c +@@ -40,7 +40,7 @@ + + + /** Cache version */ +-static const uint16_t CACHE_VERSION = 6; ++static const uint16_t CACHE_VERSION = 7; + /** Key size */ + #define KEY_HSIZE (sizeof(uint8_t) + sizeof(uint16_t)) + #define KEY_SIZE (KEY_HSIZE + KNOT_DNAME_MAXLEN) diff -Nru knot-resolver-5.6.0/debian/patches/0008-lib-dnssec-kr_rrset_validate_with_key-deduplicate-cl.patch knot-resolver-5.6.0/debian/patches/0008-lib-dnssec-kr_rrset_validate_with_key-deduplicate-cl.patch --- knot-resolver-5.6.0/debian/patches/0008-lib-dnssec-kr_rrset_validate_with_key-deduplicate-cl.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0008-lib-dnssec-kr_rrset_validate_with_key-deduplicate-cl.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,43 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Sat, 30 Dec 2023 09:20:56 +0100 +Subject: lib/dnssec kr_rrset_validate_with_key(): deduplicate cleanup + +--- + lib/dnssec.c | 11 +++++------ + 1 file changed, 5 insertions(+), 6 deletions(-) + +diff --git a/lib/dnssec.c b/lib/dnssec.c +index d6ae3cc..c536357 100644 +--- a/lib/dnssec.c ++++ b/lib/dnssec.c +@@ -356,9 +356,8 @@ static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx, + int retv = validate_rrsig_rr(&val_flgs, covered_labels, rdata_j, + key_alg, keytag, vctx); + if (retv == kr_error(EAGAIN)) { +- kr_dnssec_key_free(&created_key); + vctx->result = retv; +- return retv; ++ goto finish; + } else if (retv != 0) { + continue; + } +@@ -392,15 +391,15 @@ static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx, + + trim_ttl(covered, rdata_j, vctx); + +- kr_dnssec_key_free(&created_key); +- vctx->result = kr_ok(); + kr_rank_set(&vctx->rrs->at[i]->rank, KR_RANK_SECURE); /* upgrade from bogus */ +- return vctx->result; ++ vctx->result = kr_ok(); ++ goto finish; + } + } + /* No applicable key found, cannot be validated. */ +- kr_dnssec_key_free(&created_key); + vctx->result = kr_error(ENOENT); ++finish: ++ kr_dnssec_key_free(&created_key); + return vctx->result; + } + diff -Nru knot-resolver-5.6.0/debian/patches/0009-lib-resolve-kr_request_set_extended_error-tweak-prio.patch knot-resolver-5.6.0/debian/patches/0009-lib-resolve-kr_request_set_extended_error-tweak-prio.patch --- knot-resolver-5.6.0/debian/patches/0009-lib-resolve-kr_request_set_extended_error-tweak-prio.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0009-lib-resolve-kr_request_set_extended_error-tweak-prio.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,25 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Mon, 1 Jan 2024 16:05:46 +0100 +Subject: lib/resolve kr_request_set_extended_error(): tweak priorities + +Keep the first error in case priorities are equal. + +At least with the current KeyTrap topic that should work better, +but blaming a single error is alchemy anyway, at least in some cases. +--- + lib/resolve.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/resolve.c b/lib/resolve.c +index aa3d521..9e82e6f 100644 +--- a/lib/resolve.c ++++ b/lib/resolve.c +@@ -1684,7 +1684,7 @@ int kr_request_set_extended_error(struct kr_request *request, int info_code, con + return KNOT_EDNS_EDE_NONE; + } + +- if (ede_priority(info_code) >= ede_priority(ede->info_code)) { ++ if (ede_priority(info_code) > ede_priority(ede->info_code)) { + ede->info_code = info_code; + ede->extra_text = extra_text; + } diff -Nru knot-resolver-5.6.0/debian/patches/0010-mitigate-KeyTrap-DoS-CVE-2023-50387.patch knot-resolver-5.6.0/debian/patches/0010-mitigate-KeyTrap-DoS-CVE-2023-50387.patch --- knot-resolver-5.6.0/debian/patches/0010-mitigate-KeyTrap-DoS-CVE-2023-50387.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0010-mitigate-KeyTrap-DoS-CVE-2023-50387.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,233 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Mon, 1 Jan 2024 16:21:10 +0100 +Subject: mitigate KeyTrap DoS = CVE-2023-50387 + +--- + daemon/engine.c | 1 + + daemon/lua/kres-gen-30.lua | 3 +++ + daemon/lua/kres-gen-31.lua | 3 +++ + daemon/lua/kres-gen-32.lua | 3 +++ + lib/defines.h | 2 ++ + lib/dnssec.c | 28 ++++++++++++++++++++++++++++ + lib/dnssec.h | 1 + + lib/layer/validate.c | 7 +++++++ + lib/resolve.h | 3 +++ + lib/rplan.h | 6 ++++++ + 10 files changed, 57 insertions(+) + +diff --git a/daemon/engine.c b/daemon/engine.c +index 26c225f..a3f439f 100644 +--- a/daemon/engine.c ++++ b/daemon/engine.c +@@ -492,6 +492,7 @@ static int init_resolver(struct engine *engine) + /* Open resolution context */ + ctx->trust_anchors = trie_create(NULL); + ctx->negative_anchors = trie_create(NULL); ++ ctx->vld_limit_crypto = KR_VLD_LIMIT_CRYPTO_DEFAULT; + ctx->pool = engine->pool; + ctx->modules = &engine->modules; + ctx->cache_rtt_tout_retry_interval = KR_NS_TIMEOUT_RETRY_INTERVAL; +diff --git a/daemon/lua/kres-gen-30.lua b/daemon/lua/kres-gen-30.lua +index 4353c5c..01ae6ac 100644 +--- a/daemon/lua/kres-gen-30.lua ++++ b/daemon/lua/kres-gen-30.lua +@@ -336,6 +336,8 @@ struct kr_query { + struct kr_qflags forward_flags; + uint32_t secret; + uint32_t uid; ++ int32_t vld_limit_crypto_remains; ++ uint32_t vld_limit_uid; + uint64_t creation_time_mono; + uint64_t timestamp_mono; + struct timeval timestamp; +@@ -353,6 +355,7 @@ struct kr_context { + knot_rrset_t *upstream_opt_rr; + trie_t *trust_anchors; + trie_t *negative_anchors; ++ int32_t vld_limit_crypto; + struct kr_zonecut root_hints; + struct kr_cache cache; + unsigned int cache_rtt_tout_retry_interval; +diff --git a/daemon/lua/kres-gen-31.lua b/daemon/lua/kres-gen-31.lua +index a68dd65..c3845ec 100644 +--- a/daemon/lua/kres-gen-31.lua ++++ b/daemon/lua/kres-gen-31.lua +@@ -336,6 +336,8 @@ struct kr_query { + struct kr_qflags forward_flags; + uint32_t secret; + uint32_t uid; ++ int32_t vld_limit_crypto_remains; ++ uint32_t vld_limit_uid; + uint64_t creation_time_mono; + uint64_t timestamp_mono; + struct timeval timestamp; +@@ -353,6 +355,7 @@ struct kr_context { + knot_rrset_t *upstream_opt_rr; + trie_t *trust_anchors; + trie_t *negative_anchors; ++ int32_t vld_limit_crypto; + struct kr_zonecut root_hints; + struct kr_cache cache; + unsigned int cache_rtt_tout_retry_interval; +diff --git a/daemon/lua/kres-gen-32.lua b/daemon/lua/kres-gen-32.lua +index 222891e..2c407d4 100644 +--- a/daemon/lua/kres-gen-32.lua ++++ b/daemon/lua/kres-gen-32.lua +@@ -337,6 +337,8 @@ struct kr_query { + struct kr_qflags forward_flags; + uint32_t secret; + uint32_t uid; ++ int32_t vld_limit_crypto_remains; ++ uint32_t vld_limit_uid; + uint64_t creation_time_mono; + uint64_t timestamp_mono; + struct timeval timestamp; +@@ -354,6 +356,7 @@ struct kr_context { + knot_rrset_t *upstream_opt_rr; + trie_t *trust_anchors; + trie_t *negative_anchors; ++ int32_t vld_limit_crypto; + struct kr_zonecut root_hints; + struct kr_cache cache; + unsigned int cache_rtt_tout_retry_interval; +diff --git a/lib/defines.h b/lib/defines.h +index 6b6dac5..e832892 100644 +--- a/lib/defines.h ++++ b/lib/defines.h +@@ -57,6 +57,8 @@ static inline int KR_COLD kr_error(int x) { + #define KR_COUNT_NO_NSADDR_LIMIT 5 + #define KR_CONSUME_FAIL_ROW_LIMIT 3 /* Maximum number of KR_STATE_FAIL in a row. */ + ++#define KR_VLD_LIMIT_CRYPTO_DEFAULT 32 /**< default for struct kr_query::vld_limit_crypto */ ++ + /* + * Defines. + */ +diff --git a/lib/dnssec.c b/lib/dnssec.c +index c536357..1e6eb58 100644 +--- a/lib/dnssec.c ++++ b/lib/dnssec.c +@@ -240,6 +240,29 @@ fail: + return NULL; + } + ++/// Return if we want to afford yet another crypto-validation (and account it). ++static bool check_crypto_limit(const kr_rrset_validation_ctx_t *vctx) ++{ ++ if (vctx->limit_crypto_remains == NULL) ++ return true; // no limiting ++ if (*vctx->limit_crypto_remains > 0) { ++ --*vctx->limit_crypto_remains; ++ return true; ++ } ++ // We got over limit. There are optional actions to do. ++ if (vctx->log_qry && kr_log_is_debug_qry(VALIDATOR, vctx->log_qry)) { ++ auto_free char *name_str = kr_dname_text(vctx->zone_name); ++ kr_log_q(vctx->log_qry, VALIDATOR, ++ "expensive crypto limited, mitigating CVE-2023-50387, current zone: %s\n", ++ name_str); ++ } ++ if (vctx->log_qry && vctx->log_qry->request) { ++ kr_request_set_extended_error(vctx->log_qry->request, KNOT_EDNS_EDE_BOGUS, ++ "EAIE: expensive crypto limited, mitigating CVE-2023-50387"); ++ } ++ return false; ++} ++ + static int kr_svldr_rrset_with_key(knot_rrset_t *rrs, const knot_rdataset_t *rrsigs, + kr_rrset_validation_ctx_t *vctx, const kr_svldr_key_t *key) + { +@@ -258,6 +281,8 @@ static int kr_svldr_rrset_with_key(knot_rrset_t *rrs, const knot_rdataset_t *rrs + } else if (retv != 0) { + continue; + } ++ if (!check_crypto_limit(vctx)) ++ return vctx->result = kr_error(E2BIG); + // We only expect non-expanded wildcard records in input; + // that also means we don't need to perform non-existence proofs. + const int trim_labels = (val_flgs & FLG_WILDCARD_EXPANSION) ? 1 : 0; +@@ -367,6 +392,9 @@ static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx, + break; + } + } ++ if (!check_crypto_limit(vctx)) { ++ goto finish; ++ } + if (kr_check_signature(rdata_j, key, covered, trim_labels) != 0) { + vctx->rrs_counters.crypto_invalid++; + continue; +diff --git a/lib/dnssec.h b/lib/dnssec.h +index 0fbd47c..ca737cf 100644 +--- a/lib/dnssec.h ++++ b/lib/dnssec.h +@@ -44,6 +44,7 @@ struct kr_rrset_validation_ctx { + uint32_t flags; /*!< Output - Flags. */ + uint32_t err_cnt; /*!< Output - Number of validation failures. */ + uint32_t cname_norrsig_cnt; /*!< Output - Number of CNAMEs missing RRSIGs. */ ++ int32_t *limit_crypto_remains; /*!< Optional pointer to struct kr_query::vld_limit_crypto_remains */ + + /** Validation result: kr_error() code. + * +diff --git a/lib/layer/validate.c b/lib/layer/validate.c +index 6c0d7ad..4882da8 100644 +--- a/lib/layer/validate.c ++++ b/lib/layer/validate.c +@@ -276,6 +276,7 @@ static int validate_records(struct kr_request *req, knot_pkt_t *answer, knot_mm_ + .err_cnt = 0, + .cname_norrsig_cnt = 0, + .result = 0, ++ .limit_crypto_remains = &qry->vld_limit_crypto_remains, + .log_qry = qry, + }; + +@@ -384,6 +385,7 @@ static int validate_keyset(struct kr_request *req, knot_pkt_t *answer, bool has_ + .has_nsec3 = has_nsec3, + .flags = 0, + .result = 0, ++ .limit_crypto_remains = &qry->vld_limit_crypto_remains, + .log_qry = qry, + }; + int ret = kr_dnskeys_trusted(&vctx, sig_rds, qry->zone_cut.trust_anchor); +@@ -1030,6 +1032,11 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt) + struct kr_request *req = ctx->req; + struct kr_query *qry = req->current_query; + ++ if (qry->vld_limit_uid != qry->uid) { ++ qry->vld_limit_uid = qry->uid; ++ qry->vld_limit_crypto_remains = req->ctx->vld_limit_crypto; ++ } ++ + /* Ignore faulty or unprocessed responses. */ + if (ctx->state & (KR_STATE_FAIL|KR_STATE_CONSUME)) { + return ctx->state; +diff --git a/lib/resolve.h b/lib/resolve.h +index 97ba07b..a2d5ec9 100644 +--- a/lib/resolve.h ++++ b/lib/resolve.h +@@ -162,6 +162,9 @@ struct kr_context + + trie_t *trust_anchors; + trie_t *negative_anchors; ++ /** Validator's limit on the number of cryptographic steps for a single upstream packet. */ ++ int32_t vld_limit_crypto; ++ + struct kr_zonecut root_hints; + struct kr_cache cache; + unsigned cache_rtt_tout_retry_interval; +diff --git a/lib/rplan.h b/lib/rplan.h +index 891781f..68174af 100644 +--- a/lib/rplan.h ++++ b/lib/rplan.h +@@ -87,6 +87,12 @@ struct kr_query { + struct kr_qflags forward_flags; + uint32_t secret; + uint32_t uid; /**< Query iteration number, unique within the kr_rplan. */ ++ ++ /** Remaining limit; see kr_query::vld_limit_crypto docs */ ++ int32_t vld_limit_crypto_remains; ++ /** ::uid value to which this remaining limit applies. */ ++ uint32_t vld_limit_uid; ++ + uint64_t creation_time_mono; /* The time of query's creation (milliseconds). + * Or time of creation of an oldest + * ancestor if it is a subquery. */ diff -Nru knot-resolver-5.6.0/debian/patches/0011-mitigate-KeyTrap-DoS-CVE-2023-50387.patch knot-resolver-5.6.0/debian/patches/0011-mitigate-KeyTrap-DoS-CVE-2023-50387.patch --- knot-resolver-5.6.0/debian/patches/0011-mitigate-KeyTrap-DoS-CVE-2023-50387.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0011-mitigate-KeyTrap-DoS-CVE-2023-50387.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,80 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Tue, 16 Jan 2024 07:35:20 +0100 +Subject: mitigate KeyTrap DoS = CVE-2023-50387 + +Improve: don't retry in this case. +--- + lib/dnssec.c | 7 ++++--- + lib/layer/validate.c | 4 ++++ + lib/resolve.c | 4 +++- + 3 files changed, 11 insertions(+), 4 deletions(-) + +diff --git a/lib/dnssec.c b/lib/dnssec.c +index 1e6eb58..262570c 100644 +--- a/lib/dnssec.c ++++ b/lib/dnssec.c +@@ -134,7 +134,7 @@ int kr_rrset_validate(kr_rrset_validation_ctx_t *vctx, knot_rrset_t *covered) + memset(&vctx->rrs_counters, 0, sizeof(vctx->rrs_counters)); + for (unsigned i = 0; i < vctx->keys->rrs.count; ++i) { + int ret = kr_rrset_validate_with_key(vctx, covered, i, NULL); +- if (ret == 0) { ++ if (ret == 0 || ret == kr_error(E2BIG)) { + return ret; + } + } +@@ -307,7 +307,7 @@ int kr_svldr_rrset(knot_rrset_t *rrs, const knot_rdataset_t *rrsigs, + } + for (ssize_t i = 0; i < ctx->keys.len; ++i) { + kr_svldr_rrset_with_key(rrs, rrsigs, &ctx->vctx, &ctx->keys.at[i]); +- if (ctx->vctx.result == 0) ++ if (ctx->vctx.result == 0 || ctx->vctx.result == kr_error(E2BIG)) + break; + } + return ctx->vctx.result; +@@ -393,6 +393,7 @@ static int kr_rrset_validate_with_key(kr_rrset_validation_ctx_t *vctx, + } + } + if (!check_crypto_limit(vctx)) { ++ vctx->result = kr_error(E2BIG); + goto finish; + } + if (kr_check_signature(rdata_j, key, covered, trim_labels) != 0) { +@@ -475,7 +476,7 @@ int kr_dnskeys_trusted(kr_rrset_validation_ctx_t *vctx, const knot_rdataset_t *s + if (ret == 0) + ret = kr_svldr_rrset_with_key(keys, sigs, vctx, &key); + svldr_key_del(&key); +- if (ret == 0) { ++ if (ret == 0 || ret == kr_error(E2BIG)) { + kr_assert(vctx->result == 0); + return vctx->result; + } +diff --git a/lib/layer/validate.c b/lib/layer/validate.c +index 4882da8..1fdc57d 100644 +--- a/lib/layer/validate.c ++++ b/lib/layer/validate.c +@@ -1177,6 +1177,10 @@ static int validate(kr_layer_t *ctx, knot_pkt_t *pkt) + ret = validate_records(req, pkt, req->rplan.pool, has_nsec3); + if (ret == KNOT_EDOWNGRADED) { + return KR_STATE_DONE; ++ } else if (ret == kr_error(E2BIG)) { ++ qry->flags.DNSSEC_BOGUS = true; ++ return KR_STATE_FAIL; ++ + } else if (ret != 0) { + /* something exceptional - no DNS key, empty pointers etc + * normally it shouldn't happen */ +diff --git a/lib/resolve.c b/lib/resolve.c +index 9e82e6f..456065b 100644 +--- a/lib/resolve.c ++++ b/lib/resolve.c +@@ -880,7 +880,9 @@ int kr_resolve_consume(struct kr_request *request, struct kr_transport **transpo + + /* Do not finish with bogus answer. */ + if (qry->flags.DNSSEC_BOGUS) { +- if (qry->flags.FORWARD || qry->flags.STUB) { ++ if (qry->flags.FORWARD || qry->flags.STUB ++ /* Probably CPU exhaustion attempt, so do not retry. */ ++ || qry->vld_limit_crypto_remains <= 0) { + return KR_STATE_FAIL; + } + /* Other servers might not have broken DNSSEC. */ diff -Nru knot-resolver-5.6.0/debian/patches/0012-lib-dnssec-fix-imprecise-assertion.patch knot-resolver-5.6.0/debian/patches/0012-lib-dnssec-fix-imprecise-assertion.patch --- knot-resolver-5.6.0/debian/patches/0012-lib-dnssec-fix-imprecise-assertion.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0012-lib-dnssec-fix-imprecise-assertion.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,22 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Fri, 23 Feb 2024 09:33:21 +0100 +Subject: lib/dnssec: fix imprecise assertion + +It was no longer correct after commit cc5051b444130 (KeyTrap). +--- + lib/dnssec.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/lib/dnssec.c b/lib/dnssec.c +index 262570c..fcae473 100644 +--- a/lib/dnssec.c ++++ b/lib/dnssec.c +@@ -477,7 +477,7 @@ int kr_dnskeys_trusted(kr_rrset_validation_ctx_t *vctx, const knot_rdataset_t *s + ret = kr_svldr_rrset_with_key(keys, sigs, vctx, &key); + svldr_key_del(&key); + if (ret == 0 || ret == kr_error(E2BIG)) { +- kr_assert(vctx->result == 0); ++ kr_assert(vctx->result == ret); + return vctx->result; + } + } diff -Nru knot-resolver-5.6.0/debian/patches/0013-daemon-more-avoidance-of-excessive-TCP-reconnections.patch knot-resolver-5.6.0/debian/patches/0013-daemon-more-avoidance-of-excessive-TCP-reconnections.patch --- knot-resolver-5.6.0/debian/patches/0013-daemon-more-avoidance-of-excessive-TCP-reconnections.patch 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/0013-daemon-more-avoidance-of-excessive-TCP-reconnections.patch 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,153 @@ +From: =?utf-8?b?VmxhZGltw61yIMSMdW7DoXQ=?= +Date: Sat, 29 Jul 2023 17:53:34 +0200 +Subject: daemon: more avoidance of excessive TCP reconnections + +Previously this penalization was only triggered if the remote server +closed TCP. Now it's extended to us closing it when the server +(only) sends back some nonsense. At least for the cases which I could +see immediately. + +That's just three trivial one-line additions; the rest is refactoring. +--- + daemon/io.c | 38 ++++++++++---------------------------- + daemon/session.c | 26 +++++++++++++++++++++----- + daemon/session.h | 5 +++-- + 3 files changed, 34 insertions(+), 35 deletions(-) + +diff --git a/daemon/io.c b/daemon/io.c +index 48bfed3..ac0d969 100644 +--- a/daemon/io.c ++++ b/daemon/io.c +@@ -337,33 +337,6 @@ void tcp_timeout_trigger(uv_timer_t *timer) + } + } + +-static void tcp_disconnect(struct session *s, int errcode) +-{ +- if (kr_log_is_debug(IO, NULL)) { +- struct sockaddr *peer = session_get_peer(s); +- char *peer_str = kr_straddr(peer); +- kr_log_debug(IO, "=> connection to '%s' closed by peer (%s)\n", +- peer_str ? peer_str : "", +- uv_strerror(errcode)); +- } +- +- if (!session_was_useful(s) && session_flags(s)->outgoing) { +- /* We want to penalize the IP address, if a task is asking a query. +- * It might not be the right task, but that doesn't matter so much +- * for attributing the useless session to the IP address. */ +- struct qr_task *t = session_tasklist_get_first(s); +- struct kr_query *qry = NULL; +- if (t) { +- struct kr_request *req = worker_task_request(t); +- qry = array_tail(req->rplan.pending); +- } +- if (qry) /* We reuse the error for connection, as it's quite similar. */ +- qry->server_selection.error(qry, worker_task_get_transport(t), +- KR_SELECTION_TCP_CONNECT_FAILED); +- } +- worker_end_tcp(s); +-} +- + static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) + { + struct session *s = handle->data; +@@ -381,7 +354,16 @@ static void tcp_recv(uv_stream_t *handle, ssize_t nread, const uv_buf_t *buf) + } + + if (nread < 0 || !buf->base) { +- tcp_disconnect(s, nread); ++ if (kr_log_is_debug(IO, NULL)) { ++ struct sockaddr *peer = session_get_peer(s); ++ char *peer_str = kr_straddr(peer); ++ kr_log_debug(IO, "=> connection to '%s' closed by peer (%s)\n", ++ peer_str ? peer_str : "", ++ uv_strerror(nread)); ++ } ++ ++ session_tcp_penalize(s); ++ worker_end_tcp(s); + return; + } + +diff --git a/daemon/session.c b/daemon/session.c +index a1f2207..ed0ff68 100644 +--- a/daemon/session.c ++++ b/daemon/session.c +@@ -123,11 +123,6 @@ void session_close(struct session *session) + } + } + +-bool session_was_useful(const struct session *session) +-{ +- return session->was_useful; +-} +- + int session_start_read(struct session *session) + { + return io_start_read(session->handle); +@@ -582,6 +577,24 @@ ssize_t session_wirebuf_trim(struct session *session, ssize_t len) + return len; + } + ++void session_tcp_penalize(struct session *s) ++{ ++ if (s->was_useful || !s->sflags.outgoing) ++ return; ++ /* We want to penalize the IP address, if a task is asking a query. ++ * It might not be the right task, but that doesn't matter so much ++ * for attributing the useless session to the IP address. */ ++ struct qr_task *t = session_tasklist_get_first(s); ++ struct kr_query *qry = NULL; ++ if (t) { ++ struct kr_request *req = worker_task_request(t); ++ qry = array_tail(req->rplan.pending); ++ } ++ if (qry) /* We reuse the error for connection, as it's quite similar. */ ++ qry->server_selection.error(qry, worker_task_get_transport(t), ++ KR_SELECTION_TCP_CONNECT_FAILED); ++} ++ + knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm) + { + session->sflags.wirebuf_error = false; +@@ -617,6 +630,7 @@ knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm) + msg_size = knot_wire_read_u16(msg_start); + if (msg_size >= session->wire_buf_size) { + session->sflags.wirebuf_error = true; ++ session_tcp_penalize(session); + return NULL; + } + if (msg_size + 2 > wirebuf_msg_data_size) { +@@ -624,6 +638,7 @@ knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm) + } + if (msg_size == 0) { + session->sflags.wirebuf_error = true; ++ session_tcp_penalize(session); + return NULL; + } + msg_start += 2; +@@ -631,6 +646,7 @@ knot_pkt_t *session_produce_packet(struct session *session, knot_mm_t *mm) + msg_size = wirebuf_msg_data_size; + } else { + session->sflags.wirebuf_error = true; ++ session_tcp_penalize(session); + return NULL; + } + +diff --git a/daemon/session.h b/daemon/session.h +index 603d7cb..1f95ac5 100644 +--- a/daemon/session.h ++++ b/daemon/session.h +@@ -91,8 +91,9 @@ int session_tasklist_finalize_expired(struct session *session); + /** Both of task lists (associated & waiting). */ + /** Check if empty. */ + bool session_is_empty(const struct session *session); +-/** Return whether session seems to have done something useful. */ +-bool session_was_useful(const struct session *session); ++/** Penalize this server if the session hasn't been useful (and is outgoing). */ ++void session_tcp_penalize(struct session *session); ++ + /** Get pointer to session flags */ + struct session_flags *session_flags(struct session *session); + /** Get pointer to peer address. */ diff -Nru knot-resolver-5.6.0/debian/patches/series knot-resolver-5.6.0/debian/patches/series --- knot-resolver-5.6.0/debian/patches/series 1970-01-01 00:00:00.000000000 +0000 +++ knot-resolver-5.6.0/debian/patches/series 2024-02-23 12:14:43.000000000 +0000 @@ -0,0 +1,13 @@ +0001-validator-lower-the-NSEC3-iteration-limit-150-50.patch +0002-validator-similarly-also-limit-excessive-NSEC3-salt-.patch +0003-lib-cache-limit-the-amount-of-work-on-SHA1.patch +0004-validator-limit-the-amount-of-work-on-SHA1-in-NSEC3-.patch +0005-validator-refuse-to-validate-answers-with-more-than-.patch +0006-validator-compatibility-with-older-libknot-versions.patch +0007-lib-cache-bump-CACHE_VERSION.patch +0008-lib-dnssec-kr_rrset_validate_with_key-deduplicate-cl.patch +0009-lib-resolve-kr_request_set_extended_error-tweak-prio.patch +0010-mitigate-KeyTrap-DoS-CVE-2023-50387.patch +0011-mitigate-KeyTrap-DoS-CVE-2023-50387.patch +0012-lib-dnssec-fix-imprecise-assertion.patch +0013-daemon-more-avoidance-of-excessive-TCP-reconnections.patch diff -Nru knot-resolver-5.6.0/debian/salsa-ci.yml knot-resolver-5.6.0/debian/salsa-ci.yml --- knot-resolver-5.6.0/debian/salsa-ci.yml 2023-01-26 20:24:03.000000000 +0000 +++ knot-resolver-5.6.0/debian/salsa-ci.yml 2024-02-22 16:34:42.000000000 +0000 @@ -3,5 +3,8 @@ - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/salsa-ci.yml - https://salsa.debian.org/salsa-ci-team/pipeline/raw/master/pipeline-jobs.yml +variables: + RELEASE: 'bookworm' + lintian: allow_failure: true