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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
|
#pragma once
#include <atomic>
#include <fstream>
#include <map>
#include <memory>
#include <optional>
#include <string>
#include <vector>
#include <optional>
#include "config.h"
#include "circular_buffer.hh"
#include "lock.hh"
#include "misc.hh"
enum class LibsslTLSVersion : uint8_t { Unknown, TLS10, TLS11, TLS12, TLS13 };
struct TLSCertKeyPair
{
std::string d_cert;
std::optional<std::string> d_key;
std::optional<std::string> d_password;
explicit TLSCertKeyPair(const std::string& cert, std::optional<std::string> key = std::nullopt, std::optional<std::string> password = std::nullopt):
d_cert(cert), d_key(std::move(key)), d_password(std::move(password)) {
}
};
class TLSConfig
{
public:
std::vector<TLSCertKeyPair> d_certKeyPairs;
std::vector<std::string> d_ocspFiles;
std::string d_ciphers;
std::string d_ciphers13;
std::string d_ticketKeyFile;
std::string d_keyLogFile;
size_t d_maxStoredSessions{20480};
time_t d_sessionTimeout{0};
time_t d_ticketsKeyRotationDelay{43200};
uint8_t d_numberOfTicketsKeys{5};
LibsslTLSVersion d_minTLSVersion{LibsslTLSVersion::TLS10};
bool d_preferServerCiphers{true};
bool d_enableTickets{true};
/* whether OpenSSL will release I/O buffers when the connection
becomes idle, saving memory */
bool d_releaseBuffers{true};
/* whether so-called secure renegotiation should be allowed for TLS < 1.3 */
bool d_enableRenegotiation{false};
/* enable TLS async mode, if supported by any engine */
bool d_asyncMode{false};
/* enable kTLS mode, if supported */
bool d_ktls{false};
/* set read ahead mode, if supported */
bool d_readAhead{true};
};
struct TLSErrorCounters
{
std::atomic<uint64_t> d_dhKeyTooSmall{0}; /* the other side sent a DH value that is not large enough */
std::atomic<uint64_t> d_inappropriateFallBack{0}; /* SCSV indicates that the client previously tried a higher version,
something bad is happening */
std::atomic<uint64_t> d_noSharedCipher{0}; /* we could not agree on a cipher to use */
std::atomic<uint64_t> d_unknownCipherType{0}; /* unknown cipher type */
std::atomic<uint64_t> d_unknownKeyExchangeType{0}; /* * unknown exchange type, weird */
std::atomic<uint64_t> d_unknownProtocol{0}; /* unknown protocol (SSLv2 or TLS 1.4, who knows? */
std::atomic<uint64_t> d_unsupportedEC{0}; /* unsupported elliptic curve */
std::atomic<uint64_t> d_unsupportedProtocol{0}; /* we don't accept this TLS version, sorry */
};
#ifdef HAVE_LIBSSL
#include <openssl/ssl.h>
void registerOpenSSLUser();
void unregisterOpenSSLUser();
/* From rfc5077 Section 4. Recommended Ticket Construction */
#define TLS_TICKETS_KEY_NAME_SIZE (16)
/* AES-256 */
#define TLS_TICKETS_CIPHER_KEY_SIZE (32)
#define TLS_TICKETS_CIPHER_ALGO (EVP_aes_256_cbc)
/* HMAC SHA-256 */
#define TLS_TICKETS_MAC_KEY_SIZE (32)
#define TLS_TICKETS_MAC_ALGO (EVP_sha256)
class OpenSSLTLSTicketKey
{
public:
OpenSSLTLSTicketKey();
OpenSSLTLSTicketKey(std::ifstream& file);
OpenSSLTLSTicketKey(const std::string& key);
~OpenSSLTLSTicketKey();
bool nameMatches(const unsigned char name[TLS_TICKETS_KEY_NAME_SIZE]) const;
#if OPENSSL_VERSION_MAJOR >= 3
int encrypt(unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, EVP_MAC_CTX* hctx) const;
bool decrypt(const unsigned char* iv, EVP_CIPHER_CTX* ectx, EVP_MAC_CTX* hctx) const;
#else
int encrypt(unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, HMAC_CTX* hctx) const;
bool decrypt(const unsigned char* iv, EVP_CIPHER_CTX* ectx, HMAC_CTX* hctx) const;
#endif
[[nodiscard]] std::string content() const;
private:
unsigned char d_name[TLS_TICKETS_KEY_NAME_SIZE];
unsigned char d_cipherKey[TLS_TICKETS_CIPHER_KEY_SIZE];
unsigned char d_hmacKey[TLS_TICKETS_MAC_KEY_SIZE];
};
class OpenSSLTLSTicketKeysRing
{
public:
OpenSSLTLSTicketKeysRing(size_t capacity);
~OpenSSLTLSTicketKeysRing();
std::shared_ptr<OpenSSLTLSTicketKey> getEncryptionKey();
std::shared_ptr<OpenSSLTLSTicketKey> getDecryptionKey(unsigned char name[TLS_TICKETS_KEY_NAME_SIZE], bool& activeKey);
size_t getKeysCount();
void loadTicketsKeys(const std::string& keyFile);
void loadTicketsKey(const std::string& key);
void rotateTicketsKey(time_t now);
private:
void addKey(std::shared_ptr<OpenSSLTLSTicketKey>&& newKey);
SharedLockGuarded<boost::circular_buffer<std::shared_ptr<OpenSSLTLSTicketKey> > > d_ticketKeys;
};
void* libssl_get_ticket_key_callback_data(SSL* s);
void libssl_set_ticket_key_callback_data(SSL_CTX* ctx, void* data);
#if OPENSSL_VERSION_MAJOR >= 3
int libssl_ticket_key_callback(SSL* s, OpenSSLTLSTicketKeysRing& keyring, unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, EVP_MAC_CTX* hctx, int enc);
#else
int libssl_ticket_key_callback(SSL* s, OpenSSLTLSTicketKeysRing& keyring, unsigned char keyName[TLS_TICKETS_KEY_NAME_SIZE], unsigned char* iv, EVP_CIPHER_CTX* ectx, HMAC_CTX* hctx, int enc);
#endif
#ifndef DISABLE_OCSP_STAPLING
int libssl_ocsp_stapling_callback(SSL* ssl, const std::map<int, std::string>& ocspMap);
#ifdef HAVE_OCSP_BASIC_SIGN
bool libssl_generate_ocsp_response(const std::string& certFile, const std::string& caCert, const std::string& caKey, const std::string& outFile, int ndays, int nmin);
#endif
#endif /* DISABLE_OCSP_STAPLING */
void libssl_set_error_counters_callback(SSL_CTX& ctx, TLSErrorCounters* counters);
LibsslTLSVersion libssl_tls_version_from_string(const std::string& str);
const std::string& libssl_tls_version_to_string(LibsslTLSVersion version);
namespace pdns::libssl {
class ServerContext
{
public:
using SharedContext = std::shared_ptr<SSL_CTX>;
using SNIToContextMap = std::map<std::string, SharedContext, std::less<>>;
SharedContext d_defaultContext;
SNIToContextMap d_sniMap;
std::map<int, std::string> d_ocspResponses;
};
}
/* return the created context, and a list of warning messages for issues not severe enough
to trigger raising an exception, like failing to load an OCSP response file */
std::pair<std::unique_ptr<SSL_CTX, decltype(&SSL_CTX_free)>, std::vector<std::string>> libssl_init_server_context_no_sni(const TLSConfig& config,
std::map<int, std::string>& ocspResponses);
std::pair<pdns::libssl::ServerContext, std::vector<std::string>> libssl_init_server_context(const TLSConfig& config);
pdns::UniqueFilePtr libssl_set_key_log_file(SSL_CTX* ctx, const std::string& logFile);
/* called in a server context, to select an ALPN value advertised by the client if any */
void libssl_set_alpn_select_callback(SSL_CTX* ctx, int (*callback)(SSL* ssl, const unsigned char** out, unsigned char* outlen, const unsigned char* inPtr, unsigned int inlen, void* arg), void* arg);
/* set the supported ALPN protos in client context */
bool libssl_set_alpn_protos(SSL_CTX* ctx, const std::vector<std::vector<uint8_t>>& protos);
std::string libssl_get_error_string();
#if defined(HAVE_LIBSSL) && OPENSSL_VERSION_MAJOR >= 3 && defined(HAVE_TLS_PROVIDERS)
std::pair<bool, std::string> libssl_load_provider(const std::string& engineName);
#endif /* HAVE_LIBSSL && OPENSSL_VERSION_MAJOR >= 3 && HAVE_TLS_PROVIDERS */
#if defined(HAVE_LIBSSL) && !defined(HAVE_TLS_PROVIDERS)
std::pair<bool, std::string> libssl_load_engine(const std::string& engineName, const std::optional<std::string>& defaultString);
#endif /* HAVE_LIBSSL && !HAVE_TLS_PROVIDERS */
#endif /* HAVE_LIBSSL */
|