From b69ff1c846250939de3e4f32ff4d07d6ee415009 Mon Sep 17 00:00:00 2001 From: Linus Nordberg Date: Wed, 30 Mar 2016 21:35:31 +0200 Subject: Add validatechain.c and move some code to common.c. dns-net2wire.c is nothing but an ugly hack on top of getdns_query.c making it save answer, validation_chain and trust anchors to three separate files. Used for testing purposes. validatechain takes the above mentioned three files as input and performs DNSSEC validation. --- tools/dnssec/Makefile | 16 ++-- tools/dnssec/common.c | 201 +++++++++++++++++++++++++++++++++++++++++++ tools/dnssec/common.h | 5 ++ tools/dnssec/dns-net2wire.c | 80 +++++++++++++++-- tools/dnssec/dns-wire2text.c | 62 +------------ tools/dnssec/net2wire.c | 105 +--------------------- tools/dnssec/validatechain.c | 110 +++++++++++++++++++++++ 7 files changed, 401 insertions(+), 178 deletions(-) create mode 100644 tools/dnssec/common.c create mode 100644 tools/dnssec/common.h create mode 100644 tools/dnssec/validatechain.c diff --git a/tools/dnssec/Makefile b/tools/dnssec/Makefile index 7009220..7c3f2fb 100644 --- a/tools/dnssec/Makefile +++ b/tools/dnssec/Makefile @@ -1,12 +1,14 @@ CFLAGS = -Wall -g -std=c99 CFLAGS_PEDANTIC = -pedantic -Werror -Wextra -all: dns-net2wire dns-wire2text +all: dns-net2wire dns-wire2text net2wire validatechain -dns-net2wire: dns-net2wire.c - $(CC) $(CFLAGS) -o $@ -lgetdns -lgetdns_ext_event -levent $< -dns-wire2text: dns-wire2text.c - $(CC) $(CFLAGS) $(CFLAGS_PEDANTIC) -o $@ -lgetdns -lgetdns_ext_event -levent $< +dns-net2wire: dns-net2wire.c common.c + $(CC) $(CFLAGS) -o $@ -lgetdns -lgetdns_ext_event -levent $^ +dns-wire2text: dns-wire2text.c common.c + $(CC) $(CFLAGS) $(CFLAGS_PEDANTIC) -o $@ -lgetdns -lgetdns_ext_event -levent $^ +validatechain: validatechain.c common.c + $(CC) $(CFLAGS) -o $@ -lgetdns -lgetdns_ext_event -levent $^ -net2wire: net2wire.c - $(CC) $(CFLAGS) -I ~/usr/include -L ~/usr/lib -o $@ -lgetdns -lgetdns_ext_event -levent $< +net2wire: net2wire.c common.c + $(CC) $(CFLAGS) -I ~/usr/include -L ~/usr/lib -o $@ -lgetdns -lgetdns_ext_event -levent $^ diff --git a/tools/dnssec/common.c b/tools/dnssec/common.c new file mode 100644 index 0000000..4382a1d --- /dev/null +++ b/tools/dnssec/common.c @@ -0,0 +1,201 @@ +#include +#include +#include +#include + +static int debug = 0; + +static getdns_return_t +dump_reply(FILE *fp, getdns_dict *reply, const char *section_name) +{ + getdns_list *section = NULL; + size_t section_len = -1; + getdns_return_t r; + uint8_t *res = NULL; + size_t res_len; + + r = getdns_dict_get_list(reply, section_name, §ion); + if (r) { + fprintf(stderr, + "unable to get section \"%s\" from reply\n", + section_name); + return r; + } + + r = getdns_list_get_length(section, §ion_len); + if (r) { + fprintf(stderr, "unable to get length of section\n"); + return r; + } + + for (size_t j = 0; j < section_len; j++) { + getdns_dict *rr = NULL; + + r = getdns_list_get_dict(section, j , &rr); + if (r) { + fprintf(stderr, "unable to get rr from entry " + "%d: %d\n", j, r); + return r; + } + + r = getdns_rr_dict2wire(rr, &res, &res_len); + if (r) { + fprintf(stderr, + "unable to convert entry %d " + "to wire format: %d\n", j, r); + return r; + } + + if (0 && debug) { + char *s = getdns_pretty_print_dict(rr); + puts(s); + free(s); + } + + if (fwrite(res, 1, res_len, fp) != res_len) { + fprintf(stderr, + "unable to write buffer to file: %s\n", + strerror(errno)); + return -errno; + } + + free(res); + } + + return 0; +} + +int +dump_tree(FILE *fp, const getdns_dict *response, const char *tree_name, const char *section_name) +{ + getdns_return_t r; + getdns_list *tree = NULL; + size_t n_replies = -1; + + r = getdns_dict_get_list(response, tree_name, &tree); + if (r) { + fprintf(stderr, "unable to get tree %s\n", tree_name); + return r; + } + + if (section_name == NULL) { + size_t tree_len = 0; + r = getdns_list_get_length(tree, &tree_len); + if (r) { + fprintf(stderr, "unable to get tree length\n"); + return r; + } + for (size_t i = 0; i < tree_len; i++) { + getdns_dict *rr = NULL; + r = getdns_list_get_dict(tree, i , &rr); + if (r) { + fprintf(stderr, "unable to get rr from entry %d: %d\n", i, r); + return r; + } + uint8_t *res = NULL; + size_t res_len = 0; + r = getdns_rr_dict2wire(rr, &res, &res_len); + if (r) { + fprintf(stderr, + "unable to convert entry %d " + "to wire format: %d\n", i, r); + return r; + } + if (fwrite(res, 1, res_len, fp) != res_len) { + fprintf(stderr, + "unable to write buffer to file: %s\n", + strerror(errno)); + return errno; + } + free(res); + } + } else { + r = getdns_list_get_length(tree, &n_replies); + if (r) { + fprintf(stderr, "unable to get number of replies\n"); + return r; + } + + for (size_t i = 0; i < n_replies; i++) { + getdns_dict *reply = NULL; + + r = getdns_list_get_dict(tree, i, &reply); + if (r) { + fprintf(stderr, "unable to get reply %d from tree\n", i); + return r; + } + + if (debug) { + char *s = getdns_pretty_print_dict(reply); + printf("Pretty-printing reply #%d:%s\n", i, s); + free(s); + } + + dump_reply(fp, reply, section_name); + } + } + + return 0; +} + +#define CHUNKSIZE 4096 + +size_t +read_buffer(FILE *infp, uint8_t **bufp_out, size_t size_hint) +{ + size_t nread = 0; + uint8_t *wirebuf = NULL; + size_t chunksize = CHUNKSIZE; + int chunks = 1; + + if (size_hint > 0) + chunksize = size_hint; + wirebuf = malloc(chunksize); + + if (wirebuf == NULL) + goto out; + + while (1) + { + size_t n = fread(wirebuf + nread, 1, chunksize, infp); + nread += n; + if (n < chunksize) + break; /* Done. */ + + wirebuf = realloc(wirebuf, ++chunks * chunksize); + if (wirebuf == NULL) + break; + } + + out: + if (bufp_out != NULL) + *bufp_out = wirebuf; + return nread; +} + +getdns_return_t +wire_rrs2list(const uint8_t *buf, size_t buf_len, getdns_list **list_out) +{ + getdns_return_t r = GETDNS_RETURN_GOOD; + getdns_list *list = getdns_list_create(); + getdns_dict *dict = NULL; + size_t rr_count = 0; + + if (list == NULL) + return GETDNS_RETURN_MEMORY_ERROR; + while (buf_len > 0) + { + r = getdns_wire2rr_dict_scan(&buf, &buf_len, &dict); + if (r) + break; + r = getdns_list_set_dict(list, rr_count, dict); + getdns_dict_destroy(dict); /* The list has a copy. */ + if (r) + break; + rr_count++; + } + + if (list_out) + *list_out = list; + return r; +} diff --git a/tools/dnssec/common.h b/tools/dnssec/common.h new file mode 100644 index 0000000..465379d --- /dev/null +++ b/tools/dnssec/common.h @@ -0,0 +1,5 @@ +int dump_tree(FILE *fp, const getdns_dict *response, const char *tree_name, const char *section_name); +size_t read_buffer(FILE *infp, uint8_t **bufp_out, size_t size_hint); +getdns_return_t wire_rrs2list(const uint8_t *buf, size_t buf_len, getdns_list **list_out); + + diff --git a/tools/dnssec/dns-net2wire.c b/tools/dnssec/dns-net2wire.c index c193139..0e5003d 100644 --- a/tools/dnssec/dns-net2wire.c +++ b/tools/dnssec/dns-net2wire.c @@ -20,6 +20,7 @@ #include #include #include +#include "common.h" #define DEBUG_SCHED(...) @@ -1140,6 +1141,53 @@ next: ; return r; } +static void +dump_ta(FILE *fp) +{ + getdns_list *trust_anchors = NULL; + getdns_return_t r = 0; + if ((r = getdns_context_get_dnssec_trust_anchors( + context, &trust_anchors)) + || trust_anchors == NULL) { + fprintf(stderr, "Unable to get trust anchors: %s\n", + getdns_get_errorstr_by_id(r)); + return; + } + + size_t list_len = 0; + r = getdns_list_get_length(trust_anchors, &list_len); + if (r) { + fprintf(stderr, "unable to get length of trust_anchors\n"); + return; + } + + for (size_t i = 0; i < list_len; i++) { + getdns_dict *rr = NULL; + uint8_t *res = NULL; + size_t res_len; + r = getdns_list_get_dict(trust_anchors, i , &rr); + if (r) { + fprintf(stderr, "unable to get rr from entry " + "%d: %d\n", i, r); + return; + } + + r = getdns_rr_dict2wire(rr, &res, &res_len); + if (r) { + fprintf(stderr, + "unable to convert entry %d " + "to wire format: %d\n", i, r); + return; + } + + if (fwrite(res, 1, res_len, fp) != res_len) + fprintf(stderr, "Could not write trust anchor to file\n"); + } +} + + +FILE *support_out_fp = NULL; + getdns_return_t do_the_call(void) { getdns_return_t r; @@ -1223,18 +1271,28 @@ getdns_return_t do_the_call(void) , "Could not print response\n"); } } -#if 1 - FILE *support_out_fp = fopen("treeout_support", "w"); - assert(support_out_fp); getdns_list *validation_chain = NULL; if ((r = getdns_dict_get_list( - response, "validation_chain", &validation_chain))) - assert(!r && "get_list validation_chain"); - if (response && support_out_fp) { - ; //fwrite(support_out_fp, fixme, fixme_len); + response, "validation_chain", &validation_chain))) { + fprintf(stderr, "get_list validation_chain: %d (%s)\n", + r, getdns_get_errorstr_by_id(r)); + exit(1); } - fclose(support_out_fp); -#endif + + if (dump_tree(support_out_fp, response, "validation_chain", NULL)) + fprintf(stderr, "Could not dump %s to file\n", "validation_chain"); + + FILE *tree_out_fp = fopen("treeout", "w"); + assert(tree_out_fp); + if (dump_tree(tree_out_fp, response, "replies_tree", "answer")) + fprintf(stderr, "Could not dump %s to file\n", "replies_tree"); + if (fclose(tree_out_fp)) assert(0); + + FILE *tree_out_ta = fopen("treeout_ta", "w"); + assert(tree_out_ta); + dump_ta(tree_out_ta); + if (fclose(tree_out_ta)) assert(0); + getdns_dict_get_int(response, "status", &status); fprintf(stdout, "Response code was: GOOD. Status was: %s\n", getdns_get_errorstr_by_id(status)); @@ -1327,6 +1385,9 @@ main(int argc, char **argv) } else fp = stdin; + support_out_fp = fopen("treeout_support", "w"); + assert(support_out_fp); + /* Make the call */ if (interactive) { getdns_eventloop_event read_line_ev = { @@ -1346,6 +1407,7 @@ main(int argc, char **argv) getdns_context_run(context); /* Clean up */ + fclose(support_out_fp); getdns_dict_destroy(extensions); done_destroy_context: getdns_context_destroy(context); diff --git a/tools/dnssec/dns-wire2text.c b/tools/dnssec/dns-wire2text.c index 896fc6e..1443bc4 100644 --- a/tools/dnssec/dns-wire2text.c +++ b/tools/dnssec/dns-wire2text.c @@ -9,6 +9,7 @@ #include #include #include +#include "common.h" #undef DEBUG @@ -34,63 +35,6 @@ hd(const char *buf, size_t buf_len) #define hd(a,b) #endif -#define CHUNKSIZE 4096 - -static size_t -read_inbuf(FILE *infp, uint8_t **bufp_out) -{ - size_t nread = 0; - uint8_t *wirebuf = malloc(CHUNKSIZE); - int chunks = 1; - - if (wirebuf == NULL) - goto out; - - while (1) - { - size_t n = fread(wirebuf + nread, 1, CHUNKSIZE, infp); - nread += n; - if (n < CHUNKSIZE) - break; /* Done. */ - - wirebuf = realloc(wirebuf, ++chunks * CHUNKSIZE); - if (wirebuf == NULL) - break; - } - - out: - if (bufp_out != NULL) - *bufp_out = wirebuf; - return nread; -} - -static getdns_return_t -wire_rrs2list(const uint8_t *buf, size_t buf_len, getdns_list **list_out) -{ - getdns_return_t r = GETDNS_RETURN_GOOD; - getdns_list *list = getdns_list_create(); - getdns_dict *dict = NULL; - size_t rr_count = 0; - - if (list == NULL) - return GETDNS_RETURN_MEMORY_ERROR; - while (buf_len > 0) - { - r = getdns_wire2rr_dict_scan(&buf, &buf_len, &dict); - if (r) - break; - r = getdns_list_set_dict(list, rr_count, dict); - getdns_dict_destroy(dict); /* The list has a copy. */ - if (r) - break; - rr_count++; - } - - if (list_out) - *list_out = list; - return r; -} - int main(int argc, char *argv[]) { @@ -108,10 +52,10 @@ main(int argc, char *argv[]) /* Read RRs in wire format. */ uint8_t *inbuf = NULL; - size_t inbuf_len = read_inbuf(infp, &inbuf); + size_t inbuf_len = read_buffer(infp, &inbuf, 0); if (inbuf == NULL) { - perror("read_inbuf"); + perror("read_buffer"); return errno; } if (infp != stdin) diff --git a/tools/dnssec/net2wire.c b/tools/dnssec/net2wire.c index f97483f..bbf1269 100644 --- a/tools/dnssec/net2wire.c +++ b/tools/dnssec/net2wire.c @@ -22,108 +22,7 @@ #include #include -static int debug = 1; - -static getdns_return_t -dump_reply(FILE *fp, getdns_dict *reply, const char *section_name) -{ - getdns_list *section = NULL; - size_t section_len = -1; - getdns_return_t r; - uint8_t res_buf[4096], *res = NULL; - size_t res_len = sizeof(res_buf); - - r = getdns_dict_get_list(reply, section_name, §ion); - if (r) { - fprintf(stderr, - "unable to get section \"%s\" from reply\n", - section_name); - return r; - } - - r = getdns_list_get_length(section, §ion_len); - if (r) { - fprintf(stderr, "unable to get length of section\n"); - return r; - } - - for (size_t j = 0; j < section_len; j++) { - getdns_dict *rr = NULL; - - r = getdns_list_get_dict(section, j , &rr); - if (r) { - fprintf(stderr, "unable to get rr from entry " - "%d: %d\n", j, r); - return r; - } - - r = getdns_rr_dict2wire(rr, &res, &res_len); - if (r) { - fprintf(stderr, - "unable to convert entry %d " - "to wire format: %d\n", j, r); - return r; - } - - if (0 && debug) { - char *s = getdns_pretty_print_dict(rr); - puts(s); - free(s); - } - - if (fwrite(res, 1, res_len, fp) != res_len) { - fprintf(stderr, - "unable to write buffer to file: %s\n", - strerror(errno)); - return -errno; - } - - free(res); - } - - return 0; -} - -int -dump_tree(FILE *fp, const getdns_dict *response, const char *tree_name) -{ - getdns_return_t r; - getdns_list *tree = NULL; - size_t n_replies = -1; - - r = getdns_dict_get_list(response, tree_name, &tree); - if (r) { - fprintf(stderr, "unable to get tree %s\n", tree_name); - return r; - } - - r = getdns_list_get_length(tree, &n_replies); - if (r) { - fprintf(stderr, "unable to get number of replies\n"); - return r; - } - - for (size_t i = 0; i < n_replies; i++) { - getdns_dict *reply = NULL; - - r = getdns_list_get_dict(tree, i, &reply); - if (r) { - fprintf(stderr, "unable to get reply %d from tree\n", i); - return r; - } - - if (debug) { - char *s = getdns_pretty_print_dict(reply); - printf("Pretty-printing reply #%d:%s\n", i, s); - free(s); - } - - dump_reply(fp, reply, "answer"); - } - - return 0; -} - +#include "common.h" /* Set up the callback function, which will also do the processing of the results */ void callback(getdns_context *context, @@ -171,7 +70,7 @@ void callback(getdns_context *context, static char *TREES[] = {"replies_tree", "validation_chain"}; static const int TREES_LEN = 2; for (int i = 0; i < TREES_LEN; i++) { - if (dump_tree(treeoutfp, response, TREES[i])) + if (dump_tree(treeoutfp, response, TREES[i], "answer")) fprintf(stderr, "Could not dump %s to file\n", TREES[i]); else if ((r = getdns_dict_get_bindata(response, "/just_address_answers/0/address_data", &address_data))) diff --git a/tools/dnssec/validatechain.c b/tools/dnssec/validatechain.c new file mode 100644 index 0000000..1ecf5f7 --- /dev/null +++ b/tools/dnssec/validatechain.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +static getdns_return_t +validate(const uint8_t *records_buf, size_t records_len, + const uint8_t *support_buf, size_t support_len, + const uint8_t *trust_anchors_buf, size_t trust_anchors_len, + time_t validation_time, uint32_t skew) +{ + getdns_return_t r = GETDNS_DNSSEC_INDETERMINATE; + + getdns_list *to_validate = NULL; + if ((r = wire_rrs2list(records_buf, records_len, &to_validate))) + goto out; + + getdns_list *support_records = NULL; + if ((r = wire_rrs2list(support_buf, support_len, &support_records))) + goto out; + + getdns_list *trust_anchors = NULL; + if ((r = wire_rrs2list(trust_anchors_buf, trust_anchors_len, &trust_anchors))) + goto out; + + /* + to_validate: The DS and an RRSIG for that DS. + + support_records: DS's and DNSKEY's with accompanying RRSIG's. + + trust_anchors: DNSKEY's (or DS?). + */ + + r = getdns_validate_dnssec2(to_validate, + support_records, + trust_anchors, + validation_time, + skew); + +out: + if (to_validate) + getdns_list_destroy(to_validate); + if (support_records) + getdns_list_destroy(support_records); + if (trust_anchors) + getdns_list_destroy(trust_anchors); + + return r; +} + +static void +usage() +{ + fprintf(stderr, "usage: dns-validatechain to-validate-file " + "support-records-file [trust-anchors-file]\n"); +} + +static int +read_file(const char *filename, uint8_t **out, size_t *out_len) +{ + FILE *fp = fopen(filename, "r"); + + if (fp == NULL) + return errno; + *out_len = read_buffer(fp, out, *out_len); + if (fclose(fp)) assert(0); + + return 0; +} + +int +main(int argc, char *argv[]) +{ + if (argc < 4) { + usage(); + exit(1); + } + + uint8_t *tv = NULL; + size_t tv_len = 8 * 1024; + if (read_file(argv[1], &tv, &tv_len)) + assert(0); + + uint8_t *sup = NULL; + size_t sup_len = 64 * 1024; + if (read_file(argv[2], &sup, &sup_len)) + assert(0); + + uint8_t *ta = NULL; + size_t ta_len = 4 * 1024; + if (read_file(argv[3], &ta, &ta_len)) + assert(0); + + getdns_return_t r = 0; + if ((r = validate(tv, tv_len, + sup, sup_len, + ta, ta_len, + time(NULL), 5)) != GETDNS_DNSSEC_SECURE) { + fprintf(stderr, "validation failed: %d (%s)\n", + r, getdns_get_errorstr_by_id(r)); + return r; + } + + return 0; +} -- cgit v1.1