Compare commits

...
12 Commits
8 changed files with 949 additions and 304 deletions

No files matched your search

+9
View File
@@ -608,6 +608,15 @@ key(s) of the root zone,
but trust anchors for limited domains are also possible. The current
root-zone trust anchors may be donwloaded from https://data.iana.org/root-anchors/root-anchors.xml
.TP
.B --dnssec-check-unsigned
As a default, dnsmasq does not check that unsigned DNS replies are
legitimate: they are assumed to be valid and passed on (without the
"authentic data" bit set, of course). This does not protect against an
attacker forging unsigned replies for signed DNS zones, but it is
fast. If this flag is set, dnsmasq will check the zones of unsigned
replies, to ensure that unsigned replies are allowed in those
zones. The cost of this is more upstream queries and slower performance.
.TP
.B --proxy-dnssec
Copy the DNSSEC Authenticated Data bit from upstream servers to downstream clients and cache it. This is an
alternative to having dnsmasq validate DNSSEC, but it depends on the security of the network between
+17 -19
View File
@@ -183,7 +183,7 @@ static void cache_blockdata_free(struct crec *crecp)
else
blockdata_free(crecp->addr.key.keydata);
}
else if (crecp->flags & F_DS)
else if ((crecp->flags & F_DS) && !(crecp->flags & F_NEG))
blockdata_free(crecp->addr.ds.keydata);
}
#endif
@@ -564,7 +564,14 @@ struct crec *cache_insert(char *name, struct all_addr *addr,
*cache_get_name(new) = 0;
if (addr)
new->addr.addr = *addr;
{
#ifdef HAVE_DNSSEC
if (flags & (F_DS | F_DNSKEY))
new->uid = addr->addr.dnssec.class;
else
#endif
new->addr.addr = *addr;
}
new->ttd = now + (time_t)ttl;
new->next = new_chain;
@@ -1304,25 +1311,16 @@ void dump_cache(time_t now)
else if (cache->flags & F_DS)
{
if (cache->flags & F_DNSKEY)
{
/* RRSIG */
a = daemon->addrbuff;
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
}
else
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
/* RRSIG */
sprintf(a, "%5u %3u %s", cache->addr.sig.keytag,
cache->addr.sig.algo, querystr("", cache->addr.sig.type_covered));
else if (!(cache->flags & F_NEG))
sprintf(a, "%5u %3u %3u", cache->addr.ds.keytag,
cache->addr.ds.algo, cache->addr.ds.digest);
}
else if (cache->flags & F_DNSKEY)
{
a = daemon->addrbuff;
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
}
sprintf(a, "%5u %3u %3u", cache->addr.key.keytag,
cache->addr.key.algo, cache->addr.key.flags);
#endif
else if (!(cache->flags & F_NEG) || !(cache->flags & F_FORWARD))
{
+2 -1
View File
@@ -31,7 +31,8 @@
#define PING_CACHE_TIME 30 /* Ping test assumed to be valid this long. */
#define DECLINE_BACKOFF 600 /* disable DECLINEd static addresses for this long */
#define DHCP_PACKET_MAX 16384 /* hard limit on DHCP packet size */
#define SMALLDNAME 40 /* most domain names are smaller than this */
#define SMALLDNAME 50 /* most domain names are smaller than this */
#define CNAME_CHAIN 10 /* chains longer than this atr dropped for loop protection */
#define HOSTSFILE "/etc/hosts"
#define ETHERSFILE "/etc/ethers"
#define DEFLEASE 3600 /* default lease time, 1 hour */
+13 -3
View File
@@ -232,7 +232,8 @@ struct event_desc {
#define OPT_DNSSEC_VALID 45
#define OPT_DNSSEC_PERMISS 46
#define OPT_DNSSEC_DEBUG 47
#define OPT_LAST 48
#define OPT_DNSSEC_NO_SIGN 48
#define OPT_LAST 49
/* 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. */
@@ -535,6 +536,10 @@ struct hostsfile {
#define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define STAT_NO_SIG 8
#define STAT_NO_DS 9
#define STAT_NEED_DS_NEG 10
#define STAT_CHASE_CNAME 11
#define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2
@@ -542,6 +547,9 @@ struct hostsfile {
#define FREC_DNSKEY_QUERY 8
#define FREC_DS_QUERY 16
#define FREC_AD_QUESTION 32
#define FREC_DO_QUESTION 64
#define FREC_ADDED_PHEADER 128
#define FREC_CHECK_NOSIGN 256
#ifdef HAVE_DNSSEC
#define HASH_SIZE 20 /* SHA-1 digest size */
@@ -1048,7 +1056,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *namebuff,
int no_cache, int secure, int *doctored);
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int *ad_reqd);
time_t now, int *ad_reqd, int *do_bit);
int check_for_bogus_wildcard(struct dns_header *header, size_t qlen, char *name,
struct bogus_addr *addr, time_t now);
unsigned char *find_pseudoheader(struct dns_header *header, size_t plen,
@@ -1083,8 +1091,10 @@ int in_zone(struct auth_zone *zone, char *name, char **cut);
size_t dnssec_generate_query(struct dns_header *header, char *end, char *name, int class, int type, union mysockaddr *addr);
int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t n, 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_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class);
int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int *class, int *neganswer);
int dnssec_chase_cname(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
int dnskey_keytag(int alg, int flags, unsigned char *rdata, int rdlen);
size_t filter_rrsigs(struct dns_header *header, size_t plen);
unsigned char* hash_questions(struct dns_header *header, size_t plen, char *name);
/* util.c */
+575 -222
View File
File diff suppressed because it is too large. Load diff
+295 -35
View File
@@ -24,6 +24,14 @@ static unsigned short get_id(void);
static void free_frec(struct frec *f);
static struct randfd *allocate_rfd(int family);
#ifdef HAVE_DNSSEC
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server, int *keycount);
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class);
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname);
#endif
/* Send a UDP packet with its source address set as "source"
unless nowild is true, when we just send it with the kernel default */
int send_from(int fd, int nowild, char *packet, size_t len,
@@ -235,7 +243,7 @@ static unsigned int search_servers(time_t now, struct all_addr **addrpp,
static int forward_query(int udpfd, union mysockaddr *udpaddr,
struct all_addr *dst_addr, unsigned int dst_iface,
struct dns_header *header, size_t plen, time_t now,
struct frec *forward, int ad_reqd)
struct frec *forward, int ad_reqd, int do_bit)
{
char *domain = NULL;
int type = 0, norebind = 0;
@@ -250,6 +258,8 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#endif
unsigned int gotname = extract_request(header, plen, daemon->namebuff, NULL);
(void)do_bit;
/* may be no servers available. */
if (!daemon->servers)
forward = NULL;
@@ -336,8 +346,10 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
forward->flags |= FREC_AD_QUESTION;
#ifdef HAVE_DNSSEC
forward->work_counter = DNSSEC_WORK;
if (do_bit)
forward->flags |= FREC_DO_QUESTION;
#endif
header->id = htons(forward->new_id);
/* In strict_order mode, always try servers in the order
@@ -393,11 +405,17 @@ static int forward_query(int udpfd, union mysockaddr *udpaddr,
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
size_t new_plen = add_do_bit(header, plen, ((char *) header) + daemon->packet_buff_sz);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD;
if (new_plen != plen)
forward->flags |= FREC_ADDED_PHEADER;
plen = new_plen;
}
#endif
@@ -506,7 +524,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,
int no_cache, int cache_secure, int ad_reqd, int check_subnet, union mysockaddr *query_source)
int no_cache, int cache_secure, int ad_reqd, int do_bit, int added_pheader, int check_subnet, union mysockaddr *query_source)
{
unsigned char *pheader, *sizep;
char **sets = 0;
@@ -514,6 +532,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
size_t plen;
(void)ad_reqd;
(void) do_bit;
#ifdef HAVE_IPSET
/* Similar algorithm to search_servers. */
@@ -553,6 +572,12 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
my_syslog(LOG_WARNING, _("discarding DNS reply: subnet option mismatch"));
return 0;
}
if (added_pheader)
{
pheader = 0;
header->arcount = htons(0);
}
}
/* RFC 4035 sect 4.6 para 3 */
@@ -624,6 +649,10 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (!(header->hb4 & HB4_CD) && ad_reqd && cache_secure)
header->hb4 |= HB4_AD;
/* If the requestor didn't set the DO bit, don't return DNSSEC info. */
if (!do_bit)
n = filter_rrsigs(header, n);
#endif
/* do this after extract_addresses. Ensure NODATA reply and remove
@@ -708,7 +737,7 @@ void reply_query(int fd, int family, time_t now)
if ((nn = resize_packet(header, (size_t)n, pheader, plen)))
{
header->hb3 &= ~(HB3_QR | HB3_TC);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0);
forward_query(-1, NULL, NULL, 0, header, nn, now, forward, 0, 0);
return;
}
}
@@ -775,13 +804,27 @@ void reply_query(int fd, int family, time_t now)
else if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else if (forward->flags & FREC_DS_QUERY)
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
{
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
if (status == STAT_NO_DS)
status = STAT_INSECURE;
}
else if (forward->flags & FREC_CHECK_NOSIGN)
status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
{
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
else
status = STAT_INSECURE;
}
}
/* 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 (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
{
struct frec *new, *orig;
@@ -811,7 +854,7 @@ void reply_query(int fd, int family, time_t now)
#ifdef HAVE_IPV6
new->rfd6 = NULL;
#endif
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY);
new->flags &= ~(FREC_DNSKEY_QUERY | FREC_DS_QUERY | FREC_CHECK_NOSIGN);
new->dependent = forward; /* to find query awaiting new one. */
forward->blocking_query = new; /* for garbage cleaning */
@@ -824,7 +867,10 @@ void reply_query(int fd, int family, time_t now)
}
else
{
new->flags |= FREC_DS_QUERY;
if (status == STAT_NEED_DS_NEG)
new->flags |= FREC_CHECK_NOSIGN;
else
new->flags |= FREC_DS_QUERY;
nn = dnssec_generate_query(header,((char *) header) + daemon->packet_buff_sz,
daemon->keyname, forward->class, T_DS, &server->addr);
}
@@ -888,11 +934,26 @@ void reply_query(int fd, int family, time_t now)
if (forward->flags & FREC_DNSKEY_QUERY)
status = dnssec_validate_by_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else if (forward->flags & FREC_DS_QUERY)
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
{
status = dnssec_validate_ds(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
if (status == STAT_NO_DS)
status = STAT_INSECURE;
}
else if (forward->flags & FREC_CHECK_NOSIGN)
status = do_check_sign(now, header, n, daemon->namebuff, daemon->keyname, forward->class);
else
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class);
if (status == STAT_NEED_DS || status == STAT_NEED_KEY)
{
status = dnssec_validate_reply(now, header, n, daemon->namebuff, daemon->keyname, &forward->class, NULL);
if (status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
status = send_check_sign(now, header, n, daemon->namebuff, daemon->keyname);
else
status = STAT_INSECURE;
}
}
if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG || status == STAT_NEED_KEY)
goto anotherkey;
}
}
@@ -927,7 +988,8 @@ void reply_query(int fd, int family, time_t now)
header->hb4 &= ~HB4_CD;
if ((nn = process_reply(header, now, server, (size_t)n, check_rebind, no_cache_dnssec, cache_secure,
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_HAS_SUBNET, &forward->source)))
forward->flags & FREC_AD_QUESTION, forward->flags & FREC_DO_QUESTION,
forward->flags & FREC_ADDED_PHEADER, forward->flags & FREC_HAS_SUBNET, &forward->source)))
{
header->id = htons(forward->orig_id);
header->hb4 |= HB4_RA; /* recursion if available */
@@ -1169,9 +1231,9 @@ void receive_query(struct listener *listen, time_t now)
else
#endif
{
int ad_reqd;
int ad_reqd, do_bit;
m = answer_request(header, ((char *) header) + daemon->packet_buff_sz, (size_t)n,
dst_addr_4, netmask, now, &ad_reqd);
dst_addr_4, netmask, now, &ad_reqd, &do_bit);
if (m >= 1)
{
@@ -1180,7 +1242,7 @@ void receive_query(struct listener *listen, time_t now)
daemon->local_answer++;
}
else if (forward_query(listen->fd, &source_addr, &dst_addr, if_index,
header, (size_t)n, now, NULL, ad_reqd))
header, (size_t)n, now, NULL, ad_reqd, do_bit))
daemon->queries_forwarded++;
else
daemon->local_answer++;
@@ -1188,6 +1250,164 @@ void receive_query(struct listener *listen, time_t now)
}
#ifdef HAVE_DNSSEC
/* UDP: we've got an unsigned answer, return STAT_INSECURE if we can prove there's no DS
and therefore the answer shouldn't be signed, or STAT_BOGUS if it should be, or
STAT_NEED_DS_NEG and keyname if we need to do the query. */
static int send_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname)
{
struct crec *crecp;
char *name_start = name;
int status = dnssec_chase_cname(now, header, plen, name, keyname);
if (status != STAT_INSECURE)
return status;
while (1)
{
crecp = cache_find_by_name(NULL, name_start, now, F_DS);
if (crecp && (crecp->flags & F_DNSSECOK))
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
strcpy(keyname, name_start);
return STAT_NEED_DS_NEG;
}
}
/* Got answer to DS query from send_check_sign, check for proven non-existence, or make the next DS query to try. */
static int do_check_sign(time_t now, struct dns_header *header, size_t plen, char *name, char *keyname, int class)
{
char *name_start;
unsigned char *p;
int status = dnssec_validate_ds(now, header, plen, name, keyname, class);
if (status != STAT_INSECURE)
{
if (status == STAT_NO_DS)
status = STAT_INSECURE;
return status;
}
p = (unsigned char *)(header+1);
if (extract_name(header, plen, &p, name, 1, 4) &&
(name_start = strchr(name, '.')))
{
name_start++; /* chop a label off and try again */
strcpy(keyname, name_start);
return STAT_NEED_DS_NEG;
}
return STAT_BOGUS;
}
/* Move toward the root, until we find a signed non-existance of a DS, in which case
an unsigned answer is OK, or we find a signed DS, in which case there should be
a signature, and the answer is BOGUS */
static int tcp_check_for_unsigned_zone(time_t now, struct dns_header *header, size_t plen, int class, char *name,
char *keyname, struct server *server, int *keycount)
{
size_t m;
unsigned char *packet, *payload;
u16 *length;
unsigned char *p = (unsigned char *)(header+1);
int status;
char *name_start = name;
/* Get first insecure entry in CNAME chain */
status = tcp_key_recurse(now, STAT_CHASE_CNAME, header, plen, class, name, keyname, server, keycount);
if (status == STAT_BOGUS)
return STAT_BOGUS;
if (!(packet = whine_malloc(65536 + MAXDNAME + RRFIXEDSZ + sizeof(u16))))
return STAT_BOGUS;
payload = &packet[2];
header = (struct dns_header *)payload;
length = (u16 *)packet;
while (1)
{
unsigned char *newhash, hash[HASH_SIZE];
unsigned char c1, c2;
struct crec *crecp = cache_find_by_name(NULL, name_start, now, F_DS);
if (--(*keycount) == 0)
return STAT_BOGUS;
if (crecp && (crecp->flags & F_DNSSECOK))
{
free(packet);
return (crecp->flags & F_NEG) ? STAT_INSECURE : STAT_BOGUS;
}
/* If we have cached insecurely that a DS doesn't exist,
ise that is a hit for where to start looking for the secure one */
if (crecp && (crecp->flags & F_NEG) && (name_start = strchr(name_start, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
m = dnssec_generate_query(header, ((char *) header) + 65536, name_start, class, T_DS, &server->addr);
/* We rely on the question section coming back unchanged, ensure it is with the hash. */
if ((newhash = hash_questions(header, (unsigned int)m, name)))
memcpy(hash, newhash, HASH_SIZE);
*length = htons(m);
if (read_write(server->tcpfd, packet, m + sizeof(u16), 0) &&
read_write(server->tcpfd, &c1, 1, 1) &&
read_write(server->tcpfd, &c2, 1, 1) &&
read_write(server->tcpfd, payload, (c1 << 8) | c2, 1))
{
m = (c1 << 8) | c2;
newhash = hash_questions(header, (unsigned int)m, name);
if (newhash && memcmp(hash, newhash, HASH_SIZE) == 0)
{
/* Note this trashes all three name workspaces */
status = tcp_key_recurse(now, STAT_NEED_DS_NEG, header, m, class, name, keyname, server, keycount);
/* We've found a DS which proves the bit of the DNS where the
original query is, is unsigned, so the answer is OK,
if unvalidated. */
if (status == STAT_NO_DS)
{
free(packet);
return STAT_INSECURE;
}
/* No DS, not got to DNSSEC-land yet, go up. */
if (status == STAT_INSECURE)
{
p = (unsigned char *)(header+1);
if (extract_name(header, plen, &p, name, 1, 4) &&
(name_start = strchr(name, '.')))
{
name_start++; /* chop a label off and try again */
continue;
}
}
}
}
free(packet);
return STAT_BOGUS;
}
}
static int tcp_key_recurse(time_t now, int status, struct dns_header *header, size_t n,
int class, char *name, char *keyname, struct server *server, int *keycount)
{
@@ -1200,11 +1420,27 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
if (status == STAT_NEED_KEY)
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
else if (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);
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
{
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
new_status = STAT_INSECURE;
}
else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname);
else
{
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
if (new_status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
else
new_status = STAT_INSECURE;
}
}
/* Can't validate because we need a key/DS whose name now in keyname.
Make query for same, and recurse to validate */
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
@@ -1234,7 +1470,9 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
{
m = (c1 << 8) | c2;
if (tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount) == STAT_SECURE)
new_status = tcp_key_recurse(now, new_status, new_header, m, class, name, keyname, server, keycount);
if (new_status == STAT_SECURE)
{
/* Reached a validated record, now try again at this level.
Note that we may get ANOTHER NEED_* if an answer needs more than one key.
@@ -1242,11 +1480,27 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
if (status == STAT_NEED_KEY)
new_status = dnssec_validate_by_ds(now, header, n, name, keyname, class);
else if (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);
else if (status == STAT_NEED_DS || status == STAT_NEED_DS_NEG)
{
new_status = dnssec_validate_ds(now, header, n, name, keyname, class);
if (status == STAT_NEED_DS && new_status == STAT_NO_DS)
new_status = STAT_INSECURE; /* Validated no DS */
}
else if (status == STAT_CHASE_CNAME)
new_status = dnssec_chase_cname(now, header, n, name, keyname);
else
{
new_status = dnssec_validate_reply(now, header, n, name, keyname, &class, NULL);
if (new_status == STAT_NO_SIG)
{
if (option_bool(OPT_DNSSEC_NO_SIGN))
new_status = tcp_check_for_unsigned_zone(now, header, n, class, name, keyname, server, keycount);
else
new_status = STAT_INSECURE;
}
}
if (new_status == STAT_NEED_DS || new_status == STAT_NEED_KEY)
goto another_tcp_key;
}
@@ -1254,7 +1508,6 @@ static int tcp_key_recurse(time_t now, int status, struct dns_header *header, si
free(packet);
}
return new_status;
}
#endif
@@ -1272,7 +1525,8 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_AUTH
int local_auth = 0;
#endif
int checking_disabled, ad_question, check_subnet, no_cache_dnssec = 0, cache_secure = 0;
int checking_disabled, ad_question, do_bit, added_pheader = 0;
int check_subnet, no_cache_dnssec = 0, cache_secure = 0;
size_t m;
unsigned short qtype;
unsigned int gotname;
@@ -1350,7 +1604,7 @@ unsigned char *tcp_request(int confd, time_t now,
{
/* m > 0 if answered from cache */
m = answer_request(header, ((char *) header) + 65536, (size_t)size,
dst_addr_4, netmask, now, &ad_question);
dst_addr_4, netmask, now, &ad_question, &do_bit);
/* Do this by steam now we're not in the select() loop */
check_log_writer(NULL);
@@ -1430,11 +1684,17 @@ unsigned char *tcp_request(int confd, time_t now,
#ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID))
{
size = add_do_bit(header, size, ((char *) header) + 65536);
size_t new_size = add_do_bit(header, size, ((char *) header) + 65536);
/* For debugging, set Checking Disabled, otherwise, have the upstream check too,
this allows it to select auth servers when one is returning bad data. */
if (option_bool(OPT_DNSSEC_DEBUG))
header->hb4 |= HB4_CD;
if (size != new_size)
added_pheader = 1;
size = new_size;
}
#endif
@@ -1533,7 +1793,7 @@ unsigned char *tcp_request(int confd, time_t now,
m = process_reply(header, now, last_server, (unsigned int)m,
option_bool(OPT_NO_REBIND) && !norebind, no_cache_dnssec,
cache_secure, ad_question, check_subnet, &peer_addr);
cache_secure, ad_question, do_bit, added_pheader, check_subnet, &peer_addr);
break;
}
+3
View File
@@ -143,6 +143,7 @@ struct myoption {
#define LOPT_DNSSEC_DEBUG 331
#define LOPT_REV_SERV 332
#define LOPT_SERVERS_FILE 333
#define LOPT_DNSSEC_CHECK 334
#ifdef HAVE_GETOPT_LONG
static const struct option opts[] =
@@ -283,6 +284,7 @@ static const struct myoption opts[] =
{ "dnssec", 0, 0, LOPT_SEC_VALID },
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
{ "dnssec-check-unsigned", 0, 0, LOPT_DNSSEC_CHECK },
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif
@@ -438,6 +440,7 @@ static struct {
{ LOPT_SEC_VALID, OPT_DNSSEC_VALID, NULL, gettext_noop("Activate DNSSEC validation"), NULL },
{ LOPT_TRUST_ANCHOR, ARG_DUP, "<domain>,[<class>],...", gettext_noop("Specify trust anchor key digest."), NULL },
{ LOPT_DNSSEC_DEBUG, OPT_DNSSEC_DEBUG, NULL, gettext_noop("Disable upstream checking for DNSSEC debugging."), NULL },
{ LOPT_DNSSEC_CHECK, OPT_DNSSEC_NO_SIGN, NULL, gettext_noop("Ensure answers without DNSSEC are in unsigned zones."), NULL },
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif
+35 -24
View File
@@ -601,11 +601,11 @@ static int filter_mac(int family, char *addrp, char *mac, size_t maclen, void *p
if (family == parm->l3->sa.sa_family)
{
if (family == AF_INET && memcmp (&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
if (family == AF_INET && memcmp(&parm->l3->in.sin_addr, addrp, INADDRSZ) == 0)
match = 1;
#ifdef HAVE_IPV6
else
if (family == AF_INET6 && memcmp (&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
if (family == AF_INET6 && memcmp(&parm->l3->in6.sin6_addr, addrp, IN6ADDRSZ) == 0)
match = 1;
#endif
}
@@ -917,8 +917,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
searched_soa = 1;
ttl = find_soa(header, qlen, name, doctored);
#ifdef HAVE_DNSSEC
if (*doctored)
secure = 0;
if (*doctored && secure)
return 0;
#endif
}
@@ -927,7 +927,7 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
for (i = ntohs(header->qdcount); i != 0; i--)
{
int found = 0, cname_count = 5;
int found = 0, cname_count = CNAME_CHAIN;
struct crec *cpp = NULL;
int flags = RCODE(header) == NXDOMAIN ? F_NXDOMAIN : 0;
int secflag = secure ? F_DNSSECOK : 0;
@@ -988,9 +988,8 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t
if (aqtype == T_CNAME)
{
if (!cname_count--)
return 0; /* looped CNAMES */
secflag = 0; /* no longer DNSSEC */
if (!cname_count-- || secure)
return 0; /* looped CNAMES, or DNSSEC, which we can't cache. */
goto cname_loop;
}
@@ -1453,7 +1452,7 @@ static unsigned long crec_ttl(struct crec *crecp, time_t now)
/* return zero if we can't answer from cache, or packet size if we can */
size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
struct in_addr local_addr, struct in_addr local_netmask,
time_t now, int *ad_reqd)
time_t now, int *ad_reqd, int *do_bit)
{
char *name = daemon->namebuff;
unsigned char *p, *ansp, *pheader;
@@ -1475,7 +1474,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
/* RFC 6840 5.7 */
*ad_reqd = header->hb4 & HB4_AD;
*do_bit = 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
@@ -1493,7 +1493,8 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader);
sec_reqd = flags & 0x8000; /* do bit */
if ((sec_reqd = flags & 0x8000))
*do_bit = 1;/* do bit */
*ad_reqd = 1;
/* If our client is advertising a larger UDP packet size
@@ -1578,19 +1579,29 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
while ((crecp = cache_find_by_name(crecp, name, now, F_DS)))
if (crecp->uid == qclass)
{
gotone = 1;
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.ds.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DS, qclass, "sbbt",
crecp->addr.ds.keytag, crecp->addr.ds.algo, crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
anscount++;
}
gotone = 1;
if (!dryrun)
{
if (crecp->flags & F_NEG)
{
if (crecp->flags & F_NXDOMAIN)
nxdomain = 1;
log_query(F_UPSTREAM, name, NULL, "secure no DS");
}
else if ((keydata = blockdata_retrieve(crecp->addr.ds.keydata, crecp->addr.ds.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.ds.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DS keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DS, qclass, "sbbt",
crecp->addr.ds.keytag, crecp->addr.ds.algo,
crecp->addr.ds.digest, crecp->addr.ds.keylen, keydata))
anscount++;
}
}
}
}
else /* DNSKEY */