summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeremy Harris <jgh146exb@wizmail.org>2015-05-19 20:28:42 +0100
committerJeremy Harris <jgh146exb@wizmail.org>2015-05-19 22:36:35 +0100
commit1705dd20918634cfce236049e47d0fe43753dbc8 (patch)
treeca9ef683885cf9993ad1abae356ff67cb4965955
parent1f155f8e69b44ee7678dd1009ae0348e5c8d768e (diff)
Change HELO-verify forward case from byname to bydns and add DNSSEC tracking
-rw-r--r--doc/doc-docbook/spec.xfpt19
-rw-r--r--doc/doc-txt/ChangeLog3
-rw-r--r--src/src/dns.c285
-rw-r--r--src/src/expand.c1
-rw-r--r--src/src/globals.c1
-rw-r--r--src/src/globals.h1
-rw-r--r--src/src/host.c9
-rw-r--r--src/src/smtp_in.c57
-rw-r--r--test/confs/060824
-rw-r--r--test/dnszones-src/db.test.ex2
-rw-r--r--test/log/06083
-rw-r--r--test/scripts/0000-Basic/060820
-rw-r--r--test/stderr/007015
-rw-r--r--test/stdout/060814
14 files changed, 267 insertions, 187 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index c1668c7ac..752712181 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -11415,7 +11415,7 @@ This variable contains the numerical value of the Exim user id.
.new
.vitem &$exim_version$&
-.vindex "&$exim_uid$&"
+.vindex "&$exim_version$&"
This variable contains the version string of the Exim build.
The first character is a major version number, currently 4.
Then after a dot, the next group of digits is a minor version number.
@@ -11681,6 +11681,7 @@ the space value is -1. See also the &%check_log_space%& option.
.vindex "&$lookup_dnssec_authenticated$&"
This variable is set after a DNS lookup done by
a dnsdb lookup expansion, dnslookup router or smtp transport.
+.cindex "DNS" "DNSSEC"
It will be empty if &(DNSSEC)& was not requested,
&"no"& if the result was not labelled as authenticated data
and &"yes"& if it was.
@@ -12198,6 +12199,14 @@ verification either failed or was not requested. A host name in parentheses is
the argument of a HELO or EHLO command. This is omitted if it is identical to
the verified host name or to the host's IP address in square brackets.
+.new
+.vitem &$sender_helo_dnssec$&
+.vindex "&$sender_helo_dnssec$&"
+This boolean variable is true if a successful HELO verification was
+.cindex "DNS" "DNSSEC"
+done using DNS information the resolver library stated was authenticatied data.
+.wen
+
.vitem &$sender_helo_name$&
.vindex "&$sender_helo_name$&"
When a message is received from a remote host that has issued a HELO or EHLO
@@ -12227,6 +12236,7 @@ resolver library states that both
the reverse and forward DNS were authenticated data. At all
other times, this variable is false.
+.cindex "DNS" "DNSSEC"
It is likely that you will need to coerce DNSSEC support on in the resolver
library, by setting:
.code
@@ -14535,14 +14545,17 @@ is an IP literal matching the calling address of the host, or
matches the host name that Exim obtains by doing a reverse lookup of the
calling host address, or
.next
-when looked up using &[gethostbyname()]& (or &[getipnodebyname()]& when
-available) yields the calling host address.
+when looked up in DNS yields the calling host address.
.endlist
However, the EHLO or HELO command is not rejected if any of the checks
fail. Processing continues, but the result of the check is remembered, and can
be detected later in an ACL by the &`verify = helo`& condition.
+If DNS was used for successful verification, the variable
+.cindex "DNS" "DNSSEC"
+&$helo_verify_dnssec$& records the DNSSEC status of the lookups.
+
.option helo_verify_hosts main "host list&!!" unset
.cindex "HELO verifying" "mandatory"
.cindex "EHLO" "verifying, mandatory"
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index c6825d5be..a0d964926 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -106,6 +106,9 @@ JH/29 Bug 1632: Removed the word "rejected" from line logged for ACL discards.
JH/30 Check the forward DNS lookup for DNSSEC, in addition to the reverse,
when evaluating $sender_host_dnssec.
+JH/31 Check the HELO verification lookup for DNSSEC, adding new
+ $sender_helo_dnssec variable.
+
Exim version 4.85
-----------------
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();
diff --git a/test/confs/0608 b/test/confs/0608
new file mode 100644
index 000000000..d04dc369f
--- /dev/null
+++ b/test/confs/0608
@@ -0,0 +1,24 @@
+# Exim test configuration 0608
+
+exim_path = EXIM_PATH
+host_lookup_order = bydns
+primary_hostname = myhost.test.ex
+spool_directory = DIR/spool
+log_file_path = DIR/spool/log/%slog
+gecos_pattern = ""
+gecos_name = CALLER_NAME
+
+# ----- Main settings -----
+
+acl_smtp_helo = check_helo
+
+
+# ----- ACLs -----
+
+begin acl
+
+check_helo:
+ require verify = helo
+ accept logwrite = helo $sender_helo_name dnssec <$sender_helo_dnssec>
+
+# End
diff --git a/test/dnszones-src/db.test.ex b/test/dnszones-src/db.test.ex
index da084529f..9cc709ecc 100644
--- a/test/dnszones-src/db.test.ex
+++ b/test/dnszones-src/db.test.ex
@@ -406,6 +406,8 @@ DNSSEC mx-sec-a-sec MX 5 a-sec
a-unsec A V4NET.0.0.100
DNSSEC a-sec A V4NET.0.0.100
+DNSSEC l-sec A 127.0.0.1
+
; ------- Testing DANE ------------
; full suite dns chain, sha512
diff --git a/test/log/0608 b/test/log/0608
new file mode 100644
index 000000000..7d807e8f0
--- /dev/null
+++ b/test/log/0608
@@ -0,0 +1,3 @@
+1999-03-02 09:44:33 exim x.yz daemon started: pid=pppp, no queue runs, listening for SMTP on port 1225
+1999-03-02 09:44:33 helo localhost dnssec <no>
+1999-03-02 09:44:33 helo l-sec dnssec <yes>
diff --git a/test/scripts/0000-Basic/0608 b/test/scripts/0000-Basic/0608
new file mode 100644
index 000000000..1715fd200
--- /dev/null
+++ b/test/scripts/0000-Basic/0608
@@ -0,0 +1,20 @@
+# HELO verify dnssec
+# Exim test configuration 0608
+#
+exim -DSERVER=server -bd -oX PORT_D
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+HELO localhost
+??? 250
+****
+#
+client 127.0.0.1 PORT_D
+??? 220
+HELO l-sec
+??? 250
+****
+#
+killdaemon
+no_msglog_check
diff --git a/test/stderr/0070 b/test/stderr/0070
index 2bf1e9cdf..c50f93612 100644
--- a/test/stderr/0070
+++ b/test/stderr/0070
@@ -137,10 +137,10 @@ MUNGED: ::1 will be omitted in what follows
>>> matched alias ten-3-alias.test.ex
>>> verifying EHLO/HELO argument "ten-3xtra.test.ex"
>>> getting IP address for ten-3xtra.test.ex
-MUNGED: ::1 will be omitted in what follows
->>> get[host|ipnode]byname[2] looked up these IP addresses:
->>> name=ten-3xtra.test.ex address=V4NET.0.0.3
+>>> ten-3xtra.test.ex in ""? no (end of list)
+>>> ten-3xtra.test.ex in "*"? yes (matched "*")
>>> IP address for ten-3xtra.test.ex matches calling address
+>>> Forward DNS security status: unverified
>>> host in hosts_connection_nolog? no (option unset)
>>> host in host_lookup? no (option unset)
>>> host in host_reject_connection? no (option unset)
@@ -158,7 +158,8 @@ MUNGED: ::1 will be omitted in what follows
>>> checking addresses for ten-1.test.ex
>>> V4NET.0.0.1 OK
>>> getting IP address for rhubarb
->>> no IP address found for host rhubarb (during SMTP connection from (rhubarb) [V4NET.0.0.1])
+>>> rhubarb in ""? no (end of list)
+>>> rhubarb in "*"? yes (matched "*")
LOG: rejected "EHLO rhubarb" from (rhubarb) [V4NET.0.0.1]
>>> host in hosts_connection_nolog? no (option unset)
>>> host in host_lookup? no (option unset)
@@ -178,7 +179,8 @@ MUNGED: ::1 will be omitted in what follows
>>> looking up host name for 99.99.99.99
>>> Test harness: host name lookup returns DEFER
>>> getting IP address for rhubarb
->>> no IP address found for host rhubarb (during SMTP connection from (rhubarb) [99.99.99.99])
+>>> rhubarb in ""? no (end of list)
+>>> rhubarb in "*"? yes (matched "*")
LOG: temporarily rejected "EHLO rhubarb" from (rhubarb) [99.99.99.99]
>>> host in hosts_connection_nolog? no (option unset)
>>> host in host_lookup? no (option unset)
@@ -264,7 +266,8 @@ MUNGED: ::1 will be omitted in what follows
>>> looking up host name for 99.99.99.99
>>> Test harness: host name lookup returns DEFER
>>> getting IP address for rhubarb
->>> no IP address found for host rhubarb (during SMTP connection from (rhubarb) [99.99.99.99])
+>>> rhubarb in ""? no (end of list)
+>>> rhubarb in "*"? yes (matched "*")
>>> require: condition test failed in ACL "rcpt"
>>> end of ACL "rcpt": not OK
LOG: H=(rhubarb) [99.99.99.99] F=<a@b> rejected RCPT <c@d>: helo not verified
diff --git a/test/stdout/0608 b/test/stdout/0608
new file mode 100644
index 000000000..9681d83d6
--- /dev/null
+++ b/test/stdout/0608
@@ -0,0 +1,14 @@
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> HELO localhost
+??? 250
+<<< 250 myhost.test.ex Hello localhost [127.0.0.1]
+End of script
+Connecting to 127.0.0.1 port 1225 ... connected
+??? 220
+<<< 220 myhost.test.ex ESMTP Exim x.yz Tue, 2 Mar 1999 09:44:33 +0000
+>>> HELO l-sec
+??? 250
+<<< 250 myhost.test.ex Hello localhost [127.0.0.1]
+End of script