Implement authenticaiton fallback on a given upstream (needs more work). Also need API option to set auth requirement.

This commit is contained in:
Sara Dickinson 2015-09-25 17:28:45 +01:00
parent e710286e45
commit af617e92a7
10 changed files with 120 additions and 39 deletions

View File

@ -91,6 +91,9 @@ doc: FORCE
example:
cd spec/example && $(MAKE) $@
test_code:
cd src && $(MAKE) $@
test:
cd src && $(MAKE) $@

View File

@ -107,14 +107,13 @@ AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT])
AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT])
dnl TLS v1.2 requires OpenSSL 1.0.1
AC_CHECK_LIB(ssl, TLSv1_2_client_method,AC_DEFINE([HAVE_LIBTLS1_2], [1],
AC_CHECK_LIB(ssl, TLSv1_2_client_method,AC_DEFINE([HAVE_TLS_v1_2], [1],
[Define if you have libssl with tls 1.2]),[AC_MSG_WARN([Cannot find TLSv1_2_client_method in libssl library. TLS will not be available.])])
])dnl End of ACX_SSL_CHECKS
dnl Authentication for TLS requires 1.0.2
AC_CHECK_LIB(ssl, SSL_CTX_get0_param, AC_DEFINE([HAVE_LIBSSL_102], [1],
[Define if you have libssl 1.0.2 or later]),[AC_MSG_WARN([libssl 1.0.2 or higher is required for TLS authentication. Authenticated TLS will not be available.])])
])dnl End of ACX_SSL_CHECKS
dnl Native OpenSSL hostname verification requires OpenSSL 1.0.2
AC_CHECK_LIB(ssl, SSL_CTX_get0_param,AC_DEFINE([HAVE_SSL_HN_AUTH], [1],
[Define if you have libssl with host name verification]),[AC_MSG_WARN([Cannot find SSL_CTX_get0_param in libssl library. Native TLS hostname verification will not be available, custom code will be used.])])
])
dnl Check for SSL, where SSL is mandatory
dnl Adds --with-ssl option, searches for openssl and defines HAVE_SSL if found

View File

@ -147,6 +147,8 @@ libgetdns_ext_ev.la: libgetdns.la libev.lo
libgetdns.la: $(GETDNS_OBJ) version.lo context.lo libmini_event.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ)
$(LIBTOOL) --tag=CC --mode=link $(CC) $(CFLAGS) -o $@ $(GETDNS_OBJ) version.lo context.lo libmini_event.lo $(GLDNS_OBJ) $(COMPAT_OBJ) $(UTIL_OBJ) $(LDFLAGS) -rpath $(libdir) -version-info $(libversion) -no-undefined -export-symbols $(srcdir)/libgetdns.symbols
test_code: FORCE
cd test && $(MAKE) $@
test: FORCE
cd test && $(MAKE) $@

View File

@ -897,6 +897,8 @@ getdns_context_create_with_extended_memory_functions(
result->edns_maximum_udp_payload_size = -1;
if ((r = create_default_dns_transports(result)))
goto error;
result->tls_auth_req = 1;
result->tls_auth_fallback_ok = 1;
result->limit_outstanding_queries = 0;
result->return_dnssec_status = GETDNS_EXTENSION_FALSE;
@ -2176,11 +2178,22 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context,
if (context->resolution_type == GETDNS_RESOLUTION_STUB) {
if (tls_is_in_transports_list(context) == 1 &&
context->tls_ctx == NULL) {
#ifdef HAVE_LIBTLS1_2
#ifdef HAVE_TLS_v1_2
/* Create client context, use TLS v1.2 only for now */
context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method());
if(context->tls_ctx == NULL)
return GETDNS_RETURN_BAD_CONTEXT;
/* Be strict and only use the cipher suites recommended in RFC7525 */
const char* const PREFERRED_CIPHERS = "EECDH+aRSA+AESGCM:EDH+aRSA+AESGCM";
if (!SSL_CTX_set_cipher_list(context->tls_ctx, PREFERRED_CIPHERS))
return GETDNS_RETURN_BAD_CONTEXT;
if ((tls_only_is_in_transports_list(context) == 1) && context->tls_auth_req)
context->tls_auth_fallback_ok = 0;
/* TODO: If no auth data provided for any upstream, fail here */
else
context->tls_auth_fallback_ok = 1;
/* By default cert chain will be verified, but note that per
connection management of the result and hostname verification is done.*/
SSL_CTX_set_verify(context->tls_ctx, SSL_VERIFY_PEER, _getdns_tls_verify_callback);
if (!SSL_CTX_set_default_verify_paths(context->tls_ctx))
return GETDNS_RETURN_BAD_CONTEXT;

View File

@ -145,6 +145,9 @@ struct getdns_context {
getdns_upstreams *upstreams;
uint16_t limit_outstanding_queries;
uint32_t dnssec_allowed_skew;
/*Make this a list*/
size_t tls_auth_req;
size_t tls_auth_fallback_ok; /*Redundant but convinient*/
getdns_transport_list_t *dns_transports;
size_t dns_transport_count;

View File

@ -104,6 +104,8 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner,
net_req->transport_current = 0;
memcpy(net_req->transports, owner->context->dns_transports,
net_req->transport_count * sizeof(getdns_transport_list_t));
net_req->tls_auth_req = owner->context->tls_auth_req;
net_req->tls_auth_fallback_ok = owner->context->tls_auth_fallback_ok;
memset(&net_req->event, 0, sizeof(net_req->event));
memset(&net_req->tcp, 0, sizeof(net_req->tcp));
net_req->query_id = 0;

View File

@ -32,6 +32,7 @@
*/
#include <openssl/err.h>
#include <openssl/conf.h>
#include <openssl/x509v3.h>
#include "config.h"
#include <fcntl.h>
@ -78,7 +79,6 @@ static void stub_timeout_cb(void *userarg);
/* General utility functions */
/*****************************/
static void
rollover_secret()
{
@ -822,20 +822,39 @@ tls_failed(getdns_upstream *upstream)
upstream->tls_hs_state == GETDNS_HS_FAILED) ? 1: 0;
}
int
_getdns_tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
int err;
err = X509_STORE_CTX_get_error(ctx);
const char * err_str;
err_str = X509_verify_cert_error_string(err);
DEBUG_STUB("--- %s, ERROR: %s\n", __FUNCTION__, err_str);
/*Always proceed without changing result*/
return preverify_ok;
}
int
_getdns_tls_verify_callback_with_fallback(int preverify_ok, X509_STORE_CTX *ctx) {
int err;
err = X509_STORE_CTX_get_error(ctx);
const char * err_str;
err_str = X509_verify_cert_error_string(err);
DEBUG_STUB("--- %s, ERROR: (%d) \"%s\"\n", __FUNCTION__, err, err_str);
/*Proceed if error is hostname mismatch*/
if (err == X509_V_ERR_HOSTNAME_MISMATCH)
return 1;
else
return preverify_ok;
}
static SSL*
tls_create_object(getdns_context *context, int fd, const char* auth_name)
tls_create_object(getdns_dns_req *dnsreq, int fd, getdns_upstream *upstream)
{
#ifdef HAVE_LIBSSL_102
/* Create SSL instance */
getdns_context *context = dnsreq->context;
if (context->tls_ctx == NULL)
return NULL;
// if (auth_name[0] == '\0') {
// DEBUG_STUB("--- %s, ERROR: No host name provided for authentication\n", __FUNCTION__);
// return NULL;
// }
SSL* ssl = SSL_new(context->tls_ctx);
X509_VERIFY_PARAM *param;
if(!ssl)
return NULL;
@ -844,26 +863,47 @@ tls_create_object(getdns_context *context, int fd, const char* auth_name)
SSL_free(ssl);
return NULL;
}
SSL_set_tlsext_host_name(ssl, auth_name);
param = SSL_get0_param(ssl);
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, auth_name, 0);
/* NOTE: this code will fallback on a given upstream, without trying
authentication on other upstreams first. This is non-optimal and is
an interim simplification. */
/* Lack of host name is OK unless only authenticated TLS is specified*/
if (upstream->tls_auth_name[0] == '\0') {
if (!dnsreq->netreqs[0]->tls_auth_fallback_ok) {
DEBUG_STUB("--- %s, ERROR: No host name provided for authentication\n", __FUNCTION__);
upstream->tls_hs_state = GETDNS_HS_FAILED;
return NULL;
} else {
SSL_set_verify(ssl, SSL_VERIFY_NONE, NULL);
}
} else {
/*Request certificate for the auth_name*/
SSL_set_tlsext_host_name(ssl, upstream->tls_auth_name);
#ifdef HAVE_SSL_HN_AUTH
/* Set up native OpenSSL hostname verification*/
X509_VERIFY_PARAM *param;
param = SSL_get0_param(ssl);
X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
X509_VERIFY_PARAM_set1_host(param, upstream->tls_auth_name, 0);
#else
/* TODO: Trigger post-handshake custom validation*/
if (!dnsreq->netreqs[0]->tls_auth_fallback_ok) {
DEBUG_STUB("--- %s, ERROR: Authentication functionality not available\n", __FUNCTION__);
upstream->tls_hs_state = GETDNS_HS_FAILED;
upstream->tls_auth_failed = 1;
return NULL;
}
#endif
/* Allow fallback from authenticated TLS if settings permit it (use NONE here?)*/
if (dnsreq->netreqs[0]->tls_auth_fallback_ok)
SSL_set_verify(ssl, SSL_VERIFY_NONE, _getdns_tls_verify_callback_with_fallback);
}
SSL_set_connect_state(ssl);
(void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
return ssl;
#else
return NULL;
#endif
}
int
_getdns_tls_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
int err;
err = X509_STORE_CTX_get_error(ctx);
const char * err_str;
err_str = X509_verify_cert_error_string(err);
DEBUG_STUB("--- %s, ERROR: %s\n", __FUNCTION__, err_str);
return 1;
}
static int
@ -920,6 +960,17 @@ tls_do_handshake(getdns_upstream *upstream)
}
}
upstream->tls_hs_state = GETDNS_HS_DONE;
/* TODO: Can we detect the authentication state of the connection here?*/
//#ifndef HAVE_SSL_HN_AUTH
/*TODO: When OpenSSL 1.0.2 not available, use custom function to validate
the hostname.*/
// X509* cert = SSL_get_peer_certificate(upstream->tls_obj);
// if(cert) { X509_free(cert); } /* Free immediately */
// if(NULL == cert) return 0;
// if (!verify_cert_hostname(cert, upstream->tls_auth_name))
// DEBUG_STUB("--- %s %s\n", __FUNCTION__, "Hostname verification failed: ");
// X509_free(cert);
//#endif
/* Reset timeout on success*/
GETDNS_CLEAR_EVENT(upstream->loop, &upstream->event);
upstream->event.read_cb = NULL;
@ -1328,9 +1379,9 @@ upstream_read_cb(void *userarg)
if (netreq->owner == upstream->starttls_req) {
dnsreq = netreq->owner;
if (is_starttls_response(netreq)) {
upstream->tls_obj = tls_create_object(dnsreq->context,
upstream->tls_obj = tls_create_object(dnsreq,
upstream->fd,
upstream->tls_auth_name);
upstream);
if (upstream->tls_obj == NULL)
upstream->tls_hs_state = GETDNS_HS_FAILED;
upstream->tls_hs_state = GETDNS_HS_WRITE;
@ -1570,7 +1621,7 @@ upstream_connect(getdns_upstream *upstream, getdns_transport_list_t transport,
return upstream->fd;
fd = tcp_connect(upstream, transport);
if (fd == -1) return -1;
upstream->tls_obj = tls_create_object(dnsreq->context, fd, upstream->tls_auth_name);
upstream->tls_obj = tls_create_object(dnsreq, fd, upstream);
if (upstream->tls_obj == NULL) {
close(fd);
return -1;
@ -1633,7 +1684,7 @@ find_upstream_for_netreq(getdns_network_req *netreq)
netreq->transports[i],
&fd);
if (fd == -1 || !upstream)
continue;
continue;
netreq->transport_current = i;
netreq->upstream = upstream;
return fd;

View File

@ -157,6 +157,9 @@ nolibldns:
@false
test: $(NOLIBCHECK) $(NOLIBLDNS) all
test_code: $(NOLIBCHECK) all
(cd $(srcdir)/../.. && find . -type f -executable -and \( -name "*.[ch]" -or -name "*.html" -or -name "*.in" -or -name "*.good" -or -name "*.ac" \) | awk 'BEGIN{e=0}{print("ERROR! Executable bit found on", $$0);e=1}END{exit(e)}')
./$(CHECK_GETDNS)
if test $(have_libevent) = 1 ; then ./$(CHECK_EVENT_PROG) ; fi

View File

@ -862,6 +862,9 @@ getdns_return_t do_the_call(void)
}
if (r == GETDNS_RETURN_GOOD && !batch_mode)
getdns_context_run(context);
if (r != GETDNS_RETURN_GOOD)
fprintf(stderr, "An error occurred: %d '%s'\n", r,
getdns_get_errorstr_by_id(r));
} else {
switch (calltype) {
case GENERAL:

View File

@ -196,8 +196,10 @@ typedef struct getdns_network_req
struct getdns_upstream *upstream;
int fd;
getdns_transport_list_t transports[GETDNS_TRANSPORTS_MAX];
size_t transport_count;
size_t transport_current;
size_t transport_count;
size_t transport_current;
size_t tls_auth_req;
size_t tls_auth_fallback_ok;
getdns_eventloop_event event;
getdns_tcp_state tcp;
uint16_t query_id;