Merge branch 'extended-error'

This commit is contained in:
Simon Kelley
2021-06-26 00:38:55 +01:00
7 changed files with 330 additions and 123 deletions

View File

@@ -1861,10 +1861,44 @@ char *querystr(char *desc, unsigned short type)
return buff ? buff : ""; return buff ? buff : "";
} }
static char *edestr(int ede)
{
switch (ede)
{
case EDE_OTHER: return "other";
case EDE_USUPDNSKEY: return "unsupported DNSKEY algorithm";
case EDE_USUPDS: return "unsupported DS digest";
case EDE_STALE: return "stale answer";
case EDE_FORGED: return "forged";
case EDE_DNSSEC_IND: return "DNSSEC indeterminate";
case EDE_DNSSEC_BOGUS: return "DNSSEC bogus";
case EDE_SIG_EXP: return "DNSSEC signature expired";
case EDE_SIG_NYV: return "DNSSEC sig not yet valid";
case EDE_NO_DNSKEY: return "DNSKEY missing";
case EDE_NO_RRSIG: return "RRSIG missing";
case EDE_NO_ZONEKEY: return "no zone key bit set";
case EDE_NO_NSEC: return "NSEC(3) missing";
case EDE_CACHED_ERR: return "cached error";
case EDE_NOT_READY: return "not ready";
case EDE_BLOCKED: return "blocked";
case EDE_CENSORED: return "censored";
case EDE_FILTERED: return "filtered";
case EDE_PROHIBITED: return "prohibited";
case EDE_STALE_NXD: return "stale NXDOMAIN";
case EDE_NOT_AUTH: return "not authoritative";
case EDE_NOT_SUP: return "not supported";
case EDE_NO_AUTH: return "no reachable authority";
case EDE_NETERR: return "network error";
case EDE_INVALID_DATA: return "invalid data";
default: return "unknown";
}
}
void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg) void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
{ {
char *source, *dest = daemon->addrbuff; char *source, *dest = daemon->addrbuff;
char *verb = "is"; char *verb = "is";
char *extra = "";
if (!option_bool(OPT_LOG)) if (!option_bool(OPT_LOG))
return; return;
@@ -1887,6 +1921,12 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
dest = "not implemented"; dest = "not implemented";
else else
sprintf(daemon->addrbuff, "%u", rcode); sprintf(daemon->addrbuff, "%u", rcode);
if (addr->log.ede != -1)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE:%s)", edestr(addr->log.ede));
}
} }
else else
inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6, inet_ntop(flags & F_IPV4 ? AF_INET : AF_INET6,
@@ -1932,7 +1972,15 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
else if (flags & F_UPSTREAM) else if (flags & F_UPSTREAM)
source = "reply"; source = "reply";
else if (flags & F_SECSTAT) else if (flags & F_SECSTAT)
{
if (addr && addr->log.ede != -1)
{
extra = daemon->addrbuff;
sprintf(extra, " (EDE:%s)", edestr(addr->log.ede));
}
source = "validation"; source = "validation";
dest = arg;
}
else if (flags & F_AUTH) else if (flags & F_AUTH)
source = "auth"; source = "auth";
else if (flags & F_SERVER) else if (flags & F_SERVER)
@@ -1966,11 +2014,11 @@ void log_query(unsigned int flags, char *name, union all_addr *addr, char *arg)
if (option_bool(OPT_EXTRALOG)) if (option_bool(OPT_EXTRALOG))
{ {
if (flags & F_NOEXTRA) if (flags & F_NOEXTRA)
my_syslog(LOG_INFO, "%u %s %s %s %s", daemon->log_display_id, source, name, verb, dest); my_syslog(LOG_INFO, "%u %s %s %s %s%s", daemon->log_display_id, source, name, verb, dest, extra);
else else
{ {
int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2); int port = prettyprint_addr(daemon->log_source_addr, daemon->addrbuff2);
my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest); my_syslog(LOG_INFO, "%u %s/%u %s %s %s %s%s", daemon->log_display_id, daemon->addrbuff2, port, source, name, verb, dest, extra);
} }
} }
else else

View File

@@ -80,10 +80,41 @@
#define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */ #define EDNS0_OPTION_MAC 65001 /* dyndns.org temporary assignment */
#define EDNS0_OPTION_CLIENT_SUBNET 8 /* IANA */ #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_NOMDEVICEID 65073 /* Nominum temporary assignment */
#define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */ #define EDNS0_OPTION_NOMCPEID 65074 /* Nominum temporary assignment */
#define EDNS0_OPTION_UMBRELLA 20292 /* Cisco Umbrella 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_RRSIG 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 { struct dns_header {
u16 id; u16 id;
u8 hb3,hb4; u8 hb3,hb4;

View File

@@ -323,6 +323,7 @@ union all_addr {
/* for log_query */ /* for log_query */
struct { struct {
unsigned short keytag, algo, digest, rcode; unsigned short keytag, algo, digest, rcode;
int ede;
} log; } log;
}; };
@@ -686,17 +687,28 @@ struct hostsfile {
#define DUMP_BOGUS 0x0040 #define DUMP_BOGUS 0x0040
#define DUMP_SEC_BOGUS 0x0080 #define DUMP_SEC_BOGUS 0x0080
/* DNSSEC status values. */ /* DNSSEC status values. */
#define STAT_SECURE 1 #define STAT_SECURE 0x10000
#define STAT_INSECURE 2 #define STAT_INSECURE 0x20000
#define STAT_BOGUS 3 #define STAT_BOGUS 0x30000
#define STAT_NEED_DS 4 #define STAT_NEED_DS 0x40000
#define STAT_NEED_KEY 5 #define STAT_NEED_KEY 0x50000
#define STAT_TRUNCATED 6 #define STAT_TRUNCATED 0x60000
#define STAT_SECURE_WILDCARD 7 #define STAT_SECURE_WILDCARD 0x70000
#define STAT_OK 8 #define STAT_OK 0x80000
#define STAT_ABANDONED 9 #define STAT_ABANDONED 0x90000
#define DNSSEC_FAIL_NYV 0x0001 /* key not yet valid */
#define DNSSEC_FAIL_EXP 0x0002 /* key expired */
#define DNSSEC_FAIL_INDET 0x0004 /* indetermined */
#define DNSSEC_FAIL_NOKEYSUP 0x0008 /* no supported key algo. */
#define DNSSEC_FAIL_NOSIG 0x0010 /* No RRsigs */
#define DNSSEC_FAIL_NOZONE 0x0020 /* No Zone bit set */
#define DNSSEC_FAIL_NONSEC 0x0040 /* No NSEC */
#define DNSSEC_FAIL_NODSSUP 0x0080 /* no supported DS algo. */
#define DNSSEC_FAIL_NOKEY 0x0100 /* no DNSKEY */
#define STAT_ISEQUAL(a, b) (((a) & 0xffff0000) == (b))
#define FREC_NOREBIND 1 #define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2 #define FREC_CHECKING_DISABLED 2
@@ -1280,7 +1292,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen);
unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen); unsigned char *skip_section(unsigned char *ansp, int count, struct dns_header *header, size_t plen);
unsigned int extract_request(struct dns_header *header, size_t qlen, unsigned int extract_request(struct dns_header *header, size_t qlen,
char *name, unsigned short *typep); char *name, unsigned short *typep);
void setup_reply(struct dns_header *header, unsigned int flags); void setup_reply(struct dns_header *header, unsigned int flags, int ede);
int extract_addresses(struct dns_header *header, size_t qlen, char *name, int extract_addresses(struct dns_header *header, size_t qlen, char *name,
time_t now, char **ipsets, int is_sign, int check_rebind, time_t now, char **ipsets, int is_sign, int check_rebind,
int no_cache_dnssec, int secure, int *doctored); int no_cache_dnssec, int secure, int *doctored);
@@ -1311,6 +1323,7 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
#endif #endif
/* dnssec.c */ /* dnssec.c */
#ifdef HAVE_DNSSEC
size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz); size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char *name, int class, int type, int edns_pktsz);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class); int dnssec_validate_by_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); int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
@@ -1319,6 +1332,8 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen); int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen);
size_t filter_rrsigs(struct dns_header *header, size_t plen); size_t filter_rrsigs(struct dns_header *header, size_t plen);
int setup_timestamp(void); int setup_timestamp(void);
int errflags_to_ede(int status);
#endif
/* hash_questions.c */ /* hash_questions.c */
void hash_questions_init(void); void hash_questions_init(void);
@@ -1742,7 +1757,7 @@ int lookup_domain(char *qdomain, int flags, int *lowout, int *highout);
int filter_servers(int seed, int flags, int *lowout, int *highout); int filter_servers(int seed, int flags, int *lowout, int *highout);
int is_local_answer(time_t now, int first, char *name); int is_local_answer(time_t now, int first, char *name);
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header,
char *name, char *limit, int first, int last); char *name, char *limit, int first, int last, int ede);
int server_samegroup(struct server *a, struct server *b); int server_samegroup(struct server *a, struct server *b);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp); int dnssec_server(struct server *server, char *keyname, int *firstp, int *lastp);

View File

@@ -215,14 +215,6 @@ 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.
@@ -534,6 +526,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
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);
@@ -558,6 +551,8 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
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);
GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */ GETSHORT(rdlen, p); /* rdlen >= 18 checked previously */
@@ -574,9 +569,28 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
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;
@@ -730,7 +744,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
} }
} }
return STAT_BOGUS; return STAT_BOGUS | failflags;
} }
@@ -751,17 +765,18 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
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)))
@@ -795,14 +810,17 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
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;
@@ -823,15 +841,23 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
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. */
@@ -846,15 +872,26 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
(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) if (sigcnt == 0)
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; valid = 1;
break; break;
} }
} }
} }
}
blockdata_free(key); blockdata_free(key);
} }
@@ -936,7 +973,7 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
} }
log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY"); log_query(F_NOEXTRA | F_UPSTREAM, name, NULL, "BOGUS DNSKEY");
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
@@ -971,10 +1008,11 @@ int dnssec_validate_ds(time_t now, struct dns_header *header, size_t plen, char
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");
return STAT_BOGUS | DNSSEC_FAIL_INDET;
} }
p = (unsigned char *)(header+1); p = (unsigned char *)(header+1);
@@ -984,13 +1022,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 /* 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");
return STAT_BOGUS; return STAT_BOGUS;
} }
if (rc != STAT_SECURE) if (!STAT_ISEQUAL(rc, STAT_SECURE))
return rc; return rc;
if (!neganswer) if (!neganswer)
@@ -1959,15 +1997,15 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
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;
} }
} }
@@ -1978,7 +2016,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
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 */
@@ -1986,13 +2024,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 */ /* 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 */
@@ -2025,21 +2063,21 @@ 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. 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) if (STAT_ISEQUAL(secure, STAT_SECURE))
for (j = 0; j <targetidx; j++) for (j = 0; j <targetidx; j++)
if ((p2 = targets[j])) if ((p2 = targets[j]))
{ {
@@ -2057,16 +2095,16 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{ {
/* Empty DS without NSECS */ /* Empty DS without NSECS */
if (qtype == T_DS) if (qtype == T_DS)
return STAT_BOGUS; return STAT_BOGUS | DNSSEC_FAIL_NONSEC;
if ((rc = zone_status(name, qclass, keyname, now)) != STAT_SECURE) if (STAT_ISEQUAL((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; /* signed zone, no NSECs */ return STAT_BOGUS | DNSSEC_FAIL_NONSEC; /* signed zone, no NSECs */
} }
} }
@@ -2130,4 +2168,31 @@ size_t dnssec_generate_query(struct dns_header *header, unsigned char *end, char
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 -1;
}
#endif /* HAVE_DNSSEC */ #endif /* HAVE_DNSSEC */

View File

@@ -340,7 +340,7 @@ int is_local_answer(time_t now, int first, char *name)
return rc; return rc;
} }
size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last) size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header *header, char *name, char *limit, int first, int last, int ede)
{ {
int trunc = 0; int trunc = 0;
unsigned char *p; unsigned char *p;
@@ -350,7 +350,7 @@ size_t make_local_answer(int flags, int gotname, size_t size, struct dns_header
if (flags & (F_NXDOMAIN | F_NOERR)) if (flags & (F_NXDOMAIN | F_NOERR))
log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL); log_query(flags | gotname | F_NEG | F_CONFIG | F_FORWARD, name, NULL, NULL);
setup_reply(header, flags); setup_reply(header, flags, ede);
if (!(p = skip_questions(header, size))) if (!(p = skip_questions(header, size)))
return 0; return 0;

View File

@@ -177,6 +177,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
int subnet, cacheable, forwarded = 0; int subnet, cacheable, forwarded = 0;
size_t edns0_len; size_t edns0_len;
unsigned char *pheader; unsigned char *pheader;
int ede = -1;
(void)do_bit; (void)do_bit;
if (header->hb4 & HB4_CD) if (header->hb4 & HB4_CD)
@@ -270,7 +271,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* no available server. */ /* no available server. */
if (!lookup_domain(daemon->namebuff, gotname, &first, &last)) if (!lookup_domain(daemon->namebuff, gotname, &first, &last))
{
ede = EDE_NOT_READY;
goto reply; goto reply;
}
/* Configured answer. */ /* Configured answer. */
if ((flags = is_local_answer(now, first, daemon->namebuff))) if ((flags = is_local_answer(now, first, daemon->namebuff)))
@@ -521,15 +525,23 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
/* could not send on, prepare to return */ /* could not send on, prepare to return */
header->id = htons(forward->frec_src.orig_id); header->id = htons(forward->frec_src.orig_id);
free_frec(forward); /* cancel */ free_frec(forward); /* cancel */
ede = EDE_NETERR;
reply: reply:
if (udpfd != -1) if (udpfd != -1)
{ {
if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, limit, first, last))) if (!(plen = make_local_answer(flags, gotname, plen, header, daemon->namebuff, limit, first, last, ede)))
return 0; return 0;
if (oph) if (oph)
{
u16 swap = htons((u16)ede);
if (ede != -1)
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); plen = add_pseudoheader(header, plen, (unsigned char *)limit, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
}
#if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS) #if defined(HAVE_CONNTRACK) && defined(HAVE_UBUS)
if (option_bool(OPT_CMARK_ALST_EN)) if (option_bool(OPT_CMARK_ALST_EN))
@@ -549,7 +561,7 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind, static size_t process_reply(struct dns_header *header, time_t now, struct server *server, size_t n, int check_rebind,
int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader, int no_cache, int cache_secure, int bogusanswer, int ad_reqd, int do_bit, int added_pheader,
int check_subnet, union mysockaddr *query_source) int check_subnet, union mysockaddr *query_source, unsigned char *limit, int ede)
{ {
unsigned char *pheader, *sizep; unsigned char *pheader, *sizep;
char **sets = 0; char **sets = 0;
@@ -604,11 +616,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
else else
{ {
unsigned short udpsz;
/* If upstream is advertising a larger UDP packet size /* If upstream is advertising a larger UDP packet size
than we allow, trim it so that we don't get overlarge than we allow, trim it so that we don't get overlarge
requests for the client. We can't do this for signed packets. */ requests for the client. We can't do this for signed packets. */
unsigned short udpsz;
GETSHORT(udpsz, sizep); GETSHORT(udpsz, sizep);
if (udpsz > daemon->edns_pktsz) if (udpsz > daemon->edns_pktsz)
{ {
@@ -645,6 +656,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
{ {
union all_addr a; union all_addr a;
a.log.rcode = rcode; a.log.rcode = rcode;
a.log.ede = ede;
log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL); log_query(F_UPSTREAM | F_RCODE, "error", &a, NULL);
return resize_packet(header, n, pheader, plen); return resize_packet(header, n, pheader, plen);
@@ -667,6 +679,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
header->hb3 &= ~HB3_AA; header->hb3 &= ~HB3_AA;
cache_secure = 0; cache_secure = 0;
ede = EDE_BLOCKED;
} }
else else
{ {
@@ -693,6 +706,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff); my_syslog(LOG_WARNING, _("possible DNS-rebind attack detected: %s"), daemon->namebuff);
munged = 1; munged = 1;
cache_secure = 0; cache_secure = 0;
ede = EDE_BLOCKED;
} }
if (doctored) if (doctored)
@@ -720,9 +734,14 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
#endif #endif
if (pheader && ede != -1)
{
u16 swap = htons((u16)ede);
n = add_pseudoheader(header, n, limit, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 1);
}
/* do this after extract_addresses. Ensure NODATA reply and remove /* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */ nameserver info. */
if (munged) if (munged)
{ {
header->ancount = htons(0); header->ancount = htons(0);
@@ -762,7 +781,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 /* 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 would invite infinite loops, since the answers to DNSKEY and DS queries
will not be cached, so they'll be repeated. */ 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) if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class); status = dnssec_validate_by_ds(now, header, plen, daemon->namebuff, daemon->keyname, forward->class);
@@ -773,7 +792,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
!option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC), !option_bool(OPT_DNSSEC_IGN_NS) && (forward->sentto->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL); NULL, NULL, NULL);
#ifdef HAVE_DUMPFILE #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, dump_packet((forward->flags & (FREC_DNSKEY_QUERY | FREC_DS_QUERY)) ? DUMP_SEC_BOGUS : DUMP_BOGUS,
header, (size_t)plen, &forward->sentto->addr, NULL); header, (size_t)plen, &forward->sentto->addr, NULL);
#endif #endif
@@ -781,7 +800,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* Can't validate, as we're missing key data. Put this /* Can't validate, as we're missing key data. Put this
answer aside, whilst we get that. */ 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; struct frec *new = NULL;
int serverind; int serverind;
@@ -800,9 +819,9 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
/* validate routines leave name of required record in daemon->keyname */ /* validate routines leave name of required record in daemon->keyname */
nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz, nn = dnssec_generate_query(header, ((unsigned char *) header) + server->edns_pktsz,
daemon->keyname, forward->class, 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); hash = hash_questions(header, nn, daemon->namebuff);
if ((new = lookup_frec_by_query(hash, flags, FREC_DNSKEY_QUERY | FREC_DS_QUERY))) if ((new = lookup_frec_by_query(hash, flags, FREC_DNSKEY_QUERY | FREC_DS_QUERY)))
@@ -872,7 +891,7 @@ static void dnssec_validate(struct frec *forward, struct dns_header *header,
#endif #endif
server_send_log(server, fd, header, nn, DUMP_SEC_QUERY, server_send_log(server, fd, header, nn, DUMP_SEC_QUERY,
F_NOEXTRA | F_DNSSEC, daemon->keyname, 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++; server->queries++;
} }
@@ -1073,6 +1092,7 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
{ {
int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0; int check_rebind = 0, no_cache_dnssec = 0, cache_secure = 0, bogusanswer = 0;
size_t nn; size_t nn;
int ede = -1;
(void)status; (void)status;
@@ -1085,36 +1105,40 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
no_cache_dnssec = 1; no_cache_dnssec = 1;
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (status != STAT_OK) if (!STAT_ISEQUAL(status, STAT_OK))
{ {
/* status is STAT_OK when validation not turned on. */
no_cache_dnssec = 0; no_cache_dnssec = 0;
if (status == STAT_TRUNCATED) if (STAT_ISEQUAL(status, STAT_TRUNCATED))
header->hb3 |= HB3_TC; header->hb3 |= HB3_TC;
else else
{ {
char *result, *domain = "result"; char *result, *domain = "result";
union all_addr a;
if (status == STAT_ABANDONED) a.log.ede = ede = errflags_to_ede(status);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{ {
result = "ABANDONED"; result = "ABANDONED";
status = STAT_BOGUS; status = STAT_BOGUS;
} }
else 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_SECURE))
domain = daemon->namebuff;
log_query(F_SECSTAT, domain, NULL, result);
}
if (status == STAT_SECURE)
cache_secure = 1; cache_secure = 1;
else if (status == STAT_BOGUS) else if (STAT_ISEQUAL(status, STAT_BOGUS))
{ {
no_cache_dnssec = 1; no_cache_dnssec = 1;
bogusanswer = 1; bogusanswer = 1;
if (extract_request(header, n, daemon->namebuff, NULL))
domain = daemon->namebuff;
}
log_query(F_SECSTAT, domain, &a, result);
} }
} }
#endif #endif
@@ -1135,7 +1159,8 @@ static void return_reply(time_t now, struct frec *forward, struct dns_header *he
if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer, if ((nn = process_reply(header, now, forward->sentto, (size_t)n, check_rebind, no_cache_dnssec, cache_secure, bogusanswer,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION, forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source))) forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->frec_src.source,
((unsigned char *)header) + daemon->edns_pktsz, ede)))
{ {
struct frec_src *src; struct frec_src *src;
@@ -1221,7 +1246,7 @@ static size_t answer_disallowed(struct dns_header *header, size_t qlen, u32 mark
ubus_event_bcast_connmark_allowlist_refused(mark, name); ubus_event_bcast_connmark_allowlist_refused(mark, name);
#endif #endif
setup_reply(header, /* flags: */ 0); setup_reply(header, /* flags: */ 0, EDE_BLOCKED);
if (!(p = skip_questions(header, qlen))) if (!(p = skip_questions(header, qlen)))
return 0; return 0;
@@ -1545,10 +1570,13 @@ void receive_query(struct listener *listen, time_t now)
#ifdef HAVE_CONNTRACK #ifdef HAVE_CONNTRACK
else if (!allowed) else if (!allowed)
{ {
u16 swap = htons(EDE_BLOCKED);
m = answer_disallowed(header, (size_t)n, (u32)mark, is_single_query ? daemon->namebuff : NULL); m = answer_disallowed(header, (size_t)n, (u32)mark, is_single_query ? daemon->namebuff : NULL);
if (have_pseudoheader && m != 0) if (have_pseudoheader && m != 0)
m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); m = add_pseudoheader(header, m, ((unsigned char *) header) + udp_size, daemon->edns_pktsz,
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
if (m >= 1) if (m >= 1)
{ {
@@ -1731,16 +1759,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 */ /* limit the amount of work we do, to avoid cycling forever on loops in the DNS */
if (--(*keycount) == 0) if (--(*keycount) == 0)
new_status = STAT_ABANDONED; 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); 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); new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
else else
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, new_status = dnssec_validate_reply(now, header, n, name, keyname, &class,
!option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC), !option_bool(OPT_DNSSEC_IGN_NS) && (server->flags & SERV_DO_DNSSEC),
NULL, NULL, NULL); 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; break;
/* Can't validate because we need a key/DS whose name now in keyname. /* Can't validate because we need a key/DS whose name now in keyname.
@@ -1758,7 +1786,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, 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 || if ((start = dnssec_server(server, daemon->keyname, &first, &last)) == -1 ||
(m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0) (m = tcp_talk(first, last, start, packet, m, have_mark, mark, &server)) == 0)
@@ -1771,13 +1799,13 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
daemon->log_display_id = ++daemon->log_id; daemon->log_display_id = ++daemon->log_id;
log_query_mysockaddr(F_NOEXTRA | F_DNSSEC, keyname, &server->addr, 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); 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; daemon->log_display_id = log_save;
if (new_status != STAT_OK) if (!STAT_ISEQUAL(new_status, STAT_OK))
break; break;
} }
@@ -1877,6 +1905,8 @@ unsigned char *tcp_request(int confd, time_t now,
while (1) while (1)
{ {
int ede = -1;
if (query_count == TCP_MAX_QUERIES || if (query_count == TCP_MAX_QUERIES ||
!packet || !packet ||
!read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) || !read_write(confd, &c1, 1, 1) || !read_write(confd, &c2, 1, 1) ||
@@ -1962,10 +1992,13 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_CONNTRACK #ifdef HAVE_CONNTRACK
else if (!allowed) else if (!allowed)
{ {
u16 swap = htons(EDE_BLOCKED);
m = answer_disallowed(header, size, (u32)mark, is_single_query ? daemon->namebuff : NULL); m = answer_disallowed(header, size, (u32)mark, is_single_query ? daemon->namebuff : NULL);
if (have_pseudoheader && m != 0) if (have_pseudoheader && m != 0)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz,
EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
} }
#endif #endif
#ifdef HAVE_AUTH #ifdef HAVE_AUTH
@@ -1998,7 +2031,9 @@ unsigned char *tcp_request(int confd, time_t now,
!strchr(daemon->namebuff, '.') && !strchr(daemon->namebuff, '.') &&
strlen(daemon->namebuff) != 0) strlen(daemon->namebuff) != 0)
flags = F_NOERR; flags = F_NOERR;
else if (lookup_domain(daemon->namebuff, gotname, &first, &last) && !(flags = is_local_answer(now, first, daemon->namebuff))) else if (!lookup_domain(daemon->namebuff, gotname, &first, &last))
ede = EDE_NOT_READY; /* No configured servers */
else if (!(flags = is_local_answer(now, first, daemon->namebuff)))
{ {
master = daemon->serverarray[first]; master = daemon->serverarray[first];
@@ -2028,7 +2063,10 @@ unsigned char *tcp_request(int confd, time_t now,
/* Loop round available servers until we succeed in connecting to one. */ /* Loop round available servers until we succeed in connecting to one. */
if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0) if ((m = tcp_talk(first, last, start, packet, size, have_mark, mark, &serv)) == 0)
{
ede = EDE_NETERR;
break; break;
}
/* get query name again for logging - may have been overwritten */ /* get query name again for logging - may have been overwritten */
if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype))) if (!(gotname = extract_request(header, (unsigned int)size, daemon->namebuff, &qtype)))
@@ -2043,27 +2081,29 @@ unsigned char *tcp_request(int confd, time_t now,
serv, have_mark, mark, &keycount); serv, have_mark, mark, &keycount);
char *result, *domain = "result"; char *result, *domain = "result";
if (status == STAT_ABANDONED) union all_addr a;
a.log.ede = ede = errflags_to_ede(status);
if (STAT_ISEQUAL(status, STAT_ABANDONED))
{ {
result = "ABANDONED"; result = "ABANDONED";
status = STAT_BOGUS; status = STAT_BOGUS;
} }
else 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_SECURE))
domain = daemon->namebuff; cache_secure = 1;
else if (STAT_ISEQUAL(status, STAT_BOGUS))
log_query(F_SECSTAT, domain, NULL, result);
if (status == STAT_BOGUS)
{ {
no_cache_dnssec = 1; no_cache_dnssec = 1;
bogusanswer = 1; bogusanswer = 1;
if (extract_request(header, m, daemon->namebuff, NULL))
domain = daemon->namebuff;
} }
if (status == STAT_SECURE) log_query(F_SECSTAT, domain, &a, result);
cache_secure = 1;
} }
#endif #endif
@@ -2080,7 +2120,7 @@ unsigned char *tcp_request(int confd, time_t now,
m = process_reply(header, now, serv, (unsigned int)m, m = process_reply(header, now, serv, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer, option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec, cache_secure, bogusanswer,
ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr); ad_reqd, do_bit, added_pheader, check_subnet, &peer_addr, ((unsigned char *)header) + 65536, ede);
} }
} }
} }
@@ -2089,12 +2129,19 @@ unsigned char *tcp_request(int confd, time_t now,
if (m == 0) if (m == 0)
{ {
if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff, if (!(m = make_local_answer(flags, gotname, size, header, daemon->namebuff,
((char *) header) + 65536, first, last))) ((char *) header) + 65536, first, last, ede)))
break; break;
if (have_pseudoheader) if (have_pseudoheader)
{
u16 swap = htons((u16)ede);
if (ede != -1)
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, EDNS0_OPTION_EDE, (unsigned char *)&swap, 2, do_bit, 0);
else
m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0); m = add_pseudoheader(header, m, ((unsigned char *) header) + 65536, daemon->edns_pktsz, 0, NULL, 0, do_bit, 0);
} }
}
check_log_writer(1); check_log_writer(1);

View File

@@ -1017,7 +1017,7 @@ unsigned int extract_request(struct dns_header *header, size_t qlen, char *name,
return F_QUERY; return F_QUERY;
} }
void setup_reply(struct dns_header *header, unsigned int flags) void setup_reply(struct dns_header *header, unsigned int flags, int ede)
{ {
/* clear authoritative and truncated flags, set QR flag */ /* clear authoritative and truncated flags, set QR flag */
header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR; header->hb3 = (header->hb3 & ~(HB3_AA | HB3_TC )) | HB3_QR;
@@ -1040,6 +1040,7 @@ void setup_reply(struct dns_header *header, unsigned int flags)
{ {
union all_addr a; union all_addr a;
a.log.rcode = REFUSED; a.log.rcode = REFUSED;
a.log.ede = ede;
log_query(F_CONFIG | F_RCODE, "error", &a, NULL); log_query(F_CONFIG | F_RCODE, "error", &a, NULL);
SET_RCODE(header, REFUSED); SET_RCODE(header, REFUSED);
} }