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.

> +This is the default for nameservers on localhost.
>  .El
>  .El
>  .Pp
> 
> -- 
> I'm not entirely sure you are real.
> 

Reply via email to