blob: 17d52d039fe5c025881c2b3e30a17f121224d9f0 [file]
#include "tlbmc/credentials/credential_manager_impl.h"
#include <filesystem> // NOLINT
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <system_error> // NOLINT
#include <thread> // NOLINT
#include <utility>
#include "owner_certificate/owner_verification_cert_configuration.pb.h"
#include "absl/functional/any_invocable.h"
#include "absl/log/log.h"
#include "absl/memory/memory.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/str_format.h"
#include "absl/strings/substitute.h"
#include "absl/synchronization/mutex.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "zatar/x509_certificate.h"
#include "g3/macros.h"
#include "tlbmc/credentials/credential_manager.h"
#include "tlbmc/redfish/routes/action_managers/file_manager.h"
#include "resource.pb.h"
#include "tlbmc/resource/syslog_config_utils.h"
#include "tlbmc/utils/shell_command_executor.h"
#include "zatar/cert_error_handling.h"
#include "zatar/generate_cert.h"
#include "openssl/asn1.h"
#include "zatar/g3_misc.h"
#include "openssl/bio.h"
#include "openssl/bn.h"
#include "openssl/buffer.h"
#include "openssl/evp.h"
#include "openssl/pem.h"
#include "openssl/rsa.h"
#include "openssl/stack.h"
#include "openssl/x509.h"
#include "google/protobuf/text_format.h"
#include "re2/re2.h"
namespace milotic_tlbmc {
using ::milotic::authn::GeneratePrivateKey;
using ::milotic::authn::GetSslErrorOnQueue;
using ::milotic::authn::ReturnErrorIfNotSuccess;
using ::milotic::authn::ReturnErrorIfNull;
using ::owner_certificate::OwnerVerificationCertConfiguration;
using ::owner_certificate::SerialConsoleAccessLevel;
using ::google::protobuf::TextFormat;
namespace {
absl::Status AddNameEntry(X509_NAME* name, std::string_view entry,
std::string_view value) {
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
X509_NAME_add_entry_by_txt(
name, std::string(entry).c_str(), MBSTRING_ASC,
reinterpret_cast<const unsigned char*>(value.data()), value.size(),
/*loc=*/-1,
/*set=*/0),
absl::StrCat("Failed to add name entry: ", value, "",
GetSslErrorOnQueue())));
return absl::OkStatus();
}
std::string GetSyslogOverrideContent(const SyslogTargetConfig& config) {
std::string protocol_str = "tcp";
auto protocol_yocto = GetYoctoStringFromSyslogProtocol(config.protocol());
if (protocol_yocto.ok()) {
protocol_str = *protocol_yocto;
} else {
LOG(ERROR) << "Failed to convert protocol enum to Yocto string: "
<< protocol_yocto.status();
}
std::string tls_mode_str = "tls";
auto tls_mode_yocto = GetYoctoStringFromSyslogTlsMode(config.tls_mode());
if (tls_mode_yocto.ok()) {
tls_mode_str = *tls_mode_yocto;
} else {
LOG(ERROR) << "Failed to convert TLS mode enum to Yocto string: "
<< tls_mode_yocto.status();
}
return absl::StrFormat(
"[Service]\n"
"Environment=PROD_SYSLOG_IP=\"%s\"\n"
"Environment=PROD_SYSLOG_PORT=\"%d\"\n"
"Environment=PROD_SYSLOG_PROTO=\"%s\"\n"
"Environment=PROD_SYSLOG_TLS_MODE=\"%s\"\n"
"TimeoutStopSec=5\n",
config.target_ip(), static_cast<int>(config.target_port()), protocol_str,
tls_mode_str);
}
} // namespace
absl::StatusOr<std::unique_ptr<CredentialManagerImpl>>
CredentialManagerImpl::Create(const CredentialManagerParams& params) {
// Check that private key are non empty strings and are files in the same
// folder
if (params.private_key_path.empty()) {
return absl::InvalidArgumentError("Private key path is empty");
}
if (params.cert_path.empty()) {
return absl::InvalidArgumentError("Cert path is empty");
}
// Check that the private key and cert are in the same folder
std::filesystem::path private_key_path_fs(params.private_key_path);
std::filesystem::path cert_path_fs(params.cert_path);
if (private_key_path_fs.parent_path() != cert_path_fs.parent_path()) {
return absl::InvalidArgumentError(
"Private key and cert are not in the same folder");
}
if (params.owner_verification_cert_path.empty()) {
return absl::InvalidArgumentError("Owner verification cert path is empty");
}
if (params.owner_verification_config_path.empty()) {
return absl::InvalidArgumentError(
"Owner verification config path is empty");
}
if (params.bmc_ssh_trusted_user_ca_keys_path.empty()) {
return absl::InvalidArgumentError(
"BMC SSH trusted user CA keys path is empty");
}
if (params.trust_bundle_path.empty()) {
return absl::InvalidArgumentError("Trust bundle path is empty");
}
if (params.trust_bundle_signature_path.empty()) {
return absl::InvalidArgumentError("Trust bundle signature path is empty");
}
if (params.os_verification_cert_path.empty()) {
return absl::InvalidArgumentError("OS verification cert path is empty");
}
if (params.os_verification_key_path.empty()) {
return absl::InvalidArgumentError("OS verification key path is empty");
}
if (params.serial_console_trusted_user_ca_keys_path.empty()) {
return absl::InvalidArgumentError(
"Serial console trusted user CA keys path is empty");
}
if (params.serial_console_detatched_signature_path.empty()) {
return absl::InvalidArgumentError(
"Serial console detatched signature path is empty");
}
if (params.serial_console_access_level_dir.empty()) {
return absl::InvalidArgumentError(
"Serial console access level path is empty");
}
if (params.syslog_client_conf_override_path.empty()) {
return absl::InvalidArgumentError(
"Syslog client conf override path is empty");
}
if (params.syslog_root_cert_path.empty()) {
return absl::InvalidArgumentError("Syslog root cert path is empty");
}
return absl::WrapUnique(new CredentialManagerImpl(params));
}
void CredentialManagerImpl::ParseOwnerVerificationCert() {
// If the write path already exists, then we should read from there.
absl::StatusOr<std::string> owner_verification_cert =
FileManager::ReadFile(params_.owner_verification_cert_path);
if (!owner_verification_cert.ok()) {
LOG(ERROR) << "Failed to read owner verification certificate: "
<< owner_verification_cert.status()
<< ". All owner verification certificate validation will fail "
"as we will use an empty certificate.";
return;
}
LOG(WARNING) << "Successfully read the owner verification certificate.";
owner_verification_cert_ = std::move(*owner_verification_cert);
}
void CredentialManagerImpl::ParseOwnerVerificationConfiguration() {
// Reading the Owner Verification Config file
absl::StatusOr<std::string> owner_verification_config =
FileManager::ReadFile(params_.owner_verification_config_path);
if (!owner_verification_config.ok()) {
LOG(ERROR) << "Failed to read owner verification config: "
<< owner_verification_config.status()
<< ". Using default config which allows firmware updates.";
return;
}
// Parse the owner verification config file.
OwnerVerificationCertConfiguration owner_verification_cert_configuration;
TextFormat::Parser parser;
parser.AllowUnknownField(true);
if (!parser.ParseFromString(*owner_verification_config,
&owner_verification_cert_configuration)) {
LOG(ERROR) << "Failed to parse owner verification config. Using default "
"config which allows firmware updates.";
return;
}
owner_verification_cert_configuration_ =
std::move(owner_verification_cert_configuration);
SerialConsoleAccessLevel serial_console_access_level =
owner_verification_cert_configuration_->serial_console_access_level();
absl::Status status;
switch (serial_console_access_level) {
case SerialConsoleAccessLevel::ACCESS_FULL:
break;
case SerialConsoleAccessLevel::ACCESS_DISABLE:
status = FileManager::WriteToFile(
"", absl::StrCat(params_.serial_console_access_level_dir,
"/serial_console_disabled"));
if (!status.ok()) {
LOG(ERROR) << "Failed to write to serial console disabled file: "
<< status;
}
break;
case SerialConsoleAccessLevel::ACCESS_READONLY:
status = FileManager::WriteToFile(
"", absl::StrCat(params_.serial_console_access_level_dir,
"/serial_console_readonly"));
if (!status.ok()) {
LOG(ERROR) << "Failed to write to serial console readonly file: "
<< status;
}
break;
default:
LOG(ERROR) << "Serial console access level is unknown.";
break;
}
}
void CredentialManagerImpl::ParseBmcSshTrustedUserCaKeys() {
absl::StatusOr<std::string> bmc_ssh_trusted_user_ca_keys_signature =
FileManager::ReadFile(params_.bmc_ssh_trusted_user_ca_keys_path);
if (!bmc_ssh_trusted_user_ca_keys_signature.ok()) {
LOG(ERROR) << "Failed to read BMC SSH trusted user CA keys signature: "
<< bmc_ssh_trusted_user_ca_keys_signature.status()
<< ". Assuming no BMC SSH trusted user CA keys signature.";
return;
}
bmc_ssh_trusted_user_ca_keys_signature_ =
std::move(*bmc_ssh_trusted_user_ca_keys_signature);
}
void CredentialManagerImpl::ParseTrustBundleFiles() {
absl::StatusOr<std::string> trust_bundle =
FileManager::ReadFile(params_.trust_bundle_path);
if (!trust_bundle.ok()) {
LOG(ERROR) << "Failed to read trust bundle: " << trust_bundle.status()
<< ". Assuming no trust bundle.";
return;
}
trust_bundle_ = std::move(*trust_bundle);
absl::StatusOr<std::string> trust_bundle_signature =
FileManager::ReadFile(params_.trust_bundle_signature_path);
if (!trust_bundle_signature.ok()) {
LOG(ERROR) << "Failed to read trust bundle signature: "
<< trust_bundle_signature.status()
<< ". Assuming no trust bundle signature.";
return;
}
trust_bundle_signature_ = std::move(*trust_bundle_signature);
}
void CredentialManagerImpl::ParseServerCert() {
absl::StatusOr<std::string> server_cert =
FileManager::ReadFile(params_.cert_path);
if (!server_cert.ok()) {
LOG(ERROR) << "Failed to read server certificate: " << server_cert.status()
<< ". Assuming no server certificate.";
return;
}
server_cert_ = std::move(*server_cert);
}
void CredentialManagerImpl::ParseOsVerificationCert() {
absl::StatusOr<std::string> os_verification_cert =
FileManager::ReadFile(params_.os_verification_cert_path);
if (!os_verification_cert.ok()) {
LOG(ERROR) << "Failed to read OS verification certificate: "
<< os_verification_cert.status()
<< ". Assuming no OS verification certificate.";
return;
}
os_verification_cert_ = std::move(*os_verification_cert);
}
void CredentialManagerImpl::ParseSerialConsoleTrustedUserCAKeys() {
absl::StatusOr<std::string> serial_console_trusted_user_ca_keys =
FileManager::ReadFile(params_.serial_console_trusted_user_ca_keys_path);
if (!serial_console_trusted_user_ca_keys.ok()) {
LOG(ERROR) << "Failed to read serial console trusted user CA keys: "
<< serial_console_trusted_user_ca_keys.status()
<< ". Assuming no serial console trusted user CA keys.";
} else {
serial_console_trusted_user_ca_keys_ =
std::move(*serial_console_trusted_user_ca_keys);
}
absl::StatusOr<std::string> serial_console_detatched_signature =
FileManager::ReadFile(params_.serial_console_detatched_signature_path);
if (!serial_console_detatched_signature.ok()) {
LOG(ERROR) << "Failed to read serial console detatched signature: "
<< serial_console_detatched_signature.status()
<< ". Assuming no serial console detatched signature.";
return;
}
serial_console_detatched_signature_ =
std::move(*serial_console_detatched_signature);
}
void CredentialManagerImpl::ParseSyslogTargetConfig() {
absl::StatusOr<std::string> syslog_client_conf_override =
FileManager::ReadFile(params_.syslog_client_conf_override_path);
if (!syslog_client_conf_override.ok()) {
LOG(ERROR) << "Failed to read syslog client conf override: "
<< syslog_client_conf_override.status()
<< ". Assuming no syslog client conf override.";
return;
}
std::string target_ip;
int64_t target_port;
SyslogProtocol protocol = SYSLOG_PROTOCOL_TCP;
SyslogTlsMode tls_mode = SYSLOG_TLS_MODE_TLS;
if (RE2::PartialMatch(*syslog_client_conf_override,
"Environment=PROD_SYSLOG_IP=\"([^\"]+)\"",
&target_ip) &&
RE2::PartialMatch(*syslog_client_conf_override,
"Environment=PROD_SYSLOG_PORT=\"(\\d+)\"",
&target_port)) {
std::string proto_str;
if (RE2::PartialMatch(*syslog_client_conf_override,
"Environment=PROD_SYSLOG_PROTO=\"([^\"]+)\"",
&proto_str)) {
auto proto_enum = GetSyslogProtocolFromYoctoString(proto_str);
if (proto_enum.ok()) {
protocol = *proto_enum;
} else {
LOG(ERROR) << "Failed to parse protocol from Yocto config: "
<< proto_enum.status();
}
}
std::string tls_mode_str;
if (RE2::PartialMatch(*syslog_client_conf_override,
"Environment=PROD_SYSLOG_TLS_MODE=\"([^\"]+)\"",
&tls_mode_str)) {
auto tls_mode_enum = GetSyslogTlsModeFromYoctoString(tls_mode_str);
if (tls_mode_enum.ok()) {
tls_mode = *tls_mode_enum;
} else {
LOG(ERROR) << "Failed to parse TLS mode from Yocto config: "
<< tls_mode_enum.status();
}
}
SyslogTargetConfig config;
config.set_target_ip(target_ip);
config.set_target_port(target_port);
config.set_protocol(protocol);
config.set_tls_mode(tls_mode);
syslog_cert_config_ = std::move(config);
}
}
void CredentialManagerImpl::ParseSyslogRootCert() {
absl::StatusOr<std::string> syslog_root_cert =
FileManager::ReadFile(params_.syslog_root_cert_path);
if (!syslog_root_cert.ok()) {
LOG(ERROR) << "Failed to read syslog root cert: "
<< syslog_root_cert.status()
<< ". Assuming no syslog root cert.";
return;
}
syslog_root_cert_ = std::move(*syslog_root_cert);
}
CredentialManagerImpl::CredentialManagerImpl(
const CredentialManagerParams& params)
: params_(params), command_executor_(std::make_unique<ShellExecutor>()) {
ParseOwnerVerificationCert();
ParseOwnerVerificationConfiguration();
ParseBmcSshTrustedUserCaKeys();
ParseTrustBundleFiles();
ParseServerCert();
ParseOsVerificationCert();
ParseSerialConsoleTrustedUserCAKeys();
ParseSyslogTargetConfig();
ParseSyslogRootCert();
}
namespace {
absl::StatusOr<bssl::UniquePtr<X509_REQ>> CreateX509Req(
const CredentialManagerImpl::CsrParams& csr_params) {
bssl::UniquePtr<X509_REQ> x509(X509_REQ_new());
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNull(
x509.get(), absl::StrCat("X509_REQ_new failed: ", GetSslErrorOnQueue())));
X509_NAME* subject = X509_REQ_get_subject_name(x509.get());
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNull(
subject, absl::StrCat("Failed to get subject name from X509_REQ: ",
GetSslErrorOnQueue())));
ECCLESIA_RETURN_IF_ERROR(AddNameEntry(subject, "C", csr_params.country));
ECCLESIA_RETURN_IF_ERROR(AddNameEntry(subject, "ST", csr_params.state));
ECCLESIA_RETURN_IF_ERROR(AddNameEntry(subject, "L", csr_params.city));
ECCLESIA_RETURN_IF_ERROR(AddNameEntry(subject, "O", csr_params.organization));
ECCLESIA_RETURN_IF_ERROR(
AddNameEntry(subject, "OU", csr_params.organizational_unit));
ECCLESIA_RETURN_IF_ERROR(AddNameEntry(subject, "CN", csr_params.common_name));
if (!csr_params.alternative_names.empty()) {
std::string san_extension;
for (int i = 0; i < csr_params.alternative_names.size(); ++i) {
std::string_view san = csr_params.alternative_names[i];
absl::StrAppend(&san_extension, "DNS:", san);
if (i < csr_params.alternative_names.size() - 1) {
absl::StrAppend(&san_extension, ",");
}
}
bssl::UniquePtr<STACK_OF(X509_EXTENSION)> extensions(
sk_X509_EXTENSION_new_null());
bssl::UniquePtr<X509_EXTENSION> san_ext(X509V3_EXT_conf_nid(
nullptr, nullptr, NID_subject_alt_name, san_extension.c_str()));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNull(
san_ext.get(), absl::StrCat("Failed to create SAN extension: ",
GetSslErrorOnQueue())));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
sk_X509_EXTENSION_push(extensions.get(), san_ext.release()),
absl::StrCat("Failed to add SAN extension to CSR: ",
GetSslErrorOnQueue())));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
X509_REQ_add_extensions(x509.get(), extensions.get()),
absl::StrCat("Failed to add extensions to CSR: ",
GetSslErrorOnQueue())));
}
return x509;
}
absl::StatusOr<std::string> GenerateCsrWithPrivateKey(
const CredentialManagerImpl::CsrParams& csr_params, EVP_PKEY* private_key) {
ECCLESIA_ASSIGN_OR_RETURN(bssl::UniquePtr<X509_REQ> x509,
CreateX509Req(csr_params));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
X509_REQ_set_pubkey(x509.get(), private_key),
absl::StrCat("Failed to set public key in CSR: ", GetSslErrorOnQueue())));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
X509_REQ_sign(x509.get(), private_key, EVP_sha256()),
absl::StrCat("Failed to sign CSR: ", GetSslErrorOnQueue())));
bssl::UniquePtr<BIO> bio_csr(BIO_new(BIO_s_mem()));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
PEM_write_bio_X509_REQ(bio_csr.get(), x509.get()),
absl::StrCat("Failed to write CSR to BIO: ", GetSslErrorOnQueue())));
char* csr = nullptr;
auto csr_len = BIO_get_mem_data(bio_csr.get(), &csr);
return std::string(csr, csr_len);
}
} // namespace
absl::StatusOr<std::string> CredentialManagerImpl::GenerateCsr(
const CsrParams& csr_params) {
ECCLESIA_ASSIGN_OR_RETURN(bssl::UniquePtr<EVP_PKEY> pkey,
GeneratePrivateKey());
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNull(
pkey.get(),
absl::StrCat("Failed to generate private key: ", GetSslErrorOnQueue())));
ECCLESIA_ASSIGN_OR_RETURN(std::string csr,
GenerateCsrWithPrivateKey(csr_params, pkey.get()));
{
absl::MutexLock lock(mutex_);
private_key_ = std::move(pkey);
}
return csr;
}
absl::Status CredentialManagerImpl::InstallServerCert(
std::string_view certificate) {
absl::MutexLock lock(mutex_);
// If GenerateCSR has not been called, we cannot install the cert.
if (private_key_ == nullptr) {
return absl::InternalError(
"GenerateCSR must be called before InstallServerCert");
}
// 1. Load the certificate from the PEM string
bssl::UniquePtr<BIO> bio_cert(BIO_new_mem_buf(
certificate.data(), static_cast<uint32_t>(certificate.length())));
bssl::UniquePtr<X509> cert(
PEM_read_bio_X509(bio_cert.get(), nullptr, nullptr, nullptr));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNull(
cert.get(),
absl::StrCat("Failed to parse certificate PEM: ", GetSslErrorOnQueue())));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
X509_check_private_key(cert.get(), private_key_.get()),
"Certificate does not match the private key"));
bssl::UniquePtr<BIO> bio_privkey(BIO_new(BIO_s_mem()));
ECCLESIA_RETURN_IF_ERROR(ReturnErrorIfNotSuccess(
PEM_write_bio_PrivateKey(bio_privkey.get(), private_key_.get(), nullptr,
nullptr, 0, nullptr, nullptr),
absl::StrCat("PEM_write_bio_PrivateKey failed: ", GetSslErrorOnQueue())));
char* private_key = nullptr;
auto private_key_len = BIO_get_mem_data(bio_privkey.get(), &private_key);
std::string private_key_pem(private_key, private_key_len);
// Install the cert and key on the machine.
// We must do directory name change here as the key and cert have to both be
// changed atomically
std::string credential_dir =
std::filesystem::path(params_.cert_path).parent_path();
std::string cert_filename =
std::filesystem::path(params_.cert_path).filename();
std::string private_key_filename =
std::filesystem::path(params_.private_key_path).filename();
// Write to a temp dir
std::string temp_credential_dir = absl::StrCat(credential_dir, "-tmp");
if (params_.cert_path == params_.private_key_path) {
// After RestartBmcwebInASeparateThread is called below that bmcweb instance
// will attempt to read the cert and key. Since the filepaths are the same,
// we must write both the cert and key to the same file.
ECCLESIA_RETURN_IF_ERROR(FileManager::WriteToFile(
absl::StrCat(certificate, "\n", private_key_pem),
absl::StrCat(temp_credential_dir, "/", cert_filename)));
} else {
ECCLESIA_RETURN_IF_ERROR(FileManager::WriteToFile(
certificate, absl::StrCat(temp_credential_dir, "/", cert_filename)));
ECCLESIA_RETURN_IF_ERROR(FileManager::WriteToFile(
private_key_pem,
absl::StrCat(temp_credential_dir, "/", private_key_filename)));
}
// Atomically change both key and cert
ECCLESIA_RETURN_IF_ERROR(
FileManager::RenamePath(temp_credential_dir, credential_dir, true));
RestartBmcwebInASeparateThread();
return absl::OkStatus();
}
void CredentialManagerImpl::RestartBmcwebInASeparateThread() {
std::thread([this]() {
absl::SleepFor(absl::Seconds(kServerRestartDelaySeconds));
absl::StatusOr<std::string> status =
command_executor_->Execute(std::string(kRestartBmcWebCommand), false);
if (!status.ok()) {
LOG(ERROR) << "Failed to restart bmcweb: " << status.status();
}
if (restart_bmcweb_callback_ != nullptr) {
restart_bmcweb_callback_(status.status());
}
}).detach();
}
absl::Status CredentialManagerImpl::InstallTrustBundle(
std::string_view trust_bundle, std::string_view signature) {
absl::MutexLock lock(mutex_);
if (last_install_time_ != absl::InfinitePast() &&
absl::Now() - last_install_time_ <
absl::Seconds(kTrustBundleRetryDelaySeconds)) {
std::string error_message = absl::StrFormat(
"A trust bundle, installed at %s, is pending to be applied at %s. "
"Retry after %s.",
absl::FormatTime(last_install_time_),
absl::FormatTime(last_install_time_ +
absl::Seconds(kServerRestartDelaySeconds)),
absl::FormatTime(last_install_time_ +
absl::Seconds(kTrustBundleRetryDelaySeconds)));
LOG(ERROR) << error_message;
return absl::UnavailableError(error_message);
}
ECCLESIA_ASSIGN_OR_RETURN(StagedTempFile staged_trust_bundle,
VerifyAndStageTrustBundle(trust_bundle, signature));
last_install_time_ = absl::Now();
ECCLESIA_RETURN_IF_ERROR(
FileManager::WriteToFile(trust_bundle, params_.trust_bundle_path));
ECCLESIA_RETURN_IF_ERROR(
FileManager::WriteToFile(signature, params_.trust_bundle_signature_path));
trust_bundle_ = std::string(trust_bundle);
trust_bundle_signature_ = std::string(signature);
RestartBmcwebInASeparateThread();
return absl::OkStatus();
}
absl::StatusOr<StagedTempFile> CredentialManagerImpl::VerifyAndStageTrustBundle(
std::string_view trust_bundle, std::string_view signature) {
ECCLESIA_ASSIGN_OR_RETURN(
StagedTempFile trust_bundle_file,
StagedTempFile::Create(trust_bundle, kTempTrustBundlePath));
absl::Status status =
VerifyAndStageCertificate(trust_bundle_file.path(), signature);
LOG(WARNING) << "Trust bundle verify result: " << status.message();
if (!status.ok()) {
return absl::Status(
status.code(), absl::StrCat("Failed to verify trust bundle signature. ",
status.message()));
}
return trust_bundle_file;
}
absl::Status CredentialManagerImpl::VerifyAndStageCertificate(
std::string_view cert_path, std::string_view signature) {
std::error_code ec;
if (!std::filesystem::exists(params_.owner_verification_cert_path, ec)) {
return absl::FailedPreconditionError(absl::Substitute(
"Owner certificate file does not exist or can't be accessed: $0",
ec.message()));
}
ECCLESIA_ASSIGN_OR_RETURN(
StagedTempFile signature_file,
StagedTempFile::Create(signature, kTempSignaturePath));
std::string command =
absl::StrFormat(kTrustBundleVerifyWithCaCommand, signature_file.path(),
cert_path, params_.owner_verification_cert_path);
return command_executor_->Execute(command, false).status();
}
absl::Status CredentialManagerImpl::InstallOsVerificationCertificate(
std::string_view cert_string) {
// Verify the OS verification certificate
ECCLESIA_RETURN_IF_ERROR(VerifyOsVerificationCertificate(cert_string));
// Parse the public key
ECCLESIA_ASSIGN_OR_RETURN(std::string public_key,
ecclesia::GetPublicKey(cert_string));
// Write the cert itself to a file
ECCLESIA_RETURN_IF_ERROR(
FileManager::WriteToFile(cert_string, params_.os_verification_cert_path));
// Write the public key to a file
ECCLESIA_RETURN_IF_ERROR(
FileManager::WriteToFile(public_key, params_.os_verification_key_path));
os_verification_cert_ = std::string(cert_string);
return absl::OkStatus();
}
absl::Status CredentialManagerImpl::VerifyOsVerificationCertificate(
std::string_view cert_string) {
absl::MutexLock lock(mutex_);
std::error_code ec;
if (!std::filesystem::exists(params_.owner_verification_cert_path, ec)) {
return absl::FailedPreconditionError(absl::Substitute(
"Owner certificate file does not exist or can't be accessed: $0",
ec.message()));
}
ECCLESIA_ASSIGN_OR_RETURN(
StagedTempFile os_cert_file,
StagedTempFile::Create(cert_string, kTempOsCertPath));
std::string command = absl::StrFormat(kOsCertVerifyCommand,
params_.owner_verification_cert_path,
os_cert_file.path());
absl::Status status = command_executor_->Execute(command, false).status();
if (!status.ok()) {
return absl::Status(
status.code(),
absl::StrCat("Failed to verify OS verification certificate. ",
status.message()));
}
return absl::OkStatus();
}
absl::Status
CredentialManagerImpl::VerifyAndStageSerialConsoleTrustedUserCAKeys(
std::string_view trusted_user_ca_keys, std::string_view signature) {
ECCLESIA_ASSIGN_OR_RETURN(
StagedTempFile trusted_user_ca_keys_file,
StagedTempFile::Create(trusted_user_ca_keys, kTempTrustedUserCAKeysPath));
absl::Status status =
VerifyAndStageCertificate(trusted_user_ca_keys_file.path(), signature);
LOG(WARNING) << "Trusted user CA keys verify result: " << status.message();
if (!status.ok()) {
return absl::Status(
status.code(),
absl::StrCat("Failed to verify trusted user CA keys signature. ",
status.message()));
}
return absl::OkStatus();
}
absl::Status CredentialManagerImpl::InstallSerialConsoleTrustedUserCAKeys(
std::string_view trusted_user_ca_keys, std::string_view signature) {
absl::MutexLock lock(mutex_);
ECCLESIA_RETURN_IF_ERROR(VerifyAndStageSerialConsoleTrustedUserCAKeys(
trusted_user_ca_keys, signature));
ECCLESIA_RETURN_IF_ERROR(FileManager::WriteToFile(
trusted_user_ca_keys, params_.serial_console_trusted_user_ca_keys_path));
ECCLESIA_RETURN_IF_ERROR(FileManager::WriteToFile(
signature, params_.serial_console_detatched_signature_path));
serial_console_trusted_user_ca_keys_ = std::string(trusted_user_ca_keys);
serial_console_detatched_signature_ = std::string(signature);
return absl::OkStatus();
}
absl::Status CredentialManagerImpl::InstallSyslogCert(
std::string_view cert_string, std::string_view target_ip,
int64_t target_port, SyslogProtocol protocol, SyslogTlsMode tls_mode) {
absl::MutexLock lock(mutex_);
std::optional<SyslogTargetConfig> config_orig = syslog_cert_config_;
if (target_ip.empty() && (target_port > 0) && (target_port <= 65535)) {
return absl::InvalidArgumentError("Target IP is empty.");
}
if (!target_ip.empty() && ((target_port <= 0) || (target_port > 65535))) {
return absl::InvalidArgumentError("Target port is empty.");
}
if (protocol == SYSLOG_PROTOCOL_UDP && tls_mode != SYSLOG_TLS_MODE_DISABLED) {
return absl::InvalidArgumentError(
"TLS is not supported when protocol is UDP.");
}
if (!target_ip.empty() && (target_port != 0)) {
SyslogTargetConfig new_config;
new_config.set_target_ip(target_ip);
new_config.set_target_port(target_port);
new_config.set_protocol(protocol);
new_config.set_tls_mode(tls_mode);
std::string override_content = GetSyslogOverrideContent(new_config);
ECCLESIA_RETURN_IF_ERROR(FileManager::WriteToFile(
override_content, params_.syslog_client_conf_override_path));
syslog_cert_config_ = std::move(new_config);
}
absl::Status status =
FileManager::WriteToFile(cert_string, params_.syslog_root_cert_path);
if (!status.ok()) {
// If cert file write fails, revert client conf override file.
if (config_orig.has_value()) {
absl::Status revert_status;
revert_status =
FileManager::WriteToFile(GetSyslogOverrideContent(*config_orig),
params_.syslog_client_conf_override_path);
if (!revert_status.ok()) {
LOG(ERROR) << "Failed to revert client conf override file: "
<< revert_status;
}
syslog_cert_config_ = std::move(config_orig);
}
return status;
}
syslog_root_cert_ = std::string(cert_string);
return absl::OkStatus();
}
} // namespace milotic_tlbmc