summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/doc-docbook/spec.xfpt40
-rw-r--r--doc/doc-txt/ChangeLog4
-rw-r--r--doc/doc-txt/NewStuff8
-rw-r--r--doc/doc-txt/OptionLists.txt2
-rw-r--r--src/src/EDITME8
-rw-r--r--src/src/config.h.defaults1
-rw-r--r--src/src/dns.c58
-rw-r--r--src/src/functions.h1
-rw-r--r--src/src/globals.c2
-rw-r--r--src/src/globals.h2
-rw-r--r--src/src/host.c9
-rw-r--r--src/src/readconf.c1
12 files changed, 135 insertions, 1 deletions
diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index 78d5b0b18..64aac1ae5 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -11687,6 +11687,31 @@ driver that successfully authenticated the client from which the message was
received. It is empty if there was no successful authentication. See also
&$authenticated_id$&.
+.new
+.vitem &$sender_host_dnssec$&
+.vindex "&$sender_host_dnssec$&"
+If &$sender_host_name$& has been populated (by reference, &%hosts_lookup%& or
+otherwise) then this boolean will have been set true if, and only if, the
+resolver library states that the reverse DNS was authenticated data. At all
+other times, this variable is false.
+
+It is likely that you will need to coerce DNSSEC support on in the resolver
+library, by setting:
+.code
+dns_use_dnssec = 1
+.endd
+
+Exim does not perform DNSSEC validation itself, instead leaving that to a
+validating resolver (eg, unbound, or bind with suitable configuration).
+
+Exim does not (currently) check to see if the forward DNS was also secured
+with DNSSEC, only the reverse DNS.
+
+If you have changed &%host_lookup_order%& so that &`bydns`& is not the first
+mechanism in the list, then this variable will be false.
+.wen
+
+
.vitem &$sender_host_name$&
.vindex "&$sender_host_name$&"
When a message is received from a remote host, this variable contains the
@@ -12836,6 +12861,9 @@ See also the &'Policy controls'& section above.
.row &%dns_ipv4_lookup%& "only v4 lookup for these domains"
.row &%dns_retrans%& "parameter for resolver"
.row &%dns_retry%& "parameter for resolver"
+.new
+.row &%dns_use_dnssec%& "parameter for resolver"
+.wen
.row &%dns_use_edns0%& "parameter for resolver"
.row &%hold_domains%& "hold delivery for these domains"
.row &%local_interfaces%& "for routing checks"
@@ -13476,6 +13504,18 @@ to set in them.
See &%dns_retrans%& above.
+.new
+.option dns_use_dnssec main integer -1
+.cindex "DNS" "resolver options"
+.cindex "DNS" "DNSSEC"
+If this option is set to a non-negative number then Exim will initialise the
+DNS resolver library to either use or not use DNSSEC, overriding the system
+default. A value of 0 coerces DNSSEC off, a value of 1 coerces DNSSEC on.
+
+If the resolver library does not support DNSSEC then this option has no effect.
+.wen
+
+
.option dns_use_edns0 main integer -1
.cindex "DNS" "resolver options"
.cindex "DNS" "EDNS0"
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index bfeaa4293..34f940592 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -12,6 +12,10 @@ PP/02 Make -n do something, by making it not do something.
PP/03 Added tls_dh_min_bits SMTP transport driver option, only honoured
by GnuTLS.
+PP/04 First step towards DNSSEC, provide $sender_host_dnssec for
+ $sender_host_name and config options to manage this, and basic check
+ routines.
+
Exim version 4.80
-----------------
diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff
index be8285b67..093feee72 100644
--- a/doc/doc-txt/NewStuff
+++ b/doc/doc-txt/NewStuff
@@ -31,6 +31,14 @@ Version 4.81
Unless you really know what you are doing, leave it alone.
+ 4. If not built with DISABLE_DNSSEC, Exim now has the main option
+ dns_use_dnssec; if set to 1 then Exim will initialise the resolver library
+ to send the DO flag to your recursive resolver. If you have a recursive
+ resolver, which can set the Authenticated Data (AD) flag in results, Exim
+ can now detect this.
+
+ Current status: work-in-progress; $sender_host_dnssec variable added.
+
Version 4.80
------------
diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt
index b8e8599ed..20d8dbdc5 100644
--- a/doc/doc-txt/OptionLists.txt
+++ b/doc/doc-txt/OptionLists.txt
@@ -180,6 +180,7 @@ dns_qualify_single boolean true smtp
dns_retrans time 0s main 1.60
dns_retry integer 0 main 1.60
dns_search_parents boolean false smtp
+dns_use_dnssec integer -1 main 4.81
dns_use_edns0 integer -1 main 4.76
domains domain list unset routers 4.00
driver string unset authenticators
@@ -840,6 +841,7 @@ DEFAULT_CRYPT optional default crypt() function
DELIVER_IN_BUFFER_SIZE optional*
DELIVER_OUT_BUFFER_SIZE optional*
DISABLE_DKIM optional disables DKIM support
+DISABLE_DNSSEC optional disables attempts to use DNSSEC
DISABLE_D_OPTION optional disables -D option
ERRNO_QUOTA optional* error code for system quota failures
EXICYCLOG_MAX optional number of old log files to keep
diff --git a/src/src/EDITME b/src/src/EDITME
index 95857c707..95a0c02f4 100644
--- a/src/src/EDITME
+++ b/src/src/EDITME
@@ -412,6 +412,14 @@ EXIM_MONITOR=eximon.bin
#------------------------------------------------------------------------------
+# By default, Exim has support for checking the AD bit in a DNS response, to
+# determine if DNSSEC validation was successful. If your system libraries
+# do not support that bit, then set DISABLE_DNSSEC to "yes"
+
+# DISABLE_DNSSEC=yes
+
+
+#------------------------------------------------------------------------------
# Compiling Exim with experimental features. These are documented in
# experimental-spec.txt. "Experimental" means that the way these features are
# implemented may still change. Backward compatibility is not guaranteed.
diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
index f02aef12c..00168f84a 100644
--- a/src/src/config.h.defaults
+++ b/src/src/config.h.defaults
@@ -41,6 +41,7 @@ it's a default value. */
#define DELIVER_IN_BUFFER_SIZE 8192
#define DELIVER_OUT_BUFFER_SIZE 8192
#define DISABLE_DKIM
+#define DISABLE_DNSSEC
#define DISABLE_D_OPTION
#define ENABLE_DISABLE_FSYNC
diff --git a/src/src/dns.c b/src/src/dns.c
index ae76e9e3f..95db52686 100644
--- a/src/src/dns.c
+++ b/src/src/dns.c
@@ -201,6 +201,36 @@ if (dns_use_edns0 >= 0)
dns_use_edns0 ? "" : "un");
#endif
+#ifndef DISABLE_DNSSEC
+# ifdef RES_USE_DNSSEC
+# ifndef RES_USE_EDNS0
+# error Have RES_USE_DNSSEC but not RES_USE_EDNS0? Something hinky ...
+# endif
+if (dns_use_dnssec >= 0)
+ {
+ if (dns_use_edns0 == 0 && dns_use_dnssec != 0)
+ {
+ DEBUG(D_resolver)
+ debug_printf("CONFLICT: dns_use_edns0 forced false, dns_use_dnssec forced true!\n");
+ }
+ else
+ {
+ if (dns_use_dnssec)
+ resp->options |= RES_USE_DNSSEC;
+ else
+ resp->options &= ~RES_USE_DNSSEC;
+ DEBUG(D_resolver) debug_printf("Coerced resolver DNSSEC support %s.\n",
+ dns_use_dnssec ? "on" : "off");
+ }
+ }
+# else
+if (dns_use_dnssec >= 0)
+ DEBUG(D_resolver)
+ debug_printf("Unable to %sset DNSSEC without resolver support.\n",
+ dns_use_dnssec ? "" : "un");
+# endif
+#endif /* DISABLE_DNSSEC */
+
os_put_dns_resolver_res(resp);
}
@@ -395,6 +425,34 @@ return &(dnss->srr);
/*************************************************
+* Return whether AD bit set in DNS result *
+*************************************************/
+
+/* We do not perform DNSSEC work ourselves; if the administrator has installed
+a verifying resolver which sets AD as appropriate, though, we'll use that.
+(AD = Authentic Data)
+
+Argument: pointer to dns answer block
+Returns: bool indicating presence of AD bit
+*/
+
+BOOL
+dns_is_secure(dns_answer *dnsa)
+{
+#ifdef DISABLE_DNSSEC
+DEBUG(D_dns)
+ debug_printf("DNSSEC support disabled at build-time; dns_is_secure() false\n");
+return FALSE;
+#else
+HEADER *h = (HEADER *)dnsa->answer;
+return h->ad ? TRUE : FALSE;
+#endif
+}
+
+
+
+
+/*************************************************
* Turn DNS type into text *
*************************************************/
diff --git a/src/src/functions.h b/src/src/functions.h
index 2758a4aec..5616db2de 100644
--- a/src/src/functions.h
+++ b/src/src/functions.h
@@ -104,6 +104,7 @@ extern dns_address *dns_address_from_rr(dns_answer *, dns_record *);
extern void dns_build_reverse(uschar *, uschar *);
extern void dns_init(BOOL, BOOL);
extern int dns_basic_lookup(dns_answer *, uschar *, int);
+extern BOOL dns_is_secure(dns_answer *);
extern int dns_lookup(dns_answer *, uschar *, int, uschar **);
extern int dns_special_lookup(dns_answer *, uschar *, int, uschar **);
extern dns_record *dns_next_rr(dns_answer *, dns_scan *, int);
diff --git a/src/src/globals.c b/src/src/globals.c
index f29fb3c49..d5cb6c15f 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -549,6 +549,7 @@ BOOL dns_csa_use_reverse = TRUE;
uschar *dns_ipv4_lookup = NULL;
int dns_retrans = 0;
int dns_retry = 0;
+int dns_use_dnssec = -1; /* <0 = not coerced */
int dns_use_edns0 = -1; /* <0 = not coerced */
uschar *dnslist_domain = NULL;
uschar *dnslist_matched = NULL;
@@ -1066,6 +1067,7 @@ uschar **sender_host_aliases = &no_aliases;
uschar *sender_host_address = NULL;
uschar *sender_host_authenticated = NULL;
unsigned int sender_host_cache[(MAX_NAMED_LIST * 2)/32];
+BOOL sender_host_dnssec = FALSE;
uschar *sender_host_name = NULL;
int sender_host_port = 0;
BOOL sender_host_notsocket = FALSE;
diff --git a/src/src/globals.h b/src/src/globals.h
index b1ec5a20f..c61158e6d 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -335,6 +335,7 @@ extern BOOL dns_csa_use_reverse; /* Check CSA in reverse DNS? (non-standar
extern uschar *dns_ipv4_lookup; /* For these domains, don't look for AAAA (or A6) */
extern int dns_retrans; /* Retransmission time setting */
extern int dns_retry; /* Number of retries */
+extern int dns_use_dnssec; /* When constructing DNS query, set DO flag */
extern int dns_use_edns0; /* Coerce EDNS0 support on/off in resolver. */
extern uschar *dnslist_domain; /* DNS (black) list domain */
extern uschar *dnslist_matched; /* DNS (black) list matched key */
@@ -660,6 +661,7 @@ extern uschar *sender_fullhost; /* Sender host name + address */
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 */
+extern BOOL sender_host_dnssec; /* true if sender_host_name verified in DNSSEC */
extern BOOL sender_host_notsocket; /* Set for -bs and -bS */
extern BOOL sender_host_unknown; /* TRUE for -bs and -bS except inetd */
extern uschar *sender_ident; /* Sender identity via RFC 1413 */
diff --git a/src/src/host.c b/src/src/host.c
index 9dc9c9a3e..03d944334 100644
--- a/src/src/host.c
+++ b/src/src/host.c
@@ -1597,7 +1597,7 @@ dns_record *rr;
dns_answer dnsa;
dns_scan dnss;
-host_lookup_deferred = host_lookup_failed = FALSE;
+sender_host_dnssec = host_lookup_deferred = host_lookup_failed = FALSE;
HDEBUG(D_host_lookup)
debug_printf("looking up host name for %s\n", sender_host_address);
@@ -1639,6 +1639,13 @@ while ((ordername = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))
int count = 0;
int old_pool = store_pool;
+ /* Ideally we'd check DNSSEC both forward and reverse, but we use the
+ gethost* routines for forward, so can't do that unless/until we rewrite. */
+ sender_host_dnssec = dns_is_secure(&dnsa);
+ DEBUG(D_dns)
+ debug_printf("Reverse DNS security status: %s\n",
+ sender_host_dnssec ? "DNSSEC verified (AD)" : "unverified");
+
store_pool = POOL_PERM; /* Save names in permanent storage */
for (rr = dns_next_rr(&dnsa, &dnss, RESET_ANSWERS);
diff --git a/src/src/readconf.c b/src/src/readconf.c
index 568990a70..553f2e455 100644
--- a/src/src/readconf.c
+++ b/src/src/readconf.c
@@ -216,6 +216,7 @@ static optionlist optionlist_config[] = {
{ "dns_ipv4_lookup", opt_stringptr, &dns_ipv4_lookup },
{ "dns_retrans", opt_time, &dns_retrans },
{ "dns_retry", opt_int, &dns_retry },
+ { "dns_use_dnssec", opt_int, &dns_use_dnssec },
{ "dns_use_edns0", opt_int, &dns_use_edns0 },
/* This option is now a no-op, retained for compability */
{ "drop_cr", opt_bool, &drop_cr },