blob: 765884de9e3ff53f85d436c07655155a4e24f7fd [file] [log] [blame]
/**
* 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.
*/
#include "Fips.h"
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/provider.h>
#include <fstream>
#include <string>
#include "MainHelper.h"
#include "utils/Environment.h"
#include "utils/OptionalUtils.h"
#include "utils/StringUtils.h"
namespace org::apache::nifi::minifi::fips {
namespace {
#ifdef WIN32
constexpr std::string_view FIPS_LIB = "fips.dll";
#elif defined(__APPLE__)
constexpr std::string_view FIPS_LIB = "fips.dylib";
#else
constexpr std::string_view FIPS_LIB = "fips.so";
#endif
bool substituteFipsDirVariable(const std::filesystem::path& file_path, const std::filesystem::path& fips_conf_dir, const std::shared_ptr<core::logging::Logger>& logger) {
std::ifstream input_file(file_path);
if (!input_file) {
logger->log_error("Failed to open file: {}", file_path.string());
return false;
}
std::ostringstream buffer;
buffer << input_file.rdbuf();
std::string content = buffer.str();
input_file.close();
const std::string placeholder = "${FIPS_CONF_DIR}";
size_t pos = content.find(placeholder, 0);
if (pos == std::string::npos) {
return true;
}
auto fips_dir_str = fips_conf_dir.generic_string();
do {
content.replace(pos, placeholder.length(), fips_dir_str);
pos += fips_dir_str.length();
} while ((pos = content.find(placeholder, pos)) != std::string::npos);
std::ofstream output_file(file_path);
if (!output_file) {
logger->log_error("Failed to open file for writing: {}", file_path.string());
return false;
}
output_file << content;
output_file.close();
return true;
}
bool generateFipsModuleConfig(const Locations& locations, const std::shared_ptr<core::logging::Logger>& logger) {
const auto& fips_bin_path = locations.fips_bin_path_;
const auto& fips_conf_path = locations.fips_conf_path_;
std::filesystem::path output_file(fips_conf_path / "fipsmodule.cnf");
logger->log_info("fipsmodule.cnf was not found, trying to run fipsinstall command to generate the file");
#ifdef WIN32
std::string command = fmt::format(R"("{}" fipsinstall -out "{}" -module "{}")", fips_bin_path / "openssl.exe", output_file, fips_bin_path / FIPS_LIB);
#else
std::string command = fmt::format(R"("{}" fipsinstall -out "{}" -module "{}")", fips_bin_path / "openssl", output_file, fips_bin_path / FIPS_LIB);
#endif
if (std::system(command.c_str()) != 0) {
logger->log_error("Failed to generate fipsmodule.cnf file");
return false;
}
logger->log_info("Successfully generated fipsmodule.cnf file");
return true;
}
} // namespace
void initializeFipsMode(const std::shared_ptr<minifi::Configure>& configure, const Locations& locations, const std::shared_ptr<core::logging::Logger>& logger) {
const auto& fips_bin_path = locations.fips_bin_path_;
const auto& fips_conf_path = locations.fips_conf_path_;
if (!(configure->get(minifi::Configure::nifi_openssl_fips_support_enable) | utils::andThen(utils::string::toBool)).value_or(false)) {
logger->log_info("FIPS mode is disabled. FIPS configs and modules will NOT be loaded.");
return;
}
if (!std::filesystem::exists(fips_bin_path / FIPS_LIB)) {
logger->log_error("FIPS mode is enabled, but {} is not available in {} directory", FIPS_LIB, fips_bin_path);
std::exit(1);
}
if (!std::filesystem::exists(fips_conf_path / "fipsmodule.cnf") && !generateFipsModuleConfig(locations, logger)) {
logger->log_error("FIPS mode is enabled, but fipsmodule.cnf is not available in {fips_conf_dir} directory, and minifi couldn't generate it automatically. "
"Run {fips_bin_dir}/openssl fipsinstall -out {fips_conf_dir}/fipsmodule.cnf -module {fips_bin_dir}/{fips_lib_name} command to generate the configuration file",
fmt::arg("fips_conf_dir", fips_conf_path),
fmt::arg("fips_bin_dir", fips_bin_path),
fmt::arg("fips_lib_name", FIPS_LIB));
std::exit(1);
}
if (!std::filesystem::exists(fips_conf_path / "openssl.cnf")) {
logger->log_error("FIPS mode is enabled, but openssl.cnf is not available in {} directory", fips_conf_path);
std::exit(1);
}
if (!substituteFipsDirVariable(fips_conf_path / "openssl.cnf", fips_conf_path, logger)) {
logger->log_error("Failed to replace FIPS_CONF_DIR variable in openssl.cnf");
std::exit(1);
}
utils::Environment::setEnvironmentVariable("OPENSSL_CONF", (fips_conf_path / "openssl.cnf").string().c_str(), true);
if (!OSSL_PROVIDER_set_default_search_path(nullptr, (fips_bin_path.string()).c_str())) {
logger->log_error("Failed to set FIPS module path: {}", fips_bin_path.string());
ERR_print_errors_fp(stderr);
std::exit(1);
}
if (OSSL_PROVIDER_available(nullptr, "fips") != 1) {
logger->log_error("FIPS provider not available in default search path");
ERR_print_errors_fp(stderr);
std::exit(1);
}
if (!EVP_default_properties_enable_fips(nullptr, 1)) {
logger->log_error("Failed to enable FIPS mode");
ERR_print_errors_fp(stderr);
std::exit(1);
}
if (!EVP_default_properties_is_fips_enabled(nullptr)) {
logger->log_error("FIPS mode is not enabled");
ERR_print_errors_fp(stderr);
std::exit(1);
}
logger->log_info("FIPS mode enabled in MiNiFi C++");
}
} // namespace org::apache::nifi::minifi::fips