]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
Use DS records as trust anchors, not DNSKEYs.
authorSimon Kelley <simon@thekelleys.org.uk>
Tue, 11 Feb 2014 11:07:22 +0000 (11:07 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Tue, 11 Feb 2014 11:07:22 +0000 (11:07 +0000)
This allows us to query for the root zone DNSKEY RRset and validate
it, thus automatically handling KSK rollover.

man/dnsmasq.8
src/cache.c
src/dnsmasq.c
src/dnsmasq.h
src/option.c
src/rfc1035.c
src/util.c
trust-anchors.conf

index 687d921efe9394e66fb467195839d9536ed7db80..f3c5de1f347e6d7d7431666774d06a8be4bec157 100644 (file)
@@ -13,7 +13,10 @@ Dnsmasq accepts DNS queries and either answers them from a small, local,
 cache or forwards them to a real, recursive, DNS server. It loads the
 contents of /etc/hosts so that local hostnames
 which do not appear in the global DNS can be resolved and also answers
-DNS queries for DHCP configured hosts. It can also act as the authoritative DNS server for one or more domains, allowing local names to appear in the global DNS.
+DNS queries for DHCP configured hosts. It can also act as the
+authoritative DNS server for one or more domains, allowing local names
+to appear in the global DNS. It can be configured to do DNSSEC
+validation.
 .PP
 The dnsmasq DHCP server supports static address assignments and multiple
 networks. It automatically
@@ -587,12 +590,15 @@ validation by clients more efficient. Note that validation by clients is the mos
 clients unable to do validation, use of the AD bit set by dnsmasq is useful, provided that the network between 
 the dnsmasq server and the client is trusted. Dnsmasq must be compiled with HAVE_DNSSEC enabled, and DNSSEC
 trust anchors provided, see 
-.B --dnsskey.
+.B --trust-anchor.
 Because the DNSSEC validation process uses the cache, it is not permitted to reduce the cache size below the default when DNSSEC is enabled.
 .TP
-.B --dnskey=[<class>],<domain>,<flags>,<algorithm>,<base64-key>
-Provide DNSKEY records to act a trust anchors for DNSSEC validation. Typically these will be the keys for root zone,
-but trust anchors for limited domains are also possible. 
+.B --trust-anchor=[<class>],<domain>,<key-tag>,<algorithm>,<digest-type>,<digest>
+Provide DS records to act a trust anchors for DNSSEC
+validation. Typically these will be the DS record(s) for Zone Signing
+key(s) of the root zone,
+but trust anchors for limited domains are also possible. The current
+root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml 
 .TP
 .B --proxy-dnssec
 Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it.  This is an 
@@ -601,7 +607,10 @@ dnsmasq and the upstream servers, and the trustworthiness of the upstream server
 .TP
 .B --dnssec-debug
 Set debugging mode for the DNSSEC validation, set the Checking Disabled bit on upstream queries, 
-and don't convert BOGUS replies to SERVFAIL responses. 
+and don't convert replies which do not validate to responses with
+a return code of SERVFAIL. Note that
+setting this may affect DNS behaviour in bad ways, it is not an
+extra-logging flag and should not be set in production.
 .TP
 .B --auth-zone=<domain>[,<subnet>[/<prefix length>][,<subnet>[/<prefix length>].....]]
 Define a DNS zone for which dnsmasq acts as authoritative server. Locally defined DNS records which are in the domain
index 940763664cb59f07c035ee7cebc15bcc42f9a72a..93865d96938a146f23680bc56bf986d7385094e2 100644 (file)
@@ -985,7 +985,7 @@ void cache_reload(void)
   struct cname *a;
   struct interface_name *intr;
 #ifdef HAVE_DNSSEC
-  struct dnskey *key;
+  struct ds_config *ds;
 #endif
 
   cache_inserted = cache_live_freed = 0;
@@ -1031,17 +1031,17 @@ void cache_reload(void)
        }
 
 #ifdef HAVE_DNSSEC
-  for (key = daemon->dnskeys; key; key = key->next)
+  for (ds = daemon->ds; ds; ds = ds->next)
     if ((cache = whine_malloc(sizeof(struct crec))) &&
-       (cache->addr.key.keydata = blockdata_alloc(key->key, key->keylen)))
+       (cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
       {
-       cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP;
-       cache->name.namep = key->name;
-       cache->addr.key.keylen = key->keylen;
-       cache->addr.key.algo = key->algo;
-       cache->addr.key.flags = key->flags;
-       cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
-       cache->uid = key->class;
+       cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
+       cache->name.namep = ds->name;
+       cache->addr.ds.keylen = ds->digestlen;
+       cache->addr.ds.algo = ds->algo;
+       cache->addr.ds.keytag = ds->keytag;
+       cache->addr.ds.digest = ds->digest_type;
+       cache->uid = ds->class;
        cache_hash(cache);
       }
 #endif
index 6c19c051aaff447b057c27853341778a1b309330..3c8a847d9f8519e53f58a0cee3a113c61fe48a82 100644 (file)
@@ -144,7 +144,7 @@ int main (int argc, char **argv)
   if (option_bool(OPT_DNSSEC_VALID))
     {
 #ifdef HAVE_DNSSEC
-      if (!daemon->dnskeys)
+      if (!daemon->ds)
        die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
       
       if (daemon->cachesize < CACHESIZ)
index 1f42818ba6fae4e136dfca2156b9d170c8eb9e65..29ae2956f4dfcabde2f40552aa62fc403619c38a 100644 (file)
@@ -295,10 +295,10 @@ struct cname {
   struct cname *next;
 }; 
 
-struct dnskey {
-  char *name, *key;
-  int keylen, class, algo, flags;
-  struct dnskey *next;
+struct ds_config {
+  char *name, *digest;
+  int digestlen, class, algo, keytag, digest_type;
+  struct ds_config *next;
 };
 
 #define ADDRLIST_LITERAL 1
@@ -930,7 +930,7 @@ extern struct daemon {
   struct prefix_class *prefix_classes;
 #endif
 #ifdef HAVE_DNSSEC
-  struct dnskey *dnskeys;
+  struct ds_config *ds;
 #endif
 
   /* globally used stuff for DNS */
@@ -1107,9 +1107,6 @@ void prettyprint_time(char *buf, unsigned int t);
 int prettyprint_addr(union mysockaddr *addr, char *buf);
 int parse_hex(char *in, unsigned char *out, int maxlen, 
              unsigned int *wildcard_mask, int *mac_type);
-#ifdef HAVE_DNSSEC
-int parse_base64(char *in, char *out);
-#endif
 int memcmp_masked(unsigned char *a, unsigned char *b, int len, 
                  unsigned int mask);
 int expand_buf(struct iovec *iov, size_t size);
index 9a230435e2bcedec1ff00c07c31cb53b84a17ca6..f7f98d5cdc7e90398c79e4dcb4200457a96627ba 100644 (file)
@@ -139,7 +139,7 @@ struct myoption {
 #define LOPT_QUIET_DHCP6  327
 #define LOPT_QUIET_RA     328
 #define LOPT_SEC_VALID    329
-#define LOPT_DNSKEY       330
+#define LOPT_TRUST_ANCHOR 330
 #define LOPT_DNSSEC_DEBUG 331
 
 #ifdef HAVE_GETOPT_LONG
@@ -277,7 +277,7 @@ static const struct myoption opts[] =
     { "ipset", 1, 0, LOPT_IPSET },
     { "synth-domain", 1, 0, LOPT_SYNTH },
     { "dnssec", 0, 0, LOPT_SEC_VALID },
-    { "dnskey", 1, 0, LOPT_DNSKEY },
+    { "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
     { "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
 #ifdef OPTION6_PREFIX_CLASS 
     { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
@@ -430,7 +430,7 @@ static struct {
   { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
   { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
   { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
-  { LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL },
+  { LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
   { LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
 #ifdef OPTION6_PREFIX_CLASS 
   { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
@@ -590,6 +590,16 @@ static int atoi_check16(char *a, int *res)
 
   return 1;
 }
+
+static int atoi_check8(char *a, int *res)
+{
+  if (!(atoi_check(a, res)) ||
+      *res < 0 ||
+      *res > 0xff)
+    return 0;
+
+  return 1;
+}
        
 static void add_txt(char *name, char *txt)
 {
@@ -3675,10 +3685,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
       }
 
 #ifdef HAVE_DNSSEC
-    case LOPT_DNSKEY:
+    case LOPT_TRUST_ANCHOR:
       {
-       struct dnskey *new = opt_malloc(sizeof(struct dnskey));
-       char *key64, *algo = NULL;
+       struct ds_config *new = opt_malloc(sizeof(struct ds_config));
+       char *cp, *cp1, *keyhex, *digest, *algo = NULL;
+       int len;
        
        new->class = C_IN;
 
@@ -3700,20 +3711,30 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
              }
          }
                  
-               if (!comma || !algo || !(key64 = split(algo)) ||
-           !atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) ||
+               if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
+           !atoi_check16(comma, &new->keytag) || 
+           !atoi_check8(algo, &new->algo) ||
+           !atoi_check8(digest, &new->digest_type) ||
            !(new->name = canonicalise_opt(arg)))
-         ret_err(_("bad DNSKEY"));
+         ret_err(_("bad trust anchor"));
            
        /* Upper bound on length */
-       new->key = opt_malloc((3*strlen(key64)/4)+1);
-       unhide_metas(key64);
-       if ((new->keylen = parse_base64(key64, new->key)) == -1)
-         ret_err(_("bad base64 in DNSKEY"));
+       len = (2*strlen(keyhex))+1;
+       new->digest = opt_malloc(len);
+       unhide_metas(keyhex);
+       /* 4034: "Whitespace is allowed within digits" */
+       for (cp = keyhex; *cp; )
+         if (isspace(*cp))
+           for (cp1 = cp; *cp1; cp1++)
+             *cp1 = *(cp1+1);
+         else
+           cp++;
+       if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
+         ret_err(_("bad HEX in trust anchor"));
+       
+       new->next = daemon->ds;
+       daemon->ds = new;
        
-       new->next = daemon->dnskeys;
-       daemon->dnskeys = new;
-
        break;
       }
 #endif
index c58b9ff67182d00f4d8469ba29ae9a136cf8ed25..b8e0f1866dd792e4ec4a3ac9749ade4cc86eba00 100644 (file)
@@ -1599,20 +1599,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                  while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
                    if (crecp->uid == qclass)
                      {
-                       if (!(crecp->flags & F_CONFIG)) /* Don't return configured keys - send upstream instead */
-                         {
-                           gotone = 1;
-                           if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
-                             {                                               
-                               struct all_addr a;
-                               a.addr.keytag =  crecp->addr.key.keytag;
-                               log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
-                               if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
-                                                       crec_ttl(crecp, now), &nameoffset,
-                                                       T_DNSKEY, qclass, "sbbt", 
-                                                       crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
-                                 anscount++;
-                             }
+                       gotone = 1;
+                       if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
+                         {                                                   
+                           struct all_addr a;
+                           a.addr.keytag =  crecp->addr.key.keytag;
+                           log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
+                           if (add_resource_record(header, limit, &trunc, nameoffset, &ansp, 
+                                                   crec_ttl(crecp, now), &nameoffset,
+                                                   T_DNSKEY, qclass, "sbbt", 
+                                                   crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
+                             anscount++;
                          }
                      }
                }
index d5839fa93af7a80f651cf1b5dbf992a950657134..7c46d407b5596ff9a38af741868432237b8b527b 100644 (file)
@@ -482,66 +482,6 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
   return i;
 }
 
-#ifdef HAVE_DNSSEC
-static int charval(char c)
-{
-  if (c >= 'A' && c <= 'Z')
-    return c - 'A';
-  
-  if (c >= 'a' && c <= 'z')
-    return c - 'a' + 26;
-
-  if (c >= '0' && c <= '9')
-    return c - '0' + 52;
-
-  if (c == '+')
-    return 62;
-  
-  if (c == '/')
-    return 63;
-
-  if (c == '=')
-    return -1;
-
-  return -2;
-}
-
-int parse_base64(char *in, char *out)
-{
-  char *p = out;
-  int i, val[4];
-
-  while (*in)
-    {
-      for (i = 0; i < 4; i++)
-       { 
-         while (*in == ' ') 
-           in++;
-         if (*in == 0)
-           return -1;
-         if ((val[i] = charval(*in++)) == -2)
-           return -1;
-       }
-          
-      while (*in == ' ') 
-       in++;
-      
-      if (val[1] == -1)
-       return -1; /* too much padding */
-      
-      *p++ = (val[0] << 2) | (val[1] >> 4);
-      
-      if (val[2] != -1)
-       *p++ = (val[1] << 4) | ( val[2] >> 2);
-
-      if (val[3] != -1)
-       *p++ = (val[2] << 6) | val[3];
-    }
-
-  return p - out;
-}
-#endif
-
 /* return 0 for no match, or (no matched octets) + 1 */
 int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
 {
index bf3969d3e7c6a6bba713a5501abc7b8277113b07..afda5186ee4071eaeac70efb2245a9d25405341b 100644 (file)
@@ -1,8 +1,9 @@
-# The root DNSSEC trust anchors, valid as at 30/01/2014
+# The root DNSSEC trust anchor, valid as at 30/01/2014
 
+# Note that this is a DS record (ie a hash of the root Zone Signing Key) 
+# If was downloaded from https://data.iana.org/root-anchors/root-anchors.xml
+
+trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
 
-dnskey=.,257,8,AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjF FVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoX bfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaD X6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpz W5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relS Qageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulq QxA+Uk1ihz0=
-dnskey=.,256,8,AwEAAb8sU6pbYMWRbkRnEuEZw9NSir707TkOcF+UL1XiK4NDJOvXRyX1 95Am5dQ7bRnnuySZ3daf37vvjUUhuIWUAQ4stht8nJfYxVQXDYjSpGH5 I6Hf/0CZEoNP6cNvrQ7AFmKkmv00xWExKQjbvnRPI4bqpMwtHVzn6Wyb BZ6kuqED
-dnskey=.,256,8,AwEAAYRU41/8smgAvuSojEP4jaj5Yll7WPaUKpYvnz2pnX2VIvRn4jsy Jns80bloenG6X9ebJVy2CFtZQLKHP8DcKmIFotdgs2HolyocY1am/+33 4RtzusM2ojkhjn1FRGtuSE9s2TSz1ISv0yVnFyu+EP/ZkiWnDfWeVrJI SEWBEr4V