diff --git a/CMakeLists.txt b/CMakeLists.txt index 33b20e1..61ea77c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,13 +17,21 @@ set (PROJECT_SOURCE_DIR "${CMAKE_SOURCE_DIR}/src") option(RELEASE "Release build, debug build if disabled" ON) option(LINK_SUPPORT "Allow hub linking" OFF) option(SSL_SUPPORT "Enable SSL support" ON) +option(USE_OPENSSL "Use OpenSSL's SSL support" OFF) option(SQLITE_SUPPORT "Enable SQLite support" ON) option(ADC_STRESS "Enable the stress tester client" OFF) find_package(Git) if (SSL_SUPPORT) - find_package(OpenSSL REQUIRED) + if (USE_OPENSSL) + find_package(OpenSSL) + else() + find_package(GnuTLS) + endif() + if (NOT GNUTLS_FOUND AND NOT OPENSSL_FOUND) + message(FATAL_ERROR "Neither OpenSSL nor GnuTLS are not found!") + endif() endif() if (MSVC) @@ -41,6 +49,7 @@ set (network_SOURCES ${PROJECT_SOURCE_DIR}/network/timeout.c ${PROJECT_SOURCE_DIR}/network/timer.c ${PROJECT_SOURCE_DIR}/network/network.c + ${PROJECT_SOURCE_DIR}/network/openssl.c ${PROJECT_SOURCE_DIR}/network/ipcalc.c ) @@ -156,16 +165,26 @@ else() endif() if(OPENSSL_FOUND) - add_definitions(-DSSL_SUPPORT=1) + set(SSL_LIBS ${OPENSSL_LIBRARIES}) + add_definitions(-DSSL_SUPPORT=1 -DSSL_USE_OPENSSL=1) include_directories(${OPENSSL_INCLUDE_DIR}) - target_link_libraries(uhub ${OPENSSL_LIBRARIES}) +endif() + +if (GNUTLS_FOUND) + set(SSL_LIBS ${GNUTLS_LIBRARIES}) + add_definitions(-DSSL_SUPPORT=1 -DSSL_USE_GNUTLS=1 ${GNUTLS_DEFINITIONS}) + include_directories(${GNUTLS_INCLUDE_DIR}) +endif() + +if(SSL_SUPPORT) + target_link_libraries(uhub ${SSL_LIBS}) if(UNIX) - target_link_libraries(uhub-admin ${OPENSSL_LIBRARIES}) + target_link_libraries(uhub-admin ${SSL_LIBS}) endif() - target_link_libraries(mod_welcome ${OPENSSL_LIBRARIES}) - target_link_libraries(mod_logging ${OPENSSL_LIBRARIES}) + target_link_libraries(mod_welcome ${SSL_LIBS}) + target_link_libraries(mod_logging ${SSL_LIBS}) if (ADC_STRESS) - target_link_libraries(adcrush ${OPENSSL_LIBRARIES}) + target_link_libraries(adcrush ${SSL_LIBS}) endif() endif() diff --git a/src/core/hub.c b/src/core/hub.c index 714f8ec..7eb6570 100644 --- a/src/core/hub.c +++ b/src/core/hub.c @@ -731,41 +731,25 @@ static int load_ssl_certificates(struct hub_info* hub, struct hub_config* config { if (config->tls_enable) { - hub->ssl_method = (SSL_METHOD*) SSLv23_method(); /* TLSv1_method() */ - hub->ssl_ctx = SSL_CTX_new(hub->ssl_method); - - /* Disable SSLv2 */ - SSL_CTX_set_options(hub->ssl_ctx, SSL_OP_NO_SSLv2); - SSL_CTX_set_quiet_shutdown(hub->ssl_ctx, 1); - - if (SSL_CTX_use_certificate_file(hub->ssl_ctx, config->tls_certificate, SSL_FILETYPE_PEM) < 0) + hub->ctx = net_ssl_context_create(); + 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)) { - LOG_ERROR("SSL_CTX_use_certificate_file: %s", ERR_error_string(ERR_get_error(), NULL)); + LOG_INFO("Enabling TLS (%s), using certificate: %s, private key: %s", net_ssl_get_provider(), config->tls_certificate, config->tls_private_key); + return 1; } - - if (SSL_CTX_use_PrivateKey_file(hub->ssl_ctx, config->tls_private_key, SSL_FILETYPE_PEM) < 0) - { - LOG_ERROR("SSL_CTX_use_PrivateKey_file: %s", ERR_error_string(ERR_get_error(), NULL)); - } - - if (SSL_CTX_check_private_key(hub->ssl_ctx) != 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; - } - LOG_INFO("Enabling TLS, using certificate: %s, private key: %s", config->tls_certificate, config->tls_private_key); + return 0; } return 1; } static void unload_ssl_certificates(struct hub_info* hub) { - if (hub->ssl_ctx) - { - SSL_CTX_free(hub->ssl_ctx); - } + if (hub->ctx) + net_ssl_context_destroy(hub->ctx); } -#endif +#endif /* SSL_SUPPORT */ struct hub_info* hub_start_service(struct hub_config* config) { diff --git a/src/core/hub.h b/src/core/hub.h index b0edee0..cf1cd4b 100644 --- a/src/core/hub.h +++ b/src/core/hub.h @@ -1,6 +1,6 @@ /* * uhub - A tiny ADC p2p connection hub - * Copyright (C) 2007-2011, Jan Vidar Krey + * Copyright (C) 2007-2012, Jan Vidar Krey * * 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 @@ -116,8 +116,7 @@ struct hub_info struct uhub_plugins* plugins; /* Plug-ins loaded for this hub instance. */ #ifdef SSL_SUPPORT - SSL_METHOD* ssl_method; - SSL_CTX* ssl_ctx; + struct ssl_context_handle* ctx; #endif /* SSL_SUPPORT */ }; diff --git a/src/core/probe.c b/src/core/probe.c index 293cf45..c217d34 100644 --- a/src/core/probe.c +++ b/src/core/probe.c @@ -85,7 +85,7 @@ static void probe_net_event(struct net_connection* con, int events, void *arg) { probe->connection = 0; } - net_con_ssl_handshake(con, net_con_ssl_mode_server, probe->hub->ssl_ctx); + net_con_ssl_handshake(con, net_con_ssl_mode_server, probe->hub->ctx); } else { diff --git a/src/network/backend.c b/src/network/backend.c index b74d798..e347b3e 100644 --- a/src/network/backend.c +++ b/src/network/backend.c @@ -1,6 +1,6 @@ /* * uhub - A tiny ADC p2p connection hub - * Copyright (C) 2007-2010, Jan Vidar Krey + * Copyright (C) 2007-2012, Jan Vidar Krey * * 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 @@ -181,11 +181,8 @@ void net_con_close(struct net_connection* con) #ifdef SSL_SUPPORT if (con->ssl) - { - SSL_shutdown(con->ssl); - SSL_clear(con->ssl); - } -#endif + net_ssl_shutdown(con); +#endif /* SSL_SUPPORT */ net_close(con->sd); con->sd = -1; diff --git a/src/network/backend.h b/src/network/backend.h index ba5febe..b908f28 100644 --- a/src/network/backend.h +++ b/src/network/backend.h @@ -1,6 +1,6 @@ /* * uhub - A tiny ADC p2p connection hub - * Copyright (C) 2007-2010, Jan Vidar Krey + * Copyright (C) 2007-2012, Jan Vidar Krey * * 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 diff --git a/src/network/common.h b/src/network/common.h index 014ddfb..8270da2 100644 --- a/src/network/common.h +++ b/src/network/common.h @@ -1,6 +1,6 @@ /* * uhub - A tiny ADC p2p connection hub - * Copyright (C) 2007-2010, Jan Vidar Krey + * Copyright (C) 2007-2012, Jan Vidar Krey * * 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 @@ -20,11 +20,10 @@ #define NET_WANT_READ NET_EVENT_READ #define NET_WANT_WRITE NET_EVENT_WRITE #define NET_WANT_ACCEPT NET_EVENT_READ -#define NET_WANT_SSL_READ 0x0010 -#define NET_WANT_SSL_WRITE 0x0020 -#define NET_WANT_SSL_ACCEPT 0x0040 -#define NET_WANT_SSL_CONNECT 0x0080 -#define NET_WANT_SSL_X509_LOOKUP 0x0100 + +#define NET_SSL_ANY NET_WANT_SSL_READ | NET_WANT_SSL_WRITE | NET_WANT_SSL_ACCEPT | NET_WANT_SSL_CONNECT | NET_WANT_SSL_X509_LOOKUP + +struct ssl_handle; /* abstract type */ #define NET_CLEANUP 0x8000 @@ -36,9 +35,7 @@ struct timeout_evt* timeout; /** timeout event handler */ #define NET_CON_STRUCT_SSL \ - SSL* ssl; /** SSL handle */ \ - uint32_t ssl_state; /** SSL state */ \ - size_t write_len; /** Length of last SSL_write(), only used if flags is NET_WANT_SSL_READ. */ \ + struct ssl_handle* ssl; /** SSL handle */ #ifdef SSL_SUPPORT #define NET_CON_STRUCT_COMMON \ diff --git a/src/network/connection.c b/src/network/connection.c index b30b46c..5f664cf 100644 --- a/src/network/connection.c +++ b/src/network/connection.c @@ -1,6 +1,6 @@ /* * uhub - A tiny ADC p2p connection hub - * Copyright (C) 2007-2010, Jan Vidar Krey + * Copyright (C) 2007-2012, Jan Vidar Krey * * 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 @@ -20,132 +20,23 @@ #include "uhub.h" #include "network/common.h" -#ifdef SSL_SUPPORT - -enum uhub_tls_state -{ - tls_st_none, - tls_st_error, - tls_st_accepting, - tls_st_connecting, - tls_st_connected, - tls_st_disconnecting, -}; - -static int handle_openssl_error(struct net_connection* con, int ret) -{ - uhub_assert(con); - - int error = SSL_get_error(con->ssl, ret); - switch (error) - { - case SSL_ERROR_ZERO_RETURN: - LOG_PROTO("SSL_get_error: ret=%d, error=%d: SSL_ERROR_ZERO_RETURN", ret, error); - con->ssl_state = tls_st_error; - return -1; - - case SSL_ERROR_WANT_READ: - LOG_PROTO("SSL_get_error: ret=%d, error=%d: SSL_ERROR_WANT_READ", ret, error); - con->flags |= NET_WANT_SSL_READ; - net_con_update(con, NET_EVENT_READ); - return 0; - - case SSL_ERROR_WANT_WRITE: - LOG_PROTO("SSL_get_error: ret=%d, error=%d: SSL_ERROR_WANT_WRITE", ret, error); - con->flags |= NET_WANT_SSL_WRITE; - net_con_update(con, NET_EVENT_READ | NET_EVENT_WRITE); - return 0; - - case SSL_ERROR_SYSCALL: - LOG_PROTO("SSL_get_error: ret=%d, error=%d: SSL_ERROR_SYSCALL", ret, error); - /* if ret == 0, connection closed, if ret == -1, check with errno */ - if (ret == 0) - return -1; - else - return -net_error(); - - case SSL_ERROR_SSL: - LOG_PROTO("SSL_get_error: ret=%d, error=%d: SSL_ERROR_SSL", ret, error); - /* internal openssl error */ - con->ssl_state = tls_st_error; - return -1; - } - return -1; -} - -ssize_t net_con_ssl_accept(struct net_connection* con) -{ - uhub_assert(con); - con->ssl_state = tls_st_accepting; - ssize_t ret = SSL_accept(con->ssl); -#ifdef NETWORK_DUMP_DEBUG - LOG_PROTO("SSL_accept() ret=%d", ret); -#endif - if (ret > 0) - { - net_con_update(con, NET_EVENT_READ); - con->ssl_state = tls_st_connected; - } - else - { - return handle_openssl_error(con, ret); - } - return ret; -} - -ssize_t net_con_ssl_connect(struct net_connection* con) -{ - uhub_assert(con); - - con->ssl_state = tls_st_connecting; - ssize_t ret = SSL_connect(con->ssl); -#ifdef NETWORK_DUMP_DEBUG - LOG_PROTO("SSL_connect() ret=%d", ret); -#endif - if (ret > 0) - { - con->ssl_state = tls_st_connected; - net_con_update(con, NET_EVENT_READ); - } - else - { - return handle_openssl_error(con, ret); - } - return ret; -} - -ssize_t net_con_ssl_handshake(struct net_connection* con, enum net_con_ssl_mode ssl_mode, SSL_CTX* ssl_ctx) -{ - uhub_assert(con); - SSL* ssl = 0; - - if (ssl_mode == net_con_ssl_mode_server) - { - ssl = SSL_new(ssl_ctx); - if (!ssl) - { - LOG_ERROR("Unable to create new SSL stream\n"); - return -1; - } - SSL_set_fd(ssl, con->sd); - net_con_set_ssl(con, ssl); - return net_con_ssl_accept(con); - } - else - { - ssl = SSL_new(SSL_CTX_new(TLSv1_method())); - SSL_set_fd(ssl, con->sd); - net_con_set_ssl(con, ssl); - return net_con_ssl_connect(con); - } -} -#endif /* SSL_SUPPORT */ - #ifdef SSL_SUPPORT void net_stats_add_tx(size_t bytes); void net_stats_add_rx(size_t bytes); #endif +static int is_blocked_or_interrupted() +{ + int err = net_error(); + return +#ifdef WINSOCK + err == WSAEWOULDBLOCK +#else + err == EWOULDBLOCK +#endif + || err == EINTR; +} + ssize_t net_con_send(struct net_connection* con, const void* buf, size_t len) { int ret; @@ -156,13 +47,7 @@ ssize_t net_con_send(struct net_connection* con, const void* buf, size_t len) ret = net_send(con->sd, buf, len, UHUB_SEND_SIGNAL); if (ret == -1) { - if ( -#ifdef WINSOCK - net_error() == WSAEWOULDBLOCK -#else - net_error() == EWOULDBLOCK -#endif - || net_error() == EINTR) + if (is_blocked_or_interrupted()) return 0; return -1; } @@ -170,19 +55,9 @@ ssize_t net_con_send(struct net_connection* con, const void* buf, size_t len) } else { - con->write_len = len; - ret = SSL_write(con->ssl, buf, len); - LOG_PROTO("SSL_write(con=%p, buf=%p, len=" PRINTF_SIZE_T ") => %d", con, buf, len, ret); - if (ret <= 0) - { - return handle_openssl_error(con, ret); - } - else - { - net_stats_add_tx(ret); - } + ret = net_ssl_send(con, buf, len); } -#endif +#endif /* SSL_SUPPORT */ return ret; } @@ -190,19 +65,13 @@ ssize_t net_con_recv(struct net_connection* con, void* buf, size_t len) { int ret; #ifdef SSL_SUPPORT - if (!net_con_is_ssl(con)) + if (!con->ssl) { #endif ret = net_recv(con->sd, buf, len, 0); if (ret == -1) { - if ( -#ifdef WINSOCK - net_error() == WSAEWOULDBLOCK -#else - net_error() == EWOULDBLOCK -#endif - || net_error() == EINTR) + if (is_blocked_or_interrupted()) return 0; return -net_error(); } @@ -214,22 +83,9 @@ ssize_t net_con_recv(struct net_connection* con, void* buf, size_t len) } else { - if (con->ssl_state == tls_st_error) - return -1; - - ret = SSL_read(con->ssl, buf, len); - LOG_PROTO("SSL_read(con=%p, buf=%p, len=" PRINTF_SIZE_T ") => %d", con, buf, len, ret); - if (ret > 0) - { - net_con_update(con, NET_EVENT_READ); - net_stats_add_rx(ret); - } - else - { - return handle_openssl_error(con, ret); - } + ret = net_ssl_recv(con, buf, len); } -#endif +#endif /* SSL_SUPPORT */ return ret; } @@ -238,13 +94,7 @@ ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len) int ret = net_recv(con->sd, buf, len, MSG_PEEK); if (ret == -1) { - if ( -#ifdef WINSOCK - net_error() == WSAEWOULDBLOCK -#else - net_error() == EWOULDBLOCK -#endif - || net_error() == EINTR) + if (is_blocked_or_interrupted()) return 0; return -net_error(); } @@ -254,19 +104,10 @@ ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len) } #ifdef SSL_SUPPORT + int net_con_is_ssl(struct net_connection* con) { - return con->ssl != 0; -} - -SSL* net_con_get_ssl(struct net_connection* con) -{ - return con->ssl; -} - -void net_con_set_ssl(struct net_connection* con, SSL* ssl) -{ - con->ssl = ssl; + return !!con->ssl; } #endif /* SSL_SUPPORT */ @@ -283,7 +124,8 @@ void* net_con_get_ptr(struct net_connection* con) void net_con_destroy(struct net_connection* con) { #ifdef SSL_SUPPORT - SSL_free(con->ssl); + if (con->ssl) + net_ssl_destroy(con); #endif hub_free(con); } @@ -301,62 +143,10 @@ void net_con_callback(struct net_connection* con, int events) } #ifdef SSL_SUPPORT - if (!con->ssl) - { + if (con->ssl) + net_ssl_callback(con, events); + else #endif con->callback(con, events, con->ptr); -#ifdef SSL_SUPPORT - } - else - { -#ifdef NETWORK_DUMP_DEBUG - LOG_PROTO("net_con_event: events=%d, con=%p, state=%d", events, con, con->ssl_state); -#endif - switch (con->ssl_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: - if (net_con_ssl_accept(con) < 0) - { - con->callback(con, NET_EVENT_READ, con->ptr); - } - break; - - case tls_st_connecting: - if (net_con_ssl_connect(con) < 0) - { - con->callback(con, NET_EVENT_READ, con->ptr); - } - break; - - case tls_st_connected: - LOG_PROTO("tls_st_connected, events=%s%s, ssl_flags=%s%s", (events & NET_EVENT_READ ? "R" : ""), (events & NET_EVENT_WRITE ? "W" : ""), con->flags & NET_WANT_SSL_READ ? "R" : "", con->flags & NET_WANT_SSL_WRITE ? "W" : ""); - if (events & NET_EVENT_WRITE && con->flags & NET_WANT_SSL_READ) - { - con->callback(con, events & NET_EVENT_READ, con->ptr); - return; - } - - if (events & NET_EVENT_READ && con->flags & NET_WANT_SSL_WRITE) - { - con->callback(con, events & NET_EVENT_READ, con->ptr); - return; - } - - con->callback(con, events, con->ptr); - break; - - case tls_st_disconnecting: - return; - } - } -#endif } diff --git a/src/network/connection.h b/src/network/connection.h index ff69262..84cc96a 100644 --- a/src/network/connection.h +++ b/src/network/connection.h @@ -1,6 +1,6 @@ /* * uhub - A tiny ADC p2p connection hub - * Copyright (C) 2007-2010, Jan Vidar Krey + * Copyright (C) 2007-2012, Jan Vidar Krey * * 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 @@ -23,6 +23,7 @@ #include "uhub.h" #include "network/common.h" #include "network/backend.h" +#include "network/tls.h" #define NET_EVENT_TIMEOUT 0x0001 #define NET_EVENT_READ 0x0002 @@ -83,29 +84,5 @@ extern ssize_t net_con_peek(struct net_connection* con, void* buf, size_t len); extern void net_con_set_timeout(struct net_connection* con, int seconds); extern void net_con_clear_timeout(struct net_connection* con); -#ifdef SSL_SUPPORT -/** - * Start SSL_accept() - */ -extern ssize_t net_con_ssl_accept(struct net_connection*); - -/** - * Start SSL_connect() - */ -extern ssize_t net_con_ssl_connect(struct net_connection*); - -enum net_con_ssl_mode -{ - net_con_ssl_mode_server, - net_con_ssl_mode_client, -}; - -extern ssize_t net_con_ssl_handshake(struct net_connection* con, enum net_con_ssl_mode, SSL_CTX* ssl_ctx); - -extern int net_con_is_ssl(struct net_connection* con); -extern SSL* net_con_get_ssl(struct net_connection* con); -extern void net_con_set_ssl(struct net_connection* con, SSL*); -#endif /* SSL_SUPPORT */ - #endif /* HAVE_UHUB_NETWORK_CONNECTION_H */ diff --git a/src/network/network.c b/src/network/network.c index a7164d6..8268934 100644 --- a/src/network/network.c +++ b/src/network/network.c @@ -59,9 +59,11 @@ int net_initialize() net_stats_initialize(); #ifdef SSL_SUPPORT +#ifdef SSL_USE_OPENSSL LOG_TRACE("Initializing OpenSSL..."); SSL_library_init(); SSL_load_error_strings(); +#endif /* SSL_USE_OPENSSL */ #endif /* SSL_SUPPORT */ net_initialized = 1; @@ -100,10 +102,12 @@ int net_destroy() net_backend_shutdown(); #ifdef SSL_SUPPORT +#ifdef SSL_USE_OPENSSL ERR_free_strings(); EVP_cleanup(); CRYPTO_cleanup_all_ex_data(); -#endif +#endif /* SSL_USE_OPENSSL */ +#endif /* SSL_SUPPORT */ #ifdef WINSOCK WSACleanup(); diff --git a/src/network/openssl.c b/src/network/openssl.c new file mode 100644 index 0000000..78f6a5d --- /dev/null +++ b/src/network/openssl.c @@ -0,0 +1,305 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2012, Jan Vidar Krey + * + * 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 . + * + */ + +#include "uhub.h" +#include "network/common.h" +#include "network/tls.h" + +#ifdef SSL_SUPPORT +#ifdef SSL_USE_OPENSSL + +#define NETWORK_DUMP_DEBUG + +struct net_ssl_openssl +{ + SSL* ssl; + enum ssl_state state; + uint32_t flags; + BIO* biow; + BIO* bior; +}; + +struct net_context_openssl +{ + SSL_METHOD* ssl_method; + SSL_CTX* ssl_ctx; +}; + +static struct net_ssl_openssl* get_handle(struct net_connection* con) +{ + uhub_assert(con); + return (struct net_ssl_openssl*) con->ssl; +} + +const char* net_ssl_get_provider() +{ + return OPENSSL_VERSION_TEXT; +} + + +/** + * Create a new SSL context. + */ +struct ssl_context_handle* net_ssl_context_create() +{ + + 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); + + /* Disable SSLv2 */ + SSL_CTX_set_options(ctx->ssl_ctx, SSL_OP_NO_SSLv2); + SSL_CTX_set_quiet_shutdown(ctx->ssl_ctx, 1); + + 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_; + SSL_CTX_free(ctx->ssl_ctx); + 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_file(ctx->ssl_ctx, pem_file, SSL_FILETYPE_PEM) < 0) + { + LOG_ERROR("SSL_CTX_use_certificate_file: %s", ERR_error_string(ERR_get_error(), NULL)); + 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_; + if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, pem_file, SSL_FILETYPE_PEM) < 0) + { + 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_; + if (SSL_CTX_check_private_key(ctx->ssl_ctx) != 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; + } + return 1; +} + +static int handle_openssl_error(struct net_connection* con, int ret, enum ssl_state forced_rwstate) +{ + 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: + handle->state = forced_rwstate; + net_con_update(con, NET_EVENT_READ); + return 0; + + case SSL_ERROR_WANT_WRITE: + handle->state = forced_rwstate; + net_con_update(con, NET_EVENT_WRITE); + return 0; + + case SSL_ERROR_SYSCALL: + handle->state = tls_st_error; + return -2; + } +} + +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; + return ret; + } + return handle_openssl_error(con, ret, tls_st_accepting); +} + +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); +#ifdef NETWORK_DUMP_DEBUG + LOG_PROTO("SSL_connect() ret=%d", ret); +#endif /* NETWORK_DUMP_DEBUG */ + + if (ret > 0) + { + handle->state = tls_st_connected; + net_con_update(con, NET_EVENT_READ); + return ret; + } + return handle_openssl_error(con, ret, tls_st_connecting); +} + +ssize_t net_con_ssl_handshake(struct net_connection* con, enum net_con_ssl_mode ssl_mode, struct ssl_context_handle* ssl_ctx) +{ + uhub_assert(con); + + struct net_context_openssl* ctx = (struct net_context_openssl*) ssl_ctx; + 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) + { + handle->ssl = SSL_new(ctx->ssl_ctx); + if (!handle->ssl) + { + LOG_ERROR("Unable to create new SSL stream\n"); + return -1; + } + SSL_set_fd(handle->ssl, con->sd); + handle->bior = SSL_get_rbio(handle->ssl); + handle->biow = SSL_get_wbio(handle->ssl); + 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); + handle->bior = SSL_get_rbio(handle->ssl); + handle->biow = SSL_get_wbio(handle->ssl); + 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); + + ERR_clear_error(); + ssize_t ret = SSL_write(handle->ssl, buf, len); + 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; + 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) +{ + struct net_ssl_openssl* handle = get_handle(con); + ssize_t ret; + + if (handle->state == tls_st_error) + return -2; + + uhub_assert(handle->state == tls_st_connected || handle->state == tls_st_need_read); + + ERR_clear_error(); + + ret = SSL_read(handle->ssl, buf, len); + 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; + return ret; + } + return handle_openssl_error(con, ret, tls_st_need_read); +} + +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); +} + +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: + if (net_con_ssl_accept(con) != 0) + con->callback(con, NET_EVENT_READ, con->ptr); + break; + + case tls_st_connecting: + if (net_con_ssl_connect(con) != 0) + 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: + con->callback(con, events, con->ptr); + break; + + case tls_st_disconnecting: + return; + } +} + + +#endif /* SSL_USE_OPENSSL */ +#endif /* SSL_SUPPORT */ \ No newline at end of file diff --git a/src/network/tls.h b/src/network/tls.h new file mode 100644 index 0000000..25391a4 --- /dev/null +++ b/src/network/tls.h @@ -0,0 +1,101 @@ +/* + * uhub - A tiny ADC p2p connection hub + * Copyright (C) 2007-2012, Jan Vidar Krey + * + * 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 . + * + */ + +#ifndef HAVE_UHUB_NETWORK_TLS_H +#define HAVE_UHUB_NETWORK_TLS_H + +#include "uhub.h" + +#ifdef SSL_SUPPORT + + +enum ssl_state +{ + tls_st_none, + tls_st_error, + 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, +}; + +enum net_con_ssl_mode +{ + net_con_ssl_mode_server, + net_con_ssl_mode_client, +}; + +struct ssl_context_handle; + +/** + * Returns a string describing the TLS/SSL provider information + */ +extern const char* net_ssl_get_provider(); + +/** + * Create a new SSL context. + */ +extern struct ssl_context_handle* net_ssl_context_create(); +extern void net_ssl_context_destroy(struct ssl_context_handle* ctx); + +/** + * Return 0 on error, 1 otherwise. + */ +extern int ssl_load_certificate(struct ssl_context_handle* ctx, const char* pem_file); + +/** + * Return 0 on error, 1 otherwise. + */ +extern int ssl_load_private_key(struct ssl_context_handle* ctx, const char* pem_file); + +/** + * Return 0 if private key does not match certificate, 1 if everything is OK. + */ +extern int ssl_check_private_key(struct ssl_context_handle* ctx); + +/** + * Start SSL_accept() + */ +extern ssize_t net_con_ssl_accept(struct net_connection*); + +/** + * Start SSL_connect() + */ +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); + +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); + + + +extern ssize_t net_con_ssl_handshake(struct net_connection* con, enum net_con_ssl_mode, struct ssl_context_handle* ssl_ctx); +extern SSL* net_con_get_ssl(struct net_connection* con); +#ifdef SSL_USE_OPENSSL +extern void net_con_set_ssl(struct net_connection* con, SSL*); +#endif // SSL_USE_OPENSSL +extern int net_con_is_ssl(struct net_connection* con); + +#endif /* SSL_SUPPORT */ +#endif /* HAVE_UHUB_NETWORK_TLS_H */ \ No newline at end of file diff --git a/src/system.h b/src/system.h index 12e5b3c..75542c1 100644 --- a/src/system.h +++ b/src/system.h @@ -99,8 +99,13 @@ #endif #ifdef SSL_SUPPORT +#ifdef SSL_USE_OPENSSL #include #include +#endif /* SSL_USE_OPENSSL */ +#ifdef SSL_USE_GNUTLS +#include +#endif /* SSL_USE_GNUTLS */ #endif #include "version.h" diff --git a/src/tools/adcclient.c b/src/tools/adcclient.c index 69bb8e4..a320da9 100644 --- a/src/tools/adcclient.c +++ b/src/tools/adcclient.c @@ -65,10 +65,6 @@ struct ADC_client char* desc; int flags; void* ptr; -#ifdef SSL_SUPPORT - const SSL_METHOD* ssl_method; - SSL_CTX* ssl_ctx; -#endif /* SSL_SUPPORT */ }; @@ -522,15 +518,7 @@ struct ADC_client* ADC_client_create(const char* nickname, const char* descripti if (sd == -1) return NULL; client->con = net_con_create(); -#if 0 - /* FIXME */ - client->timer = 0; /* FIXME: hub_malloc(sizeof(struct net_timer)); */ -#endif net_con_initialize(client->con, sd, event_callback, client, 0); -#if 0 - /* FIXME */ - net_timer_initialize(client->timer, timer_callback, client); -#endif ADC_client_set_state(client, ps_none); client->nick = hub_strdup(nickname); @@ -547,10 +535,6 @@ void ADC_client_destroy(struct ADC_client* client) { ADC_TRACE; ADC_client_disconnect(client); -#if 0 - /* FIXME */ - net_timer_shutdown(client->timer); -#endif ioq_send_destroy(client->send_queue); ioq_recv_destroy(client->recv_queue); hub_free(client->timer); @@ -598,6 +582,8 @@ static void ADC_client_on_connected(struct ADC_client* client) net_con_update(client->con, NET_EVENT_READ | NET_EVENT_WRITE); client->callback(client, ADC_CLIENT_SSL_HANDSHAKE, 0); ADC_client_set_state(client, ps_conn_ssl); + + net_con_ssl_handshake(client->con, net_con_ssl_mode_client, NULL); } else #endif @@ -613,9 +599,9 @@ static void ADC_client_on_connected(struct ADC_client* client) static void ADC_client_on_connected_ssl(struct ADC_client* client) { ADC_TRACE; - net_con_update(client->con, NET_EVENT_READ); client->callback(client, ADC_CLIENT_SSL_OK, 0); client->callback(client, ADC_CLIENT_CONNECTED, 0); + net_con_update(client->con, NET_EVENT_READ); ADC_client_send(client, adc_msg_create(ADC_HANDSHAKE)); ADC_client_set_state(client, ps_protocol); } @@ -727,4 +713,4 @@ const char* ADC_client_get_description(const struct ADC_client* client) void* ADC_client_get_ptr(const struct ADC_client* client) { return client->ptr; -} \ No newline at end of file +}