summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/connection.h1
-rw-r--r--src/module.c26
-rw-r--r--src/redismodule.h2
-rw-r--r--src/tls.c30
-rw-r--r--tests/modules/misc.c19
-rw-r--r--tests/unit/moduleapi/misc.tcl10
6 files changed, 88 insertions, 0 deletions
diff --git a/src/connection.h b/src/connection.h
index e00d2ea17..03281a3d9 100644
--- a/src/connection.h
+++ b/src/connection.h
@@ -230,6 +230,7 @@ int connSockName(connection *conn, char *ip, size_t ip_len, int *port);
const char *connGetInfo(connection *conn, char *buf, size_t buf_len);
/* Helpers for tls special considerations */
+sds connTLSGetPeerCert(connection *conn);
int tlsHasPendingData();
int tlsProcessPendingData();
diff --git a/src/module.c b/src/module.c
index ef39d5d3a..7b4b761fe 100644
--- a/src/module.c
+++ b/src/module.c
@@ -5737,6 +5737,31 @@ int RM_DeauthenticateAndCloseClient(RedisModuleCtx *ctx, uint64_t client_id) {
return REDISMODULE_OK;
}
+/* Return the X.509 client-side certificate used by the client to authenticate
+ * this connection.
+ *
+ * The return value is an allocated RedisModuleString that is a X.509 certificate
+ * encoded in PEM (Base64) format. It should be freed (or auto-freed) by the caller.
+ *
+ * A NULL value is returned in the following conditions:
+ *
+ * - Connection ID does not exist
+ * - Connection is not a TLS connection
+ * - Connection is a TLS connection but no client ceritifcate was used
+ */
+RedisModuleString *RM_GetClientCertificate(RedisModuleCtx *ctx, uint64_t client_id) {
+ client *c = lookupClientByID(client_id);
+ if (c == NULL) return NULL;
+
+ sds cert = connTLSGetPeerCert(c->conn);
+ if (!cert) return NULL;
+
+ RedisModuleString *s = createObject(OBJ_STRING, cert);
+ if (ctx != NULL) autoMemoryAdd(ctx, REDISMODULE_AM_STRING, s);
+
+ return s;
+}
+
/* --------------------------------------------------------------------------
* Modules Dictionary API
*
@@ -8202,5 +8227,6 @@ void moduleRegisterCoreAPI(void) {
REGISTER_API(DeauthenticateAndCloseClient);
REGISTER_API(AuthenticateClientWithACLUser);
REGISTER_API(AuthenticateClientWithUser);
+ REGISTER_API(GetClientCertificate);
REGISTER_API(GetCommandKeys);
}
diff --git a/src/redismodule.h b/src/redismodule.h
index 092ad3914..7b3545585 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -727,6 +727,7 @@ REDISMODULE_API int (*RedisModule_SetModuleUserACL)(RedisModuleUser *user, const
REDISMODULE_API int (*RedisModule_AuthenticateClientWithACLUser)(RedisModuleCtx *ctx, const char *name, size_t len, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_AuthenticateClientWithUser)(RedisModuleCtx *ctx, RedisModuleUser *user, RedisModuleUserChangedFunc callback, void *privdata, uint64_t *client_id) REDISMODULE_ATTR;
REDISMODULE_API int (*RedisModule_DeauthenticateAndCloseClient)(RedisModuleCtx *ctx, uint64_t client_id) REDISMODULE_ATTR;
+REDISMODULE_API RedisModuleString * (*RedisModule_GetClientCertificate)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR;
REDISMODULE_API int *(*RedisModule_GetCommandKeys)(RedisModuleCtx *ctx, const char *cmdname, RedisModuleString **argv, int argc, int *num_keys) REDISMODULE_ATTR;
#endif
@@ -970,6 +971,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
REDISMODULE_GET_API(DeauthenticateAndCloseClient);
REDISMODULE_GET_API(AuthenticateClientWithACLUser);
REDISMODULE_GET_API(AuthenticateClientWithUser);
+ REDISMODULE_GET_API(GetClientCertificate);
REDISMODULE_GET_API(GetCommandKeys);
#endif
diff --git a/src/tls.c b/src/tls.c
index 0a9e07895..25580a4f1 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -37,6 +37,7 @@
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
+#include <openssl/pem.h>
#define REDIS_TLS_PROTO_TLSv1 (1<<0)
#define REDIS_TLS_PROTO_TLSv1_1 (1<<1)
@@ -868,6 +869,30 @@ int tlsProcessPendingData() {
return processed;
}
+/* Fetch the peer certificate used for authentication on the specified
+ * connection and return it as a PEM-encoded sds.
+ */
+sds connTLSGetPeerCert(connection *conn_) {
+ tls_connection *conn = (tls_connection *) conn_;
+ if (conn_->type->get_type(conn_) != CONN_TYPE_TLS || !conn->ssl) return NULL;
+
+ X509 *cert = SSL_get_peer_certificate(conn->ssl);
+ if (!cert) return NULL;
+
+ BIO *bio = BIO_new(BIO_s_mem());
+ if (bio == NULL || !PEM_write_bio_X509(bio, cert)) {
+ if (bio != NULL) BIO_free(bio);
+ return NULL;
+ }
+
+ const char *bio_ptr;
+ long long bio_len = BIO_get_mem_data(bio, &bio_ptr);
+ sds cert_pem = sdsnewlen(bio_ptr, bio_len);
+ BIO_free(bio);
+
+ return cert_pem;
+}
+
#else /* USE_OPENSSL */
void tlsInit(void) {
@@ -897,4 +922,9 @@ int tlsProcessPendingData() {
return 0;
}
+sds connTLSGetPeerCert(connection *conn_) {
+ (void) conn_;
+ return NULL;
+}
+
#endif
diff --git a/tests/modules/misc.c b/tests/modules/misc.c
index a43bb84ac..621b92b2d 100644
--- a/tests/modules/misc.c
+++ b/tests/modules/misc.c
@@ -195,6 +195,23 @@ int test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_OK;
}
+int test_getclientcert(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
+{
+ (void) argv;
+ (void) argc;
+
+ RedisModuleString *cert = RedisModule_GetClientCertificate(ctx,
+ RedisModule_GetClientId(ctx));
+ if (!cert) {
+ RedisModule_ReplyWithNull(ctx);
+ } else {
+ RedisModule_ReplyWithString(ctx, cert);
+ RedisModule_FreeString(ctx, cert);
+ }
+
+ return REDISMODULE_OK;
+}
+
int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
{
(void) argv;
@@ -283,6 +300,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
+ if (RedisModule_CreateCommand(ctx,"test.getclientcert", test_getclientcert,"",0,0,0) == REDISMODULE_ERR)
+ return REDISMODULE_ERR;
if (RedisModule_CreateCommand(ctx,"test.log_tsctx", test_log_tsctx,"",0,0,0) == REDISMODULE_ERR)
return REDISMODULE_ERR;
diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl
index 2c9ac4fd0..e825f33ff 100644
--- a/tests/unit/moduleapi/misc.tcl
+++ b/tests/unit/moduleapi/misc.tcl
@@ -87,6 +87,16 @@ start_server {tags {"modules"}} {
assert { [dict get $info flags] == "${ssl_flag}::tracking::" }
}
+ test {test module getclientcert api} {
+ set cert [r test.getclientcert]
+
+ if {$::tls} {
+ assert {$cert != ""}
+ } else {
+ assert {$cert == ""}
+ }
+ }
+
test {test detached thread safe cnotext} {
r test.log_tsctx "info" "Test message"
verify_log_message 0 "*<misc> Test message*" 0