From 79670d3c32ccb37fe06f25d8192943b58606a32a Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Fri, 17 Nov 2023 16:55:17 +0000 Subject: [PATCH] Lookups: Fix dnsdb lookup of multi-chunk TXT. Bug 3054 Broken=by: f6b1f8e7d642 --- doc/ChangeLog | 6 ++++- src/dns.c | 4 +++- src/lookups/dnsdb.c | 45 +++++++++++++++--------------------- test/dnszones-src/db.test.ex | 1 + test/scripts/2200-dnsdb/2200 | 1 + test/src/fakens.c | 1 + test/stdout/2200 | 1 + 7 files changed, 31 insertions(+), 28 deletions(-) --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -87,10 +87,13 @@ JH/34 Bug 3013: Fix use of $recipients w JH/35 Bug 3014: GnuTLS: fix expiry date for an auto-generated server certificate. Find and fix by Andreas Metzler. JH/39 Bug 3023: Fix crash induced by some combinations of zero-length strings and ${tr...}. Found and diagnosed by Heiko Schlichting. + +JH/06 Bug 3054: Fix dnsdb lookup for a TXT record with multiple chunks. This + was broken by hardening introduced for Bug 3033. Exim version 4.96 ----------------- JH/01 Move the wait-for-next-tick (needed for unique message IDs) from --- a/src/dns.c +++ b/src/dns.c @@ -428,11 +428,13 @@ TRACE trace = "R-namelen"; namelen = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, dnss->aptr, (DN_EXPAND_ARG4_TYPE) &dnss->srr.name, DNS_MAXNAME); if (namelen < 0) goto null_return; /* Move the pointer past the name and fill in the rest of the data structure -from the following bytes. */ +from the following bytes. We seem to be assuming here that the RR blob passed +to us by the resolver library is the same as that defined for an RR by RFC 1035 +section 3.2.1 */ TRACE trace = "R-name"; if (dnss_inc_aptr(dnsa, dnss, namelen)) goto null_return; /* Check space for type, class, TTL & data-size-word */ --- a/src/lookups/dnsdb.c +++ b/src/lookups/dnsdb.c @@ -390,46 +390,36 @@ while ((domain = string_nextinlist(&keys } continue; } /* Other kinds of record just have one piece of data each, but there may be - several of them, of course. */ + several of them, of course. TXT & SPF can have data in multiple chunks. */ if (yield->ptr) yield = string_catn(yield, outsep, 1); if (type == T_TXT || type == T_SPF) - { - if (!outsep2) /* output only the first item of data */ + for (unsigned data_offset = 0; data_offset + 1 < rr->size; ) { - uschar n = (rr->data)[0]; - /* size byte + data bytes must not excced the RRs length */ - if (n + 1 <= rr->size) - yield = string_catn(yield, US (rr->data+1), n); + uschar chunk_len = (rr->data)[data_offset]; + int remain; + + if (outsep2 && *outsep2 && data_offset != 0) + yield = string_catn(yield, outsep2, 1); + + /* Apparently there are resolvers that do not check RRs before passing + them on, and glibc fails to do so. So every application must... + Check for chunk len exceeding RR */ + + remain = rr->size - ++data_offset; + if (chunk_len > remain) + chunk_len = remain; + yield = string_catn(yield, US ((rr->data) + data_offset), chunk_len); + data_offset += chunk_len; + + if (!outsep2) break; /* output only the first chunk of the RR */ + } - else - { - /* output all items */ - int data_offset = 0; - while (data_offset < rr->size) - { - uschar chunk_len = (rr->data)[data_offset]; - int remain = rr->size - data_offset; - - /* Apparently there are resolvers that do not check RRs before passing - them on, and glibc fails to do so. So every application must... - Check for chunk len exceeding RR */ - - if (chunk_len > remain) - chunk_len = remain; - - if (*outsep2 && data_offset != 0) - yield = string_catn(yield, outsep2, 1); - yield = string_catn(yield, US ((rr->data) + ++data_offset), --chunk_len); - data_offset += chunk_len; - } - } - } else if (type == T_TLSA) if (rr->size < 3) continue; else {