Commit a25720a3 authored by Simon Kelley's avatar Simon Kelley

protocol handling for DNSSEC

parent cc111e0b
...@@ -230,7 +230,8 @@ struct event_desc { ...@@ -230,7 +230,8 @@ struct event_desc {
#define OPT_QUIET_DHCP6 43 #define OPT_QUIET_DHCP6 43
#define OPT_QUIET_RA 44 #define OPT_QUIET_RA 44
#define OPT_DNSSEC_VALID 45 #define OPT_DNSSEC_VALID 45
#define OPT_LAST 46 #define OPT_DNSSEC_PERMISS 46
#define OPT_LAST 47
/* extra flags for my_syslog, we use a couple of facilities since they are known /* extra flags for my_syslog, we use a couple of facilities since they are known
not to occupy the same bits as priorities, no matter how syslog.h is set up. */ not to occupy the same bits as priorities, no matter how syslog.h is set up. */
......
...@@ -511,7 +511,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server ...@@ -511,7 +511,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
if (cache_secure) if (!(header->hb4 & HB4_CD) && cache_secure)
header->hb4 |= HB4_AD; header->hb4 |= HB4_AD;
#endif #endif
...@@ -556,6 +556,31 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server ...@@ -556,6 +556,31 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
} }
} }
#ifdef HAVE_DNSSEC
if (no_cache && !(header->hb4 & HB4_CD))
{
if (option_bool(OPT_DNSSEC_PERMISS))
{
unsigned short type;
char types[20];
if (extract_request(header, (size_t)n, daemon->namebuff, &type))
{
querystr("", types, type);
my_syslog(LOG_WARNING, _("DNSSEC validation failed: query %s%s"), daemon->namebuff, types);
}
else
my_syslog(LOG_WARNING, _("DNSSEC validation failed for unknown query"));
}
else
{
/* Bogus reply, turn into SERVFAIL */
SET_RCODE(header, SERVFAIL);
munged = 1;
}
}
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove /* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */ nameserver info. */
...@@ -824,7 +849,6 @@ void reply_query(int fd, int family, time_t now) ...@@ -824,7 +849,6 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_SECURE) if (status == STAT_SECURE)
cache_secure = 1; cache_secure = 1;
/* TODO return SERVFAIL here */
else if (status == STAT_BOGUS) else if (status == STAT_BOGUS)
no_cache_dnssec = 1; no_cache_dnssec = 1;
......
...@@ -140,7 +140,7 @@ struct myoption { ...@@ -140,7 +140,7 @@ struct myoption {
#define LOPT_QUIET_RA 328 #define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329 #define LOPT_SEC_VALID 329
#define LOPT_DNSKEY 330 #define LOPT_DNSKEY 330
#define LOPT_DNSSEC_PERM 331
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
...@@ -278,6 +278,7 @@ static const struct myoption opts[] = ...@@ -278,6 +278,7 @@ static const struct myoption opts[] =
{ "synth-domain", 1, 0, LOPT_SYNTH }, { "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID }, { "dnssec", 0, 0, LOPT_SEC_VALID },
{ "dnskey", 1, 0, LOPT_DNSKEY }, { "dnskey", 1, 0, LOPT_DNSKEY },
{ "dnssec-permissive", 0, 0, LOPT_DNSSEC_PERM },
#ifdef OPTION6_PREFIX_CLASS #ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS }, { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif #endif
...@@ -428,10 +429,9 @@ static struct { ...@@ -428,10 +429,9 @@ static struct {
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL }, { LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL },
{ LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL }, { LOPT_IPSET, ARG_DUP, "/<domain>/<ipset>[,<ipset>...]", gettext_noop("Specify ipsets to which matching domains should be added"), NULL },
{ LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL }, { LOPT_SYNTH, ARG_DUP, "<domain>,<range>,[<prefix>]", gettext_noop("Specify a domain and address range for synthesised names"), NULL },
#ifdef HAVE_DNSSEC
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL }, { LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
{ LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL }, { LOPT_DNSKEY, ARG_DUP, "<domain>,<algo>,<key>", gettext_noop("Specify trust anchor DNSKEY"), NULL },
#endif { LOPT_DNSSEC_PERM, OPT_DNSSEC_PERMISS, NULL, gettext_noop("Do NOT return SERVFAIL whne DNSSEC validation fails."), NULL },
#ifdef OPTION6_PREFIX_CLASS #ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL }, { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif #endif
......
...@@ -511,14 +511,17 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned ...@@ -511,14 +511,17 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
int optno, unsigned char *opt, size_t optlen, int set_do) int optno, unsigned char *opt, size_t optlen, int set_do)
{ {
unsigned char *lenp, *datap, *p; unsigned char *lenp, *datap, *p;
int rdlen; int rdlen, is_sign;
if (ntohs(header->arcount) == 0) if (!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)))
{ {
if (is_sign)
return plen;
/* We are adding the pseudoheader */ /* We are adding the pseudoheader */
if (!(p = skip_questions(header, plen)) || if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p, !(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount), ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen))) header, plen)))
return plen; return plen;
*p++ = 0; /* empty name */ *p++ = 0; /* empty name */
...@@ -531,16 +534,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned ...@@ -531,16 +534,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
rdlen = 0; rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4))) if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */ return plen; /* Too big */
header->arcount = htons(1); header->arcount = htons(ntohs(header->arcount) + 1);
datap = p; datap = p;
} }
else else
{ {
int i, is_sign; int i;
unsigned short code, len, flags; unsigned short code, len, flags;
/* Must be at the end, if exists */
if (ntohs(header->arcount) != 1 || if (ntohs(header->arcount) != 1 ||
!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
is_sign || is_sign ||
(!(p = skip_name(p, header, plen, 10)))) (!(p = skip_name(p, header, plen, 10))))
return plen; return plen;
...@@ -1147,7 +1150,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t ...@@ -1147,7 +1150,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
/* If the packet holds exactly one query /* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */ return F_IPV4 or F_IPV6 and leave the name from the query in name */
unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep) unsigned int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
{ {
unsigned char *p = (unsigned char *)(header+1); unsigned char *p = (unsigned char *)(header+1);
...@@ -1447,23 +1449,30 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1447,23 +1449,30 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
int nameoffset; int nameoffset;
unsigned short flag; unsigned short flag;
int q, ans, anscount = 0, addncount = 0; int q, ans, anscount = 0, addncount = 0;
int dryrun = 0, sec_reqd = 0; int dryrun = 0, sec_reqd = 0, have_pseudoheader = 0;
int is_sign; int is_sign;
struct crec *crecp; struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec; struct mx_srv_record *rec;
size_t len;
/* Don't return AD set even for local data if checking disabled. */
if (header->hb4 & HB4_CD)
sec_data = 0;
/* If there is an RFC2671 pseudoheader then it will be overwritten by /* If there is an RFC2671 pseudoheader then it will be overwritten by
partial replies, so we have to do a dry run to see if we can answer partial replies, so we have to do a dry run to see if we can answer
the query. We check to see if the do bit is set, if so we always the query. We check to see if the do bit is set, if so we always
forward rather than answering from the cache, which doesn't include forward rather than answering from the cache, which doesn't include
security information. */ security information, unless we're in DNSSEC validation mode. */
if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign)) if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{ {
unsigned short udpsz, flags; unsigned short udpsz, flags;
unsigned char *psave = pheader; unsigned char *psave = pheader;
have_pseudoheader = 1;
GETSHORT(udpsz, pheader); GETSHORT(udpsz, pheader);
pheader += 2; /* ext_rcode */ pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader); GETSHORT(flags, pheader);
...@@ -1637,7 +1646,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1637,7 +1646,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun) if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL); log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL);
} }
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
{ {
ans = 1; ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) if (!(crecp->flags & (F_HOSTS | F_DHCP)))
...@@ -1834,7 +1843,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1834,7 +1843,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun) if (!dryrun)
log_query(crecp->flags, name, NULL, NULL); log_query(crecp->flags, name, NULL, NULL);
} }
else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd) else if ((crecp->flags & (F_HOSTS | F_DHCP)) || !sec_reqd || option_bool(OPT_DNSSEC_VALID))
{ {
/* If we are returning local answers depending on network, /* If we are returning local answers depending on network,
filter here. */ filter here. */
...@@ -2060,12 +2069,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -2060,12 +2069,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (trunc) if (trunc)
header->hb3 |= HB3_TC; header->hb3 |= HB3_TC;
header->hb4 &= ~HB4_AD;
if (option_bool(OPT_DNSSEC_VALID) || option_bool(OPT_DNSSEC_PROXY))
if (sec_data)
header->hb4 |= HB4_AD;
if (nxdomain) if (nxdomain)
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
else else
...@@ -2073,6 +2076,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -2073,6 +2076,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
header->ancount = htons(anscount); header->ancount = htons(anscount);
header->nscount = htons(0); header->nscount = htons(0);
header->arcount = htons(addncount); header->arcount = htons(addncount);
return ansp - (unsigned char *)header;
header->hb4 &= ~HB4_AD;
len = ansp - (unsigned char *)header;
if (have_pseudoheader)
{
len = add_pseudoheader(header, len, (unsigned char *)limit, 0, NULL, 0, sec_reqd);
if (sec_reqd && sec_data)
header->hb4 |= HB4_AD;
}
return len ;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment