diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/src/dns.c | 285 | ||||
-rw-r--r-- | src/src/expand.c | 1 | ||||
-rw-r--r-- | src/src/globals.c | 1 | ||||
-rw-r--r-- | src/src/globals.h | 1 | ||||
-rw-r--r-- | src/src/host.c | 9 | ||||
-rw-r--r-- | src/src/smtp_in.c | 57 |
6 files changed, 176 insertions, 178 deletions
diff --git a/src/src/dns.c b/src/src/dns.c index ad98a9d4e..79c4ed3fc 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -796,7 +796,7 @@ for (i = 0; i < 10; i++) cname_rr.data = type_rr.data = NULL; for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) { if (rr->type == type) @@ -895,25 +895,25 @@ switch (type) { /* The "mx hosts only" type doesn't require any special action here */ case T_MXH: - return dns_lookup(dnsa, name, T_MX, fully_qualified_name); + return dns_lookup(dnsa, name, T_MX, fully_qualified_name); /* Find nameservers for the domain or the nearest enclosing zone, excluding the root servers. */ case T_ZNS: - type = T_NS; - /* FALLTHROUGH */ + type = T_NS; + /* FALLTHROUGH */ case T_SOA: - { - const uschar *d = name; - while (d != 0) - { - int rc = dns_lookup(dnsa, d, type, fully_qualified_name); - if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; - while (*d != 0 && *d != '.') d++; - if (*d++ == 0) break; - } - return DNS_NOMATCH; - } + { + const uschar *d = name; + while (d != 0) + { + int rc = dns_lookup(dnsa, d, type, fully_qualified_name); + if (rc != DNS_NOMATCH && rc != DNS_NODATA) return rc; + while (*d != 0 && *d != '.') d++; + if (*d++ == 0) break; + } + return DNS_NOMATCH; + } /* Try to look up the Client SMTP Authorization SRV record for the name. If there isn't one, search from the top downwards for a CSA record in a parent @@ -922,148 +922,147 @@ switch (type) can tell whether to look at the explicit authorization field or the subdomain assertion field. */ case T_CSA: - { - uschar *srvname, *namesuff, *tld, *p; - int priority, weight, port; - int limit, rc, i; - BOOL ipv6; - dns_record *rr; - dns_scan dnss; - - DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); - - srvname = string_sprintf("_client._smtp.%s", name); - rc = dns_lookup(dnsa, srvname, T_SRV, NULL); - if (rc == DNS_SUCCEED || rc == DNS_AGAIN) - { - if (rc == DNS_SUCCEED) *fully_qualified_name = string_copy(name); - return rc; - } + { + uschar *srvname, *namesuff, *tld, *p; + int priority, weight, port; + int limit, rc, i; + BOOL ipv6; + dns_record *rr; + dns_scan dnss; + + DEBUG(D_dns) debug_printf("CSA lookup of %s\n", name); + + srvname = string_sprintf("_client._smtp.%s", name); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_SUCCEED || rc == DNS_AGAIN) + { + if (rc == DNS_SUCCEED) *fully_qualified_name = string_copy(name); + return rc; + } - /* Search for CSA subdomain assertion SRV records from the top downwards, - starting with the 2nd level domain. This order maximizes cache-friendliness. - We skip the top level domains to avoid loading their nameservers and because - we know they'll never have CSA SRV records. */ + /* Search for CSA subdomain assertion SRV records from the top downwards, + starting with the 2nd level domain. This order maximizes cache-friendliness. + We skip the top level domains to avoid loading their nameservers and because + we know they'll never have CSA SRV records. */ - namesuff = Ustrrchr(name, '.'); - if (namesuff == NULL) return DNS_NOMATCH; + namesuff = Ustrrchr(name, '.'); + if (namesuff == NULL) return DNS_NOMATCH; + tld = namesuff + 1; + ipv6 = FALSE; + limit = dns_csa_search_limit; + + /* Use more appropriate search parameters if we are in the reverse DNS. */ + + if (strcmpic(namesuff, US".arpa") == 0) + if (namesuff - 8 > name && strcmpic(namesuff - 8, US".in-addr.arpa") == 0) + { + namesuff -= 8; tld = namesuff + 1; - ipv6 = FALSE; - limit = dns_csa_search_limit; + limit = 3; + } + else if (namesuff - 4 > name && strcmpic(namesuff - 4, US".ip6.arpa") == 0) + { + namesuff -= 4; + tld = namesuff + 1; + ipv6 = TRUE; + limit = 3; + } - /* Use more appropriate search parameters if we are in the reverse DNS. */ + DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); - if (strcmpic(namesuff, US".arpa") == 0) - { - if (namesuff - 8 > name && strcmpic(namesuff - 8, US".in-addr.arpa") == 0) - { - namesuff -= 8; - tld = namesuff + 1; - limit = 3; - } - else if (namesuff - 4 > name && strcmpic(namesuff - 4, US".ip6.arpa") == 0) - { - namesuff -= 4; - tld = namesuff + 1; - ipv6 = TRUE; - limit = 3; - } - } + /* Do not perform the search if the top level or 2nd level domains do not + exist. This is quite common, and when it occurs all the search queries would + go to the root or TLD name servers, which is not friendly. So we check the + AUTHORITY section; if it contains the root's SOA record or the TLD's SOA then + the TLD or the 2LD (respectively) doesn't exist and we can skip the search. + If the TLD and the 2LD exist but the explicit CSA record lookup failed, then + the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */ + + if (rc == DNS_NOMATCH) + { + /* This is really gross. The successful return value from res_search() is + the packet length, which is stored in dnsa->answerlen. If we get a + negative DNS reply then res_search() returns -1, which causes the bounds + checks for name decompression to fail when it is treated as a packet + length, which in turn causes the authority search to fail. The correct + packet length has been lost inside libresolv, so we have to guess a + replacement value. (The only way to fix this properly would be to + re-implement res_search() and res_query() so that they don't muddle their + success and packet length return values.) For added safety we only reset + the packet length if the packet header looks plausible. */ + + HEADER *h = (HEADER *)dnsa->answer; + if (h->qr == 1 && h->opcode == QUERY && h->tc == 0 + && (h->rcode == NOERROR || h->rcode == NXDOMAIN) + && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 + && ntohs(h->nscount) >= 1) + dnsa->answerlen = MAXPACKET; + + for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT) + ) + if (rr->type != T_SOA) continue; + else if (strcmpic(rr->name, US"") == 0 || + strcmpic(rr->name, tld) == 0) return DNS_NOMATCH; + else break; + } - DEBUG(D_dns) debug_printf("CSA TLD %s\n", tld); + for (i = 0; i < limit; i++) + { + if (ipv6) + { + /* Scan through the IPv6 reverse DNS in chunks of 16 bits worth of IP + address, i.e. 4 hex chars and 4 dots, i.e. 8 chars. */ + namesuff -= 8; + if (namesuff <= name) return DNS_NOMATCH; + } + else + /* Find the start of the preceding domain name label. */ + do + if (--namesuff <= name) return DNS_NOMATCH; + while (*namesuff != '.'); + + DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); + + srvname = string_sprintf("_client._smtp.%s", namesuff + 1); + rc = dns_lookup(dnsa, srvname, T_SRV, NULL); + if (rc == DNS_AGAIN) return rc; + if (rc != DNS_SUCCEED) continue; + + /* Check that the SRV record we have found is worth returning. We don't + just return the first one we find, because some lower level SRV record + might make stricter assertions than its parent domain. */ + + for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); + rr; + rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) + { + if (rr->type != T_SRV) continue; - /* Do not perform the search if the top level or 2nd level domains do not - exist. This is quite common, and when it occurs all the search queries would - go to the root or TLD name servers, which is not friendly. So we check the - AUTHORITY section; if it contains the root's SOA record or the TLD's SOA then - the TLD or the 2LD (respectively) doesn't exist and we can skip the search. - If the TLD and the 2LD exist but the explicit CSA record lookup failed, then - the AUTHORITY SOA will be the 2LD's or a subdomain thereof. */ + /* Extract the numerical SRV fields (p is incremented) */ + p = rr->data; + GETSHORT(priority, p); + GETSHORT(weight, p); weight = weight; /* compiler quietening */ + GETSHORT(port, p); - if (rc == DNS_NOMATCH) - { - /* This is really gross. The successful return value from res_search() is - the packet length, which is stored in dnsa->answerlen. If we get a - negative DNS reply then res_search() returns -1, which causes the bounds - checks for name decompression to fail when it is treated as a packet - length, which in turn causes the authority search to fail. The correct - packet length has been lost inside libresolv, so we have to guess a - replacement value. (The only way to fix this properly would be to - re-implement res_search() and res_query() so that they don't muddle their - success and packet length return values.) For added safety we only reset - the packet length if the packet header looks plausible. */ - - HEADER *h = (HEADER *)dnsa->answer; - if (h->qr == 1 && h->opcode == QUERY && h->tc == 0 - && (h->rcode == NOERROR || h->rcode == NXDOMAIN) - && ntohs(h->qdcount) == 1 && ntohs(h->ancount) == 0 - && ntohs(h->nscount) >= 1) - dnsa->answerlen = MAXPACKET; - - for (rr = dns_next_rr(dnsa, &dnss, RESET_AUTHORITY); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - if (rr->type != T_SOA) continue; - else if (strcmpic(rr->name, US"") == 0 || - strcmpic(rr->name, tld) == 0) return DNS_NOMATCH; - else break; - } + /* Check the CSA version number */ + if (priority != 1) continue; - for (i = 0; i < limit; i++) + /* If it's making an interesting assertion, return this response. */ + if (port & 1) { - if (ipv6) - { - /* Scan through the IPv6 reverse DNS in chunks of 16 bits worth of IP - address, i.e. 4 hex chars and 4 dots, i.e. 8 chars. */ - namesuff -= 8; - if (namesuff <= name) return DNS_NOMATCH; - } - else - /* Find the start of the preceding domain name label. */ - do - if (--namesuff <= name) return DNS_NOMATCH; - while (*namesuff != '.'); - - DEBUG(D_dns) debug_printf("CSA parent search at %s\n", namesuff + 1); - - srvname = string_sprintf("_client._smtp.%s", namesuff + 1); - rc = dns_lookup(dnsa, srvname, T_SRV, NULL); - if (rc == DNS_AGAIN) return rc; - if (rc != DNS_SUCCEED) continue; - - /* Check that the SRV record we have found is worth returning. We don't - just return the first one we find, because some lower level SRV record - might make stricter assertions than its parent domain. */ - - for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); - rr != NULL; - rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) - { - if (rr->type != T_SRV) continue; - - /* Extract the numerical SRV fields (p is incremented) */ - p = rr->data; - GETSHORT(priority, p); - GETSHORT(weight, p); weight = weight; /* compiler quietening */ - GETSHORT(port, p); - - /* Check the CSA version number */ - if (priority != 1) continue; - - /* If it's making an interesting assertion, return this response. */ - if (port & 1) - { - *fully_qualified_name = namesuff + 1; - return DNS_SUCCEED; - } - } + *fully_qualified_name = namesuff + 1; + return DNS_SUCCEED; } - return DNS_NOMATCH; } + } + return DNS_NOMATCH; + } default: - if (type >= 0) - return dns_lookup(dnsa, name, type, fully_qualified_name); + if (type >= 0) + return dns_lookup(dnsa, name, type, fully_qualified_name); } /* Control should never reach here */ diff --git a/src/src/expand.c b/src/src/expand.c index 209270163..7e10ee553 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -648,6 +648,7 @@ static var_entry var_table[] = { { "sender_address_local_part", vtype_localpart, &sender_address }, { "sender_data", vtype_stringptr, &sender_data }, { "sender_fullhost", vtype_stringptr, &sender_fullhost }, + { "sender_helo_dnssec", vtype_bool, &sender_helo_dnssec }, { "sender_helo_name", vtype_stringptr, &sender_helo_name }, { "sender_host_address", vtype_stringptr, &sender_host_address }, { "sender_host_authenticated",vtype_stringptr, &sender_host_authenticated }, diff --git a/src/src/globals.c b/src/src/globals.c index 3cbbbd311..c0d03daaa 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1202,6 +1202,7 @@ uschar *sender_address_unrewritten = NULL; uschar *sender_data = NULL; unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; uschar *sender_fullhost = NULL; +BOOL sender_helo_dnssec = FALSE; uschar *sender_helo_name = NULL; uschar **sender_host_aliases = &no_aliases; uschar *sender_host_address = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index d90854cbe..1aca0714f 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -755,6 +755,7 @@ extern uschar *sender_address_unrewritten; /* Set if rewritten by verify */ extern uschar *sender_data; /* lookup result for senders */ extern unsigned int sender_domain_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for sender domain */ extern uschar *sender_fullhost; /* Sender host name + address */ +extern BOOL sender_helo_dnssec; /* True if HELO verify used DNS and was DNSSEC */ extern uschar *sender_helo_name; /* Host name from HELO/EHLO */ extern uschar **sender_host_aliases; /* Points to list of alias names */ extern unsigned int sender_host_cache[(MAX_NAMED_LIST * 2)/32]; /* Cache bits for incoming host */ diff --git a/src/src/host.c b/src/src/host.c index 4772a7c6c..5629d7db2 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -1668,11 +1668,10 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) store_pool = POOL_PERM; /* Save names in permanent storage */ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) - { - if (rr->type == T_PTR) count++; - } + if (rr->type == T_PTR) + count++; /* Get store for the list of aliases. For compatibility with gethostbyaddr, we make an empty list if there are none. */ @@ -1682,7 +1681,7 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) /* Re-scan and extract the names */ for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS); - rr != NULL; + rr; rr = dns_next_rr(&dnsa, &dnss, RESET_NEXT)) { uschar *s = NULL; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index b2f8b0fc8..aa3936288 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -3003,31 +3003,25 @@ else /* If a host name is known, check it and all its aliases. */ - if (sender_host_name != NULL) - { - helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0; - - if (helo_verified) + if (sender_host_name) + if ((helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0)) { - /*XXX have sender_host_dnssec */ + sender_helo_dnssec = sender_host_dnssec; HDEBUG(D_receive) debug_printf("matched host name\n"); } else { uschar **aliases = sender_host_aliases; - while (*aliases != NULL) - { - helo_verified = strcmpic(*aliases++, sender_helo_name) == 0; - if (helo_verified) break; - /*XXX have sender_host_dnssec */ - } - HDEBUG(D_receive) - { - if (helo_verified) + while (*aliases) + if ((helo_verified = strcmpic(*aliases++, sender_helo_name) == 0)) + { + sender_helo_dnssec = sender_host_dnssec; + break; + } + + HDEBUG(D_receive) if (helo_verified) debug_printf("matched alias %s\n", *(--aliases)); - } } - } /* Final attempt: try a forward lookup of the helo name */ @@ -3035,31 +3029,34 @@ else { int rc; host_item h; + dnssec_domains d; + host_item *hh; + h.name = sender_helo_name; h.address = NULL; h.mx = MX_NONE; h.next = NULL; + d.request = US"*"; + d.require = US""; + HDEBUG(D_receive) debug_printf("getting IP address for %s\n", sender_helo_name); -/*XXX would like to determine dnssec status here */ -/* need to change to bydns */ - rc = host_find_byname(&h, NULL, 0, NULL, TRUE); + rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A, + NULL, NULL, NULL, &d, NULL, NULL); if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL) - { - host_item *hh = &h; - while (hh != NULL) - { + for (hh = &h; hh; hh = hh->next) if (Ustrcmp(hh->address, sender_host_address) == 0) { helo_verified = TRUE; + if (h.dnssec == DS_YES) sender_helo_dnssec = TRUE; HDEBUG(D_receive) - debug_printf("IP address for %s matches calling address\n", - sender_helo_name); + { + debug_printf("IP address for %s matches calling address\n" + "Forward DNS security status: %sverified\n", + sender_helo_name, sender_helo_dnssec ? "" : "un"); + } break; } - hh = hh->next; - } - } } } @@ -3473,7 +3470,7 @@ while (done <= 0) now obsolescent, since the verification can now be requested selectively at ACL time. */ - helo_verified = helo_verify_failed = FALSE; + helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE; if (helo_required || helo_verify) { BOOL tempfail = !smtp_verify_helo(); |