mirror of https://github.com/getdnsapi/getdns.git
1279 lines
35 KiB
C
1279 lines
35 KiB
C
/**
|
|
*
|
|
* \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 <openssl/err.h>
|
|
#include <openssl/conf.h>
|
|
#include <openssl/x509.h>
|
|
#include <openssl/x509v3.h>
|
|
#include <openssl/pem.h>
|
|
#include <openssl/bio.h>
|
|
#include <openssl/ssl.h>
|
|
|
|
#include <openssl/opensslv.h>
|
|
#include <openssl/crypto.h>
|
|
|
|
#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, "<unknown>");
|
|
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 */
|