2012-10-12 12:24:03 +00:00
|
|
|
/*
|
|
|
|
* uhub - A tiny ADC p2p connection hub
|
2014-05-14 09:38:08 +00:00
|
|
|
* Copyright (C) 2007-2014, Jan Vidar Krey
|
2012-10-12 12:24:03 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "uhub.h"
|
|
|
|
#include "network/common.h"
|
|
|
|
#include "network/tls.h"
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
#include "network/backend.h"
|
2012-10-12 12:24:03 +00:00
|
|
|
|
|
|
|
#ifdef SSL_SUPPORT
|
|
|
|
#ifdef SSL_USE_OPENSSL
|
|
|
|
|
2012-10-17 20:40:15 +00:00
|
|
|
void net_stats_add_tx(size_t bytes);
|
|
|
|
void net_stats_add_rx(size_t bytes);
|
2012-10-15 18:39:03 +00:00
|
|
|
|
2012-10-12 12:24:03 +00:00
|
|
|
struct net_ssl_openssl
|
|
|
|
{
|
|
|
|
SSL* ssl;
|
2012-10-17 20:40:15 +00:00
|
|
|
BIO* bio;
|
2012-10-12 12:24:03 +00:00
|
|
|
enum ssl_state state;
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
int events;
|
|
|
|
int ssl_read_events;
|
|
|
|
int ssl_write_events;
|
2012-10-16 18:15:38 +00:00
|
|
|
uint32_t flags;
|
2012-10-17 20:40:15 +00:00
|
|
|
size_t bytes_rx;
|
|
|
|
size_t bytes_tx;
|
2012-10-12 12:24:03 +00:00
|
|
|
};
|
|
|
|
|
2012-10-15 18:39:03 +00:00
|
|
|
struct net_context_openssl
|
|
|
|
{
|
2014-07-29 11:31:42 +00:00
|
|
|
SSL_CTX* ssl;
|
2012-10-15 18:39:03 +00:00
|
|
|
};
|
|
|
|
|
2012-10-12 12:24:03 +00:00
|
|
|
static struct net_ssl_openssl* get_handle(struct net_connection* con)
|
|
|
|
{
|
|
|
|
uhub_assert(con);
|
|
|
|
return (struct net_ssl_openssl*) con->ssl;
|
|
|
|
}
|
|
|
|
|
2014-10-16 21:13:26 +00:00
|
|
|
#ifdef DEBUG
|
2014-10-16 21:08:17 +00:00
|
|
|
static const char* get_state_str(enum ssl_state state)
|
|
|
|
{
|
|
|
|
switch (state)
|
|
|
|
{
|
|
|
|
case tls_st_none: return "tls_st_none";
|
|
|
|
case tls_st_error: return "tls_st_error";
|
|
|
|
case tls_st_accepting: return "tls_st_accepting";
|
|
|
|
case tls_st_connecting: return "tls_st_connecting";
|
|
|
|
case tls_st_connected: return "tls_st_connected";
|
|
|
|
case tls_st_disconnecting: return "tls_st_disconnecting";
|
|
|
|
}
|
|
|
|
uhub_assert(!"This should not happen - invalid state!");
|
|
|
|
return "(UNKNOWN STATE)";
|
|
|
|
}
|
2014-10-16 21:13:26 +00:00
|
|
|
#endif
|
2014-10-16 21:08:17 +00:00
|
|
|
|
|
|
|
static void net_ssl_set_state(struct net_ssl_openssl* handle, enum ssl_state new_state)
|
|
|
|
{
|
2014-10-16 21:13:26 +00:00
|
|
|
LOG_DEBUG("net_ssl_set_state(): prev_state=%s, new_state=%s", get_state_str(handle->state), get_state_str(new_state));
|
2014-10-16 21:08:17 +00:00
|
|
|
handle->state = new_state;
|
|
|
|
}
|
|
|
|
|
2012-10-15 18:39:03 +00:00
|
|
|
const char* net_ssl_get_provider()
|
|
|
|
{
|
|
|
|
return OPENSSL_VERSION_TEXT;
|
|
|
|
}
|
|
|
|
|
2012-10-17 18:53:05 +00:00
|
|
|
int net_ssl_library_init()
|
|
|
|
{
|
|
|
|
LOG_TRACE("Initializing OpenSSL...");
|
|
|
|
SSL_library_init();
|
|
|
|
SSL_load_error_strings();
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int net_ssl_library_shutdown()
|
|
|
|
{
|
|
|
|
ERR_clear_error();
|
2018-02-26 10:58:41 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
2012-10-17 18:53:05 +00:00
|
|
|
ERR_remove_state(0);
|
2018-02-26 10:58:41 +00:00
|
|
|
#endif
|
2012-10-17 18:53:05 +00:00
|
|
|
|
|
|
|
ENGINE_cleanup();
|
|
|
|
CONF_modules_unload(1);
|
|
|
|
|
2014-10-16 21:08:17 +00:00
|
|
|
ERR_free_strings();
|
2012-10-17 18:53:05 +00:00
|
|
|
EVP_cleanup();
|
2014-10-16 21:08:17 +00:00
|
|
|
CRYPTO_cleanup_all_ex_data();
|
2013-03-22 19:00:40 +00:00
|
|
|
|
2012-10-17 18:53:05 +00:00
|
|
|
// sk_SSL_COMP_free(SSL_COMP_get_compression_methods());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2012-10-17 20:40:15 +00:00
|
|
|
static void add_io_stats(struct net_ssl_openssl* handle)
|
|
|
|
{
|
2018-02-26 10:58:41 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
|
|
unsigned long num_read = handle->bio->num_read;
|
|
|
|
unsigned long num_write = handle->bio->num_write;
|
|
|
|
#else
|
|
|
|
unsigned long num_read = BIO_number_read(handle->bio);
|
|
|
|
unsigned long num_write = BIO_number_written(handle->bio);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (num_read > handle->bytes_rx)
|
2012-10-17 20:40:15 +00:00
|
|
|
{
|
2018-02-26 10:58:41 +00:00
|
|
|
net_stats_add_rx(num_read - handle->bytes_rx);
|
|
|
|
handle->bytes_rx = num_read;
|
2012-10-17 20:40:15 +00:00
|
|
|
}
|
2012-10-17 18:53:05 +00:00
|
|
|
|
2018-02-26 10:58:41 +00:00
|
|
|
if (num_write > handle->bytes_tx)
|
2012-10-17 20:40:15 +00:00
|
|
|
{
|
2018-02-26 10:58:41 +00:00
|
|
|
net_stats_add_tx(num_write - handle->bytes_tx);
|
|
|
|
handle->bytes_tx = num_write;
|
2012-10-17 20:40:15 +00:00
|
|
|
}
|
|
|
|
}
|
2012-10-15 18:39:03 +00:00
|
|
|
|
2014-07-29 11:31:42 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-26 10:58:41 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
2014-07-29 11:31:42 +00:00
|
|
|
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;
|
2018-02-26 10:58:41 +00:00
|
|
|
#else
|
|
|
|
LOG_WARN("tls_version is obsolete, and should not be used.");
|
|
|
|
return TLS_method();
|
|
|
|
#endif
|
2014-07-29 11:31:42 +00:00
|
|
|
}
|
|
|
|
|
2019-03-13 21:06:41 +00:00
|
|
|
/**
|
|
|
|
* List of supported protocols for ALPN.
|
|
|
|
* We only support "adc" protocol.
|
|
|
|
*/
|
|
|
|
unsigned char alpn_protocols[] = {
|
|
|
|
3, 'a', 'd', 'c',
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Callback for the server to select a protocol from the list
|
|
|
|
* sent by the client via ALPN.
|
|
|
|
*/
|
2019-03-31 17:33:46 +00:00
|
|
|
static int alpn_server_select_protocol(SSL *ssl, const unsigned char **out, unsigned char *outlen,
|
2019-03-13 21:06:41 +00:00
|
|
|
const unsigned char *in, unsigned int inlen, void *arg)
|
|
|
|
{
|
|
|
|
int res = SSL_select_next_proto((unsigned char **)out, outlen,
|
|
|
|
alpn_protocols, sizeof(alpn_protocols), in, inlen);
|
|
|
|
if (res == OPENSSL_NPN_NO_OVERLAP)
|
|
|
|
{
|
|
|
|
// set default protocol
|
|
|
|
*out = alpn_protocols;
|
|
|
|
*outlen = 1+alpn_protocols[0];
|
|
|
|
}
|
|
|
|
return SSL_TLSEXT_ERR_OK;
|
|
|
|
}
|
|
|
|
|
2012-10-15 18:39:03 +00:00
|
|
|
/**
|
|
|
|
* Create a new SSL context.
|
|
|
|
*/
|
2014-07-29 11:31:42 +00:00
|
|
|
struct ssl_context_handle* net_ssl_context_create(const char* tls_version, const char* tls_ciphersuite)
|
2012-10-15 18:39:03 +00:00
|
|
|
{
|
|
|
|
struct net_context_openssl* ctx = (struct net_context_openssl*) hub_malloc_zero(sizeof(struct net_context_openssl));
|
2014-07-29 11:31:42 +00:00
|
|
|
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);
|
2012-10-15 18:39:03 +00:00
|
|
|
|
|
|
|
/* Disable SSLv2 */
|
2014-07-29 11:31:42 +00:00
|
|
|
SSL_CTX_set_options(ctx->ssl, SSL_OP_NO_SSLv2);
|
|
|
|
|
2014-10-16 21:09:50 +00:00
|
|
|
// #ifdef SSL_OP_NO_SSLv3
|
|
|
|
/* Disable SSLv3 */
|
|
|
|
SSL_CTX_set_options(ctx->ssl, SSL_OP_NO_SSLv3);
|
|
|
|
// #endif
|
|
|
|
|
2014-07-29 11:31:42 +00:00
|
|
|
// FIXME: Why did we need this again?
|
|
|
|
SSL_CTX_set_quiet_shutdown(ctx->ssl, 1);
|
2012-11-01 09:17:17 +00:00
|
|
|
|
|
|
|
#ifdef SSL_OP_NO_COMPRESSION
|
2014-07-29 11:31:42 +00:00
|
|
|
/* Disable compression */
|
2012-11-01 09:17:17 +00:00
|
|
|
LOG_TRACE("Disabling SSL compression."); /* "CRIME" attack */
|
2014-07-29 11:31:42 +00:00
|
|
|
SSL_CTX_set_options(ctx->ssl, SSL_OP_NO_COMPRESSION);
|
2012-11-01 09:17:17 +00:00
|
|
|
#endif
|
|
|
|
|
2014-07-29 11:31:42 +00:00
|
|
|
/* 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;
|
|
|
|
}
|
2012-10-15 18:39:03 +00:00
|
|
|
|
2019-03-13 21:06:41 +00:00
|
|
|
SSL_CTX_set_alpn_select_cb(ctx->ssl, alpn_server_select_protocol, NULL);
|
|
|
|
|
2012-10-15 18:39:03 +00:00
|
|
|
return (struct ssl_context_handle*) ctx;
|
|
|
|
}
|
|
|
|
|
2014-08-06 15:37:06 +00:00
|
|
|
void net_ssl_context_destroy(struct ssl_context_handle* ctx_)
|
2012-10-15 18:39:03 +00:00
|
|
|
{
|
|
|
|
struct net_context_openssl* ctx = (struct net_context_openssl*) ctx_;
|
2014-07-29 11:31:42 +00:00
|
|
|
SSL_CTX_free(ctx->ssl);
|
2012-10-15 18:39:03 +00:00
|
|
|
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_;
|
2014-07-29 11:31:42 +00:00
|
|
|
if (SSL_CTX_use_certificate_chain_file(ctx->ssl, pem_file) < 0)
|
2012-10-15 18:39:03 +00:00
|
|
|
{
|
2014-07-29 11:31:42 +00:00
|
|
|
LOG_ERROR("SSL_CTX_use_certificate_chain_file: %s", ERR_error_string(ERR_get_error(), NULL));
|
2012-10-15 18:39:03 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ssl_load_private_key(struct ssl_context_handle* ctx_, const char* pem_file)
|
|
|
|
{
|
|
|
|
struct net_context_openssl* ctx = (struct net_context_openssl*) ctx_;
|
2014-07-29 11:31:42 +00:00
|
|
|
if (SSL_CTX_use_PrivateKey_file(ctx->ssl, pem_file, SSL_FILETYPE_PEM) < 0)
|
2012-10-15 18:39:03 +00:00
|
|
|
{
|
|
|
|
LOG_ERROR("SSL_CTX_use_PrivateKey_file: %s", ERR_error_string(ERR_get_error(), NULL));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int ssl_check_private_key(struct ssl_context_handle* ctx_)
|
|
|
|
{
|
|
|
|
struct net_context_openssl* ctx = (struct net_context_openssl*) ctx_;
|
2014-07-29 11:31:42 +00:00
|
|
|
if (SSL_CTX_check_private_key(ctx->ssl) != 1)
|
2012-10-15 18:39:03 +00:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
static int handle_openssl_error(struct net_connection* con, int ret, int read)
|
2012-10-16 18:15:38 +00:00
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
|
|
|
int err = SSL_get_error(handle->ssl, ret);
|
|
|
|
switch (err)
|
|
|
|
{
|
|
|
|
case SSL_ERROR_ZERO_RETURN:
|
|
|
|
// Not really an error, but SSL was shut down.
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_READ:
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
if (read)
|
|
|
|
handle->ssl_read_events = NET_EVENT_READ;
|
|
|
|
else
|
|
|
|
handle->ssl_write_events = NET_EVENT_READ;
|
2012-10-16 18:15:38 +00:00
|
|
|
return 0;
|
|
|
|
|
|
|
|
case SSL_ERROR_WANT_WRITE:
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
if (read)
|
|
|
|
handle->ssl_read_events = NET_EVENT_WRITE;
|
|
|
|
else
|
|
|
|
handle->ssl_write_events = NET_EVENT_WRITE;
|
2012-10-16 18:15:38 +00:00
|
|
|
return 0;
|
|
|
|
|
2014-10-16 21:08:17 +00:00
|
|
|
case SSL_ERROR_SSL:
|
|
|
|
net_ssl_set_state(handle, tls_st_error);
|
|
|
|
return -2;
|
|
|
|
|
2012-10-16 18:15:38 +00:00
|
|
|
case SSL_ERROR_SYSCALL:
|
2014-10-16 21:08:17 +00:00
|
|
|
net_ssl_set_state(handle, tls_st_error);
|
2012-10-16 18:15:38 +00:00
|
|
|
return -2;
|
|
|
|
}
|
2014-05-14 19:00:42 +00:00
|
|
|
|
|
|
|
return -2;
|
2012-10-16 18:15:38 +00:00
|
|
|
}
|
|
|
|
|
2012-10-12 12:24:03 +00:00
|
|
|
ssize_t net_con_ssl_accept(struct net_connection* con)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
|
|
|
ssize_t ret;
|
2014-10-16 21:08:17 +00:00
|
|
|
net_ssl_set_state(handle, tls_st_accepting);
|
2012-10-12 12:24:03 +00:00
|
|
|
|
|
|
|
ret = SSL_accept(handle->ssl);
|
|
|
|
LOG_PROTO("SSL_accept() ret=%d", ret);
|
|
|
|
if (ret > 0)
|
|
|
|
{
|
|
|
|
net_con_update(con, NET_EVENT_READ);
|
2014-10-16 21:08:17 +00:00
|
|
|
net_ssl_set_state(handle, tls_st_connected);
|
2012-10-16 18:15:38 +00:00
|
|
|
return ret;
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
2012-10-16 18:15:38 +00:00
|
|
|
return handle_openssl_error(con, ret, tls_st_accepting);
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t net_con_ssl_connect(struct net_connection* con)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
|
|
|
ssize_t ret;
|
2014-10-16 21:08:17 +00:00
|
|
|
net_ssl_set_state(handle, tls_st_connecting);
|
2012-10-12 12:24:03 +00:00
|
|
|
|
|
|
|
ret = SSL_connect(handle->ssl);
|
|
|
|
LOG_PROTO("SSL_connect() ret=%d", ret);
|
2012-10-15 18:39:03 +00:00
|
|
|
|
2012-10-12 12:24:03 +00:00
|
|
|
if (ret > 0)
|
|
|
|
{
|
|
|
|
net_con_update(con, NET_EVENT_READ);
|
2014-10-16 21:08:17 +00:00
|
|
|
net_ssl_set_state(handle, tls_st_connected);
|
2012-10-16 18:15:38 +00:00
|
|
|
return ret;
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
2014-10-16 21:08:17 +00:00
|
|
|
|
|
|
|
ret = handle_openssl_error(con, ret, tls_st_connecting);
|
|
|
|
LOG_ERROR("net_con_ssl_connect: ret=%d", ret);
|
|
|
|
return ret;
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
|
|
|
|
2012-10-15 18:39:03 +00:00
|
|
|
ssize_t net_con_ssl_handshake(struct net_connection* con, enum net_con_ssl_mode ssl_mode, struct ssl_context_handle* ssl_ctx)
|
2012-10-12 12:24:03 +00:00
|
|
|
{
|
|
|
|
uhub_assert(con);
|
2014-08-06 15:37:06 +00:00
|
|
|
uhub_assert(ssl_ctx);
|
2012-10-12 12:24:03 +00:00
|
|
|
|
2012-10-15 18:39:03 +00:00
|
|
|
struct net_context_openssl* ctx = (struct net_context_openssl*) ssl_ctx;
|
2012-10-12 12:24:03 +00:00
|
|
|
struct net_ssl_openssl* handle = (struct net_ssl_openssl*) hub_malloc_zero(sizeof(struct net_ssl_openssl));
|
|
|
|
|
|
|
|
if (ssl_mode == net_con_ssl_mode_server)
|
|
|
|
{
|
2014-07-29 11:31:42 +00:00
|
|
|
handle->ssl = SSL_new(ctx->ssl);
|
2012-10-12 12:24:03 +00:00
|
|
|
if (!handle->ssl)
|
|
|
|
{
|
|
|
|
LOG_ERROR("Unable to create new SSL stream\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
SSL_set_fd(handle->ssl, con->sd);
|
2012-10-17 20:40:15 +00:00
|
|
|
handle->bio = SSL_get_rbio(handle->ssl);
|
2012-10-12 12:24:03 +00:00
|
|
|
con->ssl = (struct ssl_handle*) handle;
|
|
|
|
return net_con_ssl_accept(con);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-08-06 15:37:06 +00:00
|
|
|
handle->ssl = SSL_new(ctx->ssl);
|
2012-10-12 12:24:03 +00:00
|
|
|
SSL_set_fd(handle->ssl, con->sd);
|
2012-10-17 20:40:15 +00:00
|
|
|
handle->bio = SSL_get_rbio(handle->ssl);
|
2012-10-12 12:24:03 +00:00
|
|
|
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);
|
2012-10-16 18:15:38 +00:00
|
|
|
|
2018-01-15 17:00:56 +00:00
|
|
|
LOG_TRACE("net_ssl_send(), state=%d", (int) handle->state);
|
2014-10-16 21:08:17 +00:00
|
|
|
|
|
|
|
if (handle->state == tls_st_error)
|
|
|
|
return -2;
|
|
|
|
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
uhub_assert(handle->state == tls_st_connected);
|
2014-10-16 21:08:17 +00:00
|
|
|
|
2012-10-16 18:15:38 +00:00
|
|
|
|
|
|
|
ERR_clear_error();
|
2012-10-12 12:24:03 +00:00
|
|
|
ssize_t ret = SSL_write(handle->ssl, buf, len);
|
2012-10-17 20:40:15 +00:00
|
|
|
add_io_stats(handle);
|
2012-10-12 12:24:03 +00:00
|
|
|
LOG_PROTO("SSL_write(con=%p, buf=%p, len=" PRINTF_SIZE_T ") => %d", con, buf, len, ret);
|
2012-10-16 18:15:38 +00:00
|
|
|
if (ret > 0)
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
handle->ssl_write_events = 0;
|
|
|
|
else
|
|
|
|
ret = handle_openssl_error(con, ret, 0);
|
|
|
|
|
|
|
|
net_ssl_update(con, handle->events); // Update backend only
|
|
|
|
return ret;
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ssize_t net_ssl_recv(struct net_connection* con, void* buf, size_t len)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
if (handle->state == tls_st_error)
|
2012-10-16 18:15:38 +00:00
|
|
|
return -2;
|
2012-10-12 12:24:03 +00:00
|
|
|
|
2014-10-16 21:08:17 +00:00
|
|
|
if (handle->state == tls_st_accepting || handle->state == tls_st_connecting)
|
|
|
|
return -1;
|
|
|
|
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
uhub_assert(handle->state == tls_st_connected);
|
2012-10-16 18:15:38 +00:00
|
|
|
|
|
|
|
ERR_clear_error();
|
|
|
|
|
2012-10-12 12:24:03 +00:00
|
|
|
ret = SSL_read(handle->ssl, buf, len);
|
2012-10-17 20:40:15 +00:00
|
|
|
add_io_stats(handle);
|
2012-10-12 12:24:03 +00:00
|
|
|
LOG_PROTO("SSL_read(con=%p, buf=%p, len=" PRINTF_SIZE_T ") => %d", con, buf, len, ret);
|
|
|
|
if (ret > 0)
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
handle->ssl_read_events = 0;
|
|
|
|
else
|
|
|
|
ret = handle_openssl_error(con, ret, 1);
|
|
|
|
|
|
|
|
net_ssl_update(con, handle->events); // Update backend only
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void net_ssl_shutdown(struct net_connection* con)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
2014-10-16 21:08:17 +00:00
|
|
|
if (handle)
|
|
|
|
{
|
|
|
|
SSL_shutdown(handle->ssl);
|
|
|
|
SSL_clear(handle->ssl);
|
|
|
|
}
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void net_ssl_destroy(struct net_connection* con)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
2014-08-06 15:37:06 +00:00
|
|
|
LOG_TRACE("net_ssl_destroy: %p", con);
|
2012-10-12 12:24:03 +00:00
|
|
|
SSL_free(handle->ssl);
|
2013-09-06 19:17:31 +00:00
|
|
|
hub_free(handle);
|
2012-10-12 12:24:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void net_ssl_callback(struct net_connection* con, int events)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
2014-10-16 21:08:17 +00:00
|
|
|
int ret;
|
2012-10-12 12:24:03 +00:00
|
|
|
|
|
|
|
switch (handle->state)
|
|
|
|
{
|
|
|
|
case tls_st_none:
|
|
|
|
con->callback(con, events, con->ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case tls_st_error:
|
2014-10-16 21:08:17 +00:00
|
|
|
con->callback(con, NET_EVENT_ERROR, con->ptr);
|
2012-10-12 12:24:03 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case tls_st_accepting:
|
2012-10-16 18:15:38 +00:00
|
|
|
if (net_con_ssl_accept(con) != 0)
|
2012-10-12 12:24:03 +00:00
|
|
|
con->callback(con, NET_EVENT_READ, con->ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case tls_st_connecting:
|
2014-10-16 21:08:17 +00:00
|
|
|
ret = net_con_ssl_connect(con);
|
|
|
|
if (ret == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (ret > 0)
|
|
|
|
{
|
2014-10-16 21:13:26 +00:00
|
|
|
LOG_DEBUG("%p SSL connected!", con);
|
2012-10-12 12:24:03 +00:00
|
|
|
con->callback(con, NET_EVENT_READ, con->ptr);
|
2014-10-16 21:08:17 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2014-10-16 21:13:26 +00:00
|
|
|
LOG_DEBUG("%p SSL handshake failed!", con);
|
2014-10-16 21:08:17 +00:00
|
|
|
con->callback(con, NET_EVENT_ERROR, con->ptr);
|
|
|
|
}
|
2012-10-12 12:24:03 +00:00
|
|
|
break;
|
|
|
|
|
2012-10-16 18:15:38 +00:00
|
|
|
case tls_st_connected:
|
Fix: Rework SSL poll event handling to avoid infinite loops
The downstream connection callback must only be invoked when the event
that SSL requests for the connection to make progress has actually
occured. Otherwise, the downstream callback might do nothing but
re-queue an unrelated event (e.g. in user_net_io_want_write), and the
event loop comes around instantly while making no progress. Track the
SSL-requested events separately and deliver the required downstream
event when they fire.
Sample strace:
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: User writes data, OpenSSL tries to write data
write(96, <snip>..., 170) = -1 EAGAIN (Resource temporarily unavailable)
: handle_openssl_error requests NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLOUT, {u32=96, u64=96}}) = 0
: User callback then requests NET_EVENT_READ|NET_EVENT_WRITE
epoll_ctl(0, EPOLL_CTL_MOD, 96, {EPOLLIN|EPOLLOUT, {u32=96, u64=96}}) =
: Data available for *reading*
epoll_wait(0, {{EPOLLIN, {u32=96, u64=96}}}, 91, 10000) = 1
: net_ssl_callback in state tls_st_need_write calls cb NET_EVENT_WRITE
: again...
2014-04-03 20:24:41 +00:00
|
|
|
if (handle->ssl_read_events & events)
|
|
|
|
events |= NET_EVENT_READ;
|
|
|
|
if (handle->ssl_write_events & events)
|
|
|
|
events |= NET_EVENT_WRITE;
|
2012-10-12 12:24:03 +00:00
|
|
|
con->callback(con, events, con->ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case tls_st_disconnecting:
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-05 15:43:24 +00:00
|
|
|
const char* net_ssl_get_tls_version(struct net_connection* con)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
|
|
|
return SSL_get_version(handle->ssl);
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* net_ssl_get_tls_cipher(struct net_connection* con)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
|
|
|
const SSL_CIPHER *cipher = SSL_get_current_cipher(handle->ssl);
|
|
|
|
return SSL_CIPHER_get_name(cipher);
|
|
|
|
}
|
2012-10-12 12:24:03 +00:00
|
|
|
|
|
|
|
#endif /* SSL_USE_OPENSSL */
|
2012-10-17 18:53:05 +00:00
|
|
|
#endif /* SSL_SUPPORT */
|
|
|
|
|