|
|
| 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 */ |