diff options
author | Yossi Gottlieb <yossigo@gmail.com> | 2020-12-11 18:31:40 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-12-11 18:31:40 +0200 |
commit | 8c291b97b95f2e011977b522acf77ead23e26f55 (patch) | |
tree | 14935b675574e1f8f2cc79f90219de537c8fc0f0 /src/tls.c | |
parent | 4e064fbab4d310b508593b46ed6ce539aea7aa25 (diff) | |
download | redis-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.c | 162 |
1 files changed, 100 insertions, 62 deletions
@@ -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; |