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