RFC7958 root-anchors.xml parsing

This commit is contained in:
Willem Toorop 2017-06-22 00:36:26 +02:00
parent 631bf3fffc
commit a2cf568190
2 changed files with 393 additions and 7 deletions

View File

@ -114,7 +114,7 @@ AC_SUBST(WPEDANTICFLAG)
AC_SUBST(WNOERRORFLAG)
case "$host_os" in
linux* ) CFLAGS="$CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE"
linux* ) CFLAGS="$CFLAGS -D_BSD_SOURCE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE"
;;
solaris* ) CFLAGS="$CFLAGS -D__EXTENSIONS__" # for strdup() from <string.h>
;;

View File

@ -32,18 +32,21 @@
#include "config.h"
#include "debug.h"
#include "anchor.h"
#include <expat.h>
#include <fcntl.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <strings.h>
#include <time.h>
#include "types-internal.h"
#include "context.h"
#include "yxml/yxml.h"
#include "gldns/parseutil.h"
#define P7SIGNER "dnssec@iana.org"
/* The ICANN CA fetched at 24 Sep 2010. Valid to 2028 */
static const char* _getdns_builtin_cert =
static char* _getdns_builtin_cert =
"-----BEGIN CERTIFICATE-----\n"
"MIIDdzCCAl+gAwIBAgIBATANBgkqhkiG9w0BAQsFADBdMQ4wDAYDVQQKEwVJQ0FO\n"
"TjEmMCQGA1UECxMdSUNBTk4gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxFjAUBgNV\n"
@ -201,10 +204,381 @@ _getdns_verify_p7sig(BIO* data, BIO* p7s, X509_STORE *store, const char* p7signe
return secure;
}
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;
/**
* 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.
*/
static time_t
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_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 <TrustAnchor>\n");
/* Determine start of <TrustAnchor> */
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 <Zone>\n");
/* Find <Zone> */
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 <TrustAnchor> section,
* try the next <TrustAnchor> section
*/
return ta_iter_next(ta);
else if (level == 0 && cur) {
/* <Zone> content ready */
(void) strncpy( ta->zone, value
, sizeof(ta->zone));
/* Reset to start of <TrustAnchor> */
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 <KeyDigest>\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 <TrustAnchor> section */
ta->zone[0] = 0;
return ta_iter_next(ta);
}
}
ta->ptr++;
}
if (ta_iter_done(ta))
return NULL;
DEBUG_ANCHOR("Found <KeyDigest>, 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 <KeyDigest> 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 = xml_convertdate(value);
break;
case VALIDUNTIL:
ta->validUntil = 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 <KeyDigest>, 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 <TrustAnchor> 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:
(void) strncpy( ta->keytag, value
, sizeof(ta->keytag));
break;
case ALGORITHM:
(void) strncpy( ta->algorithm, value
, sizeof(ta->algorithm));
break;
case DIGESTTYPE:
(void) strncpy( ta->digesttype, value
, sizeof(ta->digesttype));
break;
case DIGEST:
(void) strncpy( 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);
}
void _getdns_context_equip_with_anchor(getdns_context *context)
{
uint8_t xml_spc[16384], *xml_data = xml_spc;
uint8_t p7s_spc[16384], *p7s_data = p7s_spc;
uint8_t xml_spc[4096], *xml_data = xml_spc;
uint8_t p7s_spc[4096], *p7s_data = p7s_spc;
size_t xml_len, p7s_len;
BIO *xml = NULL, *p7s = NULL, *crt = NULL;
@ -227,7 +601,7 @@ void _getdns_context_equip_with_anchor(getdns_context *context)
DEBUG_ANCHOR("ERROR %s(): Failed allocating p7s BIO\n"
, __FUNC__);
else if (!(crt = BIO_new_mem_buf(_getdns_builtin_cert, -1)))
else if (!(crt = BIO_new_mem_buf((void *)_getdns_builtin_cert, -1)))
DEBUG_ANCHOR("ERROR %s(): Failed allocating crt BIO\n"
, __FUNC__);
@ -244,7 +618,19 @@ void _getdns_context_equip_with_anchor(getdns_context *context)
, __FUNC__);
else if (_getdns_verify_p7sig(xml, p7s, store, "dnssec@iana.org")) {
DEBUG_ANCHOR("Verifying trust-anchors SUCCEEDED, Yay!\n");
ta_iter ta_spc, *ta;
for ( ta = ta_iter_init(&ta_spc, (char *)xml_data, xml_len)
; ta; ta = ta_iter_next(ta)) {
DEBUG_ANCHOR( "%s IN DS %s %s %s %s\n"
, ta->zone
, ta->keytag
, ta->algorithm
, ta->digesttype
, ta->digest
);
}
} else {
DEBUG_ANCHOR("Verifying trust-anchors failed!\n");
}