Use DS records as trust anchors, not DNSKEYs.

This allows us to query for the root zone DNSKEY RRset and validate
it, thus automatically handling KSK rollover.
This commit is contained in:
Simon Kelley
2014-02-11 11:07:22 +00:00
parent 83349b8aa4
commit ee4158678a
8 changed files with 85 additions and 120 deletions

View File

@@ -985,7 +985,7 @@ void cache_reload(void)
struct cname *a;
struct interface_name *intr;
#ifdef HAVE_DNSSEC
struct dnskey *key;
struct ds_config *ds;
#endif
cache_inserted = cache_live_freed = 0;
@@ -1031,17 +1031,17 @@ void cache_reload(void)
}
#ifdef HAVE_DNSSEC
for (key = daemon->dnskeys; key; key = key->next)
for (ds = daemon->ds; ds; ds = ds->next)
if ((cache = whine_malloc(sizeof(struct crec))) &&
(cache->addr.key.keydata = blockdata_alloc(key->key, key->keylen)))
(cache->addr.ds.keydata = blockdata_alloc(ds->digest, ds->digestlen)))
{
cache->flags = F_FORWARD | F_IMMORTAL | F_DNSKEY | F_CONFIG | F_NAMEP;
cache->name.namep = key->name;
cache->addr.key.keylen = key->keylen;
cache->addr.key.algo = key->algo;
cache->addr.key.flags = key->flags;
cache->addr.key.keytag = dnskey_keytag(key->algo, key->flags, (unsigned char *)key->key, key->keylen);
cache->uid = key->class;
cache->flags = F_FORWARD | F_IMMORTAL | F_DS | F_CONFIG | F_NAMEP;
cache->name.namep = ds->name;
cache->addr.ds.keylen = ds->digestlen;
cache->addr.ds.algo = ds->algo;
cache->addr.ds.keytag = ds->keytag;
cache->addr.ds.digest = ds->digest_type;
cache->uid = ds->class;
cache_hash(cache);
}
#endif

View File

@@ -144,7 +144,7 @@ int main (int argc, char **argv)
if (option_bool(OPT_DNSSEC_VALID))
{
#ifdef HAVE_DNSSEC
if (!daemon->dnskeys)
if (!daemon->ds)
die(_("No trust anchors provided for DNSSEC"), NULL, EC_BADCONF);
if (daemon->cachesize < CACHESIZ)

View File

@@ -295,10 +295,10 @@ struct cname {
struct cname *next;
};
struct dnskey {
char *name, *key;
int keylen, class, algo, flags;
struct dnskey *next;
struct ds_config {
char *name, *digest;
int digestlen, class, algo, keytag, digest_type;
struct ds_config *next;
};
#define ADDRLIST_LITERAL 1
@@ -930,7 +930,7 @@ extern struct daemon {
struct prefix_class *prefix_classes;
#endif
#ifdef HAVE_DNSSEC
struct dnskey *dnskeys;
struct ds_config *ds;
#endif
/* globally used stuff for DNS */
@@ -1107,9 +1107,6 @@ void prettyprint_time(char *buf, unsigned int t);
int prettyprint_addr(union mysockaddr *addr, char *buf);
int parse_hex(char *in, unsigned char *out, int maxlen,
unsigned int *wildcard_mask, int *mac_type);
#ifdef HAVE_DNSSEC
int parse_base64(char *in, char *out);
#endif
int memcmp_masked(unsigned char *a, unsigned char *b, int len,
unsigned int mask);
int expand_buf(struct iovec *iov, size_t size);

View File

@@ -139,7 +139,7 @@ struct myoption {
#define LOPT_QUIET_DHCP6 327
#define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329
#define LOPT_DNSKEY 330
#define LOPT_TRUST_ANCHOR 330
#define LOPT_DNSSEC_DEBUG 331
#ifdef HAVE_GETOPT_LONG
@@ -277,7 +277,7 @@ static const struct myoption opts[] =
{ "ipset", 1, 0, LOPT_IPSET },
{ "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID },
{ "dnskey", 1, 0, LOPT_DNSKEY },
{ "trust-anchor", 1, 0, LOPT_TRUST_ANCHOR },
{ "dnssec-debug", 0, 0, LOPT_DNSSEC_DEBUG },
#ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
@@ -430,7 +430,7 @@ static struct {
{ 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_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_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 },
#ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
@@ -590,6 +590,16 @@ static int atoi_check16(char *a, int *res)
return 1;
}
static int atoi_check8(char *a, int *res)
{
if (!(atoi_check(a, res)) ||
*res < 0 ||
*res > 0xff)
return 0;
return 1;
}
static void add_txt(char *name, char *txt)
{
@@ -3675,10 +3685,11 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
#ifdef HAVE_DNSSEC
case LOPT_DNSKEY:
case LOPT_TRUST_ANCHOR:
{
struct dnskey *new = opt_malloc(sizeof(struct dnskey));
char *key64, *algo = NULL;
struct ds_config *new = opt_malloc(sizeof(struct ds_config));
char *cp, *cp1, *keyhex, *digest, *algo = NULL;
int len;
new->class = C_IN;
@@ -3700,20 +3711,30 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
}
}
if (!comma || !algo || !(key64 = split(algo)) ||
!atoi_check16(comma, &new->flags) || !atoi_check16(algo, &new->algo) ||
if (!comma || !algo || !(digest = split(algo)) || !(keyhex = split(digest)) ||
!atoi_check16(comma, &new->keytag) ||
!atoi_check8(algo, &new->algo) ||
!atoi_check8(digest, &new->digest_type) ||
!(new->name = canonicalise_opt(arg)))
ret_err(_("bad DNSKEY"));
ret_err(_("bad trust anchor"));
/* Upper bound on length */
new->key = opt_malloc((3*strlen(key64)/4)+1);
unhide_metas(key64);
if ((new->keylen = parse_base64(key64, new->key)) == -1)
ret_err(_("bad base64 in DNSKEY"));
len = (2*strlen(keyhex))+1;
new->digest = opt_malloc(len);
unhide_metas(keyhex);
/* 4034: "Whitespace is allowed within digits" */
for (cp = keyhex; *cp; )
if (isspace(*cp))
for (cp1 = cp; *cp1; cp1++)
*cp1 = *(cp1+1);
else
cp++;
if ((new->digestlen = parse_hex(keyhex, (unsigned char *)new->digest, len, NULL, NULL)) == -1)
ret_err(_("bad HEX in trust anchor"));
new->next = daemon->ds;
daemon->ds = new;
new->next = daemon->dnskeys;
daemon->dnskeys = new;
break;
}
#endif

View File

@@ -1599,20 +1599,17 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
while ((crecp = cache_find_by_name(crecp, name, now, F_DNSKEY)))
if (crecp->uid == qclass)
{
if (!(crecp->flags & F_CONFIG)) /* Don't return configured keys - send upstream instead */
{
gotone = 1;
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.key.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DNSKEY, qclass, "sbbt",
crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
anscount++;
}
gotone = 1;
if (!dryrun && (keydata = blockdata_retrieve(crecp->addr.key.keydata, crecp->addr.key.keylen, NULL)))
{
struct all_addr a;
a.addr.keytag = crecp->addr.key.keytag;
log_query(F_KEYTAG | (crecp->flags & F_CONFIG), name, &a, "DNSKEY keytag %u");
if (add_resource_record(header, limit, &trunc, nameoffset, &ansp,
crec_ttl(crecp, now), &nameoffset,
T_DNSKEY, qclass, "sbbt",
crecp->addr.key.flags, 3, crecp->addr.key.algo, crecp->addr.key.keylen, keydata))
anscount++;
}
}
}

View File

@@ -482,66 +482,6 @@ int parse_hex(char *in, unsigned char *out, int maxlen,
return i;
}
#ifdef HAVE_DNSSEC
static int charval(char c)
{
if (c >= 'A' && c <= 'Z')
return c - 'A';
if (c >= 'a' && c <= 'z')
return c - 'a' + 26;
if (c >= '0' && c <= '9')
return c - '0' + 52;
if (c == '+')
return 62;
if (c == '/')
return 63;
if (c == '=')
return -1;
return -2;
}
int parse_base64(char *in, char *out)
{
char *p = out;
int i, val[4];
while (*in)
{
for (i = 0; i < 4; i++)
{
while (*in == ' ')
in++;
if (*in == 0)
return -1;
if ((val[i] = charval(*in++)) == -2)
return -1;
}
while (*in == ' ')
in++;
if (val[1] == -1)
return -1; /* too much padding */
*p++ = (val[0] << 2) | (val[1] >> 4);
if (val[2] != -1)
*p++ = (val[1] << 4) | ( val[2] >> 2);
if (val[3] != -1)
*p++ = (val[2] << 6) | val[3];
}
return p - out;
}
#endif
/* return 0 for no match, or (no matched octets) + 1 */
int memcmp_masked(unsigned char *a, unsigned char *b, int len, unsigned int mask)
{