Redis-Fast

 view release on metacpan or  search on metacpan

deps/hiredis/ssl.c  view on Meta::CPAN

    SSL_library_init();
#ifdef HIREDIS_USE_CRYPTO_LOCKS
    initOpensslLocks();
#endif

    return REDIS_OK;
}

/**
 * redisSSLContext helper context destruction.
 */

const char *redisSSLContextGetError(redisSSLContextError error)
{
    switch (error) {
        case REDIS_SSL_CTX_NONE:
            return "No Error";
        case REDIS_SSL_CTX_CREATE_FAILED:
            return "Failed to create OpenSSL SSL_CTX";
        case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
            return "Client cert and key must both be specified or skipped";
        case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
            return "Failed to load CA Certificate or CA Path";
        case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
            return "Failed to load client certificate";
        case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
            return "Failed to load private key";
        case REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED:
            return "Failed to open system certificate store";
        case REDIS_SSL_CTX_OS_CERT_ADD_FAILED:
            return "Failed to add CA certificates obtained from system to the SSL context";
        default:
            return "Unknown error code";
    }
}

void redisFreeSSLContext(redisSSLContext *ctx)
{
    if (!ctx)
        return;

    if (ctx->server_name) {
        hi_free(ctx->server_name);
        ctx->server_name = NULL;
    }

    if (ctx->ssl_ctx) {
        SSL_CTX_free(ctx->ssl_ctx);
        ctx->ssl_ctx = NULL;
    }

    hi_free(ctx);
}


/**
 * redisSSLContext helper context initialization.
 */

redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
        const char *cert_filename, const char *private_key_filename,
        const char *server_name, redisSSLContextError *error)
{
    redisSSLOptions options = {
        .cacert_filename = cacert_filename,
        .capath = capath,
        .cert_filename = cert_filename,
        .private_key_filename = private_key_filename,
        .server_name = server_name,
        .verify_mode = REDIS_SSL_VERIFY_PEER,
    };

    return redisCreateSSLContextWithOptions(&options, error);
}

redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, redisSSLContextError *error) {
    const char *cacert_filename = options->cacert_filename;
    const char *capath = options->capath;
    const char *cert_filename = options->cert_filename;
    const char *private_key_filename = options->private_key_filename;
    const char *server_name = options->server_name;

#ifdef _WIN32
    HCERTSTORE win_store = NULL;
    PCCERT_CONTEXT win_ctx = NULL;
#endif

    redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
    if (ctx == NULL)
        goto error;

    const SSL_METHOD *ssl_method;
#if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
    ssl_method = TLS_client_method();
#else
    ssl_method = SSLv23_client_method();
#endif

    ctx->ssl_ctx = SSL_CTX_new(ssl_method);
    if (!ctx->ssl_ctx) {
        if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
        goto error;
    }

#if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
    SSL_CTX_set_min_proto_version(ctx->ssl_ctx, TLS1_2_VERSION);
#else
    SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
#endif

    SSL_CTX_set_verify(ctx->ssl_ctx, options->verify_mode, NULL);

    if ((cert_filename != NULL && private_key_filename == NULL) ||
            (private_key_filename != NULL && cert_filename == NULL)) {
        if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
        goto error;
    }

    if (capath || cacert_filename) {
#ifdef _WIN32
        if (0 == strcmp(cacert_filename, "wincert")) {
            win_store = CertOpenSystemStore(NULL, "Root");
            if (!win_store) {
                if (error) *error = REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED;
                goto error;
            }
            X509_STORE* store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
            while (win_ctx = CertEnumCertificatesInStore(win_store, win_ctx)) {
                X509* x509 = NULL;
                x509 = d2i_X509(NULL, (const unsigned char**)&win_ctx->pbCertEncoded, win_ctx->cbCertEncoded);
                if (x509) {
                    if ((1 != X509_STORE_add_cert(store, x509)) ||
                        (1 != SSL_CTX_add_client_CA(ctx->ssl_ctx, x509)))
                    {
                        if (error) *error = REDIS_SSL_CTX_OS_CERT_ADD_FAILED;
                        goto error;
                    }
                    X509_free(x509);
                }
            }
            CertFreeCertificateContext(win_ctx);
            CertCloseStore(win_store, 0);
        } else
#endif
        if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
            if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
            goto error;
        }
    } else {
        if (!SSL_CTX_set_default_verify_paths(ctx->ssl_ctx)) {
            if (error) *error = REDIS_SSL_CTX_CLIENT_DEFAULT_CERT_FAILED;
            goto error;
        }
    }

    if (cert_filename) {
        if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
            if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
            goto error;
        }
        if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
            if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
            goto error;
        }
    }

    if (server_name)
        ctx->server_name = hi_strdup(server_name);

    return ctx;

error:
#ifdef _WIN32
    CertFreeCertificateContext(win_ctx);
    CertCloseStore(win_store, 0);
#endif
    redisFreeSSLContext(ctx);
    return NULL;
}

/**
 * SSL Connection initialization.
 */


static int redisSSLConnect(redisContext *c, SSL *ssl) {
    if (c->privctx) {
        __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
        return REDIS_ERR;
    }

    redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
    if (rssl == NULL) {
        __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
        return REDIS_ERR;
    }

    c->funcs = &redisContextSSLFuncs;
    rssl->ssl = ssl;

    SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
    SSL_set_fd(rssl->ssl, c->fd);
    SSL_set_connect_state(rssl->ssl);

    ERR_clear_error();
    int rv = SSL_connect(rssl->ssl);
    if (rv == 1) {
        c->privctx = rssl;
        return REDIS_OK;
    }

    rv = SSL_get_error(rssl->ssl, rv);
    if (((c->flags & REDIS_BLOCK) == 0) &&
        (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
        c->privctx = rssl;
        return REDIS_OK;
    }

    if (c->err == 0) {
        char err[512];
        if (rv == SSL_ERROR_SYSCALL)



( run in 3.575 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )