diff options
Diffstat (limited to 'debian/patches/CVE-2019-19331.patch')
-rw-r--r-- | debian/patches/CVE-2019-19331.patch | 371 |
1 files changed, 371 insertions, 0 deletions
diff --git a/debian/patches/CVE-2019-19331.patch b/debian/patches/CVE-2019-19331.patch new file mode 100644 index 0000000..67420ea --- /dev/null +++ b/debian/patches/CVE-2019-19331.patch @@ -0,0 +1,371 @@ +From: Markus Koschany <apo@debian.org> +Date: Mon, 11 Mar 2024 14:37:42 +0200 +Subject: CVE-2019-19331 + +Bug-Debian: https://bugs.debian.org/946181 +Origin: https://gitlab.nic.cz/knot/knot-resolver/-/commit/782682db6d4b17d9d1559a1d13ae718a07eb260e +--- + daemon/lua/kres-gen.lua | 2 + + daemon/lua/kres-gen.sh | 1 + + lib/cache/api.c | 1 + + lib/dnssec.c | 1 + + lib/layer/iterate.c | 17 +++++- + lib/resolve.c | 1 + + lib/utils.c | 143 +++++++++++++++++++++++++++++++++++++++++++++--- + lib/utils.h | 10 +++- + modules/dns64/dns64.lua | 1 + + 9 files changed, 167 insertions(+), 10 deletions(-) + +diff --git a/daemon/lua/kres-gen.lua b/daemon/lua/kres-gen.lua +index 9e9f586..a92288b 100644 +--- a/daemon/lua/kres-gen.lua ++++ b/daemon/lua/kres-gen.lua +@@ -135,6 +135,7 @@ struct ranked_rr_array_entry { + _Bool yielded : 1; + _Bool to_wire : 1; + _Bool expiring : 1; ++ _Bool in_progress : 1; + knot_rrset_t *rr; + }; + typedef struct ranked_rr_array_entry ranked_rr_array_entry_t; +@@ -310,6 +311,7 @@ int kr_bitcmp(const char *, const char *, int); + int kr_family_len(int); + struct sockaddr *kr_straddr_socket(const char *, int); + int kr_ranked_rrarray_add(ranked_rr_array_t *, const knot_rrset_t *, uint8_t, _Bool, uint32_t, knot_mm_t *); ++int kr_ranked_rrarray_finalize(ranked_rr_array_t *, uint32_t, knot_mm_t *); + void kr_qflags_set(struct kr_qflags *, struct kr_qflags); + void kr_qflags_clear(struct kr_qflags *, struct kr_qflags); + int kr_zonecut_add(struct kr_zonecut *, const knot_dname_t *, const void *, int); +diff --git a/daemon/lua/kres-gen.sh b/daemon/lua/kres-gen.sh +index 538fe23..6c4aa64 100755 +--- a/daemon/lua/kres-gen.sh ++++ b/daemon/lua/kres-gen.sh +@@ -163,6 +163,7 @@ EOF + kr_family_len + kr_straddr_socket + kr_ranked_rrarray_add ++ kr_ranked_rrarray_finalize + kr_qflags_set + kr_qflags_clear + kr_zonecut_add +diff --git a/lib/cache/api.c b/lib/cache/api.c +index 4c7f3d2..90aaf0b 100644 +--- a/lib/cache/api.c ++++ b/lib/cache/api.c +@@ -605,6 +605,7 @@ static int stash_rrarray_entry(ranked_rr_array_t *arr, int arr_i, + /* TODO: ATM we assume that some properties are the same + * for all RRSIGs in the set (esp. label count). */ + ranked_rr_array_entry_t *e = arr->at[j]; ++ assert(!e->in_progress); + bool ok = e->qry_uid == qry->uid && !e->cached + && e->rr->type == KNOT_RRTYPE_RRSIG + && knot_rrsig_type_covered(e->rr->rrs.rdata) == rr->type +diff --git a/lib/dnssec.c b/lib/dnssec.c +index 4f8ad8a..9973656 100644 +--- a/lib/dnssec.c ++++ b/lib/dnssec.c +@@ -447,6 +447,7 @@ int kr_dnssec_matches_name_and_type(const ranked_rr_array_t *rrs, uint32_t qry_u + int ret = kr_error(ENOENT); + for (size_t i = 0; i < rrs->len; ++i) { + const ranked_rr_array_entry_t *entry = rrs->at[i]; ++ assert(!entry->in_progress); + const knot_rrset_t *nsec = entry->rr; + if (entry->qry_uid != qry_uid || entry->yielded) { + continue; +diff --git a/lib/layer/iterate.c b/lib/layer/iterate.c +index feaadf0..0423a3d 100644 +--- a/lib/layer/iterate.c ++++ b/lib/layer/iterate.c +@@ -1119,13 +1119,15 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt) + return resolve_error(pkt, req); + } + ++ int state; + /* Forwarding/stub mode is special. */ + if (query->flags.STUB) { +- return process_stub(pkt, req); ++ state = process_stub(pkt, req); ++ goto rrarray_finalize; + } + + /* Resolve authority to see if it's referral or authoritative. */ +- int state = process_authority(pkt, req); ++ state = process_authority(pkt, req); + switch(state) { + case KR_STATE_CONSUME: /* Not referral, process answer. */ + VERBOSE_MSG("<= rcode: %s\n", rcode ? rcode->name : "??"); +@@ -1139,6 +1141,17 @@ static int resolve(kr_layer_t *ctx, knot_pkt_t *pkt) + break; + } + ++rrarray_finalize: ++ /* Finish construction of libknot-format RRsets. */ ++ (void)0; ++ ranked_rr_array_t *selected[] = kr_request_selected(req); ++ for (knot_section_t i = KNOT_ANSWER; i <= KNOT_ADDITIONAL; ++i) { ++ int ret = kr_ranked_rrarray_finalize(selected[i], query->uid, &req->pool); ++ if (unlikely(ret)) { ++ return KR_STATE_FAIL; ++ } ++ } ++ + return state; + } + +diff --git a/lib/resolve.c b/lib/resolve.c +index b771cdc..65b167f 100644 +--- a/lib/resolve.c ++++ b/lib/resolve.c +@@ -484,6 +484,7 @@ static int write_extra_ranked_records(const ranked_rr_array_t *arr, uint16_t reo + + for (size_t i = 0; i < arr->len; ++i) { + ranked_rr_array_entry_t * entry = arr->at[i]; ++ assert(!entry->in_progress); + if (!entry->to_wire) { + continue; + } +diff --git a/lib/utils.c b/lib/utils.c +index fe9ab03..2b093f6 100644 +--- a/lib/utils.c ++++ b/lib/utils.c +@@ -677,6 +677,11 @@ static int to_wire_ensure_unique(ranked_rr_array_t *array, size_t index) + return kr_ok(); + } + ++/* Implementation overview of _add() and _finalize(): ++ * - for rdata we just maintain a list of pointers (in knot_rrset_t::additional) ++ * - we only construct the final rdataset at the end (and thus more efficiently) ++ */ ++typedef array_t(knot_rdata_t *) rdata_array_t; + int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr, + uint8_t rank, bool to_wire, uint32_t qry_uid, knot_mm_t *pool) + { +@@ -691,47 +696,93 @@ int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr, + } + if (stashed->qry_uid != qry_uid) { + break; ++ /* We do not guarantee merging RRs "across" any point that switched ++ * to processing a different upstream packet (i.e. qry_uid). ++ * In particular, iterator never returns KR_STATE_YIELD. */ + } + if (!rrsets_match(stashed->rr, rr)) { + continue; + } + /* Found the entry to merge with. Check consistency and merge. */ +- bool ok = stashed->rank == rank && !stashed->cached; ++ bool ok = stashed->rank == rank && !stashed->cached && stashed->in_progress; + if (!ok) { + assert(false); + return kr_error(EEXIST); + } ++ /* assert(rr->rrs.count == 1); */ ++ /* ^^ shouldn't be a problem for this function, but it's probably a bug */ ++ + /* It may happen that an RRset is first considered useful + * (to_wire = false, e.g. due to being part of glue), + * and later we may find we also want it in the answer. */ + stashed->to_wire = stashed->to_wire || to_wire; + +- return knot_rdataset_merge(&stashed->rr->rrs, &rr->rrs, pool); ++ /* We just add the reference into this in_progress RRset. */ ++ rdata_array_t *ra = stashed->rr->additional; ++ if (ra == NULL) { ++ /* RRset not in array format yet -> convert it. */ ++ ra = stashed->rr->additional = mm_alloc(pool, sizeof(*ra)); ++ if (!ra) { ++ return kr_error(ENOMEM); ++ } ++ array_init(*ra); ++ int ret = array_reserve_mm(*ra, stashed->rr->rrs.count + rr->rrs.count, ++ kr_memreserve, pool); ++ if (ret) { ++ return kr_error(ret); ++ } ++ knot_rdata_t *r_it = stashed->rr->rrs.rdata; ++ for (int ri = 0; ri < stashed->rr->rrs.count; ++ ++ri, r_it = knot_rdataset_next(r_it)) { ++ if (array_push(*ra, r_it) < 0) { ++ abort(); ++ } ++ } ++ } else { ++ int ret = array_reserve_mm(*ra, ra->len + rr->rrs.count, ++ kr_memreserve, pool); ++ if (ret) { ++ return kr_error(ret); ++ } ++ } ++ /* Append to the array. */ ++ knot_rdata_t *r_it = rr->rrs.rdata; ++ for (int ri = 0; ri < rr->rrs.count; ++ ++ri, r_it = knot_rdataset_next(r_it)) { ++ if (array_push(*ra, r_it) < 0) { ++ abort(); ++ } ++ } ++ return kr_ok(); + } + + /* No stashed rrset found, add */ + int ret = array_reserve_mm(*array, array->len + 1, kr_memreserve, pool); +- if (ret != 0) { +- return kr_error(ENOMEM); ++ if (ret) { ++ return kr_error(ret); + } + + ranked_rr_array_entry_t *entry = mm_alloc(pool, sizeof(ranked_rr_array_entry_t)); + if (!entry) { + return kr_error(ENOMEM); + } +- knot_rrset_t *copy = knot_rrset_copy(rr, pool); +- if (!copy) { ++ ++ knot_rrset_t *rr_new = knot_rrset_new(rr->owner, rr->type, rr->rclass, rr->ttl, pool); ++ if (!rr_new) { + mm_free(pool, entry); + return kr_error(ENOMEM); + } ++ rr_new->rrs = rr->rrs; ++ assert(rr_new->additional == NULL); + + entry->qry_uid = qry_uid; +- entry->rr = copy; ++ entry->rr = rr_new; + entry->rank = rank; + entry->revalidation_cnt = 0; + entry->cached = false; + entry->yielded = false; + entry->to_wire = to_wire; ++ entry->in_progress = true; + if (array_push(*array, entry) < 0) { + /* Silence coverity. It shouldn't be possible to happen, + * due to the array_reserve_mm call above. */ +@@ -742,6 +793,84 @@ int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr, + return to_wire_ensure_unique(array, array->len - 1); + } + ++/** Comparator for qsort() on an array of knot_data_t pointers. */ ++static int rdata_p_cmp(const void *rp1, const void *rp2) ++{ ++ /* Just correct types of the parameters and pass them dereferenced. */ ++ const knot_rdata_t ++ *const *r1 = rp1, ++ *const *r2 = rp2; ++ return knot_rdata_cmp(*r1, *r2); ++} ++int kr_ranked_rrarray_finalize(ranked_rr_array_t *array, uint32_t qry_uid, knot_mm_t *pool) ++{ ++ for (ssize_t array_i = array->len - 1; array_i >= 0; --array_i) { ++ ranked_rr_array_entry_t *stashed = array->at[array_i]; ++ if (stashed->qry_uid != qry_uid) { ++ continue; /* We apparently can't always short-cut the cycle. */ ++ } ++ if (!stashed->in_progress) { ++ continue; ++ } ++ rdata_array_t *ra = stashed->rr->additional; ++ if (!ra) { ++ /* No array, so we just need to copy the rdataset. */ ++ knot_rdataset_t *rds = &stashed->rr->rrs; ++ knot_rdataset_t tmp = *rds; ++ int ret = knot_rdataset_copy(rds, &tmp, pool); ++ if (ret) { ++ return kr_error(ret); ++ } ++ } else { ++ /* Multiple RRs; first: sort the array. */ ++ stashed->rr->additional = NULL; ++ qsort(ra->at, ra->len, sizeof(ra->at[0]), rdata_p_cmp); ++ /* Prune duplicates: NULL all except the last instance. */ ++ int dup_count = 0; ++ for (int i = 0; i + 1 < ra->len; ++i) { ++ if (knot_rdata_cmp(ra->at[i], ra->at[i + 1]) == 0) { ++ ra->at[i] = NULL; ++ ++dup_count; ++ QRVERBOSE(NULL, "iter", "deleted duplicate RR\n"); ++ } ++ } ++ /* Prepare rdataset, except rdata contents. */ ++ int size_sum = 0; ++ for (int i = 0; i < ra->len; ++i) { ++ if (ra->at[i]) { ++ size_sum += knot_rdata_size(ra->at[i]->len); ++ } ++ } ++ knot_rdataset_t *rds = &stashed->rr->rrs; ++ rds->count = ra->len - dup_count; ++ #if KNOT_VERSION_HEX >= 0x020900 ++ rds->size = size_sum; ++ #endif ++ if (size_sum) { ++ rds->rdata = mm_alloc(pool, size_sum); ++ if (!rds->rdata) { ++ return kr_error(ENOMEM); ++ } ++ } else { ++ rds->rdata = NULL; ++ } ++ /* Everything is ready; now just copy all the rdata. */ ++ uint8_t *raw_it = (uint8_t *)rds->rdata; ++ for (int i = 0; i < ra->len; ++i) { ++ if (ra->at[i] && size_sum/*linters*/) { ++ const int size = knot_rdata_size(ra->at[i]->len); ++ memcpy(raw_it, ra->at[i], size); ++ raw_it += size; ++ } ++ } ++ assert(raw_it == (uint8_t *)rds->rdata + size_sum); ++ } ++ stashed->in_progress = false; ++ } ++ return kr_ok(); ++} ++ ++ + int kr_ranked_rrarray_set_wire(ranked_rr_array_t *array, bool to_wire, + uint32_t qry_uid, bool check_dups, + bool (*extraCheck)(const ranked_rr_array_entry_t *)) +diff --git a/lib/utils.h b/lib/utils.h +index 21eabac..3040e66 100644 +--- a/lib/utils.h ++++ b/lib/utils.h +@@ -165,6 +165,7 @@ struct ranked_rr_array_entry { + bool yielded : 1; + bool to_wire : 1; /**< whether to be put into the answer */ + bool expiring : 1; /**< low remaining TTL; see is_expiring; only used in cache ATM */ ++ bool in_progress : 1; /**< build of RRset in progress, i.e. different format of RR data */ + knot_rrset_t *rr; + }; + typedef struct ranked_rr_array_entry ranked_rr_array_entry_t; +@@ -351,10 +352,17 @@ KR_EXPORT + int kr_rrkey(char *key, uint16_t class, const knot_dname_t *owner, + uint16_t type, uint16_t additional); + +-/** @internal Add RRSet copy to ranked RR array. */ ++/** Add RRSet copy to a ranked RR array. ++ * ++ * To convert to standard RRs inside, you need to call _finalize() afterwards, ++ * and the memory of rr->rrs.rdata has to remain until then. ++ */ + KR_EXPORT + int kr_ranked_rrarray_add(ranked_rr_array_t *array, const knot_rrset_t *rr, + uint8_t rank, bool to_wire, uint32_t qry_uid, knot_mm_t *pool); ++/** Finalize in_progress sets - all with matching qry_uid. */ ++KR_EXPORT ++int kr_ranked_rrarray_finalize(ranked_rr_array_t *array, uint32_t qry_uid, knot_mm_t *pool); + + /** @internal Mark the RRSets from particular query as + * "have (not) to be recorded in the final answer". +diff --git a/modules/dns64/dns64.lua b/modules/dns64/dns64.lua +index 8e3eaa0..8700661 100644 +--- a/modules/dns64/dns64.lua ++++ b/modules/dns64/dns64.lua +@@ -98,6 +98,7 @@ function M.layer.consume(state, req, pkt) + req.pool) + end + end ++ ffi.C.kr_ranked_rrarray_finalize(req.answ_selected, qry.uid, req.pool); + end + + return M |