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.
      */

Reply via email to