Neo4j-Client
view release on metacpan or search on metacpan
build/lib/src/openssl.c view on Meta::CPAN
/* vi:set ts=4 sw=4 expandtab:
*
* Copyright 2016, Chris Leishman (http://github.com/cleishm)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "../../config.h"
#include "openssl.h"
#include "errno.h"
#include "logging.h"
#include "openssl_iostream.h"
#include "thread.h"
#include "tofu.h"
#include "util.h"
#include <assert.h>
// FIXME: openssl 1.1.0-pre4 has issues with cast-qual
// (and perhaps earlier versions?)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-qual"
#include <openssl/x509v3.h>
#include <openssl/err.h>
#include <openssl/crypto.h>
#include <openssl/opensslv.h>
#pragma GCC diagnostic pop
#define NEO4J_CYPHER_LIST "HIGH:!EXPORT:!aNULL@STRENGTH"
static neo4j_mutex_t *thread_locks;
#ifndef HAVE_ASN1_STRING_GET0_DATA
#define ASN1_STRING_get0_data(x) ASN1_STRING_data(x)
#endif
#ifdef HAVE_CRYPTO_SET_LOCKING_CALLBACK
static void locking_callback(int mode, int type, const char *file, int line);
#endif
static SSL_CTX *new_ctx(const neo4j_config_t *config, neo4j_logger_t *logger);
static int load_private_key(SSL_CTX *ctx, const neo4j_config_t *config,
neo4j_logger_t *logger);
static int pem_pw_callback(char *buf, int size, int rwflag, void *userdata);
static int load_certificate_authorities(SSL_CTX *ctx,
const neo4j_config_t *config, neo4j_logger_t *logger);
static int verify(SSL *ssl, const char *hostname, int port,
const neo4j_config_t *config, uint_fast32_t flags,
neo4j_logger_t *logger);
static int cert_fingerprint(X509* cert, char *buf, size_t n,
neo4j_logger_t *logger);
static int sha512_digest(unsigned char *buf, unsigned int *np,
const void *s, size_t n, neo4j_logger_t *logger);
static int verify_hostname(X509* cert, const char *hostname,
neo4j_logger_t *logger);
static int check_subject_alt_name(X509* cert, const char *hostname,
neo4j_logger_t *logger);
static int check_common_name(X509 *cert, const char *hostname,
neo4j_logger_t *logger);
static int openssl_error(neo4j_logger_t *logger, uint_fast8_t level,
const char *file, unsigned int line);
int neo4j_openssl_init(void)
{
SSL_library_init();
SSL_load_error_strings();
ERR_load_BIO_strings();
OpenSSL_add_all_algorithms();
if (neo4j_openssl_iostream_init())
{
return -1;
}
int num_locks = CRYPTO_num_locks();
thread_locks = calloc(num_locks, sizeof(neo4j_mutex_t));
if (thread_locks == NULL)
{
return -1;
}
for (int i = 0; i < num_locks; i++)
{
int err = neo4j_mutex_init(&(thread_locks[i]));
if (err)
{
for (; i > 0; --i)
{
neo4j_mutex_destroy(&(thread_locks[i-1]));
}
free(thread_locks);
errno = err;
return -1;
}
}
#ifdef HAVE_CRYPTO_SET_LOCKING_CALLBACK
if (CRYPTO_get_locking_callback() == NULL)
{
CRYPTO_set_locking_callback(locking_callback);
CRYPTO_set_id_callback(neo4j_current_thread_id);
build/lib/src/openssl.c view on Meta::CPAN
{
#if OPENSSL_VERSION_NUMBER < 0x10100004
if (result == 0)
#else
// FIXME: when no handshake, openssl 1.1.0-pre4 reports result == -1
// but error == 0 (and perhaps earlier versions?)
if (result == 0 || ERR_peek_error() == 0)
#endif
{
ERR_get_error(); // pop the error
errno = NEO4J_NO_SERVER_TLS_SUPPORT;
goto failure;
}
errno = openssl_error(logger, NEO4J_LOG_ERROR, __FILE__, __LINE__);
goto failure;
}
SSL *ssl = NULL;
BIO_get_ssl(ssl_bio, &ssl);
assert(ssl != NULL);
if (verify(ssl, hostname, port, config, flags, logger))
{
goto failure;
}
neo4j_logger_release(logger);
return ssl_bio;
int errsv;
failure:
errsv = errno;
BIO_free(ssl_bio);
neo4j_logger_release(logger);
errno = errsv;
return NULL;
}
SSL_CTX *new_ctx(const neo4j_config_t *config, neo4j_logger_t *logger)
{
SSL_CTX *ctx = SSL_CTX_new(SSLv23_method());
if (ctx == NULL)
{
errno = openssl_error(logger, NEO4J_LOG_ERROR, __FILE__, __LINE__);
goto failure;
}
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
if (SSL_CTX_set_cipher_list(ctx, NEO4J_CYPHER_LIST) != 1)
{
errno = openssl_error(logger, NEO4J_LOG_ERROR, __FILE__, __LINE__);
goto failure;
}
// Necessary when using blocking sockets
SSL_CTX_set_mode(ctx, SSL_MODE_AUTO_RETRY);
// Caching should be done at the protocol layer anyway
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
if (load_private_key(ctx, config, logger))
{
goto failure;
}
if (load_certificate_authorities(ctx, config, logger))
{
goto failure;
}
return ctx;
int errsv;
failure:
errsv = errno;
SSL_CTX_free(ctx);
errno = errsv;
return NULL;
}
int load_private_key(SSL_CTX *ctx, const neo4j_config_t *config,
neo4j_logger_t *logger)
{
const char *private_key = config->tls_private_key_file;
if (private_key == NULL)
{
return 0;
}
if (SSL_CTX_use_certificate_chain_file(ctx, private_key) != 1)
{
errno = openssl_error(logger, NEO4J_LOG_ERROR, __FILE__, __LINE__);
return -1;
}
if (config->tls_pem_pw_callback != NULL)
{
SSL_CTX_set_default_passwd_cb_userdata(ctx, (void *)(intptr_t)config);
SSL_CTX_set_default_passwd_cb(ctx, pem_pw_callback);
}
return 0;
}
int pem_pw_callback(char *buf, int size, int rwflag, void *userdata)
{
const neo4j_config_t *config = (const neo4j_config_t *)userdata;
if (config->tls_pem_pw_callback != NULL)
{
return 0;
}
return config->tls_pem_pw_callback(config->tls_pem_pw_callback_userdata,
buf, size);
}
int load_certificate_authorities(SSL_CTX *ctx, const neo4j_config_t *config,
neo4j_logger_t *logger)
{
const char *ca_file = config->tls_ca_file;
const char *ca_dir = config->tls_ca_dir;
if (ca_file == NULL && ca_dir == NULL)
{
return 0;
}
if (SSL_CTX_load_verify_locations(ctx, ca_file, ca_dir) != 1)
{
errno = openssl_error(logger, NEO4J_LOG_ERROR, __FILE__, __LINE__);
return -1;
}
return 0;
}
int verify(SSL *ssl, const char *hostname, int port,
const neo4j_config_t *config, uint_fast32_t flags,
neo4j_logger_t *logger)
{
X509 *cert = SSL_get_peer_certificate(ssl);
if (cert == NULL)
{
neo4j_log_error(logger, "Server did not present a TLS certificate");
errno = NEO4J_TLS_VERIFICATION_FAILED;
return -1;
}
( run in 0.706 second using v1.01-cache-2.11-cpan-39bf76dae61 )