Commit 0975a58e authored by Simon Kelley's avatar Simon Kelley

Merge branch 'master' of ssh://central/var/cache/git/dnsmasq

parents a59ff5f3 a25720a3
......@@ -51,33 +51,33 @@ top!=pwd
# GNU make way.
top?=$(CURDIR)
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
sec_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags libcrypto`
sec_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs libcrypto`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1`
dbus_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --libs dbus-1`
idn_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --cflags libidn`
idn_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_IDN $(PKG_CONFIG) --libs libidn`
ct_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --cflags libnetfilter_conntrack`
ct_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_CONNTRACK $(PKG_CONFIG) --libs libnetfilter_conntrack`
lua_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --cflags lua5.1`
lua_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_LUASCRIPT $(PKG_CONFIG) --libs lua5.1`
nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed`
nettle_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs nettle hogweed`
sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \
dnsmasq.o dhcp.o lease.o rfc2131.o netlink.o dbus.o bpf.o \
helper.o tftp.o log.o conntrack.o dhcp6.o rfc3315.o \
dhcp-common.o outpacket.o radv.o slaac.o auth.o ipset.o \
domain.o dnssec.o dnssec-openssl.o blockdata.o
domain.o dnssec.o blockdata.o
hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.h \
dns-protocol.h radv-protocol.h
dns-protocol.h radv-protocol.h ip6addr.h
all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(sec_cflags)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(sec_libs)" \
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(nettle_cflags)" \
build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs)" \
-f $(top)/Makefile dnsmasq
mostly_clean :
......
......@@ -67,21 +67,6 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
return ret;
}
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt)
{
if (*p == NULL)
*p = (*key)->key;
else if (*p == (*key)->key + KEYBLOCK_LEN)
{
*key = (*key)->next;
if (*key == NULL)
return 0;
*p = (*key)->key;
}
return MIN(cnt, (size_t)((*key)->key + KEYBLOCK_LEN - (*p)));
}
void blockdata_free(struct blockdata *blocks)
{
struct blockdata *tmp;
......@@ -96,24 +81,38 @@ void blockdata_free(struct blockdata *blocks)
}
}
/* copy blocks into data[], return 1 if data[] unchanged by so doing */
int blockdata_retrieve(struct blockdata *block, size_t len, void *data)
/* if data == NULL, return pointer to static block of sufficient size */
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
{
size_t blen;
struct blockdata *b;
int match = 1;
void *new, *d;
for (b = block; len > 0 && b; b = b->next)
static unsigned int buff_len = 0;
static unsigned char *buff = NULL;
if (!data)
{
if (len > buff_len)
{
if (!(new = whine_malloc(len)))
return NULL;
if (buff)
free(buff);
buff = new;
}
data = buff;
}
for (d = data, b = block; len > 0 && b; b = b->next)
{
blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
if (memcmp(data, b->key, blen) != 0)
match = 0;
memcpy(data, b->key, blen);
data += blen;
memcpy(d, b->key, blen);
d += blen;
len -= blen;
}
return match;
return data;
}
#endif
......@@ -136,24 +136,6 @@ struct dns_header {
(cp) += 4; \
}
#define CHECKED_GETCHAR(var, ptr, len) do { \
if ((len) < 1) return 0; \
var = *ptr++; \
(len) -= 1; \
} while (0)
#define CHECKED_GETSHORT(var, ptr, len) do { \
if ((len) < 2) return 0; \
GETSHORT(var, ptr); \
(len) -= 2; \
} while (0)
#define CHECKED_GETLONG(var, ptr, len) do { \
if ((len) < 4) return 0; \
GETLONG(var, ptr); \
(len) -= 4; \
} while (0)
#define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
......
......@@ -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. */
......@@ -993,8 +994,7 @@ struct crec *cache_enumerate(int init);
#ifdef HAVE_DNSSEC
void blockdata_report(void);
struct blockdata *blockdata_alloc(char *data, size_t len);
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt);
int blockdata_retrieve(struct blockdata *block, size_t len, void *data);
void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
void blockdata_free(struct blockdata *blocks);
#endif
......
/* dnssec-crypto.h is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef DNSSEC_CRYPTO_H
#define DNSSEC_CRYPTO_H
struct blockdata;
/*
* vtable for a signature verification algorithm.
*
* Each algorithm verifies that a certain signature over a (possibly non-contigous)
* array of data has been made with the specified key.
*
* Sample of usage:
*
* // First, set the signature we need to check. Notice: data is not copied
* // nor consumed, so the pointer must stay valid.
* alg->set_signature(sig, 16);
*
* // Second, get push the data through the corresponding digest algorithm;
* // data is consumed immediately, so the buffers can be freed or modified.
* digestalg_begin(alg->get_digestalgo());
* digestalg_add_data(buf1, 123);
* digestalg_add_data(buf2, 45);
* digestalg_add_data(buf3, 678);
* alg->set_digest(digestalg_final());
*
* // Third, verify if we got the correct key for this signature.
* alg->verify(key1, 16);
* alg->verify(key2, 16);
*/
typedef struct VerifyAlgCtx VerifyAlgCtx;
typedef struct
{
int digest_algo;
int (*verify)(VerifyAlgCtx *ctx, struct blockdata *key, unsigned key_len);
} VerifyAlg;
struct VerifyAlgCtx
{
const VerifyAlg *vtbl;
unsigned char *sig;
size_t siglen;
unsigned char digest[64]; /* TODO: if memory problems, use VLA */
};
int verifyalg_supported(int algo);
VerifyAlgCtx* verifyalg_alloc(int algo);
void verifyalg_free(VerifyAlgCtx *a);
int verifyalg_algonum(VerifyAlgCtx *a);
/* Functions to calculate the digest of a key */
/* RFC4034 digest algorithms */
#define DIGESTALG_SHA1 1
#define DIGESTALG_SHA256 2
#define DIGESTALG_MD5 256
#define DIGESTALG_SHA512 257
int digestalg_supported(int algo);
void digestalg_begin(int algo);
void digestalg_add_data(void *data, unsigned len);
void digestalg_add_keydata(struct blockdata *key, size_t len);
unsigned char *digestalg_final(void);
int digestalg_len(void);
#endif /* DNSSEC_CRYPTO_H */
/* dnssec-openssl.c is Copyright (c) 2012 Giovanni Bajo <rasky@develer.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; version 2 dated June, 1991, or
(at your option) version 3 dated 29 June, 2007.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "dnsmasq.h"
#ifdef HAVE_DNSSEC
#include "dnssec-crypto.h"
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <string.h>
#define POOL_SIZE 1
static union _Pool
{
VerifyAlgCtx ctx;
} Pool[POOL_SIZE];
static char pool_used = 0;
static void print_hex(unsigned char *data, unsigned len)
{
while (len > 0)
{
printf("%02x", *data++);
--len;
}
printf("\n");
}
static int keydata_to_bn(BIGNUM *ret, struct blockdata **key_data, unsigned char **p, unsigned len)
{
size_t cnt;
BIGNUM temp;
BN_init(ret);
cnt = blockdata_walk(key_data, p, len);
BN_bin2bn(*p, cnt, ret);
len -= cnt;
*p += cnt;
while (len > 0)
{
if (!(cnt = blockdata_walk(key_data, p, len)))
return 0;
BN_lshift(ret, ret, cnt*8);
BN_init(&temp);
BN_bin2bn(*p, cnt, &temp);
BN_add(ret, ret, &temp);
len -= cnt;
*p += cnt;
}
return 1;
}
static int rsasha1_parse_key(BIGNUM *exp, BIGNUM *mod, struct blockdata *key_data, unsigned key_len)
{
unsigned char *p = key_data->key;
size_t exp_len, mod_len;
CHECKED_GETCHAR(exp_len, p, key_len);
if (exp_len == 0)
CHECKED_GETSHORT(exp_len, p, key_len);
if (exp_len >= key_len)
return 0;
mod_len = key_len - exp_len;
return keydata_to_bn(exp, &key_data, &p, exp_len) &&
keydata_to_bn(mod, &key_data, &p, mod_len);
}
static int dsasha1_parse_key(BIGNUM *Q, BIGNUM *P, BIGNUM *G, BIGNUM *Y, struct blockdata *key_data, unsigned key_len)
{
unsigned char *p = key_data->key;
int T;
CHECKED_GETCHAR(T, p, key_len);
return
keydata_to_bn(Q, &key_data, &p, 20) &&
keydata_to_bn(P, &key_data, &p, 64+T*8) &&
keydata_to_bn(G, &key_data, &p, 64+T*8) &&
keydata_to_bn(Y, &key_data, &p, 64+T*8);
}
static int rsa_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len, int nid, int dlen)
{
int validated = 0;
RSA *rsa = RSA_new();
rsa->e = BN_new();
rsa->n = BN_new();
if (rsasha1_parse_key(rsa->e, rsa->n, key_data, key_len)
&& RSA_verify(nid, ctx->digest, dlen, ctx->sig, ctx->siglen, rsa))
validated = 1;
RSA_free(rsa);
return validated;
}
static int rsamd5_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_md5, 16);
}
static int rsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha1, 20);
}
static int rsasha256_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha256, 32);
}
static int rsasha512_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
return rsa_verify(ctx, key_data, key_len, NID_sha512, 64);
}
static int dsasha1_verify(VerifyAlgCtx *ctx, struct blockdata *key_data, unsigned key_len)
{
static unsigned char asn1_signature[] =
{
0x30, 0x2E, // sequence
0x02, 21, // large integer (21 bytes)
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // R
0x02, 21, // large integer (21 bytes)
0x00, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // S
};
int validated = 0;
/* A DSA signature is made of 2 bignums (R & S). We could parse them manually with BN_bin2bn(),
but OpenSSL does not have an API to verify a DSA signature given R and S, and insists
in having a ASN.1 BER sequence (as per RFC3279).
We prepare a hard-coded ASN.1 sequence, and just fill in the R&S numbers in it. */
memcpy(asn1_signature+5, ctx->sig+1, 20);
memcpy(asn1_signature+28, ctx->sig+21, 20);
DSA *dsa = DSA_new();
dsa->q = BN_new();
dsa->p = BN_new();
dsa->g = BN_new();
dsa->pub_key = BN_new();
if (dsasha1_parse_key(dsa->q, dsa->p, dsa->g, dsa->pub_key, key_data, key_len)
&& DSA_verify(0, ctx->digest, 20, asn1_signature, countof(asn1_signature), dsa) > 0)
validated = 1;
DSA_free(dsa);
return validated;
}
#define VALG_UNSUPPORTED() { \
0,0 \
} /**/
#define VALG_VTABLE(alg, digest) { \
digest, \
alg ## _verify \
} /**/
/* Updated registry that merges various RFCs:
https://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xml */
static const VerifyAlg valgs[] =
{
VALG_UNSUPPORTED(), /* 0: reserved */
VALG_VTABLE(rsamd5, DIGESTALG_MD5), /* 1: RSAMD5 */
VALG_UNSUPPORTED(), /* 2: DH */
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 3: DSA */
VALG_UNSUPPORTED(), /* 4: ECC */
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 5: RSASHA1 */
VALG_VTABLE(dsasha1, DIGESTALG_SHA1), /* 6: DSA-NSEC3-SHA1 */
VALG_VTABLE(rsasha1, DIGESTALG_SHA1), /* 7: RSASHA1-NSEC3-SHA1 */
VALG_VTABLE(rsasha256, DIGESTALG_SHA256), /* 8: RSASHA256 */
VALG_UNSUPPORTED(), /* 9: unassigned */
VALG_VTABLE(rsasha512, DIGESTALG_SHA512), /* 10: RSASHA512 */
VALG_UNSUPPORTED(), /* 11: unassigned */
VALG_UNSUPPORTED(), /* 12: ECC-GOST */
VALG_UNSUPPORTED(), /* 13: ECDSAP256SHA256 */
VALG_UNSUPPORTED(), /* 14: ECDSAP384SHA384 */
};
/* TODO: remove if we don't need this anymore
(to be rechecked if we ever remove OpenSSL) */
static const int valgctx_size[] =
{
0, /* 0: reserved */
sizeof(VerifyAlgCtx), /* 1: RSAMD5 */
0, /* 2: DH */
sizeof(VerifyAlgCtx), /* 3: DSA */
0, /* 4: ECC */
sizeof(VerifyAlgCtx), /* 5: RSASHA1 */
sizeof(VerifyAlgCtx), /* 6: DSA-NSEC3-SHA1 */
sizeof(VerifyAlgCtx), /* 7: RSASHA1-NSEC3-SHA1 */
sizeof(VerifyAlgCtx), /* 8: RSASHA256 */
0, /* 9: unassigned */
sizeof(VerifyAlgCtx), /* 10: RSASHA512 */
0, /* 11: unassigned */
0, /* 12: ECC-GOST */
0, /* 13: ECDSAP256SHA256 */
0, /* 14: ECDSAP384SHA384 */
};
int verifyalg_supported(int algo)
{
return (algo < countof(valgctx_size) && valgctx_size[algo] != 0);
}
VerifyAlgCtx* verifyalg_alloc(int algo)
{
int i;
VerifyAlgCtx *ret = 0;
if (pool_used == (1<<POOL_SIZE)-1)
ret = whine_malloc(valgctx_size[algo]);
else
for (i = 0; i < POOL_SIZE; ++i)
if (!(pool_used & (1 << i)))
{
ret = (VerifyAlgCtx*)&Pool[i];
pool_used |= 1 << i;
break;
}
if (ret)
ret->vtbl = &valgs[algo];
return ret;
}
void verifyalg_free(VerifyAlgCtx *a)
{
int pool_idx = ((char*)a - (char*)&Pool[0]) / sizeof(Pool[0]);
if (pool_idx < 0 || pool_idx >= POOL_SIZE)
{
free(a);
return;
}
pool_used &= ~(1 << pool_idx);
}
int verifyalg_algonum(VerifyAlgCtx *a)
{
int num = a->vtbl - valgs;
if (num < 0 || num >= countof(valgs))
return -1;
return num;
}
static EVP_MD_CTX digctx;
int digestalg_supported(int algo)
{
return (algo == DIGESTALG_SHA1 ||
algo == DIGESTALG_SHA256 ||
algo == DIGESTALG_MD5 ||
algo == DIGESTALG_SHA512);
}
void digestalg_begin(int algo)
{
EVP_MD_CTX_init(&digctx);
if (algo == DIGESTALG_SHA1)
EVP_DigestInit_ex(&digctx, EVP_sha1(), NULL);
else if (algo == DIGESTALG_SHA256)
EVP_DigestInit_ex(&digctx, EVP_sha256(), NULL);
else if (algo == DIGESTALG_SHA512)
EVP_DigestInit_ex(&digctx, EVP_sha512(), NULL);
else if (algo == DIGESTALG_MD5)
EVP_DigestInit_ex(&digctx, EVP_md5(), NULL);
}
int digestalg_len()
{
return EVP_MD_CTX_size(&digctx);
}
void digestalg_add_data(void *data, unsigned len)
{
EVP_DigestUpdate(&digctx, data, len);
}
void digestalg_add_keydata(struct blockdata *key, size_t len)
{
size_t cnt; unsigned char *p = NULL;
while (len)
{
cnt = blockdata_walk(&key, &p, len);
EVP_DigestUpdate(&digctx, p, cnt);
p += cnt;
len -= cnt;
}
}
unsigned char* digestalg_final(void)
{
static unsigned char digest[32];
EVP_DigestFinal(&digctx, digest, NULL);
return digest;
}
#endif /* HAVE_DNSSEC */
......@@ -19,13 +19,210 @@
#ifdef HAVE_DNSSEC
#include "dnssec-crypto.h"
#include <nettle/rsa.h>
#include <nettle/dsa.h>
#include <nettle/nettle-meta.h>
#include <gmp.h>
#define SERIAL_UNDEF -100
#define SERIAL_EQ 0
#define SERIAL_LT -1
#define SERIAL_GT 1
/* http://www.iana.org/assignments/ds-rr-types/ds-rr-types.xhtml */
static char *ds_digest_name(int digest)
{
switch (digest)
{
case 1: return "sha1";
case 2: return "sha256";
case 3: return "gosthash94";
case 4: return "sha384";
default: return NULL;
}
}
/* http://www.iana.org/assignments/dns-sec-alg-numbers/dns-sec-alg-numbers.xhtml */
static char *algo_digest_name(int algo)
{
switch (algo)
{
case 1: return "md5";
case 3: return "sha1";
case 5: return "sha1";
case 6: return "sha1";
case 7: return "sha1";
case 8: return "sha256";
case 10: return "sha512";
case 12: return "gosthash94";
case 13: return "sha256";
case 14: return "sha384";
default: return NULL;
}
}
/* Find pointer to correct hash function in nettle library */
static const struct nettle_hash *hash_find(char *name)
{
int i;
if (!name)
return NULL;
for (i = 0; nettle_hashes[i]; i++)
{
if (strcmp(nettle_hashes[i]->name, name) == 0)
return nettle_hashes[i];
}
return NULL;
}
/* expand ctx and digest memory allocations if necessary and init hash function */
static int hash_init(const struct nettle_hash *hash, void **ctxp, unsigned char **digestp)
{
static void *ctx = NULL;
static unsigned char *digest = NULL;
static unsigned int ctx_sz = 0;
static unsigned int digest_sz = 0;
void *new;
if (ctx_sz < hash->context_size)
{
if (!(new = whine_malloc(hash->context_size)))
return 0;
if (ctx)
free(ctx);
ctx = new;
ctx_sz = hash->context_size;
}
if (digest_sz < hash->digest_size)
{
if (!(new = whine_malloc(hash->digest_size)))
return 0;
if (digest)
free(digest);
digest = new;
digest_sz = hash->digest_size;
}
*ctxp = ctx;
*digestp = digest;
hash->init(ctx);
return 1;
}
static int rsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, int algo)
{
unsigned char *p;
size_t exp_len;
static struct rsa_public_key *key = NULL;
static mpz_t sig_mpz;
if (key == NULL)
{
if (!(key = whine_malloc(sizeof(struct rsa_public_key))))
return 0;
nettle_rsa_public_key_init(key);
mpz_init(sig_mpz);
}
if ((key_len < 3) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
key_len--;
if ((exp_len = *p++) == 0)
{
GETSHORT(exp_len, p);
key_len -= 2;
}
if (exp_len >= key_len)
return 0;
key->size = key_len - exp_len;
mpz_import(key->e, exp_len, 1, 1, 0, 0, p);
mpz_import(key->n, key->size, 1, 1, 0, 0, p + exp_len);
mpz_import(sig_mpz, sig_len, 1, 1, 0, 0, sig);
switch (algo)
{
case 1:
return nettle_rsa_md5_verify_digest(key, digest, sig_mpz);
case 5: case 7:
return nettle_rsa_sha1_verify_digest(key, digest, sig_mpz);
case 8:
return nettle_rsa_sha256_verify_digest(key, digest, sig_mpz);
case 10:
return nettle_rsa_sha512_verify_digest(key, digest, sig_mpz);
}
return 0;
}
static int dsa_verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, int algo)
{
unsigned char *p;
unsigned int t;
static struct dsa_public_key *key = NULL;
static struct dsa_signature *sig_struct;
if (key == NULL)
{
if (!(sig_struct = whine_malloc(sizeof(struct dsa_signature))) ||
!(key = whine_malloc(sizeof(struct dsa_public_key))))
return 0;
nettle_dsa_public_key_init(key);
nettle_dsa_signature_init(sig_struct);
}
if ((sig_len < 41) || !(p = blockdata_retrieve(key_data, key_len, NULL)))
return 0;
t = *p++;
if (key_len < (213 + (t * 24)))
return 0;
mpz_import(key->q, 20, 1, 1, 0, 0, p); p += 20;
mpz_import(key->p, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(key->g, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(key->y, 64 + (t*8), 1, 1, 0, 0, p); p += 64 + (t*8);
mpz_import(sig_struct->r, 20, 1, 1, 0, 0, sig+1);
mpz_import(sig_struct->s, 20, 1, 1, 0, 0, sig+21);
(void)algo;
return nettle_dsa_sha1_verify_digest(key, digest, sig_struct);
}
static int verify(struct blockdata *key_data, unsigned int key_len, unsigned char *sig, size_t sig_len,
unsigned char *digest, int algo)
{
switch (algo)
{
case 1: case 5: case 7: case 8: case 10:
return rsa_verify(key_data, key_len, sig, sig_len, digest, algo);
case 3: case 6:
return dsa_verify(key_data, key_len, sig, sig_len, digest, algo);
}
return 0;
}
/* Convert from presentation format to wire format, in place.
Also map UC -> LC.
Note that using extract_name to get presentation format
......@@ -362,7 +559,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (type_covered == type &&
check_date_range(sig_inception, sig_expiration) &&
verifyalg_supported(algo) &&
hash_find(algo_digest_name(algo)) &&
labels <= name_labels)
{
if (sigidx == sig_sz)
......@@ -404,9 +601,11 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* Now try all the sigs to try and find one which validates */
for (j = 0; j <sigidx; j++)
{
unsigned char *psav;
int i, wire_len;
VerifyAlgCtx *alg;
unsigned char *psav, *sig;
int i, wire_len, sig_len;
const struct nettle_hash *hash;
void *ctx;
unsigned char *digest;
u32 nsigttl;
p = sigs[j];
......@@ -427,16 +626,18 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
if (!key && !(crecp = cache_find_by_name(NULL, keyname, now, F_DNSKEY)))
return STAT_NEED_KEY;
alg = verifyalg_alloc(algo);
alg->sig = p;
alg->siglen = rdlen - (p - psav);
sig = p;
sig_len = rdlen - (p - psav);
if (!(hash = hash_find(algo_digest_name(algo))) ||
!hash_init(hash, &ctx, &digest))
continue;
nsigttl = htonl(orig_ttl);
digestalg_begin(alg->vtbl->digest_algo);
digestalg_add_data(psav, 18);
hash->update(ctx, 18, psav);
wire_len = to_wire(keyname);
digestalg_add_data(keyname, wire_len);
hash->update(ctx, (unsigned int)wire_len, (unsigned char*)keyname);
from_wire(keyname);
for (i = 0; i < rrsetidx; ++i)
......@@ -462,9 +663,9 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
}
wire_len = to_wire(name_start);
digestalg_add_data(name_start, wire_len);
digestalg_add_data(p, 4); /* class and type */
digestalg_add_data(&nsigttl, 4);
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name_start);
hash->update(ctx, 4, p); /* class and type */
hash->update(ctx, 4, (unsigned char *)&nsigttl);
p += 8; /* skip class, type, ttl */
GETSHORT(rdlen, p);
......@@ -479,27 +680,27 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
for (len = 0; (seg = get_rdata(header, plen, end, name, &cp, &dp)) != 0; len += seg);
len += end - cp;
len = htons(len);
digestalg_add_data(&len, 2);
hash->update(ctx, 2, (unsigned char *)&len);
/* Now canonicalise again and digest. */
cp = p;
dp = rr_desc;
while ((seg = get_rdata(header, plen, end, name, &cp, &dp)))
digestalg_add_data(name, seg);
hash->update(ctx, seg, (unsigned char *)name);
if (cp != end)
digestalg_add_data(cp, end - cp);
hash->update(ctx, end - cp, cp);
}
hash->digest(ctx, hash->digest_size, digest);
/* namebuff used for workspace above, restore to leave unchanged on exit */
p = (unsigned char*)(rrset[0]);
extract_name(header, plen, &p, name, 1, 0);
memcpy(alg->digest, digestalg_final(), digestalg_len());
if (key)
{
if (algo_in == algo && keytag_in == key_tag &&
alg->vtbl->verify(alg, key, keylen))
verify(key, keylen, sig, sig_len, digest, algo))
return STAT_SECURE;
}
else
......@@ -507,7 +708,7 @@ static int validate_rrset(time_t now, struct dns_header *header, size_t plen, in
/* iterate through all possible keys 4035 5.3.1 */
for (; crecp; crecp = cache_find_by_name(crecp, keyname, now, F_DNSKEY))
if (crecp->addr.key.algo == algo && crecp->addr.key.keytag == key_tag &&
alg->vtbl->verify(alg, crecp->addr.key.keydata, crecp->uid))
verify(crecp->addr.key.keydata, crecp->uid, sig, sig_len, digest, algo))
return STAT_SECURE;
}
}
......@@ -579,7 +780,6 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
psave = p;
/* length at least covers flags, protocol and algo now. */
GETSHORT(flags, p);
if (*p++ != 3)
return STAT_INSECURE;
......@@ -606,30 +806,41 @@ int dnssec_validate_by_ds(time_t now, struct dns_header *header, size_t plen, ch
continue;
for (recp1 = crecp; recp1; recp1 = cache_find_by_name(recp1, name, now, F_DS))
if (recp1->addr.key.algo == algo &&
recp1->addr.key.keytag == keytag &&
(flags & 0x100) && /* zone key flag */
digestalg_supported(recp1->addr.key.digest))
{
int wire_len = to_wire(name);
digestalg_begin(recp1->addr.key.digest);
digestalg_add_data(name, wire_len);
digestalg_add_data((char *)psave, rdlen);
{
void *ctx;
unsigned char *digest, *ds_digest;
const struct nettle_hash *hash;
if (recp1->addr.key.algo == algo &&
recp1->addr.key.keytag == keytag &&
(flags & 0x100) && /* zone key flag */
(hash = hash_find(ds_digest_name(recp1->addr.key.digest))) &&
hash_init(hash, &ctx, &digest))
from_wire(name);
if (recp1->uid == digestalg_len() &&
blockdata_retrieve(recp1->addr.key.keydata, recp1->uid, digestalg_final()) &&
validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag))
{
struct all_addr a;
valid = 1;
a.addr.keytag = keytag;
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
break;
}
}
{
int wire_len = to_wire(name);
/* Note that digest may be different between DSs, so
we can't move this outside the loop. */
hash->update(ctx, (unsigned int)wire_len, (unsigned char *)name);
hash->update(ctx, (unsigned int)rdlen, psave);
hash->digest(ctx, hash->digest_size, digest);
from_wire(name);
if (recp1->uid == (int)hash->digest_size &&
(ds_digest = blockdata_retrieve(recp1->addr.key.keydata, recp1->uid, NULL)) &&
memcmp (ds_digest, digest, recp1->uid) == 0 &&
validate_rrset(now, header, plen, class, T_DNSKEY, name, keyname, key, rdlen - 4, algo, keytag))
{
struct all_addr a;
valid = 1;
a.addr.keytag = keytag;
log_query(F_KEYTAG | F_UPSTREAM, name, &a, "DNSKEY keytag %u");
break;
}
}
}
}
if (valid)
......
......@@ -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
......@@ -3687,7 +3687,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
/* Upper bound on length */
new->key = opt_malloc((3*strlen(key64)/4));
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"));
......
......@@ -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 ;
}
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