diff options
-rw-r--r-- | .github/release-drafter-config.yml | 49 | ||||
-rw-r--r-- | .github/workflows/release-drafter.yml | 19 | ||||
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | README.md | 20 | ||||
-rw-r--r-- | hiredis_ssl.h | 33 | ||||
-rw-r--r-- | ssl.c | 21 |
6 files changed, 132 insertions, 12 deletions
diff --git a/.github/release-drafter-config.yml b/.github/release-drafter-config.yml new file mode 100644 index 0000000..de4fea7 --- /dev/null +++ b/.github/release-drafter-config.yml @@ -0,0 +1,49 @@ +name-template: '$NEXT_MAJOR_VERSION' +tag-template: 'v$NEXT_MAJOR_VERSION' +autolabeler: + - label: 'maintenance' + files: + - '*.md' + - '.github/*' + - label: 'bug' + branch: + - '/bug-.+' + - label: 'maintenance' + branch: + - '/maintenance-.+' + - label: 'feature' + branch: + - '/feature-.+' +categories: + - title: 'Breaking Changes' + labels: + - 'breakingchange' + + - title: '๐งช Experimental Features' + labels: + - 'experimental' + - title: '๐ New Features' + labels: + - 'feature' + - 'enhancement' + - title: '๐ Bug Fixes' + labels: + - 'fix' + - 'bugfix' + - 'bug' + - 'BUG' + - title: '๐งฐ Maintenance' + label: 'maintenance' +change-template: '- $TITLE (#$NUMBER)' +exclude-labels: + - 'skip-changelog' +template: | + ## Changes + + $CHANGES + + ## Contributors + We'd like to thank all the contributors who worked on this release! + + $CONTRIBUTORS + diff --git a/.github/workflows/release-drafter.yml b/.github/workflows/release-drafter.yml new file mode 100644 index 0000000..ec2d88b --- /dev/null +++ b/.github/workflows/release-drafter.yml @@ -0,0 +1,19 @@ +name: Release Drafter + +on: + push: + # branches to consider in the event; optional, defaults to all + branches: + - master + +jobs: + update_release_draft: + runs-on: ubuntu-latest + steps: + # Drafts your next Release notes as Pull Requests are merged into "master" + - uses: release-drafter/release-drafter@v5 + with: + # (Optional) specify config name to use, relative to .github/. Default: release-drafter.yml + config-name: release-drafter-config.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/CMakeLists.txt b/CMakeLists.txt index ee149e6..f77dfd5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -CMAKE_MINIMUM_REQUIRED(VERSION 3.4.0) +CMAKE_MINIMUM_REQUIRED(VERSION 3.0.0) OPTION(ENABLE_SSL "Build hiredis_ssl for SSL support" OFF) OPTION(DISABLE_TESTS "If tests should be compiled or not" OFF) @@ -250,7 +250,7 @@ following two execution paths: * Read from the socket until a single reply could be parsed The function `redisGetReply` is exported as part of the Hiredis API and can be used when a reply -is expected on the socket. To pipeline commands, the only things that needs to be done is +is expected on the socket. To pipeline commands, the only thing that needs to be done is filling up the output buffer. For this cause, two commands can be used that are identical to the `redisCommand` family, apart from not returning a reply: ```c @@ -357,9 +357,9 @@ void(const redisAsyncContext *c, int status); ``` On a *connect*, the `status` argument is set to `REDIS_OK` if the connection attempt succeeded. In this -case, the context is ready to accept commands. If it is called with `REDIS_ERR` then the +case, the context is ready to accept commands. If it is called with `REDIS_ERR` then the connection attempt failed. The `err` field in the context can be accessed to find out the cause of the error. -After a failed connection attempt, the context object is automatically freed by the libary after calling +After a failed connection attempt, the context object is automatically freed by the library after calling the connect callback. This may be a good point to create a new context and retry the connection. On a disconnect, the `status` argument is set to `REDIS_OK` when disconnection was initiated by the @@ -375,7 +375,7 @@ api will return `REDIS_ERR`. The function to set the callbacks have the followin int redisAsyncSetConnectCallback(redisAsyncContext *ac, redisConnectCallback *fn); int redisAsyncSetDisconnectCallback(redisAsyncContext *ac, redisDisconnectCallback *fn); ``` -`ac->data` may be used to pass user data to both of thes callbacks. An typical implementation +`ac->data` may be used to pass user data to both callbacks. A typical implementation might look something like this: ```c void appOnConnect(redisAsyncContext *c, int status) @@ -444,9 +444,9 @@ For every command issued, with the exception of **SUBSCRIBE** and **PSUBSCRIBE** called exactly once. Even if the context object id disconnected or deleted, every pending callback will be called with a `NULL` reply. -For **SUBSCRIBE** and **PSUBSCRIBE**, the callbacks may be called repeatedly until a `unsubscribe` +For **SUBSCRIBE** and **PSUBSCRIBE**, the callbacks may be called repeatedly until an `unsubscribe` message arrives. This will be the last invocation of the callback. In case of error, the callbacks -may reive a final `NULL` reply instead. +may receive a final `NULL` reply instead. ### Disconnecting @@ -604,7 +604,7 @@ redisSSLContext *ssl_context; /* An error variable to indicate what went wrong, if the context fails to * initialize. */ -redisSSLContextError ssl_error; +redisSSLContextError ssl_error = REDIS_SSL_CTX_NONE; /* Initialize global OpenSSL state. * @@ -622,11 +622,11 @@ ssl_context = redisCreateSSLContext( "redis.mydomain.com", /* Server name to request (SNI), optional */ &ssl_error); -if(ssl_context == NULL || ssl_error != 0) { +if(ssl_context == NULL || ssl_error != REDIS_SSL_CTX_NONE) { /* Handle error and abort... */ /* e.g. printf("SSL error: %s\n", - (ssl_error != 0) ? + (ssl_error != REDIS_SSL_CTX_NONE) ? redisSSLContextGetError(ssl_error) : "Unknown error"); // Abort */ @@ -708,7 +708,7 @@ If you have a unique use-case where you don't want hiredis to automatically inte redisSetPushCallback(context, NULL); ``` - _Note: With no handler configured, calls to `redisCommand` may generate more than one reply, so this strategy is only applicable when there's some kind of blocking`redisGetReply()` loop (e.g. `MONITOR` or `SUBSCRIBE` workloads)._ + _Note: With no handler configured, calls to `redisCommand` may generate more than one reply, so this strategy is only applicable when there's some kind of blocking `redisGetReply()` loop (e.g. `MONITOR` or `SUBSCRIBE` workloads)._ ## Allocator injection diff --git a/hiredis_ssl.h b/hiredis_ssl.h index e3d3e1c..26bc9e9 100644 --- a/hiredis_ssl.h +++ b/hiredis_ssl.h @@ -61,6 +61,27 @@ typedef enum { REDIS_SSL_CTX_OS_CERT_ADD_FAILED /* Failed to add CA certificates obtained from system to the SSL context */ } redisSSLContextError; +/* Constants that mirror OpenSSL's verify modes. By default, + * REDIS_SSL_VERIFY_PEER is used with redisCreateSSLContext(). + * Some Redis clients disable peer verification if there are no + * certificates specified. + */ +#define REDIS_SSL_VERIFY_NONE 0x00 +#define REDIS_SSL_VERIFY_PEER 0x01 +#define REDIS_SSL_VERIFY_FAIL_IF_NO_PEER_CERT 0x02 +#define REDIS_SSL_VERIFY_CLIENT_ONCE 0x04 +#define REDIS_SSL_VERIFY_POST_HANDSHAKE 0x08 + +/* Options to create an OpenSSL context. */ +typedef struct { + const char *cacert_filename; + const char *capath; + const char *cert_filename; + const char *private_key_filename; + const char *server_name; + int verify_mode; +} redisSSLOptions; + /** * Return the error message corresponding with the specified error code. */ @@ -102,6 +123,18 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char * const char *server_name, redisSSLContextError *error); /** + * Helper function to initialize an OpenSSL context that can be used + * to initiate SSL connections. This is a more extensible version of redisCreateSSLContext(). + * + * options contains a structure of SSL options to use. + * + * If error is non-null, it will be populated in case the context creation fails + * (returning a NULL). +*/ +redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, + redisSSLContextError *error); + +/** * Free a previously created OpenSSL context. */ void redisFreeSSLContext(redisSSLContext *redis_ssl_ctx); @@ -219,6 +219,25 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char * const char *cert_filename, const char *private_key_filename, const char *server_name, redisSSLContextError *error) { + redisSSLOptions options = { + .cacert_filename = cacert_filename, + .capath = capath, + .cert_filename = cert_filename, + .private_key_filename = private_key_filename, + .server_name = server_name, + .verify_mode = REDIS_SSL_VERIFY_PEER, + }; + + return redisCreateSSLContextWithOptions(&options, error); +} + +redisSSLContext *redisCreateSSLContextWithOptions(redisSSLOptions *options, redisSSLContextError *error) { + const char *cacert_filename = options->cacert_filename; + const char *capath = options->capath; + const char *cert_filename = options->cert_filename; + const char *private_key_filename = options->private_key_filename; + const char *server_name = options->server_name; + #ifdef _WIN32 HCERTSTORE win_store = NULL; PCCERT_CONTEXT win_ctx = NULL; @@ -235,7 +254,7 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char * } SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3); - SSL_CTX_set_verify(ctx->ssl_ctx, SSL_VERIFY_PEER, NULL); + SSL_CTX_set_verify(ctx->ssl_ctx, options->verify_mode, NULL); if ((cert_filename != NULL && private_key_filename == NULL) || (private_key_filename != NULL && cert_filename == NULL)) { |