Merge branch 'ssl_fixes'

This commit is contained in:
Jan Vidar Krey 2014-07-30 11:47:27 +02:00
commit 4263750bc5
9 changed files with 202 additions and 58 deletions

View File

@ -428,7 +428,6 @@
<since>0.3.3</since>
</option>
<option name="tls_certificate" type="file" default="">
<short>Certificate file</short>
<description><![CDATA[
@ -445,6 +444,40 @@
<since>0.3.0</since>
</option>
<option name="tls_ciphersuite" type="string" default="ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS">
<short>List of TLS ciphers to use</short>
<description><![CDATA[
This is a colon separated list of preferred ciphers in the OpenSSL format.
]]></description>
<since>0.5.0</since>
<example><![CDATA[
<p>
High security with emphasis on forward secrecy:<br />
tls_ciphersuite = "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS"
</p>
<p>
Allow ChaCha20/Poly1305 which are secure, yet generally faster:<br />
tls_ciphersuite = "ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:DHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA"
</p>
]]></example>
</option>
<option name="tls_version" type="string" default="1.2">
<short>Specify minimum TLS version supported.</short>
<description><![CDATA[
<p>
This allows you to specify the minimum TLS version the hub requires from connecting clients in order to
connect to the hub.
</p>
<p>
TLS version 1.2 is recommended and enabled by default.
TLS version 1.1 is acceptable without any known flaws, and allows for older clients to connect.
TLS version 1.0 should be avoided, even though it is the most compatible with older ADC clients.
</p>
]]></description>
<since>0.5.0</since>
</option>
<option name="file_acl" type="file" default="">
<short>File containing access control lists</short>
<description><![CDATA[

View File

@ -3,7 +3,7 @@
* Copyright (C) 2007-2014, Jan Vidar Krey
*
* THIS FILE IS AUTOGENERATED - DO NOT MODIFY
* Created 2014-05-14 11:38, by config.py
* Created 2014-07-29 12:22, by config.py
*/
void config_defaults(struct hub_config* config)
@ -51,6 +51,8 @@ void config_defaults(struct hub_config* config)
config->tls_require_redirect_addr = hub_strdup("");
config->tls_certificate = hub_strdup("");
config->tls_private_key = hub_strdup("");
config->tls_ciphersuite = hub_strdup("ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS");
config->tls_version = hub_strdup("1.2");
config->file_acl = hub_strdup("");
config->file_plugins = hub_strdup("");
config->msg_hub_full = hub_strdup("Hub is full");
@ -552,6 +554,26 @@ static int apply_config(struct hub_config* config, char* key, char* data, int li
return 0;
}
if (!strcmp(key, "tls_ciphersuite"))
{
if (!apply_string(key, data, &config->tls_ciphersuite, (char*) ""))
{
LOG_ERROR("Configuration parse error on line %d", line_count);
return -1;
}
return 0;
}
if (!strcmp(key, "tls_version"))
{
if (!apply_string(key, data, &config->tls_version, (char*) ""))
{
LOG_ERROR("Configuration parse error on line %d", line_count);
return -1;
}
return 0;
}
if (!strcmp(key, "file_acl"))
{
if (!apply_string(key, data, &config->file_acl, (char*) ""))
@ -955,6 +977,10 @@ void free_config(struct hub_config* config)
hub_free(config->tls_private_key);
hub_free(config->tls_ciphersuite);
hub_free(config->tls_version);
hub_free(config->file_acl);
hub_free(config->file_plugins);
@ -1164,6 +1190,12 @@ void dump_config(struct hub_config* config, int ignore_defaults)
if (!ignore_defaults || strcmp(config->tls_private_key, "") != 0)
fprintf(stdout, "tls_private_key = \"%s\"\n", config->tls_private_key);
if (!ignore_defaults || strcmp(config->tls_ciphersuite, "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS") != 0)
fprintf(stdout, "tls_ciphersuite = \"%s\"\n", config->tls_ciphersuite);
if (!ignore_defaults || strcmp(config->tls_version, "1.2") != 0)
fprintf(stdout, "tls_version = \"%s\"\n", config->tls_version);
if (!ignore_defaults || strcmp(config->file_acl, "") != 0)
fprintf(stdout, "file_acl = \"%s\"\n", config->file_acl);

View File

@ -3,7 +3,7 @@
* Copyright (C) 2007-2014, Jan Vidar Krey
*
* THIS FILE IS AUTOGENERATED - DO NOT MODIFY
* Created 2014-05-14 11:38, by config.py
* Created 2014-07-29 12:22, by config.py
*/
struct hub_config
@ -51,6 +51,8 @@ struct hub_config
char* tls_require_redirect_addr; /*<<< A redirect address in case a client connects using "adc://" when "adcs://" is required. (default: "") */
char* tls_certificate; /*<<< Certificate file (default: "") */
char* tls_private_key; /*<<< Private key file (default: "") */
char* tls_ciphersuite; /*<<< List of TLS ciphers to use (default: "ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+3DES:!aNULL:!MD5:!DSS") */
char* tls_version; /*<<< Specify minimum TLS version supported. (default: "1.2") */
char* file_acl; /*<<< File containing access control lists (default: "") */
char* file_plugins; /*<<< Plugin configuration file (default: "") */
char* msg_hub_full; /*<<< "Hub is full" */

View File

@ -731,7 +731,11 @@ static int load_ssl_certificates(struct hub_info* hub, struct hub_config* config
{
if (config->tls_enable)
{
hub->ctx = net_ssl_context_create();
hub->ctx = net_ssl_context_create(config->tls_version, config->tls_ciphersuite);
if (!hub->ctx)
return 0;
if (ssl_load_certificate(hub->ctx, config->tls_certificate) &&
ssl_load_private_key(hub->ctx, config->tls_private_key) &&
ssl_check_private_key(hub->ctx))

View File

@ -102,14 +102,7 @@ void net_backend_shutdown()
}
void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events)
{
con->callback = callback;
con->ptr = (void*) ptr;
net_con_update(con, events);
}
void net_con_update(struct net_connection* con, int events)
void net_backend_update(struct net_connection* con, int events)
{
g_backend->handler.con_mod(g_backend->data, con, events);
}

View File

@ -75,6 +75,14 @@ extern void net_backend_shutdown();
*/
extern int net_backend_process();
/**
* Update the event mask.
*
* @param con Connection handle.
* @param events Event mask (NET_EVENT_*)
*/
extern void net_backend_update(struct net_connection* con, int events);
/**
* Get the current time.
*/

View File

@ -19,6 +19,7 @@
#include "uhub.h"
#include "network/common.h"
#include "network/backend.h"
static int is_blocked_or_interrupted()
{
@ -116,10 +117,27 @@ void* net_con_get_ptr(struct net_connection* con)
return con->ptr;
}
void net_con_destroy(struct net_connection* con)
void net_con_update(struct net_connection* con, int events)
{
#ifdef SSL_SUPPORT
if (con->ssl)
net_ssl_update(con, events);
else
#endif
net_backend_update(con, events);
}
void net_con_reinitialize(struct net_connection* con, net_connection_cb callback, const void* ptr, int events)
{
con->callback = callback;
con->ptr = (void*) ptr;
net_con_update(con, events);
}
void net_con_destroy(struct net_connection* con)
{
#ifdef SSL_SUPPORT
if (con && con->ssl)
net_ssl_destroy(con);
#endif
hub_free(con);

View File

@ -20,6 +20,7 @@
#include "uhub.h"
#include "network/common.h"
#include "network/tls.h"
#include "network/backend.h"
#ifdef SSL_SUPPORT
#ifdef SSL_USE_OPENSSL
@ -32,6 +33,9 @@ struct net_ssl_openssl
SSL* ssl;
BIO* bio;
enum ssl_state state;
int events;
int ssl_read_events;
int ssl_write_events;
uint32_t flags;
size_t bytes_rx;
size_t bytes_tx;
@ -39,8 +43,7 @@ struct net_ssl_openssl
struct net_context_openssl
{
SSL_METHOD* ssl_method;
SSL_CTX* ssl_ctx;
SSL_CTX* ssl;
};
static struct net_ssl_openssl* get_handle(struct net_connection* con)
@ -93,26 +96,61 @@ static void add_io_stats(struct net_ssl_openssl* handle)
}
}
static const SSL_METHOD* get_ssl_method(const char* tls_version)
{
if (!tls_version || !*tls_version)
{
LOG_ERROR("tls_version is not set.");
return 0;
}
if (!strcmp(tls_version, "1.0"))
return TLSv1_method();
if (!strcmp(tls_version, "1.1"))
return TLSv1_1_method();
if (!strcmp(tls_version, "1.2"))
return TLSv1_2_method();
LOG_ERROR("Unable to recognize tls_version.");
return 0;
}
/**
* Create a new SSL context.
*/
struct ssl_context_handle* net_ssl_context_create()
struct ssl_context_handle* net_ssl_context_create(const char* tls_version, const char* tls_ciphersuite)
{
struct net_context_openssl* ctx = (struct net_context_openssl*) hub_malloc_zero(sizeof(struct net_context_openssl));
ctx->ssl_method = (SSL_METHOD*) SSLv23_method(); /* TLSv1_method() */
ctx->ssl_ctx = SSL_CTX_new(ctx->ssl_method);
const SSL_METHOD* ssl_method = get_ssl_method(tls_version);
if (!ssl_method)
{
hub_free(ctx);
return 0;
}
ctx->ssl = SSL_CTX_new(ssl_method);
/* Disable SSLv2 */
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ctx->ssl, SSL_OP_NO_SSLv2);
// FIXME: Why did we need this again?
SSL_CTX_set_quiet_shutdown(ctx->ssl, 1);
#ifdef SSL_OP_NO_COMPRESSION
/* Disable compression? */
/* Disable compression */
LOG_TRACE("Disabling SSL compression."); /* "CRIME" attack */
SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_COMPRESSION);
SSL_CTX_set_options(ctx->ssl, SSL_OP_NO_COMPRESSION);
#endif
SSL_CTX_set_quiet_shutdown(ctx->ssl_ctx, 1);
/* Set preferred cipher suite */
if (SSL_CTX_set_cipher_list(ctx->ssl, tls_ciphersuite) != 1)
{
LOG_ERROR("Unable to set cipher suite.");
SSL_CTX_free(ctx->ssl);
hub_free(ctx);
return 0;
}
return (struct ssl_context_handle*) ctx;
}
@ -120,16 +158,16 @@ struct ssl_context_handle* net_ssl_context_create()
extern void net_ssl_context_destroy(struct ssl_context_handle* ctx_)
{
struct net_context_openssl* ctx = (struct net_context_openssl*) ctx_;
SSL_CTX_free(ctx->ssl_ctx);
SSL_CTX_free(ctx->ssl);
hub_free(ctx);
}
int ssl_load_certificate(struct ssl_context_handle* ctx_, const char* pem_file)
{
struct net_context_openssl* ctx = (struct net_context_openssl*) ctx_;
if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, pem_file) < 0)
if (SSL_CTX_use_certificate_chain_file(ctx->ssl, pem_file) < 0)
{
LOG_ERROR("SSL_CTX_use_certificate_file: %s", ERR_error_string(ERR_get_error(), NULL));
LOG_ERROR("SSL_CTX_use_certificate_chain_file: %s", ERR_error_string(ERR_get_error(), NULL));
return 0;
}
@ -139,7 +177,7 @@ int ssl_load_certificate(struct ssl_context_handle* ctx_, const char* pem_file)
int ssl_load_private_key(struct ssl_context_handle* ctx_, const char* pem_file)
{
struct net_context_openssl* ctx = (struct net_context_openssl*) ctx_;
if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem_file, SSL_FILETYPE_PEM) < 0)
if (SSL_CTX_use_PrivateKey_file(ctx->ssl, pem_file, SSL_FILETYPE_PEM) < 0)
{
LOG_ERROR("SSL_CTX_use_PrivateKey_file: %s", ERR_error_string(ERR_get_error(), NULL));
return 0;
@ -150,7 +188,7 @@ int ssl_load_private_key(struct ssl_context_handle* ctx_, const char* pem_file)
int ssl_check_private_key(struct ssl_context_handle* ctx_)
{
struct net_context_openssl* ctx = (struct net_context_openssl*) ctx_;
if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 1)
if (SSL_CTX_check_private_key(ctx->ssl) != 1)
{
LOG_FATAL("SSL_CTX_check_private_key: Private key does not match the certificate public key: %s", ERR_error_string(ERR_get_error(), NULL));
return 0;
@ -158,7 +196,7 @@ int ssl_check_private_key(struct ssl_context_handle* ctx_)
return 1;
}
static int handle_openssl_error(struct net_connection* con, int ret, enum ssl_state forced_rwstate)
static int handle_openssl_error(struct net_connection* con, int ret, int read)
{
struct net_ssl_openssl* handle = get_handle(con);
int err = SSL_get_error(handle->ssl, ret);
@ -169,13 +207,17 @@ static int handle_openssl_error(struct net_connection* con, int ret, enum ssl_st
return -1;
case SSL_ERROR_WANT_READ:
handle->state = forced_rwstate;
net_con_update(con, NET_EVENT_READ);
if (read)
handle->ssl_read_events = NET_EVENT_READ;
else
handle->ssl_write_events = NET_EVENT_READ;
return 0;
case SSL_ERROR_WANT_WRITE:
handle->state = forced_rwstate;
net_con_update(con, NET_EVENT_WRITE);
if (read)
handle->ssl_read_events = NET_EVENT_WRITE;
else
handle->ssl_write_events = NET_EVENT_WRITE;
return 0;
case SSL_ERROR_SYSCALL:
@ -230,7 +272,7 @@ ssize_t net_con_ssl_handshake(struct net_connection* con, enum net_con_ssl_mode
if (ssl_mode == net_con_ssl_mode_server)
{
handle->ssl = SSL_new(ctx->ssl_ctx);
handle->ssl = SSL_new(ctx->ssl);
if (!handle->ssl)
{
LOG_ERROR("Unable to create new SSL stream\n");
@ -249,26 +291,26 @@ ssize_t net_con_ssl_handshake(struct net_connection* con, enum net_con_ssl_mode
con->ssl = (struct ssl_handle*) handle;
return net_con_ssl_connect(con);
}
}
ssize_t net_ssl_send(struct net_connection* con, const void* buf, size_t len)
{
struct net_ssl_openssl* handle = get_handle(con);
uhub_assert(handle->state == tls_st_connected || handle->state == tls_st_need_write);
uhub_assert(handle->state == tls_st_connected);
ERR_clear_error();
ssize_t ret = SSL_write(handle->ssl, buf, len);
add_io_stats(handle);
LOG_PROTO("SSL_write(con=%p, buf=%p, len=" PRINTF_SIZE_T ") => %d", con, buf, len, ret);
if (ret > 0)
{
handle->state = tls_st_connected;
handle->ssl_write_events = 0;
else
ret = handle_openssl_error(con, ret, 0);
net_ssl_update(con, handle->events); // Update backend only
return ret;
}
return handle_openssl_error(con, ret, tls_st_need_write);
}
ssize_t net_ssl_recv(struct net_connection* con, void* buf, size_t len)
{
@ -278,7 +320,7 @@ ssize_t net_ssl_recv(struct net_connection* con, void* buf, size_t len)
if (handle->state == tls_st_error)
return -2;
uhub_assert(handle->state == tls_st_connected || handle->state == tls_st_need_read);
uhub_assert(handle->state == tls_st_connected);
ERR_clear_error();
@ -286,11 +328,19 @@ ssize_t net_ssl_recv(struct net_connection* con, void* buf, size_t len)
add_io_stats(handle);
LOG_PROTO("SSL_read(con=%p, buf=%p, len=" PRINTF_SIZE_T ") => %d", con, buf, len, ret);
if (ret > 0)
{
handle->state = tls_st_connected;
handle->ssl_read_events = 0;
else
ret = handle_openssl_error(con, ret, 1);
net_ssl_update(con, handle->events); // Update backend only
return ret;
}
return handle_openssl_error(con, ret, tls_st_need_read);
void net_ssl_update(struct net_connection* con, int events)
{
struct net_ssl_openssl* handle = get_handle(con);
handle->events = events;
net_backend_update(con, handle->events | handle->ssl_read_events | handle->ssl_write_events);
}
void net_ssl_shutdown(struct net_connection* con)
@ -331,15 +381,11 @@ void net_ssl_callback(struct net_connection* con, int events)
con->callback(con, NET_EVENT_READ, con->ptr);
break;
case tls_st_need_read:
con->callback(con, NET_EVENT_READ, con->ptr);
break;
case tls_st_need_write:
con->callback(con, NET_EVENT_WRITE, con->ptr);
break;
case tls_st_connected:
if (handle->ssl_read_events & events)
events |= NET_EVENT_READ;
if (handle->ssl_write_events & events)
events |= NET_EVENT_WRITE;
con->callback(con, events, con->ptr);
break;

View File

@ -32,8 +32,6 @@ enum ssl_state
tls_st_accepting,
tls_st_connecting,
tls_st_connected,
tls_st_need_read, /* special case of connected */
tls_st_need_write, /* special case of connected */
tls_st_disconnecting,
};
@ -58,8 +56,9 @@ extern int net_ssl_library_shutdown();
/**
* Create a new SSL context.
* Specify a TLS version as a string: "1.2" for TLS 1.2.
*/
extern struct ssl_context_handle* net_ssl_context_create();
extern struct ssl_context_handle* net_ssl_context_create(const char* tls_version, const char* tls_ciphersuite);
extern void net_ssl_context_destroy(struct ssl_context_handle* ctx);
/**
@ -90,6 +89,15 @@ extern ssize_t net_con_ssl_connect(struct net_connection*);
extern ssize_t net_ssl_send(struct net_connection* con, const void* buf, size_t len);
extern ssize_t net_ssl_recv(struct net_connection* con, void* buf, size_t len);
/**
* Update the event mask. Additional events may be requested depending on the
* needs of the TLS layer.
*
* @param con Connection handle.
* @param events Event mask (NET_EVENT_*)
*/
extern void net_ssl_update(struct net_connection* con, int events);
extern void net_ssl_shutdown(struct net_connection* con);
extern void net_ssl_destroy(struct net_connection* con);
extern void net_ssl_callback(struct net_connection* con, int events);