X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/c9433c53ad292c2f7ec05aa9d083767f95d07858..b94ea1bd61485a97c2d0dc2cab4c4d86ffe82e89:/src/src/dns.c diff --git a/src/src/dns.c b/src/src/dns.c index 63856ead3..1347deec8 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -2,9 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ -/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions for interfacing with the DNS. */ @@ -258,7 +259,7 @@ else { int v6[4]; - g = string_get_tainted(32, is_tainted(string)); + g = string_get_tainted(32, string); (void)host_aton(string, v6); /* The original specification for IPv6 reverse lookup was to invert each @@ -298,13 +299,23 @@ return string_from_gstring(g); +/* Check a pointer for being past the end of a dns answer. +Exactly one past the end is defined as ok. +Return TRUE iff bad. +*/ +static BOOL +dnsa_bad_ptr(const dns_answer * dnsa, const uschar * ptr) +{ +return ptr > dnsa->answer + dnsa->answerlen; +} + /* Increment the aptr in dnss, checking against dnsa length. Return: TRUE for a bad result */ static BOOL dnss_inc_aptr(const dns_answer * dnsa, dns_scan * dnss, unsigned delta) { -return (dnss->aptr += delta) >= dnsa->answer + dnsa->answerlen; +return dnsa_bad_ptr(dnsa, dnss->aptr += delta); } /************************************************* @@ -384,11 +395,15 @@ if (reset != RESET_NEXT) 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; + /* skip name, type, class & TTL */ TRACE trace = "A-hdr"; if (dnss_inc_aptr(dnsa, dnss, namelen+8)) goto null_return; + + if (dnsa_bad_ptr(dnsa, dnss->aptr + sizeof(uint16_t))) goto null_return; GETSHORT(dnss->srr.size, dnss->aptr); /* size of data portion */ - /* skip over it */ + + /* skip over it, checking for a bogus size */ TRACE trace = "A-skip"; if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) goto null_return; } @@ -421,17 +436,22 @@ from the following bytes. */ TRACE trace = "R-name"; if (dnss_inc_aptr(dnsa, dnss, namelen)) goto null_return; -GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */ +/* Check space for type, class, TTL & data-size-word */ +if (dnsa_bad_ptr(dnsa, dnss->aptr + 3 * sizeof(uint16_t) + sizeof(uint32_t))) + goto null_return; + +GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */ + TRACE trace = "R-class"; -if (dnss_inc_aptr(dnsa, dnss, 2)) goto null_return; /* Don't want class */ -GETLONG(dnss->srr.ttl, dnss->aptr); /* TTL */ -GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */ -dnss->srr.data = dnss->aptr; /* The record's data follows */ +(void) dnss_inc_aptr(dnsa, dnss, sizeof(uint16_t)); /* skip class */ -/* Unchecked increment ok here since no further access on this iteration; -will be checked on next at "R-name". */ +GETLONG(dnss->srr.ttl, dnss->aptr); /* TTL */ +GETSHORT(dnss->srr.size, dnss->aptr); /* Size of data portion */ +dnss->srr.data = dnss->aptr; /* The record's data follows */ -dnss->aptr += dnss->srr.size; /* Advance to next RR */ +/* skip over it, checking for a bogus size */ +if (dnss_inc_aptr(dnsa, dnss, dnss->srr.size)) + goto null_return; /* Return a pointer to the dns_record structure within the dns_answer. This is for convenience so that the scans can use nice-looking for loops. */ @@ -637,7 +657,7 @@ if ((previous = tree_search(tree_dns_fails, node_name))) e = previous->data.ptr; else { - e = store_get_perm(DNS_FAILNODE_SIZE, is_tainted(name)); + e = store_get_perm(DNS_FAILNODE_SIZE, name); new = (void *)(e+1); dns_fail_tag(new->name, name, type); new->data.ptr = e; @@ -743,17 +763,17 @@ if (fake_dnsa_len_for_fail(dnsa, type)) /* Skip the mname & rname strings */ if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0) + p, (DN_EXPAND_ARG4_TYPE)discard_buf, sizeof(discard_buf))) < 0) break; p += len; if ((len = dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, - p, (DN_EXPAND_ARG4_TYPE)discard_buf, 256)) < 0) + p, (DN_EXPAND_ARG4_TYPE)discard_buf, sizeof(discard_buf))) < 0) break; p += len; /* Skip the SOA serial, refresh, retry & expire. Grab the TTL */ - if (p > dnsa->answer + dnsa->answerlen - 5 * INT32SZ) + if (dnsa_bad_ptr(dnsa, p + 5 * INT32SZ)) break; p += 4 * INT32SZ; GETLONG(ttl, p); @@ -801,6 +821,7 @@ dns_basic_lookup(dns_answer * dnsa, const uschar * name, int type) int rc; #ifndef STAND_ALONE const uschar * save_domain; +static BOOL try_again_recursion = FALSE; #endif /* DNS lookup failures of any kind are cached in a tree. This is mainly so that @@ -905,11 +926,31 @@ if (dnsa->answerlen < 0) switch (h_errno) /* Cut this out for various test programs */ #ifndef STAND_ALONE - save_domain = deliver_domain; - deliver_domain = string_copy(name); /* set $domain */ - rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, - &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); - deliver_domain = save_domain; + /* Permitting dns_again_means nonexist for TLSA lookups breaks the + doewngrade resistance of dane, so avoid for those. */ + + if (type == T_TLSA) + rc = FAIL; + else + { + if (try_again_recursion) + { + log_write(0, LOG_MAIN|LOG_PANIC, + "dns_again_means_nonexist recursion seen for %s" + " (assuming nonexist)", name); + return dns_fail_return(name, type, dns_expire_from_soa(dnsa, type), + DNS_NOMATCH); + } + + try_again_recursion = TRUE; + save_domain = deliver_domain; + deliver_domain = string_copy(name); /* set $domain */ + rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); + deliver_domain = save_domain; + try_again_recursion = FALSE; + } + if (rc != OK) { DEBUG(D_dns) debug_printf("returning DNS_AGAIN\n"); @@ -1065,7 +1106,7 @@ for (int i = 0; i <= dns_cname_loops; i++) return DNS_FAIL; /* DNS data comes from the outside, hence tainted */ - data = store_get(256, TRUE); + data = store_get(256, GET_TAINTED); if (dn_expand(dnsa->answer, dnsa->answer + dnsa->answerlen, cname_rr.data, (DN_EXPAND_ARG4_TYPE)data, 256) < 0) return DNS_FAIL; @@ -1148,7 +1189,7 @@ switch (type) case T_CSA: { uschar *srvname, *namesuff, *tld; - int priority, weight, port; + int priority, dummy_weight, port; int limit, rc, i; BOOL ipv6; dns_record *rr; @@ -1236,8 +1277,9 @@ switch (type) const uschar * p = rr->data; /* Extract the numerical SRV fields (p is incremented) */ + if (rr_bad_size(rr, 3 * sizeof(uint16_t))) continue; GETSHORT(priority, p); - GETSHORT(weight, p); + GETSHORT(dummy_weight, p); GETSHORT(port, p); /* Check the CSA version number */ @@ -1293,7 +1335,7 @@ if (rr->type == T_A) if (p + 4 <= dnsa_lim) { /* the IP is not regarded as tainted */ - yield = store_get(sizeof(dns_address) + 20, FALSE); + yield = store_get(sizeof(dns_address) + 20, GET_UNTAINTED); (void)sprintf(CS yield->address, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); yield->next = NULL; } @@ -1307,7 +1349,7 @@ else { struct in6_addr in6; for (int i = 0; i < 16; i++) in6.s6_addr[i] = rr->data[i]; - yield = store_get(sizeof(dns_address) + 50, FALSE); + yield = store_get(sizeof(dns_address) + 50, GET_UNTAINTED); inet_ntop(AF_INET6, &in6, CS yield->address, 50); yield->next = NULL; } @@ -1324,7 +1366,7 @@ dns_pattern_init(void) { if (check_dns_names_pattern[0] != 0 && !regex_check_dns_names) regex_check_dns_names = - regex_must_compile(check_dns_names_pattern, FALSE, TRUE); + regex_must_compile(check_dns_names_pattern, MCS_NOFLAGS, TRUE); } /* vi: aw ai sw=2