diff --git a/configure.ac b/configure.ac index 0b5d2bc9..434df5c7 100644 --- a/configure.ac +++ b/configure.ac @@ -114,7 +114,7 @@ AC_SUBST(WPEDANTICFLAG) AC_SUBST(WNOERRORFLAG) case "$host_os" in - linux* ) CFLAGS="$CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE" + linux* ) CFLAGS="$CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE" ;; solaris* ) CFLAGS="$CFLAGS -D__EXTENSIONS__" # for strdup() from ;; diff --git a/src/Makefile.in b/src/Makefile.in index b8168487..bbe58805 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -67,7 +67,7 @@ C99COMPATFLAGS=@C99COMPATFLAGS@ DEFAULT_EVENTLOOP_OBJ=@DEFAULT_EVENTLOOP@.lo -GETDNS_OBJ=anchor.lo const-info.lo convert.lo dict.lo dnssec.lo general.lo \ +GETDNS_OBJ=const-info.lo convert.lo dict.lo dnssec.lo general.lo \ list.lo request-internal.lo pubkey-pinning.lo rr-dict.lo \ rr-iter.lo server.lo stub.lo sync.lo ub_loop.lo util-internal.lo \ mdns.lo @@ -86,7 +86,7 @@ YXML_OBJ=yxml.lo EXTENSION_OBJ=$(DEFAULT_EVENTLOOP_OBJ) libevent.lo libev.lo -NON_C99_OBJS=context.lo libuv.lo +NON_C99_OBJS=libuv.lo context.lo anchor.lo .SUFFIXES: .c .o .a .lo .h @@ -121,6 +121,9 @@ $(YXML_OBJ): $(EXTENSION_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) -c $(srcdir)/extension/$(@:.lo=.c) -o $@ +anchor.lo: + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) $(C99COMPATFLAGS) -c $(srcdir)/anchor.c -o anchor.lo + context.lo: $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WPEDANTICFLAG) $(C99COMPATFLAGS) -c $(srcdir)/context.c -o context.lo @@ -158,8 +161,8 @@ libgetdns_ext_ev.la: libgetdns.la libev.lo $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ libev.lo libgetdns.la $(LDFLAGS) $(EXTENSION_LIBEV_LDFLAGS) $(EXTENSION_LIBEV_EXT_LIBS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/extension/libev.symbols -libgetdns.la: $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YXML_OBJ) - $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YXML_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols +libgetdns.la: $(GETDNS_OBJ) version.lo context.lo anchor.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YXML_OBJ) + $(LIBTOOL) --tag=CC --mode=link $(CC) -o $@ $(GETDNS_OBJ) version.lo context.lo anchor.lo $(DEFAULT_EVENTLOOP_OBJ) $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(JSMN_OBJ) $(YXML_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols test: default cd test && $(MAKE) $@ diff --git a/src/anchor.c b/src/anchor.c index c096f471..bb63c200 100644 --- a/src/anchor.c +++ b/src/anchor.c @@ -42,6 +42,9 @@ #include "context.h" #include "yxml/yxml.h" #include "gldns/parseutil.h" +#include "gldns/gbuffer.h" +#include "gldns/str2wire.h" +#include "gldns/pkthdr.h" #define P7SIGNER "dnssec@iana.org" @@ -575,7 +578,94 @@ static ta_iter *ta_iter_init(ta_iter *ta, const char *doc, size_t doc_len) return ta_iter_next(ta); } -void _getdns_context_equip_with_anchor(getdns_context *context) +uint16_t _getdns_parse_xml_trust_anchors_buf( + gldns_buffer *gbuf, time_t now, char *xml_data, size_t xml_len) +{ + ta_iter ta_spc, *ta; + uint16_t ta_count = 0; + size_t 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); + + for ( ta = ta_iter_init(&ta_spc, (char *)xml_data, xml_len) + ; ta; ta = ta_iter_next(ta)) { + + if (now < ta->validFrom) + DEBUG_ANCHOR("Disregarding trust anchor " + "%s for %s which is not yet valid", + ta->keytag, ta->zone); + + else if (ta->validUntil != 0 && now > ta->validUntil) + DEBUG_ANCHOR("Disregarding trust anchor " + "%s for %s which is not valid anymore", + ta->keytag, ta->zone); + + else { + uint8_t zone[256]; + size_t zone_len = sizeof(zone); + uint8_t digest[sizeof(ta->digest)/2]; + size_t digest_len = sizeof(digest); + uint16_t keytag; + uint8_t algorithm; + uint8_t digesttype; + char *endptr; + + DEBUG_ANCHOR( "Installing trust anchor: " + "%s IN DS %s %s %s %s\n" + , ta->zone + , ta->keytag + , ta->algorithm + , ta->digesttype + , ta->digest + ); + if (gldns_str2wire_dname_buf(ta->zone, zone, &zone_len)) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable zone: \"%s\"", ta->zone); + continue; + } + keytag = (uint16_t)strtol(ta->keytag, &endptr, 10); + if (endptr == ta->keytag || *endptr != 0) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable keytag: \"%s\"", ta->keytag); + continue; + } + algorithm = (uint16_t)strtol(ta->algorithm, &endptr, 10); + if (endptr == ta->algorithm || *endptr != 0) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable algorithm: \"%s\"", ta->algorithm); + continue; + } + digesttype = (uint16_t)strtol(ta->digesttype, &endptr, 10); + if (endptr == ta->digesttype || *endptr != 0) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable digesttype: \"%s\"", ta->digesttype); + continue; + } + if (gldns_str2wire_hex_buf(ta->digest, digest, &digest_len)) { + DEBUG_ANCHOR("Not installing trust anchor because " + "of unparsable digest: \"%s\"", ta->digest); + continue; + } + gldns_buffer_write(gbuf, zone, zone_len); + gldns_buffer_write_u16(gbuf, GETDNS_RRTYPE_DS); + gldns_buffer_write_u16(gbuf, GETDNS_RRCLASS_IN); + gldns_buffer_write_u32(gbuf, 3600); + gldns_buffer_write_u16(gbuf, digest_len + 4); /* rdata_len */ + gldns_buffer_write_u16(gbuf, keytag); + gldns_buffer_write_u8(gbuf, algorithm); + gldns_buffer_write_u8(gbuf, digesttype); + gldns_buffer_write(gbuf, digest, digest_len); + ta_count += 1; + } + } + gldns_buffer_write_u16_at(gbuf, pkt_start+GLDNS_ANCOUNT_OFF, ta_count); + return ta_count; +} + +void _getdns_context_equip_with_anchor(getdns_context *context, time_t now) { uint8_t xml_spc[4096], *xml_data = xml_spc; uint8_t p7s_spc[4096], *p7s_data = p7s_spc; @@ -618,19 +708,41 @@ void _getdns_context_equip_with_anchor(getdns_context *context) , __FUNC__); else if (_getdns_verify_p7sig(xml, p7s, store, "dnssec@iana.org")) { - ta_iter ta_spc, *ta; + uint8_t ta_spc[sizeof(context->trust_anchors_spc)]; + size_t ta_len; + uint8_t *ta = NULL; + gldns_buffer gbuf; - for ( ta = ta_iter_init(&ta_spc, (char *)xml_data, xml_len) - ; ta; ta = ta_iter_next(ta)) { + gldns_buffer_init_vfixed_frm_data( + &gbuf, ta_spc, sizeof(ta_spc)); - DEBUG_ANCHOR( "%s IN DS %s %s %s %s\n" - , ta->zone - , ta->keytag - , ta->algorithm - , ta->digesttype - , ta->digest - ); + if (!_getdns_parse_xml_trust_anchors_buf(&gbuf, now, + (char *)xml_data, xml_len)) + DEBUG_ANCHOR("Failed to parse trust anchor XML data"); + else if ((ta_len = gldns_buffer_position(&gbuf)) > sizeof(ta_spc)) { + if ((ta = GETDNS_XMALLOC(context->mf, uint8_t, ta_len))) { + gldns_buffer_init_frm_data(&gbuf, ta, + gldns_buffer_position(&gbuf)); + if (!_getdns_parse_xml_trust_anchors_buf( + &gbuf, now, (char *)xml_data, xml_len)) { + DEBUG_ANCHOR("Failed to re-parse trust" + " anchor XML data"); + GETDNS_FREE(context->mf, ta); + } else { + context->trust_anchors = ta; + context->trust_anchors_len = ta_len; + context->trust_anchors_source = GETDNS_TASRC_XML; + } + } else + DEBUG_ANCHOR("Could not allocate space for XML file"); + } else { + (void)memcpy(context->trust_anchors_spc, ta_spc, ta_len); + context->trust_anchors = context->trust_anchors_spc; + context->trust_anchors_len = ta_len; + context->trust_anchors_source = GETDNS_TASRC_XML; } + DEBUG_ANCHOR("ta: %p, ta_len: %d\n", context->trust_anchors, (int)context->trust_anchors_len); + } else { DEBUG_ANCHOR("Verifying trust-anchors failed!\n"); } diff --git a/src/anchor.h b/src/anchor.h index db63a452..b619781c 100644 --- a/src/anchor.h +++ b/src/anchor.h @@ -35,8 +35,9 @@ #define ANCHOR_H_ #include "getdns/getdns.h" +#include -void _getdns_context_equip_with_anchor(getdns_context *context); +void _getdns_context_equip_with_anchor(getdns_context *context, time_t now); #endif /* anchor.h */ diff --git a/src/context.c b/src/context.c index 3d6144b6..87f309de 100644 --- a/src/context.c +++ b/src/context.c @@ -1406,6 +1406,7 @@ getdns_context_create_with_extended_memory_functions( result->suffixes = no_suffixes; result->suffixes_len = sizeof(no_suffixes); + result->trust_anchors_source = GETDNS_TASRC_NONE; gldns_buffer_init_vfixed_frm_data(&gbuf, result->trust_anchors_spc , sizeof(result->trust_anchors_spc)); @@ -1423,12 +1424,16 @@ getdns_context_create_with_extended_memory_functions( , result->trust_anchors , result->trust_anchors_len); if (!_getdns_parse_ta_file(NULL, &gbuf)) { + GETDNS_FREE(result->mf, result->trust_anchors); result->trust_anchors = NULL; result->trust_anchors_len = 0; - } + } else + result->trust_anchors_source = GETDNS_TASRC_ZONE; } - } else + } else { result->trust_anchors = result->trust_anchors_spc; + result->trust_anchors_source = GETDNS_TASRC_ZONE; + } result->upstreams = NULL; @@ -1514,7 +1519,8 @@ getdns_context_create_with_extended_memory_functions( #else /* XXX implement Windows-style unlock here */ #endif - _getdns_context_equip_with_anchor(result); + if (result->trust_anchors_source == GETDNS_TASRC_NONE) + _getdns_context_equip_with_anchor(result, time(NULL)); #ifdef HAVE_LIBUNBOUND result->unbound_ctx = NULL; diff --git a/src/context.h b/src/context.h index 862460b5..ae2315f1 100644 --- a/src/context.h +++ b/src/context.h @@ -92,6 +92,12 @@ typedef enum getdns_conn_state { GETDNS_CONN_BACKOFF } getdns_conn_state_t; +typedef enum getdns_tasrc { + GETDNS_TASRC_NONE, + GETDNS_TASRC_ZONE, + GETDNS_TASRC_XML +} getdns_tasrc; + typedef enum getdns_tsig_algo { GETDNS_NO_TSIG = 0, /* Do not use tsig */ GETDNS_HMAC_MD5 = 1, /* 128 bits */ @@ -272,6 +278,7 @@ struct getdns_context { size_t suffixes_len; uint8_t *trust_anchors; size_t trust_anchors_len; + getdns_tasrc trust_anchors_source; getdns_upstreams *upstreams; uint16_t limit_outstanding_queries; uint32_t dnssec_allowed_skew;