1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169
|
// OpenVPN -- An application to securely tunnel IP networks
// over a single port, with support for SSL/TLS-based
// session authentication and key exchange,
// packet encryption, packet authentication, and
// packet compression.
//
// Copyright (C) 2012- OpenVPN Inc.
//
// SPDX-License-Identifier: MPL-2.0 OR AGPL-3.0-only WITH openvpn3-openssl-exception
//
#pragma once
#include <string>
#include <memory>
#include <optional>
#include <stdexcept>
#include <openvpn/buffer/buffer.hpp>
#include <openvpn/ssl/sslchoose.hpp>
#include <openvpn/ssl/sslapi.hpp>
namespace openvpn {
/**
@brief The SslApiBuilder struct is used to initialize and configure an SSL/TLS API in OpenVPN.
@class SslApiBuilder
It takes in a configuration pointer for the SSL library and uses that to initialize an SSL connection
object. It does not directly produce any outputs, but allows accessing the initialized SSLAPI server
object via the get() method.
Important transforms are using the SSLAPI config to initialize the SSLAPI object correctly. This
handles the low-level details of configuring SSL securely via the sslctx abstraction layer.
*/
struct SslApiBuilder
{
/**
@brief Construct a new SslApiBuilder object
@param cfg configuration that should be installed
*/
SslApiBuilder(SSLLib::SSLAPI::Config::Ptr cfg)
: mConfig(std::move(cfg)),
mFactory(mConfig->new_factory()),
mServer(mFactory->ssl()) {};
SslApiBuilder(const SslApiBuilder &) = delete;
SslApiBuilder(SslApiBuilder &&) noexcept = delete;
SslApiBuilder &operator=(const SslApiBuilder &) = delete;
SslApiBuilder &operator=(SslApiBuilder &&) = delete;
public: // API
/**
@brief get a reference to the encapsulated ssl object
@return openvpn::SSLAPI& a reference to the ready-to-use ssl object
*/
openvpn::SSLAPI &get()
{
return *mServer;
}
private: // Data
SSLLib::SSLAPI::Config::Ptr mConfig; ///< Configuration for this SSL server
openvpn::SSLFactoryAPI::Ptr mFactory; ///< Factory from the SSL configuration
openvpn::SSLAPI::Ptr mServer; ///< Server created from the factory - depends on mConfig and mFactory
};
/**
@brief defines a class that handles SSL/TLS handshaking
@class AccHandshaker
Defines a class that handles SSL/TLS handshaking for device authentication.
It takes in a configuration pointer for the SSL library and uses that to initialize an SSL connection
object. The main methods are the constructor which takes the SSL config pointer and initializes the internal
SSL object using that config. The process_msg method takes in a message string, passes it into the SSL object
to continue the handshake, and returns any response message the SSL object generates during the handshake.
This allows incrementally processing the handshake protocol messages. The reset method reinitializes the SSL
object if the config changes.
Internally it contains a unique pointer to a SslApiBuilder object. The SslApiBuilder initializes the lower
level SSL objects like the SSL context, factory, and server instance using the provided configuration. So the
AccHandshaker gives a simple interface to perform an SSL handshake using an SSL configuration. It handles
setting up the SSL objects correctly, feeding the handshake messages into the SSL library, and getting any
responses back out. This allows verifying possession of the correct certificates and keys.
*/
struct AccHandshaker
{
using MsgT = std::optional<std::string>;
AccHandshaker() = default;
AccHandshaker(SSLLib::SSLAPI::Config::Ptr cfg);
MsgT process_msg(const MsgT &msg);
std::string details();
void reset(SSLLib::SSLAPI::Config::Ptr cfg);
private:
std::unique_ptr<SslApiBuilder> mSslApi;
};
/**
@brief Construct a new AccHandshaker object
@param cfg an initialized confiog object type Config::Ptr
*/
inline AccHandshaker::AccHandshaker(SSLLib::SSLAPI::Config::Ptr cfg)
: mSslApi(new SslApiBuilder(std::move(cfg)))
{
mSslApi->get().start_handshake();
}
/**
@brief Incrementally process the CLIENT HELLO / SERVER HELLO exchange
@param msg optional cipher text from the TLS peer
@return optional<string> reply for the given msg text if any
@exception std::exception derived type with more information regarding the problem
The function will stop returning reply data when it's done handshaking. A handshake failure may result in
an exception derived from std::exception being thrown.
*/
inline AccHandshaker::MsgT AccHandshaker::process_msg(const MsgT &msg)
{
if (!mSslApi)
throw std::runtime_error("AccHandshaker::process_msg: not configured");
MsgT ret = std::nullopt;
auto &api = mSslApi->get();
if (msg)
{
api.write_ciphertext(BufferAllocatedRc::Create(reinterpret_cast<const unsigned char *>(msg->c_str()),
msg->size(),
0));
// Won't handshake without this even though there is no data available.
uint8_t cleartext[8];
api.read_cleartext(cleartext, sizeof(cleartext));
}
if (api.read_ciphertext_ready())
{
auto reply = api.read_ciphertext();
ret = {reinterpret_cast<const char *>(reply->c_data()),
reinterpret_cast<const char *>(reply->c_data_end())};
}
return ret;
}
/**
@brief returns ssl_handshake_details() if the SSLAPI is available
@return std::string containing SSLAPI details
@exception std::exception derived type with more information regarding the problem
*/
inline std::string AccHandshaker::details()
{
if (!mSslApi)
throw std::runtime_error("AccHandshaker::details: not configured");
return mSslApi->get().ssl_handshake_details();
}
/**
@brief Re-init the handshaker
@param cfg configuration that should be installed
@exception throws an object derived from std::exception if there is a problem with the init process
Rebuilds the SSLAPI object with the specified configuration and begins the handshake process. Data
exchange for the actual handshake is done via calls to process_msg.
*/
inline void AccHandshaker::reset(SSLLib::SSLAPI::Config::Ptr cfg)
{
mSslApi.reset(new SslApiBuilder(std::move(cfg)));
mSslApi->get().start_handshake();
}
} // namespace openvpn
|