mirror of https://github.com/getdnsapi/getdns.git
Implement authenticaiton fallback on a given upstream (needs more work). Also need API option to set auth requirement.
This commit is contained in:
parent
e710286e45
commit
af617e92a7
|
@ -91,6 +91,9 @@ doc: FORCE
|
|||
example:
|
||||
cd spec/example && $(MAKE) $@
|
||||
|
||||
test_code:
|
||||
cd src && $(MAKE) $@
|
||||
|
||||
test:
|
||||
cd src && $(MAKE) $@
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) $@
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
111
src/stub.c
111
src/stub.c
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue