Implement rfc6840 (AD flag processing) if using trusted name servers
authorjca <jca@openbsd.org>
Mon, 22 Nov 2021 20:18:27 +0000 (20:18 +0000)
committerjca <jca@openbsd.org>
Mon, 22 Nov 2021 20:18:27 +0000 (20:18 +0000)
libc can't do DNSSEC validation but it can ask a "security-aware"
resolver to do so.  Let's send queries with the AD flag set when
appropriate, and let applications look at the AD flag in responses in
a safe way, ie clear the AD flag if the resolvers aren't trusted.
By default we only trust resolvers if resolv.conf(5) only lists name
servers on localhost - the obvious candidates being unwind(8) and
unbound(8).  For non-localhost resolvers, an admin who trusts *all the
name servers* listed in resolv.conf(5) *and the network path leading to
them* can annotate this with "options trust-ad".

AD flag processing gives ssh -o VerifyHostkeyDNS=Yes a chance to fetch
SSHFP records in a secure manner, and tightens the situation for other
applications, eg those using RES_USE_DNSSEC for DANE.  It should be
noted that postfix currently assumes trusted name servers by default and
forces RES_TRUSTAD if available.

RES_TRUSTAD and "options trust-ad" were first introduced in glibc by
Florian Weimer.  Florian Obser (florian@) contributed various
improvements, fixed a bug and added automatic trust for name servers on
localhost.

ok florian@ phessler@

include/resolv.h
lib/libc/asr/asr.c
lib/libc/asr/asr_debug.c
lib/libc/asr/res_mkquery.c
lib/libc/asr/res_send_async.c
lib/libc/net/res_init.3
share/man/man5/resolv.conf.5

index fb02483..449779e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: resolv.h,v 1.22 2019/01/14 06:23:06 otto Exp $        */
+/*     $OpenBSD: resolv.h,v 1.23 2021/11/22 20:18:27 jca Exp $ */
 
 /*
  * Copyright (C) 1995, 1996, 1997, and 1998 WIDE Project.
@@ -191,6 +191,7 @@ struct __res_state_ext {
 /* DNSSEC extensions: use higher bit to avoid conflict with ISC use */
 #define        RES_USE_DNSSEC  0x20000000      /* use DNSSEC using OK bit in OPT */
 #define        RES_USE_CD      0x10000000      /* set Checking Disabled flag */
+#define        RES_TRUSTAD     0x80000000      /* Request AD, keep it in responses. */
 
 #define RES_DEFAULT    (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH)
 
index 8bcb61b..7cbf6aa 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: asr.c,v 1.66 2021/11/05 13:08:58 kn Exp $     */
+/*     $OpenBSD: asr.c,v 1.67 2021/11/22 20:18:27 jca Exp $    */
 /*
  * Copyright (c) 2010-2012 Eric Faurot <eric@openbsd.org>
  *
@@ -661,7 +661,8 @@ pass0(char **tok, int n, struct asr_ctx *ac)
                                d = strtonum(tok[i] + 6, 1, 16, &e);
                                if (e == NULL)
                                        ac->ac_ndots = d;
-                       }
+                       } else if (!strcmp(tok[i], "trust-ad"))
+                               ac->ac_options |= RES_TRUSTAD;
                }
        }
 }
@@ -672,7 +673,10 @@ pass0(char **tok, int n, struct asr_ctx *ac)
 static int
 asr_ctx_from_string(struct asr_ctx *ac, const char *str)
 {
-       char             buf[512], *ch;
+       struct sockaddr_in6     *sin6;
+       struct sockaddr_in      *sin;
+       int                      i, trustad;
+       char                     buf[512], *ch;
 
        asr_ctx_parse(ac, str);
 
@@ -702,6 +706,27 @@ asr_ctx_from_string(struct asr_ctx *ac, const char *str)
                                break;
                }
 
+       trustad = 1;
+       for (i = 0; i < ac->ac_nscount && trustad; i++) {
+               switch (ac->ac_ns[i]->sa_family) {
+               case AF_INET:
+                       sin = (struct sockaddr_in *)ac->ac_ns[i];
+                       if (sin->sin_addr.s_addr != htonl(INADDR_LOOPBACK))
+                               trustad = 0;
+                       break;
+               case AF_INET6:
+                       sin6 = (struct sockaddr_in6 *)ac->ac_ns[i];
+                       if (!IN6_IS_ADDR_LOOPBACK(&sin6->sin6_addr))
+                               trustad = 0;
+                       break;
+               default:
+                       trustad = 0;
+                       break;
+               }
+       }
+       if (trustad)
+               ac->ac_options |= RES_TRUSTAD;
+
        return (0);
 }
 
index f9378d1..791a067 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: asr_debug.c,v 1.27 2021/04/02 07:00:30 eric Exp $     */
+/*     $OpenBSD: asr_debug.c,v 1.28 2021/11/22 20:18:27 jca Exp $      */
 /*
  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
  *
@@ -286,6 +286,7 @@ _asr_dump_config(FILE *f, struct asr *a)
        PRINTOPT(RES_USE_EDNS0, "USE_EDNS0");
        PRINTOPT(RES_USE_DNSSEC, "USE_DNSSEC");
        PRINTOPT(RES_USE_CD, "USE_CD");
+       PRINTOPT(RES_TRUSTAD, "TRUSTAD");
        if (o)
                fprintf(f, " 0x%08x", o);
        fprintf(f, "\n");
index c3d5af3..3e02dbb 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: res_mkquery.c,v 1.13 2019/01/14 06:49:42 otto Exp $   */
+/*     $OpenBSD: res_mkquery.c,v 1.14 2021/11/22 20:18:27 jca Exp $    */
 /*
  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
  *
@@ -62,6 +62,8 @@ res_mkquery(int op, const char *dname, int class, int type,
                h.flags |= RD_MASK;
        if (ac->ac_options & RES_USE_CD)
                h.flags |= CD_MASK;
+       if (ac->ac_options & RES_TRUSTAD)
+               h.flags |= AD_MASK;
        h.qdcount = 1;
        if (ac->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
                h.arcount = 1;
index c5cc41f..a309070 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: res_send_async.c,v 1.39 2019/09/28 11:21:07 eric Exp $        */
+/*     $OpenBSD: res_send_async.c,v 1.40 2021/11/22 20:18:27 jca Exp $ */
 /*
  * Copyright (c) 2012 Eric Faurot <eric@openbsd.org>
  *
@@ -42,6 +42,7 @@ static int udp_recv(struct asr_query *);
 static int tcp_write(struct asr_query *);
 static int tcp_read(struct asr_query *);
 static int validate_packet(struct asr_query *);
+static void clear_ad(struct asr_result *);
 static int setup_query(struct asr_query *, const char *, const char *, int, int);
 static int ensure_ibuf(struct asr_query *, size_t);
 static int iter_ns(struct asr_query *);
@@ -258,6 +259,8 @@ res_send_async_run(struct asr_query *as, struct asr_result *ar)
                as->as.dns.ibuf = NULL;
                ar->ar_errno = 0;
                ar->ar_rcode = as->as.dns.rcode;
+               if (!(as->as_ctx->ac_options & RES_TRUSTAD))
+                       clear_ad(ar);
                async_set_state(as, ASR_STATE_HALT);
                break;
 
@@ -378,6 +381,9 @@ setup_query(struct asr_query *as, const char *name, const char *dom,
                h.flags |= RD_MASK;
        if (as->as_ctx->ac_options & RES_USE_CD)
                h.flags |= CD_MASK;
+       if (as->as_ctx->ac_options & RES_TRUSTAD)
+               h.flags |= AD_MASK;
+
        h.qdcount = 1;
        if (as->as_ctx->ac_options & (RES_USE_EDNS0 | RES_USE_DNSSEC))
                h.arcount = 1;
@@ -747,6 +753,21 @@ validate_packet(struct asr_query *as)
        return (-1);
 }
 
+/*
+ * Clear AD flag in the answer.
+ */
+static void
+clear_ad(struct asr_result *ar)
+{
+       struct asr_dns_header   *h;
+       uint16_t                 flags;
+
+       h = (struct asr_dns_header *)ar->ar_data;
+       flags = ntohs(h->flags);
+       flags &= ~(AD_MASK);
+       h->flags = htons(flags);
+}
+
 /*
  * Set the async context nameserver index to the next nameserver, cycling
  * over the list until the maximum retry counter is reached.  Return 0 on
index 4a4d095..03e6fca 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: res_init.3,v 1.4 2020/04/25 21:06:17 jca Exp $
+.\"    $OpenBSD: res_init.3,v 1.5 2021/11/22 20:18:27 jca Exp $
 .\"
 .\" Copyright (c) 1985, 1991, 1993
 .\"    The Regents of the University of California.  All rights reserved.
@@ -27,7 +27,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd $Mdocdate: April 25 2020 $
+.Dd $Mdocdate: November 22 2021 $
 .Dt RES_INIT 3
 .Os
 .Sh NAME
@@ -179,6 +179,18 @@ This option has no effect.
 In the past, it turned off the legacy
 .Ev HOSTALIASES
 feature.
+.It Dv RES_TRUSTAD
+If set, the resolver routines will set the AD flag in DNS queries and
+preserve the value of the AD flag in DNS replies.
+If not set, the resolver routines will clear the AD flag in responses.
+Direct use of this option to enable AD bit processing is discouraged.
+Instead the use of trusted name servers should be annotated with
+.Dq options trust-ad
+in
+.Xr resolv.conf 5 .
+This option is automatically enabled if
+.Xr resolv.conf 5
+only lists name servers on localhost.
 .It Dv RES_USE_INET6
 With this option
 .Xr gethostbyname 3
index 8d3b91c..5808cc6 100644 (file)
@@ -1,4 +1,4 @@
-.\"    $OpenBSD: resolv.conf.5,v 1.62 2021/08/24 07:30:32 florian Exp $
+.\"    $OpenBSD: resolv.conf.5,v 1.63 2021/11/22 20:18:27 jca Exp $
 .\"    $NetBSD: resolv.conf.5,v 1.7 1996/03/06 18:22:16 scottr Exp $
 .\"
 .\" Copyright (c) 1986, 1991 The Regents of the University of California.
@@ -30,7 +30,7 @@
 .\"
 .\"     @(#)resolver.5 5.12 (Berkeley) 5/10/91
 .\"
-.Dd $Mdocdate: August 24 2021 $
+.Dd $Mdocdate: November 22 2021 $
 .Dt RESOLV.CONF 5
 .Os
 .Sh NAME
@@ -259,6 +259,18 @@ first as an absolute name before any search list elements are appended to it.
 .It Cm tcp
 Forces the use of TCP for queries.
 Normal behaviour is to query via UDP but fall back to TCP on failure.
+.It Cm trust-ad
+A name server indicating that it performed DNSSEC validation by setting the
+Authentic Data (AD) flag in the answer can only be trusted if the
+name server itself is trusted and the network path is trusted.
+Generally this is not the case and the AD flag is cleared in the answer.
+The
+.Cm trust-ad
+option lets the system administrator indicate that the name server and the
+network path are trusted.
+This option is automatically enabled if
+.Nm resolv.conf
+only lists name servers on localhost.
 .El
 .El
 .Pp