/** * * \file tls.c * @brief getdns TLS functions */ /* * Copyright (c) 2018-2019, NLnet Labs * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the names of the copyright holders nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL Verisign, Inc. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "debug.h" #include "context.h" #include "const-info.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. */ static char const * const _getdns_tls_context_default_cipher_list = #ifndef HAVE_SSL_CTX_SET_CIPHERSUITES "TLS13-AES-256-GCM-SHA384:TLS13-AES-128-GCM-SHA256:" "TLS13-CHACHA20-POLY1305-SHA256:" #endif "EECDH+AESGCM:EECDH+CHACHA20"; static char const * const _getdns_tls_context_default_cipher_suites = "TLS_AES_256_GCM_SHA384:TLS_AES_128_GCM_SHA256:" "TLS_CHACHA20_POLY1305_SHA256"; static char const * const _getdns_tls_connection_opportunistic_cipher_list = "DEFAULT"; static int _getdns_tls_verify_always_ok(int ok, X509_STORE_CTX *ctx) { # if defined(STUB_DEBUG) && STUB_DEBUG char buf[8192]; X509 *cert; int err; int depth; cert = X509_STORE_CTX_get_current_cert(ctx); err = X509_STORE_CTX_get_error(ctx); depth = X509_STORE_CTX_get_error_depth(ctx); if (cert) X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf)); else strcpy(buf, ""); DEBUG_STUB("DEBUG Cert verify: depth=%d verify=%d err=%d subject=%s errorstr=%s\n", depth, ok, err, buf, X509_verify_cert_error_string(err)); # else /* defined(STUB_DEBUG) && STUB_DEBUG */ (void)ok; (void)ctx; # endif /* #else defined(STUB_DEBUG) && STUB_DEBUG */ return 1; } static _getdns_tls_x509* _getdns_tls_x509_new(struct mem_funcs* mfs, X509* cert) { _getdns_tls_x509* res; if (!cert) return NULL; res = GETDNS_MALLOC(*mfs, _getdns_tls_x509); if (res) res->ssl = cert; return res; } static const EVP_MD* get_digester(int algorithm) { const EVP_MD* digester; switch (algorithm) { #ifdef HAVE_EVP_MD5 case GETDNS_HMAC_MD5 : digester = EVP_md5() ; break; #endif #ifdef HAVE_EVP_SHA1 case GETDNS_HMAC_SHA1 : digester = EVP_sha1() ; break; #endif #ifdef HAVE_EVP_SHA224 case GETDNS_HMAC_SHA224: digester = EVP_sha224(); break; #endif #ifdef HAVE_EVP_SHA256 case GETDNS_HMAC_SHA256: digester = EVP_sha256(); break; #endif #ifdef HAVE_EVP_SHA384 case GETDNS_HMAC_SHA384: digester = EVP_sha384(); break; #endif #ifdef HAVE_EVP_SHA512 case GETDNS_HMAC_SHA512: digester = EVP_sha512(); break; #endif default : digester = NULL; } return digester; } #if HAVE_DECL_SSL_SET_MIN_PROTO_VERSION static int _getdns_tls_version2openssl_version(getdns_tls_version_t v) { switch (v) { # ifdef SSL3_VERSION case GETDNS_SSL3 : return SSL3_VERSION; # endif # ifdef TLS1_VERSION case GETDNS_TLS1 : return TLS1_VERSION; # endif # ifdef TLS1_1_VERSION case GETDNS_TLS1_1: return TLS1_1_VERSION; # endif # ifdef TLS1_2_VERSION case GETDNS_TLS1_2: return TLS1_2_VERSION; # endif # ifdef TLS1_3_VERSION case GETDNS_TLS1_3: return TLS1_3_VERSION; # endif default : # if defined(TLS_MAX_VERSION) return TLS_MAX_VERSION; # elif defined(TLS1_3_VERSION) return TLS1_3_VERSION; # elif defined(TLS1_2_VERSION) return TLS1_2_VERSION; # elif defined(TLS1_1_VERSION) return TLS1_1_VERSION; # elif defined(TLS1_VERSION) return TLS1_VERSION; # elif defined(SSL3_VERSION) return SSL3_VERSION; # else return -1; # endif } } #endif #ifdef USE_WINSOCK /* For windows, the CA trust store is not read by openssl. Add code to open the trust store using wincrypt API and add the root certs into openssl trust store */ static int add_WIN_cacerts_to_openssl_store(SSL_CTX* tls_ctx, const getdns_log_config* log) { HCERTSTORE hSystemStore; PCCERT_CONTEXT pTargetCert = NULL; _getdns_log(log, GETDNS_LOG_SYS_STUB, GETDNS_LOG_DEBUG , "%s: %s\n", STUB_DEBUG_SETUP_TLS , "Adding Windows certificates from system root store to CA store") ; /* load just once per context lifetime for this version of getdns TODO: dynamically update CA trust changes as they are available */ assert(tls_ctx); /* Call wincrypt's CertOpenStore to open the CA root store. */ if ((hSystemStore = CertOpenStore( CERT_STORE_PROV_SYSTEM, 0, 0, /* NOTE: mingw does not have this const: replace with 1 << 16 from code CERT_SYSTEM_STORE_CURRENT_USER, */ 1 << 16, L"root")) == 0) { _getdns_log(log, GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s\n", STUB_DEBUG_SETUP_TLS , "Could not CertOpenStore()"); return 0; } X509_STORE* store = SSL_CTX_get_cert_store(tls_ctx); if (!store) { _getdns_log(log, GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s\n", STUB_DEBUG_SETUP_TLS , "Could not SSL_CTX_get_cert_store()"); return 0; } /* failure if the CA store is empty or the call fails */ if ((pTargetCert = CertEnumCertificatesInStore( hSystemStore, pTargetCert)) == 0) { _getdns_log(log, GETDNS_LOG_SYS_STUB, GETDNS_LOG_NOTICE , "%s: %s\n", STUB_DEBUG_SETUP_TLS , "CA certificate store for Windows is empty."); return 0; } /* iterate over the windows cert store and add to openssl store */ do { X509 *cert1 = d2i_X509(NULL, (const unsigned char **)&pTargetCert->pbCertEncoded, pTargetCert->cbCertEncoded); if (!cert1) { /* return error if a cert fails */ _getdns_log(log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s %d:%s\n" , STUB_DEBUG_SETUP_TLS , "Unable to parse certificate in memory" , ERR_get_error() , ERR_error_string(ERR_get_error(), NULL)); return 0; } else { /* return error if a cert add to store fails */ if (X509_STORE_add_cert(store, cert1) == 0) { unsigned long error = ERR_peek_last_error(); /* Ignore error X509_R_CERT_ALREADY_IN_HASH_TABLE which means the * certificate is already in the store. */ if(ERR_GET_LIB(error) != ERR_LIB_X509 || ERR_GET_REASON(error) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { _getdns_log(log , GETDNS_LOG_SYS_STUB , GETDNS_LOG_ERR , "%s: %s %d:%s\n" , STUB_DEBUG_SETUP_TLS , "Error adding certificate" , ERR_get_error() , ERR_error_string( ERR_get_error() , NULL) ); X509_free(cert1); return 0; } } X509_free(cert1); } } while ((pTargetCert = CertEnumCertificatesInStore( hSystemStore, pTargetCert)) != 0); /* Clean up memory and quit. */ if (pTargetCert) CertFreeCertificateContext(pTargetCert); if (hSystemStore) { if (!CertCloseStore(hSystemStore, 0)) { _getdns_log(log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s\n", STUB_DEBUG_SETUP_TLS , "Could not CertCloseStore()"); return 0; } } _getdns_log(log, GETDNS_LOG_SYS_STUB, GETDNS_LOG_INFO , "%s: %s\n", STUB_DEBUG_SETUP_TLS , "Completed adding Windows certificates to CA store successfully") ; return 1; } #endif 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, const getdns_log_config* log) { _getdns_tls_context* res; if (!(res = GETDNS_MALLOC(*mfs, struct _getdns_tls_context))) return NULL; res->log = log; /* Create client context, use TLS v1.2 only for now */ # ifdef HAVE_TLS_CLIENT_METHOD res->ssl = SSL_CTX_new(TLS_client_method()); # else res->ssl = SSL_CTX_new(TLSv1_2_client_method()); # endif if(res->ssl == NULL) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error creating TLS context" , ssl_err); GETDNS_FREE(*mfs, res); return NULL; } return res; } getdns_return_t _getdns_tls_context_free(struct mem_funcs* mfs, _getdns_tls_context* ctx) { if (!ctx || !ctx->ssl) return GETDNS_RETURN_INVALID_PARAMETER; SSL_CTX_free(ctx->ssl); GETDNS_FREE(*mfs, ctx); return GETDNS_RETURN_GOOD; } void _getdns_tls_context_pinset_init(_getdns_tls_context* ctx) { int osr; #if defined(HAVE_SSL_CTX_DANE_ENABLE) osr = SSL_CTX_dane_enable(ctx->ssl); #elif defined(USE_DANESSL) osr = DANESSL_CTX_init(ctx->ssl); #else #error Must have either DANE SSL or OpenSSL v1.1. #endif if (!osr) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_WARNING , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Could not enable DANE on TLX context" , ssl_err); } } getdns_return_t _getdns_tls_context_set_min_max_tls_version(_getdns_tls_context* ctx, getdns_tls_version_t min, getdns_tls_version_t max) { #if HAVE_DECL_SSL_SET_MIN_PROTO_VERSION char ssl_err[256]; int min_ssl = _getdns_tls_version2openssl_version(min); int max_ssl = _getdns_tls_version2openssl_version(max); if (!ctx || !ctx->ssl) return GETDNS_RETURN_INVALID_PARAMETER; if (min && !SSL_CTX_set_min_proto_version(ctx->ssl, min_ssl)) { struct const_info* ci = _getdns_get_const_info(min); ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS context with " "minimum TLS version" , ci->name , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } if (max && !SSL_CTX_set_max_proto_version(ctx->ssl, max_ssl)) { struct const_info* ci = _getdns_get_const_info(min); ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS context with " "minimum TLS version" , ci->name , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } return GETDNS_RETURN_GOOD; #else /* * We've used TLSv1_2_client_method() creating the context, so * error if they asked for anything other than TLS 1.2 or better. */ (void) ctx; if ((!min || min == GETDNS_TLS1_2) && !max) return GETDNS_RETURN_GOOD; _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s\n" , STUB_DEBUG_SETUP_TLS , "This version of OpenSSL does not " "support setting of minimum or maximum " "TLS versions"); return GETDNS_RETURN_NOT_IMPLEMENTED; #endif } const char* _getdns_tls_context_get_default_cipher_list() { return _getdns_tls_context_default_cipher_list; } getdns_return_t _getdns_tls_context_set_cipher_list(_getdns_tls_context* ctx, const char* list) { if (!ctx || !ctx->ssl) return GETDNS_RETURN_INVALID_PARAMETER; if (!list) list = _getdns_tls_context_default_cipher_list; if (!SSL_CTX_set_cipher_list(ctx->ssl, list)) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS context with " "cipher list" , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } return GETDNS_RETURN_GOOD; } const char* _getdns_tls_context_get_default_cipher_suites() { return _getdns_tls_context_default_cipher_suites; } getdns_return_t _getdns_tls_context_set_cipher_suites(_getdns_tls_context* ctx, const char* list) { if (!ctx || !ctx->ssl) return GETDNS_RETURN_INVALID_PARAMETER; #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES if (!list) list = _getdns_tls_context_default_cipher_suites; if (!SSL_CTX_set_ciphersuites(ctx->ssl, list)) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS context with " "cipher suites" , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } #else if (list) { _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s\n" , STUB_DEBUG_SETUP_TLS , "This version of OpenSSL does not " "support configuring cipher suites"); return GETDNS_RETURN_NOT_IMPLEMENTED; } #endif return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_context_set_curves_list(_getdns_tls_context* ctx, const char* list) { if (!ctx || !ctx->ssl) return GETDNS_RETURN_INVALID_PARAMETER; #if HAVE_TLS_CTX_CURVES_LIST if (list && !SSL_CTX_set1_curves_list(ctx->ssl, list)) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS context with " "curves list" , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } #else if (list) { _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB, GETDNS_LOG_ERR , "%s: %s\n" , STUB_DEBUG_SETUP_TLS , "This version of OpenSSL does not " "support configuring curves list"); return GETDNS_RETURN_NOT_IMPLEMENTED; } #endif return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_context_set_ca(_getdns_tls_context* ctx, const char* file, const char* path) { if (!ctx || !ctx->ssl) return GETDNS_RETURN_INVALID_PARAMETER; if (file || path) { if (!SSL_CTX_load_verify_locations(ctx->ssl, file, path)) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err , sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB , GETDNS_LOG_WARNING , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Could not load verify locations" , ssl_err); } else { _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB , GETDNS_LOG_DEBUG , "%s: %s\n" , STUB_DEBUG_SETUP_TLS , "Verify locations loaded"); } return GETDNS_RETURN_GOOD; /* pass */ } #ifndef USE_WINSOCK else if (SSL_CTX_set_default_verify_paths(ctx->ssl)) return GETDNS_RETURN_GOOD; else { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err , sizeof(ssl_err)); _getdns_log(ctx->log , GETDNS_LOG_SYS_STUB , GETDNS_LOG_WARNING , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Could not load default verify locations" , ssl_err); } #else else if (add_WIN_cacerts_to_openssl_store(ctx->ssl, ctx->log)) return GETDNS_RETURN_GOOD; #endif /* USE_WINSOCK */ return GETDNS_RETURN_GENERIC_ERROR; } _getdns_tls_connection* _getdns_tls_connection_new(struct mem_funcs* mfs, _getdns_tls_context* ctx, int fd, const getdns_log_config* log) { _getdns_tls_connection* res; if (!ctx || !ctx->ssl) return NULL; if (!(res = GETDNS_MALLOC(*mfs, struct _getdns_tls_connection))) return NULL; res->ssl = SSL_new(ctx->ssl); if (!res->ssl) { GETDNS_FREE(*mfs, res); return NULL; } if (!SSL_set_fd(res->ssl, fd)) { SSL_free(res->ssl); GETDNS_FREE(*mfs, res); return NULL; } res->log = log; /* Connection is a client. */ SSL_set_connect_state(res->ssl); /* If non-application data received, retry read. */ SSL_set_mode(res->ssl, SSL_MODE_AUTO_RETRY); return res; } getdns_return_t _getdns_tls_connection_free(struct mem_funcs* mfs, _getdns_tls_connection* conn) { if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; SSL_free(conn->ssl); GETDNS_FREE(*mfs, conn); return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_connection_shutdown(_getdns_tls_connection* conn) { if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; #ifdef USE_DANESSL DANESSL_cleanup(conn->ssl); #endif switch (SSL_shutdown(conn->ssl)) { case 0: return GETDNS_RETURN_CONTEXT_UPDATE_FAIL; case 1: return GETDNS_RETURN_GOOD; default: return GETDNS_RETURN_GENERIC_ERROR; } } getdns_return_t _getdns_tls_connection_set_min_max_tls_version(_getdns_tls_connection* conn, getdns_tls_version_t min, getdns_tls_version_t max) { #if HAVE_DECL_SSL_SET_MIN_PROTO_VERSION char ssl_err[256]; int min_ssl = _getdns_tls_version2openssl_version(min); int max_ssl = _getdns_tls_version2openssl_version(max); if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; if (min && !SSL_set_min_proto_version(conn->ssl, min_ssl)) { struct const_info* ci = _getdns_get_const_info(min); ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS connection with " "minimum TLS version" , ci->name , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } if (max && !SSL_set_max_proto_version(conn->ssl, max_ssl)) { struct const_info* ci = _getdns_get_const_info(min); ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS connection with " "minimum TLS version" , ci->name , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } return GETDNS_RETURN_GOOD; #else /* * We've used TLSv1_2_client_method() creating the context, so * error if they asked for anything other than TLS 1.2 or better. */ (void) conn; if ((!min || min == GETDNS_TLS1_2) && !max) return GETDNS_RETURN_GOOD; _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s\n" , STUB_DEBUG_SETUP_TLS , "This version of OpenSSL does not " "support setting of minimum or maximum " "TLS versions"); return GETDNS_RETURN_NOT_IMPLEMENTED; #endif } getdns_return_t _getdns_tls_connection_set_cipher_list(_getdns_tls_connection* conn, const char* list) { if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; if (!list) list = _getdns_tls_connection_opportunistic_cipher_list; if (!SSL_set_cipher_list(conn->ssl, list)) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS connection with " "cipher list" , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_connection_set_cipher_suites(_getdns_tls_connection* conn, const char* list) { if (!conn || !conn->ssl || !list) return GETDNS_RETURN_INVALID_PARAMETER; #ifdef HAVE_SSL_SET_CIPHERSUITES if (!SSL_set_ciphersuites(conn->ssl, list)) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS connection with " "cipher suites" , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } #else if (list) { _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s\n" , STUB_DEBUG_SETUP_TLS , "This version of OpenSSL does not " "support configuring cipher suites"); return GETDNS_RETURN_NOT_IMPLEMENTED; } #endif return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_connection_set_curves_list(_getdns_tls_connection* conn, const char* list) { if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; #if HAVE_TLS_CONN_CURVES_LIST if (list && !SSL_set1_curves_list(conn->ssl, list)) { char ssl_err[256]; ERR_error_string_n( ERR_get_error() , ssl_err, sizeof(ssl_err)); _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s (%s)\n" , STUB_DEBUG_SETUP_TLS , "Error configuring TLS connection with " "curves list" , ssl_err); return GETDNS_RETURN_BAD_CONTEXT; } #else if (list) { _getdns_log(conn->log , GETDNS_LOG_UPSTREAM_STATS, GETDNS_LOG_ERR , "%s: %s\n" , STUB_DEBUG_SETUP_TLS , "This version of OpenSSL does not " "support configuring curves list"); return GETDNS_RETURN_NOT_IMPLEMENTED; } #endif return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_connection_set_session(_getdns_tls_connection* conn, _getdns_tls_session* s) { if (!conn || !conn->ssl || !s || !s->ssl) return GETDNS_RETURN_INVALID_PARAMETER; if (!SSL_set_session(conn->ssl, s->ssl)) return GETDNS_RETURN_GENERIC_ERROR; return GETDNS_RETURN_GOOD; } _getdns_tls_session* _getdns_tls_connection_get_session(struct mem_funcs* mfs, _getdns_tls_connection* conn) { _getdns_tls_session* res; if (!conn || !conn->ssl) return NULL; if (!(res = GETDNS_MALLOC(*mfs, struct _getdns_tls_session))) return NULL; res->ssl = SSL_get1_session(conn->ssl); if (!res->ssl) { GETDNS_FREE(*mfs, res); return NULL; } return res; } const char* _getdns_tls_connection_get_version(_getdns_tls_connection* conn) { if (!conn || !conn->ssl) return NULL; return SSL_get_version(conn->ssl); } getdns_return_t _getdns_tls_connection_do_handshake(_getdns_tls_connection* conn) { int r; int err; if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; ERR_clear_error(); r = SSL_do_handshake(conn->ssl); if (r == 1) return GETDNS_RETURN_GOOD; err = SSL_get_error(conn->ssl, r); switch (err) { case SSL_ERROR_WANT_READ: return GETDNS_RETURN_TLS_WANT_READ; case SSL_ERROR_WANT_WRITE: return GETDNS_RETURN_TLS_WANT_WRITE; default: return GETDNS_RETURN_GENERIC_ERROR; } } _getdns_tls_x509* _getdns_tls_connection_get_peer_certificate(struct mem_funcs* mfs, _getdns_tls_connection* conn) { if (!conn || !conn->ssl) return NULL; return _getdns_tls_x509_new(mfs, SSL_get_peer_certificate(conn->ssl)); } getdns_return_t _getdns_tls_connection_is_session_reused(_getdns_tls_connection* conn) { if (!conn || !conn->ssl) return GETDNS_RETURN_INVALID_PARAMETER; if (SSL_session_reused(conn->ssl)) return GETDNS_RETURN_GOOD; else return GETDNS_RETURN_TLS_CONNECTION_FRESH; } getdns_return_t _getdns_tls_connection_setup_hostname_auth(_getdns_tls_connection* conn, const char* auth_name) { 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; } getdns_return_t _getdns_tls_connection_set_host_pinset(_getdns_tls_connection* conn, const char* auth_name, const sha256_pin_t* pinset) { if (!conn || !conn->ssl || !auth_name) return GETDNS_RETURN_INVALID_PARAMETER; #if defined(USE_DANESSL) /* 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" , STUB_DEBUG_SETUP_TLS, __FUNC__, upstream->tls_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 = SSL_dane_tlsa_add(conn->ssl, 2, 1, 1, (unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH); DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_tlsa_add() -> %d\n" , STUB_DEBUG_SETUP_TLS, __FUNC__, osr); if (osr > 0) ++n_pins; osr = SSL_dane_tlsa_add(conn->ssl, 3, 1, 1, (unsigned char *)pin_p->pin, SHA256_DIGEST_LENGTH); DEBUG_STUB("%s %-35s: DEBUG: SSL_dane_tlsa_add() -> %d\n" , STUB_DEBUG_SETUP_TLS, __FUNC__, osr); 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; } getdns_return_t _getdns_tls_connection_certificate_verify(_getdns_tls_connection* conn, long* errnum, const char** errmsg) { if (!conn || !conn->ssl) 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; if (errmsg) *errmsg = "Pinset validation failure"; 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; } getdns_return_t _getdns_tls_connection_read(_getdns_tls_connection* conn, uint8_t* buf, size_t to_read, size_t* read) { int sread; if (!conn || !conn->ssl || !read) return GETDNS_RETURN_INVALID_PARAMETER; ERR_clear_error(); sread = SSL_read(conn->ssl, buf, to_read); if (sread <= 0) { switch (SSL_get_error(conn->ssl, sread)) { case SSL_ERROR_WANT_READ: return GETDNS_RETURN_TLS_WANT_READ; case SSL_ERROR_WANT_WRITE: return GETDNS_RETURN_TLS_WANT_WRITE; default: return GETDNS_RETURN_GENERIC_ERROR; } } *read = sread; return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_connection_write(_getdns_tls_connection* conn, uint8_t* buf, size_t to_write, size_t* written) { int swritten; if (!conn || !conn->ssl || !written) return GETDNS_RETURN_INVALID_PARAMETER; ERR_clear_error(); swritten = SSL_write(conn->ssl, buf, to_write); if (swritten <= 0) { switch(SSL_get_error(conn->ssl, swritten)) { case SSL_ERROR_WANT_READ: /* SSL_write will not do partial writes, because * SSL_MODE_ENABLE_PARTIAL_WRITE is not default, * but the write could fail because of renegotiation. * In that case SSL_get_error() will return * SSL_ERROR_WANT_READ or, SSL_ERROR_WANT_WRITE. * Return for retry in such cases. */ return GETDNS_RETURN_TLS_WANT_READ; case SSL_ERROR_WANT_WRITE: return GETDNS_RETURN_TLS_WANT_WRITE; default: return GETDNS_RETURN_GENERIC_ERROR; } } *written = swritten; return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_session_free(struct mem_funcs* mfs, _getdns_tls_session* s) { if (!s || !s->ssl) return GETDNS_RETURN_INVALID_PARAMETER; SSL_SESSION_free(s->ssl); GETDNS_FREE(*mfs, s); return GETDNS_RETURN_GOOD; } getdns_return_t _getdns_tls_get_api_information(getdns_dict* dict) { if (! getdns_dict_set_int( dict, "openssl_build_version_number", OPENSSL_VERSION_NUMBER) #ifdef HAVE_OPENSSL_VERSION_NUM && ! getdns_dict_set_int( dict, "openssl_version_number", OpenSSL_version_num()) #endif #ifdef HAVE_OPENSSL_VERSION && ! getdns_dict_util_set_string( dict, "openssl_version_string", OpenSSL_version(OPENSSL_VERSION)) && ! getdns_dict_util_set_string( dict, "openssl_cflags", OpenSSL_version(OPENSSL_CFLAGS)) && ! getdns_dict_util_set_string( dict, "openssl_built_on", OpenSSL_version(OPENSSL_BUILT_ON)) && ! getdns_dict_util_set_string( dict, "openssl_platform", OpenSSL_version(OPENSSL_PLATFORM)) && ! getdns_dict_util_set_string( dict, "openssl_dir", OpenSSL_version(OPENSSL_DIR)) && ! getdns_dict_util_set_string( dict, "openssl_engines_dir", OpenSSL_version(OPENSSL_ENGINES_DIR)) #endif ) return GETDNS_RETURN_GOOD; return GETDNS_RETURN_GENERIC_ERROR; } void _getdns_tls_x509_free(struct mem_funcs* mfs, _getdns_tls_x509* cert) { if (cert && cert->ssl) X509_free(cert->ssl); GETDNS_FREE(*mfs, cert); } int _getdns_tls_x509_to_der(struct mem_funcs* mfs, _getdns_tls_x509* cert, getdns_bindata* bindata) { unsigned char* buf = NULL; int len; if (!cert || !cert->ssl ) return 0; if (bindata == NULL) return i2d_X509(cert->ssl, NULL); len = i2d_X509(cert->ssl, &buf); if (len == 0 || (bindata->data = GETDNS_XMALLOC(*mfs, uint8_t, len)) == NULL) { bindata->size = 0; bindata->data = NULL; } else { bindata->size = len; (void) memcpy(bindata->data, buf, len); OPENSSL_free(buf); } return len; } unsigned char* _getdns_tls_hmac_hash(struct mem_funcs* mfs, int algorithm, const void* key, size_t key_size, const void* data, size_t data_size, size_t* output_size) { const EVP_MD* digester = get_digester(algorithm); unsigned char* res; unsigned int md_len; if (!digester) return NULL; res = (unsigned char*) GETDNS_XMALLOC(*mfs, unsigned char, GETDNS_TLS_MAX_DIGEST_LENGTH); if (!res) return NULL; (void) HMAC(digester, key, key_size, data, data_size, res, &md_len); if (output_size) *output_size = md_len; return res; } _getdns_tls_hmac* _getdns_tls_hmac_new(struct mem_funcs* mfs, int algorithm, const void* key, size_t key_size) { const EVP_MD *digester = get_digester(algorithm); _getdns_tls_hmac* res; if (!digester) return NULL; if (!(res = GETDNS_MALLOC(*mfs, struct _getdns_tls_hmac))) return NULL; #ifdef HAVE_HMAC_CTX_NEW res->ctx = HMAC_CTX_new(); if (!res->ctx) { GETDNS_FREE(*mfs, res); return NULL; } #else res->ctx = &res->ctx_space; HMAC_CTX_init(res->ctx); #endif if (!HMAC_Init_ex(res->ctx, key, key_size, digester, NULL)) { #ifdef HAVE_HMAC_CTX_NEW HMAC_CTX_free(res->ctx); #endif GETDNS_FREE(*mfs, res); return NULL; } return res; } getdns_return_t _getdns_tls_hmac_add(_getdns_tls_hmac* h, const void* data, size_t data_size) { if (!h || !h->ctx || !data) return GETDNS_RETURN_INVALID_PARAMETER; if (!HMAC_Update(h->ctx, data, data_size)) return GETDNS_RETURN_GENERIC_ERROR; else return GETDNS_RETURN_GOOD; } unsigned char* _getdns_tls_hmac_end(struct mem_funcs* mfs, _getdns_tls_hmac* h, size_t* output_size) { unsigned char* res; unsigned int md_len; res = (unsigned char*) GETDNS_XMALLOC(*mfs, unsigned char, GETDNS_TLS_MAX_DIGEST_LENGTH); if (!res) return NULL; (void) HMAC_Final(h->ctx, res, &md_len); #ifdef HAVE_HMAC_CTX_NEW HMAC_CTX_free(h->ctx); #endif GETDNS_FREE(*mfs, h); if (output_size) *output_size = md_len; return res; } void _getdns_tls_sha1(const void* data, size_t data_size, unsigned char* buf) { SHA1(data, data_size, buf); } void _getdns_tls_cookie_sha256(uint32_t secret, void* addr, size_t addrlen, unsigned char* buf, size_t* buflen) { const EVP_MD *md; EVP_MD_CTX *mdctx; unsigned int md_len; md = EVP_sha256(); mdctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(mdctx, md, NULL); EVP_DigestUpdate(mdctx, &secret, sizeof(secret)); EVP_DigestUpdate(mdctx, addr, addrlen); EVP_DigestFinal_ex(mdctx, buf, &md_len); EVP_MD_CTX_destroy(mdctx); *buflen = md_len; } /* tls.c */