Skip to content

Commit 6088281

Browse files
committed
TCP input TLS - Add Ssl, wrapper around SSL.
1 parent a15c328 commit 6088281

File tree

3 files changed

+194
-0
lines changed

3 files changed

+194
-0
lines changed

src/plugins/input/tcp/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ add_library(tcp-input MODULE
1313
src/Acceptor.cpp
1414
src/Plugin.cpp
1515
src/IpxPlugin.cpp
16+
src/tls/Ssl.cpp
1617
)
1718

1819
find_package(LibLz4 REQUIRED)
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* \file
3+
* \author Jakub Antonín Štigler <[email protected]>
4+
* \brief Wrapper around SSL from OpenSSL. (source file)
5+
* \date 2025
6+
*
7+
* Copyright: (C) 2023 CESNET, z.s.p.o.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#include "Ssl.hpp"
12+
13+
namespace tcp_in {
14+
namespace tls {
15+
16+
bool Ssl::accept() {
17+
auto res = SSL_accept(m_ssl.get());
18+
if (res <= 0) {
19+
auto err = SSL_get_error(m_ssl.get(), res);
20+
// Nonblocking client needs to wait.
21+
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) {
22+
return false;
23+
}
24+
throw_ssl_err(err, "Failed performing TLS handshake with client.");
25+
}
26+
return true;
27+
}
28+
29+
ReadResult Ssl::read_ex(std::vector<std::uint8_t> &buf) {
30+
buf.resize(buf.capacity());
31+
std::size_t len;
32+
auto ret = SSL_read_ex(m_ssl.get(), buf.data(), buf.size(), &len);
33+
34+
if (ret <= 0) {
35+
auto err = SSL_get_error(m_ssl.get(), ret);
36+
buf.resize(0);
37+
switch (err) {
38+
case SSL_ERROR_SSL:
39+
// The TLS connection is broken.
40+
case SSL_ERROR_ZERO_RETURN:
41+
return ReadResult::FINISHED;
42+
case SSL_ERROR_SYSCALL:
43+
return ReadResult::CLOSED;
44+
case SSL_ERROR_WANT_READ:
45+
case SSL_ERROR_WANT_WRITE:
46+
return ReadResult::WAIT;
47+
default:
48+
throw_ssl_err(err, "SSL failed to read data.");
49+
}
50+
}
51+
52+
buf.resize(std::min(len, buf.size()));
53+
return ReadResult::READ;
54+
}
55+
56+
} // namespace tls
57+
} // namespace tcp_in
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
/**
2+
* \file
3+
* \author Jakub Antonín Štigler <[email protected]>
4+
* \brief Wrapper around SSL from OpenSSL. (header file)
5+
* \date 2025
6+
*
7+
* Copyright: (C) 2023 CESNET, z.s.p.o.
8+
* SPDX-License-Identifier: BSD-3-Clause
9+
*/
10+
11+
#pragma once
12+
13+
#include <cstdint>
14+
#include <memory>
15+
#include <vector>
16+
17+
#include <openssl/ssl.h>
18+
19+
#include "SslBio.hpp"
20+
#include "throw_ssl_err.hpp"
21+
22+
namespace tcp_in {
23+
namespace tls {
24+
25+
/** Describes result of read operation. */
26+
enum class ReadResult {
27+
/** Successufully read some data. */
28+
READ,
29+
/** Non blocking socket needs to wait for more data. */
30+
WAIT,
31+
/** Peer will not send any further data. */
32+
FINISHED,
33+
/** The connection has been closed. (socket cannot read) */
34+
CLOSED,
35+
};
36+
37+
/**
38+
* @brief Wrapper around `SSL` from OpenSSL.
39+
*
40+
* This is basically io stream for data encrypted with TLS.
41+
*/
42+
class Ssl {
43+
public:
44+
/**
45+
* @brief Construct `Ssl` from the given `SSL_CTX`.
46+
* @param ctx Base context for the ssl object.
47+
* @throws `std::runtime_error` if failed to create new bio.
48+
*/
49+
Ssl(SSL_CTX *ctx) : m_ssl(SSL_new(ctx)) {
50+
if (!m_ssl) {
51+
throw_ssl_err("Failed to create SSL object.");
52+
}
53+
}
54+
55+
/**
56+
* @brief Set the bio object from which this will read data.
57+
* @param bio Bio object from which to read. This will take ownership of the given `SslBio`.
58+
*/
59+
void set_bio(SslBio bio) noexcept {
60+
auto raw_bio = bio.release_ptr();
61+
SSL_set_bio(m_ssl.get(), raw_bio, raw_bio);
62+
}
63+
64+
/**
65+
* @brief Set the server name for SNI.
66+
* @param hostname Name of the server.
67+
* @throws `std::runtime_error` on failure.
68+
*/
69+
void set_tlsext_host_name(const char *hostname) {
70+
if (!SSL_set_tlsext_host_name(m_ssl.get(), hostname)) {
71+
throw_ssl_err("Failed to set the SNI hostname.");
72+
}
73+
}
74+
75+
/**
76+
* @brief Set the expected DNS name of the server.
77+
* @param hostname Hostname of the server.
78+
* @throws `std::runtime_error` on failure.
79+
*/
80+
void set1_host(const char *hostname) {
81+
if (!SSL_set1_host(m_ssl.get(), hostname)) {
82+
throw_ssl_err(
83+
"Failed to set the cergificate verification hostname."
84+
);
85+
}
86+
}
87+
88+
/**
89+
* @brief Perform TLS handshake.
90+
*
91+
* In case of nonblocking server, this can be multiple times to continue the tls handshake.
92+
* @returns `true` if TLS handshake is successfully complete. `false` if the tls handshake is
93+
* incomplete because the underlaying socket is nonblocking and it needs to wait for more data.
94+
* @throws `std::runtime_error` on failure.
95+
*/
96+
bool accept();
97+
98+
/**
99+
* @brief Read application data from the TLS connection.
100+
*
101+
* @param [out] buf Will be cleared. The read data will be put here. Up to the capacity of the
102+
* buffer will be used.
103+
* @return result of the read operation:
104+
* - `READ`: Successfully read some data. `buf` shouldn't be empty.
105+
* - `WAIT`: Non blocking socket needs to wait for more data. `buf` should be empty.
106+
* - `FINISHED`: Peer will not send any further data. `buf` should be empty.
107+
* - `CLOSED`: The connection has been closed. (socket cannot read) `buf` should be empty.
108+
* @throws `std::runtime_error` on failure.
109+
*/
110+
ReadResult read_ex(std::vector<std::uint8_t> &buf);
111+
112+
/**
113+
* @brief Shuts the connection down.
114+
* @return `true` on successful shutdown, false if shutdown is in process but not complete yet.
115+
* @throws `std::runtime_error` on failure.
116+
*/
117+
bool shutdown() {
118+
auto ret = SSL_shutdown(m_ssl.get());
119+
if (ret < 0) {
120+
throw_ssl_err("SSL failed to shutdown connection.");
121+
}
122+
return ret == 1;
123+
}
124+
125+
private:
126+
/** Deleter for SSL. */
127+
class Deleter {
128+
public:
129+
void operator()(SSL *ssl) { SSL_free(ssl); }
130+
};
131+
132+
std::unique_ptr<SSL, Deleter> m_ssl;
133+
};
134+
135+
} // namespace tls
136+
} // namespace tcp_in

0 commit comments

Comments
 (0)