#include "config.h" #include "certs_manager.hpp" #include "x509_utils.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace phosphor::certs { namespace { namespace fs = std::filesystem; using ::phosphor::logging::commit; using ::phosphor::logging::elog; using ::phosphor::logging::report; using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate; using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; using ::sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; using NotAllowedReason = ::phosphor::logging::xyz::openbmc_project::Common::NotAllowed::REASON; using InvalidCertificateReason = ::phosphor::logging::xyz::openbmc_project:: Certs::InvalidCertificate::REASON; using ::sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; using Argument = ::phosphor::logging::xyz::openbmc_project::Common::InvalidArgument; // RAII support for openSSL functions. using X509ReqPtr = std::unique_ptr; using EVPPkeyPtr = std::unique_ptr; using BignumPtr = std::unique_ptr; using X509StorePtr = std::unique_ptr; constexpr int supportedKeyBitLength = 2048; constexpr int defaultKeyBitLength = 2048; // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349 constexpr auto defaultKeyCurveID = "secp224r1"; // PEM certificate block markers, defined in go/rfc/7468. constexpr std::string_view beginCertificate = "-----BEGIN CERTIFICATE-----"; constexpr std::string_view endCertificate = "-----END CERTIFICATE-----"; /** * @brief Splits the given authorities list file and returns an array of * individual PEM encoded x509 certificate. * * @param[in] sourceFilePath - Path to the authorities list file. * * @return An array of individual PEM encoded x509 certificate */ std::vector splitCertificates(const std::string& sourceFilePath) { std::ifstream inputCertFileStream; inputCertFileStream.exceptions( std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit); std::stringstream pemStream; std::vector certificatesList; try { inputCertFileStream.open(sourceFilePath); pemStream << inputCertFileStream.rdbuf(); inputCertFileStream.close(); } catch (const std::exception& e) { lg2::error("Failed to read certificates list, ERR:{ERR}, SRC:{SRC}", "ERR", e, "SRC", sourceFilePath); elog(); } std::string pem = pemStream.str(); size_t begin = 0; // |begin| points to the current start position for searching the next // |beginCertificate| block. When we find the beginning of the certificate, // we extract the content between the beginning and the end of the current // certificate. And finally we move |begin| to the end of the current // certificate to start searching the next potential certificate. for (begin = pem.find(beginCertificate, begin); begin != std::string::npos; begin = pem.find(beginCertificate, begin)) { size_t end = pem.find(endCertificate, begin); if (end == std::string::npos) { lg2::error( "invalid PEM contains a BEGIN identifier without an END"); elog(InvalidCertificateReason( "invalid PEM contains a BEGIN identifier without an END")); } end += endCertificate.size(); certificatesList.emplace_back(pem.substr(begin, end - begin)); begin = end; } return certificatesList; } } // namespace Manager::Manager(sdbusplus::bus_t& bus, sdeventplus::Event& event, const char* path, CertificateType type, const std::string& unit, const std::string& installPath) : internal::ManagerInterface(bus, path), bus(bus), event(event), objectPath(path), certType(type), unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)), certParentInstallPath(fs::path(certInstallPath).parent_path()) { try { // Create certificate directory if not existing. // Set correct certificate directory permissions. fs::path certDirectory; try { if (certType == CertificateType::authority) { certDirectory = certInstallPath; } else { certDirectory = certParentInstallPath; } if (!fs::exists(certDirectory)) { fs::create_directories(certDirectory); } auto permission = fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec; fs::permissions(certDirectory, permission, fs::perm_options::replace); storageUpdate(); } catch (const fs::filesystem_error& e) { lg2::error( "Failed to create directory, ERR:{ERR}, DIRECTORY:{DIRECTORY}", "ERR", e, "DIRECTORY", certParentInstallPath); report(); } // Generating RSA private key file if certificate type is server/client if (certType != CertificateType::authority) { createRSAPrivateKeyFile(); } // restore any existing certificates createCertificates(); // watch is not required for authority certificates if (certType != CertificateType::authority) { // watch for certificate file create/replace certWatchPtr = std::make_unique< Watch>(event, certInstallPath, [this]() { try { // if certificate file existing update it if (!installedCerts.empty()) { lg2::info("Inotify callback to update " "certificate properties"); installedCerts[0]->populateProperties(); } else { lg2::info( "Inotify callback to create certificate object"); createCertificates(); } } catch (const InternalFailure& e) { commit(); } catch (const InvalidCertificate& e) { commit(); } }); } else { try { const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem"; if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath)) { lg2::notice( "Legacy certificate detected, will be installed from," "SINGLE_CERTPATH:{SINGLE_CERTPATH}", "SINGLE_CERTPATH", singleCertPath); install(singleCertPath); if (!fs::remove(singleCertPath)) { lg2::error("Unable to remove old certificate from," "SINGLE_CERTPATH:{SINGLE_CERTPATH}", "SINGLE_CERTPATH", singleCertPath); elog(); } } } catch (const std::exception& ex) { lg2::error( "Error in restoring legacy certificate, ERROR_STR:{ERROR_STR}", "ERROR_STR", ex); } } } catch (const std::exception& ex) { lg2::error( "Error in certificate manager constructor, ERROR_STR:{ERROR_STR}", "ERROR_STR", ex); } } std::string Manager::install(const std::string filePath) { if (certType != CertificateType::authority && !installedCerts.empty()) { elog(NotAllowedReason("Certificate already exist")); } else if (certType == CertificateType::authority && installedCerts.size() >= maxNumAuthorityCertificates) { elog(NotAllowedReason("Certificates limit reached")); } std::string certObjectPath; if (isCertificateUnique(filePath)) { certObjectPath = objectPath + '/' + std::to_string(certIdCounter); installedCerts.emplace_back(std::make_unique( bus, certObjectPath, certType, certInstallPath, filePath, certWatchPtr.get(), *this, /*restore=*/false)); reloadOrReset(unitToRestart); certIdCounter++; } else { elog(NotAllowedReason("Certificate already exist")); } return certObjectPath; } std::vector Manager::installAll(const std::string filePath) { if (certType != CertificateType::authority) { elog(NotAllowedReason( "The InstallAll interface is only allowed for " "Authority certificates")); } if (!installedCerts.empty()) { elog(NotAllowedReason( "There are already root certificates; Call DeleteAll then " "InstallAll, or use ReplaceAll")); } fs::path sourceFile(filePath); if (!fs::exists(sourceFile)) { lg2::error("File is Missing, FILE:{FILE}", "FILE", filePath); elog(); } std::vector authorities = splitCertificates(sourceFile); if (authorities.size() > maxNumAuthorityCertificates) { elog(NotAllowedReason("Certificates limit reached")); } lg2::info("Starts authority list install"); fs::path authorityStore(certInstallPath); // Atomically install all the certificates fs::path tempPath = Certificate::generateUniqueFilePath(authorityStore); fs::create_directory(tempPath); // Copies the authorities list Certificate::copyCertificate(sourceFile, tempPath / defaultAuthoritiesListFileName); std::vector> tempCertificates; uint64_t tempCertIdCounter = certIdCounter; X509StorePtr x509Store = getX509Store(sourceFile); for (const auto& authority : authorities) { std::string certObjectPath = objectPath + '/' + std::to_string(tempCertIdCounter); tempCertificates.emplace_back(std::make_unique( bus, certObjectPath, certType, tempPath, *x509Store, authority, certWatchPtr.get(), *this, /*restore=*/false)); tempCertIdCounter++; } // We are good now, issue swap installedCerts = std::move(tempCertificates); certIdCounter = tempCertIdCounter; // Rename all the certificates including the authorities list for (const fs::path& f : fs::directory_iterator(tempPath)) { if (fs::is_symlink(f)) { continue; } fs::rename(/*from=*/f, /*to=*/certInstallPath / f.filename()); } // Update file locations and create symbol links for (const auto& cert : installedCerts) { cert->setCertInstallPath(certInstallPath); cert->setCertFilePath( certInstallPath / fs::path(cert->getCertFilePath()).filename()); cert->storageUpdate(); } // Remove the temporary folder fs::remove_all(tempPath); std::vector objects; for (const auto& certificate : installedCerts) { objects.emplace_back(certificate->getObjectPath()); } lg2::info("Finishes authority list install; reload units starts"); reloadOrReset(unitToRestart); return objects; } std::vector Manager::replaceAll(std::string filePath) { installedCerts.clear(); certIdCounter = 1; storageUpdate(); return installAll(std::move(filePath)); } void Manager::deleteAll() { // TODO: #Issue 4 when a certificate is deleted system auto generates // certificate file. At present we are not supporting creation of // certificate object for the auto-generated certificate file as // deletion if only applicable for REST server and Bmcweb does not allow // deletion of certificates installedCerts.clear(); // If the authorities list exists, delete it as well if (certType == CertificateType::authority) { if (fs::path authoritiesList = fs::path(certInstallPath) / defaultAuthoritiesListFileName; fs::exists(authoritiesList)) { fs::remove(authoritiesList); } } certIdCounter = 1; storageUpdate(); reloadOrReset(unitToRestart); } void Manager::deleteCertificate(const Certificate* const certificate) { const std::vector>::iterator& certIt = std::find_if(installedCerts.begin(), installedCerts.end(), [certificate](const std::unique_ptr& cert) { return (cert.get() == certificate); }); if (certIt != installedCerts.end()) { installedCerts.erase(certIt); storageUpdate(); reloadOrReset(unitToRestart); } else { lg2::error("Certificate does not exist, ID:{ID}", "ID", certificate->getCertId()); elog(); } } void Manager::replaceCertificate(Certificate* const certificate, const std::string& filePath) { if (isCertificateUnique(filePath, certificate)) { certificate->install(filePath, false); storageUpdate(); reloadOrReset(unitToRestart); } else { elog(NotAllowedReason("Certificate already exist")); } } std::string Manager::generateCSR( std::vector alternativeNames, std::string challengePassword, std::string city, std::string commonName, std::string contactPerson, std::string country, std::string email, std::string givenName, std::string initials, int64_t keyBitLength, std::string keyCurveId, std::string keyPairAlgorithm, std::vector keyUsage, std::string organization, std::string organizationalUnit, std::string state, std::string surname, std::string unstructuredName) { // We support only one CSR. csrPtr.reset(nullptr); auto pid = fork(); if (pid == -1) { lg2::error("Error occurred during forking process"); report(); } else if (pid == 0) { try { generateCSRHelper( alternativeNames, challengePassword, city, commonName, contactPerson, country, email, givenName, initials, keyBitLength, keyCurveId, keyPairAlgorithm, keyUsage, organization, organizationalUnit, state, surname, unstructuredName); exit(EXIT_SUCCESS); } catch (const InternalFailure& e) { // commit the error reported in child process and exit // Callback method from SDEvent Loop looks for exit status exit(EXIT_FAILURE); commit(); } catch (const InvalidArgument& e) { // commit the error reported in child process and exit // Callback method from SDEvent Loop looks for exit status exit(EXIT_FAILURE); commit(); } } else { using namespace sdeventplus::source; Child::Callback callback = [this](Child& eventSource, const siginfo_t* si) { eventSource.set_enabled(Enabled::On); if (si->si_status != 0) { this->createCSRObject(Status::failure); } else { this->createCSRObject(Status::success); } }; try { sigset_t ss; if (sigemptyset(&ss) < 0) { lg2::error("Unable to initialize signal set"); elog(); } if (sigaddset(&ss, SIGCHLD) < 0) { lg2::error("Unable to add signal to signal set"); elog(); } // Block SIGCHLD first, so that the event loop can handle it if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0) { lg2::error("Unable to block signal"); elog(); } if (childPtr) { childPtr.reset(); } childPtr = std::make_unique(event, pid, WEXITED | WSTOPPED, std::move(callback)); } catch (const InternalFailure& e) { commit(); } } auto csrObjectPath = objectPath + '/' + "csr"; return csrObjectPath; } std::vector>& Manager::getCertificates() { return installedCerts; } void Manager::generateCSRHelper( std::vector alternativeNames, std::string challengePassword, std::string city, std::string commonName, std::string contactPerson, std::string country, std::string email, std::string givenName, std::string initials, int64_t keyBitLength, std::string keyCurveId, std::string keyPairAlgorithm, std::vector keyUsage, std::string organization, std::string organizationalUnit, std::string state, std::string surname, std::string unstructuredName) { int ret = 0; X509ReqPtr x509Req(X509_REQ_new(), ::X509_REQ_free); // set subject of x509 req X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get()); if (!alternativeNames.empty()) { for (auto& name : alternativeNames) { addEntry(x509Name, "subjectAltName", name); } } addEntry(x509Name, "challengePassword", challengePassword); addEntry(x509Name, "L", city); addEntry(x509Name, "CN", commonName); addEntry(x509Name, "name", contactPerson); addEntry(x509Name, "C", country); addEntry(x509Name, "emailAddress", email); addEntry(x509Name, "GN", givenName); addEntry(x509Name, "initials", initials); addEntry(x509Name, "algorithm", keyPairAlgorithm); if (!keyUsage.empty()) { for (auto& usage : keyUsage) { if (isExtendedKeyUsage(usage)) { addEntry(x509Name, "extendedKeyUsage", usage); } else { addEntry(x509Name, "keyUsage", usage); } } } addEntry(x509Name, "O", organization); addEntry(x509Name, "OU", organizationalUnit); addEntry(x509Name, "ST", state); addEntry(x509Name, "SN", surname); addEntry(x509Name, "unstructuredName", unstructuredName); EVPPkeyPtr pKey(nullptr, ::EVP_PKEY_free); lg2::info("Given Key pair algorithm, KEYPAIRALGORITHM:{KEYPAIRALGORITHM}", "KEYPAIRALGORITHM", keyPairAlgorithm); // Used EC algorithm as default if user did not give algorithm type. if (keyPairAlgorithm == "RSA") pKey = getRSAKeyPair(keyBitLength); else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty())) pKey = generateECKeyPair(keyCurveId); else { lg2::error("Given Key pair algorithm is not supported. Supporting " "RSA and EC only"); elog( Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"), Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str())); } ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get()); if (ret == 0) { lg2::error("Error occurred while setting Public key"); ERR_print_errors_fp(stderr); elog(); } // Write private key to file writePrivateKey(pKey, defaultPrivateKeyFileName); // set sign key of x509 req ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256()); if (ret == 0) { lg2::error("Error occurred while signing key of x509"); ERR_print_errors_fp(stderr); elog(); } lg2::info("Writing CSR to file"); fs::path csrFilePath = certParentInstallPath / defaultCSRFileName; writeCSR(csrFilePath.string(), x509Req); } bool Manager::isExtendedKeyUsage(const std::string& usage) { const static std::array usageList = { "ServerAuthentication", "ClientAuthentication", "OCSPSigning", "Timestamping", "CodeSigning", "EmailProtection"}; auto it = std::find_if( usageList.begin(), usageList.end(), [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); }); return it != usageList.end(); } EVPPkeyPtr Manager::generateRSAKeyPair(const int64_t keyBitLength) { int64_t keyBitLen = keyBitLength; // set keybit length to default value if not set if (keyBitLen <= 0) { lg2::info("KeyBitLength is not given.Hence, using default KeyBitLength:" "{DEFAULTKEYBITLENGTH}", "DEFAULTKEYBITLENGTH", defaultKeyBitLength); keyBitLen = defaultKeyBitLength; } #if (OPENSSL_VERSION_NUMBER < 0x30000000L) // generate rsa key BignumPtr bne(BN_new(), ::BN_free); auto ret = BN_set_word(bne.get(), RSA_F4); if (ret == 0) { lg2::error("Error occurred during BN_set_word call"); ERR_print_errors_fp(stderr); elog(); } using RSAPtr = std::unique_ptr; RSAPtr rsa(RSA_new(), ::RSA_free); ret = RSA_generate_key_ex(rsa.get(), keyBitLen, bne.get(), nullptr); if (ret != 1) { lg2::error( "Error occurred during RSA_generate_key_ex call: {KEYBITLENGTH}", "KEYBITLENGTH", keyBitLen); ERR_print_errors_fp(stderr); elog(); } // set public key of x509 req EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); ret = EVP_PKEY_assign_RSA(pKey.get(), rsa.get()); if (ret == 0) { lg2::error("Error occurred during assign rsa key into EVP"); ERR_print_errors_fp(stderr); elog(); } // Now |rsa| is managed by |pKey| rsa.release(); return pKey; #else auto ctx = std::unique_ptr( EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free); if (!ctx) { lg2::error("Error occurred creating EVP_PKEY_CTX from algorithm"); ERR_print_errors_fp(stderr); elog(); } if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) || (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), static_cast(keyBitLen)) <= 0)) { lg2::error("Error occurred initializing keygen context"); ERR_print_errors_fp(stderr); elog(); } EVP_PKEY* pKey = nullptr; if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0) { lg2::error("Error occurred during generate EC key"); ERR_print_errors_fp(stderr); elog(); } return {pKey, &::EVP_PKEY_free}; #endif } EVPPkeyPtr Manager::generateECKeyPair(const std::string& curveId) { std::string curId(curveId); if (curId.empty()) { lg2::info("KeyCurveId is not given. Hence using default curve id," "DEFAULTKEYCURVEID:{DEFAULTKEYCURVEID}", "DEFAULTKEYCURVEID", defaultKeyCurveID); curId = defaultKeyCurveID; } int ecGrp = OBJ_txt2nid(curId.c_str()); if (ecGrp == NID_undef) { lg2::error( "Error occurred during convert the curve id string format into NID," "KEYCURVEID:{KEYCURVEID}", "KEYCURVEID", curId); elog(); } #if (OPENSSL_VERSION_NUMBER < 0x30000000L) EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp); if (ecKey == nullptr) { lg2::error( "Error occurred during create the EC_Key object from NID, ECGROUP:{ECGROUP}", "ECGROUP", ecGrp); ERR_print_errors_fp(stderr); elog(); } // If you want to save a key and later load it with // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE // flag on the key. EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE); int ret = EC_KEY_generate_key(ecKey); if (ret == 0) { EC_KEY_free(ecKey); lg2::error("Error occurred during generate EC key"); ERR_print_errors_fp(stderr); elog(); } EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey); if (ret == 0) { EC_KEY_free(ecKey); lg2::error("Error occurred during assign EC Key into EVP"); ERR_print_errors_fp(stderr); elog(); } return pKey; #else auto holderOfKey = [](EVP_PKEY* key) { return std::unique_ptr{ key, &::EVP_PKEY_free}; }; // Create context to set up curve parameters. auto ctx = std::unique_ptr( EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free); if (!ctx) { lg2::error("Error occurred creating EVP_PKEY_CTX for params"); ERR_print_errors_fp(stderr); elog(); } // Set up curve parameters. EVP_PKEY* params = nullptr; if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) || (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <= 0) || (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) || (EVP_PKEY_paramgen(ctx.get(), ¶ms) <= 0)) { lg2::error("Error occurred setting curve parameters"); ERR_print_errors_fp(stderr); elog(); } // Move parameters to RAII holder. auto pparms = holderOfKey(params); // Create new context for key. ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr)); if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0)) { lg2::error("Error occurred initializing keygen context"); ERR_print_errors_fp(stderr); elog(); } EVP_PKEY* pKey = nullptr; if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0) { lg2::error("Error occurred during generate EC key"); ERR_print_errors_fp(stderr); elog(); } return holderOfKey(pKey); #endif } void Manager::writePrivateKey(const EVPPkeyPtr& pKey, const std::string& privKeyFileName) { lg2::info("Writing private key to file"); // write private key to file fs::path privKeyPath = certParentInstallPath / privKeyFileName; FILE* fp = std::fopen(privKeyPath.c_str(), "w"); if (fp == nullptr) { lg2::error("Error occurred creating private key file"); elog(); } int ret = PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, nullptr, nullptr); std::fclose(fp); if (ret == 0) { lg2::error("Error occurred while writing private key to file"); elog(); } } void Manager::addEntry(X509_NAME* x509Name, const char* field, const std::string& bytes) { if (bytes.empty()) { return; } int ret = X509_NAME_add_entry_by_txt( x509Name, field, MBSTRING_ASC, reinterpret_cast(bytes.c_str()), -1, -1, 0); if (ret != 1) { lg2::error("Unable to set entry, FIELD:{FIELD}, VALUE:{VALUE}", "FIELD", field, "VALUE", bytes); ERR_print_errors_fp(stderr); elog(); } } void Manager::createCSRObject(const Status& status) { if (csrPtr) { csrPtr.reset(nullptr); } auto csrObjectPath = objectPath + '/' + "csr"; csrPtr = std::make_unique(bus, csrObjectPath.c_str(), certInstallPath.c_str(), status); } void Manager::writeCSR(const std::string& filePath, const X509ReqPtr& x509Req) { if (fs::exists(filePath)) { lg2::info("Removing the existing file, FILENAME:{FILENAME}", "FILENAME", filePath); if (!fs::remove(filePath.c_str())) { lg2::error("Unable to remove the file, FILENAME:{FILENAME}", "FILENAME", filePath); elog(); } } FILE* fp = std::fopen(filePath.c_str(), "w"); if (fp == nullptr) { lg2::error( "Error opening the file to write the CSR, FILENAME:{FILENAME}", "FILENAME", filePath); elog(); } int rc = PEM_write_X509_REQ(fp, x509Req.get()); if (!rc) { lg2::error("PEM write routine failed, FILENAME:{FILENAME}", "FILENAME", filePath); std::fclose(fp); elog(); } std::fclose(fp); } void Manager::createCertificates() { auto certObjectPath = objectPath + '/'; if (certType == CertificateType::authority) { // Check whether install path is a directory. if (!fs::is_directory(certInstallPath)) { lg2::error("Certificate installation path exists and it is " "not a directory"); elog(); } // If the authorities list exists, recover from it and return if (fs::path authoritiesListFilePath = fs::path(certInstallPath) / defaultAuthoritiesListFileName; fs::exists(authoritiesListFilePath)) { // remove all other files and directories for (auto& path : fs::directory_iterator(certInstallPath)) { if (path.path() != authoritiesListFilePath) { fs::remove_all(path); } } installAll(authoritiesListFilePath); return; } for (auto& path : fs::directory_iterator(certInstallPath)) { try { // Assume here any regular file located in certificate directory // contains certificates body. Do not want to use soft links // would add value. if (fs::is_regular_file(path)) { installedCerts.emplace_back(std::make_unique( bus, certObjectPath + std::to_string(certIdCounter++), certType, certInstallPath, path.path(), certWatchPtr.get(), *this, /*restore=*/true)); } } catch (const InternalFailure& e) { report(); } catch (const InvalidCertificate& e) { report(InvalidCertificateReason( "Existing certificate file is corrupted")); } } } else if (fs::exists(certInstallPath)) { try { installedCerts.emplace_back(std::make_unique( bus, certObjectPath + '1', certType, certInstallPath, certInstallPath, certWatchPtr.get(), *this, /*restore=*/false)); } catch (const InternalFailure& e) { report(); } catch (const InvalidCertificate& e) { report(InvalidCertificateReason( "Existing certificate file is corrupted")); } } } void Manager::createRSAPrivateKeyFile() { fs::path rsaPrivateKeyFileName = certParentInstallPath / defaultRSAPrivateKeyFileName; try { if (!fs::exists(rsaPrivateKeyFileName)) { writePrivateKey(generateRSAKeyPair(supportedKeyBitLength), defaultRSAPrivateKeyFileName); } } catch (const InternalFailure& e) { report(); } } EVPPkeyPtr Manager::getRSAKeyPair(const int64_t keyBitLength) { if (keyBitLength != supportedKeyBitLength) { lg2::error( "Given Key bit length is not supported, GIVENKEYBITLENGTH:" "{GIVENKEYBITLENGTH}, SUPPORTEDKEYBITLENGTH:{SUPPORTEDKEYBITLENGTH}", "GIVENKEYBITLENGTH", keyBitLength, "SUPPORTEDKEYBITLENGTH", supportedKeyBitLength); elog( Argument::ARGUMENT_NAME("KEYBITLENGTH"), Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str())); } fs::path rsaPrivateKeyFileName = certParentInstallPath / defaultRSAPrivateKeyFileName; FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r"); if (!privateKeyFile) { lg2::error( "Unable to open RSA private key file to read, RSAKEYFILE:{RSAKEYFILE}," "ERRORREASON:{ERRORREASON}", "RSAKEYFILE", rsaPrivateKeyFileName, "ERRORREASON", strerror(errno)); elog(); } EVPPkeyPtr privateKey( PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr), ::EVP_PKEY_free); std::fclose(privateKeyFile); if (!privateKey) { lg2::error("Error occurred during PEM_read_PrivateKey call"); elog(); } return privateKey; } void Manager::storageUpdate() { if (certType == CertificateType::authority) { // Remove symbolic links in the certificate directory for (auto& certPath : fs::directory_iterator(certInstallPath)) { try { if (fs::is_symlink(certPath)) { fs::remove(certPath); } } catch (const std::exception& e) { lg2::error( "Failed to remove symlink for certificate, ERR:{ERR} SYMLINK:{SYMLINK}", "ERR", e, "SYMLINK", certPath.path().string()); elog(); } } } for (const auto& cert : installedCerts) { cert->storageUpdate(); } } void Manager::reloadOrReset(const std::string& unit) { if (!unit.empty()) { try { constexpr auto defaultSystemdService = "org.freedesktop.systemd1"; constexpr auto defaultSystemdObjectPath = "/org/freedesktop/systemd1"; constexpr auto defaultSystemdInterface = "org.freedesktop.systemd1.Manager"; auto method = bus.new_method_call( defaultSystemdService, defaultSystemdObjectPath, defaultSystemdInterface, "ReloadOrRestartUnit"); method.append(unit, "replace"); bus.call_noreply(method); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to reload or restart service, ERR:{ERR}, UNIT:{UNIT}", "ERR", e, "UNIT", unit); elog(); } } } bool Manager::isCertificateUnique(const std::string& filePath, const Certificate* const certToDrop) { if (std::any_of( installedCerts.begin(), installedCerts.end(), [&filePath, certToDrop](const std::unique_ptr& cert) { return cert.get() != certToDrop && cert->isSame(filePath); })) { return false; } else { return true; } } } // namespace phosphor::certs