From 5e64f1262b95b2bc7e0941ac26bc6d1864fe73c6 Mon Sep 17 00:00:00 2001 From: Daniel Kahn Gillmor Date: Sun, 20 Dec 2015 13:03:21 -0500 Subject: [PATCH] add getdns_pubkey_pinset_sanity_check() --- src/getdns/getdns_extra.h.in | 20 ++++++++++ src/libgetdns.symbols | 1 + src/pubkey-pinning.c | 75 ++++++++++++++++++++++++++++++++++++ 3 files changed, 96 insertions(+) diff --git a/src/getdns/getdns_extra.h.in b/src/getdns/getdns_extra.h.in index 12161e10..dea756a6 100644 --- a/src/getdns/getdns_extra.h.in +++ b/src/getdns/getdns_extra.h.in @@ -378,6 +378,26 @@ getdns_dict* getdns_pubkey_pin_create_from_string( getdns_context* context, const char* str); + +/** + * Test whether a given pinset is reasonable, including: + * + * is it well-formed? + * are there at least two pins? + * are the digests used sane? + * + * @param pinset the set of public key pins to check for sanity. This + * should be a list of dicts. + * @return errorlist if not NULL, a list of human-readable strings is + * appended to errorlist. + * @return GETDNS_RETURN_GOOD if the pinset passes the sanity check. + */ +getdns_return_t getdns_pubkey_pinset_sanity_check( + const getdns_list* pinset, + getdns_list* errorlist); + + + /* WARNING! Function getdns_strerror is not in the API specification and * is likely to be removed from future versions of our implementation, to be * replaced by getdns_get_errorstr_by_id or something similar. diff --git a/src/libgetdns.symbols b/src/libgetdns.symbols index 9f7276cb..8b35ff37 100644 --- a/src/libgetdns.symbols +++ b/src/libgetdns.symbols @@ -115,6 +115,7 @@ getdns_pretty_snprint_list getdns_print_json_dict getdns_print_json_list getdns_pubkey_pin_create_from_string +getdns_pubkey_pinset_sanity_check getdns_root_trust_anchor getdns_rr_dict2str getdns_rr_dict2str_buf diff --git a/src/pubkey-pinning.c b/src/pubkey-pinning.c index b20f0817..fba25d7c 100644 --- a/src/pubkey-pinning.c +++ b/src/pubkey-pinning.c @@ -137,4 +137,79 @@ getdns_dict* getdns_pubkey_pin_create_from_string( return NULL; } + +/* Test whether a given pinset is reasonable, including: + + * is it well-formed? + * are there at least two pins? + * are the digests used sane? + + if errorlist is NULL, the sanity check just returns success or + failure. + + if errorlist is not NULL, we append human-readable strings to + report the errors. +*/ + +#define PKP_SC_ERR(e) { \ + err.size = sizeof(e); \ + err.data = (uint8_t*)e; \ + if (errorlist) \ + getdns_list_set_bindata(errorlist, \ + preverrs + errorcount, &err); \ + errorcount++; \ + } +#define PKP_SC_HARDERR(e, val) { \ + PKP_SC_ERR(e); return val; \ + } +getdns_return_t getdns_pubkey_pinset_sanity_check( + const getdns_list* pinset, + getdns_list* errorlist) +{ + size_t errorcount = 0, preverrs = 0, pins = 0, i; + getdns_bindata err; + getdns_dict * pin; + getdns_bindata * data; + + if (errorlist) + if (getdns_list_get_length(errorlist, &preverrs)) + return GETDNS_RETURN_INVALID_PARAMETER; + + if (getdns_list_get_length(pinset, &pins)) + PKP_SC_HARDERR("Can't get length of pinset", + GETDNS_RETURN_INVALID_PARAMETER); + if (pins < 2) + PKP_SC_ERR("This pinset has fewer than 2 pins"); + for (i = 0; i < pins; i++) + { + /* is it a dict? */ + if (getdns_list_get_dict(pinset, i, &pin)) { + PKP_SC_ERR("Could not retrieve a pin"); + } else { + /* does the pin have the right digest type? */ + if (getdns_dict_get_bindata(pin, "digest", &data)) { + PKP_SC_ERR("Pin has no 'digest' entry"); + } else { + if (data->size != sha256.size || + memcmp(data->data, sha256.data, sha256.size)) + PKP_SC_ERR("Pin has 'digest' other than sha256"); + } + /* if it does, is the value the right length? */ + if (getdns_dict_get_bindata(pin, "value", &data)) { + PKP_SC_ERR("Pin has no 'value' entry"); + } else { + if (data->size != SHA256_DIGEST_LENGTH) + PKP_SC_ERR("Pin has the wrong size 'value' (should be 32 octets for sha256)"); + } + + /* should we choke if it has some other key? for + * extensibility, we will not treat this as an + * error.*/ + } + } + + if (errorcount > 0) + return GETDNS_RETURN_GENERIC_ERROR; + return GETDNS_RETURN_GOOD; +} /* pubkey-pinning.c */