429959
From b09d07ee2aa891c5b8dd2469c4a73c9dd61e2384 Mon Sep 17 00:00:00 2001
429959
From: Simon Kelley <simon@thekelleys.org.uk>
429959
Date: Wed, 25 Oct 2017 17:48:19 +0100
429959
Subject: [PATCH 1/2] Fix caching logic for validated answers.
429959
429959
The current logic is naive in the case that there is more than
429959
one RRset in an answer (Typically, when a non-CNAME query is answered
429959
by one or more CNAME RRs, and then then an answer RRset.)
429959
429959
If all the RRsets validate, then they are cached and marked as validated,
429959
but if any RRset doesn't validate, then the AD flag is not set (good) and
429959
ALL the RRsets are cached marked as not validated.
429959
429959
This breaks when, eg, the answer contains a validated CNAME, pointing
429959
to a non-validated answer. A subsequent query for the CNAME without do
429959
will get an answer with the AD flag wrongly reset, and worse, the same
429959
query with do will get a cached answer without RRSIGS, rather than
429959
being forwarded.
429959
429959
The code now records the validation of individual RRsets and that
429959
is used to correctly set the "validated" bits in the cache entries.
429959
429959
(cherry picked from commit a6004d7f17687ac2455f724d0b57098c413f128d)
429959
---
429959
 src/dnsmasq.c |   2 +
429959
 src/dnsmasq.h |   5 +-
429959
 src/dnssec.c  | 174 +++++++++++++++++++++++++++++---------------------
429959
 src/forward.c |  19 ++++--
429959
 src/rfc1035.c |  58 ++++++++++++-----
429959
 5 files changed, 162 insertions(+), 96 deletions(-)
429959
429959
diff --git a/src/dnsmasq.c b/src/dnsmasq.c
429959
index 50b2029..f3d2671 100644
429959
--- a/src/dnsmasq.c
429959
+++ b/src/dnsmasq.c
429959
@@ -118,6 +118,8 @@ int main (int argc, char **argv)
429959
       daemon->namebuff = safe_malloc(MAXDNAME * 2);
429959
       daemon->keyname = safe_malloc(MAXDNAME * 2);
429959
       daemon->workspacename = safe_malloc(MAXDNAME * 2);
429959
+      /* one char flag per possible RR in answer section. */
429959
+      daemon->rr_status = safe_malloc(256);
429959
     }
429959
 #endif
429959
 
429959
diff --git a/src/dnsmasq.h b/src/dnsmasq.h
429959
index 5a68162..89d138a 100644
429959
--- a/src/dnsmasq.h
429959
+++ b/src/dnsmasq.h
429959
@@ -1006,6 +1006,7 @@ extern struct daemon {
429959
 #ifdef HAVE_DNSSEC
429959
   char *keyname; /* MAXDNAME size buffer */
429959
   char *workspacename; /* ditto */
429959
+  char *rr_status; /* 256 bytes as flags for individual RRs */
429959
 #endif
429959
   unsigned int local_answer, queries_forwarded, auth_answer;
429959
   struct frec *frec_list;
429959
@@ -1118,7 +1119,7 @@ size_t setup_reply(struct dns_header *header, size_t  qlen,
429959
 		   unsigned long local_ttl);
429959
 int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff, 
429959
 		      time_t now, char **ipsets, int is_sign, int checkrebind,
429959
-		      int no_cache, int secure, int *doctored);
429959
+		      int no_cache_dnssec, int secure, int *doctored, char *rr_status);
429959
 size_t answer_request(struct dns_header *header, char *limit, size_t qlen,  
429959
 		      struct in_addr local_addr, struct in_addr local_netmask, 
429959
 		      time_t now, int ad_reqd, int do_bit, int have_pseudoheader);
429959
@@ -1151,7 +1152,7 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
429959
 int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, char *name, char *keyname, int class);
429959
 int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
429959
 int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class,
429959
-			  int check_unsigned, int *neganswer, int *nons);
429959
+			  int check_unsigned, int *neganswer, int *nons, char *rr_status);
429959
 int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
429959
 size_t filter_rrsigs(struct dns_header *header, size_t plen);
429959
 unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
429959
diff --git a/src/dnssec.c b/src/dnssec.c
429959
index f45c804..3121eb1 100644
429959
--- a/src/dnssec.c
429959
+++ b/src/dnssec.c
429959
@@ -1177,8 +1177,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
429959
   if (qtype != T_DS || qclass != class)
429959
     rc = STAT_BOGUS;
429959
   else
429959
-    rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons);
429959
-  /* Note dnssec_validate_reply() will have cached positive answers */
429959
+    rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, NULL);
429959
   
429959
   if (rc == STAT_INSECURE)
429959
     rc = STAT_BOGUS;
429959
@@ -1962,18 +1961,25 @@ static int zone_status(char *name, int class, char *keyname, time_t now)
429959
    STAT_INSECURE at least one RRset not validated, because in unsigned zone.
429959
    STAT_BOGUS    signature is wrong, bad packet, no validation where there should be.
429959
    STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname, class in *class)
429959
-   STAT_NEED_DS  need DS to complete validation (name is returned in keyname) 
429959
+   STAT_NEED_DS  need DS to complete validation (name is returned in keyname)
429959
+
429959
+   If non-NULL, rr_status points to a char array which corressponds to the RRs in the 
429959
+   answer section (only). This is set to 1 for each RR which is validated, and 0 for any which aren't.
429959
 */
429959
 int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, 
429959
-			  int *class, int check_unsigned, int *neganswer, int *nons)
429959
+			  int *class, int check_unsigned, int *neganswer, int *nons, char *rr_status)
429959
 {
429959
   static unsigned char **targets = NULL;
429959
   static int target_sz = 0;
429959
 
429959
   unsigned char *ans_start, *p1, *p2;
429959
-  int type1, class1, rdlen1, type2, class2, rdlen2, qclass, qtype, targetidx;
429959
+  int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
429959
   int i, j, rc;
429959
+  int secure = STAT_SECURE;
429959
 
429959
+  if (rr_status)
429959
+    memset(rr_status, 0, ntohs(header->ancount));
429959
+  
429959
   if (neganswer)
429959
     *neganswer = 0;
429959
   
429959
@@ -2030,7 +2036,10 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
429959
   
429959
   for (p1 = ans_start, i = 0; i < ntohs(header->ancount) + ntohs(header->nscount); i++)
429959
     {
429959
-      if (!extract_name(header, plen, &p1, name, 1, 10))
429959
+       if (i != 0 && !ADD_RDLEN(header, p1, plen, rdlen1))
429959
+	 return STAT_BOGUS;
429959
+
429959
+       if (!extract_name(header, plen, &p1, name, 1, 10))
429959
 	return STAT_BOGUS; /* bad packet */
429959
       
429959
       GETSHORT(type1, p1);
429959
@@ -2039,106 +2048,125 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
429959
       GETSHORT(rdlen1, p1);
429959
       
429959
       /* Don't try and validate RRSIGs! */
429959
-      if (type1 != T_RRSIG)
429959
+      if (type1 == T_RRSIG)
429959
+	continue;
429959
+      
429959
+      /* Check if we've done this RRset already */
429959
+      for (p2 = ans_start, j = 0; j < i; j++)
429959
 	{
429959
-	  /* Check if we've done this RRset already */
429959
-	  for (p2 = ans_start, j = 0; j < i; j++)
429959
-	    {
429959
-	      if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
429959
-		return STAT_BOGUS; /* bad packet */
429959
-	      
429959
-	      GETSHORT(type2, p2);
429959
-	      GETSHORT(class2, p2);
429959
-	      p2 += 4; /* TTL */
429959
-	      GETSHORT(rdlen2, p2);
429959
-	      
429959
-	      if (type2 == type1 && class2 == class1 && rc == 1)
429959
-		break; /* Done it before: name, type, class all match. */
429959
-	      
429959
-	      if (!ADD_RDLEN(header, p2, plen, rdlen2))
429959
-		return STAT_BOGUS;
429959
-	    }
429959
+	  if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
429959
+	    return STAT_BOGUS; /* bad packet */
429959
+	  
429959
+	  GETSHORT(type2, p2);
429959
+	  GETSHORT(class2, p2);
429959
+	  p2 += 4; /* TTL */
429959
+	  GETSHORT(rdlen2, p2);
429959
+	  
429959
+	  if (type2 == type1 && class2 == class1 && rc == 1)
429959
+	    break; /* Done it before: name, type, class all match. */
429959
 	  
429959
+	  if (!ADD_RDLEN(header, p2, plen, rdlen2))
429959
+	    return STAT_BOGUS;
429959
+	}
429959
+      
429959
+      if (j != i)
429959
+	{
429959
+	  /* Done already: copy the validation status */
429959
+	  if (rr_status && (i < ntohs(header->ancount)))
429959
+	    rr_status[i] = rr_status[j];
429959
+	}
429959
+      else
429959
+	{
429959
 	  /* Not done, validate now */
429959
-	  if (j == i)
429959
+	  int sigcnt, rrcnt;
429959
+	  char *wildname;
429959
+	  
429959
+	  if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
429959
+	    return STAT_BOGUS;
429959
+	  
429959
+	  /* No signatures for RRset. We can be configured to assume this is OK and return a INSECURE result. */
429959
+	  if (sigcnt == 0)
429959
 	    {
429959
-	      int sigcnt, rrcnt;
429959
-	      char *wildname;
429959
-	      
429959
-	      if (!explore_rrset(header, plen, class1, type1, name, keyname, &sigcnt, &rrcnt))
429959
-		return STAT_BOGUS;
429959
-
429959
-	      /* No signatures for RRset. We can be configured to assume this is OK and return a INSECURE result. */
429959
-	      if (sigcnt == 0)
429959
+	      if (check_unsigned)
429959
 		{
429959
-		  if (check_unsigned)
429959
-		    {
429959
-		      rc = zone_status(name, class1, keyname, now);
429959
-		      if (rc == STAT_SECURE)
429959
-			rc = STAT_BOGUS;
429959
-		       if (class)
429959
-			 *class = class1; /* Class for NEED_DS or NEED_KEY */
429959
-		    }
429959
-		  else 
429959
-		    rc = STAT_INSECURE; 
429959
-		  
429959
-		  return rc;
429959
+		  rc = zone_status(name, class1, keyname, now);
429959
+		  if (rc == STAT_SECURE)
429959
+		    rc = STAT_BOGUS;
429959
+		  if (class)
429959
+		    *class = class1; /* Class for NEED_DS or NEED_KEY */
429959
 		}
429959
+	      else 
429959
+		rc = STAT_INSECURE; 
429959
 	      
429959
+	      if (rc != STAT_INSECURE)
429959
+		return rc;
429959
+	    }
429959
+	  else
429959
+	    {
429959
 	      /* explore_rrset() gives us key name from sigs in keyname.
429959
 		 Can't overwrite name here. */
429959
 	      strcpy(daemon->workspacename, keyname);
429959
 	      rc = zone_status(daemon->workspacename, class1, keyname, now);
429959
-
429959
-	      if (rc != STAT_SECURE)
429959
+	      
429959
+	      if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
429959
 		{
429959
 		  /* Zone is insecure, don't need to validate RRset */
429959
 		  if (class)
429959
 		    *class = class1; /* Class for NEED_DS or NEED_KEY */
429959
 		  return rc;
429959
-		} 
429959
-	      
429959
-	      rc = validate_rrset(now, header, plen, class1, type1, sigcnt, rrcnt, name, keyname, &wildname, NULL, 0, 0, 0);
429959
+		}
429959
 	      
429959
-	      if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
429959
-		{
429959
-		  if (class)
429959
-		    *class = class1; /* Class for DS or DNSKEY */
429959
-		  return rc;
429959
-		} 
429959
-	      else 
429959
+	      /* Zone is insecure, don't need to validate RRset */
429959
+	      if (rc == STAT_SECURE)
429959
 		{
429959
+		  rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
429959
+				      rrcnt, name, keyname, &wildname, NULL, 0, 0, 0);
429959
+		  
429959
+		  if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)
429959
+		    {
429959
+		      if (class)
429959
+			*class = class1; /* Class for DS or DNSKEY */
429959
+		      return rc;
429959
+		    } 
429959
+		  
429959
 		  /* rc is now STAT_SECURE or STAT_SECURE_WILDCARD */
429959
-		 
429959
+		  
429959
+		  /* Note that RR is validated */
429959
+		   if (rr_status && (i < ntohs(header->ancount)))
429959
+		     rr_status[i] = 1;
429959
+		   
429959
 		  /* Note if we've validated either the answer to the question
429959
 		     or the target of a CNAME. Any not noted will need NSEC or
429959
 		     to be in unsigned space. */
429959
-
429959
 		  for (j = 0; j 
429959
 		    if ((p2 = targets[j]))
429959
 		      {
429959
-			if (!(rc = extract_name(header, plen, &p2, name, 0, 10)))
429959
+			int rc1;
429959
+			if (!(rc1 = extract_name(header, plen, &p2, name, 0, 10)))
429959
 			  return STAT_BOGUS; /* bad packet */
429959
 			
429959
-			if (class1 == qclass && rc == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
429959
+			if (class1 == qclass && rc1 == 1 && (type1 == T_CNAME || type1 == qtype || qtype == T_ANY ))
429959
 			  targets[j] = NULL;
429959
 		      }
429959
-			    
429959
-		   /* An attacker replay a wildcard answer with a different
429959
-		      answer and overlay a genuine RR. To prove this
429959
-		      hasn't happened, the answer must prove that
429959
-		      the gennuine record doesn't exist. Check that here. 
429959
-		      Note that we may not yet have validated the NSEC/NSEC3 RRsets. 
429959
-		      That's not a problem since if the RRsets later fail
429959
-		      we'll return BOGUS then. */
429959
-		  if (rc == STAT_SECURE_WILDCARD && !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
429959
+		  
429959
+		  /* An attacker replay a wildcard answer with a different
429959
+		     answer and overlay a genuine RR. To prove this
429959
+		     hasn't happened, the answer must prove that
429959
+		     the genuine record doesn't exist. Check that here. 
429959
+		     Note that we may not yet have validated the NSEC/NSEC3 RRsets. 
429959
+		     That's not a problem since if the RRsets later fail
429959
+		     we'll return BOGUS then. */
429959
+		  if (rc == STAT_SECURE_WILDCARD &&
429959
+		      !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL))
429959
 		    return STAT_BOGUS;
429959
+
429959
+		  rc = STAT_SECURE;
429959
 		}
429959
 	    }
429959
 	}
429959
 
429959
-      if (!ADD_RDLEN(header, p1, plen, rdlen1))
429959
-	return STAT_BOGUS;
429959
+      if (rc == STAT_INSECURE)
429959
+	secure = STAT_INSECURE;
429959
     }
429959
 
429959
   /* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
429959
@@ -2172,7 +2200,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
429959
 	  }
429959
       }
429959
   
429959
-  return STAT_SECURE;
429959
+  return secure;
429959
 }
429959
 
429959
 
429959
diff --git a/src/forward.c b/src/forward.c
429959
index a729c06..245c448 100644
429959
--- a/src/forward.c
429959
+++ b/src/forward.c
429959
@@ -561,7 +561,8 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
429959
   char **sets = 0;
429959
   int munged = 0, is_sign;
429959
   size_t plen; 
429959
-
429959
+  char *rr_status = NULL;
429959
+  
429959
   (void)ad_reqd;
429959
   (void)do_bit;
429959
   (void)bogusanswer;
429959
@@ -651,6 +652,11 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
429959
 	server->flags |= SERV_WARNED_RECURSIVE;
429959
     }  
429959
 
429959
+#ifdef HAVE_DNSSEC
429959
+  if (option_bool(OPT_DNSSEC_VALID))
429959
+    rr_status = daemon->rr_status;
429959
+#endif
429959
+
429959
   if (daemon->bogus_addr && RCODE(header) != NXDOMAIN &&
429959
       check_for_bogus_wildcard(header, n, daemon->namebuff, daemon->bogus_addr, now))
429959
     {
429959
@@ -676,7 +682,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
429959
 	  cache_secure = 0;
429959
 	}
429959
       
429959
-      if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored))
429959
+      if (extract_addresses(header, n, daemon->namebuff, now, sets, is_sign, check_rebind, no_cache, cache_secure, &doctored, rr_status))
429959
 	{
429959
 	  my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
429959
 	  munged = 1;
429959
@@ -856,7 +862,7 @@ void reply_query(int fd, int family, time_t now)
429959
   if (forward->forwardall == 0 || --forward->forwardall == 1 || RCODE(header) != REFUSED)
429959
     {
429959
       int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
429959
-
429959
+      
429959
       if (option_bool(OPT_NO_REBIND))
429959
 	check_rebind = !(forward->flags & FREC_NOREBIND);
429959
       
429959
@@ -896,7 +902,8 @@ void reply_query(int fd, int family, time_t now)
429959
 		    status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
429959
 		  else
429959
 		    status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, 
429959
-						   option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
429959
+						   option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC),
429959
+						   NULL, NULL, daemon->rr_status);
429959
 		}
429959
 	      
429959
 	      /* Can't validate, as we're missing key data. Put this
429959
@@ -1480,7 +1487,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
429959
       else if (status == STAT_NEED_DS)
429959
 	new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
429959
       else 
429959
-	new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, option_bool(OPT_DNSSEC_NO_SIGN), NULL, NULL);
429959
+	new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
429959
+					   option_bool(OPT_DNSSEC_NO_SIGN) && (server->flags & SERV_DO_DNSSEC),
429959
+					   NULL, NULL, daemon->rr_status);
429959
       
429959
       if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
429959
 	break;
429959
diff --git a/src/rfc1035.c b/src/rfc1035.c
429959
index f78b5cb..607412f 100644
429959
--- a/src/rfc1035.c
429959
+++ b/src/rfc1035.c
429959
@@ -571,7 +571,8 @@ static int find_soa(struct dns_header *header, size_t qlen, char *name, int *doc
429959
    expired and cleaned out that way. 
429959
    Return 1 if we reject an address because it look like part of dns-rebinding attack. */
429959
 int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t now, 
429959
-		      char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec, int secure, int *doctored)
429959
+		      char **ipsets, int is_sign, int check_rebind, int no_cache_dnssec,
429959
+		      int secure, int *doctored, char *rr_status)
429959
 {
429959
   unsigned char *p, *p1, *endrr, *namep;
429959
   int i, j, qtype, qclass, aqtype, aqclass, ardlen, res, searched_soa = 0;
429959
@@ -582,6 +583,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
 #else
429959
   (void)ipsets; /* unused */
429959
 #endif
429959
+
429959
   
429959
   cache_start_insert();
429959
 
429959
@@ -590,10 +592,16 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
     {
429959
       searched_soa = 1;
429959
       ttl = find_soa(header, qlen, name, doctored);
429959
-#ifdef HAVE_DNSSEC
429959
-      if (*doctored && secure)
429959
-	return 0;
429959
-#endif
429959
+
429959
+      if (*doctored)
429959
+	{
429959
+	  if (secure)
429959
+	    return 0;
429959
+	  if (rr_status)
429959
+	    for (i = 0; i < ntohs(header->ancount); i++)
429959
+	      if (rr_status[i])
429959
+		return 0;
429959
+	}
429959
     }
429959
   
429959
   /* go through the questions. */
429959
@@ -604,7 +612,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
       int found = 0, cname_count = CNAME_CHAIN;
429959
       struct crec *cpp = NULL;
429959
       int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
429959
-      int secflag = secure ?  F_DNSSECOK : 0;
429959
+      int cname_short = 0;
429959
       unsigned long cttl = ULONG_MAX, attl;
429959
 
429959
       namep = p;
429959
@@ -632,8 +640,9 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
 	      if (!(p1 = skip_questions(header, qlen)))
429959
 		return 0;
429959
 	      
429959
-	      for (j = ntohs(header->ancount); j != 0; j--) 
429959
+	      for (j = 0; j < ntohs(header->ancount); j++) 
429959
 		{
429959
+		  int secflag = 0;
429959
 		  unsigned char *tmp = namep;
429959
 		  /* the loop body overwrites the original name, so get it back here. */
429959
 		  if (!extract_name(header, qlen, &tmp, name, 1, 0) ||
429959
@@ -659,11 +668,21 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
 		    {
429959
 		      if (!extract_name(header, qlen, &p1, name, 1, 0))
429959
 			return 0;
429959
-		      
429959
+
429959
+		      if (rr_status && rr_status[j])
429959
+			{
429959
+			  /* validated RR anywhere in CNAME chain, don't cache. */
429959
+			  if (cname_short || aqtype == T_CNAME)
429959
+			    return 0;
429959
+
429959
+			  secflag = F_DNSSECOK;
429959
+			}
429959
+
429959
 		      if (aqtype == T_CNAME)
429959
 			{
429959
-			  if (!cname_count-- || secure)
429959
-			    return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
429959
+			  if (!cname_count--)
429959
+			    return 0; /* looped CNAMES, we can't cache. */
429959
+			  cname_short = 1;
429959
 			  goto cname_loop;
429959
 			}
429959
 		      
429959
@@ -685,7 +704,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
 		  ttl = find_soa(header, qlen, NULL, doctored);
429959
 		}
429959
 	      if (ttl)
429959
-		cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | secflag);	
429959
+		cache_insert(NULL, &addr, now, ttl, name_encoding | F_REVERSE | F_NEG | flags | (secure ?  F_DNSSECOK : 0));	
429959
 	    }
429959
 	}
429959
       else
429959
@@ -713,8 +732,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
 	  if (!(p1 = skip_questions(header, qlen)))
429959
 	    return 0;
429959
 	  
429959
-	  for (j = ntohs(header->ancount); j != 0; j--) 
429959
+	  for (j = 0; j < ntohs(header->ancount); j++) 
429959
 	    {
429959
+	      int secflag = 0;
429959
+	      
429959
 	      if (!(res = extract_name(header, qlen, &p1, name, 0, 10)))
429959
 		return 0; /* bad packet */
429959
 	      
429959
@@ -731,6 +752,10 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
 	      
429959
 	      if (aqclass == C_IN && res != 2 && (aqtype == T_CNAME || aqtype == qtype))
429959
 		{
429959
+#ifdef HAVE_DNSSEC
429959
+		  if (rr_status && rr_status[j])
429959
+		      secflag = F_DNSSECOK;
429959
+#endif		  
429959
 		  if (aqtype == T_CNAME)
429959
 		    {
429959
 		      if (!cname_count--)
429959
@@ -822,7 +847,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
429959
 		 pointing at this, inherit its TTL */
429959
 	      if (ttl || cpp)
429959
 		{
429959
-		  newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | secflag);	
429959
+		  newc = cache_insert(name, NULL, now, ttl ? ttl : cttl, F_FORWARD | F_NEG | flags | (secure ? F_DNSSECOK : 0));	
429959
 		  if (newc && cpp)
429959
 		    {
429959
 		      cpp->addr.cname.target.cache = newc;
429959
@@ -937,7 +962,7 @@ int check_for_local_domain(char *name, time_t now)
429959
   /* Note: the call to cache_find_by_name is intended to find any record which matches
429959
      ie A, AAAA, CNAME. */
429959
 
429959
-  if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME |F_NO_RR)) &&
429959
+  if ((crecp = cache_find_by_name(NULL, name, now, F_IPV4 | F_IPV6 | F_CNAME | F_NO_RR)) &&
429959
       (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG)))
429959
     return 1;
429959
   
429959
@@ -1689,8 +1714,9 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
429959
 
429959
 	  if (qtype == T_CNAME || qtype == T_ANY)
429959
 	    {
429959
-	      if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME)) &&
429959
-		  (qtype == T_CNAME || (crecp->flags & (F_HOSTS | F_DHCP | F_CONFIG  | (dryrun ? F_NO_RR : 0)))))
429959
+	      if ((crecp = cache_find_by_name(NULL, name, now, F_CNAME | (dryrun ? F_NO_RR : 0))) &&
429959
+		  (qtype == T_CNAME || (crecp->flags & F_CONFIG)) &&
429959
+		  ((crecp->flags & F_CONFIG) || !do_bit || !(crecp->flags & F_DNSSECOK)))
429959
 		{
429959
 		  if (!(crecp->flags & F_DNSSECOK))
429959
 		    sec_data = 0;
429959
-- 
429959
2.20.1
429959