]> git.ipfire.org Git - people/ms/dnsmasq.git/commitdiff
protocol handling for DNSSEC
authorSimon Kelley <simon@thekelleys.org.uk>
Tue, 14 Jan 2014 23:13:55 +0000 (23:13 +0000)
committerSimon Kelley <simon@thekelleys.org.uk>
Tue, 14 Jan 2014 23:13:55 +0000 (23:13 +0000)
src/dnsmasq.h
src/forward.c
src/option.c
src/rfc1035.c

index 4d91d92a765ed711fea7b4e5dd548316cc364877..7ffef8fd65adba4b04a11f47a774e437306513b0 100644 (file)
@@ -230,7 +230,8 @@ struct event_desc {
 #define OPT_QUIET_DHCP6    43
 #define OPT_QUIET_RA      44
 #define OPT_DNSSEC_VALID   45
-#define OPT_LAST           46
+#define OPT_DNSSEC_PERMISS 46
+#define OPT_LAST           47
 
 /* extra flags for my_syslog, we use a couple of facilities since they are known 
    not to occupy the same bits as priorities, no matter how syslog.h is set up. */
index 8167229d160da29fa8bf5711e6908d17332b391f..0dd66f014674fe8d1f9008da118eaff6c4364be2 100644 (file)
@@ -511,7 +511,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
   if (option_bool(OPT_DNSSEC_VALID))
     header->hb4 &= ~HB4_AD;
   
-  if (cache_secure)
+  if (!(header->hb4 & HB4_CD) && cache_secure)
     header->hb4 |= HB4_AD;
 #endif
   
@@ -556,6 +556,31 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
        }
     }
   
+#ifdef HAVE_DNSSEC
+  if (no_cache && !(header->hb4 & HB4_CD)) 
+    {
+      if (option_bool(OPT_DNSSEC_PERMISS))
+       {
+         unsigned short type;
+         char types[20];
+         
+         if (extract_request(header, (size_t)n, daemon->namebuff, &type))
+           {
+             querystr("", types, type);
+             my_syslog(LOG_WARNING, _("DNSSEC validation failed: query %s%s"), daemon->namebuff, types);
+           }
+         else
+           my_syslog(LOG_WARNING, _("DNSSEC validation failed for unknown query"));
+       }
+      else
+       {
+         /* Bogus reply, turn into SERVFAIL */
+         SET_RCODE(header, SERVFAIL);
+         munged = 1;
+       }
+    }
+#endif
+
   /* do this after extract_addresses. Ensure NODATA reply and remove
      nameserver info. */
   
@@ -824,7 +849,6 @@ void reply_query(int fd, int family, time_t now)
 
          if (status == STAT_SECURE)
            cache_secure = 1;
-         /* TODO return SERVFAIL here */
          else if (status == STAT_BOGUS)
            no_cache_dnssec = 1;
          
index 912876f2341b3bca651cb32a1911e368996e61a7..760fd621a0a4d8387311c1c96cde7dbb22350612 100644 (file)
@@ -140,7 +140,7 @@ struct myoption {
 #define LOPT_QUIET_RA     328
 #define LOPT_SEC_VALID    329
 #define LOPT_DNSKEY       330
-
+#define LOPT_DNSSEC_PERM  331
 
 #ifdef HAVE_GETOPT_LONG
 static const struct option opts[] =  
@@ -278,6 +278,7 @@ static const struct myoption opts[] =
     { "synth-domain", 1, 0, LOPT_SYNTH },
     { "dnssec", 0, 0, LOPT_SEC_VALID },
     { "dnskey", 1, 0, LOPT_DNSKEY },
+    { "dnssec-permissive", 0, 0, LOPT_DNSSEC_PERM },
 #ifdef OPTION6_PREFIX_CLASS 
     { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
 #endif
@@ -428,10 +429,9 @@ static struct {
   { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
   { 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 },
-#ifdef HAVE_DNSSEC
   { 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 },
-#endif
+  { LOPT_DNSSEC_PERM, OPT_DNSSEC_PERMISS, NULL, gettext_noop("Do NOT return SERVFAIL whne DNSSEC validation fails."), NULL },
 #ifdef OPTION6_PREFIX_CLASS 
   { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
 #endif
index 0b254e30be466abdacee244bf90a95188a712951..6827544fe704ee85c4957815b81f92d49e4698ac 100644 (file)
@@ -511,14 +511,17 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
                               int optno, unsigned char *opt, size_t optlen, int set_do)
 { 
   unsigned char *lenp, *datap, *p;
-  int rdlen;
+  int rdlen, is_sign;
   
-  if (ntohs(header->arcount) == 0)
+  if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
     {
+      if (is_sign)
+       return plen;
+
       /* We are adding the pseudoheader */
       if (!(p = skip_questions(header, plen)) ||
          !(p = skip_section(p, 
-                            ntohs(header->ancount) + ntohs(header->nscount), 
+                            ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount)
                             header, plen)))
        return plen;
       *p++ = 0; /* empty name */
@@ -531,16 +534,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
       rdlen = 0;
       if (((ssize_t)optlen) > (limit - (p + 4)))
        return plen; /* Too big */
-      header->arcount = htons(1);
+      header->arcount = htons(ntohs(header->arcount) + 1);
       datap = p;
     }
   else
     {
-      int i, is_sign;
+      int i;
       unsigned short code, len, flags;
       
+      /* Must be at the end, if exists */
       if (ntohs(header->arcount) != 1 ||
-         !(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
          is_sign ||
          (!(p = skip_name(p, header, plen, 10))))
        return plen;
@@ -1147,7 +1150,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
 
 /* If the packet holds exactly one query
    return F_IPV4 or F_IPV6  and leave the name from the query in name */
-
 unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
 {
   unsigned char *p = (unsigned char *)(header+1);
@@ -1447,23 +1449,30 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
   int nameoffset;
   unsigned short flag;
   int q, ans, anscount = 0, addncount = 0;
-  int dryrun = 0, sec_reqd = 0;
+  int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
   int is_sign;
   struct crec *crecp;
   int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
   struct mx_srv_record *rec;
+  size_t len;
  
+  /* Don't return AD set even for local data if checking disabled. */
+  if (header->hb4 & HB4_CD)
+    sec_data = 0;
+
   /* If there is an RFC2671 pseudoheader then it will be overwritten by
      partial replies, so we have to do a dry run to see if we can answer
      the query. We check to see if the do bit is set, if so we always
      forward rather than answering from the cache, which doesn't include
-     security information. */
+     security information, unless we're in DNSSEC validation mode. */
 
   if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
     { 
       unsigned short udpsz, flags;
       unsigned char *psave = pheader;
 
+      have_pseudoheader = 1;
+
       GETSHORT(udpsz, pheader);
       pheader += 2; /* ext_rcode */
       GETSHORT(flags, pheader);
@@ -1637,7 +1646,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                        if (!dryrun)
                          log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
                      }
-                   else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+                   else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
                      {
                        ans = 1;
                        if (!(crecp->flags & (F_HOSTS | F_DHCP)))
@@ -1834,7 +1843,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
                          if (!dryrun)
                            log_query(crecp->flags, name, NULL, NULL);
                        }
-                     else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd)
+                     else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd ||  option_bool(OPT_DNSSEC_VALID))
                        {
                          /* If we are returning local answers depending on network,
                             filter here. */
@@ -2060,12 +2069,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
   if (trunc)
     header->hb3 |= HB3_TC;
   
-  header->hb4 &= ~HB4_AD;
-  
-  if (option_bool(OPT_DNSSEC_VALID) || option_bool(OPT_DNSSEC_PROXY))
-    if (sec_data)
-      header->hb4 |= HB4_AD;
-  
   if (nxdomain)
     SET_RCODE(header, NXDOMAIN);
   else
@@ -2073,6 +2076,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
   header->ancount = htons(anscount);
   header->nscount = htons(0);
   header->arcount = htons(addncount);
-  return ansp - (unsigned char *)header;
+  
+  header->hb4 &= ~HB4_AD;
+  len = ansp - (unsigned char *)header;
+  
+  if (have_pseudoheader)
+    {
+      len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
+      if (sec_reqd && sec_data)
+       header->hb4 |= HB4_AD;
+
+    }
+  
+  return len                           ;
 }