Initial changes for extended DNS error codes.

This commit is contained in:
Simon Kelley
2021-06-21 17:37:46 +01:00
parent be291d979d
commit 11c52d032b
4 changed files with 84 additions and 53 deletions

View File

@@ -80,10 +80,41 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */
#define EDNS0_OPTION_EDE 15 /* IANA - RFC 8914 */
#define EDNS0_OPTION_NOMDEVICEID 65073 /* Nominum temporary assignment */
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella temporary assignment */
/* RFC-8914 extended errors */
#define EDE_OTHER 0 /* Other */
#define EDE_USUPDNSKEY 1 /* Unsupported DNSKEY algo */
#define EDE_USUPDS 2 /* Unsupported DS Digest */
#define EDE_STALE 3 /* Stale answer */
#define EDE_FORGED 4 /* Forged answer */
#define EDE_DNSSEC_IND 5 /* DNSSEC Indeterminate */
#define EDE_DNSSEC_BOGUS 6 /* DNSSEC Bogus */
#define EDE_SIG_EXP 7 /* Signature Expired */
#define EDE_SIG_NYV 8 /* Signature Not Yet Valid */
#define EDE_NO_DNSKEY 9 /* DNSKEY missing */
#define EDE_NO_RRISG 10 /* RRSIGs missing */
#define EDE_NO_ZONEKEY 11 /* No Zone Key Bit Set */
#define EDE_NO_NSEC 12 /* NSEC Missing */
#define EDE_CACHED_ERR 13 /* Cached Error */
#define EDE_NOT_READY 14 /* Not Ready */
#define EDE_BLOCKED 15 /* Blocked */
#define EDE_CENSORED 16 /* Censored */
#define EDE_FILTERED 17 /* Filtered */
#define EDE_PROHIBITED 18 /* Prohibited */
#define EDE_STALE_NXD 19 /* Stale NXDOMAIN */
#define EDE_NOT_AUTH 20 /* Not Authoritative */
#define EDE_NOT_SUP 21 /* Not Supported */
#define EDE_NO_AUTH 22 /* No Reachable Authority */
#define EDE_NETERR 23 /* Network error */
#define EDE_INVALID_DATA 24 /* Invalid Data */
struct dns_header {
u16 id;
u8 hb3,hb4;

View File

@@ -686,17 +686,18 @@ struct hostsfile {
#define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080
/* DNSSEC status values. */
#define STAT_SECURE 1
#define STAT_INSECURE 2
#define STAT_BOGUS 3
#define STAT_NEED_DS 4
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_OK 8
#define STAT_ABANDONED 9
#define STAT_SECURE 0x10000
#define STAT_INSECURE 0x20000
#define STAT_BOGUS 0x30000
#define STAT_NEED_DS 0x40000
#define STAT_NEED_KEY 0x50000
#define STAT_TRUNCATED 0x60000
#define STAT_SECURE_WILDCARD 0x70000
#define STAT_OK 0x80000
#define STAT_ABANDONED 0x90000
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2

View File

@@ -847,8 +847,9 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
memcmp(ds_digest, digest, recp1->addr.ds.keylen) == 0 &&
explore_rrset(header, plen, class, T_DNSKEY, name, keyname, &sigcnt, &rrcnt) &&
sigcnt != 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)
STAT_ISEQUAL(validate_rrset(now, header, plen, class, T_DNSKEY, sigcnt, rrcnt, name, keyname,
NULL, key, rdlen - 4, algo, keytag, &sig_ttl),
STAT_SECURE))
{
valid = 1;
break;
@@ -971,7 +972,7 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
else
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);
rc = STAT_BOGUS;
@@ -984,13 +985,13 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
/* 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
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_BOGUS) || (STAT_ISEQUAL(rc, STAT_NEED_KEY) && hostname_isequal(name, keyname)))
{
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DS");
return STAT_BOGUS;
}
if (rc != STAT_SECURE)
if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc;
if (!neganswer)
@@ -1959,15 +1960,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (check_unsigned && i < ntohs(header->ancount))
{
rc = zone_status(name, class1, keyname, now);
if (rc == STAT_SECURE)
if (STAT_ISEQUAL(rc, STAT_SECURE))
rc = STAT_BOGUS;
if (class)
*class = class1; /* Class for NEED_DS or NEED_KEY */
}
else
else
rc = STAT_INSECURE;
if (rc != STAT_INSECURE)
if (!STAT_ISEQUAL(rc, STAT_INSECURE))
return rc;
}
}
@@ -1978,7 +1979,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
strcpy(daemon->workspacename, keyname);
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)
*class = class1; /* Class for NEED_DS or NEED_KEY */
@@ -1986,13 +1987,13 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
/* Zone is insecure, don't need to validate RRset */
if (rc == STAT_SECURE)
if (STAT_ISEQUAL(rc, STAT_SECURE))
{
unsigned long sig_ttl;
rc = validate_rrset(now, header, plen, class1, type1, sigcnt,
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)
*class = class1; /* Class for DS or DNSKEY */
@@ -2025,7 +2026,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
Note that we may not yet have validated the NSEC/NSEC3 RRsets.
That's not a problem since if the RRsets later fail
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))
return STAT_BOGUS;
@@ -2034,12 +2035,12 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
}
}
if (rc == STAT_INSECURE)
if (STAT_ISEQUAL(rc, STAT_INSECURE))
secure = STAT_INSECURE;
}
/* OK, all the RRsets validate, now see if we have a missing answer or CNAME target. */
if (secure == STAT_SECURE)
if (STAT_ISEQUAL(secure, STAT_SECURE))
for (j = 0; j <targetidx; j++)
if ((p2 = targets[j]))
{
@@ -2059,7 +2060,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
if (qtype == T_DS)
return STAT_BOGUS;
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE)
if (STAT_ISEQUAL((rc = zone_status(name, qclass, keyname, now)), STAT_SECURE))
{
if (class)
*class = qclass; /* Class for NEED_DS or NEED_KEY */

View File

@@ -556,7 +556,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
int munged = 0, is_sign;
unsigned int rcode = RCODE(header);
size_t plen;
(void)ad_reqd;
(void)do_bit;
(void)bogusanswer;
@@ -604,11 +604,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
}
else
{
unsigned short udpsz;
/* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge
requests for the client. We can't do this for signed packets. */
unsigned short udpsz;
GETSHORT(udpsz, sizep);
if (udpsz > daemon->edns_pktsz)
{
@@ -722,7 +721,6 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
/* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */
if (munged)
{
header->ancount = htons(0);
@@ -762,7 +760,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* As soon as anything returns BOGUS, we stop and unwind, to do otherwise
would invite infinite loops, since the answers to DNSKEY and DS queries
will not be cached, so they'll be repeated. */
if (status != STAT_BOGUS && status != STAT_TRUNCATED && status != STAT_ABANDONED)
if (!STAT_ISEQUAL(status, STAT_BOGUS) && !STAT_ISEQUAL(status, STAT_TRUNCATED) && !STAT_ISEQUAL(status, STAT_ABANDONED))
{
if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class);
@@ -773,7 +771,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL);
#ifdef HAVE_DUMPFILE
if (status == STAT_BOGUS)
if (STAT_ISEQUAL(status, STAT_BOGUS))
dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
header, (size_t)plen, &forward->sentto->addr, NULL);
#endif
@@ -781,7 +779,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Can't validate, as we're missing key data. Put this
answer aside, whilst we get that. */
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
if (STAT_ISEQUAL(status, STAT_NEED_DS) || STAT_ISEQUAL(status, STAT_NEED_KEY))
{
struct frec *new = NULL;
int serverind;
@@ -800,9 +798,9 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* validate routines leave name of required record in daemon->keyname */
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class,
status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz);
flags = (status == STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
flags = STAT_ISEQUAL(status, STAT_NEED_KEY) ? FREC_DNSKEY_QUERY : FREC_DS_QUERY;
hash = hash_questions(header, nn, daemon->namebuff);
if ((new = lookup_frec_by_query(hash, flags, FREC_DNSKEY_QUERY | FREC_DS_QUERY)))
@@ -872,7 +870,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
#endif
server_send_log(server, fd, header, nn, DUMP_SEC_QUERY,
F_NOEXTRA | F_DNSSEC, daemon->keyname,
querystr("dnssec-query", status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
querystr("dnssec-query", STAT_ISEQUAL(status, STAT_NEED_KEY) ? T_DNSKEY : T_DS));
server->queries++;
}
@@ -1085,33 +1083,33 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
no_cache_dnssec = 1;
#ifdef HAVE_DNSSEC
if (status != STAT_OK)
if (!STAT_ISEQUAL(status, STAT_OK))
{
no_cache_dnssec = 0;
if (status == STAT_TRUNCATED)
if (STAT_ISEQUAL(status, STAT_TRUNCATED))
header->hb3 |= HB3_TC;
else
{
char *result, *domain = "result";
if (status == STAT_ABANDONED)
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{
result = "ABANDONED";
status = STAT_BOGUS;
}
else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
if (status == STAT_BOGUS && extract_request(header, n, daemon->namebuff, NULL))
if (STAT_ISEQUAL(status, STAT_BOGUS) && extract_request(header, n, daemon->namebuff, NULL))
domain = daemon->namebuff;
log_query(F_SECSTAT, domain, NULL, result);
}
if (status == STAT_SECURE)
if (STAT_ISEQUAL(status, STAT_SECURE))
cache_secure = 1;
else if (status == STAT_BOGUS)
else if (STAT_ISEQUAL(status, STAT_BOGUS))
{
no_cache_dnssec = 1;
bogusanswer = 1;
@@ -1731,16 +1729,16 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
/* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
if (--(*keycount) == 0)
new_status = STAT_ABANDONED;
else if (status == STAT_NEED_KEY)
else if (STAT_ISEQUAL(status, STAT_NEED_KEY))
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
else if (status == STAT_NEED_DS)
else if (STAT_ISEQUAL(status, STAT_NEED_DS))
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL);
if (new_status != STAT_NEED_DS && new_status != STAT_NEED_KEY)
if (!STAT_ISEQUAL(new_status, STAT_NEED_DS) && !STAT_ISEQUAL(new_status, STAT_NEED_KEY))
break;
/* Can't validate because we need a key/DS whose name now in keyname.
@@ -1758,7 +1756,7 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
}
m = dnssec_generate_query(new_header, ((unsigned char *) new_header) + 65536, keyname, class,
new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS, server->edns_pktsz);
STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS, server->edns_pktsz);
if ((start = dnssec_server(server, daemon->keyname, &first, &last)) == -1 ||
(m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0)
@@ -1771,13 +1769,13 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
daemon->log_display_id = ++daemon->log_id;
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, keyname, &server->addr,
querystr("dnssec-query", new_status == STAT_NEED_KEY ? T_DNSKEY : T_DS));
querystr("dnssec-query", STAT_ISEQUAL(new_status, STAT_NEED_KEY) ? T_DNSKEY : T_DS));
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, have_mark, mark, keycount);
daemon->log_display_id = log_save;
if (new_status != STAT_OK)
if (!STAT_ISEQUAL(new_status, STAT_OK))
break;
}
@@ -2043,26 +2041,26 @@ unsigned char *tcp_request(int confd, time_t now,
serv, have_mark, mark, &keycount);
char *result, *domain = "result";
if (status == STAT_ABANDONED)
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{
result = "ABANDONED";
status = STAT_BOGUS;
}
else
result = (status == STAT_SECURE ? "SECURE" : (status == STAT_INSECURE ? "INSECURE" : "BOGUS"));
result = (STAT_ISEQUAL(status, STAT_SECURE) ? "SECURE" : (STAT_ISEQUAL(status, STAT_INSECURE) ? "INSECURE" : "BOGUS"));
if (status == STAT_BOGUS && extract_request(header, m, daemon->namebuff, NULL))
if (STAT_ISEQUAL(status, STAT_BOGUS) && extract_request(header, m, daemon->namebuff, NULL))
domain = daemon->namebuff;
log_query(F_SECSTAT, domain, NULL, result);
if (status == STAT_BOGUS)
if (STAT_ISEQUAL(status, STAT_BOGUS))
{
no_cache_dnssec = 1;
bogusanswer = 1;
}
if (status == STAT_SECURE)
if (STAT_ISEQUAL(status, STAT_SECURE))
cache_secure = 1;
}
#endif