add support for optional TCP SSL/TLS sockets;

pull/51/head
Bryan Biedenkapp 2 years ago
parent b4f9b2d201
commit cda6b5965f

@ -30,6 +30,14 @@ else ()
message(CHECK_PASS "no")
endif (ENABLE_TUI_SUPPORT)
option(ENABLE_TCP_SSL "Enable TCP SSL support" off)
message(CHECK_START "Enable TCP SSL support")
if (ENABLE_TCP_SSL)
message(CHECK_PASS "yes")
else ()
message(CHECK_PASS "no")
endif (ENABLE_TCP_SSL)
# Cross-compile options
option(CROSS_COMPILE_ARM "Cross-compile for 32-bit ARM" off)
option(CROSS_COMPILE_AARCH64 "Cross-compile for 64-bit ARM" off)
@ -155,6 +163,11 @@ if (ENABLE_TUI_SUPPORT AND NOT FC_INCLUDED)
set(FINALCUT_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/_deps/finalcut-src/src)
endif (ENABLE_TUI_SUPPORT AND NOT FC_INCLUDED)
if (ENABLE_TCP_SSL)
find_package(OpenSSL REQUIRED)
include_directories("${OPENSSL_INCLUDE_DIR}")
endif (ENABLE_TCP_SSL)
#
# Set GIT_VER compiler directive
#

@ -20,6 +20,10 @@ else()
set(ENABLE_SETUP_TUI off)
endif (ENABLE_TUI_SUPPORT)
if (ENABLE_TCP_SSL)
add_definitions(-DENABLE_TCP_SSL)
endif (ENABLE_TCP_SSL)
option(DISABLE_MONITOR "Disable dvmmon compilation" off)
if (DISABLE_MONITOR)
message(CHECK_START "Disable dvmmon compilation - enabled")

@ -0,0 +1,26 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#include "common/Defines.h"
#include "common/network/tcp/SecureTcpClient.h"
#if defined(ENABLE_TCP_SSL)
using namespace network::tcp;
// ---------------------------------------------------------------------------
// Static Class Members
// ---------------------------------------------------------------------------
std::string SecureTcpClient::m_sslHostname = std::string();
#endif // ENABLE_TCP_SSL

@ -0,0 +1,189 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__SECURE_TCP_CLIENT_H__)
#define __SECURE_TCP_CLIENT_H__
#if defined(ENABLE_TCP_SSL)
#include "Defines.h"
#include "common/Log.h"
#include "common/network/tcp/Socket.h"
#include <cassert>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/x509v3.h>
namespace network
{
namespace tcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Implements a secure TCP client.
// ---------------------------------------------------------------------------
class HOST_SW_API SecureTcpClient : public Socket
{
public:
auto operator=(SecureTcpClient&) -> SecureTcpClient& = delete;
auto operator=(SecureTcpClient&&) -> SecureTcpClient& = delete;
SecureTcpClient(SecureTcpClient&) = delete;
/// <summary>Initializes a new instance of the SecureTcpClient class.</summary>
/// <param name="fd"></param>
/// <param name="sslCtx"></param>
/// <param name="client"></param>
/// <param name="clientLen"></param>
SecureTcpClient(const int fd, SSL_CTX* sslCtx, sockaddr_in& client, int clientLen) : Socket(fd),
m_sockaddr(),
m_pSSL(nullptr),
m_pSSLCtx(nullptr)
{
::memcpy(reinterpret_cast<char*>(&m_sockaddr), reinterpret_cast<char*>(&client), clientLen);
initSsl(sslCtx);
if (SSL_accept(m_pSSL) <= 0) {
LogError(LOG_NET, "Cannot accept SSL client, %s err: %d", ERR_error_string(ERR_get_error(), NULL), errno);
throw std::runtime_error("Cannot accept SSL client");
}
}
/// <summary>Initializes a new instance of the SecureTcpClient class.</summary>
/// <param name="address"></param>
/// <param name="port"></param>
SecureTcpClient(const std::string& address, const uint16_t port) :
m_pSSL(nullptr),
m_pSSLCtx(nullptr)
{
assert(!address.empty());
assert(port > 0U);
OpenSSL_add_ssl_algorithms();
const SSL_METHOD* method = SSLv23_client_method();
SSL_load_error_strings();
m_pSSLCtx = SSL_CTX_new(method);
init();
sockaddr_in addr = {};
initAddr(address, port, addr);
::memcpy(reinterpret_cast<char*>(&m_sockaddr), reinterpret_cast<char*>(&addr), sizeof(addr));
ssize_t ret = ::connect(m_fd, reinterpret_cast<sockaddr*>(&addr), sizeof(addr));
if (ret < 0) {
LogError(LOG_NET, "Failed to connect to server, err: %d", errno);
}
initSsl(m_pSSLCtx);
if (SSL_connect(m_pSSL) <= 0) {
LogError(LOG_NET, "Failed to connect to server, %s err: %d", ERR_error_string(ERR_get_error(), NULL), errno);
throw std::runtime_error("Failed to SSL connect to server");
}
}
/// <summary>Finalizes a instance of the SecureTcpClient class.</summary>
~SecureTcpClient() override
{
if (m_pSSL != nullptr) {
SSL_shutdown(m_pSSL);
SSL_free(m_pSSL);
}
if (m_pSSLCtx != nullptr)
SSL_CTX_free(m_pSSLCtx);
}
/// <summary>
/// Read data from the socket.
/// </summary>
/// <param name="buffer">Buffer to read data into.</param>
/// <param name="length">Length of data to read.</param>
/// <returns></returns>
[[nodiscard]] ssize_t read(uint8_t* buffer, size_t len) noexcept override
{
return SSL_read(m_pSSL, buffer, (int)len);
}
/// <summary>
/// Write data to the socket.
/// </summary>
/// <param name="buffer">Buffer containing data to write to socket.</param>
/// <param name="length">Length of data to write.</param>
/// <returns></returns>
ssize_t write(const uint8_t* buffer, size_t len) noexcept override
{
return SSL_write(m_pSSL, buffer, (int)len);
}
/// <summary></summary>
/// <returns></returns>
sockaddr_storage getAddress() const { return m_sockaddr; }
/// <summary></summary>
/// <param name="hostname"></param>
static void setHostname(std::string hostname) { m_sslHostname = hostname; }
private:
sockaddr_storage m_sockaddr;
static std::string m_sslHostname;
SSL* m_pSSL;
SSL_CTX* m_pSSLCtx;
/// <summary>
///
/// </summary>
void init() noexcept(false)
{
m_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (m_fd < 0) {
LogError(LOG_NET, "Cannot create the TCP socket, err: %d", errno);
throw std::runtime_error("Cannot create the TCP socket");
}
int reuse = 1;
if (::setsockopt(m_fd, IPPROTO_TCP, TCP_NODELAY, (char*)& reuse, sizeof(reuse)) != 0) {
LogError(LOG_NET, "Cannot set the TCP socket option, err: %d", errno);
throw std::runtime_error("Cannot set the TCP socket option");
}
}
/// <summary>
///
/// </summary>
/// <param name="sslCtx"></param>
void initSsl(SSL_CTX* sslCtx) noexcept(false)
{
m_pSSL = SSL_new(sslCtx);
if (m_pSSL == nullptr) {
LogError(LOG_NET, "Failed to create SSL client, %s err: %d", ERR_error_string(ERR_get_error(), NULL), errno);
throw std::runtime_error("Failed to create SSL client");
}
SSL_set_hostflags(m_pSSL, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS);
if (!SSL_set1_host(m_pSSL, SecureTcpClient::m_sslHostname.c_str())) {
LogError(LOG_NET, "Failed to set SSL hostname, %s err: %d", ERR_error_string(ERR_get_error(), NULL), errno);
throw std::runtime_error("Failed to set SSL hostname");
}
SSL_set_verify(m_pSSL, SSL_VERIFY_NONE, NULL);
SSL_set_fd(m_pSSL, m_fd);
}
};
} // namespace tcp
} // namespace network
#endif // ENABLE_TCP_SSL
#endif // __SECURE_TCP_CLIENT_H__

@ -0,0 +1,144 @@
// SPDX-License-Identifier: GPL-2.0-only
/**
* Digital Voice Modem - Common Library
* GPLv2 Open Source. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* @package DVM / Common Library
* @license GPLv2 License (https://opensource.org/licenses/GPL-2.0)
*
* Copyright (C) 2024 Bryan Biedenkapp, N2PLL
*
*/
#if !defined(__SECURE_TCP_SERVER_H__)
#define __SECURE_TCP_SERVER_H__
#if defined(ENABLE_TCP_SSL)
#include "Defines.h"
#include "common/network/tcp/Socket.h"
#include "common/Log.h"
#include "common/network/tcp/SecureTcpClient.h"
#include <cassert>
#include <openssl/ssl.h>
#include <openssl/err.h>
namespace network
{
namespace tcp
{
// ---------------------------------------------------------------------------
// Class Declaration
// Implements a secure TCP server listener.
// ---------------------------------------------------------------------------
class HOST_SW_API SecureTcpListener : public Socket
{
public:
auto operator=(SecureTcpListener&) -> SecureTcpListener& = delete;
auto operator=(SecureTcpListener&&) -> SecureTcpListener& = delete;
SecureTcpListener(SecureTcpListener&) = delete;
/// <summary>Initializes a new instance of the SecureTcpListener class.</summary>
/// <param name="keyFile"></param>
/// <param name="certFile"></param>
SecureTcpListener(const std::string& keyFile, const std::string& certFile) :
m_pSSLCtx(nullptr),
m_keyFile(keyFile),
m_certFile(certFile)
{
assert(!m_certFile.empty());
assert(!m_keyFile.empty());
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
const SSL_METHOD* method = SSLv23_server_method();
m_pSSLCtx = SSL_CTX_new(method);
if (m_pSSLCtx == nullptr) {
LogError(LOG_NET, "Cannot create server SSL context, %s err: %d", ERR_error_string(ERR_get_error(), NULL), errno);
throw std::runtime_error("Cannot create server SSL context");
}
m_fd = ::socket(AF_INET, SOCK_STREAM, 0);
if (m_fd < 0) {
LogError(LOG_NET, "Cannot create the TCP socket, err: %d", errno);
throw std::runtime_error("Cannot create the TCP socket");
}
int reuse = 1;
if (::setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, (char*)& reuse, sizeof(reuse)) != 0) {
LogError(LOG_NET, "Cannot set the TCP socket option, err: %d", errno);
throw std::runtime_error("Cannot set the TCP socket option");
}
initSecureFiles();
}
/// <summary>Initializes a new instance of the SecureTcpListener class.</summary>
/// <param name="keyFile"></param>
/// <param name="certFile"></param>
/// <param name="port"></param>
/// <param name="address"></param>
SecureTcpListener(const std::string& keyFile, const std::string& certFile, const uint16_t port, const std::string& address = "0.0.0.0") : SecureTcpListener(keyFile, certFile)
{
if (!bind(address, port)) {
LogError(LOG_NET, "Cannot to bind secure TCP server, err: %d", errno);
throw std::runtime_error("Cannot to bind secure TCP server");
}
}
/// <summary>Finalizes a instance of the SecureTcpListener class.</summary>
~SecureTcpListener() override
{
if (m_pSSLCtx != nullptr)
SSL_CTX_free(m_pSSLCtx);
}
/// <summary>
/// Accept a new TCP connection either secure or unsecure.
/// </summary>
/// <returns>Newly accepted TCP connection</returns>
[[nodiscard]] SecureTcpClient* accept()
{
sockaddr_in client = {};
socklen_t clientLen = sizeof(client);
int fd = Socket::accept(reinterpret_cast<sockaddr*>(&client), &clientLen);
if (fd < 0) {
return nullptr;
}
return new SecureTcpClient(fd, m_pSSLCtx, client, clientLen);
}
private:
SSL_CTX* m_pSSLCtx;
const std::string m_keyFile;
const std::string m_certFile;
/// <summary>
///
/// </summary>
void initSecureFiles()
{
if (SSL_CTX_use_certificate_file(m_pSSLCtx, m_certFile.c_str(), SSL_FILETYPE_PEM) != 1) {
ERR_print_errors_fp(stderr);
throw std::runtime_error("Failed to use PEM certificate file");
}
if (SSL_CTX_use_PrivateKey_file(m_pSSLCtx, m_keyFile.c_str(), SSL_FILETYPE_PEM) != 1) {
ERR_print_errors_fp(stderr);
throw std::runtime_error("Failed to use PEM private key file");
}
if (SSL_CTX_check_private_key(m_pSSLCtx) != 1) {
ERR_print_errors_fp(stderr);
throw std::runtime_error("Keys do not match!");
}
}
};
} // namespace tcp
} // namespace network
#endif // ENABLE_TCP_SSL
#endif // __SECURE_TCP_SERVER_H__

@ -60,7 +60,7 @@ namespace network
/// <param name="backlog"></param>
TcpListener(const std::string& ipAddr, const uint16_t port, const int backlog) noexcept(false) : TcpListener(port, ipAddr)
{
if (listen(backlog) < 0) {
if (listen(ipAddr, port, backlog) < 0) {
LogError(LOG_NET, "Failed to listen on TCP server, err: %d", errno);
throw std::runtime_error("Failed to listen on TCP server.");
}

Loading…
Cancel
Save

Powered by TurnKey Linux.