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 )