Skip to content

Commit

Permalink
TLS: Fix DH applying parameters, and check SSL version for ciphersuites
Browse files Browse the repository at this point in the history
  • Loading branch information
attipaci committed Jan 9, 2025
1 parent b03a5e7 commit b3493f2
Showing 1 changed file with 125 additions and 58 deletions.
183 changes: 125 additions & 58 deletions src/redisx-tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "redisx-priv.h"

#if WITH_TLS
# include <openssl/decoder.h> // For DH parameters
# include <openssl/err.h>
#endif

Expand Down Expand Up @@ -41,6 +42,59 @@ void rDestroyClientTLS(ClientPrivate *cp) {
}
}

/**
* Loads parameters from a file for DH-based ciphers.
*
* Based on https://github.com/openssl/openssl/blob/master/apps/dhparam.c
*
* @param ctx The SSL context for which to set DH parameters
* @param filename The DH parameter filename (in PEM format)
* @return X_SUCCESS (0) if successful, or else an error code &lt;0.
*
* @sa rConnectTLSClient()
*/
static int rSetSHParamsFromFile(SSL_CTX *ctx, const char *filename) {
static const char *fn = "rSetSHParamsFromFile";

BIO *in;
OSSL_DECODER_CTX *dctx = NULL;
EVP_PKEY *pkey = NULL;
int status = X_SUCCESS;

in = BIO_new_file(filename, "rb");
if(!in) return x_error(0, errno, fn, "Could not open DH parameters: %s", filename);

dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "PEM", NULL, NULL, OSSL_KEYMGMT_SELECT_DOMAIN_PARAMETERS, NULL, NULL);
if(!dctx) {
status = x_error(X_FAILURE, errno, fn, "Failed to create decoder context");
goto cleanup; // @suppress("Goto statement used")
}

if(!OSSL_DECODER_from_bio(dctx, in)) {
status = x_error(X_FAILURE, errno, fn, "Could not decode DH parameters from: %s", filename);
goto cleanup; // @suppress("Goto statement used")
}

if (!EVP_PKEY_is_a(pkey, "DH")) {
status = x_error(X_FAILURE, errno, fn, "Invalid DH parameters in: %s", filename);
goto cleanup; // @suppress("Goto statement used")
}

// The call references the key, does nor copy it. Therefore we should not free it
// if successful. The set key is freed then later when the context is freed.
if(!SSL_CTX_set0_tmp_dh_pkey(ctx, pkey)) {
EVP_PKEY_free(pkey);
status = x_error(X_FAILURE, errno, fn, "Failed to set DH parameters");
}

cleanup:

if(dctx) OSSL_DECODER_CTX_free(dctx);
BIO_free(in);

return status;
}

/**
* Connects a client using the specified TLS configuration.
*
Expand Down Expand Up @@ -111,13 +165,19 @@ int rConnectTLSClient(ClientPrivate *cp, const TLSConfig *tls) {
goto abort; // @suppress("Goto statement used")
}

#if OPENSSL_VERSION_NUMBER >= 0x1010100f
// Since OpenSSL version 1.1.1
if(tls->cipher_suites) if(!SSL_CTX_set_ciphersuites(cp->ctx, tls->cipher_suites)) {
x_error(0, errno, fn, "Failed to set ciphers= suites %s", tls->ciphers);
goto abort; // @suppress("Goto statement used")
}
#endif

if(tls->dh_params) if(!SSL_CTX_set_tmp_dh(cp->ctx, tls->dh_params)) {
x_error(0, errno, fn, "Failed to set DH-based cypher parameters from: %s", tls->dh_params);
if(tls->dh_params) {
if(rSetSHParamsFromFile(cp->ctx, tls->dh_params) != X_SUCCESS) goto abort; // @suppress("Goto statement used")
}
else if(!SSL_CTX_set_dh_auto(cp->ctx, 1)) {
x_error(0, errno, fn, "Failed to set automatic DH-based cypher parameters");
goto abort; // @suppress("Goto statement used")
}

Expand Down Expand Up @@ -282,26 +342,26 @@ int redisxSetMutualTLS(Redis *redis, const char *cert_file, const char *key_file
int redisxSetTLSCiphers(Redis *redis, const char *cipher_list) {
static const char *fn = "redisxSetTLSCiphers";

#if WITH_TLS
RedisPrivate *p;
TLSConfig *tls;
#if WITH_TLS
RedisPrivate *p;
TLSConfig *tls;

prop_error(fn, rConfigLock(redis));
prop_error(fn, rConfigLock(redis));

p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;
p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;

tls->ciphers = xStringCopyOf(cipher_list);
tls->ciphers = xStringCopyOf(cipher_list);

rConfigUnlock(redis);
rConfigUnlock(redis);

return X_SUCCESS;
#else
(void) redis;
(void) ca_file;
return X_SUCCESS;
#else
(void) redis;
(void) cipher_list;

return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
}

/**
Expand All @@ -318,26 +378,34 @@ int redisxSetTLSCiphers(Redis *redis, const char *cipher_list) {
int redisxSetTLSCipherSuites(Redis *redis, const char *list) {
static const char *fn = "redisxSetTLSCiphers";

#if WITH_TLS
RedisPrivate *p;
TLSConfig *tls;
#if WITH_TLS
# if OPENSSL_VERSION_NUMBER >= 0x1010100f
// Since OpenSSL version 1.1.1
RedisPrivate *p;
TLSConfig *tls;

prop_error(fn, rConfigLock(redis));

prop_error(fn, rConfigLock(redis));
p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;

p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;
tls->cipher_suites = xStringCopyOf(list);

tls->cipher_suites = xStringCopyOf(list);
rConfigUnlock(redis);

rConfigUnlock(redis);
return X_SUCCESS;
# else
(void) redis;
(void) list;

return X_SUCCESS;
#else
(void) redis;
(void) ca_file;
return x_error(X_FAILURE, ENOSYS, fn, "Needs OpenSSL >= 1.1.1 (for TLSv1.3+)");
# endif // OpenSSL >= 1.1.1
#else
(void) redis;
(void) list;

return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
}

/**
Expand Down Expand Up @@ -389,26 +457,26 @@ int redisxSetDHCipherParams(Redis *redis, const char *dh_params_file) {
int redisxSetTLSServerName(Redis *redis, const char *host) {
static const char *fn = "redisxSetDHCipherParams";

#if WITH_TLS
RedisPrivate *p;
TLSConfig *tls;
#if WITH_TLS
RedisPrivate *p;
TLSConfig *tls;

prop_error(fn, rConfigLock(redis));
prop_error(fn, rConfigLock(redis));

p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;
p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;

tls->hostname = xStringCopyOf(host);
tls->hostname = xStringCopyOf(host);

rConfigUnlock(redis);
rConfigUnlock(redis);

return X_SUCCESS;
#else
(void) redis;
(void) host;
return X_SUCCESS;
#else
(void) redis;
(void) host;

return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
}

/**
Expand All @@ -423,24 +491,23 @@ int redisxSetTLSServerName(Redis *redis, const char *host) {
int redisxSetTLSSkipVerify(Redis *redis, boolean value) {
static const char *fn = "redisxTLSSkipVerify";

#if WITH_TLS
RedisPrivate *p;
TLSConfig *tls;
#if WITH_TLS
RedisPrivate *p;
TLSConfig *tls;

prop_error(fn, rConfigLock(redis));
prop_error(fn, rConfigLock(redis));

p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;
p = (RedisPrivate *) redis->priv;
tls = &p->config.tls;

tls->skip_verify = (value != 0);
tls->skip_verify = (value != 0);

rConfigUnlock(redis);
rConfigUnlock(redis);

return X_SUCCESS;
#else
(void) redis;
(void) host;
return X_SUCCESS;
#else
(void) redis;

return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
return x_error(X_FAILURE, ENOSYS, fn, "RedisX was built without TLS support");
#endif
}

0 comments on commit b3493f2

Please sign in to comment.