summaryrefslogtreecommitdiff
path: root/src/redis-cli.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/redis-cli.c')
-rw-r--r--src/redis-cli.c132
1 files changed, 129 insertions, 3 deletions
diff --git a/src/redis-cli.c b/src/redis-cli.c
index 2830273bb..6d07f7ba6 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -47,6 +47,10 @@
#include <math.h>
#include <hiredis.h>
+#ifdef USE_OPENSSL
+#include <openssl/ssl.h>
+#include <hiredis_ssl.h>
+#endif
#include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
#include "dict.h"
#include "adlist.h"
@@ -188,6 +192,12 @@ static struct config {
char *hostip;
int hostport;
char *hostsocket;
+ int tls;
+ char *sni;
+ char *cacert;
+ char *cacertdir;
+ char *cert;
+ char *key;
long repeat;
long interval;
int dbnum;
@@ -758,6 +768,71 @@ static int cliSelect(void) {
return REDIS_ERR;
}
+/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if
+ * not building with TLS support.
+ */
+static int cliSecureConnection(redisContext *c, const char **err) {
+#ifdef USE_OPENSSL
+ static SSL_CTX *ssl_ctx = NULL;
+
+ if (!ssl_ctx) {
+ ssl_ctx = SSL_CTX_new(SSLv23_client_method());
+ if (!ssl_ctx) {
+ *err = "Failed to create SSL_CTX";
+ goto error;
+ }
+
+ SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
+
+ if (config.cacert || config.cacertdir) {
+ if (!SSL_CTX_load_verify_locations(ssl_ctx, config.cacert, config.cacertdir)) {
+ *err = "Invalid CA Certificate File/Directory";
+ goto error;
+ }
+ } else {
+ if (!SSL_CTX_set_default_verify_paths(ssl_ctx)) {
+ *err = "Failed to use default CA paths";
+ goto error;
+ }
+ }
+
+ if (config.cert && !SSL_CTX_use_certificate_chain_file(ssl_ctx, config.cert)) {
+ *err = "Invalid client certificate";
+ goto error;
+ }
+
+ if (config.key && !SSL_CTX_use_PrivateKey_file(ssl_ctx, config.key, SSL_FILETYPE_PEM)) {
+ *err = "Invalid private key";
+ goto error;
+ }
+ }
+
+ SSL *ssl = SSL_new(ssl_ctx);
+ if (!ssl) {
+ *err = "Failed to create SSL object";
+ return REDIS_ERR;
+ }
+
+ if (config.sni && !SSL_set_tlsext_host_name(ssl, config.sni)) {
+ *err = "Failed to configure SNI";
+ SSL_free(ssl);
+ return REDIS_ERR;
+ }
+
+ return redisInitiateSSL(c, ssl);
+
+error:
+ SSL_CTX_free(ssl_ctx);
+ ssl_ctx = NULL;
+ return REDIS_ERR;
+#else
+ (void) c;
+ (void) err;
+ return REDIS_OK;
+#endif
+}
+
/* Select RESP3 mode if redis-cli was started with the -3 option. */
static int cliSwitchProto(void) {
redisReply *reply;
@@ -789,6 +864,16 @@ static int cliConnect(int flags) {
context = redisConnectUnix(config.hostsocket);
}
+ if (!context->err && config.tls) {
+ const char *err = NULL;
+ if (cliSecureConnection(context, &err) == REDIS_ERR && err) {
+ fprintf(stderr, "Could not negotiate a TLS connection: %s\n", err);
+ context = NULL;
+ redisFree(context);
+ return REDIS_ERR;
+ }
+ }
+
if (context->err) {
if (!(flags & CC_QUIET)) {
fprintf(stderr,"Could not connect to Redis at ");
@@ -804,6 +889,7 @@ static int cliConnect(int flags) {
return REDIS_ERR;
}
+
/* Set aggressive KEEP_ALIVE socket option in the Redis context socket
* in order to prevent timeouts caused by the execution of long
* commands. At the same time this improves the detection of real
@@ -1305,6 +1391,13 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ..
redisFree(c);
c = redisConnect(config.hostip,config.hostport);
+ if (!c->err && config.tls) {
+ const char *err = NULL;
+ if (cliSecureConnection(c, &err) == REDIS_ERR && err) {
+ fprintf(stderr, "TLS Error: %s\n", err);
+ exit(1);
+ }
+ }
usleep(1000000);
}
@@ -1498,6 +1591,20 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"--cluster-search-multiple-owners")) {
config.cluster_manager_command.flags |=
CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS;
+#ifdef USE_OPENSSL
+ } else if (!strcmp(argv[i],"--tls")) {
+ config.tls = 1;
+ } else if (!strcmp(argv[i],"--sni")) {
+ config.sni = argv[++i];
+ } else if (!strcmp(argv[i],"--cacertdir")) {
+ config.cacertdir = argv[++i];
+ } else if (!strcmp(argv[i],"--cacert")) {
+ config.cacert = argv[++i];
+ } else if (!strcmp(argv[i],"--cert")) {
+ config.cert = argv[++i];
+ } else if (!strcmp(argv[i],"--key")) {
+ config.key = argv[++i];
+#endif
} else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
sds version = cliVersion();
printf("redis-cli %s\n", version);
@@ -1591,6 +1698,15 @@ static void usage(void) {
" -x Read last argument from STDIN.\n"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default: \\n).\n"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).\n"
+#ifdef USE_OPENSSL
+" --tls Establish a secure TLS connection.\n"
+" --cacert CA Certificate file to verify with.\n"
+" --cacertdir Directory where trusted CA certificates are stored.\n"
+" If neither cacert nor cacertdir are specified, the default\n"
+" system-wide trusted root certs configuration will apply.\n"
+" --cert Client certificate to authenticate with.\n"
+" --key Private key file to authenticate with.\n"
+#endif
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n"
" --no-raw Force formatted output even when STDOUT is not a tty.\n"
@@ -1615,7 +1731,9 @@ static void usage(void) {
" --pipe Transfer raw Redis protocol from stdin to server.\n"
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n"
" no reply is received within <n> seconds.\n"
-" Default timeout: %d. Use 0 to wait forever.\n"
+" Default timeout: %d. Use 0 to wait forever.\n",
+ REDIS_CLI_DEFAULT_PIPE_TIMEOUT);
+ fprintf(stderr,
" --bigkeys Sample Redis keys looking for keys with many elements (complexity).\n"
" --memkeys Sample Redis keys looking for keys consuming a lot of memory.\n"
" --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.\n"
@@ -1638,8 +1756,7 @@ static void usage(void) {
" line interface.\n"
" --help Output this help and exit.\n"
" --version Output version and exit.\n"
-"\n",
- REDIS_CLI_DEFAULT_PIPE_TIMEOUT);
+"\n");
/* Using another fprintf call to avoid -Woverlength-strings compile warning */
fprintf(stderr,
"Cluster Manager Commands:\n"
@@ -2407,6 +2524,15 @@ cleanup:
static int clusterManagerNodeConnect(clusterManagerNode *node) {
if (node->context) redisFree(node->context);
node->context = redisConnect(node->ip, node->port);
+ if (!node->context->err && config.tls) {
+ const char *err = NULL;
+ if (cliSecureConnection(node->context, &err) == REDIS_ERR && err) {
+ fprintf(stderr,"TLS Error: %s\n", err);
+ redisFree(node->context);
+ node->context = NULL;
+ return 0;
+ }
+ }
if (node->context->err) {
fprintf(stderr,"Could not connect to Redis at ");
fprintf(stderr,"%s:%d: %s\n", node->ip, node->port,