mirror of https://github.com/getdnsapi/getdns.git
Provide access to the pinsets during the TLS verification callback
We do this by associating a getdns_upstream object with the SSL object handled by that upstream. This allows us to collapse the verification callback code to a single function. Note that if we've agreed that fallback is ok, we are now willing to accept *any* cert verification error, not just HOSTNAME_MISMATCH. This is fine, because the alternative is falling back to cleartext, which would be worse. We also always set SSL_VERIFY_PEER, since we might as well try to do so; we'll drop the verification error ourselves if we know we're OK with falling back.
This commit is contained in:
parent
614d317fd8
commit
d09675539e
|
@ -149,6 +149,7 @@ typedef struct getdns_upstream {
|
||||||
unsigned has_prev_client_cookie : 1;
|
unsigned has_prev_client_cookie : 1;
|
||||||
unsigned has_server_cookie : 1;
|
unsigned has_server_cookie : 1;
|
||||||
unsigned server_cookie_len : 5;
|
unsigned server_cookie_len : 5;
|
||||||
|
unsigned tls_fallback_ok : 1;
|
||||||
|
|
||||||
/* TSIG */
|
/* TSIG */
|
||||||
uint8_t tsig_dname[256];
|
uint8_t tsig_dname[256];
|
||||||
|
|
|
@ -308,5 +308,54 @@ _getdns_get_pubkey_pinset_list(getdns_context *ctx,
|
||||||
return r;
|
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.
|
||||||
|
|
||||||
|
see doc/HOWTO/proxy_certificates.txt as an example
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
_get_ssl_getdns_upstream_idx()
|
||||||
|
{
|
||||||
|
static volatile int idx = -1;
|
||||||
|
if (idx < 0) {
|
||||||
|
CRYPTO_w_lock(CRYPTO_LOCK_X509_STORE);
|
||||||
|
if (idx < 0)
|
||||||
|
idx = SSL_get_ex_new_index(0, "associated getdns upstream",
|
||||||
|
NULL,NULL,NULL);
|
||||||
|
CRYPTO_w_unlock(CRYPTO_LOCK_X509_STORE);
|
||||||
|
}
|
||||||
|
return idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
getdns_upstream*
|
||||||
|
_getdns_upstream_from_x509_store(X509_STORE_CTX *store)
|
||||||
|
{
|
||||||
|
int uidx = _get_ssl_getdns_upstream_idx();
|
||||||
|
int sslidx = SSL_get_ex_data_X509_STORE_CTX_idx();
|
||||||
|
const SSL *ssl;
|
||||||
|
|
||||||
|
/* all *_get_ex_data() should return NULL on failure anyway */
|
||||||
|
ssl = X509_STORE_CTX_get_ex_data(store, sslidx);
|
||||||
|
if (ssl)
|
||||||
|
return (getdns_upstream*) SSL_get_ex_data(ssl, uidx);
|
||||||
|
else
|
||||||
|
return NULL;
|
||||||
|
/* TODO: if we want more details about errors somehow, we
|
||||||
|
* might call ERR_get_error (see CRYPTO_set_ex_data(3ssl))*/
|
||||||
|
}
|
||||||
|
|
||||||
|
getdns_return_t
|
||||||
|
_getdns_associate_upstream_with_SSL(SSL *ssl,
|
||||||
|
getdns_upstream *upstream)
|
||||||
|
{
|
||||||
|
int uidx = _get_ssl_getdns_upstream_idx();
|
||||||
|
if (SSL_set_ex_data(ssl, uidx, upstream))
|
||||||
|
return GETDNS_RETURN_GOOD;
|
||||||
|
else
|
||||||
|
return GETDNS_RETURN_GENERIC_ERROR;
|
||||||
|
/* TODO: if we want more details about errors somehow, we
|
||||||
|
* might call ERR_get_error (see CRYPTO_set_ex_data(3ssl))*/
|
||||||
|
}
|
||||||
|
|
||||||
/* pubkey-pinning.c */
|
/* pubkey-pinning.c */
|
||||||
|
|
|
@ -49,5 +49,17 @@ _getdns_get_pubkey_pinset_list(getdns_context *ctx,
|
||||||
getdns_list **pinset_list);
|
getdns_list **pinset_list);
|
||||||
|
|
||||||
|
|
||||||
|
/* internal functions for associating X.509 verification processes in
|
||||||
|
* OpenSSL with getdns_upstream objects. */
|
||||||
|
|
||||||
|
getdns_upstream*
|
||||||
|
_getdns_upstream_from_x509_store(X509_STORE_CTX *store);
|
||||||
|
|
||||||
|
|
||||||
|
getdns_return_t
|
||||||
|
_getdns_associate_upstream_with_SSL(SSL *ssl,
|
||||||
|
getdns_upstream *upstream);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
/* pubkey-pinning.h */
|
/* pubkey-pinning.h */
|
||||||
|
|
55
src/stub.c
55
src/stub.c
|
@ -47,6 +47,7 @@
|
||||||
#include "context.h"
|
#include "context.h"
|
||||||
#include "util-internal.h"
|
#include "util-internal.h"
|
||||||
#include "general.h"
|
#include "general.h"
|
||||||
|
#include "pubkey-pinning.h"
|
||||||
|
|
||||||
#define STUB_OUT_OF_OPTIONS -5 /* upstream options exceeded MAXIMUM_UPSTREAM_OPTION_SPACE */
|
#define STUB_OUT_OF_OPTIONS -5 /* upstream options exceeded MAXIMUM_UPSTREAM_OPTION_SPACE */
|
||||||
#define STUB_TLS_SETUP_ERROR -4
|
#define STUB_TLS_SETUP_ERROR -4
|
||||||
|
@ -841,40 +842,20 @@ tls_auth_status_ok(getdns_upstream *upstream, getdns_network_req *netreq) {
|
||||||
int
|
int
|
||||||
tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
|
||||||
{
|
{
|
||||||
#if defined(STUB_DEBUG) && STUB_DEBUG
|
|
||||||
int err;
|
int err;
|
||||||
const char * err_str;
|
getdns_upstream *upstream;
|
||||||
|
|
||||||
err = X509_STORE_CTX_get_error(ctx);
|
err = X509_STORE_CTX_get_error(ctx);
|
||||||
err_str = X509_verify_cert_error_string(err);
|
upstream = _getdns_upstream_from_x509_store(ctx);
|
||||||
DEBUG_STUB("--- %s, VERIFY RESULT: %s\n", __FUNCTION__, err_str);
|
DEBUG_STUB("--- %s, VERIFY RESULT: (%d) \"%s\"\n", __FUNCTION__,
|
||||||
#endif
|
err, X509_verify_cert_error_string(err));
|
||||||
/*Always proceed without changing result*/
|
|
||||||
return preverify_ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
tls_verify_callback_with_fallback(int preverify_ok, X509_STORE_CTX *ctx)
|
|
||||||
{
|
|
||||||
#ifdef X509_V_ERR_HOSTNAME_MISMATCH
|
#ifdef X509_V_ERR_HOSTNAME_MISMATCH
|
||||||
int err;
|
|
||||||
# if defined(STUB_DEBUG) && STUB_DEBUG
|
|
||||||
const char * err_str;
|
|
||||||
# endif
|
|
||||||
|
|
||||||
err = X509_STORE_CTX_get_error(ctx);
|
|
||||||
# if defined(STUB_DEBUG) && STUB_DEBUG
|
|
||||||
err_str = X509_verify_cert_error_string(err);
|
|
||||||
DEBUG_STUB("--- %s, VERIFY RESULT: (%d) \"%s\"\n", __FUNCTION__, err, err_str);
|
|
||||||
# endif
|
|
||||||
/*Proceed if error is hostname mismatch*/
|
/*Proceed if error is hostname mismatch*/
|
||||||
if (err == X509_V_ERR_HOSTNAME_MISMATCH) {
|
if (upstream && upstream->tls_fallback_ok && err == X509_V_ERR_HOSTNAME_MISMATCH)
|
||||||
DEBUG_STUB("--- %s, PROCEEDING WITHOUT HOSTNAME VALIDATION!!\n", __FUNCTION__);
|
DEBUG_STUB("--- %s, PROCEEDING WITHOUT HOSTNAME VALIDATION!!\n", __FUNCTION__);
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
#endif
|
#endif
|
||||||
return preverify_ok;
|
return (upstream && upstream->tls_fallback_ok) ? 1 : preverify_ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SSL*
|
static SSL*
|
||||||
|
@ -892,11 +873,17 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
|
||||||
SSL_free(ssl);
|
SSL_free(ssl);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
/* make sure we'll be able to find the context again when we need it */
|
||||||
|
if (_getdns_associate_upstream_with_SSL(ssl, upstream) != GETDNS_RETURN_GOOD) {
|
||||||
|
SSL_free(ssl);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
/* NOTE: this code will fallback on a given upstream, without trying
|
/* NOTE: this code will fallback on a given upstream, without trying
|
||||||
authentication on other upstreams first. This is non-optimal and but avoids
|
authentication on other upstreams first. This is non-optimal and but avoids
|
||||||
multiple TLS handshakes before getting a usable connection. */
|
multiple TLS handshakes before getting a usable connection. */
|
||||||
|
|
||||||
|
upstream->tls_fallback_ok = 0;
|
||||||
/* If we have a hostname, always use it */
|
/* If we have a hostname, always use it */
|
||||||
if (upstream->tls_auth_name[0] != '\0') {
|
if (upstream->tls_auth_name[0] != '\0') {
|
||||||
/*Request certificate for the auth_name*/
|
/*Request certificate for the auth_name*/
|
||||||
|
@ -920,12 +907,8 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
/* Allow fallback to opportunistic if settings permit it*/
|
/* Allow fallback to opportunistic if settings permit it*/
|
||||||
if (dnsreq->netreqs[0]->tls_auth_min == GETDNS_AUTHENTICATION_HOSTNAME)
|
if (dnsreq->netreqs[0]->tls_auth_min != GETDNS_AUTHENTICATION_HOSTNAME)
|
||||||
SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_callback);
|
upstream->tls_fallback_ok = 1;
|
||||||
else {
|
|
||||||
SSL_set_verify(ssl, SSL_VERIFY_NONE, tls_verify_callback_with_fallback);
|
|
||||||
SSL_set_cipher_list(ssl, "DEFAULT");
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
/* Lack of host name is OK unless only authenticated TLS is specified*/
|
/* Lack of host name is OK unless only authenticated TLS is specified*/
|
||||||
if (dnsreq->netreqs[0]->tls_auth_min == GETDNS_AUTHENTICATION_HOSTNAME) {
|
if (dnsreq->netreqs[0]->tls_auth_min == GETDNS_AUTHENTICATION_HOSTNAME) {
|
||||||
|
@ -937,10 +920,12 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
|
||||||
/* no hostname verification, so we will make opportunistic connections */
|
/* no hostname verification, so we will make opportunistic connections */
|
||||||
DEBUG_STUB("--- %s, PROCEEDING WITHOUT HOSTNAME VALIDATION!!\n", __FUNCTION__);
|
DEBUG_STUB("--- %s, PROCEEDING WITHOUT HOSTNAME VALIDATION!!\n", __FUNCTION__);
|
||||||
upstream->tls_auth_failed = 1;
|
upstream->tls_auth_failed = 1;
|
||||||
SSL_set_verify(ssl, SSL_VERIFY_NONE, tls_verify_callback_with_fallback);
|
upstream->tls_fallback_ok = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (upstream->tls_fallback_ok)
|
||||||
SSL_set_cipher_list(ssl, "DEFAULT");
|
SSL_set_cipher_list(ssl, "DEFAULT");
|
||||||
}
|
SSL_set_verify(ssl, SSL_VERIFY_PEER, tls_verify_callback);
|
||||||
}
|
|
||||||
|
|
||||||
SSL_set_connect_state(ssl);
|
SSL_set_connect_state(ssl);
|
||||||
(void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
(void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
|
||||||
|
|
Loading…
Reference in New Issue