mirror of
https://github.com/pi-hole/dnsmasq.git
synced 2025-12-19 18:28:25 +00:00
protocol handling for DNSSEC
This commit is contained in:
@@ -230,7 +230,8 @@ struct event_desc {
|
||||
#define OPT_QUIET_DHCP6 43
|
||||
#define OPT_QUIET_RA 44
|
||||
#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
|
||||
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
|
||||
if (option_bool(OPT_DNSSEC_VALID))
|
||||
header->hb4 &= ~HB4_AD;
|
||||
|
||||
if (cache_secure)
|
||||
if (!(header->hb4 & HB4_CD) && cache_secure)
|
||||
header->hb4 |= HB4_AD;
|
||||
#endif
|
||||
|
||||
@@ -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
|
||||
nameserver info. */
|
||||
|
||||
@@ -824,7 +849,6 @@ void reply_query(int fd, int family, time_t now)
|
||||
|
||||
if (status == STAT_SECURE)
|
||||
cache_secure = 1;
|
||||
/* TODO return SERVFAIL here */
|
||||
else if (status == STAT_BOGUS)
|
||||
no_cache_dnssec = 1;
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ struct myoption {
|
||||
#define LOPT_QUIET_RA 328
|
||||
#define LOPT_SEC_VALID 329
|
||||
#define LOPT_DNSKEY 330
|
||||
|
||||
#define LOPT_DNSSEC_PERM 331
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
static const struct option opts[] =
|
||||
@@ -278,6 +278,7 @@ static const struct myoption opts[] =
|
||||
{ "synth-domain", 1, 0, LOPT_SYNTH },
|
||||
{ "dnssec", 0, 0, LOPT_SEC_VALID },
|
||||
{ "dnskey", 1, 0, LOPT_DNSKEY },
|
||||
{ "dnssec-permissive", 0, 0, LOPT_DNSSEC_PERM },
|
||||
#ifdef OPTION6_PREFIX_CLASS
|
||||
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
|
||||
#endif
|
||||
@@ -428,10 +429,9 @@ static struct {
|
||||
{ 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_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_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
|
||||
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
|
||||
#endif
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
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 */
|
||||
if (!(p = skip_questions(header, plen)) ||
|
||||
!(p = skip_section(p,
|
||||
ntohs(header->ancount) + ntohs(header->nscount),
|
||||
ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
|
||||
header, plen)))
|
||||
return plen;
|
||||
*p++ = 0; /* empty name */
|
||||
@@ -531,16 +534,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
|
||||
rdlen = 0;
|
||||
if (((ssize_t)optlen) > (limit - (p + 4)))
|
||||
return plen; /* Too big */
|
||||
header->arcount = htons(1);
|
||||
header->arcount = htons(ntohs(header->arcount) + 1);
|
||||
datap = p;
|
||||
}
|
||||
else
|
||||
{
|
||||
int i, is_sign;
|
||||
int i;
|
||||
unsigned short code, len, flags;
|
||||
|
||||
/* Must be at the end, if exists */
|
||||
if (ntohs(header->arcount) != 1 ||
|
||||
!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
|
||||
is_sign ||
|
||||
(!(p = skip_name(p, header, plen, 10))))
|
||||
return plen;
|
||||
@@ -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
|
||||
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 char *p = (unsigned char *)(header+1);
|
||||
@@ -1447,23 +1449,30 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
int nameoffset;
|
||||
unsigned short flag;
|
||||
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;
|
||||
struct crec *crecp;
|
||||
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
|
||||
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
|
||||
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
|
||||
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))
|
||||
{
|
||||
unsigned short udpsz, flags;
|
||||
unsigned char *psave = pheader;
|
||||
|
||||
have_pseudoheader = 1;
|
||||
|
||||
GETSHORT(udpsz, pheader);
|
||||
pheader += 2; /* ext_rcode */
|
||||
GETSHORT(flags, pheader);
|
||||
@@ -1637,7 +1646,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (!dryrun)
|
||||
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;
|
||||
if (!(crecp->flags & (F_HOSTS | F_DHCP)))
|
||||
@@ -1834,7 +1843,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (!dryrun)
|
||||
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,
|
||||
filter here. */
|
||||
@@ -2060,12 +2069,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
if (trunc)
|
||||
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)
|
||||
SET_RCODE(header, NXDOMAIN);
|
||||
else
|
||||
@@ -2073,6 +2076,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
|
||||
header->ancount = htons(anscount);
|
||||
header->nscount = htons(0);
|
||||
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 ;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user