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 ...@@ -51,33 +51,33 @@ top!=pwd
# GNU make way. # GNU make way.
top?=$(CURDIR) top?=$(CURDIR)
dbus_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DBUS $(PKG_CONFIG) --cflags dbus-1` 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` 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_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` 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_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` 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_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` 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` nettle_cflags = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --cflags nettle hogweed`
sec_libs = `echo $(COPTS) | $(top)/bld/pkg-wrapper HAVE_DNSSEC $(PKG_CONFIG) --libs libcrypto` 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` sunos_libs = `if uname | grep SunOS >/dev/null 2>&1; then echo -lsocket -lnsl -lposix4; fi`
version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"' version = -DVERSION='\"`$(top)/bld/get-version $(top)`\"'
objs = cache.o rfc1035.o util.o option.o forward.o network.o \ 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 \ 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 \ 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 \ 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 \ 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) all : $(BUILDDIR)
@cd $(BUILDDIR) && $(MAKE) \ @cd $(BUILDDIR) && $(MAKE) \
top="$(top)" \ top="$(top)" \
build_cflags="$(version) $(dbus_cflags) $(idn_cflags) $(ct_cflags) $(lua_cflags) $(sec_cflags)" \ 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) $(sec_libs)" \ build_libs="$(dbus_libs) $(idn_libs) $(ct_libs) $(lua_libs) $(sunos_libs) $(nettle_libs)" \
-f $(top)/Makefile dnsmasq -f $(top)/Makefile dnsmasq
mostly_clean : mostly_clean :
......
...@@ -67,21 +67,6 @@ struct blockdata *blockdata_alloc(char *data, size_t len) ...@@ -67,21 +67,6 @@ struct blockdata *blockdata_alloc(char *data, size_t len)
return ret; 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) void blockdata_free(struct blockdata *blocks)
{ {
struct blockdata *tmp; struct blockdata *tmp;
...@@ -96,24 +81,38 @@ void blockdata_free(struct blockdata *blocks) ...@@ -96,24 +81,38 @@ void blockdata_free(struct blockdata *blocks)
} }
} }
/* copy blocks into data[], return 1 if data[] unchanged by so doing */ /* if data == NULL, return pointer to static block of sufficient size */
int blockdata_retrieve(struct blockdata *block, size_t len, void *data) void *blockdata_retrieve(struct blockdata *block, size_t len, void *data)
{ {
size_t blen; size_t blen;
struct blockdata *b; 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; blen = len > KEYBLOCK_LEN ? KEYBLOCK_LEN : len;
if (memcmp(data, b->key, blen) != 0) memcpy(d, b->key, blen);
match = 0; d += blen;
memcpy(data, b->key, blen);
data += blen;
len -= blen; len -= blen;
} }
return match; return data;
} }
#endif #endif
...@@ -136,24 +136,6 @@ struct dns_header { ...@@ -136,24 +136,6 @@ struct dns_header {
(cp) += 4; \ (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) \ #define CHECK_LEN(header, pp, plen, len) \
((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen)) ((size_t)((pp) - (unsigned char *)(header) + (len)) <= (plen))
......
...@@ -230,7 +230,8 @@ struct event_desc { ...@@ -230,7 +230,8 @@ struct event_desc {
#define OPT_QUIET_DHCP6 43 #define OPT_QUIET_DHCP6 43
#define OPT_QUIET_RA 44 #define OPT_QUIET_RA 44
#define OPT_DNSSEC_VALID 45 #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 /* 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. */ 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); ...@@ -993,8 +994,7 @@ struct crec *cache_enumerate(int init);
#ifdef HAVE_DNSSEC #ifdef HAVE_DNSSEC
void blockdata_report(void); void blockdata_report(void);
struct blockdata *blockdata_alloc(char *data, size_t len); struct blockdata *blockdata_alloc(char *data, size_t len);
size_t blockdata_walk(struct blockdata **key, unsigned char **p, size_t cnt); void *blockdata_retrieve(struct blockdata *block, size_t len, void *data);
int blockdata_retrieve(struct blockdata *block, size_t len, void *data);
void blockdata_free(struct blockdata *blocks); void blockdata_free(struct blockdata *blocks);
#endif #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 */
This diff is collapsed.
...@@ -511,7 +511,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server ...@@ -511,7 +511,7 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server
if (option_bool(OPT_DNSSEC_VALID)) if (option_bool(OPT_DNSSEC_VALID))
header->hb4 &= ~HB4_AD; header->hb4 &= ~HB4_AD;
if (cache_secure) if (!(header->hb4 & HB4_CD) && cache_secure)
header->hb4 |= HB4_AD; header->hb4 |= HB4_AD;
#endif #endif
...@@ -556,6 +556,31 @@ static size_t process_reply(struct dns_header *header, time_t now, struct server ...@@ -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 /* do this after extract_addresses. Ensure NODATA reply and remove
nameserver info. */ nameserver info. */
...@@ -824,7 +849,6 @@ void reply_query(int fd, int family, time_t now) ...@@ -824,7 +849,6 @@ void reply_query(int fd, int family, time_t now)
if (status == STAT_SECURE) if (status == STAT_SECURE)
cache_secure = 1; cache_secure = 1;
/* TODO return SERVFAIL here */
else if (status == STAT_BOGUS) else if (status == STAT_BOGUS)
no_cache_dnssec = 1; no_cache_dnssec = 1;
......
...@@ -140,7 +140,7 @@ struct myoption { ...@@ -140,7 +140,7 @@ struct myoption {
#define LOPT_QUIET_RA 328 #define LOPT_QUIET_RA 328
#define LOPT_SEC_VALID 329 #define LOPT_SEC_VALID 329
#define LOPT_DNSKEY 330 #define LOPT_DNSKEY 330
#define LOPT_DNSSEC_PERM 331
#ifdef HAVE_GETOPT_LONG #ifdef HAVE_GETOPT_LONG
static const struct option opts[] = static const struct option opts[] =
...@@ -278,6 +278,7 @@ static const struct myoption opts[] = ...@@ -278,6 +278,7 @@ static const struct myoption opts[] =
{ "synth-domain", 1, 0, LOPT_SYNTH }, { "synth-domain", 1, 0, LOPT_SYNTH },
{ "dnssec", 0, 0, LOPT_SEC_VALID }, { "dnssec", 0, 0, LOPT_SEC_VALID },
{ "dnskey", 1, 0, LOPT_DNSKEY }, { "dnskey", 1, 0, LOPT_DNSKEY },
{ "dnssec-permissive", 0, 0, LOPT_DNSSEC_PERM },
#ifdef OPTION6_PREFIX_CLASS #ifdef OPTION6_PREFIX_CLASS
{ "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS }, { "dhcp-prefix-class", 1, 0, LOPT_PREF_CLSS },
#endif #endif
...@@ -428,10 +429,9 @@ static struct { ...@@ -428,10 +429,9 @@ static struct {
{ LOPT_AUTHPEER, ARG_DUP, "<ipaddr>[,<ipaddr>...]", gettext_noop("Peers which are allowed to do zone transfer"), NULL }, { 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_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_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_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_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 #ifdef OPTION6_PREFIX_CLASS
{ LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL }, { LOPT_PREF_CLSS, ARG_DUP, "set:tag,<class>", gettext_noop("Specify DHCPv6 prefix class"), NULL },
#endif #endif
...@@ -3687,7 +3687,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma ...@@ -3687,7 +3687,7 @@ static int one_opt(int option, char *arg, char *errstr, char *gen_err, int comma
/* Upper bound on length */ /* Upper bound on length */
new->key = opt_malloc((3*strlen(key64)/4)); new->key = opt_malloc((3*strlen(key64)/4)+1);
unhide_metas(key64); unhide_metas(key64);
if ((new->keylen = parse_base64(key64, new->key)) == -1) if ((new->keylen = parse_base64(key64, new->key)) == -1)
ret_err(_("bad base64 in DNSKEY")); ret_err(_("bad base64 in DNSKEY"));
......
...@@ -511,14 +511,17 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned ...@@ -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) int optno, unsigned char *opt, size_t optlen, int set_do)
{ {
unsigned char *lenp, *datap, *p; 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 */ /* We are adding the pseudoheader */
if (!(p = skip_questions(header, plen)) || if (!(p = skip_questions(header, plen)) ||
!(p = skip_section(p, !(p = skip_section(p,
ntohs(header->ancount) + ntohs(header->nscount), ntohs(header->ancount) + ntohs(header->nscount) + ntohs(header->arcount),
header, plen))) header, plen)))
return plen; return plen;
*p++ = 0; /* empty name */ *p++ = 0; /* empty name */
...@@ -531,16 +534,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned ...@@ -531,16 +534,16 @@ static size_t add_pseudoheader(struct dns_header *header, size_t plen, unsigned
rdlen = 0; rdlen = 0;
if (((ssize_t)optlen) > (limit - (p + 4))) if (((ssize_t)optlen) > (limit - (p + 4)))
return plen; /* Too big */ return plen; /* Too big */
header->arcount = htons(1); header->arcount = htons(ntohs(header->arcount) + 1);
datap = p; datap = p;
} }
else else
{ {
int i, is_sign; int i;
unsigned short code, len, flags; unsigned short code, len, flags;
/* Must be at the end, if exists */
if (ntohs(header->arcount) != 1 || if (ntohs(header->arcount) != 1 ||
!(p = find_pseudoheader(header, plen, NULL, NULL, &is_sign)) ||
is_sign || is_sign ||
(!(p = skip_name(p, header, plen, 10)))) (!(p = skip_name(p, header, plen, 10))))
return plen; return plen;
...@@ -1147,7 +1150,6 @@ int extract_addresses(struct dns_header *header, size_t qlen, char *name, time_t ...@@ -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 /* If the packet holds exactly one query
return F_IPV4 or F_IPV6 and leave the name from the query in name */ 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 int extract_request(struct dns_header *header, size_t qlen, char *name, unsigned short *typep)
{ {
unsigned char *p = (unsigned char *)(header+1); unsigned char *p = (unsigned char *)(header+1);
...@@ -1447,23 +1449,30 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1447,23 +1449,30 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
int nameoffset; int nameoffset;
unsigned short flag; unsigned short flag;
int q, ans, anscount = 0, addncount = 0; 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; int is_sign;
struct crec *crecp; struct crec *crecp;
int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1; int nxdomain = 0, auth = 1, trunc = 0, sec_data = 1;
struct mx_srv_record *rec; 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 /* 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 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 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 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)) if (find_pseudoheader(header, qlen, NULL, &pheader, &is_sign))
{ {
unsigned short udpsz, flags; unsigned short udpsz, flags;
unsigned char *psave = pheader; unsigned char *psave = pheader;
have_pseudoheader = 1;
GETSHORT(udpsz, pheader); GETSHORT(udpsz, pheader);
pheader += 2; /* ext_rcode */ pheader += 2; /* ext_rcode */
GETSHORT(flags, pheader); GETSHORT(flags, pheader);
...@@ -1637,7 +1646,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1637,7 +1646,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun) if (!dryrun)
log_query(crecp->flags & ~F_FORWARD, name, &addr, NULL); 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; ans = 1;
if (!(crecp->flags & (F_HOSTS | F_DHCP))) if (!(crecp->flags & (F_HOSTS | F_DHCP)))
...@@ -1834,7 +1843,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -1834,7 +1843,7 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (!dryrun) if (!dryrun)
log_query(crecp->flags, name, NULL, NULL); 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, /* If we are returning local answers depending on network,
filter here. */ filter here. */
...@@ -2060,12 +2069,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -2060,12 +2069,6 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
if (trunc) if (trunc)
header->hb3 |= HB3_TC; 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) if (nxdomain)
SET_RCODE(header, NXDOMAIN); SET_RCODE(header, NXDOMAIN);
else else
...@@ -2073,6 +2076,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen, ...@@ -2073,6 +2076,18 @@ size_t answer_request(struct dns_header *header, char *limit, size_t qlen,
header->ancount = htons(anscount); header->ancount = htons(anscount);
header->nscount = htons(0); header->nscount = htons(0);
header->arcount = htons(addncount); 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