Module API: Add RM_GetClientCertificate(). (#7866)
This API function makes it possible to retrieve the X.509 certificate used by clients to authenticate TLS connections. (cherry picked from commit 929f1e2ec73c147251d693631a746baaaf5d4127)
This commit is contained in:
parent
298808cf32
commit
02edabc9aa
@ -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);
|
const char *connGetInfo(connection *conn, char *buf, size_t buf_len);
|
||||||
|
|
||||||
/* Helpers for tls special considerations */
|
/* Helpers for tls special considerations */
|
||||||
|
sds connTLSGetPeerCert(connection *conn);
|
||||||
int tlsHasPendingData();
|
int tlsHasPendingData();
|
||||||
int tlsProcessPendingData();
|
int tlsProcessPendingData();
|
||||||
|
|
||||||
|
26
src/module.c
26
src/module.c
@ -5736,6 +5736,31 @@ int RM_DeauthenticateAndCloseClient(RedisModuleCtx *ctx, uint64_t client_id) {
|
|||||||
return REDISMODULE_OK;
|
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
|
* Modules Dictionary API
|
||||||
*
|
*
|
||||||
@ -8194,5 +8219,6 @@ void moduleRegisterCoreAPI(void) {
|
|||||||
REGISTER_API(DeauthenticateAndCloseClient);
|
REGISTER_API(DeauthenticateAndCloseClient);
|
||||||
REGISTER_API(AuthenticateClientWithACLUser);
|
REGISTER_API(AuthenticateClientWithACLUser);
|
||||||
REGISTER_API(AuthenticateClientWithUser);
|
REGISTER_API(AuthenticateClientWithUser);
|
||||||
|
REGISTER_API(GetClientCertificate);
|
||||||
REGISTER_API(GetCommandKeys);
|
REGISTER_API(GetCommandKeys);
|
||||||
}
|
}
|
||||||
|
@ -725,6 +725,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_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_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 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;
|
REDISMODULE_API int *(*RedisModule_GetCommandKeys)(RedisModuleCtx *ctx, const char *cmdname, RedisModuleString **argv, int argc, int *num_keys) REDISMODULE_ATTR;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -968,6 +969,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
|||||||
REDISMODULE_GET_API(DeauthenticateAndCloseClient);
|
REDISMODULE_GET_API(DeauthenticateAndCloseClient);
|
||||||
REDISMODULE_GET_API(AuthenticateClientWithACLUser);
|
REDISMODULE_GET_API(AuthenticateClientWithACLUser);
|
||||||
REDISMODULE_GET_API(AuthenticateClientWithUser);
|
REDISMODULE_GET_API(AuthenticateClientWithUser);
|
||||||
|
REDISMODULE_GET_API(GetClientCertificate);
|
||||||
REDISMODULE_GET_API(GetCommandKeys);
|
REDISMODULE_GET_API(GetCommandKeys);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
30
src/tls.c
30
src/tls.c
@ -37,6 +37,7 @@
|
|||||||
#include <openssl/ssl.h>
|
#include <openssl/ssl.h>
|
||||||
#include <openssl/err.h>
|
#include <openssl/err.h>
|
||||||
#include <openssl/rand.h>
|
#include <openssl/rand.h>
|
||||||
|
#include <openssl/pem.h>
|
||||||
|
|
||||||
#define REDIS_TLS_PROTO_TLSv1 (1<<0)
|
#define REDIS_TLS_PROTO_TLSv1 (1<<0)
|
||||||
#define REDIS_TLS_PROTO_TLSv1_1 (1<<1)
|
#define REDIS_TLS_PROTO_TLSv1_1 (1<<1)
|
||||||
@ -868,6 +869,30 @@ int tlsProcessPendingData() {
|
|||||||
return processed;
|
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 */
|
#else /* USE_OPENSSL */
|
||||||
|
|
||||||
void tlsInit(void) {
|
void tlsInit(void) {
|
||||||
@ -897,4 +922,9 @@ int tlsProcessPendingData() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sds connTLSGetPeerCert(connection *conn_) {
|
||||||
|
(void) conn_;
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -195,6 +195,23 @@ int test_setlfu(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|||||||
return REDISMODULE_OK;
|
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)
|
int test_clientinfo(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
||||||
{
|
{
|
||||||
(void) argv;
|
(void) argv;
|
||||||
@ -283,6 +300,8 @@ int RedisModule_OnLoad(RedisModuleCtx *ctx, RedisModuleString **argv, int argc)
|
|||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
|
if (RedisModule_CreateCommand(ctx,"test.clientinfo", test_clientinfo,"",0,0,0) == REDISMODULE_ERR)
|
||||||
return 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)
|
if (RedisModule_CreateCommand(ctx,"test.log_tsctx", test_log_tsctx,"",0,0,0) == REDISMODULE_ERR)
|
||||||
return REDISMODULE_ERR;
|
return REDISMODULE_ERR;
|
||||||
|
|
||||||
|
@ -87,6 +87,16 @@ start_server {tags {"modules"}} {
|
|||||||
assert { [dict get $info flags] == "${ssl_flag}::tracking::" }
|
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} {
|
test {test detached thread safe cnotext} {
|
||||||
r test.log_tsctx "info" "Test message"
|
r test.log_tsctx "info" "Test message"
|
||||||
verify_log_message 0 "*<misc> Test message*" 0
|
verify_log_message 0 "*<misc> Test message*" 0
|
||||||
|
Loading…
x
Reference in New Issue
Block a user