/** * * /brief functions for DNSSEC trust anchor management */ /* * Copyright (c) 2017, NLnet Labs, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the names of the copyright holders nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "debug.h" #include "anchor.h" #include #include #include #include "types-internal.h" #include "context.h" #include "dnssec.h" #include "yxml/yxml.h" #include "gldns/parseutil.h" #include "gldns/gbuffer.h" #include "gldns/str2wire.h" #include "gldns/wire2str.h" #include "gldns/pkthdr.h" #include "gldns/keyraw.h" #include "general.h" #include "util-internal.h" #include "platform.h" typedef struct ta_iter { uint8_t yxml_buf[4096]; yxml_t x; const char *start; const char *ptr; const char *end; char zone[1024]; time_t validFrom; time_t validUntil; char keytag[6]; char algorithm[4]; char digesttype[4]; char digest[2048]; } ta_iter; static void strcpytrunc(char* dst, const char* src, size_t dstsize) { size_t to_copy = strlen(src); if (to_copy >= dstsize) to_copy = dstsize -1; memcpy(dst, src, to_copy); dst[to_copy] = '\0'; } /** * XML convert DateTime element to time_t. * [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm] * (with optional .ssssss fractional seconds) * @param str: the string * @return a time_t representation or 0 on failure. */ time_t _getdns_xml_convertdate(const char* str) { time_t t = 0; struct tm tm; const char* s; /* for this application, ignore minus in front; * only positive dates are expected */ s = str; if(s[0] == '-') s++; memset(&tm, 0, sizeof(tm)); /* parse initial content of the string (lots of whitespace allowed) */ s = strptime(s, "%t%Y%t-%t%m%t-%t%d%tT%t%H%t:%t%M%t:%t%S%t", &tm); if(!s) { DEBUG_ANCHOR("xml_convertdate parse failure %s\n", str); return 0; } /* parse remainder of date string */ if(*s == '.') { /* optional '.' and fractional seconds */ int frac = 0, n = 0; if(sscanf(s+1, "%d%n", &frac, &n) < 1) { DEBUG_ANCHOR("xml_convertdate f failure %s\n", str); return 0; } /* fraction is not used, time_t has second accuracy */ s++; s+=n; } if(*s == 'Z' || *s == 'z') { /* nothing to do for this */ s++; } else if(*s == '+' || *s == '-') { /* optional timezone spec: Z or +hh:mm or -hh:mm */ int hr = 0, mn = 0, n = 0; if(sscanf(s+1, "%d:%d%n", &hr, &mn, &n) < 2) { DEBUG_ANCHOR("xml_convertdate tz failure %s\n", str); return 0; } if(*s == '+') { tm.tm_hour += hr; tm.tm_min += mn; } else { tm.tm_hour -= hr; tm.tm_min -= mn; } s++; s += n; } if(*s != 0) { /* not ended properly */ /* but ignore, (lenient) */ } t = gldns_mktime_from_utc(&tm); if(t == (time_t)-1) { DEBUG_ANCHOR("xml_convertdate mktime failure\n"); return 0; } return t; } static inline int ta_iter_done(ta_iter *ta) { return *ta->ptr == 0 || ta->ptr >= ta->end; } static ta_iter *ta_iter_next(ta_iter *ta) { yxml_ret_t r = YXML_OK; yxml_t ta_x; const char *ta_start; int level; char value[2048]; char *cur, *tmp; enum { VALIDFROM, VALIDUNTIL } attr_type; enum { KEYTAG, ALGORITHM, DIGESTTYPE, DIGEST } elem_type; cur = value; value[0] = 0; if (!ta->zone[0]) { DEBUG_ANCHOR("Determine start of \n"); /* Determine start of */ while (!ta_iter_done(ta) && ( yxml_parse(&ta->x, *ta->ptr) != YXML_ELEMSTART || strcasecmp(ta->x.elem, "trustanchor"))) ta->ptr++; if (ta_iter_done(ta)) return NULL; ta_start = ta->ptr; ta_x = ta->x; DEBUG_ANCHOR("Find \n"); /* Find */ level = 0; while (!ta_iter_done(ta) && !ta->zone[0]) { switch ((r = yxml_parse(&ta->x, *ta->ptr))) { case YXML_ELEMSTART: level += 1; if (level == 1 && strcasecmp(ta->x.elem, "zone") == 0) { cur = value; *cur = 0; } break; case YXML_ELEMEND: level -= 1; if (level < 0) /* End of section, * try the next section */ return ta_iter_next(ta); else if (level == 0 && cur) { /* content ready */ strcpytrunc( ta->zone, value , sizeof(ta->zone)); /* Reset to start of */ cur = NULL; ta->ptr = ta_start; ta->x = ta_x; } break; case YXML_CONTENT: if (!cur || level != 1) break; tmp = ta->x.data; while (*tmp && cur < value + sizeof(value)) *cur++ = *tmp++; if (cur >= value + sizeof(value)) cur = NULL; else *cur = 0; break; default: break; } ta->ptr++; } if (ta_iter_done(ta)) return NULL; } assert(ta->zone[0]); DEBUG_ANCHOR("Zone: %s, Find \n", ta->zone); level = 0; while (!ta_iter_done(ta)) { r = yxml_parse(&ta->x, *ta->ptr); if (r == YXML_ELEMSTART) { level += 1; DEBUG_ANCHOR("elem start: %s, level: %d\n", ta->x.elem, level); if (level == 1 && strcasecmp(ta->x.elem, "keydigest") == 0) break; } else if (r == YXML_ELEMEND) { level -= 1; if (level < 0) { /* End of section */ ta->zone[0] = 0; return ta_iter_next(ta); } } ta->ptr++; } if (ta_iter_done(ta)) return NULL; DEBUG_ANCHOR("Found , Parse attributes\n"); ta->validFrom = ta->validUntil = 0; *ta->keytag = *ta->algorithm = *ta->digesttype = *ta->digest = 0; cur = NULL; value[0] = 0; attr_type = -1; while (!ta_iter_done(ta)) { switch ((r = yxml_parse(&ta->x, *ta->ptr))) { case YXML_ELEMSTART: break; case YXML_ELEMEND: /* End of section, try next */ return ta_iter_next(ta); case YXML_ATTRSTART: DEBUG_ANCHOR("attrstart: %s\n", ta->x.attr); if (strcasecmp(ta->x.attr, "validfrom") == 0) attr_type = VALIDFROM; else if (strcasecmp(ta->x.attr, "validuntil") == 0) attr_type = VALIDUNTIL; else break; cur = value; *cur = 0; break; case YXML_ATTREND: if (!cur) break; cur = NULL; DEBUG_ANCHOR("attrval: %s\n", value); switch (attr_type) { case VALIDFROM: ta->validFrom = _getdns_xml_convertdate(value); break; case VALIDUNTIL: ta->validUntil = _getdns_xml_convertdate(value); break; } break; case YXML_ATTRVAL: if (!cur) break; tmp = ta->x.data; while (*tmp && cur < value + sizeof(value)) *cur++ = *tmp++; if (cur >= value + sizeof(value)) cur = NULL; else *cur = 0; break; case YXML_OK: case YXML_CONTENT: break; default: DEBUG_ANCHOR("r: %d\n", (int)r); return NULL; break; } if (r == YXML_ELEMSTART) break; ta->ptr++; } if (ta_iter_done(ta)) return NULL; assert(r == YXML_ELEMSTART); DEBUG_ANCHOR("Within , Parse child elements\n"); cur = NULL; value[0] = 0; elem_type = -1; for (;;) { switch (r) { case YXML_ELEMSTART: level += 1; DEBUG_ANCHOR("elem start: %s, level: %d\n", ta->x.elem, level); if (level != 2) break; else if (strcasecmp(ta->x.elem, "keytag") == 0) elem_type = KEYTAG; else if (strcasecmp(ta->x.elem, "algorithm") == 0) elem_type = ALGORITHM; else if (strcasecmp(ta->x.elem, "digesttype") == 0) elem_type = DIGESTTYPE; else if (strcasecmp(ta->x.elem, "digest") == 0) elem_type = DIGEST; else break; cur = value; *cur = 0; break; case YXML_ELEMEND: level -= 1; if (level < 0) { /* End of section */ ta->zone[0] = 0; return ta_iter_next(ta); } else if (level != 1 || !cur) break; cur = NULL; DEBUG_ANCHOR("elem end: %s\n", value); switch (elem_type) { case KEYTAG: strcpytrunc( ta->keytag, value , sizeof(ta->keytag)); break; case ALGORITHM: strcpytrunc( ta->algorithm, value , sizeof(ta->algorithm)); break; case DIGESTTYPE: strcpytrunc( ta->digesttype, value , sizeof(ta->digesttype)); break; case DIGEST: strcpytrunc( ta->digest, value , sizeof(ta->digest)); break; } break; case YXML_CONTENT: if (!cur) break; tmp = ta->x.data; while (*tmp && cur < value + sizeof(value)) *cur++ = *tmp++; if (cur >= value + sizeof(value)) cur = NULL; else *cur = 0; break; default: break; } if (level == 0) break; ta->ptr++; if (ta_iter_done(ta)) return NULL; r = yxml_parse(&ta->x, *ta->ptr); } return ta->validFrom && *ta->keytag && *ta->algorithm && *ta->digesttype && *ta->digest ? ta : ta_iter_next(ta); } static ta_iter *ta_iter_init(ta_iter *ta, const char *doc, size_t doc_len) { ta->ptr = ta->start = doc; ta->end = ta->start + doc_len; yxml_init(&ta->x, ta->yxml_buf, sizeof(ta->yxml_buf)); ta->zone[0] = 0; return ta_iter_next(ta); } uint16_t _getdns_parse_xml_trust_anchors_buf( gldns_buffer *gbuf, uint64_t *now_ms, 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_ms == 0) *now_ms = _getdns_get_now_ms(); if ((time_t)(*now_ms / 1000) < ta->validFrom) DEBUG_ANCHOR("Disregarding trust anchor " "%s for %s which is not yet valid", ta->keytag, ta->zone); else if (ta->validUntil != 0 && (time_t)(*now_ms / 1000) > 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; } static const char tas_write_p7s_buf[] = "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "\r\n"; static const char tas_write_xml_p7s_buf[] = "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "\r\n" "GET %s HTTP/1.1\r\n" "Host: %s\r\n" "\r\n"; static inline const char * rt_str(uint16_t rt) { return rt == GETDNS_RRTYPE_A ? "A" : rt == GETDNS_RRTYPE_AAAA ? "AAAA" : "?"; } static int tas_busy(tas_connection *a) { return a && a->req != NULL; } static int tas_fetching(tas_connection *a) { return a->fd >= 0; } static void tas_rinse(getdns_context *context, tas_connection *a) { if (a->event.ev) GETDNS_CLEAR_EVENT(a->loop, &a->event); a->event.ev = NULL; if (a->fd >= 0) close(a->fd); a->fd = -1; if (a->xml.data) GETDNS_FREE(context->mf, a->xml.data); a->xml.data = NULL; a->xml.size = 0; if (a->tcp.read_buf && a->tcp.read_buf != context->tas_hdr_spc) GETDNS_FREE(context->mf, a->tcp.read_buf); a->tcp.read_buf = NULL; } static void tas_cleanup(getdns_context *context, tas_connection *a) { tas_rinse(context, a); if (a->req) _getdns_context_cancel_request(a->req->owner); if (a->http) GETDNS_FREE(context->mf, (void *)a->http); (void) memset(a, 0, sizeof(*a)); a->fd = -1; } static void tas_success(getdns_context *context, tas_connection *a) { tas_connection *other = &context->a == a ? &context->aaaa : &context->a; tas_cleanup(context, a); tas_cleanup(context, other); _getdns_log( &context->log, GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_INFO , "Successfully fetched new trust anchors\n"); context->trust_anchors_source = GETDNS_TASRC_XML; _getdns_ta_notify_dnsreqs(context); } static void tas_fail(getdns_context *context, tas_connection *a) { tas_connection *other = &context->a == a ? &context->aaaa : &context->a; uint16_t rt = &context->a == a ? GETDNS_RRTYPE_A : GETDNS_RRTYPE_AAAA; tas_cleanup(context, a); if (!tas_busy(other)) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Fatal error fetching trust anchor: " "%s connection failed too\n", rt_str(rt)); context->trust_anchors_source = GETDNS_TASRC_FAILED; context->trust_anchors_backoff_expiry = _getdns_get_now_ms() + context->trust_anchors_backoff_time; _getdns_ta_notify_dnsreqs(context); } else _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_WARNING , "%s connection failed, waiting for %s\n" , rt_str(rt) , rt_str( rt == GETDNS_RRTYPE_A ? GETDNS_RRTYPE_AAAA : GETDNS_RRTYPE_A)); } static void tas_connect(getdns_context *context, tas_connection *a); static void tas_next(getdns_context *context, tas_connection *a) { tas_connection *other = a == &context->a ? &context->aaaa : &context->a; DEBUG_ANCHOR("Try next address\n"); if (a->rr) { if (!(a->rr = _getdns_rrtype_iter_next(a->rr))) tas_fail(context, a); else tas_rinse(context, a); } if (other->rr) tas_connect(context, other); else if (a->rr) tas_connect(context, a); } static void tas_timeout_cb(void *userarg) { getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; getdns_context *context = (getdns_context *)dnsreq->user_pointer; tas_connection *a; if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) a = &context->a; else a = &context->aaaa; _getdns_log( &context->log, GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_WARNING , "Trust anchor fetch timeout\n"); GETDNS_CLEAR_EVENT(a->loop, &a->event); tas_next(context, a); } static void tas_reconnect_cb(void *userarg) { getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; getdns_context *context = (getdns_context *)dnsreq->user_pointer; tas_connection *a; if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) a = &context->a; else a = &context->aaaa; _getdns_log( &context->log, GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "Waiting for second document timeout. Reconnecting...\n"); GETDNS_CLEAR_EVENT(a->loop, &a->event); close(a->fd); a->fd = -1; if (a->state == TAS_READ_PS7_HDR) { a->state = TAS_RETRY; tas_connect(context, a); } else tas_next(context, a); } static void tas_read_cb(void *userarg); static void tas_write_cb(void *userarg); static void tas_doc_read(getdns_context *context, tas_connection *a) { assert(a->tcp.read_pos == a->tcp.read_buf + a->tcp.read_buf_len); assert(context); if (a->state == TAS_READ_XML_DOC) { if (a->xml.data) GETDNS_FREE(context->mf, a->xml.data); a->xml.data = a->tcp.read_buf; a->xml.size = a->tcp.read_buf_len; } else assert(a->state == TAS_READ_PS7_DOC || a->state == TAS_RETRY_PS7_DOC); a->state += 1; GETDNS_CLEAR_EVENT(a->loop, &a->event); if (a->state == TAS_DONE || a->state == TAS_RETRY_DONE) { getdns_bindata p7s_bd; uint8_t *tas = context->trust_anchors_spc; size_t tas_len = sizeof(context->trust_anchors_spc); const char *verify_email = NULL; getdns_bindata verify_CA; getdns_return_t r; uint64_t now_ms = 0; p7s_bd.data = a->tcp.read_buf; p7s_bd.size = a->tcp.read_buf_len; if ((r = getdns_context_get_trust_anchors_verify_CA( context, (const char **)&verify_CA.data))) _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Cannot get trust anchor verify CA: " "\"%s\"\n", getdns_get_errorstr_by_id(r)); else if (!(verify_CA.size = strlen((const char *)verify_CA.data))) ; /* pass */ else if ((r = getdns_context_get_trust_anchors_verify_email( context, &verify_email))) _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Cannot get trust anchor verify email: " "\"%s\"\n", getdns_get_errorstr_by_id(r)); else if (!(tas = _getdns_tas_validate(&context->mf, &a->xml, &p7s_bd, &verify_CA, verify_email, &now_ms, tas, &tas_len))) ; /* pass */ else { context->trust_anchors = tas; context->trust_anchors_len = tas_len; (void) _getdns_context_write_priv_file( context, "root-anchors.xml", &a->xml); (void) _getdns_context_write_priv_file( context, "root-anchors.p7s", &p7s_bd); tas_success(context, a); return; } tas_fail(context, a); return; } /* First try to read signatures immediately */ a->state += 1; assert(a->state == TAS_READ_PS7_HDR); a->tcp.read_buf = context->tas_hdr_spc; a->tcp.read_buf_len = sizeof(context->tas_hdr_spc); /* Check for surplus read bytes, for the P7S headers */ if (a->tcp.to_read > 0) { a->tcp.read_pos = a->tcp.read_buf + a->tcp.to_read; a->tcp.to_read = sizeof(context->tas_hdr_spc) - a->tcp.to_read; } else { a->tcp.read_pos = a->tcp.read_buf; a->tcp.to_read = sizeof(context->tas_hdr_spc); } GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 2000, getdns_eventloop_event_init(&a->event, a->req->owner, tas_read_cb, NULL, tas_reconnect_cb)); return; } static void tas_read_cb(void *userarg) { getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; getdns_context *context = (getdns_context *)dnsreq->user_pointer; tas_connection *a; ssize_t n, i; if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) a = &context->a; else a = &context->aaaa; DEBUG_ANCHOR( "state: %d, to_read: %d\n" , (int)a->state, (int)a->tcp.to_read); #ifdef USE_WINSOCK n = recv(a->fd, (char *)a->tcp.read_pos, a->tcp.to_read, 0); #else n = read(a->fd, a->tcp.read_pos, a->tcp.to_read); #endif if (n == 0) { DEBUG_ANCHOR("Connection closed\n"); GETDNS_CLEAR_EVENT(a->loop, &a->event); close(a->fd); a->fd = -1; if (a->state == TAS_READ_PS7_HDR) { a->state = TAS_RETRY; tas_connect(context, a); } else tas_next(context, a); return; } else if (n > 0 && ( a->state == TAS_READ_XML_DOC || a->state == TAS_READ_PS7_DOC || a->state == TAS_RETRY_PS7_DOC)) { assert(n <= (ssize_t)a->tcp.to_read); DEBUG_ANCHOR("read: %d bytes at %p, for doc %p of size %d\n", (int)n, (void *)a->tcp.read_pos , (void *)a->tcp.read_buf, (int)a->tcp.read_buf_len); a->tcp.read_pos += n; a->tcp.to_read -= n; if (a->tcp.to_read == 0) tas_doc_read(context, a); return; } else if (n > 0) { ssize_t p = 0; int doc_len = -1; int len; char *ln; char *endptr; n += a->tcp.read_pos - a->tcp.read_buf; for (i = 0; i < (n - 1); i++) { if (a->tcp.read_buf[i] != '\r' || a->tcp.read_buf[i+1] != '\n') continue; len = (int)(i - p); ln = (char *)&a->tcp.read_buf[p]; DEBUG_ANCHOR("line: \"%.*s\"\n", len, ln); if (len >= 16 && !strncasecmp(ln, "Content-Length: ", 16)) { ln[len] = 0; doc_len = (int)strtol(ln + 16, &endptr , 10); if (endptr == ln || *endptr != 0) doc_len = -1; } if (i - p == 0) { i += 2; break; } p = i + 2; i++; } if (doc_len > 0) { uint8_t *doc = GETDNS_XMALLOC( context->mf, uint8_t, doc_len + 1); doc[doc_len] = 0; DEBUG_ANCHOR("i: %d, n: %d, doc_len: %d\n" , (int)i, (int)n, doc_len); if (!doc) _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR , GETDNS_LOG_ERR , "Memory error while reading " "trust anchor\n"); else { ssize_t surplus = n - i; a->state += 1; /* With pipelined read, the buffer might * contain the full document, plus a piece * of the headers of the next document! * Currently context->tas_hdr_spc is kept * small enough to anticipate this. */ if (surplus <= 0) { a->tcp.read_pos = doc; a->tcp.to_read = doc_len; } else if (surplus > doc_len) { (void) memcpy( doc, a->tcp.read_buf + i, doc_len); a->tcp.read_pos = doc + doc_len; /* Special value to indicate a begin * of the next reply is already * present. Detectable by: * (read_pos == read_buf + read_buf_len) * && to_read > 0; */ a->tcp.to_read = surplus - doc_len; (void) memmove(a->tcp.read_buf, a->tcp.read_buf + i + doc_len, surplus - doc_len); } else { assert(surplus <= doc_len); (void) memcpy( doc, a->tcp.read_buf + i, surplus); a->tcp.read_pos = doc + surplus; a->tcp.to_read = doc_len - surplus; } a->tcp.read_buf = doc; a->tcp.read_buf_len = doc_len; if (a->tcp.read_pos == doc + doc_len) tas_doc_read(context, a); return; } } } else if (_getdns_socketerror_wants_retry()) return; _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Error while receiving trust anchor: %s\n" , _getdns_errnostr()); GETDNS_CLEAR_EVENT(a->loop, &a->event); tas_next(context, a); } static void tas_write_cb(void *userarg) { getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; getdns_context *context = (getdns_context *)dnsreq->user_pointer; tas_connection *a; ssize_t written; if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) a = &context->a; else a = &context->aaaa; DEBUG_ANCHOR( "state: %d, to_write: %d\n" , (int)a->state, (int)a->tcp.write_buf_len); #ifdef USE_WINSOCK DEBUG_ANCHOR("sending to: %d\n", a->fd); written = send(a->fd, (const char *)a->tcp.write_buf, a->tcp.write_buf_len, 0); #else written = write(a->fd, a->tcp.write_buf, a->tcp.write_buf_len); #endif if (written >= 0) { assert(written <= (ssize_t)a->tcp.write_buf_len); a->tcp.write_buf += written; a->tcp.write_buf_len -= written; if (a->tcp.write_buf_len > 0) /* Write remainder */ return; a->state += 1; a->tcp.read_buf = context->tas_hdr_spc; a->tcp.read_buf_len = sizeof(context->tas_hdr_spc); a->tcp.read_pos = a->tcp.read_buf; a->tcp.to_read = sizeof(context->tas_hdr_spc); GETDNS_CLEAR_EVENT(a->loop, &a->event); DEBUG_ANCHOR("All written, schedule read\n"); GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 2000, getdns_eventloop_event_init(&a->event, a->req->owner, tas_read_cb, NULL, tas_timeout_cb)); return; } else if (_getdns_socketerror_wants_retry()) return; _getdns_log( &context->log, GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Error while sending to trust anchor site: %s\n" , _getdns_errnostr()); GETDNS_CLEAR_EVENT(a->loop, &a->event); tas_next(context, a); } static getdns_return_t _getdns_get_tas_url_hostname( getdns_context *context, char *hostname, const char **path) { getdns_return_t r; const char *url; char *next_slash; size_t s; if ((r = getdns_context_get_trust_anchors_url(context, &url))) return r; if ((next_slash = strchr(url + 7 /* "http://" */, '/'))) { if (next_slash - url - 7 > 254) return GETDNS_RETURN_NO_SUCH_LIST_ITEM; if (path) *path = next_slash; strncpy(hostname, url + 7, next_slash - url - 7); hostname[next_slash - url - 7] = 0; } else { if (path) *path = url + strlen(url); strncpy(hostname, url + 7, 254); hostname[254] = 0; } s = strlen(hostname); if (s && s < 255 && hostname[s - 1] != '.') { hostname[s] = '.'; hostname[s+1] = '\0'; } return GETDNS_RETURN_GOOD; } static void tas_connect(getdns_context *context, tas_connection *a) { char a_buf[40]; int r; #ifdef HAVE_FCNTL int flag; #elif defined(HAVE_IOCTLSOCKET) unsigned long on = 1; #endif if (a->rr->rr_i.nxt - (a->rr->rr_i.rr_type + 10) != ( a->req->request_type == GETDNS_RRTYPE_A ? 4 : a->req->request_type == GETDNS_RRTYPE_AAAA ? 16 : -1)) { tas_next(context, a); return; } _getdns_log( &context->log, GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "Setting op connection to: %s\n" , inet_ntop( ( a->req->request_type == GETDNS_RRTYPE_A ? AF_INET : AF_INET6) , a->rr->rr_i.rr_type + 10 , a_buf, sizeof(a_buf))); if ((a->fd = socket(( a->req->request_type == GETDNS_RRTYPE_A ? AF_INET : AF_INET6), SOCK_STREAM, IPPROTO_TCP)) == -1) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Error creating socket: %s\n", _getdns_errnostr()); tas_next(context, a); return; } #ifdef HAVE_FCNTL if((flag = fcntl(a->fd, F_GETFL)) != -1) { flag |= O_NONBLOCK; if(fcntl(a->fd, F_SETFL, flag) == -1) { /* ignore error, continue blockingly */ } } #elif defined(HAVE_IOCTLSOCKET) if(ioctlsocket(a->fd, FIONBIO, &on) != 0) { /* ignore error, continue blockingly */ } #endif if (a->req->request_type == GETDNS_RRTYPE_A) { struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(80); (void) memcpy(&addr.sin_addr, a->rr->rr_i.rr_type + 10, 4); r = connect(a->fd, (struct sockaddr *)&addr, sizeof(addr)); } else { struct sockaddr_in6 addr; addr.sin6_family = AF_INET6; addr.sin6_port = htons(80); addr.sin6_flowinfo = 0; (void) memcpy(&addr.sin6_addr, a->rr->rr_i.rr_type + 10, 16); addr.sin6_scope_id = 0; r = connect(a->fd, (struct sockaddr *)&addr, sizeof(addr)); } if (r == 0 || (r == -1 && (_getdns_socketerror() == _getdns_EINPROGRESS || _getdns_socketerror() == _getdns_EWOULDBLOCK))) { char tas_hostname[256]; const char *path = "", *fmt; getdns_return_t R; char *write_buf; size_t buf_sz, path_len, hostname_len; a->state += 1; if (a->http) { GETDNS_FREE(context->mf, (void *)a->http); a->http = NULL; a->tcp.write_buf = NULL; a->tcp.write_buf_len = 0; a->tcp.written = 0; } if ((R = _getdns_get_tas_url_hostname( context, tas_hostname, &path))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Cannot get hostname from trust anchor " "url: \"%s\"\n" , getdns_get_errorstr_by_id(r)); goto error; } hostname_len = strlen(tas_hostname); if (hostname_len > 0 && tas_hostname[hostname_len - 1] == '.') tas_hostname[--hostname_len] = '\0'; path_len = strlen(path); if (path_len < 4) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Trust anchor path \"%s\" too small\n" , path); goto error; } if (a->state == TAS_RETRY_GET_PS7) { buf_sz = sizeof(tas_write_p7s_buf) + 1 * (hostname_len - 2) + 1 * (path_len - 2); fmt = tas_write_p7s_buf; } else { buf_sz = sizeof(tas_write_xml_p7s_buf) + 2 * (hostname_len - 2) + 2 * (path_len - 2); fmt = tas_write_xml_p7s_buf; } if (!(write_buf = GETDNS_XMALLOC(context->mf, char, buf_sz))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Cannot allocate write buffer for " "sending to trust anchor host\n"); goto error; } if (a->state == TAS_RETRY_GET_PS7) { (void) snprintf( write_buf, buf_sz, fmt , path, tas_hostname); write_buf[4 + path_len - 3] = write_buf[4 + path_len - 3] == 'X' ? 'P' : 'p'; write_buf[4 + path_len - 2] = '7'; write_buf[4 + path_len - 1] = write_buf[4 + path_len - 1] == 'L' ? 'S' : 's'; } else { (void) snprintf( write_buf, buf_sz, fmt , path, tas_hostname , path, tas_hostname); write_buf[29 + hostname_len + 2 * path_len - 3] = write_buf[29 + hostname_len + 2 * path_len - 3] == 'X' ? 'P' : 'p'; write_buf[29 + hostname_len + 2 * path_len - 2] = '7'; write_buf[29 + hostname_len + 2 * path_len - 1] = write_buf[29 + hostname_len + 2 * path_len - 1] == 'L' ? 'S' : 's'; } DEBUG_ANCHOR("Write buffer: \"%s\"\n", write_buf); a->tcp.write_buf = (uint8_t *)(a->http = write_buf); a->tcp.write_buf_len = buf_sz - 1; a->tcp.written = 0; GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 2000, getdns_eventloop_event_init(&a->event, a->req->owner, NULL, tas_write_cb, tas_timeout_cb)); DEBUG_ANCHOR("Scheduled write with event\n"); return; } else _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Error connecting to trust anchor host: %s\n " , _getdns_errnostr()); error: tas_next(context, a); } static void tas_happy_eyeballs_cb(void *userarg) { getdns_dns_req *dnsreq = (getdns_dns_req *)userarg; getdns_context *context = (getdns_context *)dnsreq->user_pointer; assert(dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A); if (tas_fetching(&context->aaaa)) return; else { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "Too late reception of AAAA for trust anchor " "host for Happy Eyeballs\n"); GETDNS_CLEAR_EVENT(context->a.loop, &context->a.event); tas_connect(context, &context->a); } } static void _tas_hostname_lookup_cb(getdns_dns_req *dnsreq) { getdns_context *context = (getdns_context *)dnsreq->user_pointer; tas_connection *a; if (dnsreq->netreqs[0]->request_type == GETDNS_RRTYPE_A) a = &context->a; else a = &context->aaaa; a->rrset = _getdns_rrset_answer( &a->rrset_spc, a->req->response, a->req->response_len); if (!a->rrset) { char tas_hostname[256] = ""; (void) _getdns_get_tas_url_hostname(context, tas_hostname, NULL); _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "%s lookup for %s returned no response\n" , rt_str(a->req->request_type), tas_hostname); } else if (a->req->response_len < dnsreq->name_len + 12 || !_getdns_dname_equal(a->req->response + 12, dnsreq->name) || a->rrset->rr_type != a->req->request_type) { char tas_hostname[256] = ""; (void) _getdns_get_tas_url_hostname(context, tas_hostname, NULL); _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "%s lookup for %s returned wrong response\n" , rt_str(a->req->request_type), tas_hostname); } else if (!(a->rr = _getdns_rrtype_iter_init(&a->rr_spc, a->rrset))) { char tas_hostname[256] = ""; (void) _getdns_get_tas_url_hostname(context, tas_hostname, NULL); _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "%s lookup for %s returned no addresses\n" , rt_str(a->req->request_type), tas_hostname); } else { tas_connection *other = a == &context->a ? &context->aaaa : &context->a; a->loop = dnsreq->loop; if (tas_fetching(other)) ; /* pass */ else if (a == &context->a && tas_busy(other)) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "Waiting 25ms for AAAA to arrive\n"); GETDNS_SCHEDULE_EVENT(a->loop, a->fd, 25, getdns_eventloop_event_init(&a->event, a->req->owner, NULL, NULL, tas_happy_eyeballs_cb)); } else { if (other->event.ev && other->event.timeout_cb == tas_happy_eyeballs_cb) { DEBUG_ANCHOR("Clearing Happy Eyeballs timer\n"); GETDNS_CLEAR_EVENT(other->loop, &other->event); } tas_connect(context, a); } return; } tas_fail(context, a); } void _getdns_start_fetching_ta( getdns_context *context, getdns_eventloop *loop, uint64_t *now_ms) { getdns_return_t r; size_t scheduled; char tas_hostname[256]; const char *verify_CA; const char *verify_email; if ((r = _getdns_get_tas_url_hostname(context, tas_hostname, NULL))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Cannot get hostname from trust anchor url: " "\"%s\"\n", getdns_get_errorstr_by_id(r)); return; } else if ((r = getdns_context_get_trust_anchors_verify_CA( context, &verify_CA))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Cannot get trust anchor verify CA: \"%s\"\n" , getdns_get_errorstr_by_id(r)); return; } else if (!verify_CA || !*verify_CA) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_INFO , "Trust anchor verification explicitly " "disabled by empty verify CA\n"); return; } else if ((r = getdns_context_get_trust_anchors_verify_email( context, &verify_email))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Cannot get trust anchor verify email: \"%s\"\n" , getdns_get_errorstr_by_id(r)); return; } else if (!verify_email || !*verify_email) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_INFO , "Trust anchor verification explicitly " "disabled by empty verify email\n"); return; } else if (!_getdns_context_can_write_appdata(context)) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_WARNING , "Not fetching TA, because " "non writeable appdata directory\n"); return; } DEBUG_ANCHOR("Hostname: %s\n", tas_hostname); DEBUG_ANCHOR("%s on the %ssynchronous loop\n", __FUNC__, loop == &context->sync_eventloop.loop ? "" : "a"); scheduled = 0; context->a.state = TAS_LOOKUP_ADDRESSES; if ((r = _getdns_general_loop(context, loop, tas_hostname, GETDNS_RRTYPE_A, no_dnssec_checking_disabled_opportunistic, context, &context->a.req, NULL, _tas_hostname_lookup_cb))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_WARNING , "Error scheduling A lookup for %s: %s\n" , tas_hostname, getdns_get_errorstr_by_id(r)); } else scheduled += 1; context->aaaa.state = TAS_LOOKUP_ADDRESSES; if ((r = _getdns_general_loop(context, loop, tas_hostname, GETDNS_RRTYPE_AAAA, no_dnssec_checking_disabled_opportunistic, context, &context->aaaa.req, NULL, _tas_hostname_lookup_cb))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_WARNING , "Error scheduling AAAA lookup for %s: %s\n" , tas_hostname, getdns_get_errorstr_by_id(r)); } else scheduled += 1; if (!scheduled) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_WARNING , "Error scheduling address lookups for %s\n" , tas_hostname); context->trust_anchors_source = GETDNS_TASRC_FAILED; if (now_ms) { if (*now_ms == 0) *now_ms = _getdns_get_now_ms(); context->trust_anchors_backoff_expiry = *now_ms + context->trust_anchors_backoff_time; } else context->trust_anchors_backoff_expiry = _getdns_get_now_ms() + context->trust_anchors_backoff_time; _getdns_ta_notify_dnsreqs(context); } else context->trust_anchors_source = GETDNS_TASRC_FETCHING; } static int _uint16_cmp(const void *a, const void *b) { return (int)*(uint16_t *)a - (int)*(uint16_t *)b; } static int _uint8x16_cmp(const void *a, const void *b) { return memcmp(a, b, RRSIG_RDATA_LEN); } static void _getdns_init_ksks(_getdns_ksks *ksks, _getdns_rrset *dnskey_set) { _getdns_rrtype_iter *rr, rr_space; _getdns_rrsig_iter *rrsig, rrsig_space; assert(ksks); assert(dnskey_set); assert(dnskey_set->rr_type == GETDNS_RRTYPE_DNSKEY); ksks->n = 0; for ( rr = _getdns_rrtype_iter_init(&rr_space, dnskey_set) ; rr && ksks->n < MAX_KSKS ; rr = _getdns_rrtype_iter_next(rr)) { if (rr->rr_i.nxt - rr->rr_i.rr_type < 12 || !(rr->rr_i.rr_type[11] & 1)) continue; /* Not a KSK */ ksks->ids[ksks->n++] = gldns_calc_keytag_raw( rr->rr_i.rr_type + 10, rr->rr_i.nxt - rr->rr_i.rr_type - 10); } qsort(ksks->ids, ksks->n, sizeof(uint16_t), _uint16_cmp); ksks->n_rrsigs = 0; for ( rrsig = _getdns_rrsig_iter_init(&rrsig_space, dnskey_set) ; rrsig && ksks->n_rrsigs < MAX_KSKS ; rrsig = _getdns_rrsig_iter_next(rrsig)) { if (rrsig->rr_i.nxt - rrsig->rr_i.rr_type < 28) continue; (void) memcpy(ksks->rrsigs[ksks->n_rrsigs++], rrsig->rr_i.rr_type + 12, RRSIG_RDATA_LEN); } qsort(ksks->rrsigs, ksks->n_rrsigs, RRSIG_RDATA_LEN, _uint8x16_cmp); } static int _getdns_ksks_equal(_getdns_ksks *a, _getdns_ksks *b) { return a == b || ( a != NULL && b != NULL && a->n == b->n && memcmp(a->ids, b->ids, a->n * sizeof(uint16_t)) == 0 && a->n_rrsigs == b->n_rrsigs && memcmp(a->rrsigs, b->rrsigs, a->n_rrsigs * RRSIG_RDATA_LEN) == 0); } static void _getdns_context_read_root_ksk(getdns_context *context) { FILE *fp; struct gldns_file_parse_state pst; size_t len, dname_len; uint8_t buf_spc[4096], *buf = buf_spc, *ptr = buf_spc; size_t buf_sz = sizeof(buf_spc); _getdns_rrset root_dnskey; uint8_t *root_dname = (uint8_t *)"\00"; if (!(fp = _getdns_context_get_priv_fp(context, "root.key"))) return; for (;;) { size_t n_rrs = 0; *pst.origin = 0; pst.origin_len = 1; *pst.prev_rr = 0; pst.prev_rr_len = 1; pst.default_ttl = 0; pst.lineno = 1; (void) memset(buf, 0, 12); ptr += 12; while (!feof(fp)) { len = buf + buf_sz - ptr; dname_len = 0; if (gldns_fp2wire_rr_buf(fp, ptr, &len, &dname_len, &pst)) break; if ((ptr += len) > buf + buf_sz) break; if (len) n_rrs += 1; if (dname_len && dname_len < sizeof(pst.prev_rr)) { memcpy(pst.prev_rr, ptr, dname_len); pst.prev_rr_len = dname_len; } } if (ptr <= buf + buf_sz) { gldns_write_uint16(buf + GLDNS_ANCOUNT_OFF, n_rrs); break; } rewind(fp); if (buf == buf_spc) buf_sz = 65536; else { GETDNS_FREE(context->mf, buf); buf_sz *= 2; } if (!(buf = GETDNS_XMALLOC(context->mf, uint8_t, buf_sz))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Error allocating memory to read " "root.key\n"); break;; } ptr = buf; }; fclose(fp); if (!buf) return; root_dnskey.name = root_dname; root_dnskey.rr_class = GETDNS_RRCLASS_IN; root_dnskey.rr_type = GETDNS_RRTYPE_DNSKEY; root_dnskey.pkt = buf; root_dnskey.pkt_len = ptr - buf; root_dnskey.sections = SECTION_ANSWER; _getdns_init_ksks(&context->root_ksk, &root_dnskey); if (buf && buf != buf_spc) GETDNS_FREE(context->mf, buf); } void _getdns_context_update_root_ksk( getdns_context *context, _getdns_rrset *dnskey_set) { _getdns_ksks root_ksk_seen; _getdns_rrtype_iter *rr, rr_space; _getdns_rrsig_iter *rrsig, rrsig_space; char str_spc[4096], *str_buf, *str_pos; int sz_needed; int remaining; size_t str_sz = 0; getdns_bindata root_key_bd; _getdns_init_ksks(&root_ksk_seen, dnskey_set); if (_getdns_ksks_equal(&context->root_ksk, &root_ksk_seen)) return; /* root DNSKEY rrset already known */ /* Try to read root DNSKEY rrset from root.key */ _getdns_context_read_root_ksk(context); if (_getdns_ksks_equal(&context->root_ksk, &root_ksk_seen)) return; /* root DNSKEY rrset same as the safed one */ /* Different root DNSKEY rrset. Perhaps because of failure to read * from disk. If we cannot write to our appdata directory, bail out */ if (context->can_write_appdata == PROP_UNABLE) return; /* We might be able to write or we do not know whether we can write * to the appdata directory. In the latter we'll try to write to * find out. The section below converts the wireformat DNSKEY rrset * to presentationformat. */ str_pos = str_buf = str_spc; remaining = sizeof(str_spc); for (;;) { for ( rr = _getdns_rrtype_iter_init(&rr_space, dnskey_set) ; rr ; rr = _getdns_rrtype_iter_next(rr)) { sz_needed = gldns_wire2str_rr_buf((uint8_t *)rr->rr_i.pos, rr->rr_i.nxt - rr->rr_i.pos, str_pos, (size_t)(remaining > 0 ? remaining : 0)); str_pos += sz_needed; remaining -= sz_needed; } for ( rrsig = _getdns_rrsig_iter_init(&rrsig_space, dnskey_set) ; rrsig ; rrsig = _getdns_rrsig_iter_next(rrsig)) { sz_needed = gldns_wire2str_rr_buf((uint8_t *)rrsig->rr_i.pos, rrsig->rr_i.nxt - rrsig->rr_i.pos, str_pos, (size_t)(remaining > 0 ? remaining : 0)); str_pos += sz_needed; remaining -= sz_needed; } if (remaining > 0) { *str_pos = 0; if (str_buf == str_spc) str_sz = sizeof(str_spc) - remaining; break; } if (str_buf != str_spc) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Error determining buffer size for root " "KSK\n"); if (str_buf) GETDNS_FREE(context->mf, str_buf); return; } if (!(str_pos = str_buf = GETDNS_XMALLOC( context->mf, char, (str_sz = sizeof(str_spc) - remaining) + 1))) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_ERR , "Error allocating memory to read " "root KSK\n"); return; } remaining = str_sz + 1; }; /* Write presentation format DNSKEY rrset to "root.key" file */ root_key_bd.size = str_sz; root_key_bd.data = (void *)str_buf; if (_getdns_context_write_priv_file( context, "root.key", &root_key_bd)) { size_t i; /* A new "root.key" file was written. When they contain * key_id's which are not in "root-anchors.xml", then update * "root-anchors.xml". */ for (i = 0; i < context->root_ksk.n; i++) { _getdns_rrset_iter tas_iter_spc, *ta; for ( ta = _getdns_rrset_iter_init(&tas_iter_spc , context->trust_anchors , context->trust_anchors_len , SECTION_ANSWER) ; ta ; ta = _getdns_rrset_iter_next(ta)) { _getdns_rrtype_iter *rr, rr_space; _getdns_rrset *rrset; if (!(rrset = _getdns_rrset_iter_value(ta))) continue; if (*rrset->name != '\0') continue; /* Not a root anchor */ if (rrset->rr_type == GETDNS_RRTYPE_DS) { for ( rr = _getdns_rrtype_iter_init( &rr_space, rrset) ; rr ; rr = _getdns_rrtype_iter_next(rr) ) { if (rr->rr_i.nxt - rr->rr_i.rr_type < 12) continue; DEBUG_ANCHOR("DS with id: %d\n" , (int)gldns_read_uint16(rr->rr_i.rr_type + 10)); if (gldns_read_uint16( rr->rr_i.rr_type + 10) == context->root_ksk.ids[i]) break; } if (rr) break; continue; } if (rrset->rr_type != GETDNS_RRTYPE_DNSKEY) continue; for ( rr = _getdns_rrtype_iter_init(&rr_space , rrset) ; rr ; rr = _getdns_rrtype_iter_next(rr)) { if (rr->rr_i.nxt-rr->rr_i.rr_type < 12 || !(rr->rr_i.rr_type[11] & 1)) continue; /* Not a KSK */ if (gldns_calc_keytag_raw( rr->rr_i.rr_type + 10, rr->rr_i.nxt-rr->rr_i.rr_type - 10) == context->root_ksk.ids[i]) break; } if (rr) break; } if (!ta) { _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR , GETDNS_LOG_NOTICE , "Key with id %d not found in TA; " "\"root-anchors.xml\" needs to be " "updated.\n" , context->root_ksk.ids[i]); context->trust_anchors_source = GETDNS_TASRC_XML_UPDATE; break; } _getdns_log( &context->log , GETDNS_LOG_SYS_ANCHOR, GETDNS_LOG_DEBUG , "Key with id %d found in TA\n" , context->root_ksk.ids[i]); } } if (str_buf && str_buf != str_spc) GETDNS_FREE(context->mf, str_buf); } /* anchor.c */