summaryrefslogtreecommitdiff
path: root/examples/redis-unstable/deps/hiredis/ssl.c
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-01-21 22:52:54 +0100
commitdcacc00e3750300617ba6e16eb346713f91a783a (patch)
tree38e2d4fb5ed9d119711d4295c6eda4b014af73fd /examples/redis-unstable/deps/hiredis/ssl.c
parent58dac10aeb8f5a041c46bddbeaf4c7966a99b998 (diff)
downloadcrep-dcacc00e3750300617ba6e16eb346713f91a783a.tar.gz
Remove testing data
Diffstat (limited to 'examples/redis-unstable/deps/hiredis/ssl.c')
-rw-r--r--examples/redis-unstable/deps/hiredis/ssl.c617
1 files changed, 0 insertions, 617 deletions
diff --git a/examples/redis-unstable/deps/hiredis/ssl.c b/examples/redis-unstable/deps/hiredis/ssl.c
deleted file mode 100644
index 9ab18cc..0000000
--- a/examples/redis-unstable/deps/hiredis/ssl.c
+++ /dev/null
@@ -1,617 +0,0 @@
1/*
2 * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com>
3 * Copyright (c) 2010-2011, Pieter Noordhuis <pcnoordhuis at gmail dot com>
4 * Copyright (c) 2019, Redis Labs
5 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are met:
10 *
11 * * Redistributions of source code must retain the above copyright notice,
12 * this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * * Neither the name of Redis nor the names of its contributors may be used
17 * to endorse or promote products derived from this software without
18 * specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
24 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
30 * POSSIBILITY OF SUCH DAMAGE.
31 */
32
33#include "hiredis.h"
34#include "async.h"
35#include "net.h"
36
37#include <assert.h>
38#include <errno.h>
39#include <string.h>
40#ifdef _WIN32
41#include <windows.h>
42#include <wincrypt.h>
43#ifdef OPENSSL_IS_BORINGSSL
44#undef X509_NAME
45#undef X509_EXTENSIONS
46#undef PKCS7_ISSUER_AND_SERIAL
47#undef PKCS7_SIGNER_INFO
48#undef OCSP_REQUEST
49#undef OCSP_RESPONSE
50#endif
51#else
52#include <pthread.h>
53#endif
54
55#include <openssl/ssl.h>
56#include <openssl/err.h>
57
58#include "win32.h"
59#include "async_private.h"
60#include "hiredis_ssl.h"
61
62#define OPENSSL_1_1_0 0x10100000L
63
64void __redisSetError(redisContext *c, int type, const char *str);
65
66struct redisSSLContext {
67 /* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
68 SSL_CTX *ssl_ctx;
69
70 /* Requested SNI, or NULL */
71 char *server_name;
72};
73
74/* The SSL connection context is attached to SSL/TLS connections as a privdata. */
75typedef struct redisSSL {
76 /**
77 * OpenSSL SSL object.
78 */
79 SSL *ssl;
80
81 /**
82 * SSL_write() requires to be called again with the same arguments it was
83 * previously called with in the event of an SSL_read/SSL_write situation
84 */
85 size_t lastLen;
86
87 /** Whether the SSL layer requires read (possibly before a write) */
88 int wantRead;
89
90 /**
91 * Whether a write was requested prior to a read. If set, the write()
92 * should resume whenever a read takes place, if possible
93 */
94 int pendingWrite;
95} redisSSL;
96
97/* Forward declaration */
98redisContextFuncs redisContextSSLFuncs;
99
100/**
101 * OpenSSL global initialization and locking handling callbacks.
102 * Note that this is only required for OpenSSL < 1.1.0.
103 */
104
105#if OPENSSL_VERSION_NUMBER < OPENSSL_1_1_0
106#define HIREDIS_USE_CRYPTO_LOCKS
107#endif
108
109#ifdef HIREDIS_USE_CRYPTO_LOCKS
110#ifdef _WIN32
111typedef CRITICAL_SECTION sslLockType;
112static void sslLockInit(sslLockType* l) {
113 InitializeCriticalSection(l);
114}
115static void sslLockAcquire(sslLockType* l) {
116 EnterCriticalSection(l);
117}
118static void sslLockRelease(sslLockType* l) {
119 LeaveCriticalSection(l);
120}
121#else
122typedef pthread_mutex_t sslLockType;
123static void sslLockInit(sslLockType *l) {
124 pthread_mutex_init(l, NULL);
125}
126static void sslLockAcquire(sslLockType *l) {
127 pthread_mutex_lock(l);
128}
129static void sslLockRelease(sslLockType *l) {
130 pthread_mutex_unlock(l);
131}
132#endif
133
134static sslLockType* ossl_locks;
135
136static void opensslDoLock(int mode, int lkid, const char *f, int line) {
137 sslLockType *l = ossl_locks + lkid;
138
139 if (mode & CRYPTO_LOCK) {
140 sslLockAcquire(l);
141 } else {
142 sslLockRelease(l);
143 }
144
145 (void)f;
146 (void)line;
147}
148
149static int initOpensslLocks(void) {
150 unsigned ii, nlocks;
151 if (CRYPTO_get_locking_callback() != NULL) {
152 /* Someone already set the callback before us. Don't destroy it! */
153 return REDIS_OK;
154 }
155 nlocks = CRYPTO_num_locks();
156 ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
157 if (ossl_locks == NULL)
158 return REDIS_ERR;
159
160 for (ii = 0; ii < nlocks; ii++) {
161 sslLockInit(ossl_locks + ii);
162 }
163 CRYPTO_set_locking_callback(opensslDoLock);
164 return REDIS_OK;
165}
166#endif /* HIREDIS_USE_CRYPTO_LOCKS */
167
168int redisInitOpenSSL(void)
169{
170 SSL_library_init();
171#ifdef HIREDIS_USE_CRYPTO_LOCKS
172 initOpensslLocks();
173#endif
174
175 return REDIS_OK;
176}
177
178/**
179 * redisSSLContext helper context destruction.
180 */
181
182const char *redisSSLContextGetError(redisSSLContextError error)
183{
184 switch (error) {
185 case REDIS_SSL_CTX_NONE:
186 return "No Error";
187 case REDIS_SSL_CTX_CREATE_FAILED:
188 return "Failed to create OpenSSL SSL_CTX";
189 case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
190 return "Client cert and key must both be specified or skipped";
191 case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
192 return "Failed to load CA Certificate or CA Path";
193 case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
194 return "Failed to load client certificate";
195 case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
196 return "Failed to load private key";
197 case REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED:
198 return "Failed to open system certificate store";
199 case REDIS_SSL_CTX_OS_CERT_ADD_FAILED:
200 return "Failed to add CA certificates obtained from system to the SSL context";
201 default:
202 return "Unknown error code";
203 }
204}
205
206void redisFreeSSLContext(redisSSLContext *ctx)
207{
208 if (!ctx)
209 return;
210
211 if (ctx->server_name) {
212 hi_free(ctx->server_name);
213 ctx->server_name = NULL;
214 }
215
216 if (ctx->ssl_ctx) {
217 SSL_CTX_free(ctx->ssl_ctx);
218 ctx->ssl_ctx = NULL;
219 }
220
221 hi_free(ctx);
222}
223
224
225/**
226 * redisSSLContext helper context initialization.
227 */
228
229redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
230 const char *cert_filename, const char *private_key_filename,
231 const char *server_name, redisSSLContextError *error)
232{
233 redisSSLOptions options = {
234 .cacert_filename = cacert_filename,
235 .capath = capath,
236 .cert_filename = cert_filename,
237 .private_key_filename = private_key_filename,
238 .server_name = server_name,
239 .verify_mode = REDIS_SSL_VERIFY_PEER,
240 };
241
242 return redisCreateSSLContextWithOptions(&options, error);
243}
244
245redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, redisSSLContextError *error) {
246 const char *cacert_filename = options->cacert_filename;
247 const char *capath = options->capath;
248 const char *cert_filename = options->cert_filename;
249 const char *private_key_filename = options->private_key_filename;
250 const char *server_name = options->server_name;
251
252#ifdef _WIN32
253 HCERTSTORE win_store = NULL;
254 PCCERT_CONTEXT win_ctx = NULL;
255#endif
256
257 redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
258 if (ctx == NULL)
259 goto error;
260
261 const SSL_METHOD *ssl_method;
262#if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
263 ssl_method = TLS_client_method();
264#else
265 ssl_method = SSLv23_client_method();
266#endif
267
268 ctx->ssl_ctx = SSL_CTX_new(ssl_method);
269 if (!ctx->ssl_ctx) {
270 if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
271 goto error;
272 }
273
274#if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
275 SSL_CTX_set_min_proto_version(ctx->ssl_ctx, TLS1_2_VERSION);
276#else
277 SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
278#endif
279
280 SSL_CTX_set_verify(ctx->ssl_ctx, options->verify_mode, NULL);
281
282 if ((cert_filename != NULL && private_key_filename == NULL) ||
283 (private_key_filename != NULL && cert_filename == NULL)) {
284 if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
285 goto error;
286 }
287
288 if (capath || cacert_filename) {
289#ifdef _WIN32
290 if (0 == strcmp(cacert_filename, "wincert")) {
291 win_store = CertOpenSystemStore(NULL, "Root");
292 if (!win_store) {
293 if (error) *error = REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED;
294 goto error;
295 }
296 X509_STORE* store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
297 while (win_ctx = CertEnumCertificatesInStore(win_store, win_ctx)) {
298 X509* x509 = NULL;
299 x509 = d2i_X509(NULL, (const unsigned char**)&win_ctx->pbCertEncoded, win_ctx->cbCertEncoded);
300 if (x509) {
301 if ((1 != X509_STORE_add_cert(store, x509)) ||
302 (1 != SSL_CTX_add_client_CA(ctx->ssl_ctx, x509)))
303 {
304 if (error) *error = REDIS_SSL_CTX_OS_CERT_ADD_FAILED;
305 goto error;
306 }
307 X509_free(x509);
308 }
309 }
310 CertFreeCertificateContext(win_ctx);
311 CertCloseStore(win_store, 0);
312 } else
313#endif
314 if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
315 if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
316 goto error;
317 }
318 } else {
319 if (!SSL_CTX_set_default_verify_paths(ctx->ssl_ctx)) {
320 if (error) *error = REDIS_SSL_CTX_CLIENT_DEFAULT_CERT_FAILED;
321 goto error;
322 }
323 }
324
325 if (cert_filename) {
326 if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
327 if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
328 goto error;
329 }
330 if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
331 if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
332 goto error;
333 }
334 }
335
336 if (server_name)
337 ctx->server_name = hi_strdup(server_name);
338
339 return ctx;
340
341error:
342#ifdef _WIN32
343 CertFreeCertificateContext(win_ctx);
344 CertCloseStore(win_store, 0);
345#endif
346 redisFreeSSLContext(ctx);
347 return NULL;
348}
349
350/**
351 * SSL Connection initialization.
352 */
353
354
355static int redisSSLConnect(redisContext *c, SSL *ssl) {
356 if (c->privctx) {
357 __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
358 return REDIS_ERR;
359 }
360
361 redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
362 if (rssl == NULL) {
363 __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
364 return REDIS_ERR;
365 }
366
367 c->funcs = &redisContextSSLFuncs;
368 rssl->ssl = ssl;
369
370 SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
371 SSL_set_fd(rssl->ssl, c->fd);
372 SSL_set_connect_state(rssl->ssl);
373
374 ERR_clear_error();
375 int rv = SSL_connect(rssl->ssl);
376 if (rv == 1) {
377 c->privctx = rssl;
378 return REDIS_OK;
379 }
380
381 rv = SSL_get_error(rssl->ssl, rv);
382 if (((c->flags & REDIS_BLOCK) == 0) &&
383 (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
384 c->privctx = rssl;
385 return REDIS_OK;
386 }
387
388 if (c->err == 0) {
389 char err[512];
390 if (rv == SSL_ERROR_SYSCALL)
391 snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
392 else {
393 unsigned long e = ERR_peek_last_error();
394 snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
395 ERR_reason_error_string(e));
396 }
397 __redisSetError(c, REDIS_ERR_IO, err);
398 }
399
400 hi_free(rssl);
401 return REDIS_ERR;
402}
403
404/**
405 * A wrapper around redisSSLConnect() for users who manage their own context and
406 * create their own SSL object.
407 */
408
409int redisInitiateSSL(redisContext *c, SSL *ssl) {
410 return redisSSLConnect(c, ssl);
411}
412
413/**
414 * A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
415 * manage their own SSL objects.
416 */
417
418int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
419{
420 if (!c || !redis_ssl_ctx)
421 return REDIS_ERR;
422
423 /* We want to verify that redisSSLConnect() won't fail on this, as it will
424 * not own the SSL object in that case and we'll end up leaking.
425 */
426 if (c->privctx)
427 return REDIS_ERR;
428
429 SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
430 if (!ssl) {
431 __redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
432 goto error;
433 }
434
435 if (redis_ssl_ctx->server_name) {
436 if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) {
437 __redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI");
438 goto error;
439 }
440 }
441
442 if (redisSSLConnect(c, ssl) != REDIS_OK) {
443 goto error;
444 }
445
446 return REDIS_OK;
447
448error:
449 if (ssl)
450 SSL_free(ssl);
451 return REDIS_ERR;
452}
453
454static int maybeCheckWant(redisSSL *rssl, int rv) {
455 /**
456 * If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
457 * and true is returned. False is returned otherwise
458 */
459 if (rv == SSL_ERROR_WANT_READ) {
460 rssl->wantRead = 1;
461 return 1;
462 } else if (rv == SSL_ERROR_WANT_WRITE) {
463 rssl->pendingWrite = 1;
464 return 1;
465 } else {
466 return 0;
467 }
468}
469
470/**
471 * Implementation of redisContextFuncs for SSL connections.
472 */
473
474static void redisSSLFree(void *privctx){
475 redisSSL *rsc = privctx;
476
477 if (!rsc) return;
478 if (rsc->ssl) {
479 SSL_free(rsc->ssl);
480 rsc->ssl = NULL;
481 }
482 hi_free(rsc);
483}
484
485static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
486 redisSSL *rssl = c->privctx;
487
488 int nread = SSL_read(rssl->ssl, buf, bufcap);
489 if (nread > 0) {
490 return nread;
491 } else if (nread == 0) {
492 __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
493 return -1;
494 } else {
495 int err = SSL_get_error(rssl->ssl, nread);
496 if (c->flags & REDIS_BLOCK) {
497 /**
498 * In blocking mode, we should never end up in a situation where
499 * we get an error without it being an actual error, except
500 * in the case of EINTR, which can be spuriously received from
501 * debuggers or whatever.
502 */
503 if (errno == EINTR) {
504 return 0;
505 } else {
506 const char *msg = NULL;
507 if (errno == EAGAIN) {
508 msg = "Resource temporarily unavailable";
509 }
510 __redisSetError(c, REDIS_ERR_IO, msg);
511 return -1;
512 }
513 }
514
515 /**
516 * We can very well get an EWOULDBLOCK/EAGAIN, however
517 */
518 if (maybeCheckWant(rssl, err)) {
519 return 0;
520 } else {
521 __redisSetError(c, REDIS_ERR_IO, NULL);
522 return -1;
523 }
524 }
525}
526
527static ssize_t redisSSLWrite(redisContext *c) {
528 redisSSL *rssl = c->privctx;
529
530 size_t len = rssl->lastLen ? rssl->lastLen : hi_sdslen(c->obuf);
531 int rv = SSL_write(rssl->ssl, c->obuf, len);
532
533 if (rv > 0) {
534 rssl->lastLen = 0;
535 } else if (rv < 0) {
536 rssl->lastLen = len;
537
538 int err = SSL_get_error(rssl->ssl, rv);
539 if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {
540 return 0;
541 } else {
542 __redisSetError(c, REDIS_ERR_IO, NULL);
543 return -1;
544 }
545 }
546 return rv;
547}
548
549static void redisSSLAsyncRead(redisAsyncContext *ac) {
550 int rv;
551 redisSSL *rssl = ac->c.privctx;
552 redisContext *c = &ac->c;
553
554 rssl->wantRead = 0;
555
556 if (rssl->pendingWrite) {
557 int done;
558
559 /* This is probably just a write event */
560 rssl->pendingWrite = 0;
561 rv = redisBufferWrite(c, &done);
562 if (rv == REDIS_ERR) {
563 __redisAsyncDisconnect(ac);
564 return;
565 } else if (!done) {
566 _EL_ADD_WRITE(ac);
567 }
568 }
569
570 rv = redisBufferRead(c);
571 if (rv == REDIS_ERR) {
572 __redisAsyncDisconnect(ac);
573 } else {
574 _EL_ADD_READ(ac);
575 redisProcessCallbacks(ac);
576 }
577}
578
579static void redisSSLAsyncWrite(redisAsyncContext *ac) {
580 int rv, done = 0;
581 redisSSL *rssl = ac->c.privctx;
582 redisContext *c = &ac->c;
583
584 rssl->pendingWrite = 0;
585 rv = redisBufferWrite(c, &done);
586 if (rv == REDIS_ERR) {
587 __redisAsyncDisconnect(ac);
588 return;
589 }
590
591 if (!done) {
592 if (rssl->wantRead) {
593 /* Need to read-before-write */
594 rssl->pendingWrite = 1;
595 _EL_DEL_WRITE(ac);
596 } else {
597 /* No extra reads needed, just need to write more */
598 _EL_ADD_WRITE(ac);
599 }
600 } else {
601 /* Already done! */
602 _EL_DEL_WRITE(ac);
603 }
604
605 /* Always reschedule a read */
606 _EL_ADD_READ(ac);
607}
608
609redisContextFuncs redisContextSSLFuncs = {
610 .close = redisNetClose,
611 .free_privctx = redisSSLFree,
612 .async_read = redisSSLAsyncRead,
613 .async_write = redisSSLAsyncWrite,
614 .read = redisSSLRead,
615 .write = redisSSLWrite
616};
617