1cfbc8dc8SJayanth Othayoth #include "certs_manager.hpp" 2cfbc8dc8SJayanth Othayoth 3f4682712SMarri Devender Rao #include <openssl/pem.h> 4f4682712SMarri Devender Rao #include <unistd.h> 5f4682712SMarri Devender Rao 66ceec40bSMarri Devender Rao #include <phosphor-logging/elog-errors.hpp> 713bf74e4SMarri Devender Rao #include <xyz/openbmc_project/Certs/error.hpp> 8cfbc8dc8SJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp> 9cfbc8dc8SJayanth Othayoth namespace phosphor 10cfbc8dc8SJayanth Othayoth { 11cfbc8dc8SJayanth Othayoth namespace certs 12cfbc8dc8SJayanth Othayoth { 131396511dSMarri Devender Rao using InternalFailure = 141396511dSMarri Devender Rao sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 15ffad1ef1SMarri Devender Rao using InvalidCertificate = 16ffad1ef1SMarri Devender Rao sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate; 17ffad1ef1SMarri Devender Rao using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON; 18cfbc8dc8SJayanth Othayoth 19f4682712SMarri Devender Rao using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>; 20f4682712SMarri Devender Rao using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>; 21c6e58c7eSRamesh Iyyar using InvalidArgument = 22c6e58c7eSRamesh Iyyar sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 23c6e58c7eSRamesh Iyyar using Argument = xyz::openbmc_project::Common::InvalidArgument; 24c6e58c7eSRamesh Iyyar 25c6e58c7eSRamesh Iyyar constexpr auto SUPPORTED_KEYBITLENGTH = 2048; 26f4682712SMarri Devender Rao 27f4682712SMarri Devender Rao Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event, 28f4682712SMarri Devender Rao const char* path, const CertificateType& type, 29f4682712SMarri Devender Rao UnitsToRestart&& unit, CertInstallPath&& installPath) : 306ceec40bSMarri Devender Rao Ifaces(bus, path), 31f4682712SMarri Devender Rao bus(bus), event(event), objectPath(path), certType(type), 32c6e58c7eSRamesh Iyyar unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)), 33c6e58c7eSRamesh Iyyar certParentInstallPath(fs::path(certInstallPath).parent_path()) 34cfbc8dc8SJayanth Othayoth { 35b57d75e2SMarri Devender Rao // create parent certificate path if not existing 36b57d75e2SMarri Devender Rao try 37b57d75e2SMarri Devender Rao { 38b57d75e2SMarri Devender Rao if (!fs::exists(certParentInstallPath)) 39b57d75e2SMarri Devender Rao { 40b57d75e2SMarri Devender Rao fs::create_directories(certParentInstallPath); 41b57d75e2SMarri Devender Rao } 42b57d75e2SMarri Devender Rao } 43b57d75e2SMarri Devender Rao catch (fs::filesystem_error& e) 44b57d75e2SMarri Devender Rao { 45b57d75e2SMarri Devender Rao log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()), 46b57d75e2SMarri Devender Rao entry("DIRECTORY=%s", certParentInstallPath.c_str())); 47b57d75e2SMarri Devender Rao report<InternalFailure>(); 48b57d75e2SMarri Devender Rao } 49b57d75e2SMarri Devender Rao 50c6e58c7eSRamesh Iyyar // Generating RSA private key file if certificate type is server/client 51c6e58c7eSRamesh Iyyar if (certType != AUTHORITY) 52c6e58c7eSRamesh Iyyar { 53c6e58c7eSRamesh Iyyar createRSAPrivateKeyFile(); 54c6e58c7eSRamesh Iyyar } 55c6e58c7eSRamesh Iyyar 56ffad1ef1SMarri Devender Rao // restore any existing certificates 57*db029c95SKowalski, Kamil createCertificates(); 58ffad1ef1SMarri Devender Rao 59ffad1ef1SMarri Devender Rao // watch is not required for authority certificates 60ffad1ef1SMarri Devender Rao if (certType != AUTHORITY) 61ffad1ef1SMarri Devender Rao { 62ffad1ef1SMarri Devender Rao // watch for certificate file create/replace 63ffad1ef1SMarri Devender Rao certWatchPtr = std::make_unique< 64ffad1ef1SMarri Devender Rao Watch>(event, certInstallPath, [this]() { 65bf7c588cSMarri Devender Rao try 66bf7c588cSMarri Devender Rao { 67ffad1ef1SMarri Devender Rao // if certificate file existing update it 68*db029c95SKowalski, Kamil if (!installedCerts.empty()) 69ffad1ef1SMarri Devender Rao { 70ffad1ef1SMarri Devender Rao log<level::INFO>( 71ffad1ef1SMarri Devender Rao "Inotify callback to update certificate properties"); 72*db029c95SKowalski, Kamil installedCerts[0]->populateProperties(); 73ffad1ef1SMarri Devender Rao } 74ffad1ef1SMarri Devender Rao else 75ffad1ef1SMarri Devender Rao { 76ffad1ef1SMarri Devender Rao log<level::INFO>( 77ffad1ef1SMarri Devender Rao "Inotify callback to create certificate object"); 78*db029c95SKowalski, Kamil createCertificates(); 79ffad1ef1SMarri Devender Rao } 80bf7c588cSMarri Devender Rao } 81bf7c588cSMarri Devender Rao catch (const InternalFailure& e) 82bf7c588cSMarri Devender Rao { 83ffad1ef1SMarri Devender Rao commit<InternalFailure>(); 84bf7c588cSMarri Devender Rao } 85bf7c588cSMarri Devender Rao catch (const InvalidCertificate& e) 86bf7c588cSMarri Devender Rao { 87ffad1ef1SMarri Devender Rao commit<InvalidCertificate>(); 88bf7c588cSMarri Devender Rao } 89ffad1ef1SMarri Devender Rao }); 90bf7c588cSMarri Devender Rao } 91*db029c95SKowalski, Kamil else 92*db029c95SKowalski, Kamil { 93*db029c95SKowalski, Kamil const std::string signleCertPath = "/etc/ssl/certs/Root-CA.pem"; 94*db029c95SKowalski, Kamil if (fs::exists(signleCertPath) && !fs::is_empty(signleCertPath)) 95*db029c95SKowalski, Kamil { 96*db029c95SKowalski, Kamil log<level::NOTICE>( 97*db029c95SKowalski, Kamil "Legacy certificate detected, will be installed from: ", 98*db029c95SKowalski, Kamil entry("SINGLE_CERTPATH=%s", signleCertPath.c_str())); 99*db029c95SKowalski, Kamil install(signleCertPath); 100*db029c95SKowalski, Kamil if (!fs::remove(signleCertPath)) 101*db029c95SKowalski, Kamil { 102*db029c95SKowalski, Kamil log<level::ERR>( 103*db029c95SKowalski, Kamil "Unable to remove old certificate from: ", 104*db029c95SKowalski, Kamil entry("SINGLE_CERTPATH=%s", signleCertPath.c_str())); 105*db029c95SKowalski, Kamil elog<InternalFailure>(); 106*db029c95SKowalski, Kamil } 107*db029c95SKowalski, Kamil } 108*db029c95SKowalski, Kamil } 109dd74bd20SJayanth Othayoth } 110589159f2SJayanth Othayoth 11106a69d7bSZbigniew Kurzynski std::string Manager::install(const std::string filePath) 112cfbc8dc8SJayanth Othayoth { 1131396511dSMarri Devender Rao using NotAllowed = 1141396511dSMarri Devender Rao sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 1151396511dSMarri Devender Rao using Reason = xyz::openbmc_project::Common::NotAllowed::REASON; 116*db029c95SKowalski, Kamil 117*db029c95SKowalski, Kamil if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty()) 1181396511dSMarri Devender Rao { 1191396511dSMarri Devender Rao elog<NotAllowed>(Reason("Certificate already exist")); 1201396511dSMarri Devender Rao } 121ffad1ef1SMarri Devender Rao 122*db029c95SKowalski, Kamil auto certObjectPath = objectPath + '/' + std::to_string(certIdCounter++); 123*db029c95SKowalski, Kamil 124*db029c95SKowalski, Kamil installedCerts.emplace_back(std::make_unique<Certificate>( 1258f80c35bSMarri Devender Rao bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath, 126*db029c95SKowalski, Kamil false, certWatchPtr)); 12706a69d7bSZbigniew Kurzynski return certObjectPath; 128589159f2SJayanth Othayoth } 129ae70b3daSDeepak Kodihalli 130ae70b3daSDeepak Kodihalli void Manager::delete_() 131ae70b3daSDeepak Kodihalli { 1326ceec40bSMarri Devender Rao // TODO: #Issue 4 when a certificate is deleted system auto generates 1336ceec40bSMarri Devender Rao // certificate file. At present we are not supporting creation of 1346ceec40bSMarri Devender Rao // certificate object for the auto-generated certificate file as 1356ceec40bSMarri Devender Rao // deletion if only applicable for REST server and Bmcweb does not allow 1366ceec40bSMarri Devender Rao // deletion of certificates 137*db029c95SKowalski, Kamil installedCerts.clear(); 138ae70b3daSDeepak Kodihalli } 139f4682712SMarri Devender Rao 140f4682712SMarri Devender Rao std::string Manager::generateCSR( 141f4682712SMarri Devender Rao std::vector<std::string> alternativeNames, std::string challengePassword, 142f4682712SMarri Devender Rao std::string city, std::string commonName, std::string contactPerson, 143f4682712SMarri Devender Rao std::string country, std::string email, std::string givenName, 144f4682712SMarri Devender Rao std::string initials, int64_t keyBitLength, std::string keyCurveId, 145f4682712SMarri Devender Rao std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 146f4682712SMarri Devender Rao std::string organization, std::string organizationalUnit, std::string state, 147f4682712SMarri Devender Rao std::string surname, std::string unstructuredName) 148f4682712SMarri Devender Rao { 149f4682712SMarri Devender Rao // We support only one CSR. 150f4682712SMarri Devender Rao csrPtr.reset(nullptr); 151f4682712SMarri Devender Rao auto pid = fork(); 152f4682712SMarri Devender Rao if (pid == -1) 153f4682712SMarri Devender Rao { 154f4682712SMarri Devender Rao log<level::ERR>("Error occurred during forking process"); 155f4682712SMarri Devender Rao report<InternalFailure>(); 156f4682712SMarri Devender Rao } 157f4682712SMarri Devender Rao else if (pid == 0) 158f4682712SMarri Devender Rao { 159f4682712SMarri Devender Rao try 160f4682712SMarri Devender Rao { 161f4682712SMarri Devender Rao generateCSRHelper(alternativeNames, challengePassword, city, 162f4682712SMarri Devender Rao commonName, contactPerson, country, email, 163f4682712SMarri Devender Rao givenName, initials, keyBitLength, keyCurveId, 164f4682712SMarri Devender Rao keyPairAlgorithm, keyUsage, organization, 165f4682712SMarri Devender Rao organizationalUnit, state, surname, 166f4682712SMarri Devender Rao unstructuredName); 167f4682712SMarri Devender Rao exit(EXIT_SUCCESS); 168f4682712SMarri Devender Rao } 169f4682712SMarri Devender Rao catch (const InternalFailure& e) 170f4682712SMarri Devender Rao { 171f4682712SMarri Devender Rao // commit the error reported in child process and exit 172f4682712SMarri Devender Rao // Callback method from SDEvent Loop looks for exit status 173f4682712SMarri Devender Rao exit(EXIT_FAILURE); 174f4682712SMarri Devender Rao commit<InternalFailure>(); 175f4682712SMarri Devender Rao } 176f4682712SMarri Devender Rao } 177f4682712SMarri Devender Rao else 178f4682712SMarri Devender Rao { 179f4682712SMarri Devender Rao using namespace sdeventplus::source; 180f4682712SMarri Devender Rao Child::Callback callback = [this](Child& eventSource, 181f4682712SMarri Devender Rao const siginfo_t* si) { 182f4682712SMarri Devender Rao eventSource.set_enabled(Enabled::On); 183f4682712SMarri Devender Rao if (si->si_status != 0) 184f4682712SMarri Devender Rao { 185f4682712SMarri Devender Rao this->createCSRObject(Status::FAILURE); 186f4682712SMarri Devender Rao } 187f4682712SMarri Devender Rao else 188f4682712SMarri Devender Rao { 189f4682712SMarri Devender Rao this->createCSRObject(Status::SUCCESS); 190f4682712SMarri Devender Rao } 191f4682712SMarri Devender Rao }; 192f4682712SMarri Devender Rao try 193f4682712SMarri Devender Rao { 194f4682712SMarri Devender Rao sigset_t ss; 195f4682712SMarri Devender Rao if (sigemptyset(&ss) < 0) 196f4682712SMarri Devender Rao { 197f4682712SMarri Devender Rao log<level::ERR>("Unable to initialize signal set"); 198f4682712SMarri Devender Rao elog<InternalFailure>(); 199f4682712SMarri Devender Rao } 200f4682712SMarri Devender Rao if (sigaddset(&ss, SIGCHLD) < 0) 201f4682712SMarri Devender Rao { 202f4682712SMarri Devender Rao log<level::ERR>("Unable to add signal to signal set"); 203f4682712SMarri Devender Rao elog<InternalFailure>(); 204f4682712SMarri Devender Rao } 205f4682712SMarri Devender Rao 206f4682712SMarri Devender Rao // Block SIGCHLD first, so that the event loop can handle it 207f4682712SMarri Devender Rao if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0) 208f4682712SMarri Devender Rao { 209f4682712SMarri Devender Rao log<level::ERR>("Unable to block signal"); 210f4682712SMarri Devender Rao elog<InternalFailure>(); 211f4682712SMarri Devender Rao } 212f4682712SMarri Devender Rao if (childPtr) 213f4682712SMarri Devender Rao { 214f4682712SMarri Devender Rao childPtr.reset(); 215f4682712SMarri Devender Rao } 216f4682712SMarri Devender Rao childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED, 217f4682712SMarri Devender Rao std::move(callback)); 218f4682712SMarri Devender Rao } 219f4682712SMarri Devender Rao catch (const InternalFailure& e) 220f4682712SMarri Devender Rao { 221f4682712SMarri Devender Rao commit<InternalFailure>(); 222f4682712SMarri Devender Rao } 223f4682712SMarri Devender Rao } 224f4682712SMarri Devender Rao auto csrObjectPath = objectPath + '/' + "csr"; 225f4682712SMarri Devender Rao return csrObjectPath; 226f4682712SMarri Devender Rao } 227f4682712SMarri Devender Rao 228*db029c95SKowalski, Kamil std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates() 229ffad1ef1SMarri Devender Rao { 230*db029c95SKowalski, Kamil return installedCerts; 231ffad1ef1SMarri Devender Rao } 232ffad1ef1SMarri Devender Rao 233f4682712SMarri Devender Rao void Manager::generateCSRHelper( 234f4682712SMarri Devender Rao std::vector<std::string> alternativeNames, std::string challengePassword, 235f4682712SMarri Devender Rao std::string city, std::string commonName, std::string contactPerson, 236f4682712SMarri Devender Rao std::string country, std::string email, std::string givenName, 237f4682712SMarri Devender Rao std::string initials, int64_t keyBitLength, std::string keyCurveId, 238f4682712SMarri Devender Rao std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 239f4682712SMarri Devender Rao std::string organization, std::string organizationalUnit, std::string state, 240f4682712SMarri Devender Rao std::string surname, std::string unstructuredName) 241f4682712SMarri Devender Rao { 242f4682712SMarri Devender Rao int ret = 0; 243f4682712SMarri Devender Rao 244f4682712SMarri Devender Rao // set version of x509 req 245f4682712SMarri Devender Rao int nVersion = 1; 246f4682712SMarri Devender Rao // TODO: Issue#6 need to make version number configurable 247f4682712SMarri Devender Rao X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free); 248f4682712SMarri Devender Rao ret = X509_REQ_set_version(x509Req.get(), nVersion); 249f4682712SMarri Devender Rao if (ret == 0) 250f4682712SMarri Devender Rao { 251f4682712SMarri Devender Rao log<level::ERR>("Error occured during X509_REQ_set_version call"); 252f4682712SMarri Devender Rao elog<InternalFailure>(); 253f4682712SMarri Devender Rao } 254f4682712SMarri Devender Rao 255f4682712SMarri Devender Rao // set subject of x509 req 256f4682712SMarri Devender Rao X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get()); 257f4682712SMarri Devender Rao 258f4682712SMarri Devender Rao if (!alternativeNames.empty()) 259f4682712SMarri Devender Rao { 260f4682712SMarri Devender Rao for (auto& name : alternativeNames) 261f4682712SMarri Devender Rao { 262f4682712SMarri Devender Rao addEntry(x509Name, "subjectAltName", name); 263f4682712SMarri Devender Rao } 264f4682712SMarri Devender Rao } 265f4682712SMarri Devender Rao addEntry(x509Name, "challengePassword", challengePassword); 266f4682712SMarri Devender Rao addEntry(x509Name, "L", city); 267f4682712SMarri Devender Rao addEntry(x509Name, "CN", commonName); 268f4682712SMarri Devender Rao addEntry(x509Name, "name", contactPerson); 269f4682712SMarri Devender Rao addEntry(x509Name, "C", country); 270f4682712SMarri Devender Rao addEntry(x509Name, "emailAddress", email); 271f4682712SMarri Devender Rao addEntry(x509Name, "GN", givenName); 272f4682712SMarri Devender Rao addEntry(x509Name, "initials", initials); 273f4682712SMarri Devender Rao addEntry(x509Name, "algorithm", keyPairAlgorithm); 274f4682712SMarri Devender Rao if (!keyUsage.empty()) 275f4682712SMarri Devender Rao { 276f4682712SMarri Devender Rao for (auto& usage : keyUsage) 277f4682712SMarri Devender Rao { 2787641105dSMarri Devender Rao if (isExtendedKeyUsage(usage)) 2797641105dSMarri Devender Rao { 2807641105dSMarri Devender Rao addEntry(x509Name, "extendedKeyUsage", usage); 2817641105dSMarri Devender Rao } 2827641105dSMarri Devender Rao else 2837641105dSMarri Devender Rao { 284f4682712SMarri Devender Rao addEntry(x509Name, "keyUsage", usage); 285f4682712SMarri Devender Rao } 286f4682712SMarri Devender Rao } 2877641105dSMarri Devender Rao } 288f4682712SMarri Devender Rao addEntry(x509Name, "O", organization); 289f4682712SMarri Devender Rao addEntry(x509Name, "ST", state); 290f4682712SMarri Devender Rao addEntry(x509Name, "SN", surname); 291f4682712SMarri Devender Rao addEntry(x509Name, "unstructuredName", unstructuredName); 292f4682712SMarri Devender Rao 2938a09b52aSRamesh Iyyar EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free); 2948a09b52aSRamesh Iyyar 2958a09b52aSRamesh Iyyar log<level::INFO>("Given Key pair algorithm", 2968a09b52aSRamesh Iyyar entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str())); 2978a09b52aSRamesh Iyyar 2988a09b52aSRamesh Iyyar // Used EC algorithm as default if user did not give algorithm type. 2998a09b52aSRamesh Iyyar if (keyPairAlgorithm == "RSA") 300c6e58c7eSRamesh Iyyar pKey = getRSAKeyPair(keyBitLength); 3018a09b52aSRamesh Iyyar else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty())) 302c6e58c7eSRamesh Iyyar pKey = generateECKeyPair(keyCurveId); 3038a09b52aSRamesh Iyyar else 3048a09b52aSRamesh Iyyar { 3058a09b52aSRamesh Iyyar log<level::ERR>("Given Key pair algorithm is not supported. Supporting " 3068a09b52aSRamesh Iyyar "RSA and EC only"); 3078a09b52aSRamesh Iyyar elog<InvalidArgument>( 3088a09b52aSRamesh Iyyar Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"), 3098a09b52aSRamesh Iyyar Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str())); 3108a09b52aSRamesh Iyyar } 3118a09b52aSRamesh Iyyar 3128a09b52aSRamesh Iyyar ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get()); 3138a09b52aSRamesh Iyyar if (ret == 0) 3148a09b52aSRamesh Iyyar { 3158a09b52aSRamesh Iyyar log<level::ERR>("Error occured while setting Public key"); 3168a09b52aSRamesh Iyyar elog<InternalFailure>(); 3178a09b52aSRamesh Iyyar } 3188a09b52aSRamesh Iyyar 3198a09b52aSRamesh Iyyar // Write private key to file 320c6e58c7eSRamesh Iyyar writePrivateKey(pKey, PRIV_KEY_FILE_NAME); 321f4682712SMarri Devender Rao 322f4682712SMarri Devender Rao // set sign key of x509 req 323f4682712SMarri Devender Rao ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256()); 3248a09b52aSRamesh Iyyar if (ret == 0) 325f4682712SMarri Devender Rao { 326f4682712SMarri Devender Rao log<level::ERR>("Error occured while signing key of x509"); 327f4682712SMarri Devender Rao elog<InternalFailure>(); 328f4682712SMarri Devender Rao } 3298a09b52aSRamesh Iyyar 330f4682712SMarri Devender Rao log<level::INFO>("Writing CSR to file"); 331c6e58c7eSRamesh Iyyar fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME; 332c6e58c7eSRamesh Iyyar writeCSR(csrFilePath.string(), x509Req); 333f4682712SMarri Devender Rao } 334f4682712SMarri Devender Rao 3357641105dSMarri Devender Rao bool Manager::isExtendedKeyUsage(const std::string& usage) 3367641105dSMarri Devender Rao { 3377641105dSMarri Devender Rao const static std::array<const char*, 6> usageList = { 3387641105dSMarri Devender Rao "ServerAuthentication", "ClientAuthentication", "OCSPSigning", 3397641105dSMarri Devender Rao "Timestamping", "CodeSigning", "EmailProtection"}; 3407641105dSMarri Devender Rao auto it = std::find_if( 3417641105dSMarri Devender Rao usageList.begin(), usageList.end(), 3427641105dSMarri Devender Rao [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); }); 3437641105dSMarri Devender Rao return it != usageList.end(); 3447641105dSMarri Devender Rao } 3458a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength) 346f4682712SMarri Devender Rao { 347f4682712SMarri Devender Rao int ret = 0; 348f4682712SMarri Devender Rao // generate rsa key 349f4682712SMarri Devender Rao BIGNUM_Ptr bne(BN_new(), ::BN_free); 350f4682712SMarri Devender Rao ret = BN_set_word(bne.get(), RSA_F4); 351f4682712SMarri Devender Rao if (ret == 0) 352f4682712SMarri Devender Rao { 353f4682712SMarri Devender Rao log<level::ERR>("Error occured during BN_set_word call"); 354f4682712SMarri Devender Rao elog<InternalFailure>(); 355f4682712SMarri Devender Rao } 356f4682712SMarri Devender Rao 3578a09b52aSRamesh Iyyar int64_t keyBitLen = keyBitLength; 358f4682712SMarri Devender Rao // set keybit length to default value if not set 3598a09b52aSRamesh Iyyar if (keyBitLen <= 0) 360f4682712SMarri Devender Rao { 3618a09b52aSRamesh Iyyar constexpr auto DEFAULT_KEYBITLENGTH = 2048; 3628a09b52aSRamesh Iyyar log<level::INFO>( 3638a09b52aSRamesh Iyyar "KeyBitLength is not given.Hence, using default KeyBitLength", 3648a09b52aSRamesh Iyyar entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH)); 3658a09b52aSRamesh Iyyar keyBitLen = DEFAULT_KEYBITLENGTH; 366f4682712SMarri Devender Rao } 367f4682712SMarri Devender Rao RSA* rsa = RSA_new(); 3688a09b52aSRamesh Iyyar ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL); 369f4682712SMarri Devender Rao if (ret != 1) 370f4682712SMarri Devender Rao { 371f4682712SMarri Devender Rao free(rsa); 372f4682712SMarri Devender Rao log<level::ERR>("Error occured during RSA_generate_key_ex call", 3738a09b52aSRamesh Iyyar entry("KEYBITLENGTH=%PRIu64", keyBitLen)); 374f4682712SMarri Devender Rao elog<InternalFailure>(); 375f4682712SMarri Devender Rao } 376f4682712SMarri Devender Rao 377f4682712SMarri Devender Rao // set public key of x509 req 378f4682712SMarri Devender Rao EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 3798a09b52aSRamesh Iyyar ret = EVP_PKEY_assign_RSA(pKey.get(), rsa); 380f4682712SMarri Devender Rao if (ret == 0) 381f4682712SMarri Devender Rao { 3828a09b52aSRamesh Iyyar free(rsa); 3838a09b52aSRamesh Iyyar log<level::ERR>("Error occured during assign rsa key into EVP"); 384f4682712SMarri Devender Rao elog<InternalFailure>(); 385f4682712SMarri Devender Rao } 386f4682712SMarri Devender Rao 3878a09b52aSRamesh Iyyar return pKey; 3888a09b52aSRamesh Iyyar } 3898a09b52aSRamesh Iyyar 3908a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId) 3918a09b52aSRamesh Iyyar { 3928a09b52aSRamesh Iyyar std::string curId(curveId); 3938a09b52aSRamesh Iyyar 3948a09b52aSRamesh Iyyar if (curId.empty()) 3958a09b52aSRamesh Iyyar { 3968a09b52aSRamesh Iyyar // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349 3978a09b52aSRamesh Iyyar constexpr auto DEFAULT_KEYCURVEID = "secp224r1"; 3988a09b52aSRamesh Iyyar log<level::INFO>( 3998a09b52aSRamesh Iyyar "KeyCurveId is not given. Hence using default curve id", 4008a09b52aSRamesh Iyyar entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID)); 4018a09b52aSRamesh Iyyar curId = DEFAULT_KEYCURVEID; 4028a09b52aSRamesh Iyyar } 4038a09b52aSRamesh Iyyar 4048a09b52aSRamesh Iyyar int ecGrp = OBJ_txt2nid(curId.c_str()); 4058a09b52aSRamesh Iyyar 4068a09b52aSRamesh Iyyar if (ecGrp == NID_undef) 4078a09b52aSRamesh Iyyar { 4088a09b52aSRamesh Iyyar log<level::ERR>( 4098a09b52aSRamesh Iyyar "Error occured during convert the curve id string format into NID", 4108a09b52aSRamesh Iyyar entry("KEYCURVEID=%s", curId.c_str())); 4118a09b52aSRamesh Iyyar elog<InternalFailure>(); 4128a09b52aSRamesh Iyyar } 4138a09b52aSRamesh Iyyar 4148a09b52aSRamesh Iyyar EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp); 4158a09b52aSRamesh Iyyar 4168a09b52aSRamesh Iyyar if (ecKey == NULL) 4178a09b52aSRamesh Iyyar { 4188a09b52aSRamesh Iyyar log<level::ERR>( 4198a09b52aSRamesh Iyyar "Error occured during create the EC_Key object from NID", 4208a09b52aSRamesh Iyyar entry("ECGROUP=%d", ecGrp)); 4218a09b52aSRamesh Iyyar elog<InternalFailure>(); 4228a09b52aSRamesh Iyyar } 4238a09b52aSRamesh Iyyar 4248a09b52aSRamesh Iyyar // If you want to save a key and later load it with 4258a09b52aSRamesh Iyyar // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE 4268a09b52aSRamesh Iyyar // flag on the key. 4278a09b52aSRamesh Iyyar EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE); 4288a09b52aSRamesh Iyyar 4298a09b52aSRamesh Iyyar int ret = EC_KEY_generate_key(ecKey); 4308a09b52aSRamesh Iyyar 4318a09b52aSRamesh Iyyar if (ret == 0) 4328a09b52aSRamesh Iyyar { 4338a09b52aSRamesh Iyyar EC_KEY_free(ecKey); 4348a09b52aSRamesh Iyyar log<level::ERR>("Error occured during generate EC key"); 4358a09b52aSRamesh Iyyar elog<InternalFailure>(); 4368a09b52aSRamesh Iyyar } 4378a09b52aSRamesh Iyyar 4388a09b52aSRamesh Iyyar EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 4398a09b52aSRamesh Iyyar ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey); 4408a09b52aSRamesh Iyyar if (ret == 0) 4418a09b52aSRamesh Iyyar { 4428a09b52aSRamesh Iyyar EC_KEY_free(ecKey); 4438a09b52aSRamesh Iyyar log<level::ERR>("Error occured during assign EC Key into EVP"); 4448a09b52aSRamesh Iyyar elog<InternalFailure>(); 4458a09b52aSRamesh Iyyar } 4468a09b52aSRamesh Iyyar 4478a09b52aSRamesh Iyyar return pKey; 4488a09b52aSRamesh Iyyar } 4498a09b52aSRamesh Iyyar 450c6e58c7eSRamesh Iyyar void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey, 451c6e58c7eSRamesh Iyyar const std::string& privKeyFileName) 4528a09b52aSRamesh Iyyar { 4538a09b52aSRamesh Iyyar log<level::INFO>("Writing private key to file"); 454f4682712SMarri Devender Rao // write private key to file 455c6e58c7eSRamesh Iyyar fs::path privKeyPath = certParentInstallPath / privKeyFileName; 456f4682712SMarri Devender Rao 457f4682712SMarri Devender Rao FILE* fp = std::fopen(privKeyPath.c_str(), "w"); 458f4682712SMarri Devender Rao if (fp == NULL) 459f4682712SMarri Devender Rao { 460f4682712SMarri Devender Rao log<level::ERR>("Error occured creating private key file"); 461f4682712SMarri Devender Rao elog<InternalFailure>(); 462f4682712SMarri Devender Rao } 4638a09b52aSRamesh Iyyar int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL); 464f4682712SMarri Devender Rao std::fclose(fp); 465f4682712SMarri Devender Rao if (ret == 0) 466f4682712SMarri Devender Rao { 467f4682712SMarri Devender Rao log<level::ERR>("Error occured while writing private key to file"); 468f4682712SMarri Devender Rao elog<InternalFailure>(); 469f4682712SMarri Devender Rao } 470f4682712SMarri Devender Rao } 471f4682712SMarri Devender Rao 472f4682712SMarri Devender Rao void Manager::addEntry(X509_NAME* x509Name, const char* field, 473f4682712SMarri Devender Rao const std::string& bytes) 474f4682712SMarri Devender Rao { 475f4682712SMarri Devender Rao if (bytes.empty()) 476f4682712SMarri Devender Rao { 477f4682712SMarri Devender Rao return; 478f4682712SMarri Devender Rao } 479f4682712SMarri Devender Rao int ret = X509_NAME_add_entry_by_txt( 480f4682712SMarri Devender Rao x509Name, field, MBSTRING_ASC, 481f4682712SMarri Devender Rao reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0); 482f4682712SMarri Devender Rao if (ret != 1) 483f4682712SMarri Devender Rao { 484f4682712SMarri Devender Rao log<level::ERR>("Unable to set entry", entry("FIELD=%s", field), 485f4682712SMarri Devender Rao entry("VALUE=%s", bytes.c_str())); 486f4682712SMarri Devender Rao elog<InternalFailure>(); 487f4682712SMarri Devender Rao } 488f4682712SMarri Devender Rao } 489f4682712SMarri Devender Rao 490f4682712SMarri Devender Rao void Manager::createCSRObject(const Status& status) 491f4682712SMarri Devender Rao { 492f4682712SMarri Devender Rao if (csrPtr) 493f4682712SMarri Devender Rao { 494f4682712SMarri Devender Rao csrPtr.reset(nullptr); 495f4682712SMarri Devender Rao } 496f4682712SMarri Devender Rao auto csrObjectPath = objectPath + '/' + "csr"; 497f4682712SMarri Devender Rao csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(), 498f4682712SMarri Devender Rao certInstallPath.c_str(), status); 499f4682712SMarri Devender Rao } 500f4682712SMarri Devender Rao 501f4682712SMarri Devender Rao void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req) 502f4682712SMarri Devender Rao { 503f4682712SMarri Devender Rao if (fs::exists(filePath)) 504f4682712SMarri Devender Rao { 505f4682712SMarri Devender Rao log<level::INFO>("Removing the existing file", 506f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 507f4682712SMarri Devender Rao if (!fs::remove(filePath.c_str())) 508f4682712SMarri Devender Rao { 509f4682712SMarri Devender Rao log<level::ERR>("Unable to remove the file", 510f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 511f4682712SMarri Devender Rao elog<InternalFailure>(); 512f4682712SMarri Devender Rao } 513f4682712SMarri Devender Rao } 514f4682712SMarri Devender Rao 515f4682712SMarri Devender Rao FILE* fp = NULL; 516f4682712SMarri Devender Rao 517f4682712SMarri Devender Rao if ((fp = std::fopen(filePath.c_str(), "w")) == NULL) 518f4682712SMarri Devender Rao { 519f4682712SMarri Devender Rao log<level::ERR>("Error opening the file to write the CSR", 520f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 521f4682712SMarri Devender Rao elog<InternalFailure>(); 522f4682712SMarri Devender Rao } 523f4682712SMarri Devender Rao 524f4682712SMarri Devender Rao int rc = PEM_write_X509_REQ(fp, x509Req.get()); 525f4682712SMarri Devender Rao if (!rc) 526f4682712SMarri Devender Rao { 527f4682712SMarri Devender Rao log<level::ERR>("PEM write routine failed", 528f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 529f4682712SMarri Devender Rao std::fclose(fp); 530f4682712SMarri Devender Rao elog<InternalFailure>(); 531f4682712SMarri Devender Rao } 532f4682712SMarri Devender Rao std::fclose(fp); 533f4682712SMarri Devender Rao } 534f4682712SMarri Devender Rao 535*db029c95SKowalski, Kamil void Manager::createCertificates() 536*db029c95SKowalski, Kamil { 537*db029c95SKowalski, Kamil auto certObjectPath = objectPath + '/'; 538*db029c95SKowalski, Kamil 539*db029c95SKowalski, Kamil if (certType == phosphor::certs::AUTHORITY) 540*db029c95SKowalski, Kamil { 541*db029c95SKowalski, Kamil // Create directory 542*db029c95SKowalski, Kamil fs::create_directories(certInstallPath); 543*db029c95SKowalski, Kamil 544*db029c95SKowalski, Kamil // Check if above created proper path 545*db029c95SKowalski, Kamil if (!fs::is_directory(certInstallPath)) 546*db029c95SKowalski, Kamil { 547*db029c95SKowalski, Kamil log<level::ERR>("Certificate installation path exists and it is " 548*db029c95SKowalski, Kamil "not a directory"); 549*db029c95SKowalski, Kamil elog<InternalFailure>(); 550*db029c95SKowalski, Kamil return; 551*db029c95SKowalski, Kamil } 552*db029c95SKowalski, Kamil 553*db029c95SKowalski, Kamil for (auto& path : fs::directory_iterator(certInstallPath)) 554ffad1ef1SMarri Devender Rao { 555ffad1ef1SMarri Devender Rao try 556ffad1ef1SMarri Devender Rao { 557*db029c95SKowalski, Kamil installedCerts.emplace_back(std::make_unique<Certificate>( 558*db029c95SKowalski, Kamil bus, certObjectPath + std::to_string(certIdCounter++), 559*db029c95SKowalski, Kamil certType, unitToRestart, certInstallPath, path.path(), true, 560*db029c95SKowalski, Kamil certWatchPtr)); 561ffad1ef1SMarri Devender Rao } 562ffad1ef1SMarri Devender Rao catch (const InternalFailure& e) 563ffad1ef1SMarri Devender Rao { 564ffad1ef1SMarri Devender Rao report<InternalFailure>(); 565ffad1ef1SMarri Devender Rao } 566ffad1ef1SMarri Devender Rao catch (const InvalidCertificate& e) 567ffad1ef1SMarri Devender Rao { 568ffad1ef1SMarri Devender Rao report<InvalidCertificate>( 569ffad1ef1SMarri Devender Rao Reason("Existing certificate file is corrupted")); 570ffad1ef1SMarri Devender Rao } 571ffad1ef1SMarri Devender Rao } 572*db029c95SKowalski, Kamil } 573*db029c95SKowalski, Kamil else if (fs::exists(certInstallPath)) 574*db029c95SKowalski, Kamil { 575*db029c95SKowalski, Kamil try 576*db029c95SKowalski, Kamil { 577*db029c95SKowalski, Kamil installedCerts.emplace_back(std::make_unique<Certificate>( 578*db029c95SKowalski, Kamil bus, certObjectPath + '1', certType, unitToRestart, 579*db029c95SKowalski, Kamil certInstallPath, certInstallPath, true, certWatchPtr)); 580*db029c95SKowalski, Kamil } 581*db029c95SKowalski, Kamil catch (const InternalFailure& e) 582*db029c95SKowalski, Kamil { 583*db029c95SKowalski, Kamil report<InternalFailure>(); 584*db029c95SKowalski, Kamil } 585*db029c95SKowalski, Kamil catch (const InvalidCertificate& e) 586*db029c95SKowalski, Kamil { 587*db029c95SKowalski, Kamil report<InvalidCertificate>( 588*db029c95SKowalski, Kamil Reason("Existing certificate file is corrupted")); 589*db029c95SKowalski, Kamil } 590*db029c95SKowalski, Kamil } 591*db029c95SKowalski, Kamil } 592c6e58c7eSRamesh Iyyar 593c6e58c7eSRamesh Iyyar void Manager::createRSAPrivateKeyFile() 594c6e58c7eSRamesh Iyyar { 595c6e58c7eSRamesh Iyyar fs::path rsaPrivateKeyFileName = 596c6e58c7eSRamesh Iyyar certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 597c6e58c7eSRamesh Iyyar 598c6e58c7eSRamesh Iyyar try 599c6e58c7eSRamesh Iyyar { 600c6e58c7eSRamesh Iyyar if (!fs::exists(rsaPrivateKeyFileName)) 601c6e58c7eSRamesh Iyyar { 602c6e58c7eSRamesh Iyyar writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH), 603c6e58c7eSRamesh Iyyar RSA_PRIV_KEY_FILE_NAME); 604c6e58c7eSRamesh Iyyar } 605c6e58c7eSRamesh Iyyar } 606c6e58c7eSRamesh Iyyar catch (const InternalFailure& e) 607c6e58c7eSRamesh Iyyar { 608c6e58c7eSRamesh Iyyar report<InternalFailure>(); 609c6e58c7eSRamesh Iyyar } 610c6e58c7eSRamesh Iyyar } 611c6e58c7eSRamesh Iyyar 612c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength) 613c6e58c7eSRamesh Iyyar { 614c6e58c7eSRamesh Iyyar if (keyBitLength != SUPPORTED_KEYBITLENGTH) 615c6e58c7eSRamesh Iyyar { 616c6e58c7eSRamesh Iyyar log<level::ERR>( 617c6e58c7eSRamesh Iyyar "Given Key bit length is not supported", 618c6e58c7eSRamesh Iyyar entry("GIVENKEYBITLENGTH=%d", keyBitLength), 619c6e58c7eSRamesh Iyyar entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH)); 620c6e58c7eSRamesh Iyyar elog<InvalidArgument>( 621c6e58c7eSRamesh Iyyar Argument::ARGUMENT_NAME("KEYBITLENGTH"), 622c6e58c7eSRamesh Iyyar Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str())); 623c6e58c7eSRamesh Iyyar } 624c6e58c7eSRamesh Iyyar fs::path rsaPrivateKeyFileName = 625c6e58c7eSRamesh Iyyar certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 626c6e58c7eSRamesh Iyyar 627c6e58c7eSRamesh Iyyar FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r"); 628c6e58c7eSRamesh Iyyar if (!privateKeyFile) 629c6e58c7eSRamesh Iyyar { 630c6e58c7eSRamesh Iyyar log<level::ERR>("Unable to open RSA private key file to read", 631c6e58c7eSRamesh Iyyar entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()), 632c6e58c7eSRamesh Iyyar entry("ERRORREASON=%s", strerror(errno))); 633c6e58c7eSRamesh Iyyar elog<InternalFailure>(); 634c6e58c7eSRamesh Iyyar } 635c6e58c7eSRamesh Iyyar 636c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr privateKey( 637c6e58c7eSRamesh Iyyar PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr), 638c6e58c7eSRamesh Iyyar ::EVP_PKEY_free); 639c6e58c7eSRamesh Iyyar std::fclose(privateKeyFile); 640c6e58c7eSRamesh Iyyar 641c6e58c7eSRamesh Iyyar if (!privateKey) 642c6e58c7eSRamesh Iyyar { 643c6e58c7eSRamesh Iyyar log<level::ERR>("Error occured during PEM_read_PrivateKey call"); 644c6e58c7eSRamesh Iyyar elog<InternalFailure>(); 645c6e58c7eSRamesh Iyyar } 646c6e58c7eSRamesh Iyyar return privateKey; 647c6e58c7eSRamesh Iyyar } 648cfbc8dc8SJayanth Othayoth } // namespace certs 649cfbc8dc8SJayanth Othayoth } // namespace phosphor 650