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;
|
|
|
|
}
|
|
|
|
|
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();
|
|
|
|
ERR_remove_state(0);
|
|
|
|
|
|
|
|
ENGINE_cleanup();
|
|
|
|
CONF_modules_unload(1);
|
|
|
|
|
|
|
|
ERR_free_strings();
|
|
|
|
EVP_cleanup();
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
if (handle->bio->num_read > handle->bytes_rx)
|
|
|
|
{
|
|
|
|
net_stats_add_rx(handle->bio->num_read - handle->bytes_rx);
|
|
|
|
handle->bytes_rx = handle->bio->num_read;
|
|
|
|
}
|
2012-10-17 18:53:05 +00:00
|
|
|
|
2012-10-17 20:40:15 +00:00
|
|
|
if (handle->bio->num_write > handle->bytes_tx)
|
|
|
|
{
|
|
|
|
net_stats_add_tx(handle->bio->num_write - handle->bytes_tx);
|
|
|
|
handle->bytes_tx = handle->bio->num_write;
|
|
|
|
}
|
|
|
|
}
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
return (struct ssl_context_handle*) ctx;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void net_ssl_context_destroy(struct ssl_context_handle* ctx_)
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
|
|
|
|
case SSL_ERROR_SYSCALL:
|
|
|
|
handle->state = tls_st_error;
|
|
|
|
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);
|
|
|
|
handle->state = tls_st_accepting;
|
|
|
|
ssize_t ret;
|
|
|
|
|
|
|
|
ret = SSL_accept(handle->ssl);
|
|
|
|
LOG_PROTO("SSL_accept() ret=%d", ret);
|
|
|
|
if (ret > 0)
|
|
|
|
{
|
|
|
|
net_con_update(con, NET_EVENT_READ);
|
|
|
|
handle->state = 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;
|
|
|
|
handle->state = tls_st_connecting;
|
|
|
|
|
|
|
|
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)
|
|
|
|
{
|
|
|
|
handle->state = tls_st_connected;
|
|
|
|
net_con_update(con, NET_EVENT_READ);
|
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_connecting);
|
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);
|
|
|
|
|
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
|
|
|
|
{
|
|
|
|
handle->ssl = SSL_new(SSL_CTX_new(TLSv1_method()));
|
|
|
|
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
|
|
|
|
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
|
|
|
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
|
|
|
|
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);
|
|
|
|
SSL_shutdown(handle->ssl);
|
|
|
|
SSL_clear(handle->ssl);
|
|
|
|
}
|
|
|
|
|
|
|
|
void net_ssl_destroy(struct net_connection* con)
|
|
|
|
{
|
|
|
|
struct net_ssl_openssl* handle = get_handle(con);
|
|
|
|
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);
|
|
|
|
|
|
|
|
switch (handle->state)
|
|
|
|
{
|
|
|
|
case tls_st_none:
|
|
|
|
con->callback(con, events, con->ptr);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case tls_st_error:
|
|
|
|
con->callback(con, NET_EVENT_READ, con->ptr);
|
|
|
|
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:
|
2012-10-16 18:15:38 +00:00
|
|
|
if (net_con_ssl_connect(con) != 0)
|
2012-10-12 12:24:03 +00:00
|
|
|
con->callback(con, NET_EVENT_READ, con->ptr);
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endif /* SSL_USE_OPENSSL */
|
2012-10-17 18:53:05 +00:00
|
|
|
#endif /* SSL_SUPPORT */
|
|
|
|
|