diff --git a/Makefile b/Makefile index 92158e0..fb8e9eb 100644 --- a/Makefile +++ b/Makefile @@ -51,24 +51,24 @@ 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 @@ -76,8 +76,8 @@ hdrs = dnsmasq.h config.h dhcp-protocol.h dhcp6-protocol.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 : diff --git a/src/blockdata.c b/src/blockdata.c index f9155d9..1da78b7 100644 --- a/src/blockdata.c +++ b/src/blockdata.c @@ -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 diff --git a/src/dns-protocol.h b/src/dns-protocol.h index 90257ad..2149e72 100644 --- a/src/dns-protocol.h +++ b/src/dns-protocol.h @@ -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)) diff --git a/src/dnsmasq.h b/src/dnsmasq.h index 0082510..4d91d92 100644 --- a/src/dnsmasq.h +++ b/src/dnsmasq.h @@ -993,8 +993,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 diff --git a/src/dnssec-crypto.h b/src/dnssec-crypto.h deleted file mode 100644 index 77c5bc5..0000000 --- a/src/dnssec-crypto.h +++ /dev/null @@ -1,83 +0,0 @@ -/* dnssec-crypto.h is Copyright (c) 2012 Giovanni Bajo - - 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 . -*/ - -#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 */ diff --git a/src/dnssec-openssl.c b/src/dnssec-openssl.c deleted file mode 100644 index 2e25f82..0000000 --- a/src/dnssec-openssl.c +++ /dev/null @@ -1,316 +0,0 @@ -/* dnssec-openssl.c is Copyright (c) 2012 Giovanni Bajo - - 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 . -*/ - -#include "dnsmasq.h" - -#ifdef HAVE_DNSSEC - -#include "dnssec-crypto.h" -#include -#include -#include -#include -#include - -#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<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 */ diff --git a/src/dnssec.c b/src/dnssec.c index 712696f..940115f 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -19,13 +19,210 @@ #ifdef HAVE_DNSSEC -#include "dnssec-crypto.h" +#include +#include +#include +#include #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 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); + { + 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)) - digestalg_begin(recp1->addr.key.digest); - digestalg_add_data(name, wire_len); - digestalg_add_data((char *)psave, rdlen); - - 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) diff --git a/src/option.c b/src/option.c index e4885ee..912876f 100644 --- a/src/option.c +++ b/src/option.c @@ -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"));