From cb1dff1ac7e6f199eb1e8c56c7300b9a03211f75 Mon Sep 17 00:00:00 2001 From: saradickinson Date: Sun, 19 Jul 2015 13:18:38 +0200 Subject: [PATCH 1/6] Add ability to verify server certificate using hostname for TLS/STARTTLS NOTE: This implementation will only work for OpenSSL v1.0.2 and later. Doing it for earlier versions is totally insane: https://wiki.openssl.org/index.php/Hostname_validation --- src/context.c | 19 ++++++++++++++++++- src/context.h | 1 + src/stub.c | 14 +++++++++++--- src/test/getdns_query.c | 8 ++++++++ 4 files changed, 38 insertions(+), 4 deletions(-) diff --git a/src/context.c b/src/context.c index 5728c83b..a5ddcb07 100644 --- a/src/context.c +++ b/src/context.c @@ -605,6 +605,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( @@ -1216,6 +1217,8 @@ getdns_set_base_dns_transports( if (!context || transport_count == 0 || transports == NULL) return GETDNS_RETURN_INVALID_PARAMETER; + /* TODO: restrict the use of each transport to once -> + sane list and correct max size for array*/ for(i=0; iaddr.ss_family = addr.ss_family; upstream_init(upstream, upstreams, ai); upstream->transport = getdns_upstream_transports[j]; + if (getdns_upstream_transports[j] == GETDNS_TRANSPORT_TLS) { + 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); } @@ -2130,11 +2144,14 @@ getdns_context_prepare_for_resolution(struct getdns_context *context, #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 || diff --git a/src/context.h b/src/context.h index 998b75b8..7c056c68 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 f9d28379..d675179b 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,14 @@ 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) { /* Create SSL instance */ if (context->tls_ctx == 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,6 +838,10 @@ 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; @@ -1302,7 +1309,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 +1550,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 31877010..e3309f66 100644 --- a/src/test/getdns_query.c +++ b/src/test/getdns_query.c @@ -55,6 +55,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; @@ -73,6 +74,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; @@ -93,6 +98,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); From dbad8a9003c0817a496a577212d1bbb8cb487e8d Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Wed, 12 Aug 2015 13:24:08 +0100 Subject: [PATCH 2/6] Restrict transport list to 1 entry for each valid transport --- src/context.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/context.c b/src/context.c index a5ddcb07..c153d36b 100644 --- a/src/context.c +++ b/src/context.c @@ -1217,16 +1217,20 @@ getdns_set_base_dns_transports( if (!context || transport_count == 0 || transports == NULL) return GETDNS_RETURN_INVALID_PARAMETER; - /* TODO: restrict the use of each transport to once -> - sane list and correct max size for array*/ + /* 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))) From 45de1f65b3bf16564dc720ca73001e2e43298b73 Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Fri, 14 Aug 2015 17:55:43 +0100 Subject: [PATCH 3/6] Update docs with details of OS X certificate handling. --- INSTALL | 6 ++++++ README.md | 8 +++++--- src/context.c | 29 ++++++++++++++++++++++++----- src/stub.c | 3 ++- 4 files changed, 37 insertions(+), 9 deletions(-) diff --git a/INSTALL b/INSTALL index 2d4f855f..ca68375d 100644 --- a/INSTALL +++ b/INSTALL @@ -254,6 +254,12 @@ 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 is 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 f7d13ab5..ecae05fd 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. +* [libopenssl 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 @@ -149,8 +150,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. @@ -214,6 +213,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 is required for TLS authentication to wokr + #### 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. @@ -222,7 +224,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/src/context.c b/src/context.c index c153d36b..ae59b018 100644 --- a/src/context.c +++ b/src/context.c @@ -578,6 +578,27 @@ priv_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) { @@ -2140,8 +2161,8 @@ 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) { + if (tls_is_in_transports_list(context) == 1 && + context->tls_ctx == NULL) { #ifdef HAVE_LIBTLS1_2 /* Create client context, use TLS v1.2 only for now */ context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()); @@ -2157,9 +2178,7 @@ getdns_context_prepare_for_resolution(struct getdns_context *context, /* 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. */ 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/stub.c b/src/stub.c index d675179b..291b9bed 100644 --- a/src/stub.c +++ b/src/stub.c @@ -826,7 +826,7 @@ static SSL* tls_create_object(getdns_context *context, int fd, const char* auth_name) { /* 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; @@ -896,6 +896,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); } } From 262263dbf4f21a9b96c1094d6462e2830e6ebf35 Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Sat, 15 Aug 2015 15:11:29 +0100 Subject: [PATCH 4/6] More detail in documentation --- INSTALL | 7 ++++--- README.md | 2 +- spec/index.html | 4 +++- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/INSTALL b/INSTALL index ca68375d..fd917338 100644 --- a/INSTALL +++ b/INSTALL @@ -254,10 +254,11 @@ 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 + 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 is required for TLS authentication to work, -however if linking against the version of OpenSSL installed via Homebrew TLS +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 ecae05fd..d8a000a4 100644 --- a/README.md +++ b/README.md @@ -214,7 +214,7 @@ 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 is required for TLS authentication to wokr + 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 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, From 2404cc2c8e879cecda08f37fc44cfb5990b538c1 Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Sat, 15 Aug 2015 15:27:58 +0100 Subject: [PATCH 5/6] Extend regression test --- src/context.c | 3 ++- src/test/tests_transports.sh | 10 ++++++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/context.c b/src/context.c index ae59b018..ad6a55b0 100644 --- a/src/context.c +++ b/src/context.c @@ -1764,7 +1764,8 @@ 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) { + 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!*/ 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" From dc7d7e7689e20d76da2cb370d8da142176a82d89 Mon Sep 17 00:00:00 2001 From: Sara Dickinson Date: Sat, 15 Aug 2015 16:35:30 +0100 Subject: [PATCH 6/6] Fix openssl dependancy --- README.md | 2 +- m4/acx_openssl.m4 | 5 +++-- src/context.c | 2 +- src/stub.c | 4 ++++ 4 files changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index d8a000a4..fde550eb 100644 --- a/README.md +++ b/README.md @@ -89,7 +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. -* [libopenssl 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) +* [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 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/src/context.c b/src/context.c index ad6a55b0..c6e71be8 100644 --- a/src/context.c +++ b/src/context.c @@ -2164,7 +2164,7 @@ 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_LIBSSL_102 /* Create client context, use TLS v1.2 only for now */ context->tls_ctx = SSL_CTX_new(TLSv1_2_client_method()); #endif diff --git a/src/stub.c b/src/stub.c index 291b9bed..53c73190 100644 --- a/src/stub.c +++ b/src/stub.c @@ -825,6 +825,7 @@ tls_failed(getdns_upstream *upstream) static SSL* tls_create_object(getdns_context *context, int fd, const char* auth_name) { +#ifdef HAVE_LIBSSL_102 /* Create SSL instance */ if (context->tls_ctx == NULL || auth_name == NULL) return NULL; @@ -845,6 +846,9 @@ tls_create_object(getdns_context *context, int fd, const char* auth_name) SSL_set_connect_state(ssl); (void) SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); return ssl; +#else + return NULL; +#endif } static int