diff --git a/.gitmodules b/.gitmodules index 27d60b78..26a1f354 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,7 @@ path = stubby url = https://github.com/getdnsapi/stubby.git branch = develop +[submodule "src/ssl_dane"] + path = src/ssl_dane + url = https://github.com/getdnsapi/ssl_dane + branch = getdns diff --git a/configure.ac b/configure.ac index 6fadac38..d5a7f25e 100644 --- a/configure.ac +++ b/configure.ac @@ -464,6 +464,24 @@ AC_ARG_WITH([gnutls], fi ACX_LIB_SSL AC_SUBST([TLSDIR], 'openssl') + + # Verify OpenSSL is at least version 1.0.2. + # We also check it's not LibreSSL, but that's a little later, not here. + AC_CHECK_FUNCS([X509_check_host SSL_dane_enable]) + if test "x$ac_cv_func_X509_check_host" != xyes; then + AC_MSG_ERROR([getdns requires OpenSSL version 1.0.2 or later]) + fi + + AC_MSG_CHECKING([whether we need to compile/link DANE support]) + DANESSL_XTRA_OBJS="" + if test "x$ac_cv_func_SSL_dane_enable" = xyes; then + AC_MSG_RESULT([no]) + else + AC_MSG_RESULT([yes]) + AC_DEFINE([USE_DANESSL], [1], [Define this to use DANE functions from the ssl_dane/danessl library.]) + DANESSL_XTRA_OBJS="danessl.lo" + fi + AC_SUBST(DANESSL_XTRA_OBJS) ]) @@ -472,17 +490,14 @@ if test $USE_NSS = "no" -a $USE_NETTLE = "no" ; then AC_MSG_CHECKING([for LibreSSL]) if grep VERSION_TEXT $ssldir/include/openssl/opensslv.h | grep "LibreSSL" >/dev/null; then AC_MSG_RESULT([yes]) - AC_DEFINE([HAVE_LIBRESSL], [1], [Define if we have LibreSSL]) - # libressl provides these compat functions, but they may also be - # declared by the OS in libc. See if they have been declared. - AC_CHECK_DECLS([strlcpy,arc4random,arc4random_uniform]) + AC_MSG_ERROR([getdns does not support LibreSSL]) else AC_MSG_RESULT([no]) fi AC_CHECK_HEADERS([openssl/conf.h openssl/ssl.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/engine.h],,, [AC_INCLUDES_DEFAULT]) AC_CHECK_HEADERS([openssl/bn.h openssl/rsa.h openssl/dsa.h],,, [AC_INCLUDES_DEFAULT]) -AC_CHECK_FUNCS([OPENSSL_config EVP_md5 EVP_sha1 EVP_sha224 EVP_sha256 EVP_sha384 EVP_sha512 FIPS_mode ENGINE_load_cryptodev EVP_PKEY_keygen ECDSA_SIG_get0 EVP_MD_CTX_new EVP_PKEY_base_id HMAC_CTX_new HMAC_CTX_free TLS_client_method DSA_SIG_set0 EVP_dss1 EVP_DigestVerify SSL_CTX_set_min_proto_version OpenSSL_version_num OpenSSL_version SSL_CTX_dane_enable SSL_dane_enable SSL_dane_tlsa_add X509_check_host X509_get_notAfter X509_get0_notAfter SSL_CTX_set_ciphersuites SSL_set_ciphersuites]) +AC_CHECK_FUNCS([OPENSSL_config EVP_md5 EVP_sha1 EVP_sha224 EVP_sha256 EVP_sha384 EVP_sha512 FIPS_mode ENGINE_load_cryptodev EVP_PKEY_keygen ECDSA_SIG_get0 EVP_MD_CTX_new EVP_PKEY_base_id HMAC_CTX_new HMAC_CTX_free TLS_client_method DSA_SIG_set0 EVP_dss1 EVP_DigestVerify SSL_CTX_set_min_proto_version OpenSSL_version_num OpenSSL_version SSL_CTX_dane_enable SSL_dane_enable SSL_dane_tlsa_add X509_check_host X509_get_notAfter X509_get0_notAfter SSL_CTX_set_ciphersuites SSL_set_ciphersuites OPENSSL_init_crypto DSA_set0_pqg DSA_set0_key RSA_set0_key]) AC_CHECK_DECLS([SSL_COMP_get_compression_methods,sk_SSL_COMP_pop_free,SSL_CTX_set_ecdh_auto,SSL_CTX_set1_curves_list,SSL_set1_curves_list,SSL_set_min_proto_version,SSL_get_min_proto_version], [], [], [ AC_INCLUDES_DEFAULT #ifdef HAVE_OPENSSL_ERR_H @@ -505,25 +520,6 @@ AC_INCLUDES_DEFAULT ]) fi -AC_MSG_CHECKING([for OpenSSL >= 1.1.1]) -AC_LANG_PUSH(C) -AC_COMPILE_IFELSE( - [AC_LANG_PROGRAM([ - [#include ] - [#if OPENSSL_VERSION_NUMBER < 0x10101000L] - [#error "OpenSSL 1.1.1 or higher required"] - [#elif defined(LIBRESSL_VERSION_NUMBER)] - [#error "LibreSSL not supported"] - [#endif] - ],[[]])], - [ - AC_MSG_RESULT([yes]) - ], - [ - AC_MSG_ERROR([OpenSSL 1.1.1 or later required]) - ]) -AC_LANG_POP(C) - AC_ARG_ENABLE(sha1, AC_HELP_STRING([--disable-sha1], [Disable SHA1 RRSIG support, does not disable nsec3 support])) case "$enable_sha1" in no) diff --git a/src/Makefile.in b/src/Makefile.in index 2047ca0e..411f5874 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -99,8 +99,9 @@ TLS_OBJ=tls.lo pubkey-pinning-internal.lo keyraw-internal.lo val_secalgo.lo anch YXML_OBJ=yxml.lo YAML_OBJ=convert_yaml_to_json.lo +DANESSL_OBJ=danessl.lo -GETDNS_XTRA_OBJS=@GETDNS_XTRA_OBJS@ +GETDNS_XTRA_OBJS=@GETDNS_XTRA_OBJS@ @DANESSL_XTRA_OBJS@ STUBBY_XTRA_OBJS=@STUBBY_XTRA_OBJS@ EXTENSION_OBJ=$(DEFAULT_EVENTLOOP_OBJ) libevent.lo libev.lo @@ -140,6 +141,9 @@ $(TLS_OBJ): $(YAML_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -c $(stubbysrcdir)/src/yaml/$(@:.lo=.c) -o $@ +$(DANESSL_OBJ): + $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) $(WNOERRORFLAG) -c $(srcdir)/ssl_dane/$(@:.lo=.c) -o $@ + $(YXML_OBJ): $(LIBTOOL) --quiet --tag=CC --mode=compile $(CC) $(CFLAGS) -I$(srcdir)/yxml -DYXML_GETDNS -Wno-unused-parameter -c $(srcdir)/yxml/$(@:.lo=.c) -o $@ diff --git a/src/openssl/keyraw-internal.c b/src/openssl/keyraw-internal.c index 75c53c00..b8077049 100644 --- a/src/openssl/keyraw-internal.c +++ b/src/openssl/keyraw-internal.c @@ -140,6 +140,8 @@ gldns_key_buf2dsa_raw(unsigned char* key, size_t len) BN_free(Y); return NULL; } + +#if defined(HAVE_DSA_SET0_PQG) && defined(HAVE_DSA_SET0_KEY) if (!DSA_set0_pqg(dsa, P, Q, G)) { /* QPG not yet attached, need to free */ BN_free(Q); @@ -156,6 +158,14 @@ gldns_key_buf2dsa_raw(unsigned char* key, size_t len) BN_free(Y); return NULL; } +#else +# ifndef S_SPLINT_S + dsa->p = P; + dsa->q = Q; + dsa->g = G; + dsa->pub_key = Y; +# endif /* splint */ +#endif return dsa; } @@ -208,12 +218,20 @@ gldns_key_buf2rsa_raw(unsigned char* key, size_t len) BN_free(modulus); return NULL; } + +#if defined(HAVE_RSA_SET0_KEY) if (!RSA_set0_key(rsa, modulus, exponent, NULL)) { BN_free(exponent); BN_free(modulus); RSA_free(rsa); return NULL; } +#else +# ifndef S_SPLINT_S + rsa->n = modulus; + rsa->e = exponent; +# endif /* splint */ +#endif return rsa; } diff --git a/src/openssl/tls-internal.h b/src/openssl/tls-internal.h index 4b4b4b49..06f95bda 100644 --- a/src/openssl/tls-internal.h +++ b/src/openssl/tls-internal.h @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, NLnet Labs + * Copyright (c) 2018-2019, NLnet Labs * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -54,12 +54,18 @@ #define GETDNS_TLS_MAX_DIGEST_LENGTH (EVP_MAX_MD_SIZE) +typedef struct sha256_pin sha256_pin_t; + typedef struct _getdns_tls_context { SSL_CTX* ssl; } _getdns_tls_context; typedef struct _getdns_tls_connection { SSL* ssl; +#if defined(USE_DANESSL) + const char* auth_name; + sha256_pin_t* pinset; +#endif } _getdns_tls_connection; typedef struct _getdns_tls_session { diff --git a/src/openssl/tls.c b/src/openssl/tls.c index 111c15d0..ba1648f1 100644 --- a/src/openssl/tls.c +++ b/src/openssl/tls.c @@ -5,7 +5,7 @@ */ /* - * Copyright (c) 2018, NLnet Labs + * Copyright (c) 2018-2019, NLnet Labs * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -47,8 +47,20 @@ #include "debug.h" #include "context.h" +#ifdef USE_DANESSL +# include "ssl_dane/danessl.h" +#endif + #include "tls.h" +/* Double check configure has worked as expected. */ +#if defined(USE_DANESSL) && \ + (defined(HAVE_SSL_DANE_ENABLE) || \ + defined(HAVE_OPENSSL_INIT_CRYPTO) || \ + defined(HAVE_SSL_CTX_DANE_ENABLE)) +#error Configure error USE_DANESSL defined with OpenSSL 1.1 functions! +#endif + /* Cipher suites recommended in RFC7525. */ char const * const _getdns_tls_context_default_cipher_list = "TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:" @@ -57,6 +69,26 @@ char const * const _getdns_tls_context_default_cipher_list = static char const * const _getdns_tls_connection_opportunistic_cipher_list = "DEFAULT"; +#if defined(USE_DANESSL) && defined(STUB_DEBUG) && STUB_DEBUG +static void _stub_debug_print_openssl_errors(void) +{ + unsigned long err; + char buffer[1024]; + const char *file; + const char *data; + int line; + int flags; + + while ((err = ERR_get_error_line_data(&file, &line, &data, &flags)) != 0) { + ERR_error_string_n(err, buffer, sizeof(buffer)); + if (flags & ERR_TXT_STRING) + DEBUG_STUB("DEBUG OpenSSL Error: %s:%s:%d:%s\n", buffer, file, line, data); + else + DEBUG_STUB("DEBUG OpenSSL Error: %s:%s:%d\n", buffer, file, line); + } +} +#endif + static int _getdns_tls_verify_always_ok(int ok, X509_STORE_CTX *ctx) { # if defined(STUB_DEBUG) && STUB_DEBUG @@ -218,10 +250,19 @@ add_WIN_cacerts_to_openssl_store(SSL_CTX* tls_ctx) void _getdns_tls_init() { +#ifdef HAVE_OPENSSL_INIT_CRYPTO OPENSSL_init_crypto( OPENSSL_INIT_ADD_ALL_CIPHERS | OPENSSL_INIT_ADD_ALL_DIGESTS | OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL); (void)OPENSSL_init_ssl(0, NULL); +#else + OpenSSL_add_all_algorithms(); + SSL_library_init(); + +# ifdef USE_DANESSL + (void) DANESSL_library_init(); +# endif +#endif } _getdns_tls_context* _getdns_tls_context_new(struct mem_funcs* mfs) @@ -255,14 +296,20 @@ getdns_return_t _getdns_tls_context_free(struct mem_funcs* mfs, _getdns_tls_cont void _getdns_tls_context_pinset_init(_getdns_tls_context* ctx) { -# if defined(STUB_DEBUG) && STUB_DEBUG - int osr = -# else - (void) -# endif - SSL_CTX_dane_enable(ctx->ssl); - DEBUG_STUB("%s %-35s: DEBUG: SSL_CTX_dane_enable() -> %d\n" - , STUB_DEBUG_SETUP_TLS, __FUNC__, osr); + int osr; + (void) osr; + +#if defined(HAVE_SSL_CTX_DANE_ENABLE) + osr = SSL_CTX_dane_enable(ctx->ssl); + DEBUG_STUB("%s %-35s: DEBUG: SSL_CTX_dane_enable() -> %d\n", + STUB_DEBUG_SETUP_TLS, __FUNC__, osr); +#elif defined(USE_DANESSL) + osr = DANESSL_CTX_init(ctx->ssl); + DEBUG_STUB("%s %-35s: DEBUG: DANESSL_CTX_init() -> %d\n", + STUB_DEBUG_SETUP_TLS, __FUNC__, osr); +#else +#error Must have either DANE SSL or OpenSSL v1.1. +#endif } getdns_return_t _getdns_tls_context_set_min_proto_1_2(_getdns_tls_context* ctx) @@ -367,6 +414,13 @@ getdns_return_t _getdns_tls_connection_shutdown(_getdns_tls_connection* conn) if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; +#ifdef USE_DANESSL +# if defined(STUB_DEBUG) && STUB_DEBUG + _stub_debug_print_openssl_errors(); +# endif + DANESSL_cleanup(conn->ssl); +#endif + switch (SSL_shutdown(conn->ssl)) { case 0: return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; case 1: return GETDNS_RETURN_GOOD; @@ -485,12 +539,19 @@ getdns_return_t _getdns_tls_connection_setup_hostname_auth(_getdns_tls_connectio if (!conn || !conn->ssl || !auth_name) return GETDNS_RETURN_INVALID_PARAMETER; +#if defined(HAVE_SSL_DANE_ENABLE) 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); +#elif defined(USE_DANESSL) + /* Stash auth name away for use in cert verification. */ + conn->auth_name = auth_name; +#else +#error Must have either DANE SSL or OpenSSL v1.1. +#endif return GETDNS_RETURN_GOOD; } @@ -499,6 +560,13 @@ getdns_return_t _getdns_tls_connection_set_host_pinset(_getdns_tls_connection* c if (!conn || !conn->ssl || !auth_name) return GETDNS_RETURN_INVALID_PARAMETER; +#if defined(USE_DANE_SSL) + /* Stash auth name and pinset away for use in cert verification. */ + conn->auth_name = auth_name; + conn->pinset = pinset; +#endif + +#if defined(HAVE_SSL_DANE_ENABLE) 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" @@ -520,6 +588,38 @@ getdns_return_t _getdns_tls_connection_set_host_pinset(_getdns_tls_connection* c if (osr > 0) ++n_pins; } +#elif defined(USE_DANESSL) + if (pinset) { + const char *auth_names[2] = { auth_name, NULL }; + int osr = DANESSL_init(conn->ssl, + *auth_name ? auth_name : NULL, + *auth_name ? auth_names : NULL); + (void) osr; + DEBUG_STUB("%s %-35s: DEBUG: DANESSL_init(\"%s\") -> %d\n" + , STUB_DEBUG_SETUP_TLS, __FUNC__, 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 = DANESSL_add_tlsa(conn->ssl, 3, 1, "sha256", + (unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH); + DEBUG_STUB("%s %-35s: DEBUG: DANESSL_add_tlsa() -> %d\n" + , STUB_DEBUG_SETUP_TLS, __FUNC__, osr); + if (osr > 0) + ++n_pins; + osr = DANESSL_add_tlsa(conn->ssl, 2, 1, "sha256", + (unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH); + DEBUG_STUB("%s %-35s: DEBUG: DANESSL_add_tlsa() -> %d\n" + , STUB_DEBUG_SETUP_TLS, __FUNC__, osr); + if (osr > 0) + ++n_pins; + } + } else { + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, _getdns_tls_verify_always_ok); + } +#else +#error Must have either DANE SSL or OpenSSL v1.1. +#endif return GETDNS_RETURN_GOOD; } @@ -529,10 +629,38 @@ getdns_return_t _getdns_tls_connection_certificate_verify(_getdns_tls_connection return GETDNS_RETURN_INVALID_PARAMETER; long verify_result = SSL_get_verify_result(conn->ssl); + + /* Since we don't have DANE validation yet, DANE validation + * failures are always pinset validation failures */ + switch (verify_result) { case X509_V_OK: +#if defined(USE_DANESSL) + { + getdns_return_t res = GETDNS_RETURN_GOOD; + X509* peer_cert = SSL_get_peer_certificate(conn->ssl); + if (peer_cert) { + if (conn->auth_name[0] && + X509_check_host(peer_cert, + conn->auth_name, + strlen(conn->auth_name), + X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, + NULL) <= 0) { + if (errnum) + *errnum = 1; + if (errmsg) + *errmsg = "Hostname mismatch"; + res = GETDNS_RETURN_GENERIC_ERROR; + } + X509_free(peer_cert); + } + return res; + } +#else return GETDNS_RETURN_GOOD; +#endif +#if defined(HAVE_SSL_DANE_ENABLE) case X509_V_ERR_DANE_NO_MATCH: if (errnum) *errnum = 0; @@ -540,13 +668,28 @@ getdns_return_t _getdns_tls_connection_certificate_verify(_getdns_tls_connection *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; +#elif defined(USE_DANESSL) + case X509_V_ERR_CERT_UNTRUSTED: + if (conn->pinset && + !DANESSL_get_match_cert(conn->ssl, NULL, NULL, NULL)) { + if (errnum) + *errnum = 0; + if (errmsg) + *errmsg = "Pinset validation failure"; + return GETDNS_RETURN_GENERIC_ERROR; + } + break; +#else +#error Must have either DANE SSL or OpenSSL v1.1. +#endif } + + /* General error if we get here. */ + if (errnum) + *errnum = verify_result; + if (errmsg) + *errmsg = X509_verify_cert_error_string(verify_result); + return GETDNS_RETURN_GENERIC_ERROR; } diff --git a/src/ssl_dane b/src/ssl_dane new file mode 160000 index 00000000..dd093e58 --- /dev/null +++ b/src/ssl_dane @@ -0,0 +1 @@ +Subproject commit dd093e585a237e0321d303ec35e84c393ef739f4