DNSSEC babystep: dns_use_dnssec & $sender_host_dnssec
authorPhil Pennock <pdp@exim.org>
Fri, 1 Jun 2012 14:15:14 +0000 (10:15 -0400)
committerPhil Pennock <pdp@exim.org>
Fri, 1 Jun 2012 14:15:14 +0000 (10:15 -0400)
12 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
doc/doc-txt/OptionLists.txt
src/src/EDITME
src/src/config.h.defaults
src/src/dns.c
src/src/functions.h
src/src/globals.c
src/src/globals.h
src/src/host.c
src/src/readconf.c

index 78d5b0b..64aac1a 100644 (file)
@@ -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"
index bfeaa42..34f9405 100644 (file)
@@ -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
 -----------------
index be8285b..093feee 100644 (file)
@@ -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
 ------------
index b8e8599..20d8dbd 100644 (file)
@@ -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
index 95857c7..95a0c02 100644 (file)
@@ -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.
index f02aef1..00168f8 100644 (file)
@@ -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
index ae76e9e..95db526 100644 (file)
@@ -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             *
 *************************************************/
 
index 2758a4a..5616db2 100644 (file)
@@ -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);
index f29fb3c..d5cb6c1 100644 (file)
@@ -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;
index b1ec5a2..c61158e 100644 (file)
@@ -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 */
index 9dc9c9a..03d9443 100644 (file)
@@ -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);
index 568990a..553f2e4 100644 (file)
@@ -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 },