On Sat, Nov 20, 2021 at 02:40:59PM +0100, Otto Moerbeek wrote:

> On Sat, Nov 20, 2021 at 12:20:32PM +0100, Florian Obser wrote:
> 
> > The Authentic Data (AD) flag indicates that the nameserver validated
> > the response using DNSSEC. For clients to trust this the nameserver
> > and the path to the nameserver must be trusted. In the general case
> > this is not true.
> > 
> > We can trust localhost so we set the AD flag on queries to request
> > validation and preserve the AD flag in answers. (*)
> > 
> > If, and only if, trusted nameservers (that are not on localhost) have
> > been added to resolv.conf and the path to them is secure the trust-ad
> > flag may be used to request validation from them and trust answers with
> > the AD flag set.
> > 
> > The trust-ad option first appeared in glibc 2.31.
> > ( https://gnutoolchain-gerrit.osci.io/r/c/glibc/+/461 and
> > https://man7.org/linux/man-pages/man5/resolv.conf.5.html )
> > 
> > Thomas Habets (thomas at habets.se) pointed out on bugs@ that
> > VerifyHostKeyDNS in ssh only works with unwind (which is good) but
> > only by accident (which is bad).
> > https://marc.info/?t=163717495900003&r=1&w=2
> > 
> > *) This is for people running unwind, unbound or some other validating
> > resolver on localhost. Yes, it is possible that someone set up some sort
> > of forwarder where they trust the DNS answers but not that they are
> > DNSSEC validated. This feels contrived and a case of DON'T DO THAT!
> > 
> > OK?
> 
> I like this much better than the sketch I posted on bugs@
> 
> Two comment wrt the docs inline.
> 
> Code looks and tests good.
> 
>       -Otto
> 
> > 
> > diff --git include/resolv.h include/resolv.h
> > index fb02483871e..2422deb5484 100644
> > --- include/resolv.h
> > +++ include/resolv.h
> > @@ -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)
> >  
> > diff --git lib/libc/asr/asr.c lib/libc/asr/asr.c
> > index 8bcb61b6000..77bc3854420 100644
> > --- lib/libc/asr/asr.c
> > +++ lib/libc/asr/asr.c
> > @@ -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);
> >  }
> >  
> > diff --git lib/libc/asr/getrrsetbyname_async.c 
> > lib/libc/asr/getrrsetbyname_async.c
> > index e5e7c23c261..06a998b0381 100644
> > --- lib/libc/asr/getrrsetbyname_async.c
> > +++ lib/libc/asr/getrrsetbyname_async.c
> > @@ -32,7 +32,7 @@
> >  #include "asr_private.h"
> >  
> >  static int getrrsetbyname_async_run(struct asr_query *, struct asr_result 
> > *);
> > -static void get_response(struct asr_result *, const char *, int);
> > +static void get_response(struct asr_result *, const char *, int, int);
> >  
> >  struct asr_query *
> >  getrrsetbyname_async(const char *hostname, unsigned int rdclass,
> > @@ -150,7 +150,8 @@ getrrsetbyname_async_run(struct asr_query *as, struct 
> > asr_result *ar)
> >                     break;
> >             }
> >  
> > -           get_response(ar, ar->ar_data, ar->ar_datalen);
> > +           get_response(ar, ar->ar_data, ar->ar_datalen,
> > +               as->as_ctx->ac_options & RES_TRUSTAD);
> >             free(ar->ar_data);
> >             async_set_state(as, ASR_STATE_HALT);
> >             break;
> > @@ -255,7 +256,7 @@ static void free_dns_response(struct dns_response *);
> >  static int count_dns_rr(struct dns_rr *, u_int16_t, u_int16_t);
> >  
> >  static void
> > -get_response(struct asr_result *ar, const char *pkt, int pktlen)
> > +get_response(struct asr_result *ar, const char *pkt, int pktlen, int 
> > trustad)
> >  {
> >     struct rrsetinfo *rrset = NULL;
> >     struct dns_response *response = NULL;
> > @@ -287,7 +288,7 @@ get_response(struct asr_result *ar, const char *pkt, 
> > int pktlen)
> >     rrset->rri_nrdatas = response->header.ancount;
> >  
> >     /* check for authenticated data */
> > -   if (response->header.ad == 1)
> > +   if (response->header.ad == 1 && trustad)
> >             rrset->rri_flags |= RRSET_VALIDATED;
> >  
> >     /* copy name from answer section */
> > diff --git lib/libc/asr/res_mkquery.c lib/libc/asr/res_mkquery.c
> > index c3d5af30f29..97b965e4c2a 100644
> > --- lib/libc/asr/res_mkquery.c
> > +++ lib/libc/asr/res_mkquery.c
> > @@ -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;
> > diff --git lib/libc/asr/res_send_async.c lib/libc/asr/res_send_async.c
> > index c5cc41f56df..d6fba28b6f7 100644
> > --- lib/libc/asr/res_send_async.c
> > +++ lib/libc/asr/res_send_async.c
> > @@ -378,6 +378,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;
> > diff --git share/man/man5/resolv.conf.5 share/man/man5/resolv.conf.5
> > index 8d3b91c0832..ac64d3e6fd6 100644
> > --- share/man/man5/resolv.conf.5
> > +++ share/man/man5/resolv.conf.5
> > @@ -259,6 +259,12 @@ 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
> > +Request DNSSEC validated data from the nameservers and preserve the
> > +Authentic Data (AD) flag in responses.
> 
> > +Otherwise the Authentic Data (AD) flag is removed from responses.
> 
> This is not what happens (though the DNS header itself is not exposed
> in the API). Maybe describe it as:
> 
> Request DNSSEC validated data from the nameservers and evaluate the AD
> flag in responses.
> 
> > +The nameservers and the network path to them must be trusted.
> 
> Maybe:
> 
> Only set this flag if the nameservers and the network paths to them are
> trusted.

The idea being that it shoud be clear that asr is not making the
trust decision, but the system admin is.

        -Otto

Reply via email to