Diff for /embedaddon/dnsmasq/src/dnssec.c between versions 1.1.1.3 and 1.1.1.4

version 1.1.1.3, 2021/03/17 00:56:46 version 1.1.1.4, 2023/09/27 11:02:07
Line 215  static int is_check_date(unsigned long curtime) Line 215  static int is_check_date(unsigned long curtime)
     return !daemon->dnssec_no_time_check;      return !daemon->dnssec_no_time_check;
 }  }
   
 /* Check whether today/now is between date_start and date_end */  
 static int check_date_range(unsigned long curtime, u32 date_start, u32 date_end)  
 {  
   /* We must explicitly check against wanted values, because of SERIAL_UNDEF */  
   return serial_compare_32(curtime, date_start) == SERIAL_GT  
     && serial_compare_32(curtime, date_end) == SERIAL_LT;  
 }  
   
 /* Return bytes of canonicalised rrdata one by one.  /* Return bytes of canonicalised rrdata one by one.
    Init state->ip with the RR, and state->end with the end of same.     Init state->ip with the RR, and state->end with the end of same.
    Init state->op to NULL.     Init state->op to NULL.
Line 534  static int validate_rrset(time_t now, struct dns_heade Line 526  static int validate_rrset(time_t now, struct dns_heade
   struct crec *crecp = NULL;    struct crec *crecp = NULL;
   u16 *rr_desc = rrfilter_desc(type);    u16 *rr_desc = rrfilter_desc(type);
   u32 sig_expiration, sig_inception;    u32 sig_expiration, sig_inception;
  int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP | DNSSEC_FAIL_NOKEYSUP;
   
   unsigned long curtime = time(0);    unsigned long curtime = time(0);
   int time_check = is_check_date(curtime);    int time_check = is_check_date(curtime);
       
Line 557  static int validate_rrset(time_t now, struct dns_heade Line 550  static int validate_rrset(time_t now, struct dns_heade
       void *ctx;        void *ctx;
       char *name_start;        char *name_start;
       u32 nsigttl, ttl, orig_ttl;        u32 nsigttl, ttl, orig_ttl;
   
         failflags &= ~DNSSEC_FAIL_NOSIG;
               
       p = sigs[j];        p = sigs[j];
       GETLONG(ttl, p);        GETLONG(ttl, p);
Line 574  static int validate_rrset(time_t now, struct dns_heade Line 569  static int validate_rrset(time_t now, struct dns_heade
       if (!extract_name(header, plen, &p, keyname, 1, 0))        if (!extract_name(header, plen, &p, keyname, 1, 0))
         return STAT_BOGUS;          return STAT_BOGUS;
   
      if ((time_check && !check_date_range(curtime, sig_inception, sig_expiration)) ||      if (!time_check)
          labels > name_labels ||        failflags &= ~(DNSSEC_FAIL_NYV | DNSSEC_FAIL_EXP);
          !(hash = hash_find(algo_digest_name(algo))) ||      else
         {
           /* We must explicitly check against wanted values, because of SERIAL_UNDEF */
           if (serial_compare_32(curtime, sig_inception) == SERIAL_LT)
             continue;
           else
             failflags &= ~DNSSEC_FAIL_NYV;
           
           if (serial_compare_32(curtime, sig_expiration) == SERIAL_GT)
             continue;
           else
             failflags &= ~DNSSEC_FAIL_EXP;
         }
 
       if (!(hash = hash_find(algo_digest_name(algo))))
         continue;
       else
         failflags &= ~DNSSEC_FAIL_NOKEYSUP;
       
       if (labels > name_labels ||
           !hash_init(hash, &ctx, &digest))            !hash_init(hash, &ctx, &digest))
         continue;          continue;
      
       /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */        /* OK, we have the signature record, see if the relevant DNSKEY is in the cache. */
       if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))        if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
         return STAT_NEED_KEY;          return STAT_NEED_KEY;
Line 710  static int validate_rrset(time_t now, struct dns_heade Line 724  static int validate_rrset(time_t now, struct dns_heade
               
       /* namebuff used for workspace above, restore to leave unchanged on exit */        /* namebuff used for workspace above, restore to leave unchanged on exit */
       p = (unsigned char*)(rrset[0]);        p = (unsigned char*)(rrset[0]);
      extract_name(header, plen, &p, name, 1, 0);      if (!extract_name(header, plen, &p, name, 1, 0))
         return STAT_BOGUS;
   
       if (key)        if (key)
         {          {
Line 730  static int validate_rrset(time_t now, struct dns_heade Line 745  static int validate_rrset(time_t now, struct dns_heade
         }          }
     }      }
   
  return STAT_BOGUS;  /* If we reach this point, no verifying key was found */
   return STAT_BOGUS | failflags | DNSSEC_FAIL_NOKEY;
 }  }
     
   
Line 751  int dnssec_validate_by_ds(time_t now, struct dns_heade Line 767  int dnssec_validate_by_ds(time_t now, struct dns_heade
   unsigned long ttl, sig_ttl;    unsigned long ttl, sig_ttl;
   struct blockdata *key;    struct blockdata *key;
   union all_addr a;    union all_addr a;
     int failflags = DNSSEC_FAIL_NOSIG | DNSSEC_FAIL_NODSSUP | DNSSEC_FAIL_NOZONE | DNSSEC_FAIL_NOKEY;
   
   if (ntohs(header->qdcount) != 1 ||    if (ntohs(header->qdcount) != 1 ||
       RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||        RCODE(header) == SERVFAIL || RCODE(header) == REFUSED ||
       !extract_name(header, plen, &p, name, 1, 4))        !extract_name(header, plen, &p, name, 1, 4))
    return STAT_BOGUS;    return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
   
   GETSHORT(qtype, p);    GETSHORT(qtype, p);
   GETSHORT(qclass, p);    GETSHORT(qclass, p);
       
   if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)    if (qtype != T_DNSKEY || qclass != class || ntohs(header->ancount) == 0)
    return STAT_BOGUS;    return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
   
   /* See if we have cached a DS record which validates this key */    /* See if we have cached a DS record which validates this key */
   if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))    if (!(crecp = cache_find_by_name(NULL, name, now, F_DS)))
Line 795  int dnssec_validate_by_ds(time_t now, struct dns_heade Line 812  int dnssec_validate_by_ds(time_t now, struct dns_heade
               
       GETSHORT(flags, p);        GETSHORT(flags, p);
       if (*p++ != 3)        if (*p++ != 3)
        return STAT_BOGUS;        return STAT_BOGUS | DNSSEC_FAIL_NOKEY;
       algo = *p++;        algo = *p++;
       keytag = dnskey_keytag(algo, flags, p, rdlen - 4);        keytag = dnskey_keytag(algo, flags, p, rdlen - 4);
       key = NULL;        key = NULL;
               
       /* key must have zone key flag set */        /* key must have zone key flag set */
       if (flags & 0x100)        if (flags & 0x100)
        key = blockdata_alloc((char*)p, rdlen - 4);        {
           key = blockdata_alloc((char*)p, rdlen - 4);
           failflags &= ~DNSSEC_FAIL_NOZONE;
         }
               
       p = psave;        p = psave;
               
Line 823  int dnssec_validate_by_ds(time_t now, struct dns_heade Line 843  int dnssec_validate_by_ds(time_t now, struct dns_heade
           unsigned char *digest, *ds_digest;            unsigned char *digest, *ds_digest;
           const struct nettle_hash *hash;            const struct nettle_hash *hash;
           int sigcnt, rrcnt;            int sigcnt, rrcnt;
          int wire_len;
           
           if (recp1->addr.ds.algo == algo &&             if (recp1->addr.ds.algo == algo && 
               recp1->addr.ds.keytag == keytag &&                recp1->addr.ds.keytag == keytag &&
              recp1->uid == (unsigned int)class &&              recp1->uid == (unsigned int)class)
              (hash = hash_find(ds_digest_name(recp1->addr.ds.digest))) && 
              hash_init(hash, &ctx, &digest)) 
             
             {              {
              int wire_len = to_wire(name);              failflags &= ~DNSSEC_FAIL_NOKEY;
                               
                 if (!(hash = hash_find(ds_digest_name(recp1->addr.ds.digest))))
                   continue;
                 else
                   failflags &= ~DNSSEC_FAIL_NODSSUP;
   
                 if (!hash_init(hash, &ctx, &digest))
                   continue;
                 
                 wire_len = to_wire(name);
                 
               /* Note that digest may be different between DSs, so                 /* Note that digest may be different between DSs, so 
                  we can't move this outside the loop. */                   we can't move this outside the loop. */
               hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);                hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
Line 846  int dnssec_validate_by_ds(time_t now, struct dns_heade Line 874  int dnssec_validate_by_ds(time_t now, struct dns_heade
                   (ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&                    (ds_digest = blockdata_retrieve(recp1->addr.ds.keydata, recp1->addr.ds.keylen, NULL)) &&
                   memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&                    memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
                   explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&                    explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
                  sigcnt != 0 && rrcnt != 0 &&                  rrcnt != 0)
                  validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,  
                                 NULL, key, rdlen - 4, algo, keytag, &sig_ttl) == STAT_SECURE) 
                 {                  {
                  valid = 1;                  if (sigcnt == 0)
                  break;                    continue;
                   else
                     failflags &= ~DNSSEC_FAIL_NOSIG;
                   
                   rc = validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname, 
                                       NULL, key, rdlen - 4, algo, keytag, &sig_ttl);
 
                   failflags &= rc;
                   
                   if (STAT_ISEQUAL(rc, STAT_SECURE))
                     {
                       valid = 1;
                       break;
                     }
                 }                  }
             }              }
         }          }
Line 916  int dnssec_validate_by_ds(time_t now, struct dns_heade Line 955  int dnssec_validate_by_ds(time_t now, struct dns_heade
                           a.log.keytag = keytag;                            a.log.keytag = keytag;
                           a.log.algo = algo;                            a.log.algo = algo;
                           if (algo_digest_name(algo))                            if (algo_digest_name(algo))
                            log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu");                            log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu", 0);
                           else                            else
                            log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)");                            log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %hu, algo %hu (not supported)", 0);
                         }                          }
                     }                      }
                 }                  }
Line 935  int dnssec_validate_by_ds(time_t now, struct dns_heade Line 974  int dnssec_validate_by_ds(time_t now, struct dns_heade
       return STAT_OK;        return STAT_OK;
     }      }
   
  log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");  log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY", 0);
  return STAT_BOGUS;  return STAT_BOGUS | failflags;
 }  }
   
 /* The DNS packet is expected to contain the answer to a DS query  /* The DNS packet is expected to contain the answer to a DS query
   Put all DSs in the answer which are valid into the cache.   Put all DSs in the answer which are valid and have hash and signature algos
    we support into the cache.
    Also handles replies which prove that there's no DS at this location,      Also handles replies which prove that there's no DS at this location, 
    either because the zone is unsigned or this isn't a zone cut. These are     either because the zone is unsigned or this isn't a zone cut. These are
    cached too.     cached too.
      If none of the DS's are for supported algos, treat the answer as if 
      it's a proof of no DS at this location. RFC4035 para 5.2.
    return codes:     return codes:
    STAT_OK          At least one valid DS found and in cache.     STAT_OK          At least one valid DS found and in cache.
    STAT_BOGUS       no DS in reply or not signed, fails validation, bad packet.     STAT_BOGUS       no DS in reply or not signed, fails validation, bad packet.
Line 954  int dnssec_validate_by_ds(time_t now, struct dns_heade Line 996  int dnssec_validate_by_ds(time_t now, struct dns_heade
 int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)  int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
 {  {
   unsigned char *p = (unsigned char *)(header+1);    unsigned char *p = (unsigned char *)(header+1);
  int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0;  int qtype, qclass, rc, i, neganswer, nons, neg_ttl = 0, found_supported = 0;
  int aclass, atype, rdlen;  int aclass, atype, rdlen, flags;
   unsigned long ttl;    unsigned long ttl;
   union all_addr a;    union all_addr a;
   
Line 971  int dnssec_validate_ds(time_t now, struct dns_header * Line 1013  int dnssec_validate_ds(time_t now, struct dns_header *
   else    else
     rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);      rc = dnssec_validate_reply(now, header, plen, name, keyname, NULL, 0, &neganswer, &nons, &neg_ttl);
       
  if (rc == STAT_INSECURE)  if (STAT_ISEQUAL(rc, STAT_INSECURE))
     {      {
       my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);        my_syslog(LOG_WARNING, _("Insecure DS reply received for %s, check domain configuration and upstream DNS server DNSSEC support"), name);
      rc = STAT_BOGUS;      log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS - not secure", 0);
       return STAT_BOGUS | DNSSEC_FAIL_INDET;
     }      }
       
   p = (unsigned char *)(header+1);    p = (unsigned char *)(header+1);
  extract_name(header, plen, &p, name, 1, 4);  if (!extract_name(header, plen, &p, name, 1, 4))
       return STAT_BOGUS;
 
   p += 4; /* qtype, qclass */    p += 4; /* qtype, qclass */
       
   /* If the key needed to validate the DS is on the same domain as the DS, we'll    /* If the key needed to validate the DS is on the same domain as the DS, we'll
      loop getting nowhere. Stop that now. This can happen of the DS answer comes       loop getting nowhere. Stop that now. This can happen of the DS answer comes
      from the DS's zone, and not the parent zone. */       from the DS's zone, and not the parent zone. */
  if (rc == STAT_BOGUS || (rc == STAT_NEED_KEY && hostname_isequal(name, keyname)))  if (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname))
     {      {
      log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");      log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS", 0);
       return STAT_BOGUS;        return STAT_BOGUS;
     }      }
       
  if (rc != STAT_SECURE)  if (!STAT_ISEQUAL(rc, STAT_SECURE))
     return rc;      return rc;
         
   if (!neganswer)    if (!neganswer)
Line 1023  int dnssec_validate_ds(time_t now, struct dns_header * Line 1068  int dnssec_validate_ds(time_t now, struct dns_header *
               algo = *p++;                algo = *p++;
               digest = *p++;                digest = *p++;
                               
              if ((key = blockdata_alloc((char*)p, rdlen - 4)))              if (!ds_digest_name(digest) || !algo_digest_name(algo))
                 {                  {
                     a.log.keytag = keytag;
                     a.log.algo = algo;
                     a.log.digest = digest;
                     log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)", 0);
                     neg_ttl = ttl;
                   } 
                 else if ((key = blockdata_alloc((char*)p, rdlen - 4)))
                   {
                   a.ds.digest = digest;                    a.ds.digest = digest;
                   a.ds.keydata = key;                    a.ds.keydata = key;
                   a.ds.algo = algo;                    a.ds.algo = algo;
                   a.ds.keytag = keytag;                    a.ds.keytag = keytag;
                   a.ds.keylen = rdlen - 4;                    a.ds.keylen = rdlen - 4;
                  
                   if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))                    if (!cache_insert(name, &a, class, now, ttl, F_FORWARD | F_DS | F_DNSSECOK))
                     {                      {
                       blockdata_free(key);                        blockdata_free(key);
Line 1041  int dnssec_validate_ds(time_t now, struct dns_header * Line 1094  int dnssec_validate_ds(time_t now, struct dns_header *
                       a.log.keytag = keytag;                        a.log.keytag = keytag;
                       a.log.algo = algo;                        a.log.algo = algo;
                       a.log.digest = digest;                        a.log.digest = digest;
                      if (ds_digest_name(digest) && algo_digest_name(algo))                      log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu", 0);
                        log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu");                      found_supported = 1;
                      else 
                        log_query(F_NOEXTRA | F_KEYTAG | F_UPSTREAM, name, &a, "DS keytag %hu, algo %hu, digest %hu (not supported)"); 
                     }                       } 
                 }                  }
                               
               p = psave;                p = psave;
             }              }
   
           if (!ADD_RDLEN(header, p, plen, rdlen))            if (!ADD_RDLEN(header, p, plen, rdlen))
             return STAT_BOGUS; /* bad packet */              return STAT_BOGUS; /* bad packet */
         }          }
   
       cache_end_insert();        cache_end_insert();
   
         /* Fall through if no supported algo DS found. */
         if (found_supported)
           return STAT_OK;
     }      }
  else  
   flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;
   
   if (neganswer)
     {      {
       int flags = F_FORWARD | F_DS | F_NEG | F_DNSSECOK;  
               
       if (RCODE(header) == NXDOMAIN)        if (RCODE(header) == NXDOMAIN)
         flags |= F_NXDOMAIN;          flags |= F_NXDOMAIN;
               
Line 1068  int dnssec_validate_ds(time_t now, struct dns_header * Line 1124  int dnssec_validate_ds(time_t now, struct dns_header *
          to store presence/absence of NS. */           to store presence/absence of NS. */
       if (nons)        if (nons)
         flags &= ~F_DNSSECOK;          flags &= ~F_DNSSECOK;
         
       cache_start_insert();  
             
       /* Use TTL from NSEC for negative cache entries */  
       if (!cache_insert(name, NULL, class, now, neg_ttl, flags))  
         return STAT_BOGUS;  
         
       cache_end_insert();    
         
       log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS");  
     }      }
     
     cache_start_insert();
     
     /* Use TTL from NSEC for negative cache entries */
     if (!cache_insert(name, NULL, class, now, neg_ttl, flags))
       return STAT_BOGUS;
     
     cache_end_insert();  
     
     if (neganswer)
       log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, nons ? "no DS/cut" : "no DS", 0);
               
   return STAT_OK;    return STAT_OK;
 }  }
Line 1456  static int prove_non_existence_nsec3(struct dns_header Line 1513  static int prove_non_existence_nsec3(struct dns_header
       if (!(p = skip_name(nsecs[i], header, plen, 15)))        if (!(p = skip_name(nsecs[i], header, plen, 15)))
         return 0; /* bad packet */          return 0; /* bad packet */
               
      p += 10; /* type, class, TTL, rdlen */     p += 10; /* type, class, TTL, rdlen */
       algo = *p++;        algo = *p++;
               
       if ((hash = hash_find(nsec3_digest_name(algo))))        if ((hash = hash_find(nsec3_digest_name(algo))))
Line 1809  static int zone_status(char *name, int class, char *ke Line 1866  static int zone_status(char *name, int class, char *ke
    STAT_NEED_DS  need DS to complete validation (name is returned in keyname)     STAT_NEED_DS  need DS to complete validation (name is returned in keyname)
   
    daemon->rr_status points to a char array which corressponds to the RRs in the      daemon->rr_status points to a char array which corressponds to the RRs in the 
   answer and auth sections. This is set to 1 for each RR which is validated, and 0 for any which aren't.   answer and auth sections. This is set to >1 for each RR which is validated, and 0 for any which aren't.
   
    When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section.     When validating replies to DS records, we're only interested in the NSEC{3} RRs in the auth section.
    Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode     Other RRs in that section missing sigs will not cause am INSECURE reply. We determine this mode
Line 1825  int dnssec_validate_reply(time_t now, struct dns_heade Line 1882  int dnssec_validate_reply(time_t now, struct dns_heade
   int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;    int type1, class1, rdlen1 = 0, type2, class2, rdlen2, qclass, qtype, targetidx;
   int i, j, rc = STAT_INSECURE;    int i, j, rc = STAT_INSECURE;
   int secure = STAT_SECURE;    int secure = STAT_SECURE;
   
   /* extend rr_status if necessary */    /* extend rr_status if necessary */
   if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))    if (daemon->rr_status_sz < ntohs(header->ancount) + ntohs(header->nscount))
     {      {
Line 1854  int dnssec_validate_reply(time_t now, struct dns_heade Line 1911  int dnssec_validate_reply(time_t now, struct dns_heade
       
    /* Find all the targets we're looking for answers to.     /* Find all the targets we're looking for answers to.
      The zeroth array element is for the query, subsequent ones       The zeroth array element is for the query, subsequent ones
     for CNAME targets, unless the query is for a CNAME. */     for CNAME targets, unless the query is for a CNAME or ANY. */
   
   if (!expand_workspace(&targets, &target_sz, 0))    if (!expand_workspace(&targets, &target_sz, 0))
     return STAT_BOGUS;      return STAT_BOGUS;
Line 1873  int dnssec_validate_reply(time_t now, struct dns_heade Line 1930  int dnssec_validate_reply(time_t now, struct dns_heade
   if (qtype == T_RRSIG)    if (qtype == T_RRSIG)
     return STAT_INSECURE;      return STAT_INSECURE;
       
  if (qtype != T_CNAME)  if (qtype != T_CNAME && qtype != T_ANY)
     for (j = ntohs(header->ancount); j != 0; j--)       for (j = ntohs(header->ancount); j != 0; j--) 
       {        {
         if (!(p1 = skip_name(p1, header, plen, 10)))          if (!(p1 = skip_name(p1, header, plen, 10)))
Line 1947  int dnssec_validate_reply(time_t now, struct dns_heade Line 2004  int dnssec_validate_reply(time_t now, struct dns_heade
             {              {
               /* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */                /* NSEC and NSEC3 records must be signed. We make this assumption elsewhere. */
               if (type1 == T_NSEC || type1 == T_NSEC3)                if (type1 == T_NSEC || type1 == T_NSEC3)
                rc = STAT_INSECURE;                return STAT_BOGUS | DNSSEC_FAIL_NOSIG;
               else if (nons && i >= ntohs(header->ancount))                else if (nons && i >= ntohs(header->ancount))
                 /* If we're validating a DS reply, rather than looking for the value of AD bit,                  /* If we're validating a DS reply, rather than looking for the value of AD bit,
                    we only care that NSEC and NSEC3 RRs in the auth section are signed.                      we only care that NSEC and NSEC3 RRs in the auth section are signed. 
Line 1959  int dnssec_validate_reply(time_t now, struct dns_heade Line 2016  int dnssec_validate_reply(time_t now, struct dns_heade
                   if (check_unsigned && i < ntohs(header->ancount))                    if (check_unsigned && i < ntohs(header->ancount))
                     {                      {
                       rc = zone_status(name, class1, keyname, now);                        rc = zone_status(name, class1, keyname, now);
                      if (rc == STAT_SECURE)                      if (STAT_ISEQUAL(rc, STAT_SECURE))
                        rc = STAT_BOGUS;                        rc = STAT_BOGUS | DNSSEC_FAIL_NOSIG;
                       
                       if (class)                        if (class)
                         *class = class1; /* Class for NEED_DS or NEED_KEY */                          *class = class1; /* Class for NEED_DS or NEED_KEY */
                     }                      }
                   else                     else 
                     rc = STAT_INSECURE;                       rc = STAT_INSECURE; 
                                       
                  if (rc != STAT_INSECURE)                  if (!STAT_ISEQUAL(rc, STAT_INSECURE))
                     return rc;                      return rc;
                 }                  }
             }              }
Line 1978  int dnssec_validate_reply(time_t now, struct dns_heade Line 2036  int dnssec_validate_reply(time_t now, struct dns_heade
               strcpy(daemon->workspacename, keyname);                strcpy(daemon->workspacename, keyname);
               rc = zone_status(daemon->workspacename, class1, keyname, now);                rc = zone_status(daemon->workspacename, class1, keyname, now);
                               
              if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)              if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
                 {                  {
                   if (class)                    if (class)
                     *class = class1; /* Class for NEED_DS or NEED_KEY */                      *class = class1; /* Class for NEED_DS or NEED_KEY */
Line 1986  int dnssec_validate_reply(time_t now, struct dns_heade Line 2044  int dnssec_validate_reply(time_t now, struct dns_heade
                 }                  }
                               
               /* Zone is insecure, don't need to validate RRset */                /* Zone is insecure, don't need to validate RRset */
              if (rc == STAT_SECURE)              if (STAT_ISEQUAL(rc, STAT_SECURE))
                 {                  {
                   unsigned long sig_ttl;                    unsigned long sig_ttl;
                   rc = validate_rrset(now, header, plen, class1, type1, sigcnt,                    rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
                                       rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);                                        rrcnt, name, keyname, &wildname, NULL, 0, 0, 0, &sig_ttl);
                                       
                  if (rc == STAT_BOGUS || rc == STAT_NEED_KEY || rc == STAT_NEED_DS)                  if (STAT_ISEQUAL(rc, STAT_BOGUS) || STAT_ISEQUAL(rc, STAT_NEED_KEY) || STAT_ISEQUAL(rc, STAT_NEED_DS))
                     {                      {
                       if (class)                        if (class)
                         *class = class1; /* Class for DS or DNSKEY */                          *class = class1; /* Class for DS or DNSKEY */
Line 2025  int dnssec_validate_reply(time_t now, struct dns_heade Line 2083  int dnssec_validate_reply(time_t now, struct dns_heade
                      Note that we may not yet have validated the NSEC/NSEC3 RRsets.                        Note that we may not yet have validated the NSEC/NSEC3 RRsets. 
                      That's not a problem since if the RRsets later fail                       That's not a problem since if the RRsets later fail
                      we'll return BOGUS then. */                       we'll return BOGUS then. */
                  if (rc == STAT_SECURE_WILDCARD &&                  if (STAT_ISEQUAL(rc, STAT_SECURE_WILDCARD) &&
                       !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))                        !prove_non_existence(header, plen, keyname, name, type1, class1, wildname, NULL, NULL))
                    return STAT_BOGUS;                    return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
   
                   rc = STAT_SECURE;                    rc = STAT_SECURE;
                 }                  }
             }              }
         }          }
   
      if (rc == STAT_INSECURE)      if (STAT_ISEQUAL(rc, STAT_INSECURE))
         secure = STAT_INSECURE;          secure = STAT_INSECURE;
     }      }
   
   /* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */    /* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
  if (secure == STAT_SECURE)  for (j = 0; j <targetidx; j++)
    for (j = 0; j <targetidx; j++)    if ((p2 = targets[j]))
      if ((p2 = targets[j]))      {
        {        if (neganswer)
          if (neganswer)          *neganswer = 1;
            *neganswer = 1;        
                  if (!extract_name(header, plen, &p2, name, 1, 10))
          if (!extract_name(header, plen, &p2, name, 1, 10))          return STAT_BOGUS; /* bad packet */
            return STAT_BOGUS; /* bad packet */        
                  /* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */
          /* NXDOMAIN or NODATA reply, unanswered question is (name, qclass, qtype) */        
                  /* For anything other than a DS record, this situation is OK if either
          /* For anything other than a DS record, this situation is OK if either           the answer is in an unsigned zone, or there's a NSEC records. */
             the answer is in an unsigned zone, or there's a NSEC records. */        if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl))
          if (!prove_non_existence(header, plen, keyname, name, qtype, qclass, NULL, nons, nsec_ttl))          {
            {            /* Empty DS without NSECS */
              /* Empty DS without NSECS */            if (qtype == T_DS)
              if (qtype == T_DS)              return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
                return STAT_BOGUS;            
                          if (!STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
              if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)              {
                {                if (class)
                  if (class)                  *class = qclass; /* Class for NEED_DS or NEED_KEY */
                    *class = qclass; /* Class for NEED_DS or NEED_KEY */                return rc;
                  return rc;              } 
                }             
                          return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
              return STAT_BOGUS; /* signed zone, no NSECs */          }
            }      }
        } 
       
   return secure;    return secure;
 }  }
Line 2130  size_t dnssec_generate_query(struct dns_header *header Line 2187  size_t dnssec_generate_query(struct dns_header *header
   return ret;    return ret;
 }  }
   
   int errflags_to_ede(int status)
   {
     /* We can end up with more than one flag set for some errors,
        so this encodes a rough priority so the (eg) No sig is reported
        before no-unexpired-sig. */
   
     if (status & DNSSEC_FAIL_NYV)
       return EDE_SIG_NYV;
     else if (status & DNSSEC_FAIL_EXP)
       return EDE_SIG_EXP;
     else if (status & DNSSEC_FAIL_NOKEYSUP)
       return EDE_USUPDNSKEY;
     else if (status & DNSSEC_FAIL_NOZONE)
       return EDE_NO_ZONEKEY;
     else if (status & DNSSEC_FAIL_NOKEY)
       return EDE_NO_DNSKEY;
     else if (status & DNSSEC_FAIL_NODSSUP)
       return EDE_USUPDS;
     else if (status & DNSSEC_FAIL_NONSEC)
       return EDE_NO_NSEC;
     else if (status & DNSSEC_FAIL_INDET)
       return EDE_DNSSEC_IND;
     else if (status & DNSSEC_FAIL_NOSIG)
       return EDE_NO_RRSIG;
     else
       return EDE_UNSET;
   }
 #endif /* HAVE_DNSSEC */  #endif /* HAVE_DNSSEC */

Removed from v.1.1.1.3  
changed lines
  Added in v.1.1.1.4


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>