summaryrefslogtreecommitdiff
path: root/src/tls.c
diff options
context:
space:
mode:
authorYossi Gottlieb <yossigo@gmail.com>2020-12-11 18:31:40 +0200
committerGitHub <noreply@github.com>2020-12-11 18:31:40 +0200
commit8c291b97b95f2e011977b522acf77ead23e26f55 (patch)
tree14935b675574e1f8f2cc79f90219de537c8fc0f0 /src/tls.c
parent4e064fbab4d310b508593b46ed6ce539aea7aa25 (diff)
downloadredis-8c291b97b95f2e011977b522acf77ead23e26f55.tar.gz
TLS: Add different client cert support. (#8076)
This adds a new `tls-client-cert-file` and `tls-client-key-file` configuration directives which make it possible to use different certificates for the TLS-server and TLS-client functions of Redis. This is an optional directive. If it is not specified the `tls-cert-file` and `tls-key-file` directives are used for TLS client functions as well. Also, `utils/gen-test-certs.sh` now creates additional server-only and client-only certs and will skip intensive operations if target files already exist.
Diffstat (limited to 'src/tls.c')
-rw-r--r--src/tls.c162
1 files changed, 100 insertions, 62 deletions
diff --git a/src/tls.c b/src/tls.c
index af16d2a41..caa987965 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -54,7 +54,8 @@
extern ConnectionType CT_Socket;
-SSL_CTX *redis_tls_ctx;
+SSL_CTX *redis_tls_ctx = NULL;
+SSL_CTX *redis_tls_client_ctx = NULL;
static int parseProtocolsConfig(const char *str) {
int i, count = 0;
@@ -163,50 +164,22 @@ void tlsInit(void) {
pending_list = listCreate();
}
-/* Attempt to configure/reconfigure TLS. This operation is atomic and will
- * leave the SSL_CTX unchanged if fails.
+/* Create a *base* SSL_CTX using the SSL configuration provided. The base context
+ * includes everything that's common for both client-side and server-side connections.
*/
-int tlsConfigure(redisTLSContextConfig *ctx_config) {
+static SSL_CTX *createSSLContext(redisTLSContextConfig *ctx_config, int protocols,
+ const char *cert_file, const char *key_file) {
char errbuf[256];
SSL_CTX *ctx = NULL;
- if (!ctx_config->cert_file) {
- serverLog(LL_WARNING, "No tls-cert-file configured!");
- goto error;
- }
-
- if (!ctx_config->key_file) {
- serverLog(LL_WARNING, "No tls-key-file configured!");
- goto error;
- }
-
- if (((server.tls_auth_clients != TLS_CLIENT_AUTH_NO) || server.tls_cluster || server.tls_replication) &&
- !ctx_config->ca_cert_file && !ctx_config->ca_cert_dir) {
- serverLog(LL_WARNING, "Either tls-ca-cert-file or tls-ca-cert-dir must be specified when tls-cluster, tls-replication or tls-auth-clients are enabled!");
- goto error;
- }
-
ctx = SSL_CTX_new(SSLv23_method());
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3);
- SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
SSL_CTX_set_options(ctx, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
#endif
- if (ctx_config->session_caching) {
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
- SSL_CTX_sess_set_cache_size(ctx, ctx_config->session_cache_size);
- SSL_CTX_set_timeout(ctx, ctx_config->session_cache_timeout);
- SSL_CTX_set_session_id_context(ctx, (void *) "redis", 5);
- } else {
- SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
- }
-
- int protocols = parseProtocolsConfig(ctx_config->protocols);
- if (protocols == -1) goto error;
-
if (!(protocols & REDIS_TLS_PROTO_TLSv1))
SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
if (!(protocols & REDIS_TLS_PROTO_TLSv1_1))
@@ -224,31 +197,21 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
SSL_CTX_set_options(ctx, SSL_OP_NO_COMPRESSION);
#endif
-#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION
- SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
-#endif
-
- if (ctx_config->prefer_server_ciphers)
- SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
-
SSL_CTX_set_mode(ctx, SSL_MODE_ENABLE_PARTIAL_WRITE|SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
-#if defined(SSL_CTX_set_ecdh_auto)
- SSL_CTX_set_ecdh_auto(ctx, 1);
-#endif
- if (SSL_CTX_use_certificate_chain_file(ctx, ctx_config->cert_file) <= 0) {
+ if (SSL_CTX_use_certificate_chain_file(ctx, cert_file) <= 0) {
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- serverLog(LL_WARNING, "Failed to load certificate: %s: %s", ctx_config->cert_file, errbuf);
+ serverLog(LL_WARNING, "Failed to load certificate: %s: %s", cert_file, errbuf);
goto error;
}
-
- if (SSL_CTX_use_PrivateKey_file(ctx, ctx_config->key_file, SSL_FILETYPE_PEM) <= 0) {
+
+ if (SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) <= 0) {
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
- serverLog(LL_WARNING, "Failed to load private key: %s: %s", ctx_config->key_file, errbuf);
+ serverLog(LL_WARNING, "Failed to load private key: %s: %s", key_file, errbuf);
goto error;
}
-
+
if ((ctx_config->ca_cert_file || ctx_config->ca_cert_dir) &&
SSL_CTX_load_verify_locations(ctx, ctx_config->ca_cert_file, ctx_config->ca_cert_dir) <= 0) {
ERR_error_string_n(ERR_get_error(), errbuf, sizeof(errbuf));
@@ -256,6 +219,77 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
goto error;
}
+ if (ctx_config->ciphers && !SSL_CTX_set_cipher_list(ctx, ctx_config->ciphers)) {
+ serverLog(LL_WARNING, "Failed to configure ciphers: %s", ctx_config->ciphers);
+ goto error;
+ }
+
+#ifdef TLS1_3_VERSION
+ if (ctx_config->ciphersuites && !SSL_CTX_set_ciphersuites(ctx, ctx_config->ciphersuites)) {
+ serverLog(LL_WARNING, "Failed to configure ciphersuites: %s", ctx_config->ciphersuites);
+ goto error;
+ }
+#endif
+
+ return ctx;
+
+error:
+ if (ctx) SSL_CTX_free(ctx);
+ return NULL;
+}
+
+/* Attempt to configure/reconfigure TLS. This operation is atomic and will
+ * leave the SSL_CTX unchanged if fails.
+ */
+int tlsConfigure(redisTLSContextConfig *ctx_config) {
+ char errbuf[256];
+ SSL_CTX *ctx = NULL;
+ SSL_CTX *client_ctx = NULL;
+
+ if (!ctx_config->cert_file) {
+ serverLog(LL_WARNING, "No tls-cert-file configured!");
+ goto error;
+ }
+
+ if (!ctx_config->key_file) {
+ serverLog(LL_WARNING, "No tls-key-file configured!");
+ goto error;
+ }
+
+ if (((server.tls_auth_clients != TLS_CLIENT_AUTH_NO) || server.tls_cluster || server.tls_replication) &&
+ !ctx_config->ca_cert_file && !ctx_config->ca_cert_dir) {
+ serverLog(LL_WARNING, "Either tls-ca-cert-file or tls-ca-cert-dir must be specified when tls-cluster, tls-replication or tls-auth-clients are enabled!");
+ goto error;
+ }
+
+ int protocols = parseProtocolsConfig(ctx_config->protocols);
+ if (protocols == -1) goto error;
+
+ /* Create server side/generla context */
+ ctx = createSSLContext(ctx_config, protocols, ctx_config->cert_file, ctx_config->key_file);
+ if (!ctx) goto error;
+
+ if (ctx_config->session_caching) {
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER);
+ SSL_CTX_sess_set_cache_size(ctx, ctx_config->session_cache_size);
+ SSL_CTX_set_timeout(ctx, ctx_config->session_cache_timeout);
+ SSL_CTX_set_session_id_context(ctx, (void *) "redis", 5);
+ } else {
+ SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_OFF);
+ }
+
+#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION
+ SSL_CTX_set_options(ctx, SSL_OP_NO_CLIENT_RENEGOTIATION);
+#endif
+
+ if (ctx_config->prefer_server_ciphers)
+ SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
+
+#if defined(SSL_CTX_set_ecdh_auto)
+ SSL_CTX_set_ecdh_auto(ctx, 1);
+#endif
+ SSL_CTX_set_options(ctx, SSL_OP_SINGLE_DH_USE);
+
if (ctx_config->dh_params_file) {
FILE *dhfile = fopen(ctx_config->dh_params_file, "r");
DH *dh = NULL;
@@ -281,25 +315,22 @@ int tlsConfigure(redisTLSContextConfig *ctx_config) {
DH_free(dh);
}
- if (ctx_config->ciphers && !SSL_CTX_set_cipher_list(ctx, ctx_config->ciphers)) {
- serverLog(LL_WARNING, "Failed to configure ciphers: %s", ctx_config->ciphers);
- goto error;
+ /* If a client-side certificate is configured, create an explicit client context */
+ if (ctx_config->client_cert_file && ctx_config->client_key_file) {
+ client_ctx = createSSLContext(ctx_config, protocols, ctx_config->client_cert_file, ctx_config->client_key_file);
+ if (!client_ctx) goto error;
}
-#ifdef TLS1_3_VERSION
- if (ctx_config->ciphersuites && !SSL_CTX_set_ciphersuites(ctx, ctx_config->ciphersuites)) {
- serverLog(LL_WARNING, "Failed to configure ciphersuites: %s", ctx_config->ciphersuites);
- goto error;
- }
-#endif
-
SSL_CTX_free(redis_tls_ctx);
+ SSL_CTX_free(redis_tls_client_ctx);
redis_tls_ctx = ctx;
+ redis_tls_client_ctx = client_ctx;
return C_OK;
error:
if (ctx) SSL_CTX_free(ctx);
+ if (client_ctx) SSL_CTX_free(client_ctx);
return C_ERR;
}
@@ -344,14 +375,21 @@ typedef struct tls_connection {
listNode *pending_list_node;
} tls_connection;
-connection *connCreateTLS(void) {
+static connection *createTLSConnection(bool client_side) {
+ SSL_CTX *ctx = redis_tls_ctx;
+ if (client_side && redis_tls_client_ctx)
+ ctx = redis_tls_client_ctx;
tls_connection *conn = zcalloc(sizeof(tls_connection));
conn->c.type = &CT_TLS;
conn->c.fd = -1;
- conn->ssl = SSL_new(redis_tls_ctx);
+ conn->ssl = SSL_new(ctx);
return (connection *) conn;
}
+connection *connCreateTLS(void) {
+ return createTLSConnection(true);
+}
+
/* Fetch the latest OpenSSL error and store it in the connection */
static void updateTLSError(tls_connection *conn) {
conn->c.last_errno = 0;
@@ -370,7 +408,7 @@ static void updateTLSError(tls_connection *conn) {
* is not in an error state.
*/
connection *connCreateAcceptedTLS(int fd, int require_auth) {
- tls_connection *conn = (tls_connection *) connCreateTLS();
+ tls_connection *conn = (tls_connection *) createTLSConnection(false);
conn->c.fd = fd;
conn->c.state = CONN_STATE_ACCEPTING;