On Wed, Apr 15, 2020 at 10:36:26AM +0200, Christian wrote:
> > I don't yet have access to systems with this recent a glibc to confirm
> > the above, but this is likely relevant to Postfix administrators who
> > enable DANE. You may need to explicitly add the "trust-ad" option to
> > your /etc/resolv.conf, while making sure that all the listed nameservers
> > are local (loopback interface).
>
> I don't have a glibc 2.31 right now, otherwise I would try it.
With a bit of luck, someone will step forward.
> Without setting the "trust-ad" option someone testing it would get the
> same symptoms as I did on musl-libc?
That's my working hypothesis.
> Hence DNSSEC information would be missing in resolver response and
> silently deactivating outgoing DANE?
Yes. But I have an untested patch for that (it is only effective if
Postfix running on a system with Glibc >= 2.31 was actually compiled
against glibc >= 2.31).
It'd be great if the patch got a code review and some testing.
--
Viktor.
diff --git a/src/dns/dns.h b/src/dns/dns.h
index f758e44a..b69cd9fe 100644
--- a/src/dns/dns.h
+++ b/src/dns/dns.h
@@ -55,20 +55,25 @@
#endif
/*
- * Disable DNSSEC at compile-time even if RES_USE_DNSSEC is available
+ * Disable DNSSEC at compile-time even if RES_USE_DNSSEC is available.
*/
#ifdef NO_DNSSEC
#undef RES_USE_DNSSEC
+#undef RES_TRUSTAD
#endif
/*
- * Compatibility with systems that lack RES_USE_DNSSEC and RES_USE_EDNS0
+ * Compatibility with systems that lack RES_USE_DNSSEC, RES_USE_EDNS0 or
+ * RES_TRUSTAD.
*/
#ifndef RES_USE_DNSSEC
#define RES_USE_DNSSEC 0
#endif
#ifndef RES_USE_EDNS0
#define RES_USE_EDNS0 0
+#endif
+#ifndef RES_TRUSTAD
+#define RES_TRUSTAD 0
#endif
/*-
@@ -239,6 +244,8 @@ extern int dns_lookup_rv(const char *, unsigned, DNS_RR **, VSTRING *,
dns_lookup_rv((name), (rflags), (list), (fqdn), (why), (int *) 0, \
(lflags), (ltype))
+extern int dns_dnssec_available(void);
+
/*
* Request flags.
*/
diff --git a/src/dns/dns_lookup.c b/src/dns/dns_lookup.c
index 17377530..0ce0f802 100644
--- a/src/dns/dns_lookup.c
+++ b/src/dns/dns_lookup.c
@@ -74,6 +74,8 @@
/* VSTRING *why;
/* int *rcode;
/* unsigned lflags;
+/*
+/* int dns_dnssec_available()
/* DESCRIPTION
/* dns_lookup() looks up DNS resource records. When requested to
/* look up data other than type CNAME, it will follow a limited
@@ -89,6 +91,10 @@
/* dns_lookup_x, dns_lookup_r(), dns_lookup_rl() and dns_lookup_rv()
/* accept or return additional information.
/*
+/* dns_dnssec_available() returns true when a (presumably) trusted
+/* AD bit can be solicited from the configured nameserver(s) and is
+/* not censored by the stub resolver library.
+/*
/* The var_dns_ncache_ttl_fix variable controls a workaround
/* for res_search(3) implementations that break the
/* DNS_REQ_FLAG_NCACHE_TTL feature. The workaround does not
@@ -455,6 +461,23 @@ static int dns_query(const char *name, int type, unsigned flags,
if ((flags & USER_FLAGS) != flags)
msg_panic("dns_query: bad flags: %d", flags);
+ if (flags & RES_USE_DNSSEC) {
+ int avail = dns_dnssec_available();
+
+#if RES_TRUSTAD != 0
+
+ /*
+ * On systems that implement RES_TRUSTAD, we can't elicit the AD bit
+ * via RES_USE_DNSSEC. The AD bit is unconditionally solicited or
+ * else stripped via the "trust-ad" option in /etc/resolv.conf.
+ */
+ flags &= ~RES_USE_DNSSEC;
+#else
+ if (!avail)
+ flags &= ~RES_USE_DNSSEC;
+#endif
+ }
+
/*
* Set extra options that aren't exposed to the application.
*/
@@ -560,8 +583,8 @@ static int dns_query(const char *name, int type, unsigned flags,
* Initialize the reply structure. Some structure members are filled on
* the fly while the reply is being parsed. Coerce AD bit to boolean.
*/
-#if RES_USE_DNSSEC != 0
- reply->dnssec_ad = (flags & RES_USE_DNSSEC) ? !!reply_header->ad : 0;
+#if (RES_USE_DNSSEC != 0) || (RES_TRUSTAD != 0)
+ reply->dnssec_ad = !!reply_header->ad;
#else
reply->dnssec_ad = 0;
#endif
@@ -1053,7 +1076,6 @@ int dns_lookup_x(const char *name, unsigned type, unsigned flags,
case DNS_RECURSE:
if (msg_verbose)
msg_info("dns_lookup: %s aliased to %s", name, cname);
-#if RES_USE_DNSSEC
/*
* Once an intermediate CNAME reply is not validated, all
@@ -1062,7 +1084,6 @@ int dns_lookup_x(const char *name, unsigned type, unsigned flags,
*/
if (maybe_secure == 0)
flags &= ~RES_USE_DNSSEC;
-#endif
name = cname;
}
}
@@ -1198,3 +1219,55 @@ int dns_lookup_rv(const char *name, unsigned flags, DNS_RR **rrlist,
vstring_free(hpref_rtext);
return (status);
}
+
+/* dns_dnssec_available - can the stub resolver report a trusted AD bit */
+
+int dns_dnssec_available(void)
+{
+ static int available = -1;
+
+ if (available >= 0)
+ return (available);
+
+#ifdef NO_DNSSEC
+ available = 0;
+#elif (RES_USE_DNSSEC == 0) && (RES_TRUSTAD == 0)
+ msg_warn("DNSSEC lookups are not supported by the C-library DNS resolver");
+ available = 0;
+#else
+ available = 1;
+
+ /*
+ * Initialize the name service.
+ */
+ if ((_res.options & RES_INIT) == 0 && res_init() < 0) {
+ msg_warn("DNSSEC lookups are not possible,"
+ " name service initialization failure");
+ available = 0;
+ }
+#if RES_TRUSTAD != 0
+
+ /*
+ * On systems with GLIBC >= 2.31, the AD bit is solicited or else
+ * censored via the "trust-ad" resolv.conf option. On such systems we
+ * gain nothing by setting RES_USE_DNSSEC and RES_USE_EDNS0, since the AD
+ * bit will still be censored unless RES_TRUSTAD is set. Just setting
+ * "trust-ad" suffices to solicit the nameserver's "AD" bit, and is more
+ * efficient than using RES_USE_DNSSEC.
+ *
+ * Since we don't know what nameservers appear in /etc/resolv.conf, it is
+ * not our job to set this bit, but we can issue a warning if it is not
+ * set, and the user is requesting RES_USE_DNSSEC.
+ */
+ if ((_res.options & RES_TRUSTAD) == 0) {
+ msg_warn("DNSSEC lookups are not possible, because the \"trust-ad\""
+ " option is not set in /etc/resolv.conf.");
+ msg_warn("The \"trust-ad\" option should only be set when the only"
+ " nameserver listed in /etc/resolv.conf is a local DNSSEC-"
+ "validating resolver listening on the loopback interface");
+ available = 0;
+ }
+#endif
+#endif
+ return (available);
+}
diff --git a/src/tls/tls_dane.c b/src/tls/tls_dane.c
index 013426b1..44b9e845 100644
--- a/src/tls/tls_dane.c
+++ b/src/tls/tls_dane.c
@@ -207,7 +207,7 @@
#undef DANE_TLSA_SUPPORT
-#if defined(TLSEXT_MAXLEN_host_name) && RES_USE_DNSSEC && RES_USE_EDNS0
+#if defined(TLSEXT_MAXLEN_host_name)
#define DANE_TLSA_SUPPORT
static int dane_tlsa_support = 1;
@@ -418,6 +418,15 @@ static void dane_init(void)
/* Don't report old news */
ERR_clear_error();
+ /*
+ * DANE TLSA support may require the "trust-ad" bit to be set in the
+ * resolver options with GLIBC >= 2.31.
+ */
+ if (!dns_dnssec_available()) {
+ msg_warn("No trusted validating resolvers, no DANE support");
+ dane_tlsa_support = 0;
+ }
+
/*
* DANE TLSA support requires working DANE digests.
*/