Wrap hostname/certificate verification.

This removes the last OpenSSL items from stub.c.
This commit is contained in:
Jim Hague 2018-11-20 14:53:31 +00:00
parent fb73bcb77e
commit 1b0a09a23f
3 changed files with 170 additions and 92 deletions

View File

@ -34,7 +34,9 @@
#include "config.h" #include "config.h"
#include <openssl/err.h> #include <openssl/err.h>
#include <openssl/conf.h>
#include <openssl/x509.h> #include <openssl/x509.h>
#include <openssl/x509v3.h>
#include <openssl/pem.h> #include <openssl/pem.h>
#include <openssl/bio.h> #include <openssl/bio.h>
#include <openssl/ssl.h> #include <openssl/ssl.h>
@ -42,8 +44,35 @@
#include <openssl/opensslv.h> #include <openssl/opensslv.h>
#include <openssl/crypto.h> #include <openssl/crypto.h>
#include "debug.h"
#include "context.h"
#include "tls.h" #include "tls.h"
static int _getdns_tls_verify_always_ok(int ok, X509_STORE_CTX *ctx)
{
# if defined(STUB_DEBUG) && STUB_DEBUG
char buf[8192];
X509 *cert;
int err;
int depth;
cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
if (cert)
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
else
strcpy(buf, "<unknown>");
DEBUG_STUB("DEBUG Cert verify: depth=%d verify=%d err=%d subject=%s errorstr=%s\n", depth, ok, err, buf, X509_verify_cert_error_string(err));
# else /* defined(STUB_DEBUG) && STUB_DEBUG */
(void)ok;
(void)ctx;
# endif /* #else defined(STUB_DEBUG) && STUB_DEBUG */
return 1;
}
static _getdns_tls_x509* _getdns_tls_x509_new(X509* cert) static _getdns_tls_x509* _getdns_tls_x509_new(X509* cert)
{ {
_getdns_tls_x509* res; _getdns_tls_x509* res;
@ -394,6 +423,76 @@ getdns_return_t _getdns_tls_connection_is_session_reused(_getdns_tls_connection*
return GETDNS_RETURN_TLS_CONNECTION_FRESH; return GETDNS_RETURN_TLS_CONNECTION_FRESH;
} }
getdns_return_t _getdns_tls_connection_setup_hostname_auth(_getdns_tls_connection* conn, const char* auth_name)
{
if (!conn || !conn->ssl || !auth_name)
return GETDNS_RETURN_INVALID_PARAMETER;
SSL_set_tlsext_host_name(conn->ssl, auth_name);
/* Set up native OpenSSL hostname verification */
X509_VERIFY_PARAM *param;
param = SSL_get0_param(conn->ssl);
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, auth_name, 0);
return GETDNS_RETURN_GOOD;
}
getdns_return_t _getdns_tls_connection_set_host_pinset(_getdns_tls_connection* conn, const char* auth_name, const sha256_pin_t* pinset)
{
if (!conn || !conn->ssl || !auth_name)
return GETDNS_RETURN_INVALID_PARAMETER;
int osr = SSL_dane_enable(conn->ssl, *auth_name ? auth_name : NULL);
(void) osr;
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_enable(\"%s\") -> %d\n"
, STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->tls_auth_name, osr);
SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, _getdns_tls_verify_always_ok);
const sha256_pin_t *pin_p;
size_t n_pins = 0;
for (pin_p = pinset; pin_p; pin_p = pin_p->next) {
osr = SSL_dane_tlsa_add(conn->ssl, 2, 1, 1,
(unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH);
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_tlsa_add() -> %d\n"
, STUB_DEBUG_SETUP_TLS, __FUNC__, osr);
if (osr > 0)
++n_pins;
osr = SSL_dane_tlsa_add(conn->ssl, 3, 1, 1,
(unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH);
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_tlsa_add() -> %d\n"
, STUB_DEBUG_SETUP_TLS, __FUNC__, osr);
if (osr > 0)
++n_pins;
}
return GETDNS_RETURN_GOOD;
}
getdns_return_t _getdns_tls_connection_verify(_getdns_tls_connection* conn, long* errnum, const char** errmsg)
{
if (!conn || !conn->ssl)
return GETDNS_RETURN_INVALID_PARAMETER;
long verify_result = SSL_get_verify_result(conn->ssl);
switch (verify_result) {
case X509_V_OK:
return GETDNS_RETURN_GOOD;
case X509_V_ERR_DANE_NO_MATCH:
if (errnum)
*errnum = 0;
if (errmsg)
*errmsg = "Pinset validation failure";
return GETDNS_RETURN_GENERIC_ERROR;
default:
if (errnum)
*errnum = verify_result;
if (errmsg)
*errmsg = X509_verify_cert_error_string(verify_result);
return GETDNS_RETURN_GENERIC_ERROR;
}
}
getdns_return_t _getdns_tls_connection_read(_getdns_tls_connection* conn, uint8_t* buf, size_t to_read, size_t* read) getdns_return_t _getdns_tls_connection_read(_getdns_tls_connection* conn, uint8_t* buf, size_t to_read, size_t* read)
{ {
int sread; int sread;

View File

@ -39,9 +39,6 @@
#define INTERCEPT_COM_DS 0 #define INTERCEPT_COM_DS 0
#include "debug.h" #include "debug.h"
#include <openssl/err.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include <fcntl.h> #include <fcntl.h>
#include "stub.h" #include "stub.h"
#include "gldns/gbuffer.h" #include "gldns/gbuffer.h"
@ -826,31 +823,6 @@ tls_requested(getdns_network_req *netreq)
1 : 0; 1 : 0;
} }
static int
_getdns_tls_verify_always_ok(int ok, X509_STORE_CTX *ctx)
{
# if defined(STUB_DEBUG) && STUB_DEBUG
char buf[8192];
X509 *cert;
int err;
int depth;
cert = X509_STORE_CTX_get_current_cert(ctx);
err = X509_STORE_CTX_get_error(ctx);
depth = X509_STORE_CTX_get_error_depth(ctx);
if (cert)
X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
else
strcpy(buf, "<unknown>");
DEBUG_STUB("DEBUG Cert verify: depth=%d verify=%d err=%d subject=%s errorstr=%s\n", depth, ok, err, buf, X509_verify_cert_error_string(err));
# else /* defined(STUB_DEBUG) && STUB_DEBUG */
(void)ok;
(void)ctx;
# endif /* #else defined(STUB_DEBUG) && STUB_DEBUG */
return 1;
}
static _getdns_tls_connection* static _getdns_tls_connection*
tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream) tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
{ {
@ -881,12 +853,7 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
/*Request certificate for the auth_name*/ /*Request certificate for the auth_name*/
DEBUG_STUB("%s %-35s: Hostname verification requested for: %s\n", DEBUG_STUB("%s %-35s: Hostname verification requested for: %s\n",
STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->tls_auth_name); STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->tls_auth_name);
SSL_set_tlsext_host_name(tls->ssl, upstream->tls_auth_name); _getdns_tls_connection_setup_hostname_auth(tls, upstream->tls_auth_name);
/* Set up native OpenSSL hostname verification */
X509_VERIFY_PARAM *param;
param = SSL_get0_param(tls->ssl);
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, upstream->tls_auth_name, 0);
/* 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_REQUIRED) if (dnsreq->netreqs[0]->tls_auth_min != GETDNS_AUTHENTICATION_REQUIRED)
upstream->tls_fallback_ok = 1; upstream->tls_fallback_ok = 1;
@ -926,32 +893,7 @@ tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
__FUNC__); __FUNC__);
} }
int osr; _getdns_tls_connection_set_host_pinset(tls, upstream->tls_auth_name, upstream->tls_pubkey_pinset);
# if defined(STUB_DEBUG) && STUB_DEBUG
osr =
# else
(void)
# endif
SSL_dane_enable(tls->ssl, *upstream->tls_auth_name ? upstream->tls_auth_name : NULL);
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_enable(\"%s\") -> %d\n"
, STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->tls_auth_name, osr);
SSL_set_verify(tls->ssl, SSL_VERIFY_PEER, _getdns_tls_verify_always_ok);
sha256_pin_t *pin_p;
size_t n_pins = 0;
for (pin_p = upstream->tls_pubkey_pinset; pin_p; pin_p = pin_p->next) {
osr = SSL_dane_tlsa_add(tls->ssl, 2, 1, 1,
(unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH);
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_tlsa_add() -> %d\n"
, STUB_DEBUG_SETUP_TLS, __FUNC__, osr);
if (osr > 0)
++n_pins;
osr = SSL_dane_tlsa_add(tls->ssl, 3, 1, 1,
(unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH);
DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_tlsa_add() -> %d\n"
, STUB_DEBUG_SETUP_TLS, __FUNC__, osr);
if (osr > 0)
++n_pins;
}
/* Session resumption. There are trade-offs here. Want to do it when /* Session resumption. There are trade-offs here. Want to do it when
possible only if we have the right type of connection. Note a change possible only if we have the right type of connection. Note a change
@ -1005,12 +947,9 @@ tls_do_handshake(getdns_upstream *upstream)
upstream->tls_auth_state = upstream->last_tls_auth_state; upstream->tls_auth_state = upstream->last_tls_auth_state;
else if (upstream->tls_pubkey_pinset || upstream->tls_auth_name[0]) { else if (upstream->tls_pubkey_pinset || upstream->tls_auth_name[0]) {
X509 *peer_cert = SSL_get_peer_certificate(upstream->tls_obj->ssl); _getdns_tls_x509* peer_cert = _getdns_tls_connection_get_peer_certificate(upstream->tls_obj);
long verify_result = SSL_get_verify_result(upstream->tls_obj->ssl);
upstream->tls_auth_state = peer_cert && verify_result == X509_V_OK if (!peer_cert) {
? GETDNS_AUTH_OK : GETDNS_AUTH_FAILED;
if (!peer_cert)
_getdns_upstream_log(upstream, _getdns_upstream_log(upstream,
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_UPSTREAM_STATS,
( upstream->tls_fallback_ok ( upstream->tls_fallback_ok
@ -1021,38 +960,42 @@ tls_do_handshake(getdns_upstream *upstream)
( upstream->tls_fallback_ok ( upstream->tls_fallback_ok
? "Tolerated because of Opportunistic profile" ? "Tolerated because of Opportunistic profile"
: "*Failure*" )); : "*Failure*" ));
upstream->tls_auth_state = GETDNS_AUTH_FAILED;
} else {
long verify_errno;
const char* verify_errmsg;
/* Since we don't have DANE validation yet, DANE validation if (!_getdns_tls_connection_verify(upstream->tls_obj, &verify_errno, &verify_errmsg)) {
* failures are always pinset validation failures upstream->tls_auth_state = GETDNS_AUTH_OK;
*/ if (verify_errno != 0) {
else if (verify_result == X509_V_ERR_DANE_NO_MATCH)
_getdns_upstream_log(upstream, _getdns_upstream_log(upstream,
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_UPSTREAM_STATS,
( upstream->tls_fallback_ok ( upstream->tls_fallback_ok
? GETDNS_LOG_INFO : GETDNS_LOG_ERR), ? GETDNS_LOG_INFO : GETDNS_LOG_ERR), "%-40s : Verify failed : TLS - %s - "
"%-40s : Verify failed : TLS - %s - "
"Pinset validation failure\n", upstream->addr_str,
( upstream->tls_fallback_ok
? "Tolerated because of Opportunistic profile"
: "*Failure*" ));
else if (verify_result != X509_V_OK)
_getdns_upstream_log(upstream,
GETDNS_LOG_UPSTREAM_STATS,
( upstream->tls_fallback_ok
? GETDNS_LOG_INFO : GETDNS_LOG_ERR),
"%-40s : Verify failed : TLS - %s - "
"(%d) \"%s\"\n", upstream->addr_str, "(%d) \"%s\"\n", upstream->addr_str,
( upstream->tls_fallback_ok ( upstream->tls_fallback_ok
? "Tolerated because of Opportunistic profile" ? "Tolerated because of Opportunistic profile"
: "*Failure*" ), verify_result, : "*Failure*" ),
X509_verify_cert_error_string(verify_result)); verify_errno, verify_errmsg);
else } else {
_getdns_upstream_log(upstream,
GETDNS_LOG_UPSTREAM_STATS,
( upstream->tls_fallback_ok
? GETDNS_LOG_INFO : GETDNS_LOG_ERR), "%-40s : Verify failed : TLS - %s - "
"%s\n", upstream->addr_str,
( upstream->tls_fallback_ok
? "Tolerated because of Opportunistic profile"
: "*Failure*" ),
verify_errno, verify_errmsg);
}
} else {
_getdns_upstream_log(upstream, _getdns_upstream_log(upstream,
GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG, GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_DEBUG,
"%-40s : Verify passed : TLS\n", "%-40s : Verify passed : TLS\n",
upstream->addr_str); upstream->addr_str);
}
X509_free(peer_cert); _getdns_tls_x509_free(peer_cert);
}
if (upstream->tls_auth_state == GETDNS_AUTH_FAILED if (upstream->tls_auth_state == GETDNS_AUTH_FAILED
&& !upstream->tls_fallback_ok) && !upstream->tls_fallback_ok)
return STUB_SETUP_ERROR; return STUB_SETUP_ERROR;

View File

@ -38,6 +38,10 @@
#include "tls-internal.h" #include "tls-internal.h"
/* Forward declare type. */
struct sha256_pin;
typedef struct sha256_pin sha256_pin_t;
/* Additional return codes required by TLS abstraction. Internal use only. */ /* Additional return codes required by TLS abstraction. Internal use only. */
#define GETDNS_RETURN_TLS_WANT_READ ((getdns_return_t) 420) #define GETDNS_RETURN_TLS_WANT_READ ((getdns_return_t) 420)
#define GETDNS_RETURN_TLS_WANT_WRITE ((getdns_return_t) 421) #define GETDNS_RETURN_TLS_WANT_WRITE ((getdns_return_t) 421)
@ -100,6 +104,38 @@ _getdns_tls_x509* _getdns_tls_connection_get_peer_certificate(_getdns_tls_connec
*/ */
getdns_return_t _getdns_tls_connection_is_session_reused(_getdns_tls_connection* conn); getdns_return_t _getdns_tls_connection_is_session_reused(_getdns_tls_connection* conn);
/**
* Set up host name verification.
*
* @param conn the connection.
* @param auth_name the hostname.
* @return GETDNS_RETURN_GOOD if all OK.
* @return GETDNS_RETURN_INVALID_PARAMETER if conn is null or has no SSL.
*/
getdns_return_t _getdns_tls_connection_setup_hostname_auth(_getdns_tls_connection* conn, const char* auth_name);
/**
* Set host pinset.
*
* @param conn the connection.
* @param auth_name the hostname.
* @return GETDNS_RETURN_GOOD if all OK.
* @return GETDNS_RETURN_INVALID_PARAMETER if conn is null or has no SSL.
*/
getdns_return_t _getdns_tls_connection_set_host_pinset(_getdns_tls_connection* conn, const char* auth_name, const sha256_pin_t* pinset);
/**
* Get result of certificate verification.
*
* @param conn the connection.
* @param errno failure error number.
* @param errmsg failure error message.
* @return GETDNS_RETURN_GOOD if all OK.
* @return GETDNS_RETURN_INVALID_PARAMETER if conn is null or has no SSL.
* @return GETDNS_RETURN_GENERIC_ERROR if verification failed.
*/
getdns_return_t _getdns_tls_connection_verify(_getdns_tls_connection* conn, long* errnum, const char** errmsg);
/** /**
* Read from TLS. * Read from TLS.
* *