Commit 5107ace1 authored by Simon Kelley's avatar Simon Kelley

NSEC3 validation. First pass.

parent 7b1eae4f
...@@ -68,6 +68,7 @@ ...@@ -68,6 +68,7 @@
#define T_RRSIG 46 #define T_RRSIG 46
#define T_NSEC 47 #define T_NSEC 47
#define T_DNSKEY 48 #define T_DNSKEY 48
#define T_NSEC3 50
#define T_TKEY 249 #define T_TKEY 249
#define T_TSIG 250 #define T_TSIG 250
#define T_AXFR 252 #define T_AXFR 252
......
...@@ -98,7 +98,10 @@ int main (int argc, char **argv) ...@@ -98,7 +98,10 @@ int main (int argc, char **argv)
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
daemon->keyname = safe_malloc(MAXDNAME); {
daemon->keyname = safe_malloc(MAXDNAME);
daemon->workspacename = safe_malloc(MAXDNAME);
}
#endif #endif
#ifdef HAVE_DHCP #ifdef HAVE_DHCP
......
...@@ -534,6 +534,7 @@ struct hostsfile { ...@@ -534,6 +534,7 @@ struct hostsfile {
#define STAT_NEED_DS 4 #define STAT_NEED_DS 4
#define STAT_NEED_KEY 5 #define STAT_NEED_KEY 5
#define STAT_TRUNCATED 6 #define STAT_TRUNCATED 6
#define STAT_SECURE_WILDCARD 7
#define FREC_NOREBIND 1 #define FREC_NOREBIND 1
#define FREC_CHECKING_DISABLED 2 #define FREC_CHECKING_DISABLED 2
...@@ -941,6 +942,7 @@ extern struct daemon { ...@@ -941,6 +942,7 @@ extern struct daemon {
char *namebuff; /* MAXDNAME size buffer */ char *namebuff; /* MAXDNAME size buffer */
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
char *keyname; /* MAXDNAME size buffer */ char *keyname; /* MAXDNAME size buffer */
char *workspacename; /* ditto */
#endif #endif
unsigned int local_answer, queries_forwarded, auth_answer; unsigned int local_answer, queries_forwarded, auth_answer;
struct frec *frec_list; struct frec *frec_list;
...@@ -1035,6 +1037,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp, ...@@ -1035,6 +1037,7 @@ int extract_name(struct dns_header *header, size_t plen, unsigned char **pp,
char *name, int isExtract, int extrabytes); char *name, int isExtract, int extrabytes);
unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes); unsigned char *skip_name(unsigned char *ansp, struct dns_header *header, size_t plen, int extrabytes);
unsigned char *skip_questions(struct dns_header *header, size_t plen); 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 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);
size_t setup_reply(struct dns_header *header, size_t qlen, size_t setup_reply(struct dns_header *header, size_t qlen,
......
...@@ -498,6 +498,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int ...@@ -498,6 +498,8 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
int rdlen1, rdlen2, left1, left2, len1, len2, len, rc; int rdlen1, rdlen2, left1, left2, len1, len2, len, rc;
u16 *dp1, *dp2; u16 *dp1, *dp2;
unsigned char *end1, *end2; unsigned char *end1, *end2;
/* Note that these have been determined to be OK previously,
so we don't need to check for NULL return here. */
unsigned char *p1 = skip_name(rrset[i], header, plen, 10); unsigned char *p1 = skip_name(rrset[i], header, plen, 10);
unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10); unsigned char *p2 = skip_name(rrset[i+1], header, plen, 10);
...@@ -559,12 +561,15 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int ...@@ -559,12 +561,15 @@ static void sort_rrset(struct dns_header *header, size_t plen, u16 *rr_desc, int
/* Validate a single RRset (class, type, name) in the supplied DNS reply /* Validate a single RRset (class, type, name) in the supplied DNS reply
Return code: Return code:
STAT_SECURE if it validates. STAT_SECURE if it validates.
STAT_SECURE_WILDCARD if it validates and is the result of wildcard expansion.
STAT_INSECURE can't validate (no RRSIG, bad packet). STAT_INSECURE can't validate (no RRSIG, bad packet).
STAT_BOGUS signature is wrong. STAT_BOGUS signature is wrong.
STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname) STAT_NEED_KEY need DNSKEY to complete validation (name is returned in keyname)
if key is non-NULL, use that key, which has the algo and tag given in the params of those names, if key is non-NULL, use that key, which has the algo and tag given in the params of those names,
otherwise find the key in the cache. otherwise find the key in the cache.
name is unchanged on exit. keyname is used as workspace and trashed.
*/ */
static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class, static int validate_rrset(time_t now, struct dns_header *header, size_t plen, int class,
int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in) int type, char *name, char *keyname, struct blockdata *key, int keylen, int algo_in, int keytag_in)
...@@ -797,7 +802,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in ...@@ -797,7 +802,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
crecp->addr.key.keytag == key_tag && crecp->addr.key.keytag == key_tag &&
crecp->uid == class && crecp->uid == class &&
verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo)) verify(crecp->addr.key.keydata, crecp->addr.key.keylen, sig, sig_len, digest, hash->digest_size, algo))
return STAT_SECURE; return (labels < name_labels) ? STAT_SECURE_WILDCARD : STAT_SECURE;
} }
} }
...@@ -1127,14 +1132,460 @@ static int hostname_cmp(const char *a, const char *b) ...@@ -1127,14 +1132,460 @@ static int hostname_cmp(const char *a, const char *b)
} }
} }
/* Find all the NSEC or NSEC3 records in a reply.
return an array of pointers to them. */
static int find_nsec_records(struct dns_header *header, size_t plen, unsigned char ***nsecsetp, int *nsecsetl, int class_reqd)
{
static unsigned char **nsecset = NULL;
static int nsecset_sz = 0;
int type_found = -1;
unsigned char *p = skip_questions(header, plen);
int type, class, rdlen, i, nsecs_found;
/* Move to NS section */
if (!p || !(p = skip_section(p, ntohs(header->ancount), header, plen)))
return 0;
for (nsecs_found = 0, i = ntohs(header->nscount); i != 0; i--)
{
unsigned char *pstart = p;
if (!(p = skip_name(p, header, plen, 10)))
return 0;
GETSHORT(type, p);
GETSHORT(class, p);
p += 4; /* TTL */
GETSHORT(rdlen, p);
if (class == class_reqd && (type == T_NSEC || type == T_NSEC3))
{
/* No mixed NSECing 'round here, thankyouverymuch */
if (type_found == T_NSEC && type == T_NSEC3)
return 0;
if (type_found == T_NSEC3 && type == T_NSEC)
return 0;
type_found = type;
if (nsecs_found == nsecset_sz)
{
unsigned char **new;
/* Protect against insane/malicious queries which bloat the workspace
and eat CPU in the sort */
if (nsecs_found >= 100)
return 0;
/* expand */
if (!(new = whine_malloc((nsecset_sz + 5) * sizeof(unsigned char **))))
return 0;
if (nsecset)
{
memcpy(new, nsecset, nsecset_sz * sizeof(unsigned char **));
free(nsecset);
}
nsecset = new;
nsecset_sz += 5;
}
nsecset[nsecs_found++] = pstart;
}
if (!ADD_RDLEN(header, p, plen, rdlen))
return 0;
}
*nsecsetp = nsecset;
*nsecsetl = nsecs_found;
return type_found;
}
static int prove_non_existance_nsec(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type)
{
int i, rc, rdlen;
unsigned char *p, *psave;
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
/* Find NSEC record that proves name doesn't exist */
for (i = 0; i < nsec_count; i++)
{
p = nsecs[i];
if (!extract_name(header, plen, &p, workspace1, 1, 10))
return STAT_INSECURE;
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
if (!extract_name(header, plen, &p, workspace2, 1, 10))
return STAT_INSECURE;
rc = hostname_cmp(workspace1, name);
if (rc == 0)
{
/* NSEC with the same name as the RR we're testing, check
that the type in question doesn't appear in the type map */
rdlen -= p - psave;
/* rdlen is now length of type map, and p points to it */
while (rdlen >= 2)
{
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_INSECURE;
if (p[0] == type >> 8)
{
/* Does the NSEC say our type exists? */
if (offset < p[1] &&
(p[offset+2] & mask) != 0)
return STAT_BOGUS;
break; /* finshed checking */
}
rdlen -= p[1];
p += p[1];
}
return STAT_SECURE;
}
else if (rc == -1)
{
/* Normal case, name falls between NSEC name and next domain name,
wrap around case, name falls between NSEC name (rc == -1) and end */
if (hostname_cmp(workspace2, name) == 1 || hostname_cmp(workspace1, workspace2) == 1)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (hostname_cmp(workspace1, workspace2) == 1 && hostname_cmp(workspace2, name) == 1)
return STAT_SECURE;
}
}
return STAT_BOGUS;
}
/* return digest length, or zero on error */
static int hash_name(char *in, unsigned char **out, struct nettle_hash const *hash,
unsigned char *salt, int salt_len, int iterations)
{
void *ctx;
unsigned char *digest;
int i;
if (!hash_init(hash, &ctx, &digest))
return 0;
hash->update(ctx, to_wire(in), (unsigned char *)in);
hash->update(ctx, salt_len, salt);
hash->digest(ctx, hash->digest_size, digest);
for(i = 0; i < iterations; i++)
{
hash->update(ctx, hash->digest_size, digest);
hash->update(ctx, salt_len, salt);
hash->digest(ctx, hash->digest_size, digest);
}
from_wire(in);
*out = digest;
return hash->digest_size;
}
/* Decode base32 to first "." or end of string */
static int base32_decode(char *in, unsigned char *out)
{
int oc = 0, on = 0, c, mask, i;
unsigned char *p = out;
while (1)
{
c = *in++;
if (c == 0 || c == '.')
break;
if (c >= '0' && c <= '9')
c -= '0';
else if (c >= 'a' && c <= 'v')
c -= 'a', c += 10;
else if (c >= 'A' && c <= 'V')
c -= 'A', c += 10;
else
return 0;
for (mask = 0x10, i = 0; i < 5; i++)
{
if (c & mask)
oc |= 1;
mask = mask >> 1;
if (((++on) & 7) == 0)
*p++ = oc;
oc = oc << 1;
}
}
if ((on & 7) != 0)
return 0;
return p - out;
}
static int prove_non_existance_nsec3(struct dns_header *header, size_t plen, unsigned char **nsecs, int nsec_count,
char *workspace1, char *workspace2, char *name, int type)
{
unsigned char *salt, *p, *digest, hash_len;
int digest_size, i, iterations, salt_len, algo = 0;
struct nettle_hash const *hash;
char *closest_encloser, *next_closest, *wildcard;
/* Look though the NSEC3 records to find the first one with
an algorithm we support (currently only algo == 1).
Take the algo, iterations, and salt of that record
as the ones we're going to use, and prune any
that don't match. */
for (i = 0; i < nsec_count; i++)
{
if (!(p = skip_name(nsecs[i], header, plen, 15)))
return STAT_INSECURE; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
algo = *p++;
if (algo == 1)
break; /* known algo */
}
/* No usable NSEC3s */
if (i == nsec_count)
return STAT_BOGUS;
p++; /* flags */
GETSHORT (iterations, p);
salt_len = *p++;
salt = p;
if (!CHECK_LEN(header, salt, plen, salt_len))
return STAT_INSECURE; /* bad packet */
/* Now prune so we only have NSEC3 records with same iterations, salt and algo */
for (i = 0; i < nsec_count; i++)
{
unsigned char *nsec3p = nsecs[i];
int this_iter;
nsecs[i] = NULL; /* Speculative, will be restored if OK. */
if (!(p = skip_name(nsec3p, header, plen, 15)))
return STAT_INSECURE; /* bad packet */
p += 10; /* type, class, TTL, rdlen */
if (*p++ != algo)
continue;
p++; /* flags */
GETSHORT (this_iter, p);
if (this_iter != iterations)
continue;
if (salt_len != *p++)
continue;
if (!CHECK_LEN(header, p, plen, salt_len))
return STAT_INSECURE; /* bad packet */
if (memcmp(p, salt, salt_len) != 0)
continue;
/* All match, put the pointer back */
nsecs[i] = nsec3p;
}
/* Algo is checked as 1 above */
if (!(hash = hash_find("sha1")))
return STAT_INSECURE;
/* Now, we need the "closest encloser NSEC3" */
closest_encloser = name;
next_closest = NULL;
do
{
if (*closest_encloser == '.')
closest_encloser++;
if ((digest_size = hash_name(closest_encloser, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_INSECURE;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
int base32_size;
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_size = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_INSECURE;
if (digest_size == base32_size &&
memcmp(digest, workspace2, digest_size) == 0)
break; /* Gotit */
}
if (i != nsec_count)
break;
next_closest = closest_encloser;
}
while ((closest_encloser = strchr(closest_encloser, '.')));
/* No usable NSEC3s */
if (i == nsec_count)
return STAT_BOGUS;
if (!next_closest)
{
/* We found an NSEC3 whose hashed name exactly matches the query, so
Now we just need to check the type map. p points to the RR data for the record. */
int hash_len, rdlen;
unsigned char *psave;
int offset = (type & 0xff) >> 3;
int mask = 0x80 >> (type & 0x07);
p += 8; /* class, type, TTL */
GETSHORT(rdlen, p);
psave = p;
p += 5 + salt_len; /* algo, flags, iterations, salt_len, salt */
hash_len = *p++;
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_INSECURE; /* bad packet */
p += hash_len;
rdlen -= p - psave;
while (rdlen >= 2)
{
if (!CHECK_LEN(header, p, plen, rdlen))
return STAT_INSECURE;
if (p[0] == type >> 8)
{
/* Does the NSEC say our type exists? */
if (offset < p[1] &&
(p[offset+2] & mask) != 0)
return STAT_BOGUS;
break; /* finshed checking */
}
rdlen -= p[1];
p += p[1];
}
return STAT_SECURE;
}
/* Look for NSEC3 that proves the non-existance of the next-closest encloser */
if ((digest_size = hash_name(next_closest, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_INSECURE;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
int base32_size;
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_size = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_INSECURE;
p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len = *p++; /* p now points to next hashed name */
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_INSECURE;
if (digest_size == base32_size && hash_len == base32_size)
{
if (memcmp(workspace2, digest, digest_size) <= 0)
{
/* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
wrap around case, name-hash falls between NSEC3 name-hash and end */
if (memcmp(p, name, digest_size) > 0 || memcmp(workspace2, p, digest_size) > 0)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_size) > 0 && memcmp(p, name, digest_size) > 0)
return STAT_SECURE;
}
}
}
/* Finally, check that there's no seat of wildcard synthesis */
if (!(wildcard = strchr(next_closest, '.')) || wildcard == next_closest)
return STAT_BOGUS;
wildcard--;
*wildcard = '*';
if ((digest_size = hash_name(wildcard, &digest, hash, salt, salt_len, iterations)) == 0)
return STAT_INSECURE;
for (i = 0; i < nsec_count; i++)
if ((p = nsecs[i]))
{
int base32_size;
if (!extract_name(header, plen, &p, workspace1, 1, 0) ||
!(base32_size = base32_decode(workspace1, (unsigned char *)workspace2)))
return STAT_INSECURE;
p += 15 + salt_len; /* class, type, TTL, rdlen, algo, flags, iterations, salt_len, salt */
hash_len = *p++; /* p now points to next hashed name */
if (!CHECK_LEN(header, p, plen, hash_len))
return STAT_INSECURE;
if (digest_size == base32_size && hash_len == base32_size)
{
if (memcmp(workspace2, digest, digest_size) <= 0)
{
/* Normal case, hash falls between NSEC3 name-hash and next domain name-hash,
wrap around case, name-hash falls between NSEC3 name-hash and end */
if (memcmp(p, name, digest_size) > 0 || memcmp(workspace2, p, digest_size) > 0)
return STAT_SECURE;
}
else
{
/* wrap around case, name falls between start and next domain name */
if (memcmp(workspace2, p, digest_size) > 0 && memcmp(p, name, digest_size) > 0)
return STAT_SECURE;
}
}
}
return STAT_BOGUS;
}
/* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */ /* Validate all the RRsets in the answer and authority sections of the reply (4035:3.2.3) */
/* Returns are the same as validate_rrset, plus the class if the missing key is in *class */ /* Returns are the same as validate_rrset, plus the class if the missing key is in *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)
{ {
unsigned char *ans_start, *p1, *p2; unsigned char *ans_start, *p1, *p2, **nsecs;
int type1, class1, rdlen1, type2, class2, rdlen2; int type1, class1, rdlen1, type2, class2, rdlen2;
int i, j, rc, have_nsec, have_nsec_equal, cname_count = 5; int i, j, rc, nsec_count, cname_count = 10;
int nsec_type = 0;
if (RCODE(header) == SERVFAIL) if (RCODE(header) == SERVFAIL)
return STAT_BOGUS; return STAT_BOGUS;
...@@ -1185,13 +1636,39 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1185,13 +1636,39 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
struct blockdata *key; struct blockdata *key;
struct crec *crecp; struct crec *crecp;
if ((rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0)) != STAT_SECURE) rc = validate_rrset(now, header, plen, class1, type1, name, keyname, NULL, 0, 0, 0);
if (rc == STAT_SECURE_WILDCARD)
{
/* An attacker replay a wildcard answer with a different
answer and overlay an genuine RR. To prove this
hasn't happened, the answer must prove that
a record doesn't exist. Check that here. */
if (!nsec_type)
{
nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1);
if (nsec_type == 0)
return STAT_INSECURE; /* Bad packet */
if (nsec_type == -1)
return STAT_BOGUS; /* No NSECs */
}
if (nsec_type == T_NSEC)
rc = prove_non_existance_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
else
rc = prove_non_existance_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
if (rc != STAT_SECURE)
return rc;
}
else if (rc != STAT_SECURE)
{ {
if (class) if (class)
*class = class1; /* Class for DS or DNSKEY */ *class = class1; /* Class for DS or DNSKEY */
return rc; return rc;
} }
/* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */ /* Cache RRsigs in answer section, and if we just validated a DS RRset, cache it */
cache_start_insert(); cache_start_insert();
...@@ -1321,7 +1798,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1321,7 +1798,7 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
{ {
/* Do we have an answer for the question? */ /* Do we have an answer for the question? */
if (type1 == type2) if (type1 == type2)
return RCODE(header) == NXDOMAIN ? STAT_INSECURE : STAT_SECURE; return RCODE(header) == NXDOMAIN ? STAT_BOGUS : STAT_SECURE;
else if (type2 == T_CNAME) else if (type2 == T_CNAME)
{ {
/* looped CNAMES */ /* looped CNAMES */
...@@ -1338,117 +1815,25 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch ...@@ -1338,117 +1815,25 @@ int dnssec_validate_reply(time_t now, struct dns_header *header, size_t plen, ch
return STAT_INSECURE; return STAT_INSECURE;
} }
/* NXDOMAIN or NODATA reply, look for NSEC records to support that. /* NXDOMAIN or NODATA reply, prove that (name, class1, type1) can't exist */
At this point, p1 points to the start of the auth section.
Use keyname as workspace */
for (have_nsec = 0, have_nsec_equal = 0, p2 = NULL, rdlen2 = 0, j = ntohs(header->nscount); j != 0; j--)
{
unsigned char *nsec_start = p1;
if (!extract_name(header, plen, &p1, keyname, 1, 10))
return STAT_INSECURE; /* bad packet */
GETSHORT(type2, p1);
GETSHORT(class2, p1);
p1 += 4; /* TTL */
GETSHORT(rdlen1, p1);
if (class1 == class2 && type2 == T_NSEC)
{
have_nsec = 1;
rc = hostname_cmp(name, keyname);
if (rc >= 0)
{
if (p2)
{
unsigned char *psave = p2;
/* new NSEC is smaller than name,
is it bigger than previous one? */
/* get previous one into name buffer */
if (!extract_name(header, plen, &psave, name, 1, 0))
return STAT_INSECURE; /* bad packet */
if (hostname_cmp(name, keyname) < 0)
{
p2 = nsec_start;
rdlen2 = rdlen1;
}
/* restore query name */
psave = (unsigned char *)(header+1);
if (!extract_name(header, plen, &psave, name, 1, 0))
return STAT_INSECURE;
}
else
{
/* There was no previous best candidate */
p2 = nsec_start;
rdlen2 = rdlen1;
}
}
if (rc == 0)
have_nsec_equal = 1;
}
if (!ADD_RDLEN(header, p1, plen, rdlen1))
return STAT_INSECURE;
}
if (p2) /* First marshall the NSEC records, if we've not done it previously */
if (!nsec_type)
{ {
unsigned char *psave; nsec_type = find_nsec_records(header, plen, &nsecs, &nsec_count, class1);
p2 = skip_name(p2, header, plen, 0);
p2 += 10; /* type, class, ttl, rdlen */
psave = p2;
extract_name(header, plen, &p2, keyname, 1, 0);
rdlen2 -= p2 - psave;
}
/* At this point, have_nsec is set if there's at least one NSEC
have_nsec_equal is set if there's an NSEC with the same name as the query;
p2 points to the type bit maps of the biggest NSEC smaller than or equal to the query
or NULL if the query is smaller than all of them.
Keyname holds the next domain name for that NSEC.
rdlen2 is the length of the bitmap field */
if (RCODE(header) == NOERROR && have_nsec_equal)
{
int offset = (type1 & 0xff) >> 3;
int mask = 0x80 >> (type1 & 0x07);
while (rdlen2 >= 2)
{
if (p2[0] == type1 >> 8)
{
/* Does the NSEC say our type exists? */
if (offset < p2[1] &&
(p2[offset+2] & mask) != 0)
return STAT_INSECURE;
break; /* finshed checking */
}
rdlen2 -= p2[1];
p2 += p2[1];
}
return STAT_SECURE; if (nsec_type == 0)
return STAT_INSECURE; /* Bad packet */
if (nsec_type == -1)
return STAT_BOGUS; /* No NSECs */
} }
if (RCODE(header) == NXDOMAIN && have_nsec) if (nsec_type == T_NSEC)
{ return prove_non_existance_nsec(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
if (!p2 || hostname_cmp(name, keyname) < 0) else
return STAT_SECURE; /* Before the first, or in a proven gap */ return prove_non_existance_nsec3(header, plen, nsecs, nsec_count, daemon->workspacename, keyname, name, type1);
}
return STAT_INSECURE;
} }
/* Compute keytag (checksum to quickly index a key). See RFC4034 */ /* Compute keytag (checksum to quickly index a key). See RFC4034 */
int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen) int dnskey_keytag(int alg, int flags, unsigned char *key, int keylen)
{ {
......
...@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen) ...@@ -337,7 +337,7 @@ unsigned char *skip_questions(struct dns_header *header, size_t plen)
return ansp; return ansp;
} }
static 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)
{ {
int i, rdlen; int i, rdlen;
......
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