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