ssl.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617
  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. #include "hiredis.h"
  33. #include "async.h"
  34. #include "net.h"
  35. #include <assert.h>
  36. #include <errno.h>
  37. #include <string.h>
  38. #ifdef _WIN32
  39. #include <windows.h>
  40. #include <wincrypt.h>
  41. #ifdef OPENSSL_IS_BORINGSSL
  42. #undef X509_NAME
  43. #undef X509_EXTENSIONS
  44. #undef PKCS7_ISSUER_AND_SERIAL
  45. #undef PKCS7_SIGNER_INFO
  46. #undef OCSP_REQUEST
  47. #undef OCSP_RESPONSE
  48. #endif
  49. #else
  50. #include <pthread.h>
  51. #endif
  52. #include <openssl/ssl.h>
  53. #include <openssl/err.h>
  54. #include "win32.h"
  55. #include "async_private.h"
  56. #include "hiredis_ssl.h"
  57. #define OPENSSL_1_1_0 0x10100000L
  58. void __redisSetError(redisContext *c, int type, const char *str);
  59. struct redisSSLContext {
  60. /* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
  61. SSL_CTX *ssl_ctx;
  62. /* Requested SNI, or NULL */
  63. char *server_name;
  64. };
  65. /* The SSL connection context is attached to SSL/TLS connections as a privdata. */
  66. typedef struct redisSSL {
  67. /**
  68. * OpenSSL SSL object.
  69. */
  70. SSL *ssl;
  71. /**
  72. * SSL_write() requires to be called again with the same arguments it was
  73. * previously called with in the event of an SSL_read/SSL_write situation
  74. */
  75. size_t lastLen;
  76. /** Whether the SSL layer requires read (possibly before a write) */
  77. int wantRead;
  78. /**
  79. * Whether a write was requested prior to a read. If set, the write()
  80. * should resume whenever a read takes place, if possible
  81. */
  82. int pendingWrite;
  83. } redisSSL;
  84. /* Forward declaration */
  85. redisContextFuncs redisContextSSLFuncs;
  86. /**
  87. * OpenSSL global initialization and locking handling callbacks.
  88. * Note that this is only required for OpenSSL < 1.1.0.
  89. */
  90. #if OPENSSL_VERSION_NUMBER < OPENSSL_1_1_0
  91. #define HIREDIS_USE_CRYPTO_LOCKS
  92. #endif
  93. #ifdef HIREDIS_USE_CRYPTO_LOCKS
  94. #ifdef _WIN32
  95. typedef CRITICAL_SECTION sslLockType;
  96. static void sslLockInit(sslLockType* l) {
  97. InitializeCriticalSection(l);
  98. }
  99. static void sslLockAcquire(sslLockType* l) {
  100. EnterCriticalSection(l);
  101. }
  102. static void sslLockRelease(sslLockType* l) {
  103. LeaveCriticalSection(l);
  104. }
  105. #else
  106. typedef pthread_mutex_t sslLockType;
  107. static void sslLockInit(sslLockType *l) {
  108. pthread_mutex_init(l, NULL);
  109. }
  110. static void sslLockAcquire(sslLockType *l) {
  111. pthread_mutex_lock(l);
  112. }
  113. static void sslLockRelease(sslLockType *l) {
  114. pthread_mutex_unlock(l);
  115. }
  116. #endif
  117. static sslLockType* ossl_locks;
  118. static void opensslDoLock(int mode, int lkid, const char *f, int line) {
  119. sslLockType *l = ossl_locks + lkid;
  120. if (mode & CRYPTO_LOCK) {
  121. sslLockAcquire(l);
  122. } else {
  123. sslLockRelease(l);
  124. }
  125. (void)f;
  126. (void)line;
  127. }
  128. static int initOpensslLocks(void) {
  129. unsigned ii, nlocks;
  130. if (CRYPTO_get_locking_callback() != NULL) {
  131. /* Someone already set the callback before us. Don't destroy it! */
  132. return REDIS_OK;
  133. }
  134. nlocks = CRYPTO_num_locks();
  135. ossl_locks = hi_malloc(sizeof(*ossl_locks) * nlocks);
  136. if (ossl_locks == NULL)
  137. return REDIS_ERR;
  138. for (ii = 0; ii < nlocks; ii++) {
  139. sslLockInit(ossl_locks + ii);
  140. }
  141. CRYPTO_set_locking_callback(opensslDoLock);
  142. return REDIS_OK;
  143. }
  144. #endif /* HIREDIS_USE_CRYPTO_LOCKS */
  145. int redisInitOpenSSL(void)
  146. {
  147. SSL_library_init();
  148. #ifdef HIREDIS_USE_CRYPTO_LOCKS
  149. initOpensslLocks();
  150. #endif
  151. return REDIS_OK;
  152. }
  153. /**
  154. * redisSSLContext helper context destruction.
  155. */
  156. const char *redisSSLContextGetError(redisSSLContextError error)
  157. {
  158. switch (error) {
  159. case REDIS_SSL_CTX_NONE:
  160. return "No Error";
  161. case REDIS_SSL_CTX_CREATE_FAILED:
  162. return "Failed to create OpenSSL SSL_CTX";
  163. case REDIS_SSL_CTX_CERT_KEY_REQUIRED:
  164. return "Client cert and key must both be specified or skipped";
  165. case REDIS_SSL_CTX_CA_CERT_LOAD_FAILED:
  166. return "Failed to load CA Certificate or CA Path";
  167. case REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED:
  168. return "Failed to load client certificate";
  169. case REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED:
  170. return "Failed to load private key";
  171. case REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED:
  172. return "Failed to open system certificate store";
  173. case REDIS_SSL_CTX_OS_CERT_ADD_FAILED:
  174. return "Failed to add CA certificates obtained from system to the SSL context";
  175. default:
  176. return "Unknown error code";
  177. }
  178. }
  179. void redisFreeSSLContext(redisSSLContext *ctx)
  180. {
  181. if (!ctx)
  182. return;
  183. if (ctx->server_name) {
  184. hi_free(ctx->server_name);
  185. ctx->server_name = NULL;
  186. }
  187. if (ctx->ssl_ctx) {
  188. SSL_CTX_free(ctx->ssl_ctx);
  189. ctx->ssl_ctx = NULL;
  190. }
  191. hi_free(ctx);
  192. }
  193. /**
  194. * redisSSLContext helper context initialization.
  195. */
  196. redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *capath,
  197. const char *cert_filename, const char *private_key_filename,
  198. const char *server_name, redisSSLContextError *error)
  199. {
  200. redisSSLOptions options = {
  201. .cacert_filename = cacert_filename,
  202. .capath = capath,
  203. .cert_filename = cert_filename,
  204. .private_key_filename = private_key_filename,
  205. .server_name = server_name,
  206. .verify_mode = REDIS_SSL_VERIFY_PEER,
  207. };
  208. return redisCreateSSLContextWithOptions(&options, error);
  209. }
  210. redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, redisSSLContextError *error) {
  211. const char *cacert_filename = options->cacert_filename;
  212. const char *capath = options->capath;
  213. const char *cert_filename = options->cert_filename;
  214. const char *private_key_filename = options->private_key_filename;
  215. const char *server_name = options->server_name;
  216. #ifdef _WIN32
  217. HCERTSTORE win_store = NULL;
  218. PCCERT_CONTEXT win_ctx = NULL;
  219. #endif
  220. redisSSLContext *ctx = hi_calloc(1, sizeof(redisSSLContext));
  221. if (ctx == NULL)
  222. goto error;
  223. const SSL_METHOD *ssl_method;
  224. #if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
  225. ssl_method = TLS_client_method();
  226. #else
  227. ssl_method = SSLv23_client_method();
  228. #endif
  229. ctx->ssl_ctx = SSL_CTX_new(ssl_method);
  230. if (!ctx->ssl_ctx) {
  231. if (error) *error = REDIS_SSL_CTX_CREATE_FAILED;
  232. goto error;
  233. }
  234. #if OPENSSL_VERSION_NUMBER >= OPENSSL_1_1_0
  235. SSL_CTX_set_min_proto_version(ctx->ssl_ctx, TLS1_2_VERSION);
  236. #else
  237. SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
  238. #endif
  239. SSL_CTX_set_verify(ctx->ssl_ctx, options->verify_mode, NULL);
  240. if ((cert_filename != NULL && private_key_filename == NULL) ||
  241. (private_key_filename != NULL && cert_filename == NULL)) {
  242. if (error) *error = REDIS_SSL_CTX_CERT_KEY_REQUIRED;
  243. goto error;
  244. }
  245. if (capath || cacert_filename) {
  246. #ifdef _WIN32
  247. if (0 == strcmp(cacert_filename, "wincert")) {
  248. win_store = CertOpenSystemStore(NULL, "Root");
  249. if (!win_store) {
  250. if (error) *error = REDIS_SSL_CTX_OS_CERTSTORE_OPEN_FAILED;
  251. goto error;
  252. }
  253. X509_STORE* store = SSL_CTX_get_cert_store(ctx->ssl_ctx);
  254. while (win_ctx = CertEnumCertificatesInStore(win_store, win_ctx)) {
  255. X509* x509 = NULL;
  256. x509 = d2i_X509(NULL, (const unsigned char**)&win_ctx->pbCertEncoded, win_ctx->cbCertEncoded);
  257. if (x509) {
  258. if ((1 != X509_STORE_add_cert(store, x509)) ||
  259. (1 != SSL_CTX_add_client_CA(ctx->ssl_ctx, x509)))
  260. {
  261. if (error) *error = REDIS_SSL_CTX_OS_CERT_ADD_FAILED;
  262. goto error;
  263. }
  264. X509_free(x509);
  265. }
  266. }
  267. CertFreeCertificateContext(win_ctx);
  268. CertCloseStore(win_store, 0);
  269. } else
  270. #endif
  271. if (!SSL_CTX_load_verify_locations(ctx->ssl_ctx, cacert_filename, capath)) {
  272. if (error) *error = REDIS_SSL_CTX_CA_CERT_LOAD_FAILED;
  273. goto error;
  274. }
  275. } else {
  276. if (!SSL_CTX_set_default_verify_paths(ctx->ssl_ctx)) {
  277. if (error) *error = REDIS_SSL_CTX_CLIENT_DEFAULT_CERT_FAILED;
  278. goto error;
  279. }
  280. }
  281. if (cert_filename) {
  282. if (!SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, cert_filename)) {
  283. if (error) *error = REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED;
  284. goto error;
  285. }
  286. if (!SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, private_key_filename, SSL_FILETYPE_PEM)) {
  287. if (error) *error = REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED;
  288. goto error;
  289. }
  290. }
  291. if (server_name)
  292. ctx->server_name = hi_strdup(server_name);
  293. return ctx;
  294. error:
  295. #ifdef _WIN32
  296. CertFreeCertificateContext(win_ctx);
  297. CertCloseStore(win_store, 0);
  298. #endif
  299. redisFreeSSLContext(ctx);
  300. return NULL;
  301. }
  302. /**
  303. * SSL Connection initialization.
  304. */
  305. static int redisSSLConnect(redisContext *c, SSL *ssl) {
  306. if (c->privctx) {
  307. __redisSetError(c, REDIS_ERR_OTHER, "redisContext was already associated");
  308. return REDIS_ERR;
  309. }
  310. redisSSL *rssl = hi_calloc(1, sizeof(redisSSL));
  311. if (rssl == NULL) {
  312. __redisSetError(c, REDIS_ERR_OOM, "Out of memory");
  313. return REDIS_ERR;
  314. }
  315. c->funcs = &redisContextSSLFuncs;
  316. rssl->ssl = ssl;
  317. SSL_set_mode(rssl->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER);
  318. SSL_set_fd(rssl->ssl, c->fd);
  319. SSL_set_connect_state(rssl->ssl);
  320. ERR_clear_error();
  321. int rv = SSL_connect(rssl->ssl);
  322. if (rv == 1) {
  323. c->privctx = rssl;
  324. return REDIS_OK;
  325. }
  326. rv = SSL_get_error(rssl->ssl, rv);
  327. if (((c->flags & REDIS_BLOCK) == 0) &&
  328. (rv == SSL_ERROR_WANT_READ || rv == SSL_ERROR_WANT_WRITE)) {
  329. c->privctx = rssl;
  330. return REDIS_OK;
  331. }
  332. if (c->err == 0) {
  333. char err[512];
  334. if (rv == SSL_ERROR_SYSCALL)
  335. snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",strerror(errno));
  336. else {
  337. unsigned long e = ERR_peek_last_error();
  338. snprintf(err,sizeof(err)-1,"SSL_connect failed: %s",
  339. ERR_reason_error_string(e));
  340. }
  341. __redisSetError(c, REDIS_ERR_IO, err);
  342. }
  343. hi_free(rssl);
  344. return REDIS_ERR;
  345. }
  346. /**
  347. * A wrapper around redisSSLConnect() for users who manage their own context and
  348. * create their own SSL object.
  349. */
  350. int redisInitiateSSL(redisContext *c, SSL *ssl) {
  351. return redisSSLConnect(c, ssl);
  352. }
  353. /**
  354. * A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
  355. * manage their own SSL objects.
  356. */
  357. int redisInitiateSSLWithContext(redisContext *c, redisSSLContext *redis_ssl_ctx)
  358. {
  359. if (!c || !redis_ssl_ctx)
  360. return REDIS_ERR;
  361. /* We want to verify that redisSSLConnect() won't fail on this, as it will
  362. * not own the SSL object in that case and we'll end up leaking.
  363. */
  364. if (c->privctx)
  365. return REDIS_ERR;
  366. SSL *ssl = SSL_new(redis_ssl_ctx->ssl_ctx);
  367. if (!ssl) {
  368. __redisSetError(c, REDIS_ERR_OTHER, "Couldn't create new SSL instance");
  369. goto error;
  370. }
  371. if (redis_ssl_ctx->server_name) {
  372. if (!SSL_set_tlsext_host_name(ssl, redis_ssl_ctx->server_name)) {
  373. __redisSetError(c, REDIS_ERR_OTHER, "Failed to set server_name/SNI");
  374. goto error;
  375. }
  376. }
  377. if (redisSSLConnect(c, ssl) != REDIS_OK) {
  378. goto error;
  379. }
  380. return REDIS_OK;
  381. error:
  382. if (ssl)
  383. SSL_free(ssl);
  384. return REDIS_ERR;
  385. }
  386. static int maybeCheckWant(redisSSL *rssl, int rv) {
  387. /**
  388. * If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
  389. * and true is returned. False is returned otherwise
  390. */
  391. if (rv == SSL_ERROR_WANT_READ) {
  392. rssl->wantRead = 1;
  393. return 1;
  394. } else if (rv == SSL_ERROR_WANT_WRITE) {
  395. rssl->pendingWrite = 1;
  396. return 1;
  397. } else {
  398. return 0;
  399. }
  400. }
  401. /**
  402. * Implementation of redisContextFuncs for SSL connections.
  403. */
  404. static void redisSSLFree(void *privctx){
  405. redisSSL *rsc = privctx;
  406. if (!rsc) return;
  407. if (rsc->ssl) {
  408. SSL_free(rsc->ssl);
  409. rsc->ssl = NULL;
  410. }
  411. hi_free(rsc);
  412. }
  413. static ssize_t redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
  414. redisSSL *rssl = c->privctx;
  415. int nread = SSL_read(rssl->ssl, buf, bufcap);
  416. if (nread > 0) {
  417. return nread;
  418. } else if (nread == 0) {
  419. __redisSetError(c, REDIS_ERR_EOF, "Server closed the connection");
  420. return -1;
  421. } else {
  422. int err = SSL_get_error(rssl->ssl, nread);
  423. if (c->flags & REDIS_BLOCK) {
  424. /**
  425. * In blocking mode, we should never end up in a situation where
  426. * we get an error without it being an actual error, except
  427. * in the case of EINTR, which can be spuriously received from
  428. * debuggers or whatever.
  429. */
  430. if (errno == EINTR) {
  431. return 0;
  432. } else {
  433. const char *msg = NULL;
  434. if (errno == EAGAIN) {
  435. msg = "Resource temporarily unavailable";
  436. }
  437. __redisSetError(c, REDIS_ERR_IO, msg);
  438. return -1;
  439. }
  440. }
  441. /**
  442. * We can very well get an EWOULDBLOCK/EAGAIN, however
  443. */
  444. if (maybeCheckWant(rssl, err)) {
  445. return 0;
  446. } else {
  447. __redisSetError(c, REDIS_ERR_IO, NULL);
  448. return -1;
  449. }
  450. }
  451. }
  452. static ssize_t redisSSLWrite(redisContext *c) {
  453. redisSSL *rssl = c->privctx;
  454. size_t len = rssl->lastLen ? rssl->lastLen : sdslen(c->obuf);
  455. int rv = SSL_write(rssl->ssl, c->obuf, len);
  456. if (rv > 0) {
  457. rssl->lastLen = 0;
  458. } else if (rv < 0) {
  459. rssl->lastLen = len;
  460. int err = SSL_get_error(rssl->ssl, rv);
  461. if ((c->flags & REDIS_BLOCK) == 0 && maybeCheckWant(rssl, err)) {
  462. return 0;
  463. } else {
  464. __redisSetError(c, REDIS_ERR_IO, NULL);
  465. return -1;
  466. }
  467. }
  468. return rv;
  469. }
  470. static void redisSSLAsyncRead(redisAsyncContext *ac) {
  471. int rv;
  472. redisSSL *rssl = ac->c.privctx;
  473. redisContext *c = &ac->c;
  474. rssl->wantRead = 0;
  475. if (rssl->pendingWrite) {
  476. int done;
  477. /* This is probably just a write event */
  478. rssl->pendingWrite = 0;
  479. rv = redisBufferWrite(c, &done);
  480. if (rv == REDIS_ERR) {
  481. __redisAsyncDisconnect(ac);
  482. return;
  483. } else if (!done) {
  484. _EL_ADD_WRITE(ac);
  485. }
  486. }
  487. rv = redisBufferRead(c);
  488. if (rv == REDIS_ERR) {
  489. __redisAsyncDisconnect(ac);
  490. } else {
  491. _EL_ADD_READ(ac);
  492. redisProcessCallbacks(ac);
  493. }
  494. }
  495. static void redisSSLAsyncWrite(redisAsyncContext *ac) {
  496. int rv, done = 0;
  497. redisSSL *rssl = ac->c.privctx;
  498. redisContext *c = &ac->c;
  499. rssl->pendingWrite = 0;
  500. rv = redisBufferWrite(c, &done);
  501. if (rv == REDIS_ERR) {
  502. __redisAsyncDisconnect(ac);
  503. return;
  504. }
  505. if (!done) {
  506. if (rssl->wantRead) {
  507. /* Need to read-before-write */
  508. rssl->pendingWrite = 1;
  509. _EL_DEL_WRITE(ac);
  510. } else {
  511. /* No extra reads needed, just need to write more */
  512. _EL_ADD_WRITE(ac);
  513. }
  514. } else {
  515. /* Already done! */
  516. _EL_DEL_WRITE(ac);
  517. }
  518. /* Always reschedule a read */
  519. _EL_ADD_READ(ac);
  520. }
  521. redisContextFuncs redisContextSSLFuncs = {
  522. .close = redisNetClose,
  523. .free_privctx = redisSSLFree,
  524. .async_read = redisSSLAsyncRead,
  525. .async_write = redisSSLAsyncWrite,
  526. .read = redisSSLRead,
  527. .write = redisSSLWrite
  528. };