libminifi/include/controllers/SSLContextService.h (229 lines of code) (raw):

/** * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN 1 #endif #ifdef WIN32 #include <windows.h> #include <wincrypt.h> #endif #include <openssl/err.h> #include <openssl/ssl.h> #include <openssl/bio.h> #include <openssl/pkcs12.h> #include <iostream> #include <memory> #include <string> #include <utility> #include "utils/StringUtils.h" #include "utils/tls/ExtendedKeyUsage.h" #include "io/validation.h" #include "core/controller/ControllerService.h" #include "core/logging/LoggerFactory.h" #include "core/PropertyDefinition.h" #include "core/PropertyDefinitionBuilder.h" #include "minifi-cpp/core/PropertyValidator.h" #include "utils/ConfigurationUtils.h" #include "utils/Export.h" #include "utils/tls/CertificateUtils.h" #include "minifi-cpp/controllers/SSLContextService.h" namespace org::apache::nifi::minifi::controllers { class SSLContext { public: SSLContext(SSL_CTX *context) // NOLINT : context_(context) { } ~SSLContext() { if (context_) { SSL_CTX_free(context_); } } protected: SSL_CTX *context_; }; /** * SSLContextService provides a configurable controller service from * which we can provide an SSL Context or component parts that go * into creating one. * * Justification: Abstracts SSL support out of processors into a * configurable controller service. */ class SSLContextServiceImpl : public core::controller::ControllerServiceImpl, public SSLContextService { public: explicit SSLContextServiceImpl(std::string_view name, const utils::Identifier &uuid = {}) : ControllerServiceImpl(name, uuid), initialized_(false), logger_(core::logging::LoggerFactory<SSLContextService>::getLogger(uuid_)) { } explicit SSLContextServiceImpl(std::string_view name, const std::shared_ptr<Configure> &configuration) : ControllerServiceImpl(name), initialized_(false), logger_(core::logging::LoggerFactory<SSLContextService>::getLogger(uuid_)) { ControllerServiceImpl::setConfiguration(configuration); SSLContextServiceImpl::initialize(); // set the properties based on the configuration std::string value; if (configuration_->get(Configure::nifi_security_client_certificate, value)) { ControllerServiceImpl::setProperty(ClientCertificate.name, value); } if (configuration_->get(Configure::nifi_security_client_private_key, value)) { ControllerServiceImpl::setProperty(PrivateKey.name, value); } if (configuration_->get(Configure::nifi_security_client_pass_phrase, value)) { ControllerServiceImpl::setProperty(Passphrase.name, value); } if (configuration_->get(Configure::nifi_security_client_ca_certificate, value)) { ControllerServiceImpl::setProperty(CACertificate.name, value); } if (configuration_->get(Configure::nifi_security_use_system_cert_store, value)) { ControllerServiceImpl::setProperty(UseSystemCertStore.name, value); } #ifdef WIN32 if (configuration_->get(Configure::nifi_security_windows_cert_store_location, value)) { ControllerServiceImpl::setProperty(CertStoreLocation.name, value); } if (configuration_->get(Configure::nifi_security_windows_server_cert_store, value)) { ControllerServiceImpl::setProperty(ServerCertStore.name, value); } if (configuration_->get(Configure::nifi_security_windows_client_cert_store, value)) { ControllerServiceImpl::setProperty(ClientCertStore.name, value); } if (configuration_->get(Configure::nifi_security_windows_client_cert_cn, value)) { ControllerServiceImpl::setProperty(ClientCertCN.name, value); } if (configuration_->get(Configure::nifi_security_windows_client_cert_key_usage, value)) { ControllerServiceImpl::setProperty(ClientCertKeyUsage.name, value); } #endif // WIN32 } void initialize() override; std::unique_ptr<SSLContext> createSSLContext(); const std::filesystem::path& getCertificateFile() const override; const std::string& getPassphrase() const override; const std::filesystem::path& getPrivateKeyFile() const override; const std::filesystem::path& getCACertificate() const override; void yield() override { } bool isRunning() const override { return getState() == core::controller::ControllerServiceState::ENABLED; } bool isWorkAvailable() override { return false; } void setMinTlsVersion(long min_version) override { // NOLINT(runtime/int) long due to SSL lib API minimum_tls_version_ = min_version; } void setMaxTlsVersion(long max_version) override { // NOLINT(runtime/int) long due to SSL lib API maximum_tls_version_ = max_version; } bool configure_ssl_context(void* ctx) override; void onEnable() override; MINIFIAPI static constexpr const char* Description = "Controller service that provides SSL/TLS capabilities to consuming interfaces"; #ifdef WIN32 MINIFIAPI static constexpr auto CertStoreLocation = core::PropertyDefinitionBuilder<utils::tls::WindowsCertStoreLocation::SIZE>::createProperty("Certificate Store Location") .withDescription("One of the Windows certificate store locations, eg. LocalMachine or CurrentUser (Windows only)") .withAllowedValues(utils::tls::WindowsCertStoreLocation::LOCATION_NAMES) .isRequired(false) .withDefaultValue(utils::tls::WindowsCertStoreLocation::DEFAULT_LOCATION) .build(); MINIFIAPI static constexpr auto ServerCertStore = core::PropertyDefinitionBuilder<>::createProperty("Server Cert Store") .withDescription("The name of the certificate store which contains the server certificate (Windows only)") .isRequired(false) .withDefaultValue("ROOT") .build(); MINIFIAPI static constexpr auto ClientCertStore = core::PropertyDefinitionBuilder<>::createProperty("Client Cert Store") .withDescription("The name of the certificate store which contains the client certificate (Windows only)") .isRequired(false) .withDefaultValue("MY") .build(); MINIFIAPI static constexpr auto ClientCertCN = core::PropertyDefinitionBuilder<>::createProperty("Client Cert CN") .withDescription("The CN that the client certificate is required to match; default: use the first available client certificate in the store (Windows only)") .isRequired(false) .build(); MINIFIAPI static constexpr auto ClientCertKeyUsage = core::PropertyDefinitionBuilder<>::createProperty("Client Cert Key Usage") .withDescription("Comma-separated list of enhanced key usage values that the client certificate is required to have (Windows only)") .isRequired(false) .withDefaultValue("Client Authentication") .build(); #endif // WIN32 MINIFIAPI static constexpr auto ClientCertificate = core::PropertyDefinitionBuilder<>::createProperty("Client Certificate") .withDescription("Client Certificate") .isRequired(false) .build(); MINIFIAPI static constexpr auto PrivateKey = core::PropertyDefinitionBuilder<>::createProperty("Private Key") .withDescription("Private Key file") .isRequired(false) .build(); MINIFIAPI static constexpr auto Passphrase = core::PropertyDefinitionBuilder<>::createProperty("Passphrase") .withDescription("Client passphrase. Either a file or unencrypted text") .isRequired(false) .isSensitive(true) .build(); MINIFIAPI static constexpr auto CACertificate = core::PropertyDefinitionBuilder<>::createProperty("CA Certificate") .withDescription("CA certificate file") .isRequired(false) .build(); MINIFIAPI static constexpr auto UseSystemCertStore = core::PropertyDefinitionBuilder<>::createProperty("Use System Cert Store") .withDescription("Whether to use the certificates in the OS's certificate store") .isRequired(false) .withValidator(core::StandardPropertyValidators::BOOLEAN_VALIDATOR) .withDefaultValue("false") .build(); MINIFIAPI static constexpr auto Properties = #ifdef WIN32 std::to_array<core::PropertyReference>({ CertStoreLocation, ServerCertStore, ClientCertStore, ClientCertCN, ClientCertKeyUsage, #else std::to_array<core::PropertyReference>({ #endif // WIN32 ClientCertificate, PrivateKey, Passphrase, CACertificate, UseSystemCertStore }); MINIFIAPI static constexpr bool SupportsDynamicProperties = false; ADD_COMMON_VIRTUAL_FUNCTIONS_FOR_CONTROLLER_SERVICES protected: virtual void initializeProperties(); mutable std::mutex initialization_mutex_; bool initialized_; std::filesystem::path certificate_; std::filesystem::path private_key_; std::string passphrase_; std::filesystem::path ca_certificate_; bool use_system_cert_store_ = false; #ifdef WIN32 std::string cert_store_location_; std::string server_cert_store_; std::string client_cert_store_; std::string client_cert_cn_; utils::tls::ExtendedKeyUsage client_cert_key_usage_; #endif // WIN32 static std::string getLatestOpenSSLErrorString() { const auto err = ERR_peek_last_error(); if (err == 0) { return ""; } static_assert(utils::configuration::DEFAULT_BUFFER_SIZE >= 1); std::array<char, utils::configuration::DEFAULT_BUFFER_SIZE> buffer{}; ERR_error_string_n(err, buffer.data(), buffer.size() - 1); return {buffer.data()}; } static bool isFileTypeP12(const std::filesystem::path& filename) { return utils::string::endsWith(filename.string(), "p12", false); } private: bool addP12CertificateToSSLContext(SSL_CTX* ctx) const; bool addPemCertificateToSSLContext(SSL_CTX* ctx) const; bool addClientCertificateFromSystemStoreToSSLContext(SSL_CTX* ctx) const; bool addServerCertificatesFromSystemStoreToSSLContext(SSL_CTX* ctx) const; #ifdef WIN32 using ClientCertCallback = std::function<bool(utils::tls::X509_unique_ptr cert, utils::tls::EVP_PKEY_unique_ptr priv_key)>; using ServerCertCallback = std::function<bool(utils::tls::X509_unique_ptr cert)>; bool findClientCertificate(ClientCertCallback cb) const; bool findServerCertificate(ServerCertCallback cb) const; bool useClientCertificate(PCCERT_CONTEXT certificate, ClientCertCallback cb) const; bool useServerCertificate(PCCERT_CONTEXT certificate, ServerCertCallback cb) const; #endif // WIN32 long minimum_tls_version_ = -1; // NOLINT(runtime/int) long due to SSL lib API long maximum_tls_version_ = -1; // NOLINT(runtime/int) long due to SSL lib API void verifyCertificateExpiration(); std::shared_ptr<core::logging::Logger> logger_; }; // NOLINT the linter gets confused by the '{'s inside #ifdef's } // namespace org::apache::nifi::minifi::controllers