Skip to content

Commit

Permalink
Adding tls key logger for EvseV2G (openssl part) (#910)
Browse files Browse the repository at this point in the history
Signed-off-by: Sebastian Lukas <sebastian.lukas@pionix.de>
Signed-off-by: James Chapman <james.chapman@pionix.de>
Co-authored-by: James Chapman <james.chapman@pionix.de>
  • Loading branch information
SebaLukas and james-ctc authored Oct 24, 2024
1 parent a6867fd commit 25472a1
Show file tree
Hide file tree
Showing 10 changed files with 223 additions and 4 deletions.
1 change: 1 addition & 0 deletions config/config-sil-dc-tls.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ active_modules:
config_module:
device: auto
tls_security: force
tls_key_logging: true
connections:
security:
- module_id: evse_security
Expand Down
5 changes: 5 additions & 0 deletions lib/staging/tls/openssl_util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,7 @@ bool certificate_subject_public_key_sha_1(openssl::sha_1_digest_t& digest, const

enum class log_level_t : std::uint8_t {
debug,
info,
warning,
error,
};
Expand All @@ -523,6 +524,10 @@ static inline void log_debug(const std::string& str) {
log(log_level_t::debug, str);
}

static inline void log_info(const std::string& str) {
log(log_level_t::info, str);
}

using log_handler_t = void (*)(log_level_t level, const std::string& err);

/**
Expand Down
3 changes: 3 additions & 0 deletions lib/staging/tls/tests/gtest_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ void log_handler(openssl::log_level_t level, const std::string& str) {
case openssl::log_level_t::debug:
// std::cout << "DEBUG: " << str << std::endl;
break;
case openssl::log_level_t::info:
std::cout << "INFO: " << str << std::endl;
break;
case openssl::log_level_t::warning:
std::cout << "WARN: " << str << std::endl;
break;
Expand Down
149 changes: 146 additions & 3 deletions lib/staging/tls/tls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,18 @@
#include "extensions/trusted_ca_keys.hpp"
#include "openssl_util.hpp"

#include <arpa/inet.h>
#include <array>
#include <cassert>
#include <csignal>
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <fcntl.h>
#include <fstream>
#include <memory>
#include <mutex>
#include <net/if.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#include <poll.h>
Expand Down Expand Up @@ -63,6 +66,7 @@ template <> class default_delete<BIO_ADDRINFO> {
} // namespace std

using ::openssl::log_error;
using ::openssl::log_info;
using ::openssl::log_warning;

namespace {
Expand Down Expand Up @@ -692,8 +696,43 @@ std::uint32_t ServerConnection::m_count{0};
std::mutex ServerConnection::m_cv_mutex;
std::condition_variable ServerConnection::m_cv;

namespace {

int ssl_keylog_file_index{-1};
int ssl_keylog_server_index{-1};

void keylog_callback(const SSL* ssl, const char* line) {

auto keylog_server = static_cast<TlsKeyLoggingServer*>(SSL_get_ex_data(ssl, ssl_keylog_server_index));

std::string key_log_msg = "TLS Handshake keys on port ";
key_log_msg += std::to_string(keylog_server->get_port()) + ": ";
key_log_msg += std::string(line);

log_info(key_log_msg);

if (keylog_server->get_fd() != -1) {
const auto result = keylog_server->send(line);
if (result not_eq strlen(line)) {
log_error("key_logging_server send() failed!");
}
}

auto keylog_file_path =
static_cast<std::filesystem::path*>(SSL_CTX_get_ex_data(SSL_get_SSL_CTX(ssl), ssl_keylog_file_index));

if (not keylog_file_path->empty()) {
std::ofstream ofs;
ofs.open(keylog_file_path->string(), std::ofstream::out | std::ofstream::app);
ofs << line << std::endl;
ofs.close();
}
}

} // namespace

ServerConnection::ServerConnection(SslContext* ctx, int soc, const char* ip_in, const char* service_in,
std::int32_t timeout_ms) :
std::int32_t timeout_ms, const ConfigItem& tls_key_interface) :
Connection(ctx, soc, ip_in, service_in, timeout_ms), m_tck_data{m_trusted_ca_keys, m_flags} {
{
std::lock_guard lock(m_cv_mutex);
Expand All @@ -703,6 +742,12 @@ ServerConnection::ServerConnection(SslContext* ctx, int soc, const char* ip_in,
SSL_set_accept_state(m_context->ctx.get());
ServerStatusRequestV2::set_data(m_context->ctx.get(), &m_flags);
ServerTrustedCaKeys::set_data(m_context->ctx.get(), &m_tck_data);

if (tls_key_interface != nullptr) {
const auto port = std::stoul(service_in);
m_keylog_server = std::make_unique<TlsKeyLoggingServer>(std::string(tls_key_interface), port);
SSL_set_ex_data(m_context->ctx.get(), ssl_keylog_server_index, m_keylog_server.get());
}
}
}

Expand Down Expand Up @@ -885,6 +930,26 @@ bool Server::init_ssl(const config_t& cfg) {
// use the first server chain
result = configure_ssl_ctx(ctx, cfg.ciphersuites, cfg.cipher_list, cfg.chains[0], true);
if (result) {

if (cfg.tls_key_logging) {
tls_key_log_file_path = std::filesystem::path(cfg.tls_key_logging_path) /= "tls_session_keys.log";

ssl_keylog_file_index = SSL_CTX_get_ex_new_index(0, std::string("").data(), nullptr, nullptr, nullptr);
ssl_keylog_server_index = SSL_get_ex_new_index(0, std::string("").data(), nullptr, nullptr, nullptr);

if (ssl_keylog_file_index == -1 or ssl_keylog_server_index == -1) {
auto error_msg = std::string("_get_ex_new_index failed: ssl_keylog_file_index: ");
error_msg += std::to_string(ssl_keylog_file_index);
error_msg += ", ssl_keylog_server_index: " + std::to_string(ssl_keylog_server_index);
log_error(error_msg);
} else {
SSL_CTX_set_ex_data(ctx, ssl_keylog_file_index, &tls_key_log_file_path);

SSL_CTX_set_keylog_callback(ctx, keylog_callback);
m_tls_key_interface = cfg.host;
}
}

int mode = SSL_VERIFY_NONE;

// TODO(james-ctc): verify may need to change based on TLS version
Expand Down Expand Up @@ -1062,8 +1127,9 @@ void Server::wait_for_connection(const ConnectionHandler& handler) {
// new connection, pass to handler
auto* ip = BIO_ADDR_hostname_string(peer.get(), 1);
auto* service = BIO_ADDR_service_string(peer.get(), 1);
auto connection =
std::make_unique<ServerConnection>(m_context->ctx.get(), soc, ip, service, m_timeout_ms);

auto connection = std::make_unique<ServerConnection>(m_context->ctx.get(), soc, ip, service,
m_timeout_ms, m_tls_key_interface);
handler(std::move(connection));
OPENSSL_free(ip);
OPENSSL_free(service);
Expand Down Expand Up @@ -1345,4 +1411,81 @@ Client::override_t Client::default_overrides() {
};
}

// ----------------------------------------------------------------------------
// TlsKeyLoggingServer

TlsKeyLoggingServer::TlsKeyLoggingServer(const std::string& interface_name, uint16_t port_) : port(port_) {
static constexpr auto LINK_LOCAL_MULTICAST = "ff02::1";
bool result{true};

fd = socket(AF_INET6, SOCK_DGRAM, 0);
if (fd == -1) {
log_error("Could not create socket");
result = false;
}

if (result) {
// source setup
// find port between 49152-65535
auto could_bind = false;
auto source_port = 49152;
for (; source_port < 65535; source_port++) {
sockaddr_in6 source_address = {AF_INET6, htons(source_port), 0, {}, 0};
if (bind(fd, reinterpret_cast<sockaddr*>(&source_address), sizeof(sockaddr_in6)) == 0) {
could_bind = true;
break;
}
}

if (could_bind) {
log_info("UDP socket bound to source port: " + std::to_string(source_port));
} else {
log_error("Could not bind");
result = false;
}
}

if (result) {
auto mreq = ipv6_mreq{};
const auto index = if_nametoindex(interface_name.c_str());
mreq.ipv6mr_interface = index;
if (inet_pton(AF_INET6, LINK_LOCAL_MULTICAST, &mreq.ipv6mr_multiaddr) <= 0) {
log_error("Failed to setup multicast address");
result = false;
}

if (setsockopt(fd, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) {
log_error("Could not add multicast group membership");
result = false;
}

if (setsockopt(fd, IPPROTO_IPV6, IPV6_MULTICAST_IF, &index, sizeof(index)) < 0) {
log_error("Could not set interface name:" + interface_name);
result = false;
}

destination_address = {AF_INET6, htons(port), 0, {}, 0};
if (inet_pton(AF_INET6, LINK_LOCAL_MULTICAST, &destination_address.sin6_addr) <= 0) {
log_error("Failed to setup server address, reset key_log_fd");
result = false;
}
}

if (!result && fd != -1) {
close(fd);
fd = -1;
}
}

TlsKeyLoggingServer::~TlsKeyLoggingServer() {
if (fd != -1) {
close(fd);
}
}

ssize_t TlsKeyLoggingServer::send(const char* line) {
return sendto(fd, line, strlen(line), 0, reinterpret_cast<const sockaddr*>(&destination_address),
sizeof(destination_address));
}

} // namespace tls
34 changes: 33 additions & 1 deletion lib/staging/tls/tls.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
#include <condition_variable>
#include <cstddef>
#include <cstdint>
#include <filesystem>
#include <functional>
#include <memory>
#include <mutex>
#include <netinet/in.h>
#include <openssl/types.h>
#include <optional>
#include <pthread.h>
Expand Down Expand Up @@ -57,6 +59,27 @@ class ConfigItem {
}
};

class TlsKeyLoggingServer {
public:
TlsKeyLoggingServer(const std::string& interface_name, uint16_t port_);
~TlsKeyLoggingServer();

ssize_t send(const char* line);

auto get_fd() const {
return fd;
}

auto get_port() const {
return port;
}

private:
int fd{-1};
uint16_t port{0};
sockaddr_in6 destination_address{};
};

// ----------------------------------------------------------------------------
// Connection represents a TLS connection

Expand Down Expand Up @@ -258,8 +281,11 @@ class ServerConnection : public Connection {
StatusFlags m_flags; //!< extension flags
server_trusted_ca_keys_t m_tck_data; //!< extension per connection data

std::unique_ptr<TlsKeyLoggingServer> m_keylog_server{nullptr};

public:
ServerConnection(SslContext* ctx, int soc, const char* ip_in, const char* service_in, std::int32_t timeout_ms);
ServerConnection(SslContext* ctx, int soc, const char* ip_in, const char* service_in, std::int32_t timeout_ms,
const ConfigItem& tls_key_interface);
ServerConnection() = delete;
ServerConnection(const ServerConnection&) = delete;
ServerConnection(ServerConnection&&) = delete;
Expand Down Expand Up @@ -381,6 +407,9 @@ class Server {
ConfigItem service{nullptr}; //!< TLS port number as a string
int socket{INVALID_SOCKET}; //!< use this specific socket - bypasses socket setup in init_socket() when set
bool ipv6_only{true}; //!< listen on IPv6 only, when false listen on IPv4 only

bool tls_key_logging{false}; //!< tls key logging is active when true
std::string tls_key_logging_path; //!< tls key logging file path
};

using ConnectionPtr = std::unique_ptr<ServerConnection>;
Expand Down Expand Up @@ -409,6 +438,9 @@ class Server {
static int s_sig_int; //!< signal to use to wakeup serve()
ConfigurationCallback m_init_callback{nullptr}; //!< callback to retrieve SSL configuration

ConfigItem m_tls_key_interface{nullptr};
std::filesystem::path tls_key_log_file_path{};

/**
* \brief initialise the server socket
* \param[in] cfg server configuration
Expand Down
3 changes: 3 additions & 0 deletions modules/EvseV2G/EvseV2G.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ void log_handler(openssl::log_level_t level, const std::string& str) {
case openssl::log_level_t::debug:
// ignore debug logs
break;
case openssl::log_level_t::info:
EVLOG_info << str;
break;
case openssl::log_level_t::warning:
EVLOG_warning << str;
break;
Expand Down
4 changes: 4 additions & 0 deletions modules/EvseV2G/connection/tls_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,10 @@ bool build_config(tls::Server::config_t& config, struct v2g_context* ctx) {
config.socket = ctx->tls_socket.fd;
config.io_timeout_ms = static_cast<std::int32_t>(ctx->network_read_timeout_tls);

config.tls_key_logging = ctx->tls_key_logging;
config.tls_key_logging_path = ctx->tls_key_logging_path;
config.host = ctx->if_name;

// information from libevse-security
const auto cert_info =
ctx->r_security->call_get_all_valid_certificates_info(LeafCertificateType::V2G, EncodingFormat::PEM, true);
Expand Down
24 changes: 24 additions & 0 deletions modules/IsoMux/IsoMux.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,27 @@
#include "log.hpp"
#include "sdp.hpp"

#include <openssl_util.hpp>
namespace {
void log_handler(openssl::log_level_t level, const std::string& str) {
switch (level) {
case openssl::log_level_t::debug:
// ignore debug logs
break;
case openssl::log_level_t::info:
EVLOG_info << str;
break;
case openssl::log_level_t::warning:
EVLOG_warning << str;
break;
case openssl::log_level_t::error:
default:
EVLOG_error << str;
break;
}
}
} // namespace

struct v2g_context* v2g_ctx = nullptr;

namespace module {
Expand All @@ -21,6 +42,9 @@ void IsoMux::init() {
v2g_ctx->proxy_port_iso20 = config.proxy_port_iso20;
v2g_ctx->selected_iso20 = false;

v2g_ctx->tls_key_logging = config.tls_key_logging;

(void)openssl::set_log_handler(log_handler);
v2g_ctx->tls_server = &tls_server;

invoke_init(*p_charger);
Expand Down
2 changes: 2 additions & 0 deletions modules/IsoMux/connection/tls_connection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@ bool build_config(tls::Server::config_t& config, struct v2g_context* ctx) {
config.socket = ctx->tls_socket.fd;
config.io_timeout_ms = static_cast<std::int32_t>(ctx->network_read_timeout_tls);

config.tls_key_logging = ctx->tls_key_logging;

// information from libevse-security
const auto cert_info =
ctx->r_security->call_get_leaf_certificate_info(LeafCertificateType::V2G, EncodingFormat::PEM, false);
Expand Down
2 changes: 2 additions & 0 deletions modules/IsoMux/v2g.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,8 @@ struct v2g_context {
} tls_socket;
tls::Server* tls_server;

bool tls_key_logging;

enum V2gMsgTypeId current_v2g_msg; /* holds the last v2g msg type */
int state; /* holds the current state id */
std::atomic_bool is_connection_terminated; /* Is set to true if the connection is terminated (CP State A/F, shutdown
Expand Down

0 comments on commit 25472a1

Please sign in to comment.