mirror of https://github.com/getdnsapi/getdns.git
Check that the pinset matches if it is configured
if the upstream is configured to allow fallback, this will not be a fatal error, but it will still be checked. Future work: * verify any certs higher in the chain than the end-entity cert * deal with raw public keys * in the fallback case, report to the user whether the pinset match failed
This commit is contained in:
parent
d09675539e
commit
a9eb9ccca9
|
@ -45,10 +45,13 @@
|
|||
* 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 <openssl/evp.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/x509.h>
|
||||
#include <string.h>
|
||||
#include "context.h"
|
||||
|
||||
|
@ -358,4 +361,84 @@ _getdns_associate_upstream_with_SSL(SSL *ssl,
|
|||
* might call ERR_get_error (see CRYPTO_set_ex_data(3ssl))*/
|
||||
}
|
||||
|
||||
getdns_return_t
|
||||
_getdns_verify_pinset_match(const sha256_pin_t *pinset,
|
||||
X509_STORE_CTX *store)
|
||||
{
|
||||
getdns_return_t ret = GETDNS_RETURN_GENERIC_ERROR;
|
||||
X509 *x;
|
||||
int i, len;
|
||||
unsigned char raw[4096];
|
||||
unsigned char *next = raw;
|
||||
unsigned char buf[sizeof(pinset->pin)];
|
||||
const sha256_pin_t *p;
|
||||
|
||||
if (pinset == NULL || store == NULL)
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
|
||||
/* start at the base of the chain (the end-entity cert) and
|
||||
* make sure that some valid element of the chain does match
|
||||
* the pinset. */
|
||||
|
||||
/* Testing with OpenSSL 1.0.1e-1 on debian indicates that
|
||||
* store->untrusted holds the chain offered by the server in
|
||||
* the order that the server offers it. If the server offers
|
||||
* bogus certificates (that is, matching and valid certs that
|
||||
* belong to private keys that the server does not control),
|
||||
* the the verification will succeed (including this pinset
|
||||
* check), but the handshake will fail outside of this
|
||||
* verification. */
|
||||
|
||||
/* TODO: how do we handle raw public keys? */
|
||||
|
||||
for (i = 0; i < sk_X509_num(store->untrusted); i++) {
|
||||
if (i > 0) {
|
||||
/* TODO: how do we ensure that the certificates in
|
||||
* each stage appropriately sign the previous one?
|
||||
* for now, to be safe, we only examine the end-entity
|
||||
* cert: */
|
||||
return GETDNS_RETURN_GENERIC_ERROR;
|
||||
}
|
||||
|
||||
x = sk_X509_value(store->untrusted, i);
|
||||
if (x->cert_info == NULL)
|
||||
continue;
|
||||
#if defined(STUB_DEBUG) && STUB_DEBUG
|
||||
DEBUG_STUB("--- %s: name of cert %d:\n", __FUNCTION__, i);
|
||||
if (x->cert_info->subject != NULL)
|
||||
X509_NAME_print_ex_fp(stderr, x->cert_info->subject, 4, XN_FLAG_ONELINE);
|
||||
fprintf(stderr, "\n");
|
||||
#endif
|
||||
if (x->cert_info->key == NULL)
|
||||
continue;
|
||||
|
||||
/* digest the cert with sha256 */
|
||||
len = i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), NULL);
|
||||
if (len > sizeof(raw)) {
|
||||
DEBUG_STUB("--- %s: pubkey %d is larger than %ld octets\n",
|
||||
__FUNCTION__, i, sizeof(raw));
|
||||
continue;
|
||||
}
|
||||
i2d_X509_PUBKEY(X509_get_X509_PUBKEY(x), &next);
|
||||
if (next - raw != len) {
|
||||
DEBUG_STUB("--- %s: pubkey %d claimed it needed %d octets, really needed %ld\n",
|
||||
__FUNCTION__, i, len, next - raw);
|
||||
continue;
|
||||
}
|
||||
SHA256(raw, len, buf);
|
||||
|
||||
/* compare it */
|
||||
for (p = pinset; p; p = p->next)
|
||||
if (0 == memcmp(buf, p->pin, sizeof(p->pin))) {
|
||||
DEBUG_STUB("--- %s: pubkey %d matched pin %p (%ld)!\n",
|
||||
__FUNCTION__, i, p, sizeof(p->pin));
|
||||
return GETDNS_RETURN_GOOD;
|
||||
} else
|
||||
DEBUG_STUB("--- %s: pubkey %d did not match pin %p!\n",
|
||||
__FUNCTION__, i, p);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* pubkey-pinning.c */
|
||||
|
|
|
@ -60,6 +60,9 @@ getdns_return_t
|
|||
_getdns_associate_upstream_with_SSL(SSL *ssl,
|
||||
getdns_upstream *upstream);
|
||||
|
||||
getdns_return_t
|
||||
_getdns_verify_pinset_match(const sha256_pin_t *pinset,
|
||||
X509_STORE_CTX *store);
|
||||
|
||||
#endif
|
||||
/* pubkey-pinning.h */
|
||||
|
|
10
src/stub.c
10
src/stub.c
|
@ -844,6 +844,7 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
|||
{
|
||||
int err;
|
||||
getdns_upstream *upstream;
|
||||
getdns_return_t pinset_ret = GETDNS_RETURN_GOOD;
|
||||
|
||||
err = X509_STORE_CTX_get_error(ctx);
|
||||
upstream = _getdns_upstream_from_x509_store(ctx);
|
||||
|
@ -855,6 +856,15 @@ tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
|||
if (upstream && upstream->tls_fallback_ok && err == X509_V_ERR_HOSTNAME_MISMATCH)
|
||||
DEBUG_STUB("--- %s, PROCEEDING WITHOUT HOSTNAME VALIDATION!!\n", __FUNCTION__);
|
||||
#endif
|
||||
if (upstream && upstream->tls_pubkey_pinset)
|
||||
pinset_ret = _getdns_verify_pinset_match(upstream->tls_pubkey_pinset, ctx);
|
||||
|
||||
if (pinset_ret != GETDNS_RETURN_GOOD) {
|
||||
DEBUG_STUB("--- %s, PINSET VALIDATION FAILURE!!\n", __FUNCTION__);
|
||||
preverify_ok = 0;
|
||||
if (upstream->tls_fallback_ok)
|
||||
DEBUG_STUB("--- %s, PROCEEDING WITHOUT PINSET VALIDATION!!\n", __FUNCTION__);
|
||||
}
|
||||
return (upstream && upstream->tls_fallback_ok) ? 1 : preverify_ok;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue