From ae1db39a33bd53cd2455e97b11d8c54d045dac0f Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Thu, 11 Jun 2015 15:40:44 +0200 Subject: [PATCH 01/28] Native stub validation --- aclocal.m4 | 4 +- configure | 17 +++-- configure.ac | 8 +-- src/config.h.in | 3 +- src/dnssec.c | 44 ++++--------- src/rr-dict.c | 162 +++++++++++++++++++----------------------------- src/rr-dict.h | 65 +++++++++---------- 7 files changed, 123 insertions(+), 180 deletions(-) diff --git a/aclocal.m4 b/aclocal.m4 index 95cd61e9..0cd56f0f 100644 --- a/aclocal.m4 +++ b/aclocal.m4 @@ -1,6 +1,6 @@ -# generated automatically by aclocal 1.15 -*- Autoconf -*- +# generated automatically by aclocal 1.14.1 -*- Autoconf -*- -# Copyright (C) 1996-2014 Free Software Foundation, Inc. +# Copyright (C) 1996-2013 Free Software Foundation, Inc. # This file is free software; the Free Software Foundation # gives unlimited permission to copy and/or distribute it, diff --git a/configure b/configure index 7c6cca29..d197b17b 100755 --- a/configure +++ b/configure @@ -760,7 +760,7 @@ with_sysroot enable_libtool_lock enable_rpath enable_tcp_fastopen -enable_broken_native_stub_dnssec +enable_native_stub_dnssec with_ssl enable_draft_edns_cookies with_libidn @@ -1406,9 +1406,8 @@ Optional Features: --disable-libtool-lock avoid locking (might break parallel builds) --disable-rpath disable hardcoded rpath (default=enabled) --enable-tcp-fastopen Enable TCP Fast Open - --enable-broken-native-stub-dnssec - Enable very experimental and broken native stub - DNSSEC support + --disable-native-stub-dnssec + Disable native stub DNSSEC support --enable-draft-edns-cookies Enable experimental edns cookies @@ -11738,20 +11737,20 @@ _ACEOF ;; esac -# Check whether --enable-broken-native-stub-dnssec was given. -if test "${enable_broken_native_stub_dnssec+set}" = set; then : - enableval=$enable_broken_native_stub_dnssec; +# Check whether --enable-native-stub-dnssec was given. +if test "${enable_native_stub_dnssec+set}" = set; then : + enableval=$enable_native_stub_dnssec; fi case "$enable_broken_native_stub_dnssec" in - yes) + yes|*) cat >>confdefs.h <<_ACEOF #define STUB_NATIVE_DNSSEC 1 _ACEOF ;; - no|*) + no) ;; esac diff --git a/configure.ac b/configure.ac index 2c096722..f4811cc9 100644 --- a/configure.ac +++ b/configure.ac @@ -132,12 +132,12 @@ case "$enable_tcp_fastopen" in ;; esac -AC_ARG_ENABLE(broken-native-stub-dnssec, AC_HELP_STRING([--enable-broken-native-stub-dnssec], [Enable very experimental and broken native stub DNSSEC support])) +AC_ARG_ENABLE(native-stub-dnssec, AC_HELP_STRING([--disable-native-stub-dnssec], [Disable native stub DNSSEC support])) case "$enable_broken_native_stub_dnssec" in - yes) - AC_DEFINE_UNQUOTED([STUB_NATIVE_DNSSEC], [1], [Define this to enable the very experimental and broken native stub DNSSEC support.]) + yes|*) + AC_DEFINE_UNQUOTED([STUB_NATIVE_DNSSEC], [1], [Define this to enable native stub DNSSEC support.]) ;; - no|*) + no) ;; esac diff --git a/src/config.h.in b/src/config.h.in index e2405b25..ff26526c 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -192,8 +192,7 @@ /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS -/* Define this to enable the very experimental and broken native stub DNSSEC - support. */ +/* Define this to enable native stub DNSSEC support. */ #undef STUB_NATIVE_DNSSEC /* System configuration dir */ diff --git a/src/dnssec.c b/src/dnssec.c index c623d6df..486cf27a 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -475,36 +475,6 @@ priv_getdns_rr_list_from_list(struct getdns_list *list, ldns_rr_list **rr_list) return r; } -static int -priv_getdns_rr_dict_with_compressed_names(getdns_dict *rr_dict) -{ - uint32_t rr_type; - getdns_dict *rdata; - - if (getdns_dict_get_int(rr_dict, "type", &rr_type)) - return 0; - if (rr_type == GETDNS_RRTYPE_RRSIG) { - if (getdns_dict_get_dict(rr_dict, "rdata", &rdata)) - return 0; - if (getdns_dict_get_int(rdata, "type_covered", &rr_type)) - return 0; - } - switch (rr_type) { - case GETDNS_RRTYPE_NS: - case GETDNS_RRTYPE_MD: - case GETDNS_RRTYPE_CNAME: - case GETDNS_RRTYPE_SOA: - case GETDNS_RRTYPE_MG: - case GETDNS_RRTYPE_MR: - case GETDNS_RRTYPE_PTR: - case GETDNS_RRTYPE_MINFO: - case GETDNS_RRTYPE_MX: - return 1; - default: - return 0; - } -} - static int ldns_dname_compare_v(const void *a, const void *b) { return ldns_dname_compare((ldns_rdf *)a, (ldns_rdf *)b); @@ -573,9 +543,6 @@ priv_getdns_dnssec_zone_from_list(struct getdns_list *list, if ((r = getdns_list_get_dict(list, i, &rr_dict))) break; - if (priv_getdns_rr_dict_with_compressed_names(rr_dict)) - continue; - if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) break; @@ -693,6 +660,17 @@ verify_rrset(ldns_dnssec_rrsets *rrset_and_sigs, ldns_rr_list *rrset = rrs2rr_list(rrset_and_sigs->rrs); ldns_rr_list *sigs = rrs2rr_list(rrset_and_sigs->signatures); s = ldns_verify(rrset, sigs, keys, good_keys); +#if 0 + if (s != 0) { + fprintf(stderr, "verify status %d\nrrset: ", s); + ldns_rr_list_print(stderr, rrset); + fprintf(stderr, "\nsigs: "); + ldns_rr_list_print(stderr, sigs); + fprintf(stderr, "\nkeys: "); + ldns_rr_list_print(stderr, keys); + fprintf(stderr, "\n\n"); + } +#endif ldns_rr_list_free(sigs); ldns_rr_list_free(rrset); return s; diff --git a/src/rr-dict.c b/src/rr-dict.c index d6dcea8c..85d779d8 100644 --- a/src/rr-dict.c +++ b/src/rr-dict.c @@ -255,9 +255,9 @@ static priv_getdns_rdata_def soa_rdata[] = { { "rname" , GETDNS_RDF_N_C }, { "serial" , GETDNS_RDF_I4 }, { "refresh" , GETDNS_RDF_I4 }, - { "refresh" , GETDNS_RDF_I4 }, { "retry" , GETDNS_RDF_I4 }, - { "expire" , GETDNS_RDF_I4 }}; + { "expire" , GETDNS_RDF_I4 }, + { "minimum" , GETDNS_RDF_I4 }}; static priv_getdns_rdata_def mg_rdata[] = { { "mgmname" , GETDNS_RDF_N_C }}; static priv_getdns_rdata_def mr_rdata[] = { @@ -742,9 +742,8 @@ static getdns_return_t priv_getdns_construct_wire_rdata_from_rdata( uint8_t **wire, size_t *wire_size) { getdns_return_t r = GETDNS_RETURN_GOOD; - const ldns_rr_descriptor *rr_descript; const priv_getdns_rr_def *def; - size_t i, size; + size_t i, j, size; struct getdns_bindata *bindata; uint32_t value; uint8_t *ptr; @@ -754,7 +753,6 @@ static getdns_return_t priv_getdns_construct_wire_rdata_from_rdata( assert(wire_size); def = priv_getdns_rr_def_lookup(rr_type); - rr_descript = ldns_rr_descript(rr_type); /* First calculate needed size */ size = 0; @@ -771,23 +769,7 @@ static getdns_return_t priv_getdns_construct_wire_rdata_from_rdata( r = GETDNS_RETURN_GENERIC_ERROR; break; } - switch (ldns_rr_descriptor_field_type(rr_descript, i)) { - - case LDNS_RDF_TYPE_CLASS: - case LDNS_RDF_TYPE_ALG : - case LDNS_RDF_TYPE_INT8 : size += 1; - break; - case LDNS_RDF_TYPE_TYPE : - case LDNS_RDF_TYPE_CERT_ALG: - case LDNS_RDF_TYPE_INT16: size += 2; - break; - case LDNS_RDF_TYPE_TIME : - case LDNS_RDF_TYPE_PERIOD: - case LDNS_RDF_TYPE_INT32: size += 4; - break; - default: r = GETDNS_RETURN_GENERIC_ERROR; - break; - } + size += def->rdata[i].type & GETDNS_RDF_FIXEDSZ; } *wire_size = size + 2; *wire = ptr = GETDNS_XMALLOC(rdata->mf, uint8_t, size + 2); @@ -815,64 +797,21 @@ static getdns_return_t priv_getdns_construct_wire_rdata_from_rdata( if ((r = getdns_dict_get_int( rdata, def->rdata[i].name, &value))) break; - - switch (ldns_rr_descriptor_field_type(rr_descript, i)) { - - case LDNS_RDF_TYPE_CLASS: - case LDNS_RDF_TYPE_ALG : - case LDNS_RDF_TYPE_INT8 : ptr[0] = (uint8_t) value & 0xff; - ptr += 1; - break; - case LDNS_RDF_TYPE_TYPE : - case LDNS_RDF_TYPE_CERT_ALG: - case LDNS_RDF_TYPE_INT16: ptr[0] = (uint8_t)(value>> 8) & 0xff; - ptr[1] = (uint8_t) value & 0xff; - ptr += 2; - break; - case LDNS_RDF_TYPE_TIME : - case LDNS_RDF_TYPE_PERIOD: - case LDNS_RDF_TYPE_INT32: ptr[0] = (uint8_t)(value>>24) & 0xff; - ptr[1] = (uint8_t)(value>>16) & 0xff; - ptr[2] = (uint8_t)(value>>8 ) & 0xff; - ptr[3] = (uint8_t) value & 0xff; - ptr += 4; - break; - default: r = GETDNS_RETURN_GENERIC_ERROR; - break; - } + + for (j = def->rdata[i].type & GETDNS_RDF_FIXEDSZ; j; j--) + *ptr++ = (uint8_t)(value >> (8 * (j - 1))) & 0xff; } if (r) GETDNS_FREE(rdata->mf, ptr); return r; } -static getdns_return_t -priv_getdns_dict_get_raw_rdata(struct getdns_dict *rdata, - uint8_t **wire, size_t *wire_size) -{ - getdns_return_t r; - struct getdns_bindata *bindata; - - if ((r = getdns_dict_get_bindata(rdata, "rdata_raw", &bindata))) - return r; - - *wire_size = bindata->size + 2; - *wire = GETDNS_XMALLOC(rdata->mf, uint8_t, *wire_size); - if (! *wire) - return GETDNS_RETURN_MEMORY_ERROR; - - (*wire)[0] = (uint8_t) (bindata->size >> 8) & 0xff; - (*wire)[1] = (uint8_t) bindata->size & 0xff; - - (void) memcpy(*wire + 2, bindata->data, bindata->size); - return GETDNS_RETURN_GOOD; -} - getdns_return_t priv_getdns_create_rr_from_dict(struct getdns_dict *rr_dict, ldns_rr **rr) { getdns_return_t r = GETDNS_RETURN_GOOD; struct getdns_bindata *name; + struct getdns_bindata *rdata_raw; struct getdns_dict *rdata; uint32_t rr_type; ldns_rdf *owner; @@ -887,41 +826,68 @@ priv_getdns_create_rr_from_dict(struct getdns_dict *rr_dict, ldns_rr **rr) *rr = ldns_rr_new(); if (! *rr) return GETDNS_RETURN_MEMORY_ERROR; - do { - r = getdns_dict_get_bindata(rr_dict, "name", &name); - if (r != GETDNS_RETURN_GOOD) + + if ((r = getdns_dict_get_bindata(rr_dict, "name", &name))) + goto error; + + owner = ldns_rdf_new_frm_data( + LDNS_RDF_TYPE_DNAME, name->size, name->data); + if (! owner) { + r = GETDNS_RETURN_MEMORY_ERROR; + goto error; + } + ldns_rr_set_owner(*rr, owner); + + if ((r = getdns_dict_get_int(rr_dict, "type", &rr_type))) + goto error; + ldns_rr_set_type(*rr, rr_type); + + if ((r = getdns_dict_get_dict(rr_dict, "rdata", &rdata))) + goto error; + + const priv_getdns_rr_def *rr_def = priv_getdns_rr_def_lookup(rr_type); + const priv_getdns_rdata_def *rd_def; + int n_rdata_fields; + + for ( rd_def = rr_def->rdata + , n_rdata_fields = rr_def->n_rdata_fields + ; n_rdata_fields + ; n_rdata_fields-- + , rd_def++ ) { + + if (rd_def->type & GETDNS_RDF_COMPRESSED) break; - owner = ldns_rdf_new_frm_data( - LDNS_RDF_TYPE_DNAME, name->size, name->data); - if (! owner) { + } + + if (n_rdata_fields == 0 && GETDNS_RETURN_GOOD == + (r = getdns_dict_get_bindata(rdata, "rdata_raw", &rdata_raw))) { + + wire_size = rdata_raw->size + 2; + wire = GETDNS_XMALLOC(rdata->mf, uint8_t, wire_size); + if (! wire) { r = GETDNS_RETURN_MEMORY_ERROR; - break; + goto error; } - ldns_rr_set_owner(*rr, owner); - r = getdns_dict_get_int(rr_dict, "type", &rr_type); - if (r != GETDNS_RETURN_GOOD) - break; - ldns_rr_set_type(*rr, rr_type); + wire[0] = (uint8_t) (rdata_raw->size >> 8) & 0xff; + wire[1] = (uint8_t) rdata_raw->size & 0xff; - r = getdns_dict_get_dict(rr_dict, "rdata", &rdata); - if (r != GETDNS_RETURN_GOOD) - break; + (void) memcpy(wire + 2, rdata_raw->data, rdata_raw->size); - r = priv_getdns_dict_get_raw_rdata(rdata, &wire, &wire_size); - if (r == GETDNS_RETURN_NO_SUCH_DICT_NAME) { - r = priv_getdns_construct_wire_rdata_from_rdata( - rdata, rr_type, &wire, &wire_size); - } - if (r != GETDNS_RETURN_GOOD) - break; - pos = 0; - s = ldns_wire2rdf(*rr, wire, wire_size, &pos); - GETDNS_FREE(rr_dict->mf, wire); - if (s == LDNS_STATUS_OK) - return r; - r = GETDNS_RETURN_GENERIC_ERROR; - } while (0); + } else if (n_rdata_fields || r == GETDNS_RETURN_NO_SUCH_DICT_NAME) { + + r = priv_getdns_construct_wire_rdata_from_rdata(rdata, rr_type, + &wire, &wire_size); + } + + pos = 0; + s = ldns_wire2rdf(*rr, wire, wire_size, &pos); + GETDNS_FREE(rr_dict->mf, wire); + if (s == LDNS_STATUS_OK) + return r; + + r = GETDNS_RETURN_GENERIC_ERROR; +error: ldns_rr_free(*rr); return r; } diff --git a/src/rr-dict.h b/src/rr-dict.h index 244f556b..cff4215a 100644 --- a/src/rr-dict.h +++ b/src/rr-dict.h @@ -51,56 +51,57 @@ typedef struct priv_getdns_rdf_special { /* draft-levine-dnsextlang'ish type rr and rdata definitions */ -#define GETDNS_RDF_INTEGER 0x010000 -#define GETDNS_RDF_BINDATA 0x020000 -#define GETDNS_RDF_DNAME 0x060000 -#define GETDNS_RDF_REPEAT 0x100000 +#define GETDNS_RDF_INTEGER 0x010000 +#define GETDNS_RDF_BINDATA 0x020000 +#define GETDNS_RDF_DNAME 0x060000 +#define GETDNS_RDF_COMPRESSED 0x080000 +#define GETDNS_RDF_REPEAT 0x100000 -#define GETDNS_RDF_FIXEDSZ 0x0000FF -#define GETDNS_RDF_LEN_VAL 0x00FF00 +#define GETDNS_RDF_FIXEDSZ 0x0000FF +#define GETDNS_RDF_LEN_VAL 0x00FF00 typedef enum priv_getdns_rdf_wf_type { - GETDNS_RDF_N = 0x060000, /* N */ - GETDNS_RDF_N_A = GETDNS_RDF_N, /* N[A] */ - GETDNS_RDF_N_A_C = GETDNS_RDF_N, /* N[A,C] */ - GETDNS_RDF_N_C = GETDNS_RDF_N, /* N[C] */ - GETDNS_RDF_N_M = 0x160000, /* N[M] */ + GETDNS_RDF_N = 0x060000, /* N */ + GETDNS_RDF_N_A = 0x060000, /* N[A] */ + GETDNS_RDF_N_C = 0x0E0000, /* N[C] */ + GETDNS_RDF_N_A_C = 0x0E0000, /* N[A,C] */ + GETDNS_RDF_N_M = 0x160000, /* N[M] */ - GETDNS_RDF_I1 = 0x010001, /* I1 */ - GETDNS_RDF_I2 = 0x010002, /* I2 */ - GETDNS_RDF_I4 = 0x010004, /* I4 */ + GETDNS_RDF_I1 = 0x010001, /* I1 */ + GETDNS_RDF_I2 = 0x010002, /* I2 */ + GETDNS_RDF_I4 = 0x010004, /* I4 */ - GETDNS_RDF_T = 0x010004, /* T */ + GETDNS_RDF_T = 0x010004, /* T */ /* Time values using ring arithmetics * (rfc1982) for TKEY['inception'], * TKEY['expiration'], * RRSIG['inception'] and * RRSIG['expiration'] */ - GETDNS_RDF_T6 = 0x020006, /* T6 */ + GETDNS_RDF_T6 = 0x020006, /* T6 */ /* Absolute time values (since epoch) * for TSIG['time_signed'] */ - GETDNS_RDF_A = 0x020004, /* A */ - GETDNS_RDF_AA = 0x020008, /* AA */ - GETDNS_RDF_AAAA = 0x020010, /* AAAA */ + GETDNS_RDF_A = 0x020004, /* A */ + GETDNS_RDF_AA = 0x020008, /* AA */ + GETDNS_RDF_AAAA = 0x020010, /* AAAA */ - GETDNS_RDF_S = 0x020100, /* S */ - GETDNS_RDF_S_L = 0x020000, /* S[L] */ - GETDNS_RDF_S_M = 0x120100, /* S[M] */ + GETDNS_RDF_S = 0x020100, /* S */ + GETDNS_RDF_S_L = 0x020000, /* S[L] */ + GETDNS_RDF_S_M = 0x120100, /* S[M] */ - GETDNS_RDF_B = 0x020000, /* B */ - GETDNS_RDF_B_C = 0x020100, /* B[C] */ + GETDNS_RDF_B = 0x020000, /* B */ + GETDNS_RDF_B_C = 0x020100, /* B[C] */ - GETDNS_RDF_B32_C = 0x020100, /* B32[C] */ + GETDNS_RDF_B32_C = 0x020100, /* B32[C] */ - GETDNS_RDF_X = 0x020000, /* X */ - GETDNS_RDF_X_C = 0x020100, /* X[C] */ + GETDNS_RDF_X = 0x020000, /* X */ + GETDNS_RDF_X_C = 0x020100, /* X[C] */ /* for NSEC3['salt'] and * NSEC3PARAM['salt']. */ - GETDNS_RDF_X_S = 0x020200, /* X[S] */ + GETDNS_RDF_X_S = 0x020200, /* X[S] */ /* for OPT['option_data'], * TKEY['key_data'], * TKEY['other_data'], @@ -109,12 +110,12 @@ typedef enum priv_getdns_rdf_wf_type { * Although those do not have an * official presentation format. */ - GETDNS_RDF_X6 = 0x020006, - GETDNS_RDF_X8 = 0x020008, + GETDNS_RDF_X6 = 0x020006, + GETDNS_RDF_X8 = 0x020008, - GETDNS_RDF_R = 0x100000, /* Repeat */ + GETDNS_RDF_R = 0x100000, /* Repeat */ - GETDNS_RDF_SPECIAL = 0x800000, + GETDNS_RDF_SPECIAL = 0x800000, } priv_getdns_rdf_type; typedef struct priv_getdns_rdata_def { From 4f78dbfe7d0e84cecc9225ea5dd73abb9688378f Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Thu, 11 Jun 2015 15:47:39 +0200 Subject: [PATCH 02/28] fix --disable-native-stub-dnssec option --- configure | 6 +++--- configure.ac | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/configure b/configure index d197b17b..b9b00f3e 100755 --- a/configure +++ b/configure @@ -11742,15 +11742,15 @@ if test "${enable_native_stub_dnssec+set}" = set; then : enableval=$enable_native_stub_dnssec; fi -case "$enable_broken_native_stub_dnssec" in +case "$enable_native_stub_dnssec" in + no) + ;; yes|*) cat >>confdefs.h <<_ACEOF #define STUB_NATIVE_DNSSEC 1 _ACEOF - ;; - no) ;; esac diff --git a/configure.ac b/configure.ac index f4811cc9..a629fc13 100644 --- a/configure.ac +++ b/configure.ac @@ -133,12 +133,12 @@ case "$enable_tcp_fastopen" in esac AC_ARG_ENABLE(native-stub-dnssec, AC_HELP_STRING([--disable-native-stub-dnssec], [Disable native stub DNSSEC support])) -case "$enable_broken_native_stub_dnssec" in +case "$enable_native_stub_dnssec" in + no) + ;; yes|*) AC_DEFINE_UNQUOTED([STUB_NATIVE_DNSSEC], [1], [Define this to enable native stub DNSSEC support.]) ;; - no) - ;; esac ACX_WITH_SSL_OPTIONAL From 97f0dddb1e92ab09cf1f683f6879c9ca457af5b6 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 12 Jun 2015 13:51:36 +0200 Subject: [PATCH 03/28] remove ldns dependency from rr-dict.c Only dnssec.c left --- src/context.c | 1 + src/dnssec.c | 33 ++++++++- src/rr-dict.c | 170 +++++++++++++------------------------------- src/rr-dict.h | 7 +- src/stub.c | 2 +- src/util-internal.c | 1 + 6 files changed, 90 insertions(+), 124 deletions(-) diff --git a/src/context.c b/src/context.c index 3558e13d..034f3ca6 100644 --- a/src/context.c +++ b/src/context.c @@ -43,6 +43,7 @@ #include #include #include +#include #include "config.h" #include "gldns/str2wire.h" diff --git a/src/dnssec.c b/src/dnssec.c index 486cf27a..bfeee146 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -50,6 +50,7 @@ #include "gldns/str2wire.h" #include "gldns/wire2str.h" #include "general.h" +#include "dict.h" void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); @@ -444,7 +445,37 @@ void priv_getdns_get_validation_chain(getdns_dns_req *dns_req) /********************** functions for validate_dnssec *************************/ static getdns_return_t -priv_getdns_rr_list_from_list(struct getdns_list *list, ldns_rr_list **rr_list) +priv_getdns_create_rr_from_dict(getdns_dict *rr_dict, ldns_rr **rr) +{ + gldns_buffer buf; + uint8_t space[8192], *xspace = NULL; + size_t xsize, pos = 0; + ldns_status s; + getdns_return_t r; + + gldns_buffer_init_frm_data(&buf, space, sizeof(space)); + if ((r = priv_getdns_rr_dict2wire(rr_dict, &buf))) + return r; + + if ((xsize = gldns_buffer_position(&buf)) > sizeof(space)) { + if (!(xspace = GETDNS_XMALLOC(rr_dict->mf, uint8_t, xsize))) + return GETDNS_RETURN_MEMORY_ERROR; + + gldns_buffer_init_frm_data(&buf, xspace, xsize); + if ((r = priv_getdns_rr_dict2wire(rr_dict, &buf))) { + GETDNS_FREE(rr_dict->mf, xspace); + return r; + } + } + s = ldns_wire2rr(rr, gldns_buffer_begin(&buf), + gldns_buffer_position(&buf), &pos, GLDNS_SECTION_ANSWER); + if (xspace) + GETDNS_FREE(rr_dict->mf, xspace); + return s ? GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; +} + +static getdns_return_t +priv_getdns_rr_list_from_list(getdns_list *list, ldns_rr_list **rr_list) { getdns_return_t r; size_t i, l; diff --git a/src/rr-dict.c b/src/rr-dict.c index 85d779d8..105c550c 100644 --- a/src/rr-dict.c +++ b/src/rr-dict.c @@ -737,158 +737,90 @@ priv_getdns_rr_type_name(int rr_type) return priv_getdns_rr_def_lookup(rr_type)->name; } -static getdns_return_t priv_getdns_construct_wire_rdata_from_rdata( - struct getdns_dict *rdata, uint32_t rr_type, - uint8_t **wire, size_t *wire_size) -{ - getdns_return_t r = GETDNS_RETURN_GOOD; - const priv_getdns_rr_def *def; - size_t i, j, size; - struct getdns_bindata *bindata; - uint32_t value; - uint8_t *ptr; - - assert(rdata); - assert(wire); - assert(wire_size); - - def = priv_getdns_rr_def_lookup(rr_type); - - /* First calculate needed size */ - size = 0; - for (i = 0; !r && i < def->n_rdata_fields; i++) { - if (def->rdata[i].type & GETDNS_RDF_BINDATA) - if ((r = getdns_dict_get_bindata(rdata, - def->rdata[i].name, &bindata))) - break; - else { - size += bindata->size; - continue; - } - else if (!(def->rdata[i].type & GETDNS_RDF_INTEGER)) { - r = GETDNS_RETURN_GENERIC_ERROR; - break; - } - size += def->rdata[i].type & GETDNS_RDF_FIXEDSZ; - } - *wire_size = size + 2; - *wire = ptr = GETDNS_XMALLOC(rdata->mf, uint8_t, size + 2); - if (! ptr) - return GETDNS_RETURN_MEMORY_ERROR; - - ptr[0] = (uint8_t) (size >> 8) & 0xff; - ptr[1] = (uint8_t) size & 0xff; - ptr += 2; - for (i = 0; !r && i < def->n_rdata_fields; i++) { - if (def->rdata[i].type & GETDNS_RDF_BINDATA) - if ((r = getdns_dict_get_bindata(rdata, - def->rdata[i].name, &bindata))) - break; - else { - (void) memcpy(ptr, bindata->data, - bindata->size); - ptr += bindata->size; - continue; - } - else if (!(def->rdata[i].type & GETDNS_RDF_INTEGER)) { - r = GETDNS_RETURN_GENERIC_ERROR; - break; - } - if ((r = getdns_dict_get_int( - rdata, def->rdata[i].name, &value))) - break; - - for (j = def->rdata[i].type & GETDNS_RDF_FIXEDSZ; j; j--) - *ptr++ = (uint8_t)(value >> (8 * (j - 1))) & 0xff; - } - if (r) - GETDNS_FREE(rdata->mf, ptr); - return r; -} - getdns_return_t -priv_getdns_create_rr_from_dict(struct getdns_dict *rr_dict, ldns_rr **rr) +priv_getdns_rr_dict2wire(getdns_dict *rr_dict, gldns_buffer *buf) { getdns_return_t r = GETDNS_RETURN_GOOD; struct getdns_bindata *name; struct getdns_bindata *rdata_raw; + struct getdns_bindata *bindata; struct getdns_dict *rdata; uint32_t rr_type; - ldns_rdf *owner; - ldns_status s; - size_t pos; - uint8_t *wire; - size_t wire_size; + uint32_t rr_class = GETDNS_RRCLASS_IN; + uint32_t rr_ttl = 0; + uint32_t value; + const priv_getdns_rr_def *rr_def; + const priv_getdns_rdata_def *rd_def; + int n_rdata_fields; + size_t j; assert(rr_dict); - assert(rr); - - *rr = ldns_rr_new(); - if (! *rr) - return GETDNS_RETURN_MEMORY_ERROR; + assert(buf); if ((r = getdns_dict_get_bindata(rr_dict, "name", &name))) goto error; - - owner = ldns_rdf_new_frm_data( - LDNS_RDF_TYPE_DNAME, name->size, name->data); - if (! owner) { - r = GETDNS_RETURN_MEMORY_ERROR; - goto error; - } - ldns_rr_set_owner(*rr, owner); + gldns_buffer_write(buf, name->data, name->size); if ((r = getdns_dict_get_int(rr_dict, "type", &rr_type))) goto error; - ldns_rr_set_type(*rr, rr_type); + gldns_buffer_write_u16(buf, (uint16_t)rr_type); - if ((r = getdns_dict_get_dict(rr_dict, "rdata", &rdata))) - goto error; + (void) getdns_dict_get_int(rr_dict, "class", &rr_class); + gldns_buffer_write_u16(buf, (uint16_t)rr_class); - const priv_getdns_rr_def *rr_def = priv_getdns_rr_def_lookup(rr_type); - const priv_getdns_rdata_def *rd_def; - int n_rdata_fields; + (void) getdns_dict_get_int(rr_dict, "ttl", &rr_ttl); + gldns_buffer_write_u32(buf, rr_ttl); + /* Does rdata contain compressed names? + * Because rdata_raw is unusable then. + */ + rr_def = priv_getdns_rr_def_lookup(rr_type); for ( rd_def = rr_def->rdata , n_rdata_fields = rr_def->n_rdata_fields - ; n_rdata_fields - ; n_rdata_fields-- - , rd_def++ ) { + ; n_rdata_fields ; n_rdata_fields-- , rd_def++ ) { if (rd_def->type & GETDNS_RDF_COMPRESSED) break; } + if ((r = getdns_dict_get_dict(rr_dict, "rdata", &rdata))) + goto error; + if (n_rdata_fields == 0 && GETDNS_RETURN_GOOD == (r = getdns_dict_get_bindata(rdata, "rdata_raw", &rdata_raw))) { - wire_size = rdata_raw->size + 2; - wire = GETDNS_XMALLOC(rdata->mf, uint8_t, wire_size); - if (! wire) { - r = GETDNS_RETURN_MEMORY_ERROR; - goto error; - } - - wire[0] = (uint8_t) (rdata_raw->size >> 8) & 0xff; - wire[1] = (uint8_t) rdata_raw->size & 0xff; - - (void) memcpy(wire + 2, rdata_raw->data, rdata_raw->size); + gldns_buffer_write_u16(buf, (uint16_t)rdata_raw->size); + gldns_buffer_write(buf, rdata_raw->data, rdata_raw->size); } else if (n_rdata_fields || r == GETDNS_RETURN_NO_SUCH_DICT_NAME) { - r = priv_getdns_construct_wire_rdata_from_rdata(rdata, rr_type, - &wire, &wire_size); + for ( rd_def = rr_def->rdata + , n_rdata_fields = rr_def->n_rdata_fields + ; n_rdata_fields ; n_rdata_fields-- , rd_def++ ) { + + if (rd_def->type & GETDNS_RDF_BINDATA) { + if ((r = getdns_dict_get_bindata(rdata, + rd_def->name, &bindata))) + break; + + gldns_buffer_write(buf, bindata->data + , bindata->size ); + continue; + } + if (!(rd_def->type & GETDNS_RDF_INTEGER)) { + r = GETDNS_RETURN_GENERIC_ERROR; + break; + } + if ((r = getdns_dict_get_int( + rdata, rd_def->name, &value))) + break; + + for (j = rd_def->type & GETDNS_RDF_FIXEDSZ; j; j--) + gldns_buffer_write_u8(buf, + (uint8_t)(value >> (8 * (j - 1))) & 0xff); + } } - - pos = 0; - s = ldns_wire2rdf(*rr, wire, wire_size, &pos); - GETDNS_FREE(rr_dict->mf, wire); - if (s == LDNS_STATUS_OK) - return r; - - r = GETDNS_RETURN_GENERIC_ERROR; error: - ldns_rr_free(*rr); return r; } diff --git a/src/rr-dict.h b/src/rr-dict.h index cff4215a..9228105e 100644 --- a/src/rr-dict.h +++ b/src/rr-dict.h @@ -32,8 +32,9 @@ #ifndef RR_DICT_H_ #define RR_DICT_H_ -#include +#include "config.h" #include "getdns/getdns.h" +#include "gldns/gbuffer.h" typedef uint8_t *(*priv_getdns_rdf_end_t)( uint8_t *pkt, uint8_t *pkt_end, uint8_t *rdf); @@ -132,8 +133,8 @@ typedef struct priv_getdns_rr_def { const priv_getdns_rr_def *priv_getdns_rr_def_lookup(uint16_t rr_type); -getdns_return_t priv_getdns_create_rr_from_dict( - struct getdns_dict *rr_dict, ldns_rr **rr); +getdns_return_t priv_getdns_rr_dict2wire( + getdns_dict *rr_dict, gldns_buffer *buf); const char *priv_getdns_rr_type_name(int rr_type); diff --git a/src/stub.c b/src/stub.c index c671b81d..63712387 100644 --- a/src/stub.c +++ b/src/stub.c @@ -31,6 +31,7 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include #include "config.h" #include #include "stub.h" @@ -40,7 +41,6 @@ #include "gldns/str2wire.h" #include "rr-iter.h" #include "context.h" -#include #include "util-internal.h" #include "general.h" diff --git a/src/util-internal.c b/src/util-internal.c index 9a6f35c7..f125090f 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -37,6 +37,7 @@ #include #include +#include #include #include "getdns/getdns.h" #include "dict.h" From 731cc37434b9950ac43babb3a8f3aefd0c564bc8 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 12 Jun 2015 15:45:37 +0200 Subject: [PATCH 04/28] Another redundant ldns reference --- src/general.c | 1 - 1 file changed, 1 deletion(-) diff --git a/src/general.c b/src/general.c index 6dd0a779..dc5ba225 100644 --- a/src/general.c +++ b/src/general.c @@ -38,7 +38,6 @@ #include #include #include -#include #include "config.h" #include "context.h" #include "types-internal.h" From 4445a5f9cc2c17f7e31066bc5bb2efe2e75ca9f6 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 12 Jun 2015 15:45:50 +0200 Subject: [PATCH 05/28] Include rdata size with compressed names --- src/rr-dict.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/rr-dict.c b/src/rr-dict.c index 105c550c..8a9f2f94 100644 --- a/src/rr-dict.c +++ b/src/rr-dict.c @@ -752,7 +752,7 @@ priv_getdns_rr_dict2wire(getdns_dict *rr_dict, gldns_buffer *buf) const priv_getdns_rr_def *rr_def; const priv_getdns_rdata_def *rd_def; int n_rdata_fields; - size_t j; + size_t j, rdata_size_mark; assert(rr_dict); assert(buf); @@ -794,6 +794,9 @@ priv_getdns_rr_dict2wire(getdns_dict *rr_dict, gldns_buffer *buf) } else if (n_rdata_fields || r == GETDNS_RETURN_NO_SUCH_DICT_NAME) { + rdata_size_mark = gldns_buffer_position(buf); + gldns_buffer_skip(buf, 2); + for ( rd_def = rr_def->rdata , n_rdata_fields = rr_def->n_rdata_fields ; n_rdata_fields ; n_rdata_fields-- , rd_def++ ) { @@ -819,6 +822,8 @@ priv_getdns_rr_dict2wire(getdns_dict *rr_dict, gldns_buffer *buf) gldns_buffer_write_u8(buf, (uint8_t)(value >> (8 * (j - 1))) & 0xff); } + gldns_buffer_write_u16_at(buf, rdata_size_mark, + (uint16_t)(gldns_buffer_position(buf)-rdata_size_mark-2)); } error: return r; From 39639a86c4094d6193df847ab9d867bdd165f28c Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 16 Jun 2015 16:11:51 +0200 Subject: [PATCH 06/28] Make dname_equal reusable + some symbol renames --- src/general.c | 4 ++-- src/stub.c | 19 ++----------------- src/util-internal.c | 16 ++++++++-------- src/util-internal.h | 8 ++++++-- 4 files changed, 18 insertions(+), 29 deletions(-) diff --git a/src/general.c b/src/general.c index dc5ba225..834785c3 100644 --- a/src/general.c +++ b/src/general.c @@ -184,10 +184,10 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, if (!context || !name || (!callbackfn && !internal_cb)) return GETDNS_RETURN_INVALID_PARAMETER; - if ((r = validate_dname(name))) + if ((r = priv_getdns_validate_dname(name))) return r; - if (extensions && (r = validate_extensions(extensions))) + if (extensions && (r = priv_getdns_validate_extensions(extensions))) return r; /* Set up the context assuming we won't use the specified namespaces. diff --git a/src/stub.c b/src/stub.c index 63712387..67aa4e82 100644 --- a/src/stub.c +++ b/src/stub.c @@ -265,21 +265,6 @@ create_starttls_request(getdns_dns_req *dnsreq, getdns_upstream *upstream, return 1; } -static int -dname_equal(uint8_t *s1, uint8_t *s2) -{ - uint8_t i; - for (;;) { - if (*s1 != *s2) - return 0; - else if (!*s1) - return 1; - for (i = *s1++, s2++; i > 0; i--, s1++, s2++) - if ((*s1 & 0xDF) != (*s2 & 0xDF)) - return 0; - } -} - static int is_starttls_response(getdns_network_req *netreq) { @@ -315,7 +300,7 @@ is_starttls_response(getdns_network_req *netreq) owner_name = priv_getdns_owner_if_or_as_decompressed( rr_iter, owner_name_space, &owner_name_len); - if (!dname_equal(starttls_name, owner_name)) + if (!priv_getdns_dname_equal(starttls_name, owner_name)) continue; if (!(rdf_iter = priv_getdns_rdf_iter_init( @@ -324,7 +309,7 @@ is_starttls_response(getdns_network_req *netreq) /* re-use the starttls_name for the response dname*/ starttls_name = priv_getdns_rdf_if_or_as_decompressed( rdf_iter,starttls_name_space,&starttls_name_len); - if (dname_equal(starttls_name, owner_name)) + if (priv_getdns_dname_equal(starttls_name, owner_name)) return 1; else return 0; diff --git a/src/util-internal.c b/src/util-internal.c index f125090f..99dae91d 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -423,8 +423,8 @@ error: return NULL; } -static int -dname_equal(uint8_t *s1, uint8_t *s2) +int +priv_getdns_dname_equal(uint8_t *s1, uint8_t *s2) { uint8_t i; for (;;) { @@ -581,7 +581,7 @@ priv_getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, owner_name = priv_getdns_owner_if_or_as_decompressed( rr_iter, owner_name_space, &owner_name_len); - if (!dname_equal(canonical_name, owner_name)) + if (!priv_getdns_dname_equal(canonical_name, owner_name)) continue; if (!(rdf_iter = priv_getdns_rdf_iter_init( @@ -654,7 +654,7 @@ priv_getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, owner_name = priv_getdns_owner_if_or_as_decompressed( rr_iter, owner_name_space, &owner_name_len); - if (!dname_equal(canonical_name, owner_name)) + if (!priv_getdns_dname_equal(canonical_name, owner_name)) continue; if (!(rdf_iter = priv_getdns_rdf_iter_init( @@ -831,7 +831,7 @@ extformatcmp(const void *a, const void *b) /*---------------------------------------- validate_extensions */ getdns_return_t -validate_extensions(struct getdns_dict * extensions) +priv_getdns_validate_extensions(struct getdns_dict * extensions) { struct getdns_dict_item *item; getdns_extension_format *extformat; @@ -853,7 +853,7 @@ validate_extensions(struct getdns_dict * extensions) return GETDNS_RETURN_EXTENSION_MISFORMAT; } return GETDNS_RETURN_GOOD; -} /* validate_extensions */ +} /* priv_getdns_validate_extensions */ getdns_return_t getdns_apply_network_result(getdns_network_req* netreq, @@ -918,7 +918,7 @@ getdns_apply_network_result(getdns_network_req* netreq, getdns_return_t -validate_dname(const char* dname) { +priv_getdns_validate_dname(const char* dname) { int len; int label_len; const char* s; @@ -981,7 +981,7 @@ validate_dname(const char* dname) { return GETDNS_RETURN_BAD_DOMAIN_NAME; } return GETDNS_RETURN_GOOD; -} /* validate_dname */ +} /* priv_getdns_validate_dname */ /* util-internal.c */ diff --git a/src/util-internal.h b/src/util-internal.h index c58abd31..0c2b196e 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -130,7 +130,11 @@ struct getdns_dict *create_getdns_response(struct getdns_dns_req *completed_requ getdns_dict *priv_getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, getdns_list *just_addrs, int *rrsigs_in_answer); -getdns_return_t validate_dname(const char* dname); +getdns_return_t priv_getdns_validate_dname(const char* dname); + +int priv_getdns_dname_equal(uint8_t *s1, uint8_t *s2); + + /** * detect unrecognized extension strings or invalid extension formats @@ -140,7 +144,7 @@ getdns_return_t validate_dname(const char* dname); * @return GETDNS_RETURN_NO_SUCH_EXTENSION A name in the extensions dict is not a valid extension. * @return GETDNS_RETURN_EXTENSION_MISFORMAT One or more of the extensions has a bad format. */ -getdns_return_t validate_extensions(struct getdns_dict * extensions); +getdns_return_t priv_getdns_validate_extensions(struct getdns_dict * extensions); #define DEBUG_ON(...) do { \ struct timeval tv; \ From 129e340e8e602972a67b528ddeadff0c43f19540 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 17 Jun 2015 14:46:44 +0200 Subject: [PATCH 07/28] Collect validation chains for RRs without sigs --- src/dnssec.c | 224 +++++++++++++++++++++++++++++++++++++++----- src/util-internal.c | 2 +- src/util-internal.h | 2 +- 3 files changed, 204 insertions(+), 24 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index bfeee146..2d3f4b8a 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -76,7 +76,7 @@ struct chain_link { }; static void launch_chain_link_lookup(struct validation_chain *chain, - priv_getdns_rdf_iter *rdf_dname); + uint8_t *dname); static void destroy_chain(struct validation_chain *chain); #ifdef STUB_NATIVE_DNSSEC @@ -170,10 +170,11 @@ static void callback_on_complete_chain(struct validation_chain *chain) } RBTREE_FOR(link, struct chain_link *, (getdns_rbtree_t *)&(chain->root)) { - for (i = 0; !getdns_list_get_dict( link->DNSKEY.result + for (i = 0; !getdns_list_get_dict( link->DS.result , i, &rr_dict); i++) (void) getdns_list_append_dict(keys, rr_dict); - for (i = 0; !getdns_list_get_dict( link->DS.result + + for (i = 0; !getdns_list_get_dict( link->DNSKEY.result , i, &rr_dict); i++) (void) getdns_list_append_dict(keys, rr_dict); } @@ -205,6 +206,8 @@ chain_response_callback(struct getdns_dns_req *dns_req) getdns_list *keys; size_t nkeys; getdns_return_t r; + uint8_t sign_name_space[256], *sign_name; + size_t sign_name_len = sizeof(sign_name_space); response->dns_req = dns_req; if (!(keys = getdns_list_create_with_context(context))) @@ -218,13 +221,16 @@ chain_response_callback(struct getdns_dns_req *dns_req) ; rr_iter = priv_getdns_rr_iter_next(rr_iter) ) { section = priv_getdns_rr_iter_section(rr_iter); - if (section != GLDNS_SECTION_ANSWER) + if (section != GLDNS_SECTION_ANSWER && + section != GLDNS_SECTION_AUTHORITY) continue; rr_type = gldns_read_uint16(rr_iter->rr_type); if (rr_type == GETDNS_RRTYPE_DS || - rr_type == GETDNS_RRTYPE_DNSKEY) { + rr_type == GETDNS_RRTYPE_DNSKEY || + rr_type == GETDNS_RRTYPE_NSEC || + rr_type == GETDNS_RRTYPE_NSEC3) { if (!(rr_dict = priv_getdns_rr_iter2rr_dict( context, rr_iter))) continue; @@ -240,11 +246,17 @@ chain_response_callback(struct getdns_dns_req *dns_req) continue; type_covered = gldns_read_uint16(rdf->pos); - if (type_covered == GETDNS_RRTYPE_DS) { + if (type_covered == GETDNS_RRTYPE_DS || + type_covered == GETDNS_RRTYPE_NSEC || + type_covered == GETDNS_RRTYPE_NSEC3) { + if ((rdf = priv_getdns_rdf_iter_init_at( - &rdf_storage, rr_iter, 7))) + &rdf_storage, rr_iter, 7)) && + (sign_name = priv_getdns_rdf_if_or_as_decompressed( + rdf, sign_name_space, &sign_name_len))) + launch_chain_link_lookup( - response->chain, rdf); + response->chain, sign_name); } else if (type_covered != GETDNS_RRTYPE_DNSKEY) continue; @@ -302,23 +314,86 @@ resolve(char* name, int rrtype, struct chain_response *response) return r; } +static void +find_delegation_point_callback(struct getdns_dns_req *dns_req) +{ + struct validation_chain *chain = + (struct validation_chain *) dns_req->user_pointer; + getdns_context *context = dns_req->context; + getdns_network_req **netreq_p, *netreq; + priv_getdns_rr_iter rr_iter_storage, *rr_iter; + priv_getdns_rdf_iter rdf_storage, *rdf; + gldns_pkt_section section; + uint16_t rr_type; + uint8_t rr_name_space[256], *rr_name; + size_t rr_name_len = sizeof(rr_name_space); + + for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) { + for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage + , netreq->response + , netreq->response_len) + ; rr_iter + ; rr_iter = priv_getdns_rr_iter_next(rr_iter) + ) { + section = priv_getdns_rr_iter_section(rr_iter); + if (section != GLDNS_SECTION_ANSWER && + section != GLDNS_SECTION_AUTHORITY) + continue; + + rr_type = gldns_read_uint16(rr_iter->rr_type); + if (rr_type != GETDNS_RRTYPE_SOA) + continue; + + if (!(rr_name = priv_getdns_owner_if_or_as_decompressed( + rr_iter, rr_name_space, &rr_name_len))) + continue; + + launch_chain_link_lookup(chain, rr_name); + } + } + chain->lock--; + getdns_context_clear_outbound_request(dns_req); + dns_req_free(dns_req); + callback_on_complete_chain(chain); +} + +static int +find_delegation_point(struct validation_chain *chain, uint8_t *dname) +{ + getdns_return_t r; + getdns_dict *extensions; + char name[1024]; + + if (!gldns_wire2str_dname_buf(dname, 256, name, sizeof(name))) + return GETDNS_RETURN_GENERIC_ERROR; + + if (!(extensions = getdns_dict_create_with_context( + chain->dns_req->context))) + return GETDNS_RETURN_MEMORY_ERROR; + + chain->lock++; + if (!(r = getdns_dict_set_int(extensions, + "dnssec_ok_checking_disabled", GETDNS_EXTENSION_TRUE))) + + r = priv_getdns_general_loop(chain->dns_req->context, + chain->dns_req->loop, name, GETDNS_RRTYPE_SOA, extensions, + chain, NULL, NULL, find_delegation_point_callback); + + getdns_dict_destroy(extensions); + if (r) + chain->lock--; + return r; +} + static void launch_chain_link_lookup( - struct validation_chain *chain, priv_getdns_rdf_iter *rdf_dname) + struct validation_chain *chain, uint8_t *dname) { int r; struct chain_link *link; - uint8_t dname_spc[256], *dname; char name[1024]; - size_t dname_spc_sz = sizeof(dname_spc); - if (!(dname = priv_getdns_rdf_if_or_as_decompressed( - rdf_dname, dname_spc, &dname_spc_sz))) - return; - - if (!gldns_wire2str_dname_buf(dname, (dname == dname_spc ? - sizeof(dname_spc) : rdf_dname->nxt - rdf_dname->pos), - name, sizeof(name))) + if (!gldns_wire2str_dname_buf(dname, 256, name, sizeof(name))) return; if ((link = (struct chain_link *) @@ -393,6 +468,18 @@ static void destroy_chain(struct validation_chain *chain) GETDNS_FREE(chain->mf, chain); } + +static int priv_getdns_dname_is_subdomain( + const uint8_t *subdomain, const uint8_t *domain) +{ + while (*domain) { + if (priv_getdns_dname_equal(subdomain, domain)) + return 1; + + domain += *domain + 1; + } + return *subdomain == 0; +} /* Do some additional requests to fetch the complete validation chain */ static void getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) @@ -403,12 +490,21 @@ getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) priv_getdns_rdf_iter rdf_storage, *rdf; gldns_pkt_section section; uint16_t rr_type; + priv_getdns_rr_iter rrsig_iter_storage, *rrsig_iter; + uint8_t rr_name_space[256], *rr_name; + uint8_t rrsig_name_space[256], *rrsig_name; + uint8_t sign_name_space[256], *sign_name; + size_t rr_name_len = sizeof(rr_name_space); + size_t rrsig_name_len = sizeof(rrsig_name_space); + size_t sign_name_len = sizeof(sign_name_space); + int rrsigs_found; if (! chain) { priv_getdns_call_user_callback( dns_req, create_getdns_response(dns_req)); return; } + chain->lock++; for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) { for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage @@ -422,17 +518,66 @@ getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) section != GLDNS_SECTION_AUTHORITY) continue; + /* Skip RRSIGs because we do only lookups for RRSIGS + * that have an rrset in the record too. + */ rr_type = gldns_read_uint16(rr_iter->rr_type); - if (rr_type != GETDNS_RRTYPE_RRSIG) + if (rr_type == GETDNS_RRTYPE_RRSIG) continue; - if (!(rdf = priv_getdns_rdf_iter_init_at( - &rdf_storage , rr_iter, 7))) + if (!(rr_name = priv_getdns_owner_if_or_as_decompressed( + rr_iter, rr_name_space, &rr_name_len))) continue; - launch_chain_link_lookup(chain, rdf); + rrsigs_found = 0; + for ( rrsig_iter = priv_getdns_rr_iter_init(&rrsig_iter_storage + , netreq->response + , netreq->response_len ) + ; rrsig_iter + ; rrsig_iter = priv_getdns_rr_iter_next(rrsig_iter) + ) { + section = priv_getdns_rr_iter_section(rrsig_iter); + if (section != GLDNS_SECTION_ANSWER && + section != GLDNS_SECTION_AUTHORITY) + continue; + + if (GETDNS_RRTYPE_RRSIG != + gldns_read_uint16(rrsig_iter->rr_type)) + continue; + + rdf = priv_getdns_rdf_iter_init(&rdf_storage + , rrsig_iter); + if (!rdf || gldns_read_uint16(rdf->pos) != rr_type) + continue; + + if (!(rrsig_name = priv_getdns_owner_if_or_as_decompressed( + rrsig_iter, rrsig_name_space, &rrsig_name_len))) + continue; + + if (!priv_getdns_dname_equal(rr_name, rrsig_name)) + continue; + + if (!(rdf = priv_getdns_rdf_iter_init_at( + &rdf_storage , rrsig_iter, 7))) + continue; + + if (!(sign_name = priv_getdns_rdf_if_or_as_decompressed( + rdf, sign_name_space, &sign_name_len))) + continue; + + if (!priv_getdns_dname_is_subdomain(sign_name, rr_name)) + continue; + + rrsigs_found++; + launch_chain_link_lookup(chain, sign_name); + } + if (rrsigs_found) + continue; + + find_delegation_point(chain, rr_name); } } + chain->lock--; callback_on_complete_chain(chain); } @@ -789,6 +934,41 @@ done_free_verifying_keys: return s; } +typedef struct getdns_rrset getdns_rrset; +struct getdns_rrset { + getdns_bindata *name; + uint16_t rr_type; + uint16_t rr_class; + uint32_t ttl; + getdns_list *rrs; + getdns_list *sigs; +}; + +typedef struct getdns_delpath_elem getdns_delpath_elem; +struct getdns_delpath_elem { + getdns_delpath_elem *e; /* self or canonical element + * (with overlapping paths) + */ + unsigned int is_zonecut : 1; + unsigned int ds_nxdomain: 1; + getdns_rrset ds; + getdns_rrset dnskey; + getdns_dict *ds_nsec; + getdns_list *ds_nsec_sigs; + getdns_dict *ds_nsec_wc; /* wildcard */ + getdns_list *ds_nsec_wc_sigs; + getdns_dict *ds_nsec_ce; /* closest encloser */ + getdns_list *ds_nsec_ce_sigs; +}; + +typedef struct getdns_delpath getdns_delpath; +struct getdns_delpath { + getdns_delpath *next; /* In a ring */ + getdns_rrset rrset; + size_t nlabels; + getdns_delpath_elem elems[]; +}; + /* * getdns_validate_dnssec * diff --git a/src/util-internal.c b/src/util-internal.c index 99dae91d..d5a3f4ae 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -424,7 +424,7 @@ error: } int -priv_getdns_dname_equal(uint8_t *s1, uint8_t *s2) +priv_getdns_dname_equal(const uint8_t *s1, const uint8_t *s2) { uint8_t i; for (;;) { diff --git a/src/util-internal.h b/src/util-internal.h index 0c2b196e..1c3adb55 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -132,7 +132,7 @@ getdns_dict *priv_getdns_create_reply_dict(getdns_context *context, getdns_return_t priv_getdns_validate_dname(const char* dname); -int priv_getdns_dname_equal(uint8_t *s1, uint8_t *s2); +int priv_getdns_dname_equal(const uint8_t *s1, const uint8_t *s2); From e328f848ebee905f8470aa1505c2db33fbfa90f4 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 19 Jun 2015 18:02:16 +0200 Subject: [PATCH 08/28] getdns_rrset and iterators --- src/dnssec.c | 374 +++++++++++++++++++++++++++++++++++++++----- src/rr-iter.c | 7 + src/rr-iter.h | 2 + src/util-internal.h | 8 + 4 files changed, 348 insertions(+), 43 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 2d3f4b8a..e2eb286c 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -52,7 +52,331 @@ #include "general.h" #include "dict.h" -void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); +#if defined(SEC_DEBUG) && SEC_DEBUG +static void debug_sec_print_rr(const char *msg, priv_getdns_rr_iter *rr) +{ + char str_spc[8192], *str = str_spc; + size_t str_len = sizeof(str_spc); + uint8_t *data = rr->pos; + size_t data_len = rr->nxt - rr->pos; + + if (!rr || !rr->pos) { + DEBUG_SEC("\n"); + return; + } + (void) gldns_wire2str_rr_scan(&data, &data_len, &str, &str_len, rr->pkt, rr->pkt_end - rr->pkt); + DEBUG_SEC("%s%s", msg, str_spc); +} +#else +#define debug_sec_print_rr(...) DEBUG_OFF(__VA_ARGS__) +#endif + + +static inline uint16_t rr_iter_type(priv_getdns_rr_iter *rr) +{ return rr->rr_type + 2 <= rr->nxt ? gldns_read_uint16(rr->rr_type) : 0; } +static inline uint16_t rr_iter_class(priv_getdns_rr_iter *rr) +{ return rr->rr_type + 4 <= rr->nxt ? gldns_read_uint16(rr->rr_type + 2) : 0; } + +static priv_getdns_rr_iter *rr_iter_ansauth(priv_getdns_rr_iter *rr) +{ + while (rr && rr->pos && !( + priv_getdns_rr_iter_section(rr) == GLDNS_SECTION_ANSWER || + priv_getdns_rr_iter_section(rr) == GLDNS_SECTION_AUTHORITY)) + + rr = priv_getdns_rr_iter_next(rr); + + return rr && rr->pos ? rr : NULL; +} + +static int rr_owner_equal(priv_getdns_rr_iter *rr, uint8_t *name) +{ + uint8_t owner_spc[256], *owner; + size_t owner_len = sizeof(owner_spc); + + return (owner = priv_getdns_owner_if_or_as_decompressed(rr, owner_spc + , &owner_len)) + && priv_getdns_dname_equal(owner, name); +} + +static priv_getdns_rr_iter *rr_iter_name_class_type(priv_getdns_rr_iter *rr, + uint8_t *name, uint16_t rr_class, uint16_t rr_type) +{ + while (rr_iter_ansauth(rr) && !( + rr_iter_type(rr) == rr_type && + rr_iter_class(rr) == rr_class && + rr_owner_equal(rr, name))) + + rr = priv_getdns_rr_iter_next(rr); + + return rr && rr->pos ? rr : NULL; +} + +static priv_getdns_rr_iter *rr_iter_not_name_class_type(priv_getdns_rr_iter *rr, + uint8_t *name, uint16_t rr_class, uint16_t rr_type) +{ + while (rr_iter_ansauth(rr) && ( + rr_iter_type(rr) == GETDNS_RRTYPE_RRSIG || ( + rr_iter_type(rr) == rr_type && + rr_iter_class(rr) == rr_class && + rr_owner_equal(rr, name)))) + + rr = priv_getdns_rr_iter_next(rr); + + return rr && rr->pos ? rr : NULL; +} + +static priv_getdns_rr_iter *rr_iter_rrsig_covering(priv_getdns_rr_iter *rr, + uint8_t *name, uint16_t rr_class, uint16_t rr_type) +{ + while (rr_iter_ansauth(rr) && !( + rr_iter_type(rr) == GETDNS_RRTYPE_RRSIG && + rr_iter_class(rr) == rr_class && + rr->rr_type + 12 <= rr->nxt && + gldns_read_uint16(rr->rr_type + 10) == rr_type && + rr_owner_equal(rr, name))) + + rr = priv_getdns_rr_iter_next(rr); + + return rr && rr->pos ? rr : NULL; +} + +typedef struct getdns_rrset { + uint8_t *name; + uint16_t rr_class; + uint16_t rr_type; + getdns_network_req *netreq; + uint8_t name_spc[]; +} getdns_rrset; + +typedef struct rrtype_iter { + priv_getdns_rr_iter rr_i; + getdns_rrset *rrset; +} rrtype_iter; + +typedef struct rrsig_iter { + priv_getdns_rr_iter rr_i; + getdns_rrset *rrset; +} rrsig_iter; + +static rrtype_iter *rrtype_iter_next(rrtype_iter *i) +{ + return (rrtype_iter *) rr_iter_name_class_type( + priv_getdns_rr_iter_next(&i->rr_i), + i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); +} + +static rrtype_iter *rrtype_iter_init(rrtype_iter *i, getdns_rrset *rrset) +{ + i->rrset = rrset; + return (rrtype_iter *) rr_iter_name_class_type( + priv_getdns_rr_iter_init(&i->rr_i, rrset->netreq->response + , rrset->netreq->response_len), + i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); +} + +static rrsig_iter *rrsig_iter_next(rrsig_iter *i) +{ + return (rrsig_iter *) rr_iter_rrsig_covering( + priv_getdns_rr_iter_next(&i->rr_i), + i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); +} + +static rrsig_iter *rrsig_iter_init(rrsig_iter *i, getdns_rrset *rrset) +{ + i->rrset = rrset; + return (rrsig_iter *) rr_iter_rrsig_covering( + priv_getdns_rr_iter_init(&i->rr_i, rrset->netreq->response + , rrset->netreq->response_len), + i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); +} + +static int rrset_has_rrsigs(getdns_rrset *rrset) +{ + rrsig_iter rrsig; + return rrsig_iter_init(&rrsig, rrset) != NULL; +} + +#if defined(SEC_DEBUG) && SEC_DEBUG +static void debug_sec_print_rrset(const char *msg, getdns_rrset *rrset) +{ + char owner[1024]; + char buf_space[2048]; + gldns_buffer buf; + rrtype_iter *rr, rr_space; + rrsig_iter *rrsig, rrsig_space; + size_t i; + + if (!rrset) { + DEBUG_SEC(""); + return; + } + gldns_buffer_init_frm_data(&buf, buf_space, sizeof(buf_space)); + if (gldns_wire2str_dname_buf(rrset->name, 256, owner, sizeof(owner))) + gldns_buffer_printf(&buf, "%s ", owner); + else gldns_buffer_printf(&buf, " "); + + switch (rrset->rr_class) { + case GETDNS_RRCLASS_IN : gldns_buffer_printf(&buf, "IN ") ; break; + case GETDNS_RRCLASS_CH : gldns_buffer_printf(&buf, "CH ") ; break; + case GETDNS_RRCLASS_HS : gldns_buffer_printf(&buf, "HS ") ; break; + case GETDNS_RRCLASS_NONE: gldns_buffer_printf(&buf, "NONE "); break; + case GETDNS_RRCLASS_ANY : gldns_buffer_printf(&buf, "ANY ") ; break; + default : gldns_buffer_printf(&buf, "CLASS%d " + , rrset->rr_class); + break; + } + gldns_buffer_printf(&buf, "%s", priv_getdns_rr_type_name(rrset->rr_type)); + + gldns_buffer_printf(&buf, ", rrs:"); + for ( rr = rrtype_iter_init(&rr_space, rrset), i = 1 + ; rr + ; rr = rrtype_iter_next(rr), i++) + gldns_buffer_printf(&buf, " %d", (int)i); + + gldns_buffer_printf(&buf, ", rrsigs:"); + for ( rrsig = rrsig_iter_init(&rrsig_space, rrset), i = 1 + ; rrsig + ; rrsig = rrsig_iter_next(rrsig), i++) + gldns_buffer_printf(&buf, " %d", (int)i); + + DEBUG_SEC("%s%s\n", msg, buf_space); +} +#else +#define debug_sec_print_rrset(...) DEBUG_OFF(__VA_ARGS__) +#endif + + + +typedef struct rrset_iter rrset_iter; +struct rrset_iter { + getdns_rrset rrset; + uint8_t name_spc[256]; + size_t name_len; + priv_getdns_rr_iter rr_i; +}; + +static rrset_iter *rrset_iter_init(rrset_iter *i, getdns_network_req *netreq) +{ + priv_getdns_rr_iter *rr; + + i->rrset.name = i->name_spc; + i->rrset.netreq = netreq; + i->name_len = 0; + + for ( rr = priv_getdns_rr_iter_init(&i->rr_i + , netreq->response + , netreq->response_len) + ;(rr = rr_iter_ansauth(rr)) + ; rr = priv_getdns_rr_iter_next(rr)) { + + if ((i->rrset.rr_type = rr_iter_type(rr)) + == GETDNS_RRTYPE_RRSIG) + continue; + + i->rrset.rr_class = rr_iter_class(rr); + + if (!(i->rrset.name = priv_getdns_owner_if_or_as_decompressed( + rr, i->name_spc, &i->name_len))) + continue; + + return i; + } + return NULL; +} + + +static rrset_iter *rrset_iter_next(rrset_iter *i) +{ + priv_getdns_rr_iter *rr; + + if (!(rr = i && i->rr_i.pos ? &i->rr_i : NULL)) + return NULL; + + if (!(rr = rr_iter_not_name_class_type(rr, + i->rrset.name, i->rrset.rr_class, i->rrset.rr_type))) + return NULL; + + i->rrset.rr_type = rr_iter_type(rr); + i->rrset.rr_class = rr_iter_class(rr); + if (!(i->rrset.name = priv_getdns_owner_if_or_as_decompressed( + rr, i->name_spc, &i->name_len))) + return rrset_iter_next(i); + + return i; +} + +static getdns_rrset *rrset_iter_value(rrset_iter *i) +{ + if (!i) + return NULL; + if (!i->rr_i.pos) + return NULL; + return &i->rrset; +} + +typedef struct chain_head chain_head; +typedef struct chain_node chain_node; + +struct chain_head { + chain_head *next; + chain_node *parent; + getdns_rrset rrset; +}; + +struct chain_node { + chain_node *parent; + + unsigned int skip: 1; /* This label plays no role */ + unsigned int cut : 1; /* At a zone cut */ + + getdns_rrset dnskey; + getdns_rrset ds ; +}; + +static void check_chain_complete(chain_head *chain) +{ +} + +static void add2val_chain( + chain_head **chain_p, getdns_network_req *netreq) +{ + rrset_iter *i, i_spc; + + assert(netreq->response); + assert(netreq->response_len >= GLDNS_HEADER_SIZE); + + /* For all things with signatures, create a chain */ + + /* For all things without signature, find SOA (zonecut) and query DS */ + + /* On empty packet, find SOA (zonecut) for the qname and query DS */ + + for (i = rrset_iter_init(&i_spc, netreq); i; i = rrset_iter_next(i)) { + debug_sec_print_rrset("rrset: ", rrset_iter_value(i)); + } +} + +static void get_val_chain(getdns_dns_req *dnsreq) +{ + getdns_network_req *netreq, **netreq_p; + chain_head *chain = NULL; + + for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) + add2val_chain(&chain, netreq); + + if (chain) + check_chain_complete(chain); + else + priv_getdns_call_user_callback(dnsreq, + create_getdns_response(dnsreq)); +} + +/******************************************************************************/ +/***************************** *******************************/ +/***************************** NEW CHAIN CODE *******************************/ +/***************************** (above) *******************************/ +/***************************** *******************************/ +/******************************************************************************/ struct validation_chain { getdns_rbtree_t root; @@ -319,10 +643,8 @@ find_delegation_point_callback(struct getdns_dns_req *dns_req) { struct validation_chain *chain = (struct validation_chain *) dns_req->user_pointer; - getdns_context *context = dns_req->context; getdns_network_req **netreq_p, *netreq; priv_getdns_rr_iter rr_iter_storage, *rr_iter; - priv_getdns_rdf_iter rdf_storage, *rdf; gldns_pkt_section section; uint16_t rr_type; uint8_t rr_name_space[256], *rr_name; @@ -584,7 +906,8 @@ getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) void priv_getdns_get_validation_chain(getdns_dns_req *dns_req) { - getdns_get_validation_chain(dns_req, NULL); + get_val_chain(dns_req); + //getdns_get_validation_chain(dns_req, NULL); } /********************** functions for validate_dnssec *************************/ @@ -775,7 +1098,7 @@ rrset_iter_init_zone(zone_iter *i, ldns_dnssec_zone *zone) } static ldns_dnssec_rrsets * -rrset_iter_value(zone_iter *i) +_rrset_iter_value(zone_iter *i) { assert(i); @@ -783,7 +1106,7 @@ rrset_iter_value(zone_iter *i) } static void -rrset_iter_next(zone_iter *i) +_rrset_iter_next(zone_iter *i) { int was_nsec_rrset; ldns_dnssec_name *name; @@ -934,41 +1257,6 @@ done_free_verifying_keys: return s; } -typedef struct getdns_rrset getdns_rrset; -struct getdns_rrset { - getdns_bindata *name; - uint16_t rr_type; - uint16_t rr_class; - uint32_t ttl; - getdns_list *rrs; - getdns_list *sigs; -}; - -typedef struct getdns_delpath_elem getdns_delpath_elem; -struct getdns_delpath_elem { - getdns_delpath_elem *e; /* self or canonical element - * (with overlapping paths) - */ - unsigned int is_zonecut : 1; - unsigned int ds_nxdomain: 1; - getdns_rrset ds; - getdns_rrset dnskey; - getdns_dict *ds_nsec; - getdns_list *ds_nsec_sigs; - getdns_dict *ds_nsec_wc; /* wildcard */ - getdns_list *ds_nsec_wc_sigs; - getdns_dict *ds_nsec_ce; /* closest encloser */ - getdns_list *ds_nsec_ce_sigs; -}; - -typedef struct getdns_delpath getdns_delpath; -struct getdns_delpath { - getdns_delpath *next; /* In a ring */ - getdns_rrset rrset; - size_t nlabels; - getdns_delpath_elem elems[]; -}; - /* * getdns_validate_dnssec * @@ -1005,7 +1293,7 @@ getdns_validate_dnssec(getdns_list *records_to_validate, } /* Create a rr_list of all the keys in the support records */ for (rrset_iter_init_zone(&i, support); - (rrset = rrset_iter_value(&i)); rrset_iter_next(&i)) + (rrset = _rrset_iter_value(&i)); _rrset_iter_next(&i)) if (ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DS || ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DNSKEY) @@ -1016,7 +1304,7 @@ getdns_validate_dnssec(getdns_list *records_to_validate, /* Now walk through the rrsets to validate */ for (rrset_iter_init_zone(&i, to_validate); - (rrset = rrset_iter_value(&i)); rrset_iter_next(&i)) { + (rrset = _rrset_iter_value(&i)); _rrset_iter_next(&i)) { if ((s = chase(rrset, support, support_keys, trusted))) break; diff --git a/src/rr-iter.c b/src/rr-iter.c index f6188307..6b68ac1c 100644 --- a/src/rr-iter.c +++ b/src/rr-iter.c @@ -98,6 +98,13 @@ priv_getdns_rr_iter_init(priv_getdns_rr_iter *i, uint8_t *pkt, size_t pkt_len) return find_rrtype(i); } +priv_getdns_rr_iter * +priv_getdns_rr_iter_rewind(priv_getdns_rr_iter *i) +{ + assert(i); + + return priv_getdns_rr_iter_init(i, i->pkt, i->pkt_end - i->pkt); +} priv_getdns_rr_iter * priv_getdns_rr_iter_next(priv_getdns_rr_iter *i) diff --git a/src/rr-iter.h b/src/rr-iter.h index cfc9faf1..858ee1d9 100644 --- a/src/rr-iter.h +++ b/src/rr-iter.h @@ -62,6 +62,8 @@ typedef struct priv_getdns_rr_iter { priv_getdns_rr_iter *priv_getdns_rr_iter_init(priv_getdns_rr_iter *i, uint8_t *pkt, size_t pkt_len); +priv_getdns_rr_iter *priv_getdns_rr_iter_rewind(priv_getdns_rr_iter *i); + priv_getdns_rr_iter *priv_getdns_rr_iter_next(priv_getdns_rr_iter *i); uint8_t *priv_getdns_owner_if_or_as_decompressed( diff --git a/src/util-internal.h b/src/util-internal.h index 1c3adb55..b81588f5 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -44,6 +44,7 @@ #define SCHED_DEBUG 0 #define WIRE_DEBUG 0 #define STUB_DEBUG 0 +#define SEC_DEBUG 1 #ifdef S_SPLINT_S # define INLINE @@ -174,6 +175,13 @@ getdns_return_t priv_getdns_validate_extensions(struct getdns_dict * extensions) #define DEBUG_STUB(...) DEBUG_OFF(__VA_ARGS__) #endif +#if defined(SEC_DEBUG) && SEC_DEBUG +#include +#define DEBUG_SEC(...) DEBUG_ON(__VA_ARGS__) +#else +#define DEBUG_SEC(...) DEBUG_OFF(__VA_ARGS__) +#endif + INLINE getdns_eventloop_event *getdns_eventloop_event_init( getdns_eventloop_event *ev,void *userarg, getdns_eventloop_callback read_cb, getdns_eventloop_callback write_cb, getdns_eventloop_callback timeout_cb) From 3631cd658a2a03f31fff9a61da66ce8f8a56419c Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 23 Jun 2015 00:00:20 +0200 Subject: [PATCH 09/28] get_val_chain for all possible scenarios --- src/dnssec.c | 1027 +++++++++++++++++++--------------------- src/general.c | 42 +- src/general.h | 4 +- src/request-internal.c | 16 +- src/types-internal.h | 2 + src/util-internal.c | 1 - 6 files changed, 532 insertions(+), 560 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index e2eb286c..013ccbbe 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -53,7 +53,7 @@ #include "dict.h" #if defined(SEC_DEBUG) && SEC_DEBUG -static void debug_sec_print_rr(const char *msg, priv_getdns_rr_iter *rr) +inline static void debug_sec_print_rr(const char *msg, priv_getdns_rr_iter *rr) { char str_spc[8192], *str = str_spc; size_t str_len = sizeof(str_spc); @@ -61,14 +61,25 @@ static void debug_sec_print_rr(const char *msg, priv_getdns_rr_iter *rr) size_t data_len = rr->nxt - rr->pos; if (!rr || !rr->pos) { - DEBUG_SEC("\n"); + DEBUG_SEC("%s\n", msg); return; } - (void) gldns_wire2str_rr_scan(&data, &data_len, &str, &str_len, rr->pkt, rr->pkt_end - rr->pkt); + (void) gldns_wire2str_rr_scan( + &data, &data_len, &str, &str_len, rr->pkt, rr->pkt_end - rr->pkt); DEBUG_SEC("%s%s", msg, str_spc); } +inline static void debug_sec_print_dname(const char *msg, uint8_t *label) +{ + char str[1024]; + + if (gldns_wire2str_dname_buf(label, 256, str, sizeof(str))) + DEBUG_SEC("%s%s\n", msg, str); + else + DEBUG_SEC("%s\n", msg); +} #else #define debug_sec_print_rr(...) DEBUG_OFF(__VA_ARGS__) +#define debug_sec_print_dname(...) DEBUG_OFF(__VA_ARGS__) #endif @@ -190,7 +201,7 @@ static rrsig_iter *rrsig_iter_init(rrsig_iter *i, getdns_rrset *rrset) i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); } -static int rrset_has_rrsigs(getdns_rrset *rrset) +inline static int rrset_has_rrsigs(getdns_rrset *rrset) { rrsig_iter rrsig; return rrsig_iter_init(&rrsig, rrset) != NULL; @@ -318,41 +329,502 @@ typedef struct chain_head chain_head; typedef struct chain_node chain_node; struct chain_head { - chain_head *next; - chain_node *parent; - getdns_rrset rrset; + struct mem_funcs my_mf; + + chain_head *next; + chain_node *parent; + size_t node_count; /* Number of nodes attached directly + * to this head. For cleaning. */ + getdns_rrset rrset; + uint8_t name_spc[]; }; struct chain_node { chain_node *parent; - unsigned int skip: 1; /* This label plays no role */ - unsigned int cut : 1; /* At a zone cut */ - getdns_rrset dnskey; getdns_rrset ds ; + getdns_rrset soa ; + + chain_head *chains; }; +static size_t count_outstanding_requests(chain_head *head) +{ + size_t count; + chain_node *node; + + if (!head) + return 0; + + for ( node = head->parent, count = 0 + ; node + ; node = node->parent) { + + if (node->dnskey.netreq && + node->dnskey.netreq->state != NET_REQ_FINISHED && + node->dnskey.netreq->state != NET_REQ_CANCELED) + count++; + + if (node->ds.netreq && + node->ds.netreq->state != NET_REQ_FINISHED && + node->ds.netreq->state != NET_REQ_CANCELED) + count++; + + if (node->soa.netreq && + node->soa.netreq->state != NET_REQ_FINISHED && + node->soa.netreq->state != NET_REQ_CANCELED) + count++; + } + return count + count_outstanding_requests(head->next); +} + +static void append_rrs2val_chain_list(getdns_context *ctxt, + getdns_list *val_chain_list, getdns_network_req *netreq) +{ + rrset_iter *i, i_spc; + getdns_rrset *rrset; + rrtype_iter *rr, rr_spc; + rrsig_iter *rrsig, rrsig_spc; + getdns_dict *rr_dict; + + for (i = rrset_iter_init(&i_spc, netreq); i; i = rrset_iter_next(i)) { + rrset = rrset_iter_value(i); + + if (rrset->rr_type != GETDNS_RRTYPE_DNSKEY && + rrset->rr_type != GETDNS_RRTYPE_DS && + rrset->rr_type != GETDNS_RRTYPE_NSEC && + rrset->rr_type != GETDNS_RRTYPE_NSEC3) + continue; + + for ( rr = rrtype_iter_init(&rr_spc, rrset) + ; rr; rr = rrtype_iter_next(rr)) { + + rr_dict = priv_getdns_rr_iter2rr_dict(ctxt, &rr->rr_i); + if (!rr_dict) continue; + + (void)getdns_list_append_dict(val_chain_list, rr_dict); + getdns_dict_destroy(rr_dict); + } + for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset) + ; rrsig; rrsig = rrsig_iter_next(rrsig)) { + + rr_dict=priv_getdns_rr_iter2rr_dict(ctxt,&rrsig->rr_i); + if (!rr_dict) continue; + + (void)getdns_list_append_dict(val_chain_list, rr_dict); + getdns_dict_destroy(rr_dict); + } + } +} + static void check_chain_complete(chain_head *chain) { + getdns_dns_req *dnsreq; + size_t o, node_count; + chain_head *head, *next; + chain_node *node; + getdns_list *val_chain_list; + getdns_dict *response_dict; + + if ((o = count_outstanding_requests(chain)) > 0) { + DEBUG_SEC("%zu outstanding requests\n", o); + return; + } + DEBUG_SEC("Chain done!\n"); + dnsreq = chain->rrset.netreq->owner; + /* TODO: DNSSEC validation of netreq! */ + + val_chain_list = dnsreq->dnssec_return_validation_chain + ? getdns_list_create_with_context(dnsreq->context) : NULL; + + /* Cleanup the chain */ + for ( head = chain; head ; head = next ) { + next = head->next; + for ( node_count = head->node_count, node = head->parent + ; node_count + ; node_count--, node = node->parent ) { + + if (node->dnskey.netreq) { + append_rrs2val_chain_list(dnsreq->context, + val_chain_list, node->dnskey.netreq); + dns_req_free(node->dnskey.netreq->owner); + } + if (node->ds.netreq) { + append_rrs2val_chain_list(dnsreq->context, + val_chain_list, node->ds.netreq); + dns_req_free(node->ds.netreq->owner); + } + } + GETDNS_FREE(head->my_mf, head); + } + + response_dict = create_getdns_response(dnsreq); + if (val_chain_list) { + (void) getdns_dict_set_list( + response_dict, "validation_chain", val_chain_list); + getdns_list_destroy(val_chain_list); + } + /* Final user callback */ + priv_getdns_call_user_callback(dnsreq, response_dict); } -static void add2val_chain( +static void val_chain_node_soa_cb(getdns_dns_req *dnsreq); +static void val_chain_sched_soa_node(chain_node *node) +{ + getdns_context *context; + getdns_eventloop *loop; + getdns_dns_req *dnsreq; + char name[1024]; + + context = node->chains->rrset.netreq->owner->context; + loop = node->chains->rrset.netreq->owner->loop; + + if (!gldns_wire2str_dname_buf(node->soa.name, 256, name, sizeof(name))) + return; + + if (! node->soa.netreq && + ! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_SOA, + dnssec_ok_checking_disabled, node, &dnsreq, NULL, + val_chain_node_soa_cb)) + + node->soa.netreq = *dnsreq->netreqs; +} + +static void val_chain_sched_soa(chain_head *head, uint8_t *dname) +{ + chain_node *node; + + if (!*dname) + return; + + for ( node = head->parent + ; node && !priv_getdns_dname_equal(dname, node->dnskey.name) + ; node = node->parent); + + if (node) + val_chain_sched_soa_node(node); +} + +static void val_chain_node_cb(getdns_dns_req *dnsreq); +static void val_chain_sched_node(chain_node *node) +{ + getdns_context *context; + getdns_eventloop *loop; + getdns_dns_req *dnsreq; + char name[1024]; + + context = node->chains->rrset.netreq->owner->context; + loop = node->chains->rrset.netreq->owner->loop; + + if (!gldns_wire2str_dname_buf(node->dnskey.name, 256, name, sizeof(name))) + return; + + DEBUG_SEC("schedule DS & DNSKEY lookup for %s\n", name); + + if (! node->dnskey.netreq /* not scheduled */ && + ! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DNSKEY, + dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb)) + + node->dnskey.netreq = *dnsreq->netreqs; + + if (! node->ds.netreq && node->parent /* not root */ && + ! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DS, + dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb)) + + node->ds.netreq = *dnsreq->netreqs; +} + +static void val_chain_sched(chain_head *head, uint8_t *dname) +{ + chain_node *node; + + for ( node = head->parent + ; node && !priv_getdns_dname_equal(dname, node->dnskey.name) + ; node = node->parent); + if (node) + val_chain_sched_node(node); +} + +static void val_chain_sched_signer_node(chain_node *node, rrsig_iter *rrsig) +{ + priv_getdns_rdf_iter rdf_spc, *rdf; + uint8_t signer_spc[256], *signer; + size_t signer_len; + + if (!(rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rrsig->rr_i, 7))) + return; + + if (!(signer = priv_getdns_rdf_if_or_as_decompressed( + rdf, signer_spc, &signer_len))) + return; + + while (node && !priv_getdns_dname_equal(signer, node->dnskey.name)) + node = node->parent; + if (node) + val_chain_sched_node(node); +} + +static void val_chain_sched_signer(chain_head *head, rrsig_iter *rrsig) +{ + val_chain_sched_signer_node(head->parent, rrsig); +} + +static void val_chain_node_cb(getdns_dns_req *dnsreq) +{ + chain_node *node = (chain_node *)dnsreq->user_pointer; + getdns_network_req *netreq = dnsreq->netreqs[0]; + rrset_iter *i, i_spc; + getdns_rrset *rrset; + rrsig_iter *rrsig, rrsig_spc; + + getdns_context_clear_outbound_request(dnsreq); + switch (netreq->request_type) { + case GETDNS_RRTYPE_DS : break; + case GETDNS_RRTYPE_DNSKEY: node->dnskey.netreq = netreq; + default : check_chain_complete(node->chains); + return; + } + for (i = rrset_iter_init(&i_spc, netreq); i; i = rrset_iter_next(i)) { + rrset = rrset_iter_value(i); + + if (rrset->rr_type != GETDNS_RRTYPE_DS && + rrset->rr_type != GETDNS_RRTYPE_NSEC && + rrset->rr_type != GETDNS_RRTYPE_NSEC3) + continue; + + for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset) + ; rrsig; rrsig = rrsig_iter_next(rrsig)) + + val_chain_sched_signer_node(node, rrsig); + } + check_chain_complete(node->chains); +} + + +static getdns_rrset *rrset_by_type( + rrset_iter *i_spc, getdns_network_req *netreq, uint16_t rr_type) +{ + rrset_iter *i; + getdns_rrset *rrset; + + for (i = rrset_iter_init(i_spc, netreq); i; i = rrset_iter_next(i)) { + rrset = rrset_iter_value(i); + if (rrset->rr_type == rr_type) + return rrset; + } + return NULL; +} + +static void val_chain_node_soa_cb(getdns_dns_req *dnsreq) +{ + chain_node *node = (chain_node *)dnsreq->user_pointer; + getdns_network_req *netreq = dnsreq->netreqs[0]; + rrset_iter i_spc; + getdns_rrset *rrset; + + getdns_context_clear_outbound_request(dnsreq); + + if ((rrset = rrset_by_type(&i_spc, netreq, GETDNS_RRTYPE_SOA))) { + + while (node && + ! priv_getdns_dname_equal(node->soa.name, rrset->name)) + node = node->parent; + + val_chain_sched_node(node); + } else + val_chain_sched_soa_node(node->parent); + + check_chain_complete(node->chains); +} + +static int is_subdomain( + const uint8_t * const subdomain, const uint8_t *domain) +{ + while (*domain) { + if (priv_getdns_dname_equal(subdomain, domain)) + return 1; + + domain += *domain + 1; + } + return *subdomain == 0; +} + +static uint8_t **reverse_labels(uint8_t *dname, uint8_t **labels) +{ + if (*dname) + labels = reverse_labels(dname + *dname + 1, labels); + *labels = dname; + return labels + 1; +} + +static chain_head *add_rrset2val_chain( + struct mem_funcs *mf, chain_head **chain_p, getdns_rrset *rrset) +{ + chain_head *head; + uint8_t *labels[128], **last_label, **label; + + size_t max_labels; /* max labels in common */ + chain_head *max_head; + chain_node *max_node; + + size_t dname_len, head_sz, node_count, n; + uint8_t *dname, *region; + chain_node *node; + + last_label = reverse_labels(rrset->name, labels); + + /* Try to find a chain with the most overlapping labels. + * max_labels will be the number of labels in common from the root + * (so at least one; the root) + * max_head will be the head of the chain with max # labebs in common + */ + max_head = NULL; + max_labels = 0; + for (head = *chain_p; head; head = head->next) { + for (label = labels; label < last_label; label++) { + if (! is_subdomain(*label, head->rrset.name)) + break; + } + if (label - labels > max_labels) { + max_labels = label - labels; + max_head = head; + } + } + /* Chain found. Now set max_node to the point in the chain where nodes + * will be common. + */ + if (max_head) { + for ( node = max_head->parent, n = 0 + ; node + ; node = node->parent, n++); + + for ( n -= max_labels, node = max_head->parent + ; n + ; n--, node = node->parent); + } else + max_node = NULL; + + /* node_count is the amount of nodes to still allocate. + * the last one's parent has to hook into the max_node. + */ + dname_len = *labels - last_label[-1] + 1; + head_sz = (sizeof(chain_head) + dname_len + 7) / 8 * 8; + node_count = last_label - labels - max_labels; + DEBUG_SEC( "%zu labels in common. %zu labels to allocate\n" + , max_labels, node_count); + + if (! (region = GETDNS_XMALLOC(*mf, uint8_t, head_sz + + node_count * sizeof(chain_node)))) + return NULL; + + /* Append the head on the linked list of heads */ + for (head = *chain_p; head && head->next; head = head->next); + if (head) + head = head->next = (chain_head *)region; + else + head = *chain_p = (chain_head *)region; + + head->my_mf = *mf; + head->next = NULL; + head->rrset.name = head->name_spc; + memcpy(head->name_spc, rrset->name, dname_len); + head->rrset.rr_class = rrset->rr_class; + head->rrset.rr_type = rrset->rr_type; + head->rrset.netreq = rrset->netreq; + head->node_count = node_count; + + if (!node_count) { + head->parent = max_head->parent; + return head; + } + + /* Initialize the nodes */ + node = (chain_node *)(region + head_sz); + head->parent = node; + + for ( node = (chain_node *)(region + head_sz), head->parent = node + , dname = head->rrset.name + ; node_count + ; node_count--, node = node->parent =&node[1], dname += *dname + 1) { + + node->chains = *chain_p; + node->ds.name = dname; + node->dnskey.name = dname; + node->soa.name = dname; + node->ds.rr_class = head->rrset.rr_class; + node->dnskey.rr_class = head->rrset.rr_class; + node->soa.rr_class = head->rrset.rr_class; + node->ds.rr_type = GETDNS_RRTYPE_DS; + node->dnskey.rr_type = GETDNS_RRTYPE_DNSKEY; + node->soa.rr_type = GETDNS_RRTYPE_SOA; + node->ds.netreq = NULL; + node->dnskey.netreq = NULL; + node->soa.netreq = NULL; + + node->soa.rr_class = head->rrset.rr_class; + } + /* On the first chain, max_node == NULL. + * Schedule a root DNSKEY query, we always need that. + */ + if (!(node[-1].parent = max_node)) + val_chain_sched(head, (uint8_t *)"\0"); + + return head; +} + +static void add_netreq2val_chain( chain_head **chain_p, getdns_network_req *netreq) { rrset_iter *i, i_spc; + getdns_rrset *rrset; + rrsig_iter *rrsig, rrsig_spc; + size_t n_rrsigs; + chain_head *head; + struct mem_funcs *mf; + getdns_rrset empty_rrset; assert(netreq->response); assert(netreq->response_len >= GLDNS_HEADER_SIZE); + mf = priv_getdns_context_mf(netreq->owner->context); + + /* On empty packet, find SOA (zonecut) for the qname and query DS */ + /* For all things with signatures, create a chain */ /* For all things without signature, find SOA (zonecut) and query DS */ - /* On empty packet, find SOA (zonecut) for the qname and query DS */ + if (GLDNS_ANCOUNT(netreq->response) == 0 && + GLDNS_NSCOUNT(netreq->response) == 0) { + empty_rrset.name = netreq->query + GLDNS_HEADER_SIZE; + empty_rrset.rr_class = GETDNS_RRCLASS_IN; + empty_rrset.rr_type = 0; + empty_rrset.netreq = netreq; + + head = add_rrset2val_chain(mf, chain_p, &empty_rrset); + val_chain_sched_soa(head, empty_rrset.name); + return; + } for (i = rrset_iter_init(&i_spc, netreq); i; i = rrset_iter_next(i)) { - debug_sec_print_rrset("rrset: ", rrset_iter_value(i)); + rrset = rrset_iter_value(i); + debug_sec_print_rrset("rrset: ", rrset); + + head = add_rrset2val_chain(mf, chain_p, rrset); + for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset), n_rrsigs = 0 + ; rrsig + ; rrsig = rrsig_iter_next(rrsig), n_rrsigs++) { + + val_chain_sched_signer(head, rrsig); + } + if (n_rrsigs) + continue; + + if (rrset->rr_type == GETDNS_RRTYPE_SOA) + val_chain_sched(head, rrset->name); + else + val_chain_sched_soa(head, rrset->name); } } @@ -362,7 +834,7 @@ static void get_val_chain(getdns_dns_req *dnsreq) chain_head *chain = NULL; for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) - add2val_chain(&chain, netreq); + add_netreq2val_chain(&chain, netreq); if (chain) check_chain_complete(chain); @@ -378,536 +850,9 @@ static void get_val_chain(getdns_dns_req *dnsreq) /***************************** *******************************/ /******************************************************************************/ -struct validation_chain { - getdns_rbtree_t root; - struct mem_funcs mf; - getdns_dns_req *dns_req; - size_t lock; - uint64_t *timeout; -}; - -struct chain_response { - int err; - getdns_list *result; - struct validation_chain *chain; - getdns_dns_req *dns_req; -}; - -struct chain_link { - getdns_rbnode_t node; - struct chain_response DNSKEY; - struct chain_response DS; -}; - -static void launch_chain_link_lookup(struct validation_chain *chain, - uint8_t *dname); -static void destroy_chain(struct validation_chain *chain); - -#ifdef STUB_NATIVE_DNSSEC -static void -native_stub_validate_dnssec(getdns_dns_req *dns_req, getdns_list *support) -{ - getdns_network_req *netreq, **netreq_p; - getdns_list *trust_anchors; - getdns_dict *reply = NULL; - getdns_list *to_validate; - getdns_list *list; - getdns_dict *rr_dict; - size_t i; - - if (!(trust_anchors = getdns_root_trust_anchor(NULL))) - return; - - for (netreq_p = dns_req->netreqs; (netreq = *netreq_p) ; netreq_p++) { - if (!(reply = priv_getdns_create_reply_dict(dns_req->context, - netreq, NULL, NULL))) - continue; - if (!(to_validate = - getdns_list_create_with_context(dns_req->context))) - break; - if (getdns_dict_get_list(reply, "answer", &list)) { - getdns_list_destroy(to_validate); - break; - } - for (i = 0; !getdns_list_get_dict(list, i, &rr_dict); i++) - (void) getdns_list_append_dict(to_validate, rr_dict); - - if (getdns_dict_get_list(reply, "authority", &list)) { - getdns_list_destroy(to_validate); - break; - } - for (i = 0; !getdns_list_get_dict(list, i, &rr_dict); i++) - (void) getdns_list_append_dict(to_validate, rr_dict); - - switch ((int)getdns_validate_dnssec( - to_validate, support, trust_anchors)) { - case GETDNS_DNSSEC_SECURE: - netreq->secure = 1; - netreq->bogus = 0; - break; - case GETDNS_DNSSEC_BOGUS: - netreq->secure = 0; - netreq->bogus = 1; - break; - default: - /* GETDNS_DNSSEC_INSECURE */ - netreq->secure = 0; - netreq->bogus = 0; - break; - } - getdns_list_destroy(to_validate); - getdns_dict_destroy(reply); - reply = NULL; - } - getdns_list_destroy(trust_anchors); - getdns_dict_destroy(reply); -} -#endif - -static void callback_on_complete_chain(struct validation_chain *chain) -{ - getdns_context *context = chain->dns_req->context; - getdns_dict *response; - struct chain_link *link; - size_t ongoing = chain->lock; - getdns_list *keys; - size_t i; - getdns_dict *rr_dict; - - RBTREE_FOR(link, struct chain_link *, - (getdns_rbtree_t *)&(chain->root)) { - if (link->DNSKEY.result == NULL && link->DNSKEY.err == 0) - ongoing++; - if (link->DS.result == NULL && link->DS.err == 0 && - (((const char *)link->node.key)[0] != '.' || - ((const char *)link->node.key)[1] != '\0' )) - ongoing++; - } - if (ongoing > 0) - return; - - if (!(keys = getdns_list_create_with_context(context))) { - priv_getdns_call_user_callback(chain->dns_req, - create_getdns_response(chain->dns_req)); - destroy_chain(chain); - return; - } - RBTREE_FOR(link, struct chain_link *, - (getdns_rbtree_t *)&(chain->root)) { - for (i = 0; !getdns_list_get_dict( link->DS.result - , i, &rr_dict); i++) - (void) getdns_list_append_dict(keys, rr_dict); - - for (i = 0; !getdns_list_get_dict( link->DNSKEY.result - , i, &rr_dict); i++) - (void) getdns_list_append_dict(keys, rr_dict); - } -#ifdef STUB_NATIVE_DNSSEC - native_stub_validate_dnssec(chain->dns_req, keys); -#endif - if ((response = create_getdns_response(chain->dns_req)) && - chain->dns_req->dnssec_return_validation_chain) { - (void)getdns_dict_set_list(response, "validation_chain", keys); - } - getdns_list_destroy(keys); - priv_getdns_call_user_callback(chain->dns_req, response); - destroy_chain(chain); -} - - -static void -chain_response_callback(struct getdns_dns_req *dns_req) -{ - struct chain_response *response = - (struct chain_response *) dns_req->user_pointer; - getdns_context *context = dns_req->context; - getdns_network_req **netreq_p, *netreq; - priv_getdns_rr_iter rr_iter_storage, *rr_iter; - priv_getdns_rdf_iter rdf_storage, *rdf; - gldns_pkt_section section; - uint16_t rr_type, type_covered; - getdns_dict *rr_dict; - getdns_list *keys; - size_t nkeys; - getdns_return_t r; - uint8_t sign_name_space[256], *sign_name; - size_t sign_name_len = sizeof(sign_name_space); - - response->dns_req = dns_req; - if (!(keys = getdns_list_create_with_context(context))) - goto done; - - for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) { - for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage - , netreq->response - , netreq->response_len) - ; rr_iter - ; rr_iter = priv_getdns_rr_iter_next(rr_iter) - ) { - section = priv_getdns_rr_iter_section(rr_iter); - if (section != GLDNS_SECTION_ANSWER && - section != GLDNS_SECTION_AUTHORITY) - continue; - - rr_type = gldns_read_uint16(rr_iter->rr_type); - - if (rr_type == GETDNS_RRTYPE_DS || - rr_type == GETDNS_RRTYPE_DNSKEY || - rr_type == GETDNS_RRTYPE_NSEC || - rr_type == GETDNS_RRTYPE_NSEC3) { - if (!(rr_dict = priv_getdns_rr_iter2rr_dict( - context, rr_iter))) - continue; - r = getdns_list_append_dict(keys, rr_dict); - getdns_dict_destroy(rr_dict); - if (r) break; - } - if (rr_type != GETDNS_RRTYPE_RRSIG) - continue; - - if (!(rdf = priv_getdns_rdf_iter_init( - &rdf_storage, rr_iter))) - continue; - - type_covered = gldns_read_uint16(rdf->pos); - if (type_covered == GETDNS_RRTYPE_DS || - type_covered == GETDNS_RRTYPE_NSEC || - type_covered == GETDNS_RRTYPE_NSEC3) { - - if ((rdf = priv_getdns_rdf_iter_init_at( - &rdf_storage, rr_iter, 7)) && - (sign_name = priv_getdns_rdf_if_or_as_decompressed( - rdf, sign_name_space, &sign_name_len))) - - launch_chain_link_lookup( - response->chain, sign_name); - - } else if (type_covered != GETDNS_RRTYPE_DNSKEY) - continue; - - if (!(rr_dict = priv_getdns_rr_iter2rr_dict( - context, rr_iter))) - continue; - r = getdns_list_append_dict(keys, rr_dict); - getdns_dict_destroy(rr_dict); - if (r) break; - } - } - if (getdns_list_get_length(keys, &nkeys)) - getdns_list_destroy(keys); - - else if (!nkeys) - getdns_list_destroy(keys); - else - response->result = keys; - - -done: if (response->err == 0 && response->result == NULL) - response->err = -1; - - callback_on_complete_chain(response->chain); -} - -static void chain_response_init( - struct validation_chain *chain, struct chain_response *response) -{ - response->err = 0; - response->result = NULL; - response->chain = chain; - response->dns_req = NULL; -} - -static int -resolve(char* name, int rrtype, struct chain_response *response) -{ - getdns_return_t r; - getdns_dict *extensions; - - if (!(extensions = getdns_dict_create_with_context( - response->chain->dns_req->context))) - return GETDNS_RETURN_MEMORY_ERROR; - - if (!(r = getdns_dict_set_int(extensions, - "dnssec_ok_checking_disabled", GETDNS_EXTENSION_TRUE))) - - r = priv_getdns_general_loop(response->chain->dns_req->context, - response->chain->dns_req->loop, name, rrtype, extensions, - response, NULL, NULL, chain_response_callback); - - getdns_dict_destroy(extensions); - return r; -} - -static void -find_delegation_point_callback(struct getdns_dns_req *dns_req) -{ - struct validation_chain *chain = - (struct validation_chain *) dns_req->user_pointer; - getdns_network_req **netreq_p, *netreq; - priv_getdns_rr_iter rr_iter_storage, *rr_iter; - gldns_pkt_section section; - uint16_t rr_type; - uint8_t rr_name_space[256], *rr_name; - size_t rr_name_len = sizeof(rr_name_space); - - for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) { - for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage - , netreq->response - , netreq->response_len) - ; rr_iter - ; rr_iter = priv_getdns_rr_iter_next(rr_iter) - ) { - section = priv_getdns_rr_iter_section(rr_iter); - if (section != GLDNS_SECTION_ANSWER && - section != GLDNS_SECTION_AUTHORITY) - continue; - - rr_type = gldns_read_uint16(rr_iter->rr_type); - if (rr_type != GETDNS_RRTYPE_SOA) - continue; - - if (!(rr_name = priv_getdns_owner_if_or_as_decompressed( - rr_iter, rr_name_space, &rr_name_len))) - continue; - - launch_chain_link_lookup(chain, rr_name); - } - } - chain->lock--; - getdns_context_clear_outbound_request(dns_req); - dns_req_free(dns_req); - callback_on_complete_chain(chain); -} - -static int -find_delegation_point(struct validation_chain *chain, uint8_t *dname) -{ - getdns_return_t r; - getdns_dict *extensions; - char name[1024]; - - if (!gldns_wire2str_dname_buf(dname, 256, name, sizeof(name))) - return GETDNS_RETURN_GENERIC_ERROR; - - if (!(extensions = getdns_dict_create_with_context( - chain->dns_req->context))) - return GETDNS_RETURN_MEMORY_ERROR; - - chain->lock++; - if (!(r = getdns_dict_set_int(extensions, - "dnssec_ok_checking_disabled", GETDNS_EXTENSION_TRUE))) - - r = priv_getdns_general_loop(chain->dns_req->context, - chain->dns_req->loop, name, GETDNS_RRTYPE_SOA, extensions, - chain, NULL, NULL, find_delegation_point_callback); - - getdns_dict_destroy(extensions); - if (r) - chain->lock--; - return r; -} - -static void -launch_chain_link_lookup( - struct validation_chain *chain, uint8_t *dname) -{ - int r; - struct chain_link *link; - char name[1024]; - - if (!gldns_wire2str_dname_buf(dname, 256, name, sizeof(name))) - return; - - if ((link = (struct chain_link *) - getdns_rbtree_search((getdns_rbtree_t *)&(chain->root), name))) - return; - - link = GETDNS_MALLOC(chain->mf, struct chain_link); - link->node.key = getdns_strdup(&chain->mf, name); - - chain_response_init(chain, &link->DNSKEY); - chain_response_init(chain, &link->DS); - - getdns_rbtree_insert(&(chain->root), (getdns_rbnode_t *)link); - - chain->lock++; - r = resolve(name, GETDNS_RRTYPE_DNSKEY, &link->DNSKEY); - if (r != 0) - link->DNSKEY.err = r; - - if (name[0] != '.' || name[1] != '\0') { - r = resolve(name, GETDNS_RRTYPE_DS, &link->DS); - if (r != 0) - link->DS.err = r; - } - chain->lock--; -} - -static struct validation_chain *create_chain(getdns_dns_req *dns_req, - uint64_t *timeout) -{ - struct validation_chain *chain = GETDNS_MALLOC( - dns_req->context->mf, struct validation_chain); - - if (! chain) - return NULL; - - getdns_rbtree_init(&(chain->root), - (int (*)(const void *, const void *)) strcmp); - chain->mf.mf_arg = dns_req->context->mf.mf_arg; - chain->mf.mf.ext.malloc = dns_req->context->mf.mf.ext.malloc; - chain->mf.mf.ext.realloc = dns_req->context->mf.mf.ext.realloc; - chain->mf.mf.ext.free = dns_req->context->mf.mf.ext.free; - chain->dns_req = dns_req; - chain->lock = 0; - chain->timeout = timeout; - return chain; -} - -static void destroy_chain_link(getdns_rbnode_t * node, void *arg) -{ - struct chain_link *link = (struct chain_link*) node; - struct validation_chain *chain = (struct validation_chain*) arg; - - free((void *)link->node.key); - - getdns_list_destroy(link->DNSKEY.result); - if (link->DNSKEY.dns_req) { - getdns_context_clear_outbound_request(link->DNSKEY.dns_req); - dns_req_free(link->DNSKEY.dns_req); - } - getdns_list_destroy(link->DS.result); - if (link->DS.dns_req) { - getdns_context_clear_outbound_request(link->DS.dns_req); - dns_req_free(link->DS.dns_req); - } - GETDNS_FREE(chain->mf, link); -} - -static void destroy_chain(struct validation_chain *chain) -{ - getdns_traverse_postorder(&(chain->root), destroy_chain_link, chain); - GETDNS_FREE(chain->mf, chain); -} - - -static int priv_getdns_dname_is_subdomain( - const uint8_t *subdomain, const uint8_t *domain) -{ - while (*domain) { - if (priv_getdns_dname_equal(subdomain, domain)) - return 1; - - domain += *domain + 1; - } - return *subdomain == 0; -} -/* Do some additional requests to fetch the complete validation chain */ -static void -getdns_get_validation_chain(getdns_dns_req *dns_req, uint64_t *timeout) -{ - getdns_network_req **netreq_p, *netreq; - struct validation_chain *chain = create_chain(dns_req, timeout); - priv_getdns_rr_iter rr_iter_storage, *rr_iter; - priv_getdns_rdf_iter rdf_storage, *rdf; - gldns_pkt_section section; - uint16_t rr_type; - priv_getdns_rr_iter rrsig_iter_storage, *rrsig_iter; - uint8_t rr_name_space[256], *rr_name; - uint8_t rrsig_name_space[256], *rrsig_name; - uint8_t sign_name_space[256], *sign_name; - size_t rr_name_len = sizeof(rr_name_space); - size_t rrsig_name_len = sizeof(rrsig_name_space); - size_t sign_name_len = sizeof(sign_name_space); - int rrsigs_found; - - if (! chain) { - priv_getdns_call_user_callback( - dns_req, create_getdns_response(dns_req)); - return; - } - chain->lock++; - for (netreq_p = dns_req->netreqs; (netreq = *netreq_p); netreq_p++) { - - for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage - , netreq->response - , netreq->response_len) - ; rr_iter - ; rr_iter = priv_getdns_rr_iter_next(rr_iter) - ) { - section = priv_getdns_rr_iter_section(rr_iter); - if (section != GLDNS_SECTION_ANSWER && - section != GLDNS_SECTION_AUTHORITY) - continue; - - /* Skip RRSIGs because we do only lookups for RRSIGS - * that have an rrset in the record too. - */ - rr_type = gldns_read_uint16(rr_iter->rr_type); - if (rr_type == GETDNS_RRTYPE_RRSIG) - continue; - - if (!(rr_name = priv_getdns_owner_if_or_as_decompressed( - rr_iter, rr_name_space, &rr_name_len))) - continue; - - rrsigs_found = 0; - for ( rrsig_iter = priv_getdns_rr_iter_init(&rrsig_iter_storage - , netreq->response - , netreq->response_len ) - ; rrsig_iter - ; rrsig_iter = priv_getdns_rr_iter_next(rrsig_iter) - ) { - section = priv_getdns_rr_iter_section(rrsig_iter); - if (section != GLDNS_SECTION_ANSWER && - section != GLDNS_SECTION_AUTHORITY) - continue; - - if (GETDNS_RRTYPE_RRSIG != - gldns_read_uint16(rrsig_iter->rr_type)) - continue; - - rdf = priv_getdns_rdf_iter_init(&rdf_storage - , rrsig_iter); - if (!rdf || gldns_read_uint16(rdf->pos) != rr_type) - continue; - - if (!(rrsig_name = priv_getdns_owner_if_or_as_decompressed( - rrsig_iter, rrsig_name_space, &rrsig_name_len))) - continue; - - if (!priv_getdns_dname_equal(rr_name, rrsig_name)) - continue; - - if (!(rdf = priv_getdns_rdf_iter_init_at( - &rdf_storage , rrsig_iter, 7))) - continue; - - if (!(sign_name = priv_getdns_rdf_if_or_as_decompressed( - rdf, sign_name_space, &sign_name_len))) - continue; - - if (!priv_getdns_dname_is_subdomain(sign_name, rr_name)) - continue; - - rrsigs_found++; - launch_chain_link_lookup(chain, sign_name); - } - if (rrsigs_found) - continue; - - find_delegation_point(chain, rr_name); - } - } - chain->lock--; - callback_on_complete_chain(chain); -} - - void priv_getdns_get_validation_chain(getdns_dns_req *dns_req) { get_val_chain(dns_req); - //getdns_get_validation_chain(dns_req, NULL); } /********************** functions for validate_dnssec *************************/ diff --git a/src/general.c b/src/general.c index 834785c3..ae6d609c 100644 --- a/src/general.c +++ b/src/general.c @@ -172,7 +172,7 @@ submit_network_request(getdns_network_req *netreq) static getdns_return_t getdns_general_ns(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions, - void *userarg, getdns_transaction_t *transaction_id, + void *userarg, getdns_dns_req **dnsreq_p, getdns_callback_t callbackfn, internal_cb_t internal_cb, int usenamespaces) { getdns_return_t r = GETDNS_RETURN_GOOD; @@ -203,8 +203,8 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, req->user_callback = callbackfn; req->internal_cb = internal_cb; - if (transaction_id) - *transaction_id = req->trans_id; + if (dnsreq_p) + *dnsreq_p = req; getdns_context_track_outbound_request(req); @@ -253,12 +253,12 @@ getdns_general_ns(getdns_context *context, getdns_eventloop *loop, getdns_return_t priv_getdns_general_loop(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions, - void *userarg, getdns_transaction_t *transaction_id, + void *userarg, getdns_dns_req **dnsreq_p, getdns_callback_t callback, internal_cb_t internal_cb) { return getdns_general_ns(context, loop, name, request_type, extensions, - userarg, transaction_id, callback, internal_cb, 0); + userarg, dnsreq_p, callback, internal_cb, 0); } /* getdns_general_loop */ @@ -270,6 +270,7 @@ priv_getdns_address_loop(getdns_context *context, getdns_eventloop *loop, getdns_dict *my_extensions = extensions; getdns_return_t r; uint32_t value; + getdns_dns_req *dnsreq = NULL; if (!my_extensions) { if (!(my_extensions=getdns_dict_create_with_context(context))) @@ -285,7 +286,9 @@ priv_getdns_address_loop(getdns_context *context, getdns_eventloop *loop, r = getdns_general_ns(context, loop, name, GETDNS_RRTYPE_AAAA, my_extensions, - userarg, transaction_id, callback, NULL, 1); + userarg, &dnsreq, callback, NULL, 1); + if (dnsreq && transaction_id) + *transaction_id = dnsreq->trans_id; if (my_extensions != extensions) getdns_dict_destroy(my_extensions); @@ -303,6 +306,7 @@ priv_getdns_hostname_loop(getdns_context *context, getdns_eventloop *loop, uint16_t req_type; char name[1024]; getdns_return_t retval; + getdns_dns_req *dnsreq = NULL; if ((retval = getdns_dict_get_bindata(address, "address_data", @@ -376,7 +380,9 @@ priv_getdns_hostname_loop(getdns_context *context, getdns_eventloop *loop, return GETDNS_RETURN_INVALID_PARAMETER; } retval = priv_getdns_general_loop(context, loop, name, req_type, - extensions, userarg, transaction_id, callback, NULL); + extensions, userarg, &dnsreq, callback, NULL); + if (dnsreq && transaction_id) + *transaction_id = dnsreq->trans_id; return retval; } /* getdns_hostname_loop */ @@ -385,8 +391,13 @@ priv_getdns_service_loop(getdns_context *context, getdns_eventloop *loop, const char *name, getdns_dict *extensions, void *userarg, getdns_transaction_t * transaction_id, getdns_callback_t callback) { - return getdns_general_ns(context, loop, name, GETDNS_RRTYPE_SRV, - extensions, userarg, transaction_id, callback, NULL, 1); + getdns_return_t r; + getdns_dns_req *dnsreq = NULL; + r = getdns_general_ns(context, loop, name, GETDNS_RRTYPE_SRV, + extensions, userarg, &dnsreq, callback, NULL, 1); + if (dnsreq && transaction_id) + *transaction_id = dnsreq->trans_id; + return r; } /* getdns_service_loop */ /** @@ -398,11 +409,16 @@ getdns_general(getdns_context *context, void *userarg, getdns_transaction_t * transaction_id, getdns_callback_t callback) { - if (!context) return GETDNS_RETURN_INVALID_PARAMETER; - return priv_getdns_general_loop(context, context->extension, - name, request_type, extensions, - userarg, transaction_id, callback, NULL); + getdns_return_t r; + getdns_dns_req *dnsreq = NULL; + if (!context) return GETDNS_RETURN_INVALID_PARAMETER; + r = priv_getdns_general_loop(context, context->extension, + name, request_type, extensions, + userarg, &dnsreq, callback, NULL); + if (dnsreq && transaction_id) + *transaction_id = dnsreq->trans_id; + return r; } /* getdns_general */ /* diff --git a/src/general.h b/src/general.h index 055f2f7e..94107e38 100644 --- a/src/general.h +++ b/src/general.h @@ -42,13 +42,13 @@ /* private inner helper used by sync and async */ -void priv_getdns_call_user_callback(getdns_dns_req *, struct getdns_dict *); +void priv_getdns_call_user_callback(getdns_dns_req *, getdns_dict *); void priv_getdns_check_dns_req_complete(getdns_dns_req *dns_req); getdns_return_t priv_getdns_general_loop(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions, - void *userarg, getdns_transaction_t *transaction_id, + void *userarg, getdns_dns_req **dnsreq, getdns_callback_t callbackfn, internal_cb_t internal_cb); getdns_return_t diff --git a/src/request-internal.c b/src/request-internal.c index b4267022..4ba94e05 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -40,6 +40,13 @@ #include "gldns/str2wire.h" #include "gldns/gbuffer.h" #include "gldns/pkthdr.h" +#include "dict.h" + +getdns_dict dnssec_ok_checking_disabled_spc = { + { RBTREE_NULL, 0, (int (*)(const void *, const void *)) strcmp }, + { 0 } +}; +getdns_dict *dnssec_ok_checking_disabled = &dnssec_ok_checking_disabled_spc; static int is_extension_set(getdns_dict *extensions, const char *extension) @@ -49,6 +56,8 @@ is_extension_set(getdns_dict *extensions, const char *extension) if (! extensions) return 0; + else if (extensions == dnssec_ok_checking_disabled) + return 0; r = getdns_dict_get_int(extensions, extension, &value); return r == GETDNS_RETURN_GOOD && value == GETDNS_EXTENSION_TRUE; @@ -209,14 +218,12 @@ dns_req_new(getdns_context *context, getdns_eventloop *loop, = is_extension_set(extensions, "dnssec_return_only_secure"); int dnssec_return_validation_chain = is_extension_set(extensions, "dnssec_return_validation_chain"); - int dnssec_ok_checking_disabled - = is_extension_set(extensions, "dnssec_ok_checking_disabled"); int edns_cookies = is_extension_set(extensions, "edns_cookies"); int dnssec_extension_set = dnssec_return_status || dnssec_return_only_secure || dnssec_return_validation_chain - || dnssec_ok_checking_disabled;; + || (extensions == dnssec_ok_checking_disabled); uint32_t edns_do_bit; int edns_maximum_udp_payload_size; @@ -251,6 +258,9 @@ dns_req_new(getdns_context *context, getdns_eventloop *loop, size_t max_query_sz, max_response_sz, netreq_sz, dnsreq_base_sz; uint8_t *region; + if (extensions == dnssec_ok_checking_disabled) + extensions = NULL; + have_add_opt_parameters = getdns_dict_get_dict(extensions, "add_opt_parameters", &add_opt_parameters) == GETDNS_RETURN_GOOD; diff --git a/src/types-internal.h b/src/types-internal.h index 53d9db2e..4c65c576 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -325,6 +325,8 @@ typedef struct getdns_dns_req { /* utility methods */ +extern getdns_dict *dnssec_ok_checking_disabled; + /* dns request utils */ getdns_dns_req *dns_req_new(getdns_context *context, getdns_eventloop *loop, const char *name, uint16_t request_type, getdns_dict *extensions); diff --git a/src/util-internal.c b/src/util-internal.c index d5a3f4ae..84c09646 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -61,7 +61,6 @@ static getdns_extension_format extformats[] = { {"add_opt_parameters", t_dict}, {"add_warning_for_bad_dns", t_int}, - {"dnssec_ok_checking_disabled", t_int}, {"dnssec_return_only_secure", t_int}, {"dnssec_return_status", t_int}, {"dnssec_return_validation_chain", t_int}, From 5c01df226c347167b4b81a369d675ee28effcf3b Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 23 Jun 2015 16:39:30 +0200 Subject: [PATCH 10/28] Init netreq dnssec status at netreq init time --- src/request-internal.c | 3 +++ src/stub.c | 8 -------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/request-internal.c b/src/request-internal.c index 4ba94e05..2a72b6c2 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -96,6 +96,9 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, net_req->state = NET_REQ_NOT_SENT; net_req->owner = owner; + net_req->secure = 0; + net_req->bogus = 0; + net_req->upstream = NULL; net_req->fd = -1; for (i = 0; i < GETDNS_BASE_TRANSPORT_MAX; i++) diff --git a/src/stub.c b/src/stub.c index 67aa4e82..48fc0bd9 100644 --- a/src/stub.c +++ b/src/stub.c @@ -1059,10 +1059,6 @@ stub_udp_read_cb(void *userarg) } netreq->response_len = read; dnsreq->upstreams->current = 0; - - /* TODO: DNSSEC */ - netreq->secure = 0; - netreq->bogus = 0; done: netreq->state = NET_REQ_FINISHED; priv_getdns_check_dns_req_complete(dnsreq); @@ -1141,10 +1137,6 @@ stub_tcp_read_cb(void *userarg) netreq->tcp.read_buf = NULL; dnsreq->upstreams->current = 0; - /* TODO: DNSSEC */ - netreq->secure = 0; - netreq->bogus = 0; - stub_cleanup(netreq); close(netreq->fd); priv_getdns_check_dns_req_complete(dnsreq); From 1babc715b73f41e5eb03df517ea8f7d926d123d6 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 23 Jun 2015 16:40:47 +0200 Subject: [PATCH 11/28] Init context->dnssec_trust_anchors with default --- src/context.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/context.c b/src/context.c index 034f3ca6..dedfbbe8 100644 --- a/src/context.c +++ b/src/context.c @@ -838,7 +838,6 @@ getdns_context_create_with_extended_memory_functions( result->dns_transport = GETDNS_TRANSPORT_UDP_FIRST_AND_FALL_BACK_TO_TCP; priv_set_base_dns_transports(result->dns_base_transports, result->dns_transport); result->limit_outstanding_queries = 0; - result->has_ta = priv_getdns_parse_ta_file(NULL, NULL); result->return_dnssec_status = GETDNS_EXTENSION_FALSE; /* unbound context is initialized here */ @@ -850,7 +849,13 @@ getdns_context_create_with_extended_memory_functions( goto error; create_local_hosts(result); + if (! (result->dnssec_trust_anchors = + getdns_list_create_with_context(result))) + goto error; + result->has_ta = priv_getdns_parse_ta_file( + NULL, result->dnssec_trust_anchors); + *context = result; return GETDNS_RETURN_GOOD; error: From c7c7884350ee651127a97dc264b730b81e56a470 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 23 Jun 2015 16:41:34 +0200 Subject: [PATCH 12/28] Generalize getdns_rrset for raw pkt, not netreq --- src/dnssec.c | 206 +++++++++++++++++++++++++++++++-------------------- 1 file changed, 125 insertions(+), 81 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 013ccbbe..3695bdab 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -152,11 +152,12 @@ static priv_getdns_rr_iter *rr_iter_rrsig_covering(priv_getdns_rr_iter *rr, } typedef struct getdns_rrset { - uint8_t *name; - uint16_t rr_class; - uint16_t rr_type; - getdns_network_req *netreq; - uint8_t name_spc[]; + uint8_t *name; + uint16_t rr_class; + uint16_t rr_type; + uint8_t *pkt; + size_t pkt_len; + uint8_t name_spc[]; } getdns_rrset; typedef struct rrtype_iter { @@ -180,8 +181,7 @@ static rrtype_iter *rrtype_iter_init(rrtype_iter *i, getdns_rrset *rrset) { i->rrset = rrset; return (rrtype_iter *) rr_iter_name_class_type( - priv_getdns_rr_iter_init(&i->rr_i, rrset->netreq->response - , rrset->netreq->response_len), + priv_getdns_rr_iter_init(&i->rr_i, rrset->pkt, rrset->pkt_len ), i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); } @@ -196,8 +196,7 @@ static rrsig_iter *rrsig_iter_init(rrsig_iter *i, getdns_rrset *rrset) { i->rrset = rrset; return (rrsig_iter *) rr_iter_rrsig_covering( - priv_getdns_rr_iter_init(&i->rr_i, rrset->netreq->response - , rrset->netreq->response_len), + priv_getdns_rr_iter_init(&i->rr_i, rrset->pkt, rrset->pkt_len), i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); } @@ -256,8 +255,6 @@ static void debug_sec_print_rrset(const char *msg, getdns_rrset *rrset) #define debug_sec_print_rrset(...) DEBUG_OFF(__VA_ARGS__) #endif - - typedef struct rrset_iter rrset_iter; struct rrset_iter { getdns_rrset rrset; @@ -266,17 +263,16 @@ struct rrset_iter { priv_getdns_rr_iter rr_i; }; -static rrset_iter *rrset_iter_init(rrset_iter *i, getdns_network_req *netreq) +static rrset_iter *rrset_iter_init(rrset_iter *i, uint8_t *pkt, size_t pkt_len) { priv_getdns_rr_iter *rr; i->rrset.name = i->name_spc; - i->rrset.netreq = netreq; + i->rrset.pkt = pkt; + i->rrset.pkt_len = pkt_len; i->name_len = 0; - for ( rr = priv_getdns_rr_iter_init(&i->rr_i - , netreq->response - , netreq->response_len) + for ( rr = priv_getdns_rr_iter_init(&i->rr_i, pkt, pkt_len) ;(rr = rr_iter_ansauth(rr)) ; rr = priv_getdns_rr_iter_next(rr)) { @@ -295,7 +291,6 @@ static rrset_iter *rrset_iter_init(rrset_iter *i, getdns_network_req *netreq) return NULL; } - static rrset_iter *rrset_iter_next(rrset_iter *i) { priv_getdns_rr_iter *rr; @@ -325,30 +320,58 @@ static getdns_rrset *rrset_iter_value(rrset_iter *i) return &i->rrset; } +/* ------------------------------------------------------------------------- */ + typedef struct chain_head chain_head; typedef struct chain_node chain_node; struct chain_head { struct mem_funcs my_mf; - chain_head *next; - chain_node *parent; - size_t node_count; /* Number of nodes attached directly - * to this head. For cleaning. */ - getdns_rrset rrset; - uint8_t name_spc[]; + chain_head *next; + chain_node *parent; + size_t node_count; /* Number of nodes attached directly + * to this head. For cleaning. */ + getdns_network_req *netreq; + getdns_rrset rrset; + uint8_t name_spc[]; }; struct chain_node { chain_node *parent; - getdns_rrset dnskey; - getdns_rrset ds ; - getdns_rrset soa ; + getdns_network_req *dnskey_req; + getdns_rrset dnskey; + getdns_network_req *ds_req; + getdns_rrset ds; + getdns_network_req *soa_req; chain_head *chains; }; +#ifdef STUB_NATIVE_DNSSEC +static int chain_head_validate_dnssec(chain_head *head, getdns_list *tas) +{ + return GETDNS_DNSSEC_INSECURE; +} + +static void chain_validate_dnssec(chain_head *chain) +{ + chain_head *head; + getdns_list *tas = + chain->netreq->owner->context->dnssec_trust_anchors; + + for (head = chain; head; head = head->next) { + switch (chain_head_validate_dnssec(head, tas)) { + case GETDNS_DNSSEC_SECURE: break; + case GETDNS_DNSSEC_BOGUS : head->netreq->bogus = 1; + default : continue; + } + /* TODO: Validate head->rrset */ + } +} +#endif + static size_t count_outstanding_requests(chain_head *head) { size_t count; @@ -361,19 +384,19 @@ static size_t count_outstanding_requests(chain_head *head) ; node ; node = node->parent) { - if (node->dnskey.netreq && - node->dnskey.netreq->state != NET_REQ_FINISHED && - node->dnskey.netreq->state != NET_REQ_CANCELED) + if (node->dnskey_req && + node->dnskey_req->state != NET_REQ_FINISHED && + node->dnskey_req->state != NET_REQ_CANCELED) count++; - if (node->ds.netreq && - node->ds.netreq->state != NET_REQ_FINISHED && - node->ds.netreq->state != NET_REQ_CANCELED) + if (node->ds_req && + node->ds_req->state != NET_REQ_FINISHED && + node->ds_req->state != NET_REQ_CANCELED) count++; - if (node->soa.netreq && - node->soa.netreq->state != NET_REQ_FINISHED && - node->soa.netreq->state != NET_REQ_CANCELED) + if (node->soa_req && + node->soa_req->state != NET_REQ_FINISHED && + node->soa_req->state != NET_REQ_CANCELED) count++; } return count + count_outstanding_requests(head->next); @@ -388,7 +411,10 @@ static void append_rrs2val_chain_list(getdns_context *ctxt, rrsig_iter *rrsig, rrsig_spc; getdns_dict *rr_dict; - for (i = rrset_iter_init(&i_spc, netreq); i; i = rrset_iter_next(i)) { + for ( i = rrset_iter_init(&i_spc,netreq->response,netreq->response_len) + ; i + ; i = rrset_iter_next(i)) { + rrset = rrset_iter_value(i); if (rrset->rr_type != GETDNS_RRTYPE_DNSKEY && @@ -432,28 +458,31 @@ static void check_chain_complete(chain_head *chain) return; } DEBUG_SEC("Chain done!\n"); - dnsreq = chain->rrset.netreq->owner; - /* TODO: DNSSEC validation of netreq! */ + dnsreq = chain->netreq->owner; + +#ifdef STUB_NATIVE_DNSSEC + chain_validate_dnssec(chain); +#endif val_chain_list = dnsreq->dnssec_return_validation_chain ? getdns_list_create_with_context(dnsreq->context) : NULL; - /* Cleanup the chain */ + /* Walk chain to add values to val_chain_list and to cleanup */ for ( head = chain; head ; head = next ) { next = head->next; for ( node_count = head->node_count, node = head->parent ; node_count ; node_count--, node = node->parent ) { - if (node->dnskey.netreq) { + if (node->dnskey_req) { append_rrs2val_chain_list(dnsreq->context, - val_chain_list, node->dnskey.netreq); - dns_req_free(node->dnskey.netreq->owner); + val_chain_list, node->dnskey_req); + dns_req_free(node->dnskey_req->owner); } - if (node->ds.netreq) { + if (node->ds_req) { append_rrs2val_chain_list(dnsreq->context, - val_chain_list, node->ds.netreq); - dns_req_free(node->ds.netreq->owner); + val_chain_list, node->ds_req); + dns_req_free(node->ds_req->owner); } } GETDNS_FREE(head->my_mf, head); @@ -477,18 +506,18 @@ static void val_chain_sched_soa_node(chain_node *node) getdns_dns_req *dnsreq; char name[1024]; - context = node->chains->rrset.netreq->owner->context; - loop = node->chains->rrset.netreq->owner->loop; + context = node->chains->netreq->owner->context; + loop = node->chains->netreq->owner->loop; - if (!gldns_wire2str_dname_buf(node->soa.name, 256, name, sizeof(name))) + if (!gldns_wire2str_dname_buf(node->ds.name, 256, name, sizeof(name))) return; - if (! node->soa.netreq && + if (! node->soa_req && ! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_SOA, dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_soa_cb)) - node->soa.netreq = *dnsreq->netreqs; + node->soa_req = dnsreq->netreqs[0]; } static void val_chain_sched_soa(chain_head *head, uint8_t *dname) @@ -499,7 +528,7 @@ static void val_chain_sched_soa(chain_head *head, uint8_t *dname) return; for ( node = head->parent - ; node && !priv_getdns_dname_equal(dname, node->dnskey.name) + ; node && !priv_getdns_dname_equal(dname, node->ds.name) ; node = node->parent); if (node) @@ -514,25 +543,25 @@ static void val_chain_sched_node(chain_node *node) getdns_dns_req *dnsreq; char name[1024]; - context = node->chains->rrset.netreq->owner->context; - loop = node->chains->rrset.netreq->owner->loop; + context = node->chains->netreq->owner->context; + loop = node->chains->netreq->owner->loop; - if (!gldns_wire2str_dname_buf(node->dnskey.name, 256, name, sizeof(name))) + if (!gldns_wire2str_dname_buf(node->ds.name, 256, name, sizeof(name))) return; DEBUG_SEC("schedule DS & DNSKEY lookup for %s\n", name); - if (! node->dnskey.netreq /* not scheduled */ && + if (! node->dnskey_req /* not scheduled */ && ! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DNSKEY, dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb)) - node->dnskey.netreq = *dnsreq->netreqs; + node->dnskey_req = dnsreq->netreqs[0]; - if (! node->ds.netreq && node->parent /* not root */ && + if (! node->ds_req && node->parent /* not root */ && ! priv_getdns_general_loop(context, loop, name, GETDNS_RRTYPE_DS, dnssec_ok_checking_disabled, node, &dnsreq, NULL, val_chain_node_cb)) - node->ds.netreq = *dnsreq->netreqs; + node->ds_req = dnsreq->netreqs[0]; } static void val_chain_sched(chain_head *head, uint8_t *dname) @@ -540,7 +569,7 @@ static void val_chain_sched(chain_head *head, uint8_t *dname) chain_node *node; for ( node = head->parent - ; node && !priv_getdns_dname_equal(dname, node->dnskey.name) + ; node && !priv_getdns_dname_equal(dname, node->ds.name) ; node = node->parent); if (node) val_chain_sched_node(node); @@ -559,7 +588,7 @@ static void val_chain_sched_signer_node(chain_node *node, rrsig_iter *rrsig) rdf, signer_spc, &signer_len))) return; - while (node && !priv_getdns_dname_equal(signer, node->dnskey.name)) + while (node && !priv_getdns_dname_equal(signer, node->ds.name)) node = node->parent; if (node) val_chain_sched_node(node); @@ -580,12 +609,18 @@ static void val_chain_node_cb(getdns_dns_req *dnsreq) getdns_context_clear_outbound_request(dnsreq); switch (netreq->request_type) { - case GETDNS_RRTYPE_DS : break; - case GETDNS_RRTYPE_DNSKEY: node->dnskey.netreq = netreq; + case GETDNS_RRTYPE_DS : node->ds.pkt = netreq->response; + node->ds.pkt_len = netreq->response_len; + break; + case GETDNS_RRTYPE_DNSKEY: node->dnskey.pkt = netreq->response; + node->dnskey.pkt_len = netreq->response_len; default : check_chain_complete(node->chains); return; } - for (i = rrset_iter_init(&i_spc, netreq); i; i = rrset_iter_next(i)) { + for ( i = rrset_iter_init(&i_spc,netreq->response,netreq->response_len) + ; i + ; i = rrset_iter_next(i)) { + rrset = rrset_iter_value(i); if (rrset->rr_type != GETDNS_RRTYPE_DS && @@ -608,9 +643,12 @@ static getdns_rrset *rrset_by_type( rrset_iter *i; getdns_rrset *rrset; - for (i = rrset_iter_init(i_spc, netreq); i; i = rrset_iter_next(i)) { + for ( i = rrset_iter_init(i_spc,netreq->response,netreq->response_len) + ; i + ; i = rrset_iter_next(i)) { + rrset = rrset_iter_value(i); - if (rrset->rr_type == rr_type) + if (rrset->rr_type == rr_type) /* Check class too? */ return rrset; } return NULL; @@ -628,7 +666,7 @@ static void val_chain_node_soa_cb(getdns_dns_req *dnsreq) if ((rrset = rrset_by_type(&i_spc, netreq, GETDNS_RRTYPE_SOA))) { while (node && - ! priv_getdns_dname_equal(node->soa.name, rrset->name)) + ! priv_getdns_dname_equal(node->ds.name, rrset->name)) node = node->parent; val_chain_sched_node(node); @@ -658,8 +696,8 @@ static uint8_t **reverse_labels(uint8_t *dname, uint8_t **labels) return labels + 1; } -static chain_head *add_rrset2val_chain( - struct mem_funcs *mf, chain_head **chain_p, getdns_rrset *rrset) +static chain_head *add_rrset2val_chain(struct mem_funcs *mf, + chain_head **chain_p, getdns_rrset *rrset, getdns_network_req *netreq) { chain_head *head; uint8_t *labels[128], **last_label, **label; @@ -731,7 +769,9 @@ static chain_head *add_rrset2val_chain( memcpy(head->name_spc, rrset->name, dname_len); head->rrset.rr_class = rrset->rr_class; head->rrset.rr_type = rrset->rr_type; - head->rrset.netreq = rrset->netreq; + head->rrset.pkt = rrset->pkt; + head->rrset.pkt_len = rrset->pkt_len; + head->netreq = netreq; head->node_count = node_count; if (!node_count) { @@ -748,21 +788,21 @@ static chain_head *add_rrset2val_chain( ; node_count ; node_count--, node = node->parent =&node[1], dname += *dname + 1) { - node->chains = *chain_p; node->ds.name = dname; node->dnskey.name = dname; - node->soa.name = dname; node->ds.rr_class = head->rrset.rr_class; node->dnskey.rr_class = head->rrset.rr_class; - node->soa.rr_class = head->rrset.rr_class; node->ds.rr_type = GETDNS_RRTYPE_DS; node->dnskey.rr_type = GETDNS_RRTYPE_DNSKEY; - node->soa.rr_type = GETDNS_RRTYPE_SOA; - node->ds.netreq = NULL; - node->dnskey.netreq = NULL; - node->soa.netreq = NULL; + node->ds.pkt = NULL; + node->ds.pkt_len = 0; + node->dnskey.pkt = NULL; + node->dnskey.pkt_len = 0; + node->ds_req = NULL; + node->dnskey_req = NULL; + node->soa_req = NULL; - node->soa.rr_class = head->rrset.rr_class; + node->chains = *chain_p; } /* On the first chain, max_node == NULL. * Schedule a root DNSKEY query, we always need that. @@ -801,17 +841,21 @@ static void add_netreq2val_chain( empty_rrset.name = netreq->query + GLDNS_HEADER_SIZE; empty_rrset.rr_class = GETDNS_RRCLASS_IN; empty_rrset.rr_type = 0; - empty_rrset.netreq = netreq; + empty_rrset.pkt = netreq->response; + empty_rrset.pkt_len = netreq->response_len; - head = add_rrset2val_chain(mf, chain_p, &empty_rrset); + head = add_rrset2val_chain(mf, chain_p, &empty_rrset, netreq); val_chain_sched_soa(head, empty_rrset.name); return; } - for (i = rrset_iter_init(&i_spc, netreq); i; i = rrset_iter_next(i)) { + for ( i = rrset_iter_init(&i_spc,netreq->response,netreq->response_len) + ; i + ; i = rrset_iter_next(i)) { + rrset = rrset_iter_value(i); debug_sec_print_rrset("rrset: ", rrset); - head = add_rrset2val_chain(mf, chain_p, rrset); + head = add_rrset2val_chain(mf, chain_p, rrset, netreq); for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset), n_rrsigs = 0 ; rrsig ; rrsig = rrsig_iter_next(rrsig), n_rrsigs++) { From ea69d30e6499dba733e15d8ee3545f9ccbbd2c14 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Thu, 25 Jun 2015 10:04:19 +0200 Subject: [PATCH 13/28] Validation of signed responses + start with unsigned responses (only the NSEC NOERROR case) --- src/dnssec.c | 554 ++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 527 insertions(+), 27 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 3695bdab..fc7f38dc 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -49,9 +49,75 @@ #include "rr-dict.h" #include "gldns/str2wire.h" #include "gldns/wire2str.h" +#include "gldns/keyraw.h" #include "general.h" #include "dict.h" +/* subdomain is same or parent of domain */ +static int is_subdomain( + const uint8_t * const subdomain, const uint8_t *domain) +{ + while (*domain) { + if (priv_getdns_dname_equal(subdomain, domain)) + return 1; + + domain += *domain + 1; + } + return *subdomain == 0; +} + +static void _getdns_list2wire(gldns_buffer *buf, getdns_list *l) +{ + getdns_dict *rr_dict; + getdns_return_t r; + size_t i, pkt_start, ancount; + uint32_t qtype, qclass; + getdns_bindata *qname; + + pkt_start = gldns_buffer_position(buf); + /* Empty header */ + gldns_buffer_write_u32(buf, 0); + gldns_buffer_write_u32(buf, 0); + gldns_buffer_write_u32(buf, 0); + + for ( i = 0 + ; (r = getdns_list_get_dict(l, i, &rr_dict)) + != GETDNS_RETURN_NO_SUCH_LIST_ITEM + ; i++ ) { + + if (r) { + if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED) + continue; + else + break; + } + if (getdns_dict_get_int(rr_dict, "qtype", &qtype) || + getdns_dict_get_bindata(rr_dict, "qname", &qname)) + continue; + (void) getdns_dict_get_int(rr_dict, "qclass", &qclass); + gldns_buffer_write(buf, qname->data, qname->size); + gldns_buffer_write_u16(buf, (uint16_t)qtype); + gldns_buffer_write_u16(buf, (uint16_t)qclass); + gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, 1); + break; + } + for ( i = 0, ancount = 0 + ; (r = getdns_list_get_dict(l, i, &rr_dict)) + != GETDNS_RETURN_NO_SUCH_LIST_ITEM + ; i++ ) { + + if (r) { + if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED) + continue; + else + break; + } + if (priv_getdns_rr_dict2wire(rr_dict, buf) == GETDNS_RETURN_GOOD) + ancount++; + } + gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_ANCOUNT_OFF, ancount); +} + #if defined(SEC_DEBUG) && SEC_DEBUG inline static void debug_sec_print_rr(const char *msg, priv_getdns_rr_iter *rr) { @@ -77,12 +143,18 @@ inline static void debug_sec_print_dname(const char *msg, uint8_t *label) else DEBUG_SEC("%s\n", msg); } +inline static void debug_sec_print_pkt(const char *msg, uint8_t *pkt, size_t pkt_len) +{ + char *str; + DEBUG_SEC("%s%s\n", msg, (str = gldns_wire2str_pkt(pkt, pkt_len))); + if (str) free(str); +} #else #define debug_sec_print_rr(...) DEBUG_OFF(__VA_ARGS__) #define debug_sec_print_dname(...) DEBUG_OFF(__VA_ARGS__) +#define debug_sec_print_pkt(...) DEBUG_OFF(__VA_ARGS__) #endif - static inline uint16_t rr_iter_type(priv_getdns_rr_iter *rr) { return rr->rr_type + 2 <= rr->nxt ? gldns_read_uint16(rr->rr_type) : 0; } static inline uint16_t rr_iter_class(priv_getdns_rr_iter *rr) @@ -291,6 +363,11 @@ static rrset_iter *rrset_iter_init(rrset_iter *i, uint8_t *pkt, size_t pkt_len) return NULL; } +static rrset_iter *rrset_iter_rewind(rrset_iter *i) +{ + return rrset_iter_init(i, i->rrset.pkt, i->rrset.pkt_len); +} + static rrset_iter *rrset_iter_next(rrset_iter *i) { priv_getdns_rr_iter *rr; @@ -350,25 +427,431 @@ struct chain_node { }; #ifdef STUB_NATIVE_DNSSEC -static int chain_head_validate_dnssec(chain_head *head, getdns_list *tas) + + +static int key_matches_signer(getdns_rrset *dnskey, getdns_rrset *rrset) { - return GETDNS_DNSSEC_INSECURE; + rrtype_iter rr_spc, *rr; + rrsig_iter rrsig_spc, *rrsig; + uint16_t keytag; + + assert(dnskey->rr_type == GETDNS_RRTYPE_DNSKEY); + + + for ( rr = rrtype_iter_init(&rr_spc, dnskey) + ; rr ; rr = rrtype_iter_next(rr) ) { + + keytag = gldns_calc_keytag_raw(rr->rr_i.rr_type + 10, + rr->rr_i.nxt - rr->rr_i.rr_type - 10); + + for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset) + ; rrsig ; rrsig = rrsig_iter_next(rrsig) ) { + + if (/* Space for keytag & signer in rrsig rdata? */ + rrsig->rr_i.nxt >= rrsig->rr_i.rr_type + 29 && + + /* Does the keytag match? */ + gldns_read_uint16(rrsig->rr_i.rr_type + 26) + == keytag && + + /* Does the signer name match? */ + priv_getdns_dname_equal(dnskey->name, + rrsig->rr_i.rr_type + 28)) + + return 1; + } + } + return 0; } -static void chain_validate_dnssec(chain_head *chain) +static ldns_rr *rr2ldns_rr(priv_getdns_rr_iter *rr) +{ + ldns_rr *rr_l; + size_t pos = rr->pos - rr->pkt; + + if (ldns_wire2rr(&rr_l, rr->pkt, rr->pkt_end - rr->pkt, &pos, + priv_getdns_rr_iter_section(rr)) == LDNS_STATUS_OK) + return rr_l; + else + return NULL; +} + +static ldns_rr_list *rrset2ldns_rr_list(getdns_rrset *rrset) +{ + rrtype_iter rr_spc, *rr; + ldns_rr_list *rr_list = ldns_rr_list_new(); + ldns_rr *rr_l; + + if (rr_list) { + for ( rr = rrtype_iter_init(&rr_spc, rrset) + ; rr ; rr = rrtype_iter_next(rr) ) + if ((rr_l = rr2ldns_rr(&rr->rr_i))) + ldns_rr_list_push_rr(rr_list, rr_l); + } + return rr_list; +} + +static int _getdns_verify_rrsig(getdns_rrset *rrset, rrsig_iter *rrsig, rrtype_iter *key) +{ + ldns_rr_list *rrset_l = rrset2ldns_rr_list(rrset); + ldns_rr *rrsig_l = rr2ldns_rr(&rrsig->rr_i); + ldns_rr *key_l = rr2ldns_rr(&key->rr_i); + int r; + /* + fprintf(stderr, "rrsig: " ); ldns_rr_print(stderr, rrsig_l); fprintf(stderr, "\n"); + fprintf(stderr, "dnskey: "); ldns_rr_print(stderr, key_l); fprintf(stderr, "\n"); + fprintf(stderr, "rrset: " ); ldns_rr_list_print(stderr, rrset_l); fprintf(stderr, "\n"); + */ + r = rrset_l && rrsig_l && key_l && ldns_verify_rrsig(rrset_l, rrsig_l, key_l) == LDNS_STATUS_OK; + ldns_rr_list_deep_free(rrset_l); + ldns_rr_free(rrsig_l); + ldns_rr_free(key_l); + return r; +} + +static int dnskey_signed_rrset(rrtype_iter *dnskey, getdns_rrset *rrset) +{ + rrsig_iter rrsig_spc, *rrsig; + uint16_t keytag; + + assert(dnskey->rrset->rr_type == GETDNS_RRTYPE_DNSKEY); + + keytag = gldns_calc_keytag_raw(dnskey->rr_i.rr_type + 10, + dnskey->rr_i.nxt - dnskey->rr_i.rr_type - 10); + + for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset) + ; rrsig ; rrsig = rrsig_iter_next(rrsig) ) { + + if (/* Space for keytag & signer in rrsig rdata? */ + rrsig->rr_i.nxt >= rrsig->rr_i.rr_type + 29 && + + /* Does the keytag match? */ + gldns_read_uint16(rrsig->rr_i.rr_type + 26) + == keytag && + + /* Does the signer name match? */ + priv_getdns_dname_equal(dnskey->rrset->name, + rrsig->rr_i.rr_type + 28) && + + /* Does the signature verify? */ + _getdns_verify_rrsig(rrset, rrsig, dnskey)) { + + debug_sec_print_rr("key ", &dnskey->rr_i); + debug_sec_print_rrset("signed ", rrset); + + return 1; + } + } + return 0; +} + +static int a_key_signed_rrset(getdns_rrset *keyset, getdns_rrset *rrset) +{ + rrtype_iter dnskey_spc, *dnskey; + + assert(keyset->rr_type == GETDNS_RRTYPE_DNSKEY); + + for ( dnskey = rrtype_iter_init(&dnskey_spc, keyset) + ; dnskey ; dnskey = rrtype_iter_next(dnskey) ) { + + if (dnskey_signed_rrset(dnskey, rrset)) + return 1; + } + return 0; +} + +static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set) +{ + rrtype_iter dnskey_spc, *dnskey; + rrtype_iter ds_spc, *ds; + uint16_t keytag; + ldns_rr *dnskey_l; + ldns_rr *ds_l = NULL; + + assert(ds_set->rr_type == GETDNS_RRTYPE_DS); + assert(dnskey_set->rr_type == GETDNS_RRTYPE_DNSKEY); + + if (!priv_getdns_dname_equal(ds_set->name, dnskey_set->name)) + return 0; + + for ( dnskey = rrtype_iter_init(&dnskey_spc, dnskey_set) + ; dnskey ; dnskey = rrtype_iter_next(dnskey)) { + + keytag = gldns_calc_keytag_raw(dnskey->rr_i.rr_type + 10, + dnskey->rr_i.nxt - dnskey->rr_i.rr_type - 10); + + dnskey_l = NULL; + + for ( ds = rrtype_iter_init(&ds_spc, ds_set) + ; ds ; ds = rrtype_iter_next(ds)) { + + if (/* Space for keytag & signer in rrsig rdata? */ + ds->rr_i.nxt >= ds->rr_i.rr_type + 12 && + + /* Does the keytag match? */ + gldns_read_uint16(ds->rr_i.rr_type + 10) + == keytag) { + + if (!dnskey_l) + dnskey_l = rr2ldns_rr(&dnskey->rr_i); + if (dnskey_l && (ds_l = rr2ldns_rr(&ds->rr_i)) && + ldns_rr_compare_ds(ds_l, dnskey_l)) { + + debug_sec_print_rr("this DS: ", &ds->rr_i); + debug_sec_print_rr("matched DNSKEY: ", &dnskey->rr_i); + + ldns_rr_free(dnskey_l); + ldns_rr_free(ds_l); + + if (dnskey_signed_rrset(dnskey, dnskey_set)) { + debug_sec_print_rrset("which signed: " + , dnskey_set); + return 1; + } else { + debug_sec_print_rrset("which did not sign: " + , dnskey_set); + } + } + } + if (ds_l) + ldns_rr_free(ds_l); + } + ldns_rr_free(dnskey_l); + } + return 0; +} + +#if 0 + uint8_t *name = rrset->name, cname_spc[256], *cname = rrset->name; + size_t anti_loop = 1000, cname_len = sizeof(cname_spc); + priv_getdns_rdf_iter rdf_spc, *rdf; + + /* First, try to find the canonical name that is denied. + * CNAMEs authentication is checked with their own chain_head + * entry points, so no need to check signatures here. + */ + while (anti_loop--) { + for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) + ; i + ; i = rrset_iter_next(i)) { + + if (i->rrset.rr_type != GETDNS_RRTYPE_CNAME) + continue; + + if (priv_getdns_dname_equal(name, i->rrset.name) && + (rdf = priv_getdns_rdf_iter_init( + &rdf_spc, &i->rr_i)) && + (cname = priv_getdns_rdf_if_or_as_decompressed( + rdf, cname_spc, &cname_len))) + break; + } + if (priv_getdns_dname_equal(name, cname)) + break; + + name = cname; + } +#endif + +static int bitmap_contains_rrtype(priv_getdns_rdf_iter *bitmap, uint16_t rr_type) +{ + uint8_t *dptr, *dend; + uint8_t window = rr_type >> 8; + uint8_t subtype = rr_type & 0xFF; + + if (!bitmap) + return 0; + dptr = bitmap->pos; + dend = bitmap->nxt; + + /* Type Bitmap = ( Window Block # | Bitmap Length | Bitmap ) + + * dptr[0] dptr[1] dptr[2:] + */ + while (dptr < dend && dptr[0] <= window) { + if (dptr[0] == window && subtype / 8 < dptr[1] && + dptr + dptr[1] + 2 <= dend) + return dptr[2 + subtype / 8] & (0x80 >> (subtype % 8)); + dptr += dptr[1] + 2; /* next window */ + } + return 0; +} + +static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) +{ + rrset_iter i_spc, *i; + size_t n_nsecs = 0, n_nsec3s = 0; + getdns_rrset nsecs[2], nsec3s[3]; + rrtype_iter nsec_space, *nsec; + priv_getdns_rdf_iter bitmap_spc, *bitmap; + + size_t j; + + assert(dnskey->rr_type == GETDNS_RRTYPE_DNSKEY); + + /* First collect nsecs and nsec3s */ + for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) + ; i + ; i = rrset_iter_next(i)) { + + if (i->rrset.rr_type == GETDNS_RRTYPE_NSEC) { + + nsecs[n_nsecs] = i->rrset; + if (++n_nsecs >= sizeof(nsecs)) + break; + + } else if (i->rrset.rr_type == GETDNS_RRTYPE_NSEC3) { + + nsecs[n_nsecs] = i->rrset; + if (++n_nsec3s >= sizeof(nsec3s)) + break; + } + } + if (!n_nsecs && !n_nsec3s) + return 0; + + /* We assume rrset already contains the canonical name */ + if (n_nsecs) { + /* The NOERROR case */ + for (j = 0; j < n_nsecs; j++) { + if (priv_getdns_dname_equal(rrset->name, nsecs[j].name)) + return (nsec = rrtype_iter_init( + &nsec_space, &nsecs[j])) && + + (bitmap = priv_getdns_rdf_iter_init_at( + &bitmap_spc, &nsec->rr_i, 1)) && + + !bitmap_contains_rrtype( + bitmap, rrset->rr_type) && + + a_key_signed_rrset(dnskey, &nsecs[j]); + } + /* The NXDOMAIN case */ + /* One should cover the name */ + /* One should cover the wildcard */ + } + /* TODO: proof nsec3 non existance */ + return 0; +} + +static int chain_node_get_trusted_keys( + chain_node *node, getdns_rrset *ta, getdns_rrset **keys) +{ + int s; + + /* Descend down to the root */ + if (! node) + return GETDNS_DNSSEC_BOGUS; + + else if (ta->rr_type == GETDNS_RRTYPE_DS) { + + if (ds_authenticates_keys(&node->ds, &node->dnskey)) { + *keys = &node->dnskey; + return GETDNS_DNSSEC_SECURE; + } + + } else if (ta->rr_type == GETDNS_RRTYPE_DNSKEY) { + + /* ta is KSK */ + if (a_key_signed_rrset(ta, &node->dnskey)) { + *keys = &node->dnskey; + return GETDNS_DNSSEC_SECURE; + } + /* ta is ZSK */ + if (key_proves_nonexistance(ta, &node->ds)) + return GETDNS_DNSSEC_INSECURE; + + if (a_key_signed_rrset(ta, &node->ds)) { + if (ds_authenticates_keys(&node->ds, &node->dnskey)) { + *keys = &node->dnskey; + return GETDNS_DNSSEC_SECURE; + } + return GETDNS_DNSSEC_BOGUS; + } + } else + return GETDNS_DNSSEC_BOGUS; + + if (GETDNS_DNSSEC_SECURE != + (s = chain_node_get_trusted_keys(node->parent, ta, keys))) + return s; + + /* keys is an authenticated dnskey rrset always now (i.e. ZSK) */ + ta = *keys; + /* Back up to the head */ + if (key_proves_nonexistance(ta, &node->ds)) + return GETDNS_DNSSEC_INSECURE; + + if (key_matches_signer(ta, &node->ds)) { + if (a_key_signed_rrset(ta, &node->ds) && + ds_authenticates_keys(&node->ds, &node->dnskey)) { + + *keys = &node->dnskey; + return GETDNS_DNSSEC_SECURE; + } + return GETDNS_DNSSEC_BOGUS; + } + return GETDNS_DNSSEC_SECURE; +} + +static int chain_head_validate_with_ta(chain_head *head, getdns_rrset *ta) +{ + getdns_rrset *keys; + int s; + + if ((s = chain_node_get_trusted_keys(head->parent, ta, &keys)) + != GETDNS_DNSSEC_SECURE) + return s; + + if (a_key_signed_rrset(keys, &head->rrset)) + return GETDNS_DNSSEC_SECURE; + else + return GETDNS_DNSSEC_BOGUS; +} + +static int chain_head_validate(chain_head *head, rrset_iter *tas) +{ + rrset_iter *i; + getdns_rrset *ta; + int s = GETDNS_DNSSEC_INDETERMINATE; + + for (i = rrset_iter_rewind(tas); i ;i = rrset_iter_next(i)) { + ta = rrset_iter_value(i); + + if ((ta->rr_type != GETDNS_RRTYPE_DNSKEY && + ta->rr_type != GETDNS_RRTYPE_DS + ) || !is_subdomain(ta->name, head->rrset.name)) + continue; + + /* The best status for any ta counts */ + switch (chain_head_validate_with_ta(head, ta)) { + case GETDNS_DNSSEC_SECURE : s = GETDNS_DNSSEC_SECURE; + case GETDNS_DNSSEC_INSECURE: if (s != GETDNS_DNSSEC_SECURE) + s = GETDNS_DNSSEC_INSECURE; + break; + case GETDNS_DNSSEC_BOGUS : if (s != GETDNS_DNSSEC_SECURE && + s != GETDNS_DNSSEC_INSECURE) + s = GETDNS_DNSSEC_BOGUS; + break; + default : break; + } + } + return s; +} + +static void chain_validate_dnssec(chain_head *chain, rrset_iter *tas) { chain_head *head; - getdns_list *tas = - chain->netreq->owner->context->dnssec_trust_anchors; for (head = chain; head; head = head->next) { - switch (chain_head_validate_dnssec(head, tas)) { - case GETDNS_DNSSEC_SECURE: break; - case GETDNS_DNSSEC_BOGUS : head->netreq->bogus = 1; - default : continue; + switch (chain_head_validate(head, tas)) { + case GETDNS_DNSSEC_SECURE: if (!head->netreq->bogus) + head->netreq->secure = 1; + break; + case GETDNS_DNSSEC_BOGUS : head->netreq->bogus = 1; + head->netreq->secure = 0; + break; + default : break; } - /* TODO: Validate head->rrset */ } + /* TODO: For secure nxdomains, test if the nsecs are correct */ } #endif @@ -447,11 +930,18 @@ static void append_rrs2val_chain_list(getdns_context *ctxt, static void check_chain_complete(chain_head *chain) { getdns_dns_req *dnsreq; + getdns_context *context; size_t o, node_count; chain_head *head, *next; chain_node *node; getdns_list *val_chain_list; getdns_dict *response_dict; +#ifdef STUB_NATIVE_DNSSEC + uint8_t tas_spc[4096], *tas; + size_t tas_sz; + gldns_buffer tas_buf; + rrset_iter tas_iter; +#endif if ((o = count_outstanding_requests(chain)) > 0) { DEBUG_SEC("%zu outstanding requests\n", o); @@ -459,13 +949,26 @@ static void check_chain_complete(chain_head *chain) } DEBUG_SEC("Chain done!\n"); dnsreq = chain->netreq->owner; + context = dnsreq->context; #ifdef STUB_NATIVE_DNSSEC - chain_validate_dnssec(chain); + gldns_buffer_init_frm_data(&tas_buf, (tas = tas_spc), sizeof(tas_spc)); + _getdns_list2wire(&tas_buf, context->dnssec_trust_anchors); + if ((tas_sz = gldns_buffer_position(&tas_buf)) > sizeof(tas_spc)) { + if ((tas = GETDNS_XMALLOC(dnsreq->my_mf, uint8_t, tas_sz))) { + gldns_buffer_init_frm_data(&tas_buf, tas, tas_sz); + _getdns_list2wire(&tas_buf, context->dnssec_trust_anchors); + } + } else if (! GLDNS_ANCOUNT(tas)) + tas = NULL; + + if (tas) + chain_validate_dnssec(chain, + rrset_iter_init(&tas_iter, tas, tas_sz)); #endif val_chain_list = dnsreq->dnssec_return_validation_chain - ? getdns_list_create_with_context(dnsreq->context) : NULL; + ? getdns_list_create_with_context(context) : NULL; /* Walk chain to add values to val_chain_list and to cleanup */ for ( head = chain; head ; head = next ) { @@ -475,12 +978,12 @@ static void check_chain_complete(chain_head *chain) ; node_count--, node = node->parent ) { if (node->dnskey_req) { - append_rrs2val_chain_list(dnsreq->context, + append_rrs2val_chain_list(context, val_chain_list, node->dnskey_req); dns_req_free(node->dnskey_req->owner); } if (node->ds_req) { - append_rrs2val_chain_list(dnsreq->context, + append_rrs2val_chain_list(context, val_chain_list, node->ds_req); dns_req_free(node->ds_req->owner); } @@ -494,7 +997,14 @@ static void check_chain_complete(chain_head *chain) response_dict, "validation_chain", val_chain_list); getdns_list_destroy(val_chain_list); } + + +#ifdef STUB_NATIVE_DNSSEC + if (tas && tas != tas_spc) + GETDNS_FREE(dnsreq->my_mf, tas); +#endif /* Final user callback */ + priv_getdns_call_user_callback(dnsreq, response_dict); } @@ -676,18 +1186,6 @@ static void val_chain_node_soa_cb(getdns_dns_req *dnsreq) check_chain_complete(node->chains); } -static int is_subdomain( - const uint8_t * const subdomain, const uint8_t *domain) -{ - while (*domain) { - if (priv_getdns_dname_equal(subdomain, domain)) - return 1; - - domain += *domain + 1; - } - return *subdomain == 0; -} - static uint8_t **reverse_labels(uint8_t *dname, uint8_t **labels) { if (*dname) @@ -740,6 +1238,8 @@ static chain_head *add_rrset2val_chain(struct mem_funcs *mf, for ( n -= max_labels, node = max_head->parent ; n ; n--, node = node->parent); + + max_node = node; } else max_node = NULL; From 19b79b066f7dd95ac691943b9142cefc28ce9759 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 26 Jun 2015 00:26:40 +0200 Subject: [PATCH 14/28] NSEC NXDOMAIN + NSEC3 denial of exist. validation --- src/dnssec.c | 522 ++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 435 insertions(+), 87 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index fc7f38dc..a63d613d 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -50,6 +50,7 @@ #include "gldns/str2wire.h" #include "gldns/wire2str.h" #include "gldns/keyraw.h" +#include "gldns/parseutil.h" #include "general.h" #include "dict.h" @@ -257,6 +258,12 @@ static rrtype_iter *rrtype_iter_init(rrtype_iter *i, getdns_rrset *rrset) i->rrset->name, i->rrset->rr_class, i->rrset->rr_type); } +inline static int rrset_has_rrs(getdns_rrset *rrset) +{ + rrtype_iter rr_spc; + return rrtype_iter_init(&rr_spc, rrset) != NULL; +} + static rrsig_iter *rrsig_iter_next(rrsig_iter *i) { return (rrsig_iter *) rr_iter_rrsig_covering( @@ -426,9 +433,14 @@ struct chain_node { chain_head *chains; }; +inline static int _dname_len(uint8_t *name) +{ + uint8_t *p; + for (p = name; *p; p += *p + 1); + return p - name + 1; +} + #ifdef STUB_NATIVE_DNSSEC - - static int key_matches_signer(getdns_rrset *dnskey, getdns_rrset *rrset) { rrtype_iter rr_spc, *rr; @@ -497,11 +509,11 @@ static int _getdns_verify_rrsig(getdns_rrset *rrset, rrsig_iter *rrsig, rrtype_i ldns_rr *rrsig_l = rr2ldns_rr(&rrsig->rr_i); ldns_rr *key_l = rr2ldns_rr(&key->rr_i); int r; - /* - fprintf(stderr, "rrsig: " ); ldns_rr_print(stderr, rrsig_l); fprintf(stderr, "\n"); - fprintf(stderr, "dnskey: "); ldns_rr_print(stderr, key_l); fprintf(stderr, "\n"); - fprintf(stderr, "rrset: " ); ldns_rr_list_print(stderr, rrset_l); fprintf(stderr, "\n"); - */ + + /* TODO: In case of wildcard (rrsig's "Labels" rdata field is smaller + * than the number of labels in the owner name) also validate the NSEC + * or NSEC3 that deniese existance of a more specific answer. + */ r = rrset_l && rrsig_l && key_l && ldns_verify_rrsig(rrset_l, rrsig_l, key_l) == LDNS_STATUS_OK; ldns_rr_list_deep_free(rrset_l); ldns_rr_free(rrsig_l); @@ -509,6 +521,27 @@ static int _getdns_verify_rrsig(getdns_rrset *rrset, rrsig_iter *rrsig, rrtype_i return r; } +static uint8_t *_getdns_nsec3_hash_label(uint8_t *label, size_t label_len, + uint8_t *name, uint8_t algorithm, uint16_t iterations, uint8_t *salt) +{ + ldns_rdf name_l = { _dname_len(name), LDNS_RDF_TYPE_DNAME, name }; + ldns_rdf *hname_l; + + if (!(hname_l = ldns_nsec3_hash_name( + &name_l, algorithm, iterations, *salt, salt + 1))) + return NULL; + + if ( label_len < hname_l->_size-1 + || label_len < *((uint8_t *)hname_l->_data) + 1 + || hname_l->_size-1 < *((uint8_t *)hname_l->_data) + 1) { + ldns_rdf_deep_free(hname_l); + return NULL; + } + memcpy(label, hname_l->_data, *((uint8_t *)hname_l->_data) + 1); + ldns_rdf_deep_free(hname_l); + return label; +} + static int dnskey_signed_rrset(rrtype_iter *dnskey, getdns_rrset *rrset) { rrsig_iter rrsig_spc, *rrsig; @@ -621,43 +654,13 @@ static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set) return 0; } -#if 0 - uint8_t *name = rrset->name, cname_spc[256], *cname = rrset->name; - size_t anti_loop = 1000, cname_len = sizeof(cname_spc); - priv_getdns_rdf_iter rdf_spc, *rdf; - - /* First, try to find the canonical name that is denied. - * CNAMEs authentication is checked with their own chain_head - * entry points, so no need to check signatures here. - */ - while (anti_loop--) { - for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) - ; i - ; i = rrset_iter_next(i)) { - - if (i->rrset.rr_type != GETDNS_RRTYPE_CNAME) - continue; - - if (priv_getdns_dname_equal(name, i->rrset.name) && - (rdf = priv_getdns_rdf_iter_init( - &rdf_spc, &i->rr_i)) && - (cname = priv_getdns_rdf_if_or_as_decompressed( - rdf, cname_spc, &cname_len))) - break; - } - if (priv_getdns_dname_equal(name, cname)) - break; - - name = cname; - } -#endif - static int bitmap_contains_rrtype(priv_getdns_rdf_iter *bitmap, uint16_t rr_type) { uint8_t *dptr, *dend; uint8_t window = rr_type >> 8; uint8_t subtype = rr_type & 0xFF; + DEBUG_SEC("bitmap: %p, type: %d\n", bitmap, (int)rr_type); if (!bitmap) return 0; dptr = bitmap->pos; @@ -675,60 +678,376 @@ static int bitmap_contains_rrtype(priv_getdns_rdf_iter *bitmap, uint16_t rr_type return 0; } -static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) +static int nsec_bitmap_excludes_rrtype(getdns_rrset *nsec_rrset, uint16_t rr_type) { - rrset_iter i_spc, *i; - size_t n_nsecs = 0, n_nsec3s = 0; - getdns_rrset nsecs[2], nsec3s[3]; - rrtype_iter nsec_space, *nsec; + rrtype_iter nsec_space, *nsec_rr; priv_getdns_rdf_iter bitmap_spc, *bitmap; - size_t j; + return (nsec_rrset->rr_type == GETDNS_RRTYPE_NSEC || + nsec_rrset->rr_type == GETDNS_RRTYPE_NSEC3 ) + && (nsec_rr = rrtype_iter_init(&nsec_space, nsec_rrset)) + && (bitmap = priv_getdns_rdf_iter_init_at(&bitmap_spc, &nsec_rr->rr_i, + nsec_rrset->rr_type == GETDNS_RRTYPE_NSEC ? 1: 5)) + && !bitmap_contains_rrtype(bitmap, rr_type); + + return 0; +} + +static uint8_t **reverse_labels(uint8_t *dname, uint8_t **labels) +{ + if (*dname) + labels = reverse_labels(dname + *dname + 1, labels); + *labels = dname; + return labels + 1; +} + +static int dname_compare(uint8_t *left, uint8_t *right) +{ + uint8_t *llabels[128], *rlabels[128], **last_llabel, **last_rlabel, + **llabel, **rlabel, *l, *r, lsz, rsz; + + last_llabel = reverse_labels(left, llabels); + last_rlabel = reverse_labels(right, rlabels); + + for ( llabel = llabels, rlabel = rlabels + ; llabel < last_llabel + ; llabel++, rlabel++ ) { + + if (rlabel == last_rlabel) + return 1; + + for ( l = *llabel, lsz = *l++, r = *rlabel, rsz = *r++ + ; lsz; l++, r++, lsz--, rsz-- ) { + + if (!rsz) + return 1; + if (*l != *r && tolower((unsigned char)*l) != + tolower((unsigned char)*r)) { + if (tolower((unsigned char)*l) < + tolower((unsigned char)*r)) + return -1; + return 1; + } + } + } + return rlabel == last_rlabel ? 0 : -1; +} + +static int nsec_covers_name(getdns_rrset *nsec, uint8_t *name) +{ + uint8_t owner_spc[256], *owner; + size_t owner_len = sizeof(owner_spc); + uint8_t next_spc[256], *next; + size_t next_len = sizeof(next_spc); + rrtype_iter rr_spc, *rr; + priv_getdns_rdf_iter rdf_spc, *rdf; + int nsec_cmp; + + if ( !(rr = rrtype_iter_init(&rr_spc, nsec)) + || !(rdf = priv_getdns_rdf_iter_init(&rdf_spc, &rr->rr_i)) + || !(owner = priv_getdns_owner_if_or_as_decompressed( + &rr->rr_i, owner_spc, &owner_len)) + || !(next = priv_getdns_rdf_if_or_as_decompressed( + rdf, next_spc, &next_len))) + return 0; + + debug_sec_print_dname("nsec owner: ", owner); + debug_sec_print_dname("name : ", name); + debug_sec_print_dname("nsec next : ", next); + + nsec_cmp = dname_compare(owner, next); + if (nsec_cmp < 0) { + DEBUG_SEC("nsec owner < next\n"); + return dname_compare(name, owner) >= 0 + && dname_compare(name, next) < 0; + } else if (nsec_cmp > 0) { + /* The wrap around nsec */ + DEBUG_SEC("nsec owner > next\n"); + return dname_compare(name, owner) >= 0; + } else { + DEBUG_SEC("nsec owner == next\n"); + return 1; + } +} + +static int nsec_covers_wildcard(getdns_rrset *nsec, uint8_t *name) +{ + uint8_t name_spc[256]; + + uint8_t signer_spc[256], *signer; + size_t signer_len = sizeof(signer_spc); + priv_getdns_rdf_iter rdf_spc, *rdf; + rrsig_iter rrsig_spc, *rrsig; + + if ( !(rrsig = rrsig_iter_init(&rrsig_spc, nsec)) + || !(rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rrsig->rr_i, 7)) + || !(signer = priv_getdns_rdf_if_or_as_decompressed( + rdf, signer_spc, &signer_len))) + return 0; + + name = memcpy(name_spc, name, _dname_len(name)); + + while (*name && is_subdomain(signer, name) + && !priv_getdns_dname_equal(signer, name)) { + + debug_sec_print_dname(" name: ", name); + debug_sec_print_dname("is a subdomain of: ", signer); + + name += *name - 1; + name[0] = 1; + name[1] = (uint8_t)'*'; + + if (nsec_covers_name(nsec, name)) + return 1; + + name += 2; + } + return 0; +} + +static uint8_t *name2nsec3_label( + getdns_rrset *nsec3, uint8_t *name, uint8_t *label, size_t label_len) +{ + rrsig_iter rrsig_spc, *rrsig; + priv_getdns_rdf_iter rdf_spc, *rdf; + uint8_t signer_spc[256], *signer; + size_t signer_len = sizeof(signer_spc); + rrtype_iter rr_spc, *rr; + + if (/* With the "first" signature */ + (rrsig = rrsig_iter_init(&rrsig_spc, nsec3)) + + /* Access the signer name rdata field (7th) */ + && (rdf = priv_getdns_rdf_iter_init_at( + &rdf_spc, &rrsig->rr_i, 7)) + + /* Verify & decompress */ + && (signer = priv_getdns_rdf_if_or_as_decompressed( + rdf, signer_spc, &signer_len)) + + /* signer of the NSEC3 is direct parent for this NSEC3? */ + && priv_getdns_dname_equal( + signer, nsec3->name + *nsec3->name + 1) + + /* signer of the NSEC3 is parent of name? */ + && is_subdomain(signer, name) + + /* Initialize rr for getting NSEC3 rdata fields */ + && (rr = rrtype_iter_init(&rr_spc, nsec3)) + + /* Check for available space to get rdata fields */ + && rr->rr_i.rr_type + 15 <= rr->rr_i.nxt + && rr->rr_i.rr_type + 14 + rr->rr_i.rr_type[14] <= rr->rr_i.nxt) + + /* Get the hashed label */ + return _getdns_nsec3_hash_label(label, label_len, name, + rr->rr_i.rr_type[10], + gldns_read_uint16(rr->rr_i.rr_type + 12), + rr->rr_i.rr_type + 14); + return NULL; +} + +static int nsec3_matches_name(getdns_rrset *nsec3, uint8_t *name) +{ + uint8_t label[64]; + + if (name2nsec3_label(nsec3, name, label, sizeof(label))) + + return *nsec3->name == label[0] /* Labels same size? */ + && memcmp(nsec3->name + 1, label + 1, label[0]) == 0; + + return 0; +} + +static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name) +{ + uint8_t label[65], next[65], owner[65]; + rrtype_iter rr_spc, *rr; + priv_getdns_rdf_iter rdf_spc, *rdf; + int nsz = 0, nsec_cmp; + + if (!name2nsec3_label(nsec3, name, label, sizeof(label)-1)) + return 0; + label[label[0]+1] = 0; + + if ( !(rr = rrtype_iter_init(&rr_spc, nsec3)) + || !(rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rr->rr_i, 4)) + || rdf->pos + *rdf->pos + 1 > rdf->nxt + || (nsz = gldns_b32_ntop_extended_hex(rdf->pos + 1, *rdf->pos, + (char *)next + 1, sizeof(next)-2)) < 0 + || *nsec3->name > sizeof(owner) - 2 + || !memcpy(owner, nsec3->name, *nsec3->name + 1)) { + + DEBUG_SEC("ERROR!!!!\n"); + } + owner[owner[0]+1] = 0; + next[(next[0] = (uint8_t)nsz)+1] = 0; + + debug_sec_print_dname("NSEC3 for: ", name); + debug_sec_print_dname(" is: ", label); + debug_sec_print_dname("inbetween: ", owner); + debug_sec_print_dname(" and: ", next); + + nsec_cmp = dname_compare(owner, next); + if (nsec_cmp < 0) { + DEBUG_SEC("nsec owner < next\n"); + return dname_compare(label, owner) >= 0 + && dname_compare(label, next) < 0; + } else if (nsec_cmp > 0) { + /* The wrap around nsec */ + DEBUG_SEC("nsec owner > next\n"); + return dname_compare(label, owner) >= 0; + } else { + DEBUG_SEC("nsec owner == next\n"); + return 1; + } +} + +static int find_nsec3_covering_name( + getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name) +{ + rrset_iter i_spc, *i; + getdns_rrset *n; + + for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) + ; i ; i = rrset_iter_next(i)) { + + if ((n = rrset_iter_value(i))->rr_type == GETDNS_RRTYPE_NSEC3 + && nsec3_covers_name(n, name) + && a_key_signed_rrset(dnskey, n)) { + + debug_sec_print_rrset("NSEC3: ", n); + debug_sec_print_dname("covered: ", name); + + return 1; + } + } + return 0; +} + + +static int nsec3_find_next_closer_and_wildcard( + getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *nc_name, int noerror) +{ + uint8_t wc_name[256] = { 1, (uint8_t)'*' }; + + if (!find_nsec3_covering_name(dnskey, rrset, nc_name)) + return 0; + + if (noerror) + return 1; /* TODO: Check for opt-out bit? */ + + nc_name += *nc_name + 1; + (void) memcpy(wc_name + 2, nc_name, _dname_len(nc_name)); + + return find_nsec3_covering_name(dnskey, rrset, wc_name); +} + +static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) +{ + getdns_rrset nsec_rrset, *cover, *wildcard, *ce; + rrset_iter i_spc, *i; + rrset_iter j_spc, *j; + uint8_t *ce_name, *nc_name; assert(dnskey->rr_type == GETDNS_RRTYPE_DNSKEY); - /* First collect nsecs and nsec3s */ + /* The NSEC NODATA case + * ==================== + * NSEC has same ownername as the rrset to deny. + * Only the rr_type is missing from the bitmap. + */ + nsec_rrset = *rrset; + nsec_rrset.rr_type = GETDNS_RRTYPE_NSEC; + if (nsec_bitmap_excludes_rrtype(&nsec_rrset, rrset->rr_type) && + a_key_signed_rrset(dnskey, &nsec_rrset)) { + + debug_sec_print_rrset("NSEC NODATA proof for: ", rrset); + return 1; + } + + /* The NSEC NXDOMAIN case + * ====================== + * - First find the NSEC that covers the owner name. + */ for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) - ; i - ; i = rrset_iter_next(i)) { + ; i ; i = rrset_iter_next(i)) { - if (i->rrset.rr_type == GETDNS_RRTYPE_NSEC) { + if ((cover=rrset_iter_value(i))->rr_type != GETDNS_RRTYPE_NSEC + || !nsec_covers_name(cover, rrset->name) + || !a_key_signed_rrset(dnskey, cover)) + continue; - nsecs[n_nsecs] = i->rrset; - if (++n_nsecs >= sizeof(nsecs)) - break; + debug_sec_print_rrset(" NSEC: ", cover); + debug_sec_print_dname("covers name: ", rrset->name); - } else if (i->rrset.rr_type == GETDNS_RRTYPE_NSEC3) { + for ( j = rrset_iter_init(&j_spc, rrset->pkt, rrset->pkt_len) + ; j ; j = rrset_iter_next(i)) { - nsecs[n_nsecs] = i->rrset; - if (++n_nsec3s >= sizeof(nsec3s)) - break; + if ((wildcard = rrset_iter_value(j))->rr_type + != GETDNS_RRTYPE_NSEC + || !nsec_covers_wildcard(wildcard, rrset->name) + || !a_key_signed_rrset(dnskey, wildcard)) + continue; + + debug_sec_print_rrset("NSEC NXDOMAIN proof for: ", rrset); + return 1; } } - if (!n_nsecs && !n_nsec3s) - return 0; - /* We assume rrset already contains the canonical name */ - if (n_nsecs) { - /* The NOERROR case */ - for (j = 0; j < n_nsecs; j++) { - if (priv_getdns_dname_equal(rrset->name, nsecs[j].name)) - return (nsec = rrtype_iter_init( - &nsec_space, &nsecs[j])) && + /* The NSEC3 NODATA case + * ===================== + * NSEC3 has same (hashed) ownername as the rrset to deny. + * Only the rr_type (and CNAME) is missing from the bitmap. + */ + for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) + ; i ; i = rrset_iter_next(i)) { - (bitmap = priv_getdns_rdf_iter_init_at( - &bitmap_spc, &nsec->rr_i, 1)) && + if ((ce = rrset_iter_value(i))->rr_type == GETDNS_RRTYPE_NSEC3 + && nsec_bitmap_excludes_rrtype(ce, rrset->rr_type) + && nsec_bitmap_excludes_rrtype(ce, GETDNS_RRTYPE_CNAME) + && nsec3_matches_name(ce, rrset->name) + && a_key_signed_rrset(dnskey, ce)) { - !bitmap_contains_rrtype( - bitmap, rrset->rr_type) && - - a_key_signed_rrset(dnskey, &nsecs[j]); + debug_sec_print_rrset("NSEC3 No Data for: ", rrset); + return 1; } - /* The NXDOMAIN case */ - /* One should cover the name */ - /* One should cover the wildcard */ - } - /* TODO: proof nsec3 non existance */ + } + /* The NSEC3 NXDOMAIN case + * ===================== + * First find the closest encloser. + */ + for ( nc_name = rrset->name, ce_name = rrset->name + *rrset->name + 1 + ; *ce_name ; nc_name = ce_name, ce_name += *ce_name + 1) { + + for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) + ; i ; i = rrset_iter_next(i)) { + + if ((ce = rrset_iter_value(i))->rr_type + == GETDNS_RRTYPE_NSEC3 + && nsec3_matches_name(ce, ce_name) + && a_key_signed_rrset(dnskey, ce)) { + + debug_sec_print_rrset( + "Closest Encloser: ", ce); + debug_sec_print_dname( + "Closest Encloser: ", ce_name); + debug_sec_print_dname( + " Next closer: ", nc_name); + + if (nsec3_find_next_closer_and_wildcard( + dnskey, rrset, nc_name, + + /* Wild card not needed on a "covering" + * NODATA response, because of opt-out? + */ + GLDNS_RCODE_WIRE(rrset->pkt) == + GETDNS_RCODE_NOERROR)) + + return 1; + } + } + } return 0; } @@ -800,10 +1119,14 @@ static int chain_head_validate_with_ta(chain_head *head, getdns_rrset *ta) != GETDNS_DNSSEC_SECURE) return s; - if (a_key_signed_rrset(keys, &head->rrset)) + if (rrset_has_rrs(&head->rrset)) { + if (a_key_signed_rrset(keys, &head->rrset)) + return GETDNS_DNSSEC_SECURE; + + } else if (key_proves_nonexistance(keys, &head->rrset)) return GETDNS_DNSSEC_SECURE; - else - return GETDNS_DNSSEC_BOGUS; + + return GETDNS_DNSSEC_BOGUS; } static int chain_head_validate(chain_head *head, rrset_iter *tas) @@ -851,7 +1174,6 @@ static void chain_validate_dnssec(chain_head *chain, rrset_iter *tas) default : break; } } - /* TODO: For secure nxdomains, test if the nsecs are correct */ } #endif @@ -1186,14 +1508,6 @@ static void val_chain_node_soa_cb(getdns_dns_req *dnsreq) check_chain_complete(node->chains); } -static uint8_t **reverse_labels(uint8_t *dname, uint8_t **labels) -{ - if (*dname) - labels = reverse_labels(dname + *dname + 1, labels); - *labels = dname; - return labels + 1; -} - static chain_head *add_rrset2val_chain(struct mem_funcs *mf, chain_head **chain_p, getdns_rrset *rrset, getdns_network_req *netreq) { @@ -1324,6 +1638,13 @@ static void add_netreq2val_chain( struct mem_funcs *mf; getdns_rrset empty_rrset; + getdns_rrset q_rrset; + uint8_t cname_spc[256]; + size_t cname_len = sizeof(cname_spc); + size_t anti_loop; + priv_getdns_rdf_iter rdf_spc, *rdf; + rrtype_iter *rr, rr_spc; + assert(netreq->response); assert(netreq->response_len >= GLDNS_HEADER_SIZE); @@ -1370,6 +1691,33 @@ static void add_netreq2val_chain( else val_chain_sched_soa(head, rrset->name); } + /* For NOERROR/NODATA or NXDOMAIN responses add extra rrset to + * the validation chain so the denial of existence will be + * checked eventually. + */ + + /* First find the canonical name for the question */ + q_rrset.name = netreq->query + GLDNS_HEADER_SIZE; + q_rrset.rr_type = GETDNS_RRTYPE_CNAME; + q_rrset.rr_class = netreq->request_class; + q_rrset.pkt = netreq->response; + q_rrset.pkt_len = netreq->response_len; + + for (anti_loop = 1000; anti_loop; anti_loop--) { + if (!(rr = rrtype_iter_init(&rr_spc, &q_rrset))) + break; + if (!(rdf = priv_getdns_rdf_iter_init(&rdf_spc, &rr->rr_i))) + break; + q_rrset.name = priv_getdns_rdf_if_or_as_decompressed( + rdf, cname_spc, &cname_len); + } + + q_rrset.rr_type = netreq->request_type; + if (!(rr = rrtype_iter_init(&rr_spc, &q_rrset))) { + + debug_sec_print_rrset("Adding NX rrset: ", &q_rrset); + add_rrset2val_chain(mf, chain_p, &q_rrset, netreq); + } } static void get_val_chain(getdns_dns_req *dnsreq) From fe4b7095b3e60a16a573a2fac0568b1bdbbfef72 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 26 Jun 2015 00:29:20 +0200 Subject: [PATCH 15/28] Set has_ta before unbound context initialization --- src/context.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/context.c b/src/context.c index dedfbbe8..611172e7 100644 --- a/src/context.c +++ b/src/context.c @@ -843,6 +843,9 @@ getdns_context_create_with_extended_memory_functions( /* unbound context is initialized here */ /* Unbound needs SSL to be init'ed this early when TLS is used. However we * don't know that till later so we will have to do this every time. */ + result->has_ta = priv_getdns_parse_ta_file( + NULL, result->dnssec_trust_anchors); + SSL_library_init(); result->unbound_ctx = NULL; if ((r = rebuild_ub_ctx(result))) @@ -853,9 +856,6 @@ getdns_context_create_with_extended_memory_functions( getdns_list_create_with_context(result))) goto error; - result->has_ta = priv_getdns_parse_ta_file( - NULL, result->dnssec_trust_anchors); - *context = result; return GETDNS_RETURN_GOOD; error: From 0411668cb44200ca581ad6f40025bb3d9bb2b69a Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 26 Jun 2015 11:39:44 +0200 Subject: [PATCH 16/28] blah --- src/context.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/context.c b/src/context.c index 611172e7..fd0c69b3 100644 --- a/src/context.c +++ b/src/context.c @@ -840,21 +840,22 @@ getdns_context_create_with_extended_memory_functions( result->limit_outstanding_queries = 0; result->return_dnssec_status = GETDNS_EXTENSION_FALSE; - /* unbound context is initialized here */ - /* Unbound needs SSL to be init'ed this early when TLS is used. However we - * don't know that till later so we will have to do this every time. */ + if (! (result->dnssec_trust_anchors = + getdns_list_create_with_context(result))) + goto error; result->has_ta = priv_getdns_parse_ta_file( NULL, result->dnssec_trust_anchors); + /* unbound context is initialized here */ + /* Unbound needs SSL to be init'ed this early when TLS is used. However we + * don't know that till later so we will have to do this every time. */ + SSL_library_init(); result->unbound_ctx = NULL; if ((r = rebuild_ub_ctx(result))) goto error; create_local_hosts(result); - if (! (result->dnssec_trust_anchors = - getdns_list_create_with_context(result))) - goto error; *context = result; return GETDNS_RETURN_GOOD; From f6c1a48b6e6be9d3b8268ad111f098bd8e65c09e Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Sat, 27 Jun 2015 23:28:23 +0200 Subject: [PATCH 17/28] Validaton of wildcard answers --- src/dnssec.c | 321 ++++++++++++++++++++++++++------------------ src/util-internal.c | 3 +- 2 files changed, 196 insertions(+), 128 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index a63d613d..2fe9eb30 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -139,7 +139,7 @@ inline static void debug_sec_print_dname(const char *msg, uint8_t *label) { char str[1024]; - if (gldns_wire2str_dname_buf(label, 256, str, sizeof(str))) + if (label && gldns_wire2str_dname_buf(label, 256, str, sizeof(str))) DEBUG_SEC("%s%s\n", msg, str); else DEBUG_SEC("%s\n", msg); @@ -433,13 +433,20 @@ struct chain_node { chain_head *chains; }; -inline static int _dname_len(uint8_t *name) +inline static size_t _dname_len(uint8_t *name) { uint8_t *p; for (p = name; *p; p += *p + 1); return p - name + 1; } +inline static size_t _dname_label_count(uint8_t *name) +{ + size_t c; + for (c = 0; *name; name += *name + 1, c++); + return c; +} + #ifdef STUB_NATIVE_DNSSEC static int key_matches_signer(getdns_rrset *dnskey, getdns_rrset *rrset) { @@ -503,22 +510,59 @@ static ldns_rr_list *rrset2ldns_rr_list(getdns_rrset *rrset) return rr_list; } -static int _getdns_verify_rrsig(getdns_rrset *rrset, rrsig_iter *rrsig, rrtype_iter *key) + +/* Verifies the signature rrsig for rrset rrset with key key. + * When the rrset was a wildcard expansion (rrsig labels < labels owner name), + * nc_name will be set to the next closer (within rrset->name). + */ +static int _getdns_verify_rrsig( + getdns_rrset *rrset, rrsig_iter *rrsig, rrtype_iter *key, uint8_t **nc_name) { ldns_rr_list *rrset_l = rrset2ldns_rr_list(rrset); ldns_rr *rrsig_l = rr2ldns_rr(&rrsig->rr_i); ldns_rr *key_l = rr2ldns_rr(&key->rr_i); int r; + size_t to_skip; + + /* nc_name should already have been initialized by the parent! */ + assert(nc_name); + assert(!*nc_name); + + r = rrset_l && rrsig_l && key_l && + ldns_verify_rrsig(rrset_l, rrsig_l, key_l) == LDNS_STATUS_OK; - /* TODO: In case of wildcard (rrsig's "Labels" rdata field is smaller - * than the number of labels in the owner name) also validate the NSEC - * or NSEC3 that deniese existance of a more specific answer. - */ - r = rrset_l && rrsig_l && key_l && ldns_verify_rrsig(rrset_l, rrsig_l, key_l) == LDNS_STATUS_OK; ldns_rr_list_deep_free(rrset_l); ldns_rr_free(rrsig_l); ldns_rr_free(key_l); - return r; + + if (!r) + return 0; + + /* Verification has already been done, so the labels rdata field is + * definitely readable + */ + assert(rrsig->rr_i.rr_type + 14 <= rrsig->rr_i.nxt); + + /* If the number of labels in the owner name mathes the "labels" rdata + * field, then this was not a wildcard expansion, and everything is + * good. + */ + if ((size_t)rrsig->rr_i.rr_type[13] == _dname_label_count(rrset->name)) + return 1; + + /* This is a valid wildcard expansion. Calculate and return the + * "Next closer" name, because we need another NSEC to cover it + * for wildcards to hold...xi + * (except for rrsigs for NSECs, but those are dealt with later) + */ + to_skip = _dname_label_count(rrset->name) + - (size_t)rrsig->rr_i.rr_type[13] - 1; + + for ( *nc_name = rrset->name + ; to_skip + ; *nc_name += **nc_name + 1, to_skip--); + + return 1; } static uint8_t *_getdns_nsec3_hash_label(uint8_t *label, size_t label_len, @@ -542,12 +586,19 @@ static uint8_t *_getdns_nsec3_hash_label(uint8_t *label, size_t label_len, return label; } -static int dnskey_signed_rrset(rrtype_iter *dnskey, getdns_rrset *rrset) +static int dnskey_signed_rrset( + rrtype_iter *dnskey, getdns_rrset *rrset, uint8_t **nc_name) { rrsig_iter rrsig_spc, *rrsig; + priv_getdns_rdf_iter rdf_spc, *rdf; + uint8_t signer_spc[256], *signer; + size_t signer_len = sizeof(signer_spc); uint16_t keytag; assert(dnskey->rrset->rr_type == GETDNS_RRTYPE_DNSKEY); + assert(nc_name); + + *nc_name = NULL; keytag = gldns_calc_keytag_raw(dnskey->rr_i.rr_type + 10, dnskey->rr_i.nxt - dnskey->rr_i.rr_type - 10); @@ -556,18 +607,21 @@ static int dnskey_signed_rrset(rrtype_iter *dnskey, getdns_rrset *rrset) ; rrsig ; rrsig = rrsig_iter_next(rrsig) ) { if (/* Space for keytag & signer in rrsig rdata? */ - rrsig->rr_i.nxt >= rrsig->rr_i.rr_type + 29 && + rrsig->rr_i.nxt >= rrsig->rr_i.rr_type + 28 /* Does the keytag match? */ - gldns_read_uint16(rrsig->rr_i.rr_type + 26) - == keytag && + && gldns_read_uint16(rrsig->rr_i.rr_type + 26) == keytag /* Does the signer name match? */ - priv_getdns_dname_equal(dnskey->rrset->name, - rrsig->rr_i.rr_type + 28) && + && (rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rrsig->rr_i, 7)) + + && (signer = priv_getdns_rdf_if_or_as_decompressed( + rdf, signer_spc, &signer_len)) + + && priv_getdns_dname_equal(dnskey->rrset->name, signer) /* Does the signature verify? */ - _getdns_verify_rrsig(rrset, rrsig, dnskey)) { + && _getdns_verify_rrsig(rrset, rrsig, dnskey, nc_name)) { debug_sec_print_rr("key ", &dnskey->rr_i); debug_sec_print_rrset("signed ", rrset); @@ -578,16 +632,37 @@ static int dnskey_signed_rrset(rrtype_iter *dnskey, getdns_rrset *rrset) return 0; } +static int find_nsec_covering_name( + getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name); + static int a_key_signed_rrset(getdns_rrset *keyset, getdns_rrset *rrset) { rrtype_iter dnskey_spc, *dnskey; + uint8_t *nc_name; assert(keyset->rr_type == GETDNS_RRTYPE_DNSKEY); for ( dnskey = rrtype_iter_init(&dnskey_spc, keyset) ; dnskey ; dnskey = rrtype_iter_next(dnskey) ) { - if (dnskey_signed_rrset(dnskey, rrset)) + if (!dnskey_signed_rrset(dnskey, rrset, &nc_name)) + continue; + + if (!nc_name) /* Not a wildcard, then success! */ + return 1; + + /* Wildcard RRSIG for a NSEC on the wildcard. + * There is no more specific! + */ + if (rrset->rr_type == GETDNS_RRTYPE_NSEC && + rrset->name[0] == 1 && rrset->name[1] == '*') + return 1; + + debug_sec_print_rrset("wildcard expanded to: ", rrset); + debug_sec_print_dname("Find NSEC covering the more sepecific: " + , nc_name); + + if (find_nsec_covering_name(keyset, rrset, nc_name)) return 1; } return 0; @@ -598,6 +673,7 @@ static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set) rrtype_iter dnskey_spc, *dnskey; rrtype_iter ds_spc, *ds; uint16_t keytag; + uint8_t *nc_name; ldns_rr *dnskey_l; ldns_rr *ds_l = NULL; @@ -619,35 +695,36 @@ static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set) ; ds ; ds = rrtype_iter_next(ds)) { if (/* Space for keytag & signer in rrsig rdata? */ - ds->rr_i.nxt >= ds->rr_i.rr_type + 12 && + ds->rr_i.nxt < ds->rr_i.rr_type + 12 /* Does the keytag match? */ - gldns_read_uint16(ds->rr_i.rr_type + 10) - == keytag) { + || gldns_read_uint16(ds->rr_i.rr_type+10)!=keytag) + continue; - if (!dnskey_l) - dnskey_l = rr2ldns_rr(&dnskey->rr_i); - if (dnskey_l && (ds_l = rr2ldns_rr(&ds->rr_i)) && - ldns_rr_compare_ds(ds_l, dnskey_l)) { + if (!dnskey_l) + if (!(dnskey_l = rr2ldns_rr(&dnskey->rr_i))) + continue; - debug_sec_print_rr("this DS: ", &ds->rr_i); - debug_sec_print_rr("matched DNSKEY: ", &dnskey->rr_i); + if (!(ds_l = rr2ldns_rr(&ds->rr_i))) + continue; - ldns_rr_free(dnskey_l); - ldns_rr_free(ds_l); - - if (dnskey_signed_rrset(dnskey, dnskey_set)) { - debug_sec_print_rrset("which signed: " - , dnskey_set); - return 1; - } else { - debug_sec_print_rrset("which did not sign: " - , dnskey_set); - } - } - } - if (ds_l) + if (!ldns_rr_compare_ds(ds_l, dnskey_l)) { ldns_rr_free(ds_l); + ds_l = NULL; + continue; + } + ldns_rr_free(dnskey_l); + + if (dnskey_signed_rrset(dnskey, dnskey_set, &nc_name) + && !nc_name /* No DNSKEY's on wildcards! */) { + + debug_sec_print_rrset( + "keyset authenticated: ", dnskey_set); + return 1; + } + DEBUG_SEC("nc_name: %p\n", nc_name); + debug_sec_print_dname("nc_name: ", nc_name); + return 0; } ldns_rr_free(dnskey_l); } @@ -701,6 +778,30 @@ static uint8_t **reverse_labels(uint8_t *dname, uint8_t **labels) return labels + 1; } +static uint8_t *dname_shared_parent(uint8_t *left, uint8_t *right) +{ + uint8_t *llabels[128], *rlabels[128], **last_llabel, **last_rlabel, + **llabel, **rlabel, *l, *r, sz; + + last_llabel = reverse_labels(left, llabels); + last_rlabel = reverse_labels(right, rlabels); + + for ( llabel = llabels, rlabel = rlabels + ; llabel < last_llabel + ; llabel++, rlabel++ ) { + + if ( rlabel == last_rlabel + || (sz = **llabel) != **rlabel) + return llabel[-1]; + + for (l = *llabel+1, r = *rlabel+1; sz; l++, r++, sz-- ) + if (*l != *r && tolower((unsigned char)*l) != + tolower((unsigned char)*r)) + return llabel[-1]; + } + return llabel[-1]; +} + static int dname_compare(uint8_t *left, uint8_t *right) { uint8_t *llabels[128], *rlabels[128], **last_llabel, **last_rlabel, @@ -733,7 +834,8 @@ static int dname_compare(uint8_t *left, uint8_t *right) return rlabel == last_rlabel ? 0 : -1; } -static int nsec_covers_name(getdns_rrset *nsec, uint8_t *name) +static int nsec_covers_name( + getdns_rrset *nsec, uint8_t *name, uint8_t **ce_name) { uint8_t owner_spc[256], *owner; size_t owner_len = sizeof(owner_spc); @@ -742,6 +844,7 @@ static int nsec_covers_name(getdns_rrset *nsec, uint8_t *name) rrtype_iter rr_spc, *rr; priv_getdns_rdf_iter rdf_spc, *rdf; int nsec_cmp; + uint8_t *common1, *common2; if ( !(rr = rrtype_iter_init(&rr_spc, nsec)) || !(rdf = priv_getdns_rdf_iter_init(&rdf_spc, &rr->rr_i)) @@ -755,6 +858,14 @@ static int nsec_covers_name(getdns_rrset *nsec, uint8_t *name) debug_sec_print_dname("name : ", name); debug_sec_print_dname("nsec next : ", next); + if (ce_name) { + common1 = dname_shared_parent(name, owner); + common2 = dname_shared_parent(name, next); + *ce_name = _dname_label_count(common1) + > _dname_label_count(common2) ? common1 : common2; + debug_sec_print_dname("nsec closest encloser: ", *ce_name); + } + nsec_cmp = dname_compare(owner, next); if (nsec_cmp < 0) { DEBUG_SEC("nsec owner < next\n"); @@ -770,41 +881,6 @@ static int nsec_covers_name(getdns_rrset *nsec, uint8_t *name) } } -static int nsec_covers_wildcard(getdns_rrset *nsec, uint8_t *name) -{ - uint8_t name_spc[256]; - - uint8_t signer_spc[256], *signer; - size_t signer_len = sizeof(signer_spc); - priv_getdns_rdf_iter rdf_spc, *rdf; - rrsig_iter rrsig_spc, *rrsig; - - if ( !(rrsig = rrsig_iter_init(&rrsig_spc, nsec)) - || !(rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rrsig->rr_i, 7)) - || !(signer = priv_getdns_rdf_if_or_as_decompressed( - rdf, signer_spc, &signer_len))) - return 0; - - name = memcpy(name_spc, name, _dname_len(name)); - - while (*name && is_subdomain(signer, name) - && !priv_getdns_dname_equal(signer, name)) { - - debug_sec_print_dname(" name: ", name); - debug_sec_print_dname("is a subdomain of: ", signer); - - name += *name - 1; - name[0] = 1; - name[1] = (uint8_t)'*'; - - if (nsec_covers_name(nsec, name)) - return 1; - - name += 2; - } - return 0; -} - static uint8_t *name2nsec3_label( getdns_rrset *nsec3, uint8_t *name, uint8_t *label, size_t label_len) { @@ -878,7 +954,8 @@ static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name) || *nsec3->name > sizeof(owner) - 2 || !memcpy(owner, nsec3->name, *nsec3->name + 1)) { - DEBUG_SEC("ERROR!!!!\n"); + DEBUG_SEC("Error getting NSEC3 owner & next labels\n"); + return 0; } owner[owner[0]+1] = 0; next[(next[0] = (uint8_t)nsz)+1] = 0; @@ -903,7 +980,7 @@ static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name) } } -static int find_nsec3_covering_name( +static int find_nsec_covering_name( getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name) { rrset_iter i_spc, *i; @@ -919,36 +996,47 @@ static int find_nsec3_covering_name( debug_sec_print_rrset("NSEC3: ", n); debug_sec_print_dname("covered: ", name); + return 1; + } + if ((n = rrset_iter_value(i))->rr_type == GETDNS_RRTYPE_NSEC + && nsec_covers_name(n, name, NULL) + && a_key_signed_rrset(dnskey, n)) { + + debug_sec_print_rrset("NSEC: ", n); + debug_sec_print_dname("covered: ", name); + return 1; } } return 0; } - -static int nsec3_find_next_closer_and_wildcard( - getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *nc_name, int noerror) +static int nsec3_find_next_closer( + getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *nc_name) { uint8_t wc_name[256] = { 1, (uint8_t)'*' }; - if (!find_nsec3_covering_name(dnskey, rrset, nc_name)) + if (!find_nsec_covering_name(dnskey, rrset, nc_name)) return 0; - if (noerror) - return 1; /* TODO: Check for opt-out bit? */ + /* Wild card not needed on a "covering" * NODATA response, + * because of opt-out? + */ + if (GLDNS_RCODE_WIRE(rrset->pkt) == GETDNS_RCODE_NOERROR) + return 1; nc_name += *nc_name + 1; (void) memcpy(wc_name + 2, nc_name, _dname_len(nc_name)); - return find_nsec3_covering_name(dnskey, rrset, wc_name); + return find_nsec_covering_name(dnskey, rrset, wc_name); } static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) { - getdns_rrset nsec_rrset, *cover, *wildcard, *ce; + getdns_rrset nsec_rrset, *cover, *ce; rrset_iter i_spc, *i; - rrset_iter j_spc, *j; uint8_t *ce_name, *nc_name; + uint8_t wc_name[256] = { 1, (uint8_t)'*' }; assert(dnskey->rr_type == GETDNS_RRTYPE_DNSKEY); @@ -966,33 +1054,23 @@ static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) return 1; } - /* The NSEC NXDOMAIN case - * ====================== + /* The NSEC Name error case + * ======================== * - First find the NSEC that covers the owner name. */ for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) ; i ; i = rrset_iter_next(i)) { if ((cover=rrset_iter_value(i))->rr_type != GETDNS_RRTYPE_NSEC - || !nsec_covers_name(cover, rrset->name) + || !nsec_covers_name(cover, rrset->name, &ce_name) || !a_key_signed_rrset(dnskey, cover)) continue; - debug_sec_print_rrset(" NSEC: ", cover); - debug_sec_print_dname("covers name: ", rrset->name); + debug_sec_print_dname("Closest Encloser: ", ce_name); + memcpy(wc_name + 2, ce_name, _dname_len(ce_name)); + debug_sec_print_dname(" Wildcard: ", wc_name); - for ( j = rrset_iter_init(&j_spc, rrset->pkt, rrset->pkt_len) - ; j ; j = rrset_iter_next(i)) { - - if ((wildcard = rrset_iter_value(j))->rr_type - != GETDNS_RRTYPE_NSEC - || !nsec_covers_wildcard(wildcard, rrset->name) - || !a_key_signed_rrset(dnskey, wildcard)) - continue; - - debug_sec_print_rrset("NSEC NXDOMAIN proof for: ", rrset); - return 1; - } + return find_nsec_covering_name(dnskey, rrset, wc_name); } /* The NSEC3 NODATA case @@ -1013,8 +1091,8 @@ static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) return 1; } } - /* The NSEC3 NXDOMAIN case - * ===================== + /* The NSEC3 Name error case + * ========================+ * First find the closest encloser. */ for ( nc_name = rrset->name, ce_name = rrset->name + *rrset->name + 1 @@ -1023,29 +1101,18 @@ static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) ; i ; i = rrset_iter_next(i)) { - if ((ce = rrset_iter_value(i))->rr_type - == GETDNS_RRTYPE_NSEC3 - && nsec3_matches_name(ce, ce_name) - && a_key_signed_rrset(dnskey, ce)) { + if ( (ce = rrset_iter_value(i))->rr_type + != GETDNS_RRTYPE_NSEC3 + || !nsec3_matches_name(ce, ce_name) + || !a_key_signed_rrset(dnskey, ce)) + continue; - debug_sec_print_rrset( - "Closest Encloser: ", ce); - debug_sec_print_dname( - "Closest Encloser: ", ce_name); - debug_sec_print_dname( - " Next closer: ", nc_name); + debug_sec_print_rrset("Closest Encloser: ", ce); + debug_sec_print_dname("Closest Encloser: ", ce_name); + debug_sec_print_dname(" Next closer: ", nc_name); - if (nsec3_find_next_closer_and_wildcard( - dnskey, rrset, nc_name, - - /* Wild card not needed on a "covering" - * NODATA response, because of opt-out? - */ - GLDNS_RCODE_WIRE(rrset->pkt) == - GETDNS_RCODE_NOERROR)) - - return 1; - } + if (nsec3_find_next_closer(dnskey, rrset, nc_name)) + return 1; } } return 0; diff --git a/src/util-internal.c b/src/util-internal.c index 84c09646..0273aea1 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -432,7 +432,8 @@ priv_getdns_dname_equal(const uint8_t *s1, const uint8_t *s2) else if (!*s1) return 1; for (i = *s1++, s2++; i > 0; i--, s1++, s2++) - if ((*s1 & 0xDF) != (*s2 & 0xDF)) + if (*s1 != *s2 && tolower((unsigned char)*s1) + != tolower((unsigned char)*s2)) return 0; } } From 170218c350a6e7922fc7b915b91ed4fc78af0e5e Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Sat, 27 Jun 2015 23:47:47 +0200 Subject: [PATCH 18/28] Expand dname rdata fields before compare --- src/dnssec.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 2fe9eb30..d7e8242b 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -453,6 +453,9 @@ static int key_matches_signer(getdns_rrset *dnskey, getdns_rrset *rrset) rrtype_iter rr_spc, *rr; rrsig_iter rrsig_spc, *rrsig; uint16_t keytag; + priv_getdns_rdf_iter rdf_spc, *rdf; + uint8_t signer_spc[256], *signer; + size_t signer_len = sizeof(signer_spc); assert(dnskey->rr_type == GETDNS_RRTYPE_DNSKEY); @@ -467,15 +470,20 @@ static int key_matches_signer(getdns_rrset *dnskey, getdns_rrset *rrset) ; rrsig ; rrsig = rrsig_iter_next(rrsig) ) { if (/* Space for keytag & signer in rrsig rdata? */ - rrsig->rr_i.nxt >= rrsig->rr_i.rr_type + 29 && + rrsig->rr_i.nxt >= rrsig->rr_i.rr_type + 28 /* Does the keytag match? */ - gldns_read_uint16(rrsig->rr_i.rr_type + 26) - == keytag && + && gldns_read_uint16(rrsig->rr_i.rr_type + 26) + == keytag /* Does the signer name match? */ - priv_getdns_dname_equal(dnskey->name, - rrsig->rr_i.rr_type + 28)) + && (rdf = priv_getdns_rdf_iter_init_at( + &rdf_spc, &rrsig->rr_i, 7)) + + && (signer = priv_getdns_rdf_if_or_as_decompressed( + rdf, signer_spc, &signer_len)) + + && priv_getdns_dname_equal(dnskey->name, signer)) return 1; } @@ -613,7 +621,8 @@ static int dnskey_signed_rrset( && gldns_read_uint16(rrsig->rr_i.rr_type + 26) == keytag /* Does the signer name match? */ - && (rdf = priv_getdns_rdf_iter_init_at(&rdf_spc, &rrsig->rr_i, 7)) + && (rdf = priv_getdns_rdf_iter_init_at( + &rdf_spc, &rrsig->rr_i, 7)) && (signer = priv_getdns_rdf_if_or_as_decompressed( rdf, signer_spc, &signer_len)) @@ -722,8 +731,9 @@ static int ds_authenticates_keys(getdns_rrset *ds_set, getdns_rrset *dnskey_set) "keyset authenticated: ", dnskey_set); return 1; } - DEBUG_SEC("nc_name: %p\n", nc_name); - debug_sec_print_dname("nc_name: ", nc_name); + debug_sec_print_rrset( + "keyset failed authentication: ", dnskey_set); + return 0; } ldns_rr_free(dnskey_l); From 4e45d31413ed05e3338894d1323eaa1eec24bab3 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Sun, 28 Jun 2015 13:41:48 +0200 Subject: [PATCH 19/28] No wildcard NSEC3 check on opt-out --- src/dnssec.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index d7e8242b..280f9804 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -642,7 +642,7 @@ static int dnskey_signed_rrset( } static int find_nsec_covering_name( - getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name); + getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name, int *opt_out); static int a_key_signed_rrset(getdns_rrset *keyset, getdns_rrset *rrset) { @@ -671,7 +671,7 @@ static int a_key_signed_rrset(getdns_rrset *keyset, getdns_rrset *rrset) debug_sec_print_dname("Find NSEC covering the more sepecific: " , nc_name); - if (find_nsec_covering_name(keyset, rrset, nc_name)) + if (find_nsec_covering_name(keyset, rrset, nc_name, NULL)) return 1; } return 0; @@ -945,7 +945,7 @@ static int nsec3_matches_name(getdns_rrset *nsec3, uint8_t *name) return 0; } -static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name) +static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name, int *opt_out) { uint8_t label[65], next[65], owner[65]; rrtype_iter rr_spc, *rr; @@ -970,6 +970,9 @@ static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name) owner[owner[0]+1] = 0; next[(next[0] = (uint8_t)nsz)+1] = 0; + if (opt_out) + *opt_out = (rr->rr_i.rr_type[11] & 1) != 0; + debug_sec_print_dname("NSEC3 for: ", name); debug_sec_print_dname(" is: ", label); debug_sec_print_dname("inbetween: ", owner); @@ -991,16 +994,19 @@ static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name) } static int find_nsec_covering_name( - getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name) + getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *name, int *opt_out) { rrset_iter i_spc, *i; getdns_rrset *n; + if (opt_out) + *opt_out = 0; + for ( i = rrset_iter_init(&i_spc, rrset->pkt, rrset->pkt_len) ; i ; i = rrset_iter_next(i)) { if ((n = rrset_iter_value(i))->rr_type == GETDNS_RRTYPE_NSEC3 - && nsec3_covers_name(n, name) + && nsec3_covers_name(n, name, opt_out) && a_key_signed_rrset(dnskey, n)) { debug_sec_print_rrset("NSEC3: ", n); @@ -1025,20 +1031,26 @@ static int nsec3_find_next_closer( getdns_rrset *dnskey, getdns_rrset *rrset, uint8_t *nc_name) { uint8_t wc_name[256] = { 1, (uint8_t)'*' }; + int opt_out; - if (!find_nsec_covering_name(dnskey, rrset, nc_name)) + if (!find_nsec_covering_name(dnskey, rrset, nc_name, &opt_out)) return 0; - /* Wild card not needed on a "covering" * NODATA response, + /* Wild card not needed on a "covering" NODATA response, * because of opt-out? + * + * We check for opt-out bit, because rcode is unreliable... + * ... the checked packet might be artificially constructed + * (if we came here via getdns_validate_dnssec) in which case + * rcode is always NOERROR. */ - if (GLDNS_RCODE_WIRE(rrset->pkt) == GETDNS_RCODE_NOERROR) + if (opt_out) return 1; nc_name += *nc_name + 1; (void) memcpy(wc_name + 2, nc_name, _dname_len(nc_name)); - return find_nsec_covering_name(dnskey, rrset, wc_name); + return find_nsec_covering_name(dnskey, rrset, wc_name, NULL); } static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) @@ -1080,7 +1092,7 @@ static int key_proves_nonexistance(getdns_rrset *dnskey, getdns_rrset *rrset) memcpy(wc_name + 2, ce_name, _dname_len(ce_name)); debug_sec_print_dname(" Wildcard: ", wc_name); - return find_nsec_covering_name(dnskey, rrset, wc_name); + return find_nsec_covering_name(dnskey, rrset, wc_name, NULL); } /* The NSEC3 NODATA case From 2b83bddd4d28f6aa20573f3b9ba84cf709ba5fee Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 29 Jun 2015 09:18:53 +0200 Subject: [PATCH 20/28] More sense making parameter names for is_subdomain --- src/dnssec.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 280f9804..8342c9a9 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -54,17 +54,17 @@ #include "general.h" #include "dict.h" -/* subdomain is same or parent of domain */ +/* parent is same or parent of subdomain,*/ static int is_subdomain( - const uint8_t * const subdomain, const uint8_t *domain) + const uint8_t * const parent, const uint8_t *subdomain) { - while (*domain) { - if (priv_getdns_dname_equal(subdomain, domain)) + while (*subdomain) { + if (priv_getdns_dname_equal(parent, subdomain)) return 1; - domain += *domain + 1; + subdomain += *subdomain + 1; } - return *subdomain == 0; + return *parent == 0; } static void _getdns_list2wire(gldns_buffer *buf, getdns_list *l) @@ -447,7 +447,6 @@ inline static size_t _dname_label_count(uint8_t *name) return c; } -#ifdef STUB_NATIVE_DNSSEC static int key_matches_signer(getdns_rrset *dnskey, getdns_rrset *rrset) { rrtype_iter rr_spc, *rr; @@ -1264,7 +1263,6 @@ static void chain_validate_dnssec(chain_head *chain, rrset_iter *tas) } } } -#endif static size_t count_outstanding_requests(chain_head *head) { From 407ecffb6731ddd15bd982558d7eb1c99f1796ff Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 29 Jun 2015 22:23:01 +0200 Subject: [PATCH 21/28] dnssec_status in netreqs --- src/dnssec.c | 28 +++++++++++++++++++++------- src/request-internal.c | 3 +-- src/stub.c | 4 ---- src/types-internal.h | 5 ++--- src/util-internal.c | 38 +++++++++++++++++++++++--------------- 5 files changed, 47 insertions(+), 31 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 8342c9a9..7deb0cb2 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1251,15 +1251,29 @@ static void chain_validate_dnssec(chain_head *chain, rrset_iter *tas) { chain_head *head; + /* The netreq status is the worst for any head */ for (head = chain; head; head = head->next) { switch (chain_head_validate(head, tas)) { - case GETDNS_DNSSEC_SECURE: if (!head->netreq->bogus) - head->netreq->secure = 1; - break; - case GETDNS_DNSSEC_BOGUS : head->netreq->bogus = 1; - head->netreq->secure = 0; - break; - default : break; + + case GETDNS_DNSSEC_SECURE: + if (head->netreq->dnssec_status == + GETDNS_DNSSEC_INDETERMINATE) + head->netreq->dnssec_status = + GETDNS_DNSSEC_SECURE; + break; + + case GETDNS_DNSSEC_INSECURE: + if (head->netreq->dnssec_status != GETDNS_DNSSEC_BOGUS) + head->netreq->dnssec_status = + GETDNS_DNSSEC_INSECURE; + break; + + case GETDNS_DNSSEC_BOGUS : + head->netreq->dnssec_status = GETDNS_DNSSEC_BOGUS; + break; + + default: + break; } } } diff --git a/src/request-internal.c b/src/request-internal.c index 2a72b6c2..60e9c67e 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -96,8 +96,7 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, net_req->state = NET_REQ_NOT_SENT; net_req->owner = owner; - net_req->secure = 0; - net_req->bogus = 0; + net_req->dnssec_status = GETDNS_DNSSEC_INDETERMINATE; net_req->upstream = NULL; net_req->fd = -1; diff --git a/src/stub.c b/src/stub.c index 48fc0bd9..eee4de9a 100644 --- a/src/stub.c +++ b/src/stub.c @@ -1220,10 +1220,6 @@ upstream_read_cb(void *userarg) upstream->tcp.read_buf = NULL; upstream->upstreams->current = 0; - /* TODO: DNSSEC */ - netreq->secure = 0; - netreq->bogus = 0; - stub_cleanup(netreq); /* More to read/write for syncronous lookups? */ diff --git a/src/types-internal.h b/src/types-internal.h index 4c65c576..303718dc 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -196,9 +196,8 @@ typedef struct getdns_network_req /* request class */ uint16_t request_class; - /* result */ - int secure; - int bogus; + /* dnssec status */ + int dnssec_status; /* For stub resolving */ struct getdns_upstream *upstream; diff --git a/src/util-internal.c b/src/util-internal.c index 0273aea1..9a675d2a 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -734,21 +734,22 @@ create_getdns_response(getdns_dns_req *completed_request) continue; nreplies++; - if (netreq->secure) + if (netreq->dnssec_status == GETDNS_DNSSEC_SECURE) nsecure++; - else if (! netreq->bogus) + else if (! netreq->dnssec_status != GETDNS_DNSSEC_BOGUS) ninsecure++; - if (dnssec_return_status && netreq->bogus) + + if (dnssec_return_status && + netreq->dnssec_status == GETDNS_DNSSEC_BOGUS) nbogus++; - else if (GLDNS_RCODE_NOERROR == - GLDNS_RCODE_WIRE(netreq->response)) - nanswers++; + if (! completed_request->dnssec_return_validation_chain) { - if (dnssec_return_status && netreq->bogus) + if (dnssec_return_status && + netreq->dnssec_status == GETDNS_DNSSEC_BOGUS) continue; else if (completed_request->dnssec_return_only_secure - && ! netreq->secure) + && netreq->dnssec_status != GETDNS_DNSSEC_SECURE) continue; } if (!(reply = priv_getdns_create_reply_dict(context, @@ -763,15 +764,18 @@ create_getdns_response(getdns_dns_req *completed_request) result, "canonical_name", canonical_name)) goto error; } + /* TODO: Check instead if canonical_name for request_type + * is in the answer section. + */ + if (GLDNS_RCODE_NOERROR == + GLDNS_RCODE_WIRE(netreq->response)) + nanswers++; + if (dnssec_return_status || completed_request->dnssec_return_validation_chain) { if (getdns_dict_set_int(reply, "dnssec_status", - ( netreq->secure ? GETDNS_DNSSEC_SECURE - : netreq->bogus ? GETDNS_DNSSEC_BOGUS - : rrsigs_in_answer && - context->has_ta ? GETDNS_DNSSEC_INDETERMINATE - : GETDNS_DNSSEC_INSECURE ))) + netreq->dnssec_status)) goto error; } @@ -861,8 +865,12 @@ getdns_apply_network_result(getdns_network_req* netreq, { size_t dname_len; - netreq->secure = ub_res->secure; - netreq->bogus = ub_res->bogus; + if (ub_res->bogus) + netreq->dnssec_status = GETDNS_DNSSEC_BOGUS; + else if (ub_res->secure) + netreq->dnssec_status = GETDNS_DNSSEC_SECURE; + else if (netreq->owner->context->has_ta) + netreq->dnssec_status = GETDNS_DNSSEC_INSECURE; if (ub_res == NULL) /* Timeout */ return GETDNS_RETURN_GOOD; From 8d5ac3afde4ee6632641e674cf3195df394532d1 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 29 Jun 2015 23:32:49 +0200 Subject: [PATCH 22/28] Store dnsreq->name in wire format --- src/context.c | 11 +++-------- src/dnssec.c | 4 ++-- src/general.c | 6 +++++- src/request-internal.c | 9 ++++++--- src/stub.c | 27 ++++++++++++--------------- src/types-internal.h | 3 ++- src/util-internal.c | 19 ++++++++----------- 7 files changed, 38 insertions(+), 41 deletions(-) diff --git a/src/context.c b/src/context.c index fd0c69b3..52b42bd1 100644 --- a/src/context.c +++ b/src/context.c @@ -2302,8 +2302,6 @@ getdns_context_local_namespace_resolve( { getdns_context *context = dnsreq->context; host_name_addrs *hnas; - uint8_t query_name[256]; - size_t query_name_len = sizeof(query_name); uint8_t lookup[256]; getdns_list empty_list = { 0 }; getdns_bindata bindata; @@ -2321,10 +2319,7 @@ getdns_context_local_namespace_resolve( return GETDNS_RETURN_GENERIC_ERROR; /*Do the lookup*/ - if (gldns_str2wire_dname_buf(dnsreq->name,query_name,&query_name_len)) - return GETDNS_RETURN_GENERIC_ERROR; - - (void)memcpy(lookup, query_name, query_name_len); + (void)memcpy(lookup, dnsreq->name, dnsreq->name_len); canonicalize_dname(lookup); if (!(hnas = (host_name_addrs *) @@ -2340,8 +2335,8 @@ getdns_context_local_namespace_resolve( if (!(*response = getdns_dict_create_with_context(context))) return GETDNS_RETURN_GENERIC_ERROR; - bindata.size = query_name_len; - bindata.data = query_name; + bindata.size = dnsreq->name_len; + bindata.data = dnsreq->name; if (getdns_dict_set_bindata(*response, "canonical_name", &bindata)) goto error; diff --git a/src/dnssec.c b/src/dnssec.c index 7deb0cb2..8b240dab 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1760,7 +1760,7 @@ static void add_netreq2val_chain( if (GLDNS_ANCOUNT(netreq->response) == 0 && GLDNS_NSCOUNT(netreq->response) == 0) { - empty_rrset.name = netreq->query + GLDNS_HEADER_SIZE; + empty_rrset.name = netreq->owner->name; empty_rrset.rr_class = GETDNS_RRCLASS_IN; empty_rrset.rr_type = 0; empty_rrset.pkt = netreq->response; @@ -1798,7 +1798,7 @@ static void add_netreq2val_chain( */ /* First find the canonical name for the question */ - q_rrset.name = netreq->query + GLDNS_HEADER_SIZE; + q_rrset.name = netreq->owner->name; q_rrset.rr_type = GETDNS_RRTYPE_CNAME; q_rrset.rr_class = netreq->request_class; q_rrset.pkt = netreq->response; diff --git a/src/general.c b/src/general.c index ae6d609c..8340e5f9 100644 --- a/src/general.c +++ b/src/general.c @@ -39,6 +39,7 @@ #include #include #include "config.h" +#include "gldns/wire2str.h" #include "context.h" #include "types-internal.h" #include "util-internal.h" @@ -136,6 +137,7 @@ submit_network_request(getdns_network_req *netreq) { getdns_return_t r; getdns_dns_req *dns_req = netreq->owner; + char name[1024]; if (dns_req->context->resolution_type == GETDNS_RESOLUTION_RECURSING /* TODO: Until DNSSEC with the new async stub resolver is finished, @@ -159,9 +161,11 @@ submit_network_request(getdns_network_req *netreq) dns_req->context->timeout, &dns_req->timeout))) return r; } + (void) gldns_wire2str_dname_buf(dns_req->name, + dns_req->name_len, name, sizeof(name)); return ub_resolve_async(dns_req->context->unbound_ctx, - dns_req->name, netreq->request_type, netreq->request_class, + name, netreq->request_type, netreq->request_class, netreq, ub_resolve_callback, &(netreq->unbound_id)) ? GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; } diff --git a/src/request-internal.c b/src/request-internal.c index 60e9c67e..cfa7871c 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -203,8 +203,6 @@ dns_req_free(getdns_dns_req * req) req->timeout.timeout_cb = NULL; } - /* free strduped name */ - GETDNS_FREE(req->my_mf, req->name); GETDNS_FREE(req->my_mf, req); } @@ -358,7 +356,12 @@ dns_req_new(getdns_context *context, getdns_eventloop *loop, result->netreqs[1] = NULL; result->my_mf = context->mf; - result->name = getdns_strdup(&(result->my_mf), name); + + result->name_len = sizeof(result->name); + if (gldns_str2wire_dname_buf(name, result->name, &result->name_len)) { + GETDNS_FREE(result->my_mf, result); + return NULL; + } result->context = context; result->loop = loop; result->canceled = 0; diff --git a/src/stub.c b/src/stub.c index eee4de9a..09d31f05 100644 --- a/src/stub.c +++ b/src/stub.c @@ -272,10 +272,10 @@ is_starttls_response(getdns_network_req *netreq) priv_getdns_rdf_iter rdf_iter_storage, *rdf_iter; uint16_t rr_type; gldns_pkt_section section; - uint8_t starttls_name_space[256], - *starttls_name = starttls_name_space; + uint8_t starttls_name_space[256], *starttls_name; uint8_t owner_name_space[256], *owner_name; - size_t starttls_name_len = 256, owner_name_len; + size_t starttls_name_len = sizeof(starttls_name_space); + size_t owner_name_len = sizeof(owner_name_space);; /* Servers that are not STARTTLS aware will refuse the CH query*/ if (GLDNS_RCODE_NOERROR != GLDNS_RCODE_WIRE(netreq->response)) @@ -284,9 +284,6 @@ is_starttls_response(getdns_network_req *netreq) if (GLDNS_ANCOUNT(netreq->response) != 1) return 0; - (void) gldns_str2wire_dname_buf( - netreq->owner->name, starttls_name_space, &starttls_name_len); - for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage , netreq->response , netreq->response_len) @@ -295,25 +292,25 @@ is_starttls_response(getdns_network_req *netreq) section = priv_getdns_rr_iter_section(rr_iter); rr_type = gldns_read_uint16(rr_iter->rr_type); - if (section != GLDNS_SECTION_ANSWER || rr_type != GETDNS_RRTYPE_TXT) + if (section != GLDNS_SECTION_ANSWER + || rr_type != GETDNS_RRTYPE_TXT) continue; owner_name = priv_getdns_owner_if_or_as_decompressed( rr_iter, owner_name_space, &owner_name_len); - if (!priv_getdns_dname_equal(starttls_name, owner_name)) + if (!priv_getdns_dname_equal(netreq->owner->name, owner_name)) continue; if (!(rdf_iter = priv_getdns_rdf_iter_init( &rdf_iter_storage, rr_iter))) continue; - /* re-use the starttls_name for the response dname*/ - starttls_name = priv_getdns_rdf_if_or_as_decompressed( - rdf_iter,starttls_name_space,&starttls_name_len); - if (priv_getdns_dname_equal(starttls_name, owner_name)) + + if ((starttls_name = priv_getdns_rdf_if_or_as_decompressed( + rdf_iter, starttls_name_space, &starttls_name_len)) && + priv_getdns_dname_equal(starttls_name, owner_name)) return 1; - else - return 0; - continue; + + return 0; } return 0; } diff --git a/src/types-internal.h b/src/types-internal.h index 303718dc..2becfd88 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -237,7 +237,8 @@ typedef struct getdns_dns_req { getdns_rbnode_t node; /* name */ - char *name; + uint8_t name[256]; + size_t name_len; /* canceled flag */ int canceled; diff --git a/src/util-internal.c b/src/util-internal.c index 9a675d2a..78be575c 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -544,8 +544,7 @@ priv_getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, if ((r = getdns_dict_set_dict(result, "header", header))) goto error; - (void) gldns_str2wire_dname_buf( - req->owner->name, canonical_name_space, &canonical_name_len); + canonical_name = req->owner->name; for ( rr_iter = priv_getdns_rr_iter_init(&rr_iter_storage , req->response @@ -863,8 +862,6 @@ getdns_return_t getdns_apply_network_result(getdns_network_req* netreq, struct ub_result* ub_res) { - size_t dname_len; - if (ub_res->bogus) netreq->dnssec_status = GETDNS_DNSSEC_BOGUS; else if (ub_res->secure) @@ -909,17 +906,17 @@ getdns_apply_network_result(getdns_network_req* netreq, GLDNS_RA_SET(netreq->response); GLDNS_RCODE_SET(netreq->response, ub_res->rcode); - dname_len = netreq->max_udp_payload_size - GLDNS_HEADER_SIZE; - if (gldns_str2wire_dname_buf(netreq->owner->name, - netreq->response + GLDNS_HEADER_SIZE, &dname_len)) - return GETDNS_RETURN_GENERIC_ERROR; + (void) memcpy( netreq->response + GLDNS_HEADER_SIZE + , netreq->owner->name, netreq->owner->name_len); - gldns_write_uint16( netreq->response + GLDNS_HEADER_SIZE + dname_len + gldns_write_uint16( netreq->response + GLDNS_HEADER_SIZE + + netreq->owner->name_len , netreq->request_type); - gldns_write_uint16( netreq->response + GLDNS_HEADER_SIZE + dname_len + 2 + gldns_write_uint16( netreq->response + GLDNS_HEADER_SIZE + + netreq->owner->name_len + 2 , netreq->request_class); - netreq->response_len = GLDNS_HEADER_SIZE + dname_len + 4; + netreq->response_len = GLDNS_HEADER_SIZE + netreq->owner->name_len + 4; return GETDNS_RETURN_GOOD; } From 3cd9caa70487c3718ccf5d7c39ba6a1f7f9715a7 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Mon, 29 Jun 2015 23:48:46 +0200 Subject: [PATCH 23/28] Evaluate DNSSEC only with stub resolution --- src/dnssec.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 8b240dab..115b95c4 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1360,7 +1360,7 @@ static void check_chain_complete(chain_head *chain) getdns_list *val_chain_list; getdns_dict *response_dict; #ifdef STUB_NATIVE_DNSSEC - uint8_t tas_spc[4096], *tas; + uint8_t tas_spc[4096], *tas = tas_spc; size_t tas_sz; gldns_buffer tas_buf; rrset_iter tas_iter; @@ -1375,19 +1375,25 @@ static void check_chain_complete(chain_head *chain) context = dnsreq->context; #ifdef STUB_NATIVE_DNSSEC - gldns_buffer_init_frm_data(&tas_buf, (tas = tas_spc), sizeof(tas_spc)); - _getdns_list2wire(&tas_buf, context->dnssec_trust_anchors); - if ((tas_sz = gldns_buffer_position(&tas_buf)) > sizeof(tas_spc)) { - if ((tas = GETDNS_XMALLOC(dnsreq->my_mf, uint8_t, tas_sz))) { - gldns_buffer_init_frm_data(&tas_buf, tas, tas_sz); - _getdns_list2wire(&tas_buf, context->dnssec_trust_anchors); - } - } else if (! GLDNS_ANCOUNT(tas)) - tas = NULL; + if (chain->netreq->unbound_id == -1) { + gldns_buffer_init_frm_data(&tas_buf, tas, sizeof(tas_spc)); + _getdns_list2wire(&tas_buf, context->dnssec_trust_anchors); + if ((tas_sz = gldns_buffer_position(&tas_buf)) + > sizeof(tas_spc)) { + if ((tas = GETDNS_XMALLOC( + dnsreq->my_mf, uint8_t, tas_sz))) { + gldns_buffer_init_frm_data( + &tas_buf, tas, tas_sz); + _getdns_list2wire( + &tas_buf, context->dnssec_trust_anchors); + } + } else if (! GLDNS_ANCOUNT(tas)) + tas = NULL; - if (tas) - chain_validate_dnssec(chain, - rrset_iter_init(&tas_iter, tas, tas_sz)); + if (tas) + chain_validate_dnssec(chain, + rrset_iter_init(&tas_iter, tas, tas_sz)); + } #endif val_chain_list = dnsreq->dnssec_return_validation_chain From 996b09ba2b8c2691ff3b497a53ce458a50c6c7fa Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 30 Jun 2015 00:12:30 +0200 Subject: [PATCH 24/28] Reminder for single RRSIG per RRSET return With the dnssec_return_validation_chain extension --- src/dnssec.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/dnssec.c b/src/dnssec.c index 115b95c4..4c2f2653 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -1375,6 +1375,11 @@ static void check_chain_complete(chain_head *chain) context = dnsreq->context; #ifdef STUB_NATIVE_DNSSEC + /* Perform validation only on GETDNS_RESOLUTION_STUB (unbound_id == -1) + * TODO: When minimizing the validation chain (i.e. returning a single + * RRSIG per RRSET, it might be usefull to perform a fake dnssec + * validation to find out which RRSIGs should be returned. + */ if (chain->netreq->unbound_id == -1) { gldns_buffer_init_frm_data(&tas_buf, tas, sizeof(tas_spc)); _getdns_list2wire(&tas_buf, context->dnssec_trust_anchors); From 41cf772fb33dfea2701350ea07f89d523faf3ed7 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Tue, 30 Jun 2015 14:43:52 +0200 Subject: [PATCH 25/28] Trust anchors in wireformat in context --- src/context.c | 98 +++++++++++++++++--------- src/context.h | 7 +- src/dict.h | 4 ++ src/dnssec.c | 166 +++++++++++++------------------------------- src/dnssec.h | 4 +- src/list.h | 4 ++ src/util-internal.c | 109 ++++++++++++++++++++++++++--- src/util-internal.h | 6 +- 8 files changed, 234 insertions(+), 164 deletions(-) diff --git a/src/context.c b/src/context.c index 52b42bd1..7abdbedb 100644 --- a/src/context.c +++ b/src/context.c @@ -771,6 +771,7 @@ getdns_context_create_with_extended_memory_functions( getdns_return_t r; struct getdns_context *result = NULL; mf_union mf; + gldns_buffer gbuf; if (!context || !malloc || !realloc || !free) return GETDNS_RETURN_INVALID_PARAMETER; @@ -815,7 +816,30 @@ getdns_context_create_with_extended_memory_functions( result->append_name = GETDNS_APPEND_NAME_ALWAYS; result->suffix = NULL; - result->dnssec_trust_anchors = NULL; + gldns_buffer_init_frm_data(&gbuf, result->trust_anchors_spc + , sizeof(result->trust_anchors_spc)); + + if (!_getdns_parse_ta_file(NULL, &gbuf)) { + result->trust_anchors = NULL; + result->trust_anchors_len = 0; + + } else if ((result->trust_anchors_len = gldns_buffer_position(&gbuf)) + > sizeof(result->trust_anchors_spc)) { + + if ((result->trust_anchors = GETDNS_XMALLOC( + result->mf, uint8_t, result->trust_anchors_len))) { + + gldns_buffer_init_frm_data(&gbuf + , result->trust_anchors + , result->trust_anchors_len); + if (!_getdns_parse_ta_file(NULL, &gbuf)) { + result->trust_anchors = NULL; + result->trust_anchors_len = 0; + } + } + } else + result->trust_anchors = result->trust_anchors_spc; + result->upstreams = NULL; result->edns_extended_rcode = 0; @@ -828,7 +852,7 @@ getdns_context_create_with_extended_memory_functions( goto error; result->fchg_resolvconf = NULL; - result->fchg_hosts = NULL; + result->fchg_hosts = NULL; if (set_from_os && (r = set_os_defaults(result))) goto error; @@ -840,12 +864,6 @@ getdns_context_create_with_extended_memory_functions( result->limit_outstanding_queries = 0; result->return_dnssec_status = GETDNS_EXTENSION_FALSE; - if (! (result->dnssec_trust_anchors = - getdns_list_create_with_context(result))) - goto error; - result->has_ta = priv_getdns_parse_ta_file( - NULL, result->dnssec_trust_anchors); - /* unbound context is initialized here */ /* Unbound needs SSL to be init'ed this early when TLS is used. However we * don't know that till later so we will have to do this every time. */ @@ -944,7 +962,10 @@ getdns_context_destroy(struct getdns_context *context) getdns_list_destroy(context->dns_root_servers); getdns_list_destroy(context->suffix); - getdns_list_destroy(context->dnssec_trust_anchors); + + if (context->trust_anchors && + context->trust_anchors != context->trust_anchors_spc) + GETDNS_FREE(context->mf, context->trust_anchors); /* destroy the contexts */ if (context->unbound_ctx) @@ -1076,7 +1097,7 @@ rebuild_ub_ctx(struct getdns_context* context) { context->dns_transport); /* Set default trust anchor */ - if (context->has_ta) { + if (context->trust_anchors) { (void) ub_ctx_add_ta_file( context->unbound_ctx, TRUST_ANCHOR_FILE); } @@ -1442,23 +1463,26 @@ getdns_context_set_suffix(struct getdns_context *context, struct getdns_list * v * */ getdns_return_t -getdns_context_set_dnssec_trust_anchors(struct getdns_context *context, - struct getdns_list * value) +getdns_context_set_dnssec_trust_anchors( + getdns_context *context, getdns_list *value) { - struct getdns_list *copy = NULL; - RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - if (value != NULL) { - if (getdns_list_copy(value, ©) != GETDNS_RETURN_GOOD) { - return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; - } - value = copy; - } - getdns_list_destroy(context->dnssec_trust_anchors); - context->dnssec_trust_anchors = value; + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS); + if (context->trust_anchors && + context->trust_anchors != context->trust_anchors_spc) + GETDNS_FREE(context->mf, context->trust_anchors); - return GETDNS_RETURN_GOOD; + if (value) { + context->trust_anchors_len = sizeof(context->trust_anchors_spc); + context->trust_anchors = _getdns_list2wire(value, + context->trust_anchors_spc, &context->trust_anchors_len, + &context->mf); + } else { + context->trust_anchors = NULL; + context->trust_anchors_len = 0; + } + dispatch_updated(context, GETDNS_CONTEXT_CODE_DNSSEC_TRUST_ANCHORS); + return GETDNS_RETURN_GOOD; } /* getdns_context_set_dnssec_trust_anchors */ static void @@ -2482,15 +2506,23 @@ getdns_context_get_suffix(getdns_context *context, getdns_list **value) { } getdns_return_t -getdns_context_get_dnssec_trust_anchors(getdns_context *context, - getdns_list **value) { - RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); - RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER); - *value = NULL; - if (context->dnssec_trust_anchors) { - return getdns_list_copy(context->dnssec_trust_anchors, value); - } - return GETDNS_RETURN_GOOD; +getdns_context_get_dnssec_trust_anchors( + getdns_context *context, getdns_list **value) +{ + RETURN_IF_NULL(context, GETDNS_RETURN_INVALID_PARAMETER); + RETURN_IF_NULL(value, GETDNS_RETURN_INVALID_PARAMETER); + + if (context->trust_anchors) { + if ((*value = getdns_list_create_with_context(context))) + _getdns_wire2list( context->trust_anchors + , context->trust_anchors_len + , *value); + else + return GETDNS_RETURN_MEMORY_ERROR; + } else + *value = NULL; + + return GETDNS_RETURN_GOOD; } getdns_return_t diff --git a/src/context.h b/src/context.h index 3ad6b342..c4c77609 100644 --- a/src/context.h +++ b/src/context.h @@ -135,7 +135,8 @@ struct getdns_context { struct getdns_list *dns_root_servers; getdns_append_name_t append_name; struct getdns_list *suffix; - struct getdns_list *dnssec_trust_anchors; + uint8_t *trust_anchors; + size_t trust_anchors_len; getdns_upstreams *upstreams; getdns_transport_t dns_transport; getdns_base_transport_t dns_base_transports[GETDNS_BASE_TRANSPORT_MAX]; @@ -163,7 +164,7 @@ struct getdns_context { /* A tree to hold local host information*/ getdns_rbtree_t local_hosts; - int has_ta; /* No DNSSEC without trust anchor */ + int return_dnssec_status; /* which resolution type the contexts are configured for @@ -189,6 +190,8 @@ struct getdns_context { struct filechg *fchg_resolvconf; struct filechg *fchg_hosts; + uint8_t trust_anchors_spc[1024]; + }; /* getdns_context */ /** internal functions **/ diff --git a/src/dict.h b/src/dict.h index 2d45b651..0a74cc14 100644 --- a/src/dict.h +++ b/src/dict.h @@ -70,6 +70,10 @@ struct getdns_dict struct mem_funcs mf; }; +inline struct getdns_dict *_getdns_dict_create_with_mf(struct mem_funcs *mf) +{ return getdns_dict_create_with_extended_memory_functions( + mf->mf_arg, mf->mf.ext.malloc, mf->mf.ext.realloc, mf->mf.ext.free); } + #endif /* dict.h */ diff --git a/src/dnssec.c b/src/dnssec.c index 4c2f2653..4b804d36 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -67,58 +67,6 @@ static int is_subdomain( return *parent == 0; } -static void _getdns_list2wire(gldns_buffer *buf, getdns_list *l) -{ - getdns_dict *rr_dict; - getdns_return_t r; - size_t i, pkt_start, ancount; - uint32_t qtype, qclass; - getdns_bindata *qname; - - pkt_start = gldns_buffer_position(buf); - /* Empty header */ - gldns_buffer_write_u32(buf, 0); - gldns_buffer_write_u32(buf, 0); - gldns_buffer_write_u32(buf, 0); - - for ( i = 0 - ; (r = getdns_list_get_dict(l, i, &rr_dict)) - != GETDNS_RETURN_NO_SUCH_LIST_ITEM - ; i++ ) { - - if (r) { - if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED) - continue; - else - break; - } - if (getdns_dict_get_int(rr_dict, "qtype", &qtype) || - getdns_dict_get_bindata(rr_dict, "qname", &qname)) - continue; - (void) getdns_dict_get_int(rr_dict, "qclass", &qclass); - gldns_buffer_write(buf, qname->data, qname->size); - gldns_buffer_write_u16(buf, (uint16_t)qtype); - gldns_buffer_write_u16(buf, (uint16_t)qclass); - gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, 1); - break; - } - for ( i = 0, ancount = 0 - ; (r = getdns_list_get_dict(l, i, &rr_dict)) - != GETDNS_RETURN_NO_SUCH_LIST_ITEM - ; i++ ) { - - if (r) { - if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED) - continue; - else - break; - } - if (priv_getdns_rr_dict2wire(rr_dict, buf) == GETDNS_RETURN_GOOD) - ancount++; - } - gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_ANCOUNT_OFF, ancount); -} - #if defined(SEC_DEBUG) && SEC_DEBUG inline static void debug_sec_print_rr(const char *msg, priv_getdns_rr_iter *rr) { @@ -1332,8 +1280,9 @@ static void append_rrs2val_chain_list(getdns_context *ctxt, for ( rr = rrtype_iter_init(&rr_spc, rrset) ; rr; rr = rrtype_iter_next(rr)) { - rr_dict = priv_getdns_rr_iter2rr_dict(ctxt, &rr->rr_i); - if (!rr_dict) continue; + if (!(rr_dict = priv_getdns_rr_iter2rr_dict( + &ctxt->mf, &rr->rr_i))) + continue; (void)getdns_list_append_dict(val_chain_list, rr_dict); getdns_dict_destroy(rr_dict); @@ -1341,8 +1290,9 @@ static void append_rrs2val_chain_list(getdns_context *ctxt, for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset) ; rrsig; rrsig = rrsig_iter_next(rrsig)) { - rr_dict=priv_getdns_rr_iter2rr_dict(ctxt,&rrsig->rr_i); - if (!rr_dict) continue; + if (!(rr_dict = priv_getdns_rr_iter2rr_dict( + &ctxt->mf, &rrsig->rr_i))) + continue; (void)getdns_list_append_dict(val_chain_list, rr_dict); getdns_dict_destroy(rr_dict); @@ -1360,9 +1310,6 @@ static void check_chain_complete(chain_head *chain) getdns_list *val_chain_list; getdns_dict *response_dict; #ifdef STUB_NATIVE_DNSSEC - uint8_t tas_spc[4096], *tas = tas_spc; - size_t tas_sz; - gldns_buffer tas_buf; rrset_iter tas_iter; #endif @@ -1380,25 +1327,9 @@ static void check_chain_complete(chain_head *chain) * RRSIG per RRSET, it might be usefull to perform a fake dnssec * validation to find out which RRSIGs should be returned. */ - if (chain->netreq->unbound_id == -1) { - gldns_buffer_init_frm_data(&tas_buf, tas, sizeof(tas_spc)); - _getdns_list2wire(&tas_buf, context->dnssec_trust_anchors); - if ((tas_sz = gldns_buffer_position(&tas_buf)) - > sizeof(tas_spc)) { - if ((tas = GETDNS_XMALLOC( - dnsreq->my_mf, uint8_t, tas_sz))) { - gldns_buffer_init_frm_data( - &tas_buf, tas, tas_sz); - _getdns_list2wire( - &tas_buf, context->dnssec_trust_anchors); - } - } else if (! GLDNS_ANCOUNT(tas)) - tas = NULL; - - if (tas) - chain_validate_dnssec(chain, - rrset_iter_init(&tas_iter, tas, tas_sz)); - } + if (chain->netreq->unbound_id == -1 && context->trust_anchors) + chain_validate_dnssec(chain, rrset_iter_init(&tas_iter, + context->trust_anchors, context->trust_anchors_len)); #endif val_chain_list = dnsreq->dnssec_return_validation_chain @@ -1432,13 +1363,7 @@ static void check_chain_complete(chain_head *chain) getdns_list_destroy(val_chain_list); } - -#ifdef STUB_NATIVE_DNSSEC - if (tas && tas != tas_spc) - GETDNS_FREE(dnsreq->my_mf, tas); -#endif /* Final user callback */ - priv_getdns_call_user_callback(dnsreq, response_dict); } @@ -2275,26 +2200,17 @@ done_free_trusted: return r; } /* getdns_validate_dnssec */ -int -priv_getdns_parse_ta_file(time_t *ta_mtime, getdns_list *ta_rrs) +uint16_t +_getdns_parse_ta_file(time_t *ta_mtime, gldns_buffer *gbuf) { struct gldns_file_parse_state pst; struct stat st; - struct { - uint16_t id; - uint16_t flags; - uint16_t qdcount; - uint16_t ancount; - uint16_t nscount; - uint16_t arcount; - uint8_t rr[8192]; /* Reasonable max size for a single RR */ - } pkt; + uint8_t rr[8192]; /* Reasonable size for a single DNSKEY or DS RR */ size_t len, dname_len; FILE *in; - priv_getdns_rr_iter rr_iter; - getdns_dict *rr_dict = NULL; - int ta_count = 0; + uint16_t ta_count = 0; + size_t pkt_start; if (stat(TRUST_ANCHOR_FILE, &st) != 0) return 0; @@ -2305,38 +2221,34 @@ priv_getdns_parse_ta_file(time_t *ta_mtime, getdns_list *ta_rrs) if (!(in = fopen(TRUST_ANCHOR_FILE, "r"))) return 0; - pkt.id = pkt.flags = pkt.qdcount = pkt.nscount = pkt.arcount = 0; - pkt.ancount = htons(1); - memset(&pst, 0, sizeof(pst)); pst.default_ttl = 3600; pst.lineno = 1; + pkt_start = gldns_buffer_position(gbuf); + /* Empty header */ + gldns_buffer_write_u32(gbuf, 0); + gldns_buffer_write_u32(gbuf, 0); + gldns_buffer_write_u32(gbuf, 0); + while (!feof(in)) { - len = sizeof(pkt.rr); + len = sizeof(rr); dname_len = 0; - if (gldns_fp2wire_rr_buf(in, pkt.rr, &len, &dname_len, &pst)) + if (gldns_fp2wire_rr_buf(in, rr, &len, &dname_len, &pst)) break; if (len == 0) /* empty, $TTL, $ORIGIN */ continue; - if (gldns_wirerr_get_type(pkt.rr, len, dname_len) + if (gldns_wirerr_get_type(rr, len, dname_len) != LDNS_RR_TYPE_DS && - gldns_wirerr_get_type(pkt.rr, len, dname_len) + gldns_wirerr_get_type(rr, len, dname_len) != LDNS_RR_TYPE_DNSKEY) continue; - if (!priv_getdns_rr_iter_init(&rr_iter, (void *)&pkt, sizeof(pkt))) - break; - if (!(rr_dict = priv_getdns_rr_iter2rr_dict(NULL, &rr_iter))) - break; - if (ta_rrs && getdns_list_append_dict(ta_rrs, rr_dict)) - break; - getdns_dict_destroy(rr_dict); - rr_dict = NULL; + + gldns_buffer_write(gbuf, rr, len); ta_count++; } - if (rr_dict) - getdns_dict_destroy(rr_dict); fclose(in); + gldns_buffer_write_u16_at(gbuf, pkt_start+GLDNS_ANCOUNT_OFF, ta_count); return ta_count; } @@ -2344,9 +2256,29 @@ priv_getdns_parse_ta_file(time_t *ta_mtime, getdns_list *ta_rrs) getdns_list * getdns_root_trust_anchor(time_t *utc_date_of_anchor) { - getdns_list *ta_rrs = getdns_list_create(); - (void) priv_getdns_parse_ta_file(utc_date_of_anchor, ta_rrs); + gldns_buffer *gbuf; + getdns_list *ta_rrs; + + if (!(ta_rrs = getdns_list_create())) + return NULL; + + if (!(gbuf = gldns_buffer_new(4096))) + goto error_free_ta_rrs; + + if (!_getdns_parse_ta_file(utc_date_of_anchor, gbuf)) + goto error_free_gbuf; + + _getdns_wire2list( gldns_buffer_export(gbuf) + , gldns_buffer_position(gbuf), ta_rrs); + + gldns_buffer_free(gbuf); return ta_rrs; + +error_free_gbuf: + gldns_buffer_free(gbuf); +error_free_ta_rrs: + getdns_list_destroy(ta_rrs); + return NULL; } /* dnssec.c */ diff --git a/src/dnssec.h b/src/dnssec.h index 30af7705..30ab6b3e 100644 --- a/src/dnssec.h +++ b/src/dnssec.h @@ -39,12 +39,14 @@ #define DNSSEC_H_ #include "getdns/getdns.h" +#include "config.h" +#include "gldns/gbuffer.h" #include "types-internal.h" /* Do some additional requests to fetch the complete validation chain */ void priv_getdns_get_validation_chain(getdns_dns_req *dns_req); -int priv_getdns_parse_ta_file(time_t *ta_mtime, getdns_list *ta_rrs); +uint16_t _getdns_parse_ta_file(time_t *ta_mtime, gldns_buffer *gbuf); #endif diff --git a/src/list.h b/src/list.h index 0fc320a4..113361ae 100644 --- a/src/list.h +++ b/src/list.h @@ -74,6 +74,10 @@ struct getdns_list struct mem_funcs mf; }; +inline struct getdns_list *_getdns_list_create_with_mf(struct mem_funcs *mf) +{ return getdns_list_create_with_extended_memory_functions( + mf->mf_arg, mf->mf.ext.malloc, mf->mf.ext.realloc, mf->mf.ext.free); } + #endif /* list.h */ diff --git a/src/util-internal.c b/src/util-internal.c index 78be575c..69475e59 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -179,7 +179,7 @@ sockaddr_to_dict(struct getdns_context *context, struct sockaddr_storage *addres } getdns_dict * -priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i) +priv_getdns_rr_iter2rr_dict(struct mem_funcs *mf, priv_getdns_rr_iter *i) { getdns_dict *rr_dict, *rdata_dict; getdns_bindata bindata; @@ -192,7 +192,7 @@ priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i) uint16_t rr_type; assert(i); - if (!(rr_dict = getdns_dict_create_with_context(context))) + if (!(rr_dict = _getdns_dict_create_with_mf(mf))) return NULL; bindata.data = priv_getdns_owner_if_or_as_decompressed( @@ -248,8 +248,8 @@ priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i) goto error; } - if (!(rdata_dict = getdns_dict_create_with_context(context))) - goto error; + if (!(rdata_dict = _getdns_dict_create_with_mf(mf))) + return NULL; if (i->rr_type + 10 <= i->nxt) { bindata.size = i->nxt - (i->rr_type + 10); @@ -330,7 +330,8 @@ priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i) /* list with rdf values */ if (! repeat_list && !(repeat_list = - getdns_list_create_with_context(context))) + _getdns_list_create_with_mf(mf))) + goto rdata_error; switch (val_type) { @@ -357,7 +358,7 @@ priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i) if (repeat_dict) { if (! repeat_list && !(repeat_list = - getdns_list_create_with_context(context))) + _getdns_list_create_with_mf(mf))) goto rdata_error; if (getdns_list_append_dict( @@ -368,7 +369,7 @@ priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i) repeat_dict = NULL; } if (!(repeat_dict = - getdns_dict_create_with_context(context))) + _getdns_dict_create_with_mf(mf))) goto rdata_error; } assert(repeat_dict); @@ -393,7 +394,7 @@ priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i) } if (repeat_dict) { if (!repeat_list && !(repeat_list = - getdns_list_create_with_context(context))) + _getdns_list_create_with_mf(mf))) goto rdata_error; if (getdns_list_append_dict(repeat_list, repeat_dict)) goto rdata_error; @@ -553,7 +554,7 @@ priv_getdns_create_reply_dict(getdns_context *context, getdns_network_req *req, ; rr_iter = priv_getdns_rr_iter_next(rr_iter)) { if (!set_dict(&rr_dict, - priv_getdns_rr_iter2rr_dict(context, rr_iter))) + priv_getdns_rr_iter2rr_dict(&context->mf, rr_iter))) continue; section = priv_getdns_rr_iter_section(rr_iter); @@ -866,7 +867,7 @@ getdns_apply_network_result(getdns_network_req* netreq, netreq->dnssec_status = GETDNS_DNSSEC_BOGUS; else if (ub_res->secure) netreq->dnssec_status = GETDNS_DNSSEC_SECURE; - else if (netreq->owner->context->has_ta) + else if (netreq->owner->context->trust_anchors) netreq->dnssec_status = GETDNS_DNSSEC_INSECURE; if (ub_res == NULL) /* Timeout */ @@ -989,4 +990,92 @@ priv_getdns_validate_dname(const char* dname) { } /* priv_getdns_validate_dname */ +static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) +{ + getdns_dict *rr_dict; + getdns_return_t r; + size_t i, pkt_start, ancount; + uint32_t qtype, qclass; + getdns_bindata *qname; + + pkt_start = gldns_buffer_position(buf); + /* Empty header */ + gldns_buffer_write_u32(buf, 0); + gldns_buffer_write_u32(buf, 0); + gldns_buffer_write_u32(buf, 0); + + for ( i = 0 + ; (r = getdns_list_get_dict(l, i, &rr_dict)) + != GETDNS_RETURN_NO_SUCH_LIST_ITEM + ; i++ ) { + + if (r) { + if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED) + continue; + else + break; + } + if (getdns_dict_get_int(rr_dict, "qtype", &qtype) || + getdns_dict_get_bindata(rr_dict, "qname", &qname)) + continue; + (void) getdns_dict_get_int(rr_dict, "qclass", &qclass); + gldns_buffer_write(buf, qname->data, qname->size); + gldns_buffer_write_u16(buf, (uint16_t)qtype); + gldns_buffer_write_u16(buf, (uint16_t)qclass); + gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, 1); + break; + } + for ( i = 0, ancount = 0 + ; (r = getdns_list_get_dict(l, i, &rr_dict)) + != GETDNS_RETURN_NO_SUCH_LIST_ITEM + ; i++ ) { + + if (r) { + if (r == GETDNS_RETURN_WRONG_TYPE_REQUESTED) + continue; + else + break; + } + if (priv_getdns_rr_dict2wire(rr_dict, buf) == GETDNS_RETURN_GOOD) + ancount++; + } + gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_ANCOUNT_OFF, ancount); +} + +uint8_t *_getdns_list2wire( + getdns_list *l, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf) +{ + gldns_buffer gbuf; + size_t sz; + + gldns_buffer_init_frm_data(&gbuf, buf, *buf_len); + _getdns_list2wire_buf(&gbuf, l); + + if ((sz = gldns_buffer_position(&gbuf)) <= *buf_len) + return buf; + + if (!(buf = GETDNS_XMALLOC(*mf, uint8_t, (*buf_len = sz)))) + return NULL; + + gldns_buffer_init_frm_data(&gbuf, buf, sz); + _getdns_list2wire_buf(&gbuf, l); + return buf; +} + +void _getdns_wire2list(uint8_t *pkt, size_t pkt_len, getdns_list *l) +{ + priv_getdns_rr_iter rr_spc, *rr; + getdns_dict *rr_dict; + + for ( rr = priv_getdns_rr_iter_init(&rr_spc, pkt, pkt_len) + ; rr ; rr = priv_getdns_rr_iter_next(rr)) { + + if (!(rr_dict = priv_getdns_rr_iter2rr_dict(&l->mf, rr))) + continue; + + (void)getdns_list_append_dict(l, rr_dict); + getdns_dict_destroy(rr_dict); + } +} + /* util-internal.c */ diff --git a/src/util-internal.h b/src/util-internal.h index b81588f5..5963fecf 100644 --- a/src/util-internal.h +++ b/src/util-internal.h @@ -123,7 +123,7 @@ getdns_return_t sockaddr_to_dict(struct getdns_context *context, struct sockaddr_storage *sockaddr, struct getdns_dict ** output); getdns_dict * -priv_getdns_rr_iter2rr_dict(getdns_context *context, priv_getdns_rr_iter *i); +priv_getdns_rr_iter2rr_dict(struct mem_funcs *mf, priv_getdns_rr_iter *i); struct getdns_dns_req; struct getdns_dict *create_getdns_response(struct getdns_dns_req *completed_request); @@ -135,6 +135,10 @@ getdns_return_t priv_getdns_validate_dname(const char* dname); int priv_getdns_dname_equal(const uint8_t *s1, const uint8_t *s2); +uint8_t *_getdns_list2wire( + getdns_list *l, uint8_t *buf, size_t *buf_len, struct mem_funcs *mf); + +void _getdns_wire2list(uint8_t *pkt, size_t pkt_len, getdns_list *l); /** From 2b3aa843376b110f69ace8c24781f5526829638b Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 1 Jul 2015 14:38:24 +0200 Subject: [PATCH 26/28] getdns_query show output of getdns_validate_dnssec --- src/test/getdns_query.c | 60 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/test/getdns_query.c b/src/test/getdns_query.c index 3fcf0920..090f5c71 100644 --- a/src/test/getdns_query.c +++ b/src/test/getdns_query.c @@ -135,6 +135,64 @@ print_usage(FILE *out, const char *progname) fprintf(out, "\t-q\tQuiet mode - don't print response\n"); } +static getdns_return_t validate_chain(getdns_dict *response) +{ + getdns_return_t r; + getdns_list *validation_chain; + getdns_list *replies_tree; + getdns_dict *reply; + getdns_list *answer; + getdns_list *trust_anchor; + size_t i; + int s; + + if (!(trust_anchor = getdns_root_trust_anchor(NULL))) + return GETDNS_RETURN_GENERIC_ERROR; + + if ((r = getdns_dict_get_list( + response, "validation_chain", &validation_chain))) + return r; + + if ((r = getdns_dict_get_list( + response, "replies_tree", &replies_tree))) + return r; + + i = 0; + while (!(r = getdns_list_get_dict(replies_tree, i++, &reply))) { + + if ((r = getdns_dict_get_list(reply, "answer", &answer))) + return r; + + fprintf( stdout + , "reply %zu, getdns_validate_dnssec returned: ", i); + switch ((s = getdns_validate_dnssec( + answer, validation_chain, trust_anchor))) { + + case GETDNS_DNSSEC_SECURE: + fprintf(stdout, "GETDNS_DNSSEC_SECURE\n"); + break; + case GETDNS_DNSSEC_BOGUS: + fprintf(stdout, "GETDNS_DNSSEC_BOGUS\n"); + break; + case GETDNS_DNSSEC_INDETERMINATE: + fprintf(stdout, "GETDNS_DNSSEC_INDETERMINATE\n"); + break; + case GETDNS_DNSSEC_INSECURE: + fprintf(stdout, "GETDNS_DNSSEC_INSECURE\n"); + break; + case GETDNS_DNSSEC_NOT_PERFORMED: + fprintf(stdout, "GETDNS_DNSSEC_NOT_PERFORMED\n"); + break; + default: + fprintf(stdout, "%d\n", (int)s); + } + } + if (r != GETDNS_RETURN_NO_SUCH_LIST_ITEM) + return r; + + return GETDNS_RETURN_GOOD; +} + void callback(getdns_context *context, getdns_callback_type_t callback_type, getdns_dict *response, void *userarg, getdns_transaction_t trans_id) { @@ -147,6 +205,7 @@ void callback(getdns_context *context, getdns_callback_type_t callback_type, : getdns_pretty_print_dict(response))) { fprintf(stdout, "ASYNC response:\n%s\n", response_str); + validate_chain(response); free(response_str); } fprintf(stderr, @@ -543,6 +602,7 @@ main(int argc, char **argv) fprintf( stdout, "SYNC response:\n%s\n" , response_str); + validate_chain(response); free(response_str); } else { r = GETDNS_RETURN_MEMORY_ERROR; From f92dd5ac0dc5c2b9c8acfda5fef9865790b58a1d Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Wed, 1 Jul 2015 21:50:47 +0200 Subject: [PATCH 27/28] getdns_validate_dnssec with new DNSSEC code --- src/dnssec.c | 579 ++++++++++++++------------------------------------- 1 file changed, 156 insertions(+), 423 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 4b804d36..8eca7759 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -53,6 +53,7 @@ #include "gldns/parseutil.h" #include "general.h" #include "dict.h" +#include "list.h" /* parent is same or parent of subdomain,*/ static int is_subdomain( @@ -364,18 +365,21 @@ struct chain_head { chain_node *parent; size_t node_count; /* Number of nodes attached directly * to this head. For cleaning. */ - getdns_network_req *netreq; getdns_rrset rrset; + getdns_network_req *netreq; + uint8_t name_spc[]; }; struct chain_node { chain_node *parent; - getdns_network_req *dnskey_req; getdns_rrset dnskey; - getdns_network_req *ds_req; + getdns_network_req *dnskey_req; + getdns_rrset ds; + getdns_network_req *ds_req; + getdns_network_req *soa_req; chain_head *chains; @@ -1195,12 +1199,44 @@ static int chain_head_validate(chain_head *head, rrset_iter *tas) return s; } -static void chain_validate_dnssec(chain_head *chain, rrset_iter *tas) +static int chain_validate_dnssec(chain_head *chain, rrset_iter *tas) +{ + int s = GETDNS_DNSSEC_INDETERMINATE; + chain_head *head; + + /* The netreq status is the worst for any head */ + for (head = chain; head; head = head->next) { + switch (chain_head_validate(head, tas)) { + case GETDNS_DNSSEC_SECURE: + if (s == GETDNS_DNSSEC_INDETERMINATE) + s = GETDNS_DNSSEC_SECURE; + break; + + case GETDNS_DNSSEC_INSECURE: + if (s != GETDNS_DNSSEC_BOGUS) + s = GETDNS_DNSSEC_INSECURE; + break; + + case GETDNS_DNSSEC_BOGUS : + s = GETDNS_DNSSEC_BOGUS; + break; + + default: + break; + } + } + return s; +} + +static void chain_set_netreq_dnssec_status(chain_head *chain, rrset_iter *tas) { chain_head *head; /* The netreq status is the worst for any head */ for (head = chain; head; head = head->next) { + if (!head->netreq) + continue; + switch (chain_head_validate(head, tas)) { case GETDNS_DNSSEC_SECURE: @@ -1328,7 +1364,7 @@ static void check_chain_complete(chain_head *chain) * validation to find out which RRSIGs should be returned. */ if (chain->netreq->unbound_id == -1 && context->trust_anchors) - chain_validate_dnssec(chain, rrset_iter_init(&tas_iter, + chain_set_netreq_dnssec_status(chain,rrset_iter_init(&tas_iter, context->trust_anchors, context->trust_anchors_len)); #endif @@ -1393,6 +1429,9 @@ static void val_chain_sched_soa(chain_head *head, uint8_t *dname) { chain_node *node; + if (!head->netreq) + return; + if (!*dname) return; @@ -1437,6 +1476,9 @@ static void val_chain_sched(chain_head *head, uint8_t *dname) { chain_node *node; + if (!head->netreq) + return; + for ( node = head->parent ; node && !priv_getdns_dname_equal(dname, node->ds.name) ; node = node->parent); @@ -1465,6 +1507,9 @@ static void val_chain_sched_signer_node(chain_node *node, rrsig_iter *rrsig) static void val_chain_sched_signer(chain_head *head, rrsig_iter *rrsig) { + if (!head->netreq) + return; + val_chain_sched_signer_node(head->parent, rrsig); } @@ -1664,15 +1709,20 @@ static chain_head *add_rrset2val_chain(struct mem_funcs *mf, return head; } -static void add_netreq2val_chain( - chain_head **chain_p, getdns_network_req *netreq) +/* Create the validation chain structure for the given packet. + * When netreq is set, queries will be scheduled for the DS + * and DNSKEY RR's for the nodes on the validation chain. + */ +static void add_pkt2val_chain(struct mem_funcs *mf, + chain_head **chain_p, uint8_t *pkt, size_t pkt_len, + uint8_t *qname, uint16_t qtype, uint16_t qclass, + getdns_network_req *netreq) { rrset_iter *i, i_spc; getdns_rrset *rrset; rrsig_iter *rrsig, rrsig_spc; size_t n_rrsigs; chain_head *head; - struct mem_funcs *mf; getdns_rrset empty_rrset; getdns_rrset q_rrset; @@ -1682,10 +1732,8 @@ static void add_netreq2val_chain( priv_getdns_rdf_iter rdf_spc, *rdf; rrtype_iter *rr, rr_spc; - assert(netreq->response); - assert(netreq->response_len >= GLDNS_HEADER_SIZE); - - mf = priv_getdns_context_mf(netreq->owner->context); + assert(pkt); + assert(pkt_len >= GLDNS_HEADER_SIZE); /* On empty packet, find SOA (zonecut) for the qname and query DS */ @@ -1693,20 +1741,19 @@ static void add_netreq2val_chain( /* For all things without signature, find SOA (zonecut) and query DS */ - if (GLDNS_ANCOUNT(netreq->response) == 0 && - GLDNS_NSCOUNT(netreq->response) == 0) { + if (GLDNS_ANCOUNT(pkt) == 0 && GLDNS_NSCOUNT(pkt) == 0 && qname) { - empty_rrset.name = netreq->owner->name; - empty_rrset.rr_class = GETDNS_RRCLASS_IN; - empty_rrset.rr_type = 0; - empty_rrset.pkt = netreq->response; - empty_rrset.pkt_len = netreq->response_len; + empty_rrset.name = qname; + empty_rrset.rr_class = qclass; + empty_rrset.rr_type = qtype; + empty_rrset.pkt = pkt; + empty_rrset.pkt_len = pkt_len; head = add_rrset2val_chain(mf, chain_p, &empty_rrset, netreq); val_chain_sched_soa(head, empty_rrset.name); return; } - for ( i = rrset_iter_init(&i_spc,netreq->response,netreq->response_len) + for ( i = rrset_iter_init(&i_spc, pkt, pkt_len) ; i ; i = rrset_iter_next(i)) { @@ -1731,14 +1778,17 @@ static void add_netreq2val_chain( /* For NOERROR/NODATA or NXDOMAIN responses add extra rrset to * the validation chain so the denial of existence will be * checked eventually. + * But only if we knew the question of course... */ + if (!qname) + return; /* First find the canonical name for the question */ - q_rrset.name = netreq->owner->name; + q_rrset.name = qname; q_rrset.rr_type = GETDNS_RRTYPE_CNAME; - q_rrset.rr_class = netreq->request_class; - q_rrset.pkt = netreq->response; - q_rrset.pkt_len = netreq->response_len; + q_rrset.rr_class = qclass; + q_rrset.pkt = pkt; + q_rrset.pkt_len = pkt_len; for (anti_loop = 1000; anti_loop; anti_loop--) { if (!(rr = rrtype_iter_init(&rr_spc, &q_rrset))) @@ -1749,7 +1799,7 @@ static void add_netreq2val_chain( rdf, cname_spc, &cname_len); } - q_rrset.rr_type = netreq->request_type; + q_rrset.rr_type = qtype; if (!(rr = rrtype_iter_init(&rr_spc, &q_rrset))) { debug_sec_print_rrset("Adding NX rrset: ", &q_rrset); @@ -1762,8 +1812,14 @@ static void get_val_chain(getdns_dns_req *dnsreq) getdns_network_req *netreq, **netreq_p; chain_head *chain = NULL; - for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) - add_netreq2val_chain(&chain, netreq); + for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) { + add_pkt2val_chain( &dnsreq->my_mf, &chain + , netreq->response, netreq->response_len + , netreq->owner->name + , netreq->request_type, netreq->request_class + , netreq + ); + } if (chain) check_chain_complete(chain); @@ -1784,353 +1840,6 @@ void priv_getdns_get_validation_chain(getdns_dns_req *dns_req) get_val_chain(dns_req); } -/********************** functions for validate_dnssec *************************/ - -static getdns_return_t -priv_getdns_create_rr_from_dict(getdns_dict *rr_dict, ldns_rr **rr) -{ - gldns_buffer buf; - uint8_t space[8192], *xspace = NULL; - size_t xsize, pos = 0; - ldns_status s; - getdns_return_t r; - - gldns_buffer_init_frm_data(&buf, space, sizeof(space)); - if ((r = priv_getdns_rr_dict2wire(rr_dict, &buf))) - return r; - - if ((xsize = gldns_buffer_position(&buf)) > sizeof(space)) { - if (!(xspace = GETDNS_XMALLOC(rr_dict->mf, uint8_t, xsize))) - return GETDNS_RETURN_MEMORY_ERROR; - - gldns_buffer_init_frm_data(&buf, xspace, xsize); - if ((r = priv_getdns_rr_dict2wire(rr_dict, &buf))) { - GETDNS_FREE(rr_dict->mf, xspace); - return r; - } - } - s = ldns_wire2rr(rr, gldns_buffer_begin(&buf), - gldns_buffer_position(&buf), &pos, GLDNS_SECTION_ANSWER); - if (xspace) - GETDNS_FREE(rr_dict->mf, xspace); - return s ? GETDNS_RETURN_GENERIC_ERROR : GETDNS_RETURN_GOOD; -} - -static getdns_return_t -priv_getdns_rr_list_from_list(getdns_list *list, ldns_rr_list **rr_list) -{ - getdns_return_t r; - size_t i, l; - struct getdns_dict *rr_dict; - ldns_rr *rr; - - if ((r = getdns_list_get_length(list, &l))) - return r; - - if (! (*rr_list = ldns_rr_list_new())) - return GETDNS_RETURN_MEMORY_ERROR; - - for (i = 0; i < l; i++) { - if ((r = getdns_list_get_dict(list, i, &rr_dict))) - break; - - if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) - break; - - if (! ldns_rr_list_push_rr(*rr_list, rr)) { - ldns_rr_free(rr); - r = GETDNS_RETURN_GENERIC_ERROR; - break; - } - } - if (r) - ldns_rr_list_deep_free(*rr_list); - return r; -} - -static int -ldns_dname_compare_v(const void *a, const void *b) { - return ldns_dname_compare((ldns_rdf *)a, (ldns_rdf *)b); -} - -ldns_status -priv_getdns_ldns_dnssec_zone_add_rr(ldns_dnssec_zone *zone, ldns_rr *rr) -{ - ldns_dnssec_name *new_name; - ldns_rbnode_t *new_node; - - if (ldns_rr_get_type(rr) != LDNS_RR_TYPE_NSEC3) - return ldns_dnssec_zone_add_rr(zone, rr); - - if (!(new_name = ldns_dnssec_name_new())) - return LDNS_STATUS_MEM_ERR; - - new_name->name = ldns_rdf_clone(ldns_rr_owner(rr)); - new_name->hashed_name = ldns_dname_label(ldns_rr_owner(rr), 0); - new_name->name_alloced = true; - - if (!(new_node = LDNS_MALLOC(ldns_rbnode_t))) { - ldns_dnssec_name_free(new_name); - return LDNS_STATUS_MEM_ERR; - } - new_node->key = new_name->name; - new_node->data = new_name; - if (!zone->names) - zone->names = ldns_rbtree_create(ldns_dname_compare_v); - (void)ldns_rbtree_insert(zone->names, new_node); - -#ifdef LDNS_DNSSEC_ZONE_HASHED_NAMES - if (!(new_node = LDNS_MALLOC(ldns_rbnode_t))) { - ldns_dnssec_name_free(new_name); - return LDNS_STATUS_MEM_ERR; - } - new_node->key = new_name->hashed_name; - new_node->data = new_name; - if (!zone->hashed_names) { - zone->_nsec3params = rr; - zone->hashed_names = ldns_rbtree_create(ldns_dname_compare_v); - } - (void)ldns_rbtree_insert(zone->hashed_names, new_node); -#endif - - return ldns_dnssec_zone_add_rr(zone, rr); -} - -static getdns_return_t -priv_getdns_dnssec_zone_from_list(struct getdns_list *list, - ldns_dnssec_zone **zone) -{ - getdns_return_t r; - size_t i, l; - struct getdns_dict *rr_dict; - ldns_rr *rr; - ldns_status s; - - if ((r = getdns_list_get_length(list, &l))) - return r; - - if (! (*zone = ldns_dnssec_zone_new())) - return GETDNS_RETURN_MEMORY_ERROR; - - for (i = 0; i < l; i++) { - if ((r = getdns_list_get_dict(list, i, &rr_dict))) - break; - - if ((r = priv_getdns_create_rr_from_dict(rr_dict, &rr))) - break; - - if ((s = priv_getdns_ldns_dnssec_zone_add_rr(*zone, rr))) { - ldns_rr_free(rr); - r = GETDNS_RETURN_GENERIC_ERROR; - break; - } - } - if (r) - ldns_dnssec_zone_free(*zone); - return r; -} - -typedef struct zone_iter { - ldns_dnssec_zone *zone; - ldns_rbnode_t *cur_node; - ldns_dnssec_rrsets *cur_rrset; - - ldns_dnssec_rrsets nsec_rrset; - ldns_dnssec_rrs nsec_rrs; -} zone_iter; - -static void -rrset_iter_init_zone(zone_iter *i, ldns_dnssec_zone *zone) -{ - ldns_dnssec_name *name; - assert(i); - - i->zone = zone; - if ((i->cur_node = zone->names - ? ldns_rbtree_first(zone->names) - : LDNS_RBTREE_NULL) == LDNS_RBTREE_NULL) { - - i->cur_rrset = NULL; - return; - } - - i->cur_rrset = ((ldns_dnssec_name *)i->cur_node->data)->rrsets; - if (!i->cur_rrset) { - name = ((ldns_dnssec_name *)i->cur_node->data); - if (name->nsec && name->nsec_signatures) { - i->cur_rrset = &i->nsec_rrset; - i->nsec_rrset.rrs = &i->nsec_rrs; - i->nsec_rrs.rr = name->nsec; - i->nsec_rrs.next = NULL; - i->nsec_rrset.type = ldns_rr_get_type(name->nsec); - i->nsec_rrset.signatures = - name->nsec_signatures; - i->nsec_rrset.next = NULL; - return; - } - } -} - -static ldns_dnssec_rrsets * -_rrset_iter_value(zone_iter *i) -{ - assert(i); - - return i->cur_rrset; -} - -static void -_rrset_iter_next(zone_iter *i) -{ - int was_nsec_rrset; - ldns_dnssec_name *name; - assert(i); - - if (! i->cur_rrset) - return; - - was_nsec_rrset = (i->cur_rrset == &i->nsec_rrset); - if (! (i->cur_rrset = i->cur_rrset->next)) { - - if (!was_nsec_rrset) { - name = ((ldns_dnssec_name *)i->cur_node->data); - if (name->nsec && name->nsec_signatures) { - i->cur_rrset = &i->nsec_rrset; - i->nsec_rrset.rrs = &i->nsec_rrs; - i->nsec_rrs.rr = name->nsec; - i->nsec_rrs.next = NULL; - i->nsec_rrset.type = ldns_rr_get_type(name->nsec); - i->nsec_rrset.signatures = - name->nsec_signatures; - i->nsec_rrset.next = NULL; - return; - } - } - i->cur_node = ldns_rbtree_next(i->cur_node); - i->cur_rrset = i->cur_node != LDNS_RBTREE_NULL - ? ((ldns_dnssec_name *)i->cur_node->data)->rrsets - : NULL; - } -} - -static ldns_rr_list * -rrs2rr_list(ldns_dnssec_rrs *rrs) -{ - ldns_rr_list *r = ldns_rr_list_new(); - if (r) - while (rrs) { - (void) ldns_rr_list_push_rr(r, rrs->rr); - rrs = rrs->next; - } - return r; -} - -static ldns_status -verify_rrset(ldns_dnssec_rrsets *rrset_and_sigs, - const ldns_rr_list *keys, ldns_rr_list *good_keys) -{ - ldns_status s; - ldns_rr_list *rrset = rrs2rr_list(rrset_and_sigs->rrs); - ldns_rr_list *sigs = rrs2rr_list(rrset_and_sigs->signatures); - s = ldns_verify(rrset, sigs, keys, good_keys); -#if 0 - if (s != 0) { - fprintf(stderr, "verify status %d\nrrset: ", s); - ldns_rr_list_print(stderr, rrset); - fprintf(stderr, "\nsigs: "); - ldns_rr_list_print(stderr, sigs); - fprintf(stderr, "\nkeys: "); - ldns_rr_list_print(stderr, keys); - fprintf(stderr, "\n\n"); - } -#endif - ldns_rr_list_free(sigs); - ldns_rr_list_free(rrset); - return s; -} - -static ldns_status -chase(ldns_dnssec_rrsets *rrset, ldns_dnssec_zone *support, - ldns_rr_list *support_keys, ldns_rr_list *trusted) -{ - ldns_status s; - ldns_rr_list *verifying_keys; - size_t i, j; - ldns_rr *rr; - ldns_dnssec_rrsets *key_rrset; - ldns_dnssec_rrs *rrs; - - /* Secure by trusted keys? */ - s = verify_rrset(rrset, trusted, NULL); - if (s == 0) - return s; - - /* No, chase with support records.. - * Is there a verifying key in the support records? - */ - verifying_keys = ldns_rr_list_new(); - s = verify_rrset(rrset, support_keys, verifying_keys); - if (s != 0) - goto done_free_verifying_keys; - - /* Ok, we have verifying keys from the support records. - * Compare them with the *trusted* keys or DSes, - * or chase them further down the validation chain. - */ - for (i = 0; i < ldns_rr_list_rr_count(verifying_keys); i++) { - /* Lookup the rrset for key rr from the support records */ - rr = ldns_rr_list_rr(verifying_keys, i); - key_rrset = ldns_dnssec_zone_find_rrset( - support, ldns_rr_owner(rr), ldns_rr_get_type(rr)); - if (! key_rrset) { - s = LDNS_STATUS_CRYPTO_NO_DNSKEY; - break; - } - /* When we signed ourselves, we have to cross domain border - * and look for a matching DS signed by a parents key - */ - if (rrset == key_rrset) { - /* Is the verifying key trusted? - * (i.e. DS in trusted) - */ - for (j = 0; j < ldns_rr_list_rr_count(trusted); j++) - if (ldns_rr_compare_ds(ldns_rr_list_rr( - trusted, j), rr)) - break; - /* If so, check for the next verifying key - * (or exit SECURE) - */ - if (j < ldns_rr_list_rr_count(trusted)) - continue; - - /* Search for a matching DS in the support records */ - key_rrset = ldns_dnssec_zone_find_rrset( - support, ldns_rr_owner(rr), LDNS_RR_TYPE_DS); - if (! key_rrset) { - s = LDNS_STATUS_CRYPTO_NO_DNSKEY; - break; - } - /* Now check if DS matches the DNSKEY! */ - for (rrs = key_rrset->rrs; rrs; rrs = rrs->next) - if (ldns_rr_compare_ds(rr, rrs->rr)) - break; - /* No DS found, try one of the other keys */ - if (! rrs) - continue; - } - /* Pursue the chase with the verifying key (or its DS) - * and we're done. - */ - s = chase(key_rrset, support, support_keys, trusted); - break; - } - if (i == ldns_rr_list_rr_count(verifying_keys)) - s = LDNS_STATUS_CRYPTO_NO_DNSKEY; -done_free_verifying_keys: - ldns_rr_list_free(verifying_keys); - return s; -} - /* * getdns_validate_dnssec * @@ -2140,65 +1849,89 @@ getdns_validate_dnssec(getdns_list *records_to_validate, getdns_list *support_records, getdns_list *trust_anchors) { - getdns_return_t r; - ldns_rr_list *trusted; - ldns_dnssec_zone *support; - ldns_rr_list *support_keys; - ldns_dnssec_zone *to_validate; - zone_iter i; - ldns_dnssec_rrsets *rrset; - ldns_dnssec_rrs *rrs; - ldns_status s = LDNS_STATUS_ERR; + uint8_t to_val_buf[4096], *to_val, + support_buf[4096], *support, + tas_buf[4096], *tas; - if ((r = priv_getdns_rr_list_from_list(trust_anchors, &trusted))) - return r; + size_t to_val_len = sizeof(to_val_buf), + support_len = sizeof(support_buf), + tas_len = sizeof(tas_buf); - if ((r = priv_getdns_dnssec_zone_from_list( - support_records, &support))) - goto done_free_trusted; + getdns_return_t r = GETDNS_RETURN_MEMORY_ERROR; + struct mem_funcs *mf; - if ((r = priv_getdns_dnssec_zone_from_list( - records_to_validate, &to_validate))) - goto done_free_support; + chain_head *chain, *head; + chain_node *node; - if (! (support_keys = ldns_rr_list_new())) { - r = GETDNS_RETURN_MEMORY_ERROR; - goto done_free_to_validate; + uint8_t qname_spc[256], *qname = NULL; + size_t qname_len = sizeof(qname_spc); + uint16_t qtype = 0, qclass = GETDNS_RRCLASS_IN; + + priv_getdns_rr_iter rr_spc, *rr; + rrset_iter tas_iter; + +#if defined(SEC_DEBUG) && SEC_DEBUG + fflush(stdout); +#endif + + if (!records_to_validate || !support_records || !trust_anchors) + return GETDNS_RETURN_INVALID_PARAMETER; + mf = &records_to_validate->mf; + + /* First convert everything to wire formatxi + */ + if (!(to_val = _getdns_list2wire(records_to_validate, + to_val_buf, &to_val_len, mf))) + return GETDNS_RETURN_MEMORY_ERROR; + + if (!(support = _getdns_list2wire(support_records, + support_buf, &support_len, mf))) + goto exit_free_to_val; + + if (!(tas = _getdns_list2wire(trust_anchors, + tas_buf, &tas_len, mf))) + goto exit_free_support; + + if (GLDNS_QDCOUNT(to_val) > 0 + && (rr = priv_getdns_rr_iter_init(&rr_spc, to_val, to_val_len)) + && (qname = priv_getdns_owner_if_or_as_decompressed( + rr, qname_spc, &qname_len)) + && rr->nxt >= rr->rr_type + 4) { + + qtype = gldns_read_uint16(rr->rr_type); + qclass = gldns_read_uint16(rr->rr_type + 2); } - /* Create a rr_list of all the keys in the support records */ - for (rrset_iter_init_zone(&i, support); - (rrset = _rrset_iter_value(&i)); _rrset_iter_next(&i)) + /* Create the chain hierarchy where all head's need to be validated. */ + chain = NULL; + add_pkt2val_chain(mf, &chain, to_val, to_val_len, + qname, qtype, qclass, NULL); - if (ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DS || - ldns_dnssec_rrsets_type(rrset) == LDNS_RR_TYPE_DNSKEY) + /* Now equip the nodes with the support records wireformat */ + for (head = chain; head; head = head->next) { + for (node = head->parent; node; node = node->parent) { - for (rrs = rrset->rrs; rrs; rrs = rrs->next) - (void) ldns_rr_list_push_rr( - support_keys, rrs->rr); - - /* Now walk through the rrsets to validate */ - for (rrset_iter_init_zone(&i, to_validate); - (rrset = _rrset_iter_value(&i)); _rrset_iter_next(&i)) { - - if ((s = chase(rrset, support, support_keys, trusted))) - break; + node->dnskey.pkt = support; + node->dnskey.pkt_len = support_len; + node->ds.pkt = support; + node->ds.pkt_len = support_len; + } } - if (s == LDNS_STATUS_CRYPTO_BOGUS) - r = GETDNS_DNSSEC_BOGUS; - else if (s != LDNS_STATUS_OK) - r = GETDNS_DNSSEC_INSECURE; - else - r = GETDNS_DNSSEC_SECURE; + /* Now equip the nodes with the support records wireformat */ + + r = (getdns_return_t)chain_validate_dnssec( + chain, rrset_iter_init(&tas_iter, tas, tas_len)); + + if (tas != tas_buf) + GETDNS_FREE(*mf, tas); +exit_free_support: + if (support != support_buf) + GETDNS_FREE(*mf, support); +exit_free_to_val: + if (to_val != to_val_buf) + GETDNS_FREE(*mf, to_val); - ldns_rr_list_free(support_keys); -done_free_to_validate: - ldns_dnssec_zone_deep_free(to_validate); -done_free_support: - ldns_dnssec_zone_deep_free(support); -done_free_trusted: - ldns_rr_list_deep_free(trusted); return r; -} /* getdns_validate_dnssec */ +} uint16_t _getdns_parse_ta_file(time_t *ta_mtime, gldns_buffer *gbuf) From 6cffc4792b04a23d299c2cee30b5d7010acb2398 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Thu, 2 Jul 2015 00:25:41 +0200 Subject: [PATCH 28/28] Validate replies with getdns_validate_dnssec You can feed it the replies_tree as the records to validate list --- src/dnssec.c | 171 +++++++++++++++++++++++++--------------- src/test/getdns_query.c | 34 +++++++- src/util-internal.c | 67 ++++++++++++++-- 3 files changed, 197 insertions(+), 75 deletions(-) diff --git a/src/dnssec.c b/src/dnssec.c index 8eca7759..ce670544 100644 --- a/src/dnssec.c +++ b/src/dnssec.c @@ -698,7 +698,6 @@ static int bitmap_contains_rrtype(priv_getdns_rdf_iter *bitmap, uint16_t rr_type uint8_t window = rr_type >> 8; uint8_t subtype = rr_type & 0xFF; - DEBUG_SEC("bitmap: %p, type: %d\n", bitmap, (int)rr_type); if (!bitmap) return 0; dptr = bitmap->pos; @@ -884,14 +883,28 @@ static uint8_t *name2nsec3_label( return NULL; } +static uint8_t *_dname_label_copy(uint8_t *dst, uint8_t *src, size_t dst_len) +{ + uint8_t *r = dst, i; + + if (!src || *src + 1 > dst_len) + return NULL; + + for (i = *src + 1; i > 0; i--) + *dst++ = tolower(*src++); + + return r; +} + static int nsec3_matches_name(getdns_rrset *nsec3, uint8_t *name) { - uint8_t label[64]; + uint8_t label[64], owner[64]; - if (name2nsec3_label(nsec3, name, label, sizeof(label))) + if (name2nsec3_label(nsec3, name, label, sizeof(label)) + && _dname_label_copy(owner, nsec3->name, sizeof(owner))) return *nsec3->name == label[0] /* Labels same size? */ - && memcmp(nsec3->name + 1, label + 1, label[0]) == 0; + && memcmp(owner + 1, label + 1, label[0]) == 0; return 0; } @@ -913,7 +926,7 @@ static int nsec3_covers_name(getdns_rrset *nsec3, uint8_t *name, int *opt_out) || (nsz = gldns_b32_ntop_extended_hex(rdf->pos + 1, *rdf->pos, (char *)next + 1, sizeof(next)-2)) < 0 || *nsec3->name > sizeof(owner) - 2 - || !memcpy(owner, nsec3->name, *nsec3->name + 1)) { + || !_dname_label_copy(owner, nsec3->name, sizeof(owner)-1)) { DEBUG_SEC("Error getting NSEC3 owner & next labels\n"); return 0; @@ -1583,7 +1596,8 @@ static void val_chain_node_soa_cb(getdns_dns_req *dnsreq) ! priv_getdns_dname_equal(node->ds.name, rrset->name)) node = node->parent; - val_chain_sched_node(node); + if (node) + val_chain_sched_node(node); } else val_chain_sched_soa_node(node->parent); @@ -1614,6 +1628,14 @@ static chain_head *add_rrset2val_chain(struct mem_funcs *mf, max_head = NULL; max_labels = 0; for (head = *chain_p; head; head = head->next) { + /* Also, try to prevent adding double rrsets */ + if ( rrset->rr_class == head->rrset.rr_class + && rrset->rr_type == head->rrset.rr_type + && rrset->pkt == head->rrset.pkt + && rrset->pkt_len == head->rrset.pkt_len + && priv_getdns_dname_equal(rrset->name, head->rrset.name)) + return NULL; + for (label = labels; label < last_label; label++) { if (! is_subdomain(*label, head->rrset.name)) break; @@ -1715,7 +1737,6 @@ static chain_head *add_rrset2val_chain(struct mem_funcs *mf, */ static void add_pkt2val_chain(struct mem_funcs *mf, chain_head **chain_p, uint8_t *pkt, size_t pkt_len, - uint8_t *qname, uint16_t qtype, uint16_t qclass, getdns_network_req *netreq) { rrset_iter *i, i_spc; @@ -1723,36 +1744,14 @@ static void add_pkt2val_chain(struct mem_funcs *mf, rrsig_iter *rrsig, rrsig_spc; size_t n_rrsigs; chain_head *head; - getdns_rrset empty_rrset; - - getdns_rrset q_rrset; - uint8_t cname_spc[256]; - size_t cname_len = sizeof(cname_spc); - size_t anti_loop; - priv_getdns_rdf_iter rdf_spc, *rdf; - rrtype_iter *rr, rr_spc; assert(pkt); assert(pkt_len >= GLDNS_HEADER_SIZE); - /* On empty packet, find SOA (zonecut) for the qname and query DS */ - /* For all things with signatures, create a chain */ /* For all things without signature, find SOA (zonecut) and query DS */ - if (GLDNS_ANCOUNT(pkt) == 0 && GLDNS_NSCOUNT(pkt) == 0 && qname) { - - empty_rrset.name = qname; - empty_rrset.rr_class = qclass; - empty_rrset.rr_type = qtype; - empty_rrset.pkt = pkt; - empty_rrset.pkt_len = pkt_len; - - head = add_rrset2val_chain(mf, chain_p, &empty_rrset, netreq); - val_chain_sched_soa(head, empty_rrset.name); - return; - } for ( i = rrset_iter_init(&i_spc, pkt, pkt_len) ; i ; i = rrset_iter_next(i)) { @@ -1760,7 +1759,9 @@ static void add_pkt2val_chain(struct mem_funcs *mf, rrset = rrset_iter_value(i); debug_sec_print_rrset("rrset: ", rrset); - head = add_rrset2val_chain(mf, chain_p, rrset, netreq); + if (!(head = add_rrset2val_chain(mf, chain_p, rrset, netreq))) + continue; + for ( rrsig = rrsig_iter_init(&rrsig_spc, rrset), n_rrsigs = 0 ; rrsig ; rrsig = rrsig_iter_next(rrsig), n_rrsigs++) { @@ -1772,16 +1773,35 @@ static void add_pkt2val_chain(struct mem_funcs *mf, if (rrset->rr_type == GETDNS_RRTYPE_SOA) val_chain_sched(head, rrset->name); + else if (rrset->rr_type == GETDNS_RRTYPE_CNAME) + val_chain_sched_soa(head, rrset->name + *rrset->name + 1); else val_chain_sched_soa(head, rrset->name); } - /* For NOERROR/NODATA or NXDOMAIN responses add extra rrset to - * the validation chain so the denial of existence will be - * checked eventually. - * But only if we knew the question of course... - */ - if (!qname) - return; +} + +/* For NOERROR/NODATA or NXDOMAIN responses add extra rrset to + * the validation chain so the denial of existence will be + * checked eventually. + * But only if we know the question of course... + */ +static void add_question2val_chain(struct mem_funcs *mf, + chain_head **chain_p, uint8_t *pkt, size_t pkt_len, + uint8_t *qname, uint16_t qtype, uint16_t qclass, + getdns_network_req *netreq) +{ + getdns_rrset q_rrset; + uint8_t cname_spc[256]; + size_t cname_len = sizeof(cname_spc); + size_t anti_loop; + priv_getdns_rdf_iter rdf_spc, *rdf; + rrtype_iter *rr, rr_spc; + + chain_head *head; + + assert(pkt); + assert(pkt_len >= GLDNS_HEADER_SIZE); + assert(qname); /* First find the canonical name for the question */ q_rrset.name = qname; @@ -1798,16 +1818,23 @@ static void add_pkt2val_chain(struct mem_funcs *mf, q_rrset.name = priv_getdns_rdf_if_or_as_decompressed( rdf, cname_spc, &cname_len); } - q_rrset.rr_type = qtype; if (!(rr = rrtype_iter_init(&rr_spc, &q_rrset))) { - + /* No answer for the question. Add a head for this rrset + * anyway, to validate proof of non-existance, or to find + * proof that the packet is insecure. + */ debug_sec_print_rrset("Adding NX rrset: ", &q_rrset); - add_rrset2val_chain(mf, chain_p, &q_rrset, netreq); + head = add_rrset2val_chain(mf, chain_p, &q_rrset, netreq); + + /* On empty packet, find SOA (zonecut) for the qname */ + if (head && GLDNS_ANCOUNT(pkt) == 0 && GLDNS_NSCOUNT(pkt) == 0) + + val_chain_sched_soa(head, q_rrset.name); } } -static void get_val_chain(getdns_dns_req *dnsreq) +void priv_getdns_get_validation_chain(getdns_dns_req *dnsreq) { getdns_network_req *netreq, **netreq_p; chain_head *chain = NULL; @@ -1815,10 +1842,15 @@ static void get_val_chain(getdns_dns_req *dnsreq) for (netreq_p = dnsreq->netreqs; (netreq = *netreq_p) ; netreq_p++) { add_pkt2val_chain( &dnsreq->my_mf, &chain , netreq->response, netreq->response_len - , netreq->owner->name - , netreq->request_type, netreq->request_class , netreq ); + add_question2val_chain( &dnsreq->my_mf, &chain + , netreq->response, netreq->response_len + , netreq->owner->name + , netreq->request_type + , netreq->request_class + , netreq + ); } if (chain) @@ -1828,18 +1860,6 @@ static void get_val_chain(getdns_dns_req *dnsreq) create_getdns_response(dnsreq)); } -/******************************************************************************/ -/***************************** *******************************/ -/***************************** NEW CHAIN CODE *******************************/ -/***************************** (above) *******************************/ -/***************************** *******************************/ -/******************************************************************************/ - -void priv_getdns_get_validation_chain(getdns_dns_req *dns_req) -{ - get_val_chain(dns_req); -} - /* * getdns_validate_dnssec * @@ -1878,7 +1898,7 @@ getdns_validate_dnssec(getdns_list *records_to_validate, return GETDNS_RETURN_INVALID_PARAMETER; mf = &records_to_validate->mf; - /* First convert everything to wire formatxi + /* First convert everything to wire format */ if (!(to_val = _getdns_list2wire(records_to_validate, to_val_buf, &to_val_len, mf))) @@ -1892,19 +1912,39 @@ getdns_validate_dnssec(getdns_list *records_to_validate, tas_buf, &tas_len, mf))) goto exit_free_support; - if (GLDNS_QDCOUNT(to_val) > 0 - && (rr = priv_getdns_rr_iter_init(&rr_spc, to_val, to_val_len)) - && (qname = priv_getdns_owner_if_or_as_decompressed( - rr, qname_spc, &qname_len)) - && rr->nxt >= rr->rr_type + 4) { + if (GLDNS_QDCOUNT(to_val) == 0 && GLDNS_ANCOUNT(to_val) == 0) { + r = GETDNS_RETURN_GENERIC_ERROR; + goto exit_free_tas; + } + + chain = NULL; + /* First create a chain (head + nodes) for each rr in the answer and + * authority section of the fake to_val packet. + */ + add_pkt2val_chain(mf, &chain, to_val, to_val_len, NULL); + + /* When records_to_validate contained replies, like the replies_tree + * list in a response dict, the returned wireformat packet may contain + * multiple questions in the question section. For each reply one + * question. + * + * For each question in the question section add a chain head. + */ + for ( rr = priv_getdns_rr_iter_init(&rr_spc, to_val, to_val_len) + ; rr && priv_getdns_rr_iter_section(rr) == GLDNS_SECTION_QUESTION + ; rr = priv_getdns_rr_iter_next(rr) ) { + + if ((qname = priv_getdns_owner_if_or_as_decompressed( + rr, qname_spc, &qname_len)) + && rr->nxt >= rr->rr_type + 4) { qtype = gldns_read_uint16(rr->rr_type); qclass = gldns_read_uint16(rr->rr_type + 2); + + add_question2val_chain(mf, &chain, to_val, to_val_len, + qname, qtype, qclass, NULL); + } } - /* Create the chain hierarchy where all head's need to be validated. */ - chain = NULL; - add_pkt2val_chain(mf, &chain, to_val, to_val_len, - qname, qtype, qclass, NULL); /* Now equip the nodes with the support records wireformat */ for (head = chain; head; head = head->next) { @@ -1921,6 +1961,7 @@ getdns_validate_dnssec(getdns_list *records_to_validate, r = (getdns_return_t)chain_validate_dnssec( chain, rrset_iter_init(&tas_iter, tas, tas_len)); +exit_free_tas: if (tas != tas_buf) GETDNS_FREE(*mf, tas); exit_free_support: diff --git a/src/test/getdns_query.c b/src/test/getdns_query.c index 090f5c71..17055339 100644 --- a/src/test/getdns_query.c +++ b/src/test/getdns_query.c @@ -141,11 +141,14 @@ static getdns_return_t validate_chain(getdns_dict *response) getdns_list *validation_chain; getdns_list *replies_tree; getdns_dict *reply; - getdns_list *answer; + getdns_list *to_validate; getdns_list *trust_anchor; size_t i; int s; + if (!(to_validate = getdns_list_create())) + return GETDNS_RETURN_MEMORY_ERROR; + if (!(trust_anchor = getdns_root_trust_anchor(NULL))) return GETDNS_RETURN_GENERIC_ERROR; @@ -157,16 +160,39 @@ static getdns_return_t validate_chain(getdns_dict *response) response, "replies_tree", &replies_tree))) return r; + fprintf(stdout, "replies_tree %zu, dnssec_status: ", i); + switch ((s = getdns_validate_dnssec( + replies_tree, validation_chain, trust_anchor))) { + + case GETDNS_DNSSEC_SECURE: + fprintf(stdout, "GETDNS_DNSSEC_SECURE\n"); + break; + case GETDNS_DNSSEC_BOGUS: + fprintf(stdout, "GETDNS_DNSSEC_BOGUS\n"); + break; + case GETDNS_DNSSEC_INDETERMINATE: + fprintf(stdout, "GETDNS_DNSSEC_INDETERMINATE\n"); + break; + case GETDNS_DNSSEC_INSECURE: + fprintf(stdout, "GETDNS_DNSSEC_INSECURE\n"); + break; + case GETDNS_DNSSEC_NOT_PERFORMED: + fprintf(stdout, "GETDNS_DNSSEC_NOT_PERFORMED\n"); + break; + default: + fprintf(stdout, "%d\n", (int)s); + } + i = 0; while (!(r = getdns_list_get_dict(replies_tree, i++, &reply))) { - if ((r = getdns_dict_get_list(reply, "answer", &answer))) + if ((r = getdns_list_set_dict(to_validate, 0, reply))) return r; fprintf( stdout - , "reply %zu, getdns_validate_dnssec returned: ", i); + , "reply %zu, dnssec_status: ", i); switch ((s = getdns_validate_dnssec( - answer, validation_chain, trust_anchor))) { + to_validate, validation_chain, trust_anchor))) { case GETDNS_DNSSEC_SECURE: fprintf(stdout, "GETDNS_DNSSEC_SECURE\n"); diff --git a/src/util-internal.c b/src/util-internal.c index 69475e59..4febac14 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -992,9 +992,10 @@ priv_getdns_validate_dname(const char* dname) { static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) { - getdns_dict *rr_dict; + getdns_dict *rr_dict, *q_dict; + getdns_list *section; getdns_return_t r; - size_t i, pkt_start, ancount; + size_t i, j, pkt_start, ancount, qdcount; uint32_t qtype, qclass; getdns_bindata *qname; @@ -1004,7 +1005,7 @@ static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) gldns_buffer_write_u32(buf, 0); gldns_buffer_write_u32(buf, 0); - for ( i = 0 + for ( i = 0, qdcount = 0 ; (r = getdns_list_get_dict(l, i, &rr_dict)) != GETDNS_RETURN_NO_SUCH_LIST_ITEM ; i++ ) { @@ -1015,6 +1016,14 @@ static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) else break; } + if (getdns_dict_get_dict(rr_dict, "question", &q_dict) + == GETDNS_RETURN_GOOD) { + + /* rr_dict was actually a reply + * with a question section/rr_dict + */ + rr_dict = q_dict; + } if (getdns_dict_get_int(rr_dict, "qtype", &qtype) || getdns_dict_get_bindata(rr_dict, "qname", &qname)) continue; @@ -1022,9 +1031,9 @@ static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) gldns_buffer_write(buf, qname->data, qname->size); gldns_buffer_write_u16(buf, (uint16_t)qtype); gldns_buffer_write_u16(buf, (uint16_t)qclass); - gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, 1); - break; + qdcount++; } + gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_QDCOUNT_OFF, qdcount); for ( i = 0, ancount = 0 ; (r = getdns_list_get_dict(l, i, &rr_dict)) != GETDNS_RETURN_NO_SUCH_LIST_ITEM @@ -1036,8 +1045,54 @@ static void _getdns_list2wire_buf(gldns_buffer *buf, getdns_list *l) else break; } - if (priv_getdns_rr_dict2wire(rr_dict, buf) == GETDNS_RETURN_GOOD) + if (priv_getdns_rr_dict2wire(rr_dict, buf) + == GETDNS_RETURN_GOOD) { + ancount++; + continue; + } + if (getdns_dict_get_list(rr_dict, "answer", §ion) + == GETDNS_RETURN_GOOD) { + + for ( j = 0 + ; (r = getdns_list_get_dict(section, j, &q_dict)) + != GETDNS_RETURN_NO_SUCH_LIST_ITEM + ; j++ ) { + + if (r) { + if (r == + GETDNS_RETURN_WRONG_TYPE_REQUESTED) + continue; + else + break; + } + if (priv_getdns_rr_dict2wire(q_dict, buf) + == GETDNS_RETURN_GOOD) + + ancount++; + } + } + if (getdns_dict_get_list(rr_dict, "authority", §ion) + == GETDNS_RETURN_GOOD) { + + for ( j = 0 + ; (r = getdns_list_get_dict(section, j, &q_dict)) + != GETDNS_RETURN_NO_SUCH_LIST_ITEM + ; j++ ) { + + if (r) { + if (r == + GETDNS_RETURN_WRONG_TYPE_REQUESTED) + continue; + else + break; + } + if (priv_getdns_rr_dict2wire(q_dict, buf) + == GETDNS_RETURN_GOOD) + + ancount++; + } + } } gldns_buffer_write_u16_at(buf, pkt_start+GLDNS_ANCOUNT_OFF, ancount); }