Extract non-OpenSSL specific code from pubkey-pinning.c, and move it back to common source.

OpenSSL-specific items are in pubkey-pinning-internal.c.
This commit is contained in:
Jim Hague 2018-12-06 14:09:30 +00:00
parent e73ab48687
commit 72d9b91a2e
6 changed files with 242 additions and 189 deletions

View File

@ -81,7 +81,7 @@ DEFAULT_EVENTLOOP_OBJ=@DEFAULT_EVENTLOOP@.lo
GETDNS_OBJ=const-info.lo convert.lo dict.lo dnssec.lo general.lo \
list.lo request-internal.lo platform.lo rr-dict.lo \
rr-iter.lo server.lo stub.lo sync.lo ub_loop.lo util-internal.lo \
mdns.lo
mdns.lo pubkey-pinning.lo
GLDNS_OBJ=keyraw.lo gbuffer.lo wire2str.lo parse.lo parseutil.lo rrdef.lo \
str2wire.lo
@ -95,7 +95,7 @@ COMPAT_OBJ=$(LIBOBJS:.o=.lo)
UTIL_OBJ=rbtree.lo lruhash.lo lookup3.lo locks.lo
JSMN_OBJ=jsmn.lo
TLS_OBJ=tls.lo pubkey-pinning.lo keyraw-internal.lo val_secalgo.lo anchor-internal.lo
TLS_OBJ=tls.lo pubkey-pinning-internal.lo keyraw-internal.lo val_secalgo.lo anchor-internal.lo
YXML_OBJ=yxml.lo
YAML_OBJ=convert_yaml_to_json.lo

View File

@ -40,26 +40,6 @@
** Interfaces from pubkey-pinning.h
**/
/* create and populate a pinset linked list from a getdns_list pinset */
getdns_return_t
_getdns_get_pubkey_pinset_from_list(const getdns_list *pinset_list,
struct mem_funcs *mf,
sha256_pin_t **pinset_out)
{
return GETDNS_RETURN_GENERIC_ERROR;
}
/* create a getdns_list version of the pinset */
getdns_return_t
_getdns_get_pubkey_pinset_list(getdns_context *ctx,
const sha256_pin_t *pinset_in,
getdns_list **pinset_list)
{
return GETDNS_RETURN_GENERIC_ERROR;
}
getdns_return_t
_getdns_associate_upstream_with_connection(_getdns_tls_connection *conn,
getdns_upstream *upstream)
@ -76,9 +56,3 @@ getdns_pubkey_pin_create_from_string(getdns_context* context, const char* str)
{
return GETDNS_RETURN_GENERIC_ERROR;
}
getdns_return_t
getdns_pubkey_pinset_sanity_check(const getdns_list* pinset, getdns_list* errorlist)
{
return GETDNS_RETURN_GENERIC_ERROR;
}

View File

View File

@ -148,167 +148,6 @@ 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) { \
if (errorlist) \
_getdns_list_append_const_bindata(errorlist, \
sizeof(e), e); \
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, pins = 0, i;
getdns_dict * pin;
getdns_bindata * data;
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;
}
getdns_return_t
_getdns_get_pubkey_pinset_from_list(const getdns_list *pinset_list,
struct mem_funcs *mf,
sha256_pin_t **pinset_out)
{
getdns_return_t r;
size_t pins, i;
sha256_pin_t *out = NULL, *onext = NULL;
getdns_dict * pin;
getdns_bindata * data = NULL;
if (r = getdns_list_get_length(pinset_list, &pins), r)
return r;
for (i = 0; i < pins; i++)
{
if (r = getdns_list_get_dict(pinset_list, i, &pin), r)
goto fail;
/* does the pin have the right digest type? */
if (r = getdns_dict_get_bindata(pin, "digest", &data), r)
goto fail;
if (data->size != sha256.size ||
memcmp(data->data, sha256.data, sha256.size)) {
r = GETDNS_RETURN_INVALID_PARAMETER;
goto fail;
}
/* if it does, is the value the right length? */
if (r = getdns_dict_get_bindata(pin, "value", &data), r)
goto fail;
if (data->size != SHA256_DIGEST_LENGTH) {
r = GETDNS_RETURN_INVALID_PARAMETER;
goto fail;
}
/* make a new pin */
onext = GETDNS_MALLOC(*mf, sha256_pin_t);
if (onext == NULL) {
r = GETDNS_RETURN_MEMORY_ERROR;
goto fail;
}
onext->next = out;
memcpy(onext->pin, data->data, SHA256_DIGEST_LENGTH);
out = onext;
}
*pinset_out = out;
return GETDNS_RETURN_GOOD;
fail:
while (out) {
onext = out->next;
GETDNS_FREE(*mf, out);
out = onext;
}
return r;
}
getdns_return_t
_getdns_get_pubkey_pinset_list(getdns_context *ctx,
const sha256_pin_t *pinset_in,
getdns_list **pinset_list)
{
getdns_list *out = getdns_list_create_with_context(ctx);
getdns_return_t r;
uint8_t buf[SHA256_DIGEST_LENGTH];
getdns_bindata value = { .size = SHA256_DIGEST_LENGTH, .data = buf };
getdns_dict *pin = NULL;
if (out == NULL)
return GETDNS_RETURN_MEMORY_ERROR;
while (pinset_in) {
pin = getdns_dict_create_with_context(ctx);
if (pin == NULL) {
r = GETDNS_RETURN_MEMORY_ERROR;
goto fail;
}
if (r = getdns_dict_set_bindata(pin, "digest", &sha256), r)
goto fail;
memcpy(buf, pinset_in->pin, sizeof(buf));
if (r = getdns_dict_set_bindata(pin, "value", &value), r)
goto fail;
if (r = _getdns_list_append_this_dict(out, pin), r)
goto fail;
pin = NULL;
pinset_in = pinset_in->next;
}
*pinset_list = out;
return GETDNS_RETURN_GOOD;
fail:
getdns_dict_destroy(pin);
getdns_list_destroy(out);
return r;
}
/* this should only happen once ever in the life of the library. it's
used to associate a getdns_context_t with an SSL_CTX, to be able to
do custom verification.

231
src/pubkey-pinning.c Normal file
View File

@ -0,0 +1,231 @@
/**
*
* /brief functions for Public Key Pinning
*
*/
/*
* Copyright (c) 2015, Daniel Kahn Gillmor
* 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.
*/
/**
* getdns Public Key Pinning
*
* a public key pinset is a list of dicts. each dict should have a
* "digest" and a "value".
*
* "digest": a string indicating the type of digest. at the moment, we
* only support a "digest" of "sha256".
*
* "value": a binary representation of the digest provided.
*
* given a such a pinset, we should be able to validate a chain
* properly according to section 2.6 of RFC 7469.
*/
#include "config.h"
#include "debug.h"
#include <getdns/getdns.h>
#include <string.h>
#include "context.h"
#include "util-internal.h"
#include "pubkey-pinning-internal.h"
/* we only support sha256 at the moment. adding support for another
digest is more complex than just adding another entry here. in
particular, you'll probably need a match for a particular cert
against all supported algorithms. better to wait on doing that
until it is a better-understood problem (i.e. wait until hpkp is
updated and follow the guidance in rfc7469bis)
*/
static const getdns_bindata sha256 = {
.size = sizeof("sha256") - 1,
.data = (uint8_t*)"sha256"
};
/* 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) { \
if (errorlist) \
_getdns_list_append_const_bindata(errorlist, \
sizeof(e), e); \
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, pins = 0, i;
getdns_dict * pin;
getdns_bindata * data;
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;
}
getdns_return_t
_getdns_get_pubkey_pinset_from_list(const getdns_list *pinset_list,
struct mem_funcs *mf,
sha256_pin_t **pinset_out)
{
getdns_return_t r;
size_t pins, i;
sha256_pin_t *out = NULL, *onext = NULL;
getdns_dict * pin;
getdns_bindata * data = NULL;
if (r = getdns_list_get_length(pinset_list, &pins), r)
return r;
for (i = 0; i < pins; i++)
{
if (r = getdns_list_get_dict(pinset_list, i, &pin), r)
goto fail;
/* does the pin have the right digest type? */
if (r = getdns_dict_get_bindata(pin, "digest", &data), r)
goto fail;
if (data->size != sha256.size ||
memcmp(data->data, sha256.data, sha256.size)) {
r = GETDNS_RETURN_INVALID_PARAMETER;
goto fail;
}
/* if it does, is the value the right length? */
if (r = getdns_dict_get_bindata(pin, "value", &data), r)
goto fail;
if (data->size != SHA256_DIGEST_LENGTH) {
r = GETDNS_RETURN_INVALID_PARAMETER;
goto fail;
}
/* make a new pin */
onext = GETDNS_MALLOC(*mf, sha256_pin_t);
if (onext == NULL) {
r = GETDNS_RETURN_MEMORY_ERROR;
goto fail;
}
onext->next = out;
memcpy(onext->pin, data->data, SHA256_DIGEST_LENGTH);
out = onext;
}
*pinset_out = out;
return GETDNS_RETURN_GOOD;
fail:
while (out) {
onext = out->next;
GETDNS_FREE(*mf, out);
out = onext;
}
return r;
}
getdns_return_t
_getdns_get_pubkey_pinset_list(getdns_context *ctx,
const sha256_pin_t *pinset_in,
getdns_list **pinset_list)
{
getdns_list *out = getdns_list_create_with_context(ctx);
getdns_return_t r;
uint8_t buf[SHA256_DIGEST_LENGTH];
getdns_bindata value = { .size = SHA256_DIGEST_LENGTH, .data = buf };
getdns_dict *pin = NULL;
if (out == NULL)
return GETDNS_RETURN_MEMORY_ERROR;
while (pinset_in) {
pin = getdns_dict_create_with_context(ctx);
if (pin == NULL) {
r = GETDNS_RETURN_MEMORY_ERROR;
goto fail;
}
if (r = getdns_dict_set_bindata(pin, "digest", &sha256), r)
goto fail;
memcpy(buf, pinset_in->pin, sizeof(buf));
if (r = getdns_dict_set_bindata(pin, "value", &value), r)
goto fail;
if (r = _getdns_list_append_this_dict(out, pin), r)
goto fail;
pin = NULL;
pinset_in = pinset_in->next;
}
*pinset_list = out;
return GETDNS_RETURN_GOOD;
fail:
getdns_dict_destroy(pin);
getdns_list_destroy(out);
return r;
}
/* pubkey-pinning.c */

View File

@ -36,6 +36,15 @@
#include "tls.h"
/**
** Internal functions, implemented in pubkey-pinning-internal.c.
**/
getdns_dict* getdns_pubkey_pin_create_from_string(getdns_context* context, const char* str);
/**
** Public interface.
**/
/* create and populate a pinset linked list from a getdns_list pinset */
getdns_return_t
_getdns_get_pubkey_pinset_from_list(const getdns_list *pinset_list,