}
#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
-static bool loadTLSCertificateAndKeys(const std::string& context, std::vector<std::pair<std::string, std::string>>& pairs, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles)
+static bool loadTLSCertificateAndKeys(const std::string& context, std::vector<TLSCertKeyPair>& pairs, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, std::vector<std::pair<int, std::string>>, std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles, std::optional<std::string> password = std::nullopt)
{
if (certFiles.type() == typeid(std::string) && keyFiles.type() == typeid(std::string)) {
auto certFile = boost::get<std::string>(certFiles);
auto keyFile = boost::get<std::string>(keyFiles);
pairs.clear();
- pairs.emplace_back(certFile, keyFile);
+ pairs.emplace_back(certFile, keyFile, password);
+ }
+ else if (certFiles.type() == typeid(std::shared_ptr<TLSCertKeyPair>)) {
+ auto cert = boost::get<std::shared_ptr<TLSCertKeyPair>>(certFiles);
+ pairs.clear();
+ pairs.emplace_back(*cert);
+ }
+ else if (certFiles.type() == typeid(std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>)) {
+ auto certs = boost::get<std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>>(certFiles);
+ pairs.clear();
+ for (const auto& cert : certs) {
+ pairs.emplace_back(*(cert.second));
+ }
}
else if (certFiles.type() == typeid(std::vector<std::pair<int, std::string>>) && keyFiles.type() == typeid(std::vector<std::pair<int, std::string>>)) {
auto certFilesVect = boost::get<std::vector<std::pair<int, std::string>>>(certFiles);
if (certFilesVect.size() == keyFilesVect.size()) {
pairs.clear();
for (size_t idx = 0; idx < certFilesVect.size(); idx++) {
- pairs.emplace_back(certFilesVect.at(idx).second, keyFilesVect.at(idx).second);
+ pairs.emplace_back(certFilesVect.at(idx).second, keyFilesVect.at(idx).second, password);
}
}
else {
}
});
- luaCtx.writeFunction("addDOHLocal", [client](const std::string& addr, boost::optional<boost::variant<std::string, std::vector<std::pair<int, std::string>>>> certFiles, boost::optional<boost::variant<std::string, std::vector<std::pair<int, std::string>>>> keyFiles, boost::optional<boost::variant<std::string, vector<pair<int, std::string>>>> urls, boost::optional<localbind_t> vars) {
+ typedef std::unordered_map<std::string, std::string> tlscertificateopts_t;
+ luaCtx.writeFunction("newTLSCertificate", [client](const std::string& cert, boost::optional<tlscertificateopts_t> opts) {
+ std::shared_ptr<TLSCertKeyPair> result = nullptr;
+ if (client) {
+ return result;
+ }
+#if defined(HAVE_DNS_OVER_TLS) || defined(HAVE_DNS_OVER_HTTPS)
+ std::optional<std::string> key, password;
+ if (opts) {
+ if (opts->count("key")) {
+ key = boost::get<const string>((*opts)["key"]);
+ }
+ if (opts->count("password")) {
+ password = boost::get<const string>((*opts)["password"]);
+ }
+ }
+ result = std::make_shared<TLSCertKeyPair>(TLSCertKeyPair{cert, key, password});
+#endif
+ return result;
+ });
+ luaCtx.writeFunction("addDOHLocal", [client](const std::string& addr, boost::optional<boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, std::vector<std::pair<int, std::string>>, std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>>> certFiles, boost::optional<boost::variant<std::string, std::vector<std::pair<int, std::string>>>> keyFiles, boost::optional<boost::variant<std::string, vector<pair<int, std::string>>>> urls, boost::optional<localbind_t> vars) {
if (client) {
return;
}
}
auto frontend = std::make_shared<DOHFrontend>();
- if (certFiles && !certFiles->empty() && keyFiles && !keyFiles->empty()) {
+ if (certFiles && !certFiles->empty()) {
if (!loadTLSCertificateAndKeys("addDOHLocal", frontend->d_tlsConfig.d_certKeyPairs, *certFiles, *keyFiles)) {
return;
}
}
});
- luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<DOHFrontend> frontend, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std ::vector<std::pair<int, std::string>>> keyFiles) {
+ luaCtx.registerFunction<void (std::shared_ptr<DOHFrontend>::*)(boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, std::vector<std::pair<int, std::string>>, std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<DOHFrontend> frontend, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, std::vector<std::pair<int, std::string>>, std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>> certFiles, boost::variant<std::string, std ::vector<std::pair<int, std::string>>> keyFiles) {
#ifdef HAVE_DNS_OVER_HTTPS
if (frontend != nullptr) {
if (loadTLSCertificateAndKeys("DOHFrontend::loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
}
});
- luaCtx.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles, boost::optional<localbind_t> vars) {
+ luaCtx.writeFunction("addTLSLocal", [client](const std::string& addr, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, std::vector<std::pair<int, std::string>>, std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles, boost::optional<localbind_t> vars) {
if (client) {
return;
}
frontend->setupTLS();
});
- luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::vector<std::pair<int, std::string>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles) {
+ luaCtx.registerFunction<void (std::shared_ptr<TLSFrontend>::*)(boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, std::vector<std::pair<int, std::string>>, std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles)>("loadNewCertificatesAndKeys", [](std::shared_ptr<TLSFrontend>& frontend, boost::variant<std::string, std::shared_ptr<TLSCertKeyPair>, std::vector<std::pair<int, std::string>>, std::vector<std::pair<int, std::shared_ptr<TLSCertKeyPair>>>> certFiles, boost::variant<std::string, std::vector<std::pair<int, std::string>>> keyFiles) {
#ifdef HAVE_DNS_OVER_TLS
if (loadTLSCertificateAndKeys("TLSFrontend::loadNewCertificatesAndKeys", frontend->d_tlsConfig.d_certKeyPairs, certFiles, keyFiles)) {
frontend->setupTLS();
``enableRenegotiation``, ``exactPathMatching``, ``maxConcurrentTCPConnections`` and ``releaseBuffers`` options added.
``internalPipeBufferSize`` now defaults to 1048576 on Linux.
+ .. versionchanged:: 1.x.0
+ ``certFile`` now accepts a TLSCertificate object or a list of such objects (see :func:`newTLSCertificate`)
+
Listen on the specified address and TCP port for incoming DNS over HTTPS connections, presenting the specified X.509 certificate.
If no certificate (or key) files are specified, listen for incoming DNS over HTTP connections instead.
:param str address: The IP Address with an optional port to listen on.
The default port is 443.
- :param str certFile(s): The path to a X.509 certificate file in PEM format, or a list of paths to such files.
- :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones.
+ :param str certFile(s): The path to a X.509 certificate file in PEM format, a list of paths to such files, or a TLSCertificate object.
+ :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones. Ignored if ``certFile`` contains TLSCertificate objects.
:param str-or-list urls: The path part of a URL, or a list of paths, to accept queries on. Any query with a path matching exactly one of these will be treated as a DoH query (sub-paths can be accepted by setting the ``exactPathMatching`` to false). The default is /dns-query.
:param table options: A table with key: value pairs with listen options.
``enableRenegotiation``, ``maxConcurrentTCPConnections``, ``maxInFlight`` and ``releaseBuffers`` options added.
.. versionchanged:: 1.8.0
``tlsAsyncMode`` option added.
+ .. versionchanged:: 1.8.0
+ ``certFile`` now accepts a TLSCertificate object or a list of such objects (see :func:`newTLSCertificate`)
Listen on the specified address and TCP port for incoming DNS over TLS connections, presenting the specified X.509 certificate.
:param str address: The IP Address with an optional port to listen on.
The default port is 853.
- :param str certFile(s): The path to a X.509 certificate file in PEM format, or a list of paths to such files.
- :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones.
+ :param str certFile(s): The path to a X.509 certificate file in PEM format, a list of paths to such files, or a TLSCertificate object.
+ :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones. Ignored if ``certFile`` contains TLSCertificate objects.
:param table options: A table with key: value pairs with listen options.
Options:
:param string engineName: The name of the engine to load.
:param string defaultString: The default string to pass to the engine. The exact value depends on the engine but represents the algorithms to register with the engine, as a list of comma-separated keywords. For example "RSA,EC,DSA,DH,PKEY,PKEY_CRYPTO,PKEY_ASN1".
+.. function:: newTLSCertificate(pathToCert[, options])
+
+ .. versionadded:: 1.8.0
+
+ Creates a TLSCertificate object suited to be used with functions like :func:`addDOHLocal` and :func:`addTLSLocal` for TLS certificate configuration
+
+ :param string pathToCert: Path to a file containing the certificate or a PCKS12 file containing both certificate and the key
+ :param table options: A table with key: value pairs with additional options.
+
+ Options:
+
+ * ``key="path/to/key"``: string - Path to a file containing the key corresponding to the certificate.
+ * ``password="pass"``: string - Password protecting the PCKS12 file if appropriate.
+
+ .. code-block:: lua
+
+ newTLSCertificate("path/to/pub.crt", {key="pat/to/private.pem"})
+ newTLSCertificate("path/to/domain.p12", {password="passphrase"}) -- use a password protected PCKS12 file
+
DOHFrontend
~~~~~~~~~~~
.. versionadded:: 1.6.1
- Create and switch to a new TLS context using the same options than were passed to the corresponding `addDOHLocal()` directive, but loading new certificates and keys from the selected files, replacing the existing ones.
+ .. versionchanged:: 1.x.0
+ ``certFile`` now accepts a TLSCertificate object or a list of such objects (see :func:`newTLSCertificate`)
- :param str certFile(s): The path to a X.509 certificate file in PEM format, or a list of paths to such files.
- :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones.
+ :param str certFile(s): The path to a X.509 certificate file in PEM format, a list of paths to such files, or a TLSCertificate object.
+ :param str keyFile(s): The path to the private key file corresponding to the certificate, or a list of paths to such files, whose order should match the certFile(s) ones. Ignored if ``certFile`` contains TLSCertificate objects.
.. method:: DOHFrontend:loadTicketsKeys(ticketsKeysFile)
#include <openssl/ocsp.h>
#include <openssl/rand.h>
#include <openssl/ssl.h>
+#include <openssl/pkcs12.h>
#include <fcntl.h>
#ifdef HAVE_LIBSODIUM
std::vector<int> keyTypes;
/* load certificate and private key */
for (const auto& pair : config.d_certKeyPairs) {
- if (SSL_CTX_use_certificate_chain_file(ctx.get(), pair.first.c_str()) != 1) {
- ERR_print_errors_fp(stderr);
- throw std::runtime_error("An error occurred while trying to load the TLS server certificate file: " + pair.first);
- }
- if (SSL_CTX_use_PrivateKey_file(ctx.get(), pair.second.c_str(), SSL_FILETYPE_PEM) != 1) {
- ERR_print_errors_fp(stderr);
- throw std::runtime_error("An error occurred while trying to load the TLS server private key file: " + pair.second);
+ if (!pair.d_key) {
+ // If no separate key is given, treat it as a pkcs12 file
+ auto fp = std::unique_ptr<FILE, int(*)(FILE*)>(fopen(pair.d_cert.c_str(), "r"), fclose);
+ if (!fp) {
+ throw std::runtime_error("Unable to open file " + pair.d_cert);
+ }
+ auto p12 = std::unique_ptr<PKCS12, void(*)(PKCS12*)>(d2i_PKCS12_fp(fp.get(), nullptr), PKCS12_free);
+ if (!p12) {
+ throw std::runtime_error("Unable to open PKCS12 file " + pair.d_cert);
+ }
+ EVP_PKEY *keyptr = nullptr;
+ X509 *certptr = nullptr;
+ STACK_OF(X509) *captr = nullptr;
+ if (!PKCS12_parse(p12.get(), (pair.d_password ? pair.d_password->c_str() : nullptr), &keyptr, &certptr, &captr))
+ {
+ ERR_print_errors_fp(stderr);
+ throw std::runtime_error("An error occured while parsing PKCS12 file " + pair.d_cert);
+ }
+ auto key = std::unique_ptr<EVP_PKEY, void(*)(EVP_PKEY*)>(keyptr, EVP_PKEY_free);
+ auto cert = std::unique_ptr<X509, void(*)(X509*)>(certptr, X509_free);
+ auto ca = std::unique_ptr<STACK_OF(X509), void(*)(STACK_OF(X509)*)>(captr, sk_X509_free);
+
+ if (SSL_CTX_use_cert_and_key(ctx.get(), cert.get(), key.get(), ca.get(), 1) != 1) {
+ ERR_print_errors_fp(stderr);
+ throw std::runtime_error("An error occurred while trying to load the TLS certificate and key from PKCS12 file " + pair.d_cert);
+ }
+ } else {
+ if (SSL_CTX_use_certificate_chain_file(ctx.get(), pair.d_cert.c_str()) != 1) {
+ ERR_print_errors_fp(stderr);
+ throw std::runtime_error("An error occurred while trying to load the TLS server certificate file: " + pair.d_cert);
+ }
+ if (SSL_CTX_use_PrivateKey_file(ctx.get(), pair.d_key->c_str(), SSL_FILETYPE_PEM) != 1) {
+ ERR_print_errors_fp(stderr);
+ throw std::runtime_error("An error occurred while trying to load the TLS server private key file: " + pair.d_key.value());
+ }
}
if (SSL_CTX_check_private_key(ctx.get()) != 1) {
ERR_print_errors_fp(stderr);
- throw std::runtime_error("The key from '" + pair.second + "' does not match the certificate from '" + pair.first + "'");
+ throw std::runtime_error("The key from '" + pair.d_key.value() + "' does not match the certificate from '" + pair.d_cert + "'");
}
/* store the type of the new key, we might need it later to select the right OCSP stapling response */
auto keyType = libssl_get_last_key_type(ctx);
if (keyType < 0) {
- throw std::runtime_error("The key from '" + pair.second + "' has an unknown type");
+ throw std::runtime_error("The key from '" + pair.d_key.value() + "' has an unknown type");
}
keyTypes.push_back(keyType);
- }
+ }
if (!config.d_ocspFiles.empty()) {
try {
#include <optional>
#include <string>
#include <vector>
+#include <optional>
#include "config.h"
#include "circular_buffer.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(key), d_password(password) {
+ }
+};
+
class TLSConfig
{
public:
- std::vector<std::pair<std::string, std::string>> d_certKeyPairs;
+ std::vector<TLSCertKeyPair> d_certKeyPairs;
std::vector<std::string> d_ocspFiles;
std::string d_ciphers;
creds = nullptr;
for (const auto& pair : fe.d_tlsConfig.d_certKeyPairs) {
- rc = gnutls_certificate_set_x509_key_file(d_creds.get(), pair.first.c_str(), pair.second.c_str(), GNUTLS_X509_FMT_PEM);
+ rc = gnutls_certificate_set_x509_key_file(d_creds.get(), pair.d_cert.c_str(), pair.d_key->c_str(), GNUTLS_X509_FMT_PEM);
if (rc != GNUTLS_E_SUCCESS) {
- throw std::runtime_error("Error loading certificate ('" + pair.first + "') and key ('" + pair.second + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
+ throw std::runtime_error("Error loading certificate ('" + pair.d_cert + "') and key ('" + pair.d_key.value() + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
}
}
for (const auto& file : fe.d_tlsConfig.d_ocspFiles) {
rc = gnutls_certificate_set_ocsp_status_request_file(d_creds.get(), file.c_str(), count);
if (rc != GNUTLS_E_SUCCESS) {
- throw std::runtime_error("Error loading OCSP response from file '" + file + "' for certificate ('" + fe.d_tlsConfig.d_certKeyPairs.at(count).first + "') and key ('" + fe.d_tlsConfig.d_certKeyPairs.at(count).second + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
+ throw std::runtime_error("Error loading OCSP response from file '" + file + "' for certificate ('" + fe.d_tlsConfig.d_certKeyPairs.at(count).d_cert + "') and key ('" + fe.d_tlsConfig.d_certKeyPairs.at(count).d_key.value() + "') for TLS context on " + fe.d_addr.toStringWithPort() + ": " + gnutls_strerror(rc));
}
++count;
}