From 1f4a55daf88541563ceaa66959acb9127604b15a Mon Sep 17 00:00:00 2001 From: Phil Pennock Date: Fri, 1 Jun 2012 10:15:14 -0400 Subject: [PATCH] DNSSEC babystep: dns_use_dnssec & $sender_host_dnssec --- doc/doc-docbook/spec.xfpt | 40 +++++++++++++++++++++++++ doc/doc-txt/ChangeLog | 4 +++ doc/doc-txt/NewStuff | 8 +++++ doc/doc-txt/OptionLists.txt | 2 ++ src/src/EDITME | 8 +++++ src/src/config.h.defaults | 1 + src/src/dns.c | 58 +++++++++++++++++++++++++++++++++++++ src/src/functions.h | 1 + src/src/globals.c | 2 ++ src/src/globals.h | 2 ++ src/src/host.c | 9 +++++- src/src/readconf.c | 1 + 12 files changed, 135 insertions(+), 1 deletion(-) 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 @@ -411,6 +411,14 @@ EXIM_MONITOR=eximon.bin # DISABLE_DKIM=yes +#------------------------------------------------------------------------------ +# 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 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); } @@ -394,6 +424,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 }, -- 2.30.2