Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ruanhaishen
hiredis
Commits
190bca88
Commit
190bca88
authored
May 22, 2020
by
Yossi Gottlieb
Browse files
New SSL API to replace redisSecureConnection().
parent
8e0264cf
Changes
6
Show whitespace changes
Inline
Side-by-side
README.md
View file @
190bca88
...
@@ -438,42 +438,68 @@ First, you'll need to make sure you include the SSL header file:
...
@@ -438,42 +438,68 @@ First, you'll need to make sure you include the SSL header file:
#include "hiredis_ssl.h"
#include "hiredis_ssl.h"
```
```
SSL can only be enabled on a
`redisContext`
connection after the connection has
You will also need to link against
`libhiredis_ssl`
,
**in addition**
to
been established and before any command has been processed. For example:
`libhiredis`
and add
`-lssl -lcrypto`
to satisfy its dependencies.
Hiredis implements SSL/TLS on top of its normal
`redisContext`
or
`redisAsyncContext`
, so you will need to establish a connection first and then
initiate an SSL/TLS handshake.
#### Hiredis OpenSSL Wrappers
Before Hiredis can negotiate an SSL/TLS connection, it is necessary to
initialize OpenSSL and create a context. You can do that in two ways:
1.
Work directly with the OpenSSL API to initialize the library's global context
and create
`SSL_CTX *`
and
`SSL *`
contexts. With an
`SSL *`
object you can
call
`redisInitiateSSL()`
.
2.
Work with a set of Hiredis-provided wrappers around OpenSSL, create a
`redisSSLContext`
object to hold configuration and use
`redisInitiateSSLWithContext()`
to initiate the SSL/TLS handshake.
```
c
```
c
/* An Hiredis SSL context. It holds SSL configuration and can be reused across
* many contexts.
*/
redisSSLContext
*
ssl
;
/* An error variable to indicate what went wrong, if the context fails to
* initialize.
*/
redisSSLContextError
ssl_error
;
/* Initialize global OpenSSL state.
*
* You should call this only once when your app initializes, and only if
* you don't explicitly or implicitly initialize OpenSSL it elsewhere.
*/
redisInitOpenSSL
();
/* Create SSL context */
ssl
=
redisCreateSSLContext
(
"cacertbundle.crt"
,
/* File name of trusted CA/ca bundle file, optional */
"/path/to/certs"
,
/* Path of trusted certificates, optional */
"client_cert.pem"
,
/* File name of client certificate file, optional */
"client_key.pem"
,
/* File name of client private key, optional */
"redis.mydomain.com"
,
/* Server name to request (SNI), optional */
&
ssl_error
)
!=
REDIS_OK
)
{
printf
(
"SSL error: %s
\n
"
,
redisSSLContextGetError
(
ssl_error
);
/* Abort... */
}
/* Create Redis context and establish connection */
c
=
redisConnect
(
"localhost"
,
6443
);
c
=
redisConnect
(
"localhost"
,
6443
);
if
(
c
==
NULL
||
c
->
err
)
{
if
(
c
==
NULL
||
c
->
err
)
{
/* Handle error and abort... */
/* Handle error and abort... */
}
}
if
(
redisSecureConnection
(
c
,
/* Negotiate SSL/TLS */
"cacertbundle.crt"
,
/* File name of trusted CA/ca bundle file */
if
(
redisInitiateSSLWithContext
(
c
,
ssl
)
!=
REDIS_OK
)
{
"client_cert.pem"
,
/* File name of client certificate file */
/* Handle error, in c->err / c->errstr */
"client_key.pem"
,
/* File name of client private key */
"redis.mydomain.com"
/* Server name to request (SNI) */
)
!=
REDIS_OK
)
{
printf
(
"SSL error: %s
\n
"
,
c
->
errstr
);
/* Abort... */
}
}
```
```
You will also need to link against
`libhiredis_ssl`
,
**in addition**
to
`libhiredis`
and add
`-lssl -lcrypto`
to satisfy its dependencies.
### OpenSSL Global State Initialization
OpenSSL needs to have certain global state initialized before it can be used.
Using
`redisSecureConnection()`
will handle this automatically on the first
call.
**
If the calling application itself also initializes and uses OpenSSL directly,
`redisSecureConnection()`
must not be used.
**
Instead, use
`redisInitiateSSL()`
which also provides greater control over the
configuration of the SSL connection, as the caller is responsible to create a
connection context using
`SSL_new()`
and configure it as required.
## AUTHORS
## AUTHORS
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
Hiredis was written by Salvatore Sanfilippo (antirez at gmail) and
...
...
examples/example-libevent-ssl.c
View file @
190bca88
...
@@ -52,13 +52,25 @@ int main (int argc, char **argv) {
...
@@ -52,13 +52,25 @@ int main (int argc, char **argv) {
const
char
*
certKey
=
argv
[
5
];
const
char
*
certKey
=
argv
[
5
];
const
char
*
caCert
=
argc
>
5
?
argv
[
6
]
:
NULL
;
const
char
*
caCert
=
argc
>
5
?
argv
[
6
]
:
NULL
;
redisSSLContext
*
ssl
;
redisSSLContextError
ssl_error
;
redisInitOpenSSL
();
ssl
=
redisCreateSSLContext
(
caCert
,
NULL
,
cert
,
certKey
,
NULL
,
&
ssl_error
);
if
(
!
ssl
)
{
printf
(
"Error: %s
\n
"
,
redisSSLContextGetError
(
ssl_error
));
return
1
;
}
redisAsyncContext
*
c
=
redisAsyncConnect
(
hostname
,
port
);
redisAsyncContext
*
c
=
redisAsyncConnect
(
hostname
,
port
);
if
(
c
->
err
)
{
if
(
c
->
err
)
{
/* Let *c leak for now... */
/* Let *c leak for now... */
printf
(
"Error: %s
\n
"
,
c
->
errstr
);
printf
(
"Error: %s
\n
"
,
c
->
errstr
);
return
1
;
return
1
;
}
}
if
(
redis
SecureConnection
(
&
c
->
c
,
caCert
,
cert
,
certKey
,
"sni"
)
!=
REDIS_OK
)
{
if
(
redis
InitiateSSLWithContext
(
&
c
->
c
,
ssl
)
!=
REDIS_OK
)
{
printf
(
"SSL Error!
\n
"
);
printf
(
"SSL Error!
\n
"
);
exit
(
1
);
exit
(
1
);
}
}
...
@@ -69,5 +81,7 @@ int main (int argc, char **argv) {
...
@@ -69,5 +81,7 @@ int main (int argc, char **argv) {
redisAsyncCommand
(
c
,
NULL
,
NULL
,
"SET key %b"
,
value
,
nvalue
);
redisAsyncCommand
(
c
,
NULL
,
NULL
,
"SET key %b"
,
value
,
nvalue
);
redisAsyncCommand
(
c
,
getCallback
,
(
char
*
)
"end-1"
,
"GET key"
);
redisAsyncCommand
(
c
,
getCallback
,
(
char
*
)
"end-1"
,
"GET key"
);
event_base_dispatch
(
base
);
event_base_dispatch
(
base
);
redisFreeSSLContext
(
ssl
);
return
0
;
return
0
;
}
}
examples/example-ssl.c
View file @
190bca88
...
@@ -7,6 +7,8 @@
...
@@ -7,6 +7,8 @@
int
main
(
int
argc
,
char
**
argv
)
{
int
main
(
int
argc
,
char
**
argv
)
{
unsigned
int
j
;
unsigned
int
j
;
redisSSLContext
*
ssl
;
redisSSLContextError
ssl_error
;
redisContext
*
c
;
redisContext
*
c
;
redisReply
*
reply
;
redisReply
*
reply
;
if
(
argc
<
4
)
{
if
(
argc
<
4
)
{
...
@@ -19,6 +21,14 @@ int main(int argc, char **argv) {
...
@@ -19,6 +21,14 @@ int main(int argc, char **argv) {
const
char
*
key
=
argv
[
4
];
const
char
*
key
=
argv
[
4
];
const
char
*
ca
=
argc
>
4
?
argv
[
5
]
:
NULL
;
const
char
*
ca
=
argc
>
4
?
argv
[
5
]
:
NULL
;
redisInitOpenSSL
();
ssl
=
redisCreateSSLContext
(
ca
,
NULL
,
cert
,
key
,
NULL
,
&
ssl_error
);
if
(
!
ssl
)
{
printf
(
"SSL Context error: %s
\n
"
,
redisSSLContextGetError
(
ssl_error
));
exit
(
1
);
}
struct
timeval
tv
=
{
1
,
500000
};
// 1.5 seconds
struct
timeval
tv
=
{
1
,
500000
};
// 1.5 seconds
redisOptions
options
=
{
0
};
redisOptions
options
=
{
0
};
REDIS_OPTIONS_SET_TCP
(
&
options
,
hostname
,
port
);
REDIS_OPTIONS_SET_TCP
(
&
options
,
hostname
,
port
);
...
@@ -35,7 +45,7 @@ int main(int argc, char **argv) {
...
@@ -35,7 +45,7 @@ int main(int argc, char **argv) {
exit
(
1
);
exit
(
1
);
}
}
if
(
redis
SecureConnection
(
c
,
ca
,
cert
,
key
,
"sni"
)
!=
REDIS_OK
)
{
if
(
redis
InitiateSSLWithContext
(
c
,
ssl
)
!=
REDIS_OK
)
{
printf
(
"Couldn't initialize SSL!
\n
"
);
printf
(
"Couldn't initialize SSL!
\n
"
);
printf
(
"Error: %s
\n
"
,
c
->
errstr
);
printf
(
"Error: %s
\n
"
,
c
->
errstr
);
redisFree
(
c
);
redisFree
(
c
);
...
@@ -93,5 +103,7 @@ int main(int argc, char **argv) {
...
@@ -93,5 +103,7 @@ int main(int argc, char **argv) {
/* Disconnects and frees the context */
/* Disconnects and frees the context */
redisFree
(
c
);
redisFree
(
c
);
redisFreeSSLContext
(
ssl
);
return
0
;
return
0
;
}
}
hiredis_ssl.h
View file @
190bca88
...
@@ -41,15 +41,81 @@ extern "C" {
...
@@ -41,15 +41,81 @@ extern "C" {
*/
*/
struct
ssl_st
;
struct
ssl_st
;
/* A wrapper around OpenSSL SSL_CTX to allow easy SSL use without directly
* calling OpenSSL.
*/
typedef
struct
redisSSLContext
redisSSLContext
;
/**
* Initialization errors that redisCreateSSLContext() may return.
*/
typedef
enum
{
REDIS_SSL_CTX_NONE
=
0
,
/* No Error */
REDIS_SSL_CTX_CREATE_FAILED
,
/* Failed to create OpenSSL SSL_CTX */
REDIS_SSL_CTX_CERT_KEY_REQUIRED
,
/* Client cert and key must both be specified or skipped */
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
,
/* Failed to load CA Certificate or CA Path */
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED
,
/* Failed to load client certificate */
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED
/* Failed to load private key */
}
redisSSLContextError
;
/**
* Return the error message corresponding with the specified error code.
*/
const
char
*
redisSSLContextGetError
(
redisSSLContextError
error
);
/**
* Helper function to initialize the OpenSSL library.
*
* OpenSSL requires one-time initialization before it can be used. Callers should
* call this function only once, and only if OpenSSL is not directly initialized
* elsewhere.
*/
int
redisInitOpenSSL
(
void
);
/**
/**
* Secure the connection using SSL. This should be done before any command is
* Helper function to initialize an OpenSSL context that can be used
* executed on the connection.
* to initiate SSL connections.
*
* cacert_filename is an optional name of a CA certificate/bundle file to load
* and use for validation.
*
* capath is an optional directory path where trusted CA certificate files are
* stored in an OpenSSL-compatible structure.
*
* cert_filename and private_key_filename are optional names of a client side
* certificate and private key files to use for authentication. They need to
* be both specified or omitted.
*
* server_name is an optional and will be used as a server name indication
* (SNI) TLS extension.
*
* If error is non-null, it will be populated in case the context creation fails
* (returning a NULL).
*/
redisSSLContext
*
redisCreateSSLContext
(
const
char
*
cacert_filename
,
const
char
*
capath
,
const
char
*
cert_filename
,
const
char
*
private_key_filename
,
const
char
*
server_name
,
redisSSLContextError
*
error
);
/**
* Free a previously created OpenSSL context.
*/
*/
int
redisSecureConnection
(
redisContext
*
c
,
const
char
*
capath
,
const
char
*
certpath
,
void
redisFreeSSLContext
(
redisSSLContext
*
redis_ssl_ctx
);
const
char
*
keypath
,
const
char
*
servername
);
/**
* Initiate SSL on an existing redisContext.
*
* This is similar to redisInitiateSSL() but does not require the caller
* to directly interact with OpenSSL, and instead uses a redisSSLContext
* previously created using redisCreateSSLContext().
*/
int
redisInitiateSSLWithContext
(
redisContext
*
c
,
redisSSLContext
*
redis_ssl_ctx
);
/**
/**
* Initiate SSL/TLS negotiation on a provided
contex
t.
* Initiate SSL/TLS negotiation on a provided
OpenSSL SSL objec
t.
*/
*/
int
redisInitiateSSL
(
redisContext
*
c
,
struct
ssl_st
*
ssl
);
int
redisInitiateSSL
(
redisContext
*
c
,
struct
ssl_st
*
ssl
);
...
...
ssl.c
View file @
190bca88
...
@@ -47,17 +47,20 @@
...
@@ -47,17 +47,20 @@
#include "win32.h"
#include "win32.h"
#include "async_private.h"
#include "async_private.h"
#include "hiredis_ssl.h"
void
__redisSetError
(
redisContext
*
c
,
int
type
,
const
char
*
str
);
void
__redisSetError
(
redisContext
*
c
,
int
type
,
const
char
*
str
);
/* The SSL context is attached to SSL/TLS connections as a privdata. */
struct
redisSSLContext
{
typedef
struct
redisSSLContext
{
/* Associated OpenSSL SSL_CTX as created by redisCreateSSLContext() */
/**
* OpenSSL SSL_CTX; It is optional and will not be set when using
* user-supplied SSL.
*/
SSL_CTX
*
ssl_ctx
;
SSL_CTX
*
ssl_ctx
;
/* Requested SNI, or NULL */
char
*
server_name
;
};
/* The SSL connection context is attached to SSL/TLS connections as a privdata. */
typedef
struct
redisSSL
{
/**
/**
* OpenSSL SSL object.
* OpenSSL SSL object.
*/
*/
...
@@ -77,43 +80,11 @@ typedef struct redisSSLContext {
...
@@ -77,43 +80,11 @@ typedef struct redisSSLContext {
* should resume whenever a read takes place, if possible
* should resume whenever a read takes place, if possible
*/
*/
int
pendingWrite
;
int
pendingWrite
;
}
redisSSL
Context
;
}
redisSSL
;
/* Forward declaration */
/* Forward declaration */
redisContextFuncs
redisContextSSLFuncs
;
redisContextFuncs
redisContextSSLFuncs
;
#ifdef HIREDIS_SSL_TRACE
/**
* Callback used for debugging
*/
static
void
sslLogCallback
(
const
SSL
*
ssl
,
int
where
,
int
ret
)
{
const
char
*
retstr
;
int
should_log
=
0
;
/* Ignore low-level SSL stuff */
if
(
where
&
SSL_CB_ALERT
)
{
should_log
=
1
;
}
if
(
where
==
SSL_CB_HANDSHAKE_START
||
where
==
SSL_CB_HANDSHAKE_DONE
)
{
should_log
=
1
;
}
if
((
where
&
SSL_CB_EXIT
)
&&
ret
==
0
)
{
should_log
=
1
;
}
if
(
!
should_log
)
{
return
;
}
retstr
=
SSL_alert_type_string
(
ret
);
printf
(
"ST(0x%x). %s. R(0x%x)%s
\n
"
,
where
,
SSL_state_string_long
(
ssl
),
ret
,
retstr
);
if
(
where
==
SSL_CB_HANDSHAKE_DONE
)
{
printf
(
"Using SSL version %s. Cipher=%s
\n
"
,
SSL_get_version
(
ssl
),
SSL_get_cipher_name
(
ssl
));
}
}
#endif
/**
/**
* OpenSSL global initialization and locking handling callbacks.
* OpenSSL global initialization and locking handling callbacks.
* Note that this is only required for OpenSSL < 1.1.0.
* Note that this is only required for OpenSSL < 1.1.0.
...
@@ -182,26 +153,130 @@ static int initOpensslLocks(void) {
...
@@ -182,26 +153,130 @@ static int initOpensslLocks(void) {
}
}
#endif
/* HIREDIS_USE_CRYPTO_LOCKS */
#endif
/* HIREDIS_USE_CRYPTO_LOCKS */
int
redisInitOpenSSL
(
void
)
{
SSL_library_init
();
#ifdef HIREDIS_USE_CRYPTO_LOCKS
initOpensslLocks
();
#endif
return
REDIS_OK
;
}
/**
* redisSSLContext helper context destruction.
*/
const
char
*
redisSSLContextGetError
(
redisSSLContextError
error
)
{
switch
(
error
)
{
case
REDIS_SSL_CTX_NONE
:
return
"No Error"
;
case
REDIS_SSL_CTX_CREATE_FAILED
:
return
"Failed to create OpenSSL SSL_CTX"
;
case
REDIS_SSL_CTX_CERT_KEY_REQUIRED
:
return
"Client cert and key must both be specified or skipped"
;
case
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
:
return
"Failed to load CA Certificate or CA Path"
;
case
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED
:
return
"Failed to load client certificate"
;
case
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED
:
return
"Failed to load private key"
;
default:
return
"Unknown error code"
;
}
}
void
redisFreeSSLContext
(
redisSSLContext
*
ctx
)
{
if
(
!
ctx
)
return
;
if
(
ctx
->
server_name
)
{
hi_free
(
ctx
->
server_name
);
ctx
->
server_name
=
NULL
;
}
if
(
ctx
->
ssl_ctx
)
{
SSL_CTX_free
(
ctx
->
ssl_ctx
);
ctx
->
ssl_ctx
=
NULL
;
}
hi_free
(
ctx
);
}
/**
* redisSSLContext helper context initialization.
*/
redisSSLContext
*
redisCreateSSLContext
(
const
char
*
cacert_filename
,
const
char
*
capath
,
const
char
*
cert_filename
,
const
char
*
private_key_filename
,
const
char
*
server_name
,
redisSSLContextError
*
error
)
{
redisSSLContext
*
ctx
=
hi_calloc
(
1
,
sizeof
(
redisSSLContext
));
ctx
->
ssl_ctx
=
SSL_CTX_new
(
SSLv23_client_method
());
if
(
!
ctx
->
ssl_ctx
)
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CREATE_FAILED
;
goto
error
;
}
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
);
if
((
cert_filename
!=
NULL
&&
private_key_filename
==
NULL
)
||
(
private_key_filename
!=
NULL
&&
cert_filename
==
NULL
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CERT_KEY_REQUIRED
;
goto
error
;
}
if
(
capath
||
cacert_filename
)
{
if
(
!
SSL_CTX_load_verify_locations
(
ctx
->
ssl_ctx
,
cacert_filename
,
capath
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
;
goto
error
;
}
}
if
(
cert_filename
)
{
if
(
!
SSL_CTX_use_certificate_chain_file
(
ctx
->
ssl_ctx
,
cert_filename
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CLIENT_CERT_LOAD_FAILED
;
goto
error
;
}
if
(
!
SSL_CTX_use_PrivateKey_file
(
ctx
->
ssl_ctx
,
private_key_filename
,
SSL_FILETYPE_PEM
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_PRIVATE_KEY_LOAD_FAILED
;
goto
error
;
}
}
if
(
server_name
)
ctx
->
server_name
=
hi_strdup
(
server_name
);
return
ctx
;
error:
redisFreeSSLContext
(
ctx
);
return
NULL
;
}
/**
/**
* SSL Connection initialization.
* SSL Connection initialization.
*/
*/
static
int
redisSSLConnect
(
redisContext
*
c
,
SSL_CTX
*
ssl_ctx
,
SSL
*
ssl
)
{
redisSSLContext
*
rssl
;
static
int
redisSSLConnect
(
redisContext
*
c
,
SSL
*
ssl
)
{
if
(
c
->
privdata
)
{
if
(
c
->
privdata
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"redisContext was already associated"
);
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"redisContext was already associated"
);
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
rssl
=
hi_calloc
(
1
,
sizeof
(
redisSSL
Context
));
redisSSL
*
rssl
=
hi_calloc
(
1
,
sizeof
(
redisSSL
));
if
(
rssl
==
NULL
)
{
if
(
rssl
==
NULL
)
{
__redisSetError
(
c
,
REDIS_ERR_OOM
,
"Out of memory"
);
__redisSetError
(
c
,
REDIS_ERR_OOM
,
"Out of memory"
);
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
c
->
funcs
=
&
redisContextSSLFuncs
;
c
->
funcs
=
&
redisContextSSLFuncs
;
rssl
->
ssl_ctx
=
ssl_ctx
;
rssl
->
ssl
=
ssl
;
rssl
->
ssl
=
ssl
;
SSL_set_mode
(
rssl
->
ssl
,
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
);
SSL_set_mode
(
rssl
->
ssl
,
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
);
...
@@ -238,84 +313,53 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
...
@@ -238,84 +313,53 @@ static int redisSSLConnect(redisContext *c, SSL_CTX *ssl_ctx, SSL *ssl) {
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
/**
* A wrapper around redisSSLConnect() for users who manage their own context and
* create their own SSL object.
*/
int
redisInitiateSSL
(
redisContext
*
c
,
SSL
*
ssl
)
{
int
redisInitiateSSL
(
redisContext
*
c
,
SSL
*
ssl
)
{
return
redisSSLConnect
(
c
,
NULL
,
ssl
);
return
redisSSLConnect
(
c
,
ssl
);
}
}
int
redisSecureConnection
(
redisContext
*
c
,
const
char
*
capath
,
/**
const
char
*
certpath
,
const
char
*
keypath
,
const
char
*
servername
)
{
* A wrapper around redisSSLConnect() for users who use redisSSLContext and don't
* manage their own SSL objects.
SSL_CTX
*
ssl_ctx
=
NULL
;
*/
SSL
*
ssl
=
NULL
;
/* Initialize global OpenSSL stuff */
static
int
isInit
=
0
;
if
(
!
isInit
)
{
isInit
=
1
;
SSL_library_init
();
#ifdef HIREDIS_USE_CRYPTO_LOCKS
if
(
initOpensslLocks
()
==
REDIS_ERR
)
{
__redisSetError
(
c
,
REDIS_ERR_OOM
,
"Out of memory"
);
goto
error
;
}
#endif
}
ssl_ctx
=
SSL_CTX_new
(
SSLv23_client_method
());
if
(
!
ssl_ctx
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Failed to create SSL_CTX"
);
goto
error
;
}
#ifdef HIREDIS_SSL_TRACE
int
redisInitiateSSLWithContext
(
redisContext
*
c
,
redisSSLContext
*
redis_ssl_ctx
)
SSL_CTX_set_info_callback
(
ssl_ctx
,
sslLogCallback
);
{
#endif
if
(
!
c
||
!
redis_ssl_ctx
)
SSL_CTX_set_options
(
ssl_ctx
,
SSL_OP_NO_SSLv2
|
SSL_OP_NO_SSLv3
);
return
REDIS_ERR
;
SSL_CTX_set_verify
(
ssl_ctx
,
SSL_VERIFY_PEER
,
NULL
);
if
((
certpath
!=
NULL
&&
keypath
==
NULL
)
||
(
keypath
!=
NULL
&&
certpath
==
NULL
))
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"certpath and keypath must be specified together"
);
goto
error
;
}
if
(
capath
)
{
/* We want to verify that redisSSLConnect() won't fail on this, as it will
if
(
!
SSL_CTX_load_verify_locations
(
ssl_ctx
,
capath
,
NULL
))
{
* not own the SSL object in that case and we'll end up leaking.
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Invalid CA certificate"
);
*/
goto
error
;
if
(
c
->
privdata
)
}
return
REDIS_ERR
;
}
if
(
certpath
)
{
if
(
!
SSL_CTX_use_certificate_chain_file
(
ssl_ctx
,
certpath
))
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Invalid client certificate"
);
goto
error
;
}
if
(
!
SSL_CTX_use_PrivateKey_file
(
ssl_ctx
,
keypath
,
SSL_FILETYPE_PEM
))
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Invalid client key"
);
goto
error
;
}
}
ssl
=
SSL_new
(
ssl_ctx
);
SSL
*
ssl
=
SSL_new
(
redis_ssl_ctx
->
ssl_ctx
);
if
(
!
ssl
)
{
if
(
!
ssl
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Couldn't create new SSL instance"
);
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Couldn't create new SSL instance"
);
goto
error
;
goto
error
;
}
}
if
(
servername
)
{
if
(
!
SSL_set_tlsext_host_name
(
ssl
,
servername
))
{
if
(
redis_ssl_ctx
->
server_name
)
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Couldn't set server name indication"
);
if
(
!
SSL_set_tlsext_host_name
(
ssl
,
redis_ssl_ctx
->
server_name
))
{
__redisSetError
(
c
,
REDIS_ERR_OTHER
,
"Failed to set server_name/SNI"
);
goto
error
;
goto
error
;
}
}
}
}
if
(
redisSSLConnect
(
c
,
ssl_ctx
,
ssl
)
==
REDIS_OK
)
return
redisSSLConnect
(
c
,
ssl
);
return
REDIS_OK
;
error:
error:
if
(
ssl
)
SSL_free
(
ssl
);
if
(
ssl
)
if
(
ssl_ctx
)
SSL_
CTX_
free
(
ssl
_ctx
);
SSL_free
(
ssl
);
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
static
int
maybeCheckWant
(
redisSSL
Context
*
rssl
,
int
rv
)
{
static
int
maybeCheckWant
(
redisSSL
*
rssl
,
int
rv
)
{
/**
/**
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
* If the error is WANT_READ or WANT_WRITE, the appropriate flags are set
* and true is returned. False is returned otherwise
* and true is returned. False is returned otherwise
...
@@ -335,23 +379,19 @@ static int maybeCheckWant(redisSSLContext *rssl, int rv) {
...
@@ -335,23 +379,19 @@ static int maybeCheckWant(redisSSLContext *rssl, int rv) {
* Implementation of redisContextFuncs for SSL connections.
* Implementation of redisContextFuncs for SSL connections.
*/
*/
static
void
redisSSLFree
Context
(
void
*
privdata
){
static
void
redisSSLFree
(
void
*
privdata
){
redisSSL
Context
*
rsc
=
privdata
;
redisSSL
*
rsc
=
privdata
;
if
(
!
rsc
)
return
;
if
(
!
rsc
)
return
;
if
(
rsc
->
ssl
)
{
if
(
rsc
->
ssl
)
{
SSL_free
(
rsc
->
ssl
);
SSL_free
(
rsc
->
ssl
);
rsc
->
ssl
=
NULL
;
rsc
->
ssl
=
NULL
;
}
}
if
(
rsc
->
ssl_ctx
)
{
SSL_CTX_free
(
rsc
->
ssl_ctx
);
rsc
->
ssl_ctx
=
NULL
;
}
hi_free
(
rsc
);
hi_free
(
rsc
);
}
}
static
int
redisSSLRead
(
redisContext
*
c
,
char
*
buf
,
size_t
bufcap
)
{
static
int
redisSSLRead
(
redisContext
*
c
,
char
*
buf
,
size_t
bufcap
)
{
redisSSL
Context
*
rssl
=
c
->
privdata
;
redisSSL
*
rssl
=
c
->
privdata
;
int
nread
=
SSL_read
(
rssl
->
ssl
,
buf
,
bufcap
);
int
nread
=
SSL_read
(
rssl
->
ssl
,
buf
,
bufcap
);
if
(
nread
>
0
)
{
if
(
nread
>
0
)
{
...
@@ -393,7 +433,7 @@ static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
...
@@ -393,7 +433,7 @@ static int redisSSLRead(redisContext *c, char *buf, size_t bufcap) {
}
}
static
int
redisSSLWrite
(
redisContext
*
c
)
{
static
int
redisSSLWrite
(
redisContext
*
c
)
{
redisSSL
Context
*
rssl
=
c
->
privdata
;
redisSSL
*
rssl
=
c
->
privdata
;
size_t
len
=
rssl
->
lastLen
?
rssl
->
lastLen
:
sdslen
(
c
->
obuf
);
size_t
len
=
rssl
->
lastLen
?
rssl
->
lastLen
:
sdslen
(
c
->
obuf
);
int
rv
=
SSL_write
(
rssl
->
ssl
,
c
->
obuf
,
len
);
int
rv
=
SSL_write
(
rssl
->
ssl
,
c
->
obuf
,
len
);
...
@@ -416,7 +456,7 @@ static int redisSSLWrite(redisContext *c) {
...
@@ -416,7 +456,7 @@ static int redisSSLWrite(redisContext *c) {
static
void
redisSSLAsyncRead
(
redisAsyncContext
*
ac
)
{
static
void
redisSSLAsyncRead
(
redisAsyncContext
*
ac
)
{
int
rv
;
int
rv
;
redisSSL
Context
*
rssl
=
ac
->
c
.
privdata
;
redisSSL
*
rssl
=
ac
->
c
.
privdata
;
redisContext
*
c
=
&
ac
->
c
;
redisContext
*
c
=
&
ac
->
c
;
rssl
->
wantRead
=
0
;
rssl
->
wantRead
=
0
;
...
@@ -446,7 +486,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
...
@@ -446,7 +486,7 @@ static void redisSSLAsyncRead(redisAsyncContext *ac) {
static
void
redisSSLAsyncWrite
(
redisAsyncContext
*
ac
)
{
static
void
redisSSLAsyncWrite
(
redisAsyncContext
*
ac
)
{
int
rv
,
done
=
0
;
int
rv
,
done
=
0
;
redisSSL
Context
*
rssl
=
ac
->
c
.
privdata
;
redisSSL
*
rssl
=
ac
->
c
.
privdata
;
redisContext
*
c
=
&
ac
->
c
;
redisContext
*
c
=
&
ac
->
c
;
rssl
->
pendingWrite
=
0
;
rssl
->
pendingWrite
=
0
;
...
@@ -475,7 +515,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
...
@@ -475,7 +515,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
}
}
redisContextFuncs
redisContextSSLFuncs
=
{
redisContextFuncs
redisContextSSLFuncs
=
{
.
free_privdata
=
redisSSLFree
Context
,
.
free_privdata
=
redisSSLFree
,
.
async_read
=
redisSSLAsyncRead
,
.
async_read
=
redisSSLAsyncRead
,
.
async_write
=
redisSSLAsyncWrite
,
.
async_write
=
redisSSLAsyncWrite
,
.
read
=
redisSSLRead
,
.
read
=
redisSSLRead
,
...
...
test.c
View file @
190bca88
...
@@ -48,6 +48,10 @@ struct config {
...
@@ -48,6 +48,10 @@ struct config {
}
ssl
;
}
ssl
;
};
};
#ifdef HIREDIS_TEST_SSL
redisSSLContext
*
_ssl_ctx
=
NULL
;
#endif
/* The following lines make up our testing "framework" :) */
/* The following lines make up our testing "framework" :) */
static
int
tests
=
0
,
fails
=
0
,
skips
=
0
;
static
int
tests
=
0
,
fails
=
0
,
skips
=
0
;
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
#define test(_s) { printf("#%02d ", ++tests); printf(_s); }
...
@@ -113,9 +117,9 @@ static int disconnect(redisContext *c, int keep_fd) {
...
@@ -113,9 +117,9 @@ static int disconnect(redisContext *c, int keep_fd) {
return
-
1
;
return
-
1
;
}
}
static
void
do_ssl_handshake
(
redisContext
*
c
,
struct
config
config
)
{
static
void
do_ssl_handshake
(
redisContext
*
c
)
{
#ifdef HIREDIS_TEST_SSL
#ifdef HIREDIS_TEST_SSL
redis
SecureConnection
(
c
,
config
.
ssl
.
ca_cert
,
config
.
ssl
.
cert
,
config
.
ssl
.
key
,
NULL
);
redis
InitiateSSLWithContext
(
c
,
_ssl_ctx
);
if
(
c
->
err
)
{
if
(
c
->
err
)
{
printf
(
"SSL error: %s
\n
"
,
c
->
errstr
);
printf
(
"SSL error: %s
\n
"
,
c
->
errstr
);
redisFree
(
c
);
redisFree
(
c
);
...
@@ -123,7 +127,6 @@ static void do_ssl_handshake(redisContext *c, struct config config) {
...
@@ -123,7 +127,6 @@ static void do_ssl_handshake(redisContext *c, struct config config) {
}
}
#else
#else
(
void
)
c
;
(
void
)
c
;
(
void
)
config
;
#endif
#endif
}
}
...
@@ -158,7 +161,7 @@ static redisContext *do_connect(struct config config) {
...
@@ -158,7 +161,7 @@ static redisContext *do_connect(struct config config) {
}
}
if
(
config
.
type
==
CONN_SSL
)
{
if
(
config
.
type
==
CONN_SSL
)
{
do_ssl_handshake
(
c
,
config
);
do_ssl_handshake
(
c
);
}
}
return
select_database
(
c
);
return
select_database
(
c
);
...
@@ -168,7 +171,7 @@ static void do_reconnect(redisContext *c, struct config config) {
...
@@ -168,7 +171,7 @@ static void do_reconnect(redisContext *c, struct config config) {
redisReconnect
(
c
);
redisReconnect
(
c
);
if
(
config
.
type
==
CONN_SSL
)
{
if
(
config
.
type
==
CONN_SSL
)
{
do_ssl_handshake
(
c
,
config
);
do_ssl_handshake
(
c
);
}
}
}
}
...
@@ -1147,6 +1150,11 @@ int main(int argc, char **argv) {
...
@@ -1147,6 +1150,11 @@ int main(int argc, char **argv) {
#ifdef HIREDIS_TEST_SSL
#ifdef HIREDIS_TEST_SSL
if
(
cfg
.
ssl
.
port
&&
cfg
.
ssl
.
host
)
{
if
(
cfg
.
ssl
.
port
&&
cfg
.
ssl
.
host
)
{
redisInitOpenSSL
();
_ssl_ctx
=
redisCreateSSLContext
(
cfg
.
ssl
.
ca_cert
,
NULL
,
cfg
.
ssl
.
cert
,
cfg
.
ssl
.
key
,
NULL
,
NULL
);
assert
(
_ssl_ctx
!=
NULL
);
printf
(
"
\n
Testing against SSL connection (%s:%d):
\n
"
,
cfg
.
ssl
.
host
,
cfg
.
ssl
.
port
);
printf
(
"
\n
Testing against SSL connection (%s:%d):
\n
"
,
cfg
.
ssl
.
host
,
cfg
.
ssl
.
port
);
cfg
.
type
=
CONN_SSL
;
cfg
.
type
=
CONN_SSL
;
...
@@ -1156,6 +1164,9 @@ int main(int argc, char **argv) {
...
@@ -1156,6 +1164,9 @@ int main(int argc, char **argv) {
test_invalid_timeout_errors
(
cfg
);
test_invalid_timeout_errors
(
cfg
);
test_append_formatted_commands
(
cfg
);
test_append_formatted_commands
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
if
(
throughput
)
test_throughput
(
cfg
);
redisFreeSSLContext
(
_ssl_ctx
);
_ssl_ctx
=
NULL
;
}
}
#endif
#endif
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment