From 53e23f1358d97e2e2bef0f13f68b9f9cbb5c5d99 Mon Sep 17 00:00:00 2001 From: Willem Toorop Date: Fri, 4 Sep 2015 10:56:30 +0200 Subject: [PATCH] Revert "Revert "Merge pull request #112 from saradickinson/features/tls_auth"" This reverts commit 6d29e6044e2acbfbde7c20695f8f46b2aeb0e7e4. --- INSTALL | 7 ++++ README.md | 8 +++-- m4/acx_openssl.m4 | 5 +-- spec/index.html | 4 ++- src/context.c | 65 +++++++++++++++++++++++++++++------- src/context.h | 1 + src/stub.c | 21 +++++++++--- src/test/getdns_query.c | 8 +++++ src/test/tests_transports.sh | 10 ++++-- 9 files changed, 105 insertions(+), 24 deletions(-) diff --git a/INSTALL b/INSTALL index 2d4f855f..fd917338 100644 --- a/INSTALL +++ b/INSTALL @@ -254,6 +254,13 @@ not `/usr/local'. It is recommended to use the following options: ./configure --prefix=/boot/common + On Mac OSX getdns will not build against the version of OpenSSL shipped with +OSX. If you link against a self-complied version of OpenSSL then manual +configuration of certificates into the default OpenSSL directory +/usr/local/etc/openssl/certs is currently required for TLS authentication to work. +However if linking against the version of OpenSSL installed via Homebrew TLS +authentication will work out of the box. + Specifying the System Type ========================== diff --git a/README.md b/README.md index 3d4513a6..5e3a53ff 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ External dependencies are linked outside the getdns API build tree (we rely on c * [libunbound from NLnet Labs](http://www.nlnetlabs.nl/projects/unbound/) version 1.4.16 or later * [libexpat](http://expat.sourceforge.net/) for libunbound. * [libidn from the FSF](http://www.gnu.org/software/libidn/) version 1. +* [libssl from the OpenSSL Project](https://www.openssl.org/) version 0.9.7 or later. (Note: version 1.0.2 or later is required for TLS support) * Doxygen is used to generate documentation, while this is not technically necessary for the build it makes things a lot more pleasant. You have to install the library and also the library-devel (or -dev) for your @@ -153,8 +154,6 @@ There are a few known issues which we have summarized below - the most recent and helpful list is being maintained in the git issues list in the repository. Other known issues are being managed in the git repository issue list. -* (#113) Changing the resolution type between stub and recursive after a query has been issued with a context will not work - the previous resolution type will continue to be used. If you want to change the resolution type you will need to create a new context and set the resolution type for that context. - * When doing a synchronous lookup with a context that has outstanding asynchronous lookups, the callbacks for the asynchronous lookups might get called as a side effect of the synchronous lookup. @@ -218,6 +217,9 @@ build the packages, this is simplythe one we chose to use. create dmg + A self-compiled version of OpenSSL or the version installed via Homebrew is required. + Note: If using a self-compiled version manual configuration of certificates into /usr/local/etc/openssl/certs is required for TLS authentication to work. + #### Homebrew If you're using [Homebrew](http://brew.sh/), you may run `brew install getdns`. By default, this will only build the core library without any 3rd party event loop support. @@ -226,7 +228,7 @@ To install the [event loop integration libraries](https://github.com/getdnsapi/g Note that in order to compile the examples, the `--with-libevent` switch is required. -As of the 0.2.0 release, when installing via Homebrew, the trust anchor is expected to be located at `$(brew --prefix)/etc/getdns-root.key`. Additionally, the openssl lib installed by Homebrew is linked against. +As of the 0.2.0 release, when installing via Homebrew, the trust anchor is expected to be located at `$(brew --prefix)/etc/getdns-root.key`. Additionally, the OpenSSL library installed by Homebrew is linked against. Note that the Homebrew OpenSSL installation clones the Keychain certificates to the default OpenSSL location so TLS authentication should work out of the box. Contributors ============ diff --git a/m4/acx_openssl.m4 b/m4/acx_openssl.m4 index 87507dce..693075c4 100644 --- a/m4/acx_openssl.m4 +++ b/m4/acx_openssl.m4 @@ -105,8 +105,9 @@ AC_DEFUN([ACX_SSL_CHECKS], [ AC_CHECK_HEADERS([openssl/ssl.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/err.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/rand.h],,, [AC_INCLUDES_DEFAULT]) -AC_CHECK_LIB(ssl, TLSv1_2_client_method,AC_DEFINE([HAVE_LIBTLS1_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 Authentication now requires 1.0.2, which supports TLSv1.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. TLS will not be available.])]) ])dnl End of ACX_SSL_CHECKS dnl Check for SSL, where SSL is mandatory diff --git a/spec/index.html b/spec/index.html index af5ad542..05375e7e 100644 --- a/spec/index.html +++ b/spec/index.html @@ -2209,7 +2209,9 @@ getdns_context_set_dns_transport_list(

The transports array contains an ordered list of transports that will be used for DNS lookups. If only one transport value is specified it will be the only transport used. Should it not be available basic resolution will fail. -Fallback transport options are specified by including multiple values in the list. +Fallback transport options are specified by including multiple values in the list. Currently the TLS and STARTTLS options +perform Strict TLS which requires a hostname to be +specified so that authentication can be performed. This hostname can be specified in the tls_auth_name parameter for an upstream. The values are GETDNS_TRANSPORT_UDP, GETDNS_TRANSPORT_TCP, diff --git a/src/context.c b/src/context.c index 8c49bcbe..fbb429bc 100644 --- a/src/context.c +++ b/src/context.c @@ -579,6 +579,27 @@ _getdns_upstream_shutdown(getdns_upstream *upstream) close(fd); } +static int +tls_is_in_transports_list(getdns_context *context) { + for (int i=0; i< context->dns_transport_count;i++) { + if (context->dns_transports[i] == GETDNS_TRANSPORT_TLS || + context->dns_transports[i] == GETDNS_TRANSPORT_STARTTLS) + return 1; + } + return 0; +} + +static int +tls_only_is_in_transports_list(getdns_context *context) { + if (context->dns_transport_count != 1) + return 0; + if (context->dns_transports[0] == GETDNS_TRANSPORT_TLS || + context->dns_transports[0] == GETDNS_TRANSPORT_STARTTLS) + return 1; + return 0; +} + + static int net_req_query_id_cmp(const void *id1, const void *id2) { @@ -606,6 +627,7 @@ upstream_init(getdns_upstream *upstream, upstream->starttls_req = NULL; upstream->transport = GETDNS_TRANSPORT_TCP; upstream->tls_hs_state = GETDNS_HS_NONE; + upstream->tls_auth_name[0] = '\0'; upstream->tcp.write_error = 0; upstream->loop = NULL; (void) getdns_eventloop_event_init( @@ -1219,14 +1241,20 @@ getdns_set_base_dns_transports( if (!context || transport_count == 0 || transports == NULL) return GETDNS_RETURN_INVALID_PARAMETER; + /* Check for valid transports and that they are used only once*/ + int u=0,t=0,l=0,s=0; for(i=0; i1 || t>1 || l>1 || s>1) + return GETDNS_RETURN_INVALID_PARAMETER; if (!(new_transports = GETDNS_XMALLOC(context->my_mf, getdns_transport_list_t, transport_count))) @@ -1665,6 +1693,7 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, getdns_dict *dict; getdns_bindata *address_type; getdns_bindata *address_data; + getdns_bindata *tls_auth_name; struct sockaddr_storage addr; getdns_bindata *scope_id; @@ -1738,6 +1767,17 @@ getdns_context_set_upstream_recursive_servers(struct getdns_context *context, upstream->addr.ss_family = addr.ss_family; upstream_init(upstream, upstreams, ai); upstream->transport = getdns_upstream_transports[j]; + if (getdns_upstream_transports[j] == GETDNS_TRANSPORT_TLS || + getdns_upstream_transports[j] == GETDNS_TRANSPORT_STARTTLS) { + if ((r = getdns_dict_get_bindata( + dict, "tls_auth_name", &tls_auth_name)) == GETDNS_RETURN_GOOD) { + /*TODO: VALIDATE THIS STRING!*/ + memcpy(upstream->tls_auth_name, + (char *)tls_auth_name->data, + tls_auth_name->size); + upstream->tls_auth_name[tls_auth_name->size] = '\0'; + } + } upstreams->count++; freeaddrinfo(ai); } @@ -2134,23 +2174,24 @@ _getdns_context_prepare_for_resolution(struct getdns_context *context, /* Transport can in theory be set per query in stub mode */ if (context->resolution_type == GETDNS_RESOLUTION_STUB) { - /*TODO[TLS]: Check if TLS is in the list of transports.*/ - if (context->tls_ctx == NULL) { -#ifdef HAVE_LIBTLS1_2 + if (tls_is_in_transports_list(context) == 1 && + context->tls_ctx == NULL) { +#ifdef HAVE_LIBSSL_102 /* Create client context, use TLS v1.2 only for now */ context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()); #endif if(context->tls_ctx == NULL) return GETDNS_RETURN_BAD_CONTEXT; + SSL_CTX_set_verify(context->tls_ctx, SSL_VERIFY_PEER, NULL); + if (!SSL_CTX_set_default_verify_paths(context->tls_ctx)) + return GETDNS_RETURN_BAD_CONTEXT; } } /* Block use of STARTTLS/TLS ONLY in recursive mode as it won't work */ /* Note: If TLS is used in recursive mode this will try TLS on port - * 53 so it is blocked here. So is STARTTLS only at the moment. */ + * 53 so it is blocked here. So is 'STARTTLS only' at the moment. */ if (context->resolution_type == GETDNS_RESOLUTION_RECURSING && - context->dns_transport_count == 1 && - (context->dns_transports[0] == GETDNS_TRANSPORT_TLS || - context->dns_transports[0] == GETDNS_TRANSPORT_STARTTLS)) + tls_only_is_in_transports_list(context) == 1) return GETDNS_RETURN_BAD_CONTEXT; if (context->resolution_type_set == context->resolution_type) diff --git a/src/context.h b/src/context.h index 3f768830..4e762843 100644 --- a/src/context.h +++ b/src/context.h @@ -101,6 +101,7 @@ typedef struct getdns_upstream { getdns_eventloop_event event; getdns_eventloop *loop; getdns_tcp_state tcp; + char tls_auth_name[256]; /* Pipelining of TCP network requests */ getdns_network_req *write_queue; diff --git a/src/stub.c b/src/stub.c index daf2499d..b9da8d17 100644 --- a/src/stub.c +++ b/src/stub.c @@ -32,6 +32,7 @@ */ #include +#include #include "config.h" #include #include "stub.h" @@ -822,12 +823,15 @@ tls_failed(getdns_upstream *upstream) } static SSL* -tls_create_object(getdns_context *context, int fd) +tls_create_object(getdns_context *context, int fd, const char* auth_name) { +#ifdef HAVE_LIBSSL_102 /* Create SSL instance */ - if (context->tls_ctx == NULL) + if (context->tls_ctx == NULL || auth_name == NULL) return NULL; SSL* ssl = SSL_new(context->tls_ctx); + X509_VERIFY_PARAM *param; + if(!ssl) return NULL; /* Connect the SSL object with a file descriptor */ @@ -835,9 +839,16 @@ tls_create_object(getdns_context *context, int fd) 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); SSL_set_connect_state(ssl); (void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); return ssl; +#else + return NULL; +#endif } static int @@ -889,6 +900,7 @@ tls_do_handshake(getdns_upstream *upstream) upstream->tls_hs_state = GETDNS_HS_WRITE; return STUB_TCP_AGAIN; default: + DEBUG_STUB("--- %s %s %d\n", __FUNCTION__, "Handshake failed: ", want); return tls_cleanup(upstream); } } @@ -1302,7 +1314,8 @@ upstream_read_cb(void *userarg) dnsreq = netreq->owner; if (is_starttls_response(netreq)) { upstream->tls_obj = tls_create_object(dnsreq->context, - upstream->fd); + upstream->fd, + upstream->tls_auth_name); if (upstream->tls_obj == NULL) upstream->tls_hs_state = GETDNS_HS_FAILED; upstream->tls_hs_state = GETDNS_HS_WRITE; @@ -1542,7 +1555,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_obj = tls_create_object(dnsreq->context, fd, upstream->tls_auth_name); if (upstream->tls_obj == NULL) { close(fd); return -1; diff --git a/src/test/getdns_query.c b/src/test/getdns_query.c index 5e85eea3..bc9d4043 100644 --- a/src/test/getdns_query.c +++ b/src/test/getdns_query.c @@ -279,6 +279,7 @@ ipaddr_dict(getdns_context *context, char *ipstr) char *s = strchr(ipstr, '%'), *scope_id_str = ""; char *p = strchr(ipstr, '@'), *portstr = ""; char *t = strchr(ipstr, '#'), *tls_portstr = ""; + char *n = strchr(ipstr, '~'), *tls_namestr = ""; uint8_t buf[sizeof(struct in6_addr)]; getdns_bindata addr; @@ -297,6 +298,10 @@ ipaddr_dict(getdns_context *context, char *ipstr) *t = 0; tls_portstr = t + 1; } + if (n) { + *n = 0; + tls_namestr = n + 1; + } if (strchr(ipstr, ':')) { getdns_dict_util_set_string(r, "address_type", "IPv6"); addr.size = 16; @@ -317,6 +322,9 @@ ipaddr_dict(getdns_context *context, char *ipstr) getdns_dict_set_int(r, "port", (int32_t)atoi(portstr)); if (*tls_portstr) getdns_dict_set_int(r, "tls_port", (int32_t)atoi(tls_portstr)); + if (*tls_namestr) { + getdns_dict_util_set_string(r, "tls_auth_name", tls_namestr); + } if (*scope_id_str) getdns_dict_util_set_string(r, "scope_id", scope_id_str); diff --git a/src/test/tests_transports.sh b/src/test/tests_transports.sh index a3c18681..98c3aec3 100755 --- a/src/test/tests_transports.sh +++ b/src/test/tests_transports.sh @@ -2,7 +2,7 @@ DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd ) SERVER_IP="8.8.8.8" -TLS_SERVER_IP="185.49.141.38" +TLS_SERVER_IP="185.49.141.38~www.dnssec-name-and-shame.com" GOOD_RESULT_SYNC="Status was: At least one response was returned" GOOD_RESULT_ASYNC="successfull" BAD_RESULT_SYNC="1 'Generic error'" @@ -58,6 +58,7 @@ usage () { ehco " -p path to getdns_query binary" echo " -s server configured for only TCP and UDP" echo " -t server configured for TLS, STARTTLS, TCP and UDP" + echo " (This must include the hostname e.g. 185.49.141.38~www.dnssec-name-and-shame.com)" } while getopts ":p:s:t:dh" opt; do @@ -70,6 +71,9 @@ while getopts ":p:s:t:dh" opt; do esac done +TLS_SERVER_IP_NO_NAME=`echo ${TLS_SERVER_IP%~*}` +echo $TLS_SERVER_IP_NO_NAME + GOOD_QUERIES=( "-s -A -q getdnsapi.net -l U @${SERVER_IP} " "-s -A -q getdnsapi.net -l T @${SERVER_IP} " @@ -78,13 +82,15 @@ GOOD_QUERIES=( GOOD_FALLBACK_QUERIES=( "-s -A -q getdnsapi.net -l LT @${SERVER_IP}" -"-s -A -q getdnsapi.net -l LU @${SERVER_IP}" +"-s -A -q getdnsapi.net -l LT @${SERVER_IP}" +"-s -A -q getdnsapi.net -l LT @${TLS_SERVER_IP_NO_NAME}" "-s -A -q getdnsapi.net -l L @${SERVER_IP} @${TLS_SERVER_IP}" "-s -G -q DNSKEY getdnsapi.net -l UT @${SERVER_IP} -b 512 -D") NOT_AVAILABLE_QUERIES=( "-s -A -q getdnsapi.net -l L @${SERVER_IP} " "-s -A -q getdnsapi.net -l S @${SERVER_IP} " +"-s -A -q getdnsapi.net -l L @${TLS_SERVER_IP_NO_NAME} " "-s -G -q DNSKEY getdnsapi.net -l U @${SERVER_IP} -b 512 -D") echo "Starting transport test"