Summary
redisCreateSSLContextWithOptions() documents cacert_filename and capath as independent optional CA sources. OpenSSL’s SSL_CTX_load_verify_locations(ctx, NULL, capath) is the intended way to load a CA directory without a bundle file.
On Windows the function enters the CA block with if (capath || cacert_filename) and then calls strcmp(cacert_filename, "wincert") with no NULL check. A caller who sets only capath (the portable OpenSSL pattern) therefore passes NULL to strcmp and crashes inside redisCreateSSLContextWithOptions, before any TLS handshake and before CertOpenSystemStore.
Non-Windows builds skip that strcmp and call SSL_CTX_load_verify_locations with a NULL file name, which is valid. The same redisSSLOptions therefore works on Linux/macOS and aborts on Windows.
This is not a general TLS vulnerability and does not affect "wincert" or a non-NULL cacert_filename. It is a missing NULL guard on the Windows-only "wincert" special case.
Present on 1.5.0-dev (29ea279), ssl.c.
Code
/* ssl.c — redisCreateSSLContextWithOptions */
const char *cacert_filename = options->cacert_filename;
const char *capath = options->capath;
...
if (capath || cacert_filename) {
#ifdef _WIN32
if (0 == strcmp(cacert_filename, "wincert")) { /* :373; cacert_filename may be NULL */
win_store = CertOpenSystemStore(NULL, "Root");
...
} else
#endif
if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
...
}
}
Public options (hiredis_ssl.h): both fields are optional. "wincert" is the Windows store sentinel; it is not required in order to use capath. redisCreateSSLContext(NULL, capath, ...) is the same call.
Reproduce
Windows PE linked against the in-tree ssl.c and OpenSSL. The crash is inside redisCreateSSLContextWithOptions after SSL_CTX_new succeeds, on the strcmp at ssl.c:373.
The I/O stubs below exist only so ssl.c links without the rest of libhiredis. They are not on the crashing path.
#include "hiredis.h"
#include "hiredis_ssl.h"
#include "async.h"
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
void __redisSetError(redisContext *c, int type, const char *str)
{ (void)c; (void)type; (void)str; }
void redisNetClose(redisContext *c) { (void)c; }
int redisBufferRead(redisContext *c) { (void)c; return REDIS_ERR; }
int redisBufferWrite(redisContext *c, int *done)
{ (void)c; (void)done; return REDIS_ERR; }
void __redisAsyncDisconnect(redisAsyncContext *ac) { (void)ac; }
void redisProcessCallbacks(redisAsyncContext *ac) { (void)ac; }
static void on_crash(int sig)
{
fprintf(stderr, "signal %d in redisCreateSSLContextWithOptions\n", sig);
fflush(stderr);
_Exit(0);
}
int main(void)
{
redisSSLContextError err = REDIS_SSL_CTX_NONE;
redisSSLOptions o = {0};
signal(SIGSEGV, on_crash);
o.capath = "C:\\certs";
o.cacert_filename = NULL;
o.verify_mode = REDIS_SSL_VERIFY_PEER;
redisCreateSSLContextWithOptions(&o, &err);
fprintf(stderr, "no crash, err=%d\n", (int)err);
return 1;
}
x86_64-w64-mingw32-gcc -O0 -g -I. -I$OPENSSL/include \
-o poc.exe poc.c ssl.c alloc.c \
$OPENSSL/lib64/libssl.a $OPENSSL/lib64/libcrypto.a \
-lws2_32 -lcrypt32 -ladvapi32 -lgdi32 -lbcrypt
poc.exe
A native Windows USE_SSL=1 build of libhiredis_ssl with the same main (no I/O stubs, -lhiredis_ssl -lhiredis) is the same call.
Linux/macOS hiredis_ssl does not compile that strcmp and does not crash with the same options.
Observed
Windows host, PE32+ x86-64, OpenSSL 3.0.15 (mingw64), hiredis ssl.c from 29ea279:
signal 11 in redisCreateSSLContextWithOptions
MinGW’s CRT maps the access violation to SIGSEGV. The process never returns from redisCreateSSLContextWithOptions and never reaches CertOpenSystemStore.
Suggested fix
Do not call strcmp on a NULL file name. Only the "wincert" sentinel should select the system store; a NULL file name with a non-NULL capath should fall through to SSL_CTX_load_verify_locations.
--- a/ssl.c
+++ b/ssl.c
@@ -370,7 +370,7 @@ redisSSLContext *redisCreateSSLContextWithOptions(...)
if (capath || cacert_filename) {
#ifdef _WIN32
- if (0 == strcmp(cacert_filename, "wincert")) {
+ if (cacert_filename && 0 == strcmp(cacert_filename, "wincert")) {
win_store = CertOpenSystemStore(NULL, "Root");
Environment
- redis/hiredis
29ea279 (1.5.0-dev)
- OpenSSL 3.0.15,
mingw64, linked into the same PE as ssl.c
- Windows host running that PE: crash inside
redisCreateSSLContextWithOptions
- Defect is compiled only for
_WIN32
Summary
redisCreateSSLContextWithOptions()documentscacert_filenameandcapathas independent optional CA sources. OpenSSL’sSSL_CTX_load_verify_locations(ctx, NULL, capath)is the intended way to load a CA directory without a bundle file.On Windows the function enters the CA block with
if (capath || cacert_filename)and then callsstrcmp(cacert_filename, "wincert")with no NULL check. A caller who sets onlycapath(the portable OpenSSL pattern) therefore passes NULL tostrcmpand crashes insideredisCreateSSLContextWithOptions, before any TLS handshake and beforeCertOpenSystemStore.Non-Windows builds skip that
strcmpand callSSL_CTX_load_verify_locationswith a NULL file name, which is valid. The sameredisSSLOptionstherefore works on Linux/macOS and aborts on Windows.This is not a general TLS vulnerability and does not affect
"wincert"or a non-NULLcacert_filename. It is a missing NULL guard on the Windows-only"wincert"special case.Present on 1.5.0-dev (
29ea279),ssl.c.Code
Public options (
hiredis_ssl.h): both fields are optional."wincert"is the Windows store sentinel; it is not required in order to usecapath.redisCreateSSLContext(NULL, capath, ...)is the same call.Reproduce
Windows PE linked against the in-tree
ssl.cand OpenSSL. The crash is insideredisCreateSSLContextWithOptionsafterSSL_CTX_newsucceeds, on thestrcmpatssl.c:373.The I/O stubs below exist only so
ssl.clinks without the rest of libhiredis. They are not on the crashing path.A native Windows
USE_SSL=1build oflibhiredis_sslwith the samemain(no I/O stubs,-lhiredis_ssl -lhiredis) is the same call.Linux/macOS
hiredis_ssldoes not compile thatstrcmpand does not crash with the same options.Observed
Windows host, PE32+ x86-64, OpenSSL 3.0.15 (
mingw64), hiredisssl.cfrom29ea279:MinGW’s CRT maps the access violation to SIGSEGV. The process never returns from
redisCreateSSLContextWithOptionsand never reachesCertOpenSystemStore.Suggested fix
Do not call
strcmpon a NULL file name. Only the"wincert"sentinel should select the system store; a NULL file name with a non-NULLcapathshould fall through toSSL_CTX_load_verify_locations.Environment
29ea279(1.5.0-dev)mingw64, linked into the same PE asssl.credisCreateSSLContextWithOptions_WIN32