1cfbc8dc8SJayanth Othayoth #include "certs_manager.hpp" 2cfbc8dc8SJayanth Othayoth 3f4682712SMarri Devender Rao #include <openssl/pem.h> 4f4682712SMarri Devender Rao #include <unistd.h> 5f4682712SMarri Devender Rao 6a3bb38fbSZbigniew Kurzynski #include <algorithm> 76ceec40bSMarri Devender Rao #include <phosphor-logging/elog-errors.hpp> 813bf74e4SMarri Devender Rao #include <xyz/openbmc_project/Certs/error.hpp> 9cfbc8dc8SJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp> 10cfbc8dc8SJayanth Othayoth namespace phosphor 11cfbc8dc8SJayanth Othayoth { 12cfbc8dc8SJayanth Othayoth namespace certs 13cfbc8dc8SJayanth Othayoth { 141396511dSMarri Devender Rao using InternalFailure = 151396511dSMarri Devender Rao sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 16ffad1ef1SMarri Devender Rao using InvalidCertificate = 17ffad1ef1SMarri Devender Rao sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate; 18ffad1ef1SMarri Devender Rao using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON; 19cfbc8dc8SJayanth Othayoth 20f4682712SMarri Devender Rao using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>; 21f4682712SMarri Devender Rao using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>; 22c6e58c7eSRamesh Iyyar using InvalidArgument = 23c6e58c7eSRamesh Iyyar sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument; 24c6e58c7eSRamesh Iyyar using Argument = xyz::openbmc_project::Common::InvalidArgument; 25c6e58c7eSRamesh Iyyar 26c6e58c7eSRamesh Iyyar constexpr auto SUPPORTED_KEYBITLENGTH = 2048; 27f4682712SMarri Devender Rao 28f4682712SMarri Devender Rao Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event, 29f4682712SMarri Devender Rao const char* path, const CertificateType& type, 30f4682712SMarri Devender Rao UnitsToRestart&& unit, CertInstallPath&& installPath) : 316ceec40bSMarri Devender Rao Ifaces(bus, path), 32f4682712SMarri Devender Rao bus(bus), event(event), objectPath(path), certType(type), 33c6e58c7eSRamesh Iyyar unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)), 34c6e58c7eSRamesh Iyyar certParentInstallPath(fs::path(certInstallPath).parent_path()) 35cfbc8dc8SJayanth Othayoth { 36*fe590c4eSZbigniew Lukwinski // Create certificate directory if not existing. 37*fe590c4eSZbigniew Lukwinski // Set correct certificate directory permitions. 38*fe590c4eSZbigniew Lukwinski fs::path certDirectory; 39b57d75e2SMarri Devender Rao try 40b57d75e2SMarri Devender Rao { 41*fe590c4eSZbigniew Lukwinski if (certType == AUTHORITY) 42b57d75e2SMarri Devender Rao { 43*fe590c4eSZbigniew Lukwinski certDirectory = certInstallPath; 44b57d75e2SMarri Devender Rao } 45*fe590c4eSZbigniew Lukwinski else 46*fe590c4eSZbigniew Lukwinski { 47*fe590c4eSZbigniew Lukwinski certDirectory = certParentInstallPath; 48*fe590c4eSZbigniew Lukwinski } 49*fe590c4eSZbigniew Lukwinski 50*fe590c4eSZbigniew Lukwinski if (!fs::exists(certDirectory)) 51*fe590c4eSZbigniew Lukwinski { 52*fe590c4eSZbigniew Lukwinski fs::create_directories(certDirectory); 53*fe590c4eSZbigniew Lukwinski } 54*fe590c4eSZbigniew Lukwinski 55667286e4SMarri Devender Rao auto permission = fs::perms::owner_read | fs::perms::owner_write | 56667286e4SMarri Devender Rao fs::perms::owner_exec; 57*fe590c4eSZbigniew Lukwinski fs::permissions(certDirectory, permission, fs::perm_options::replace); 58b57d75e2SMarri Devender Rao } 59b57d75e2SMarri Devender Rao catch (fs::filesystem_error& e) 60b57d75e2SMarri Devender Rao { 61b57d75e2SMarri Devender Rao log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()), 62b57d75e2SMarri Devender Rao entry("DIRECTORY=%s", certParentInstallPath.c_str())); 63b57d75e2SMarri Devender Rao report<InternalFailure>(); 64b57d75e2SMarri Devender Rao } 65b57d75e2SMarri Devender Rao 66c6e58c7eSRamesh Iyyar // Generating RSA private key file if certificate type is server/client 67c6e58c7eSRamesh Iyyar if (certType != AUTHORITY) 68c6e58c7eSRamesh Iyyar { 69c6e58c7eSRamesh Iyyar createRSAPrivateKeyFile(); 70c6e58c7eSRamesh Iyyar } 71c6e58c7eSRamesh Iyyar 72ffad1ef1SMarri Devender Rao // restore any existing certificates 73db029c95SKowalski, Kamil createCertificates(); 74ffad1ef1SMarri Devender Rao 75ffad1ef1SMarri Devender Rao // watch is not required for authority certificates 76ffad1ef1SMarri Devender Rao if (certType != AUTHORITY) 77ffad1ef1SMarri Devender Rao { 78ffad1ef1SMarri Devender Rao // watch for certificate file create/replace 79ffad1ef1SMarri Devender Rao certWatchPtr = std::make_unique< 80ffad1ef1SMarri Devender Rao Watch>(event, certInstallPath, [this]() { 81bf7c588cSMarri Devender Rao try 82bf7c588cSMarri Devender Rao { 83ffad1ef1SMarri Devender Rao // if certificate file existing update it 84db029c95SKowalski, Kamil if (!installedCerts.empty()) 85ffad1ef1SMarri Devender Rao { 86ffad1ef1SMarri Devender Rao log<level::INFO>( 87ffad1ef1SMarri Devender Rao "Inotify callback to update certificate properties"); 88db029c95SKowalski, Kamil installedCerts[0]->populateProperties(); 89ffad1ef1SMarri Devender Rao } 90ffad1ef1SMarri Devender Rao else 91ffad1ef1SMarri Devender Rao { 92ffad1ef1SMarri Devender Rao log<level::INFO>( 93ffad1ef1SMarri Devender Rao "Inotify callback to create certificate object"); 94db029c95SKowalski, Kamil createCertificates(); 95ffad1ef1SMarri Devender Rao } 96bf7c588cSMarri Devender Rao } 97bf7c588cSMarri Devender Rao catch (const InternalFailure& e) 98bf7c588cSMarri Devender Rao { 99ffad1ef1SMarri Devender Rao commit<InternalFailure>(); 100bf7c588cSMarri Devender Rao } 101bf7c588cSMarri Devender Rao catch (const InvalidCertificate& e) 102bf7c588cSMarri Devender Rao { 103ffad1ef1SMarri Devender Rao commit<InvalidCertificate>(); 104bf7c588cSMarri Devender Rao } 105ffad1ef1SMarri Devender Rao }); 106bf7c588cSMarri Devender Rao } 107db029c95SKowalski, Kamil else 108db029c95SKowalski, Kamil { 109db029c95SKowalski, Kamil const std::string signleCertPath = "/etc/ssl/certs/Root-CA.pem"; 110db029c95SKowalski, Kamil if (fs::exists(signleCertPath) && !fs::is_empty(signleCertPath)) 111db029c95SKowalski, Kamil { 112db029c95SKowalski, Kamil log<level::NOTICE>( 113db029c95SKowalski, Kamil "Legacy certificate detected, will be installed from: ", 114db029c95SKowalski, Kamil entry("SINGLE_CERTPATH=%s", signleCertPath.c_str())); 115db029c95SKowalski, Kamil install(signleCertPath); 116db029c95SKowalski, Kamil if (!fs::remove(signleCertPath)) 117db029c95SKowalski, Kamil { 118db029c95SKowalski, Kamil log<level::ERR>( 119db029c95SKowalski, Kamil "Unable to remove old certificate from: ", 120db029c95SKowalski, Kamil entry("SINGLE_CERTPATH=%s", signleCertPath.c_str())); 121db029c95SKowalski, Kamil elog<InternalFailure>(); 122db029c95SKowalski, Kamil } 123db029c95SKowalski, Kamil } 124db029c95SKowalski, Kamil } 125dd74bd20SJayanth Othayoth } 126589159f2SJayanth Othayoth 12706a69d7bSZbigniew Kurzynski std::string Manager::install(const std::string filePath) 128cfbc8dc8SJayanth Othayoth { 1291396511dSMarri Devender Rao using NotAllowed = 1301396511dSMarri Devender Rao sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed; 1311396511dSMarri Devender Rao using Reason = xyz::openbmc_project::Common::NotAllowed::REASON; 132db029c95SKowalski, Kamil 133db029c95SKowalski, Kamil if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty()) 1341396511dSMarri Devender Rao { 1351396511dSMarri Devender Rao elog<NotAllowed>(Reason("Certificate already exist")); 1361396511dSMarri Devender Rao } 1373b07b77aSZbigniew Lukwinski else if (certType == phosphor::certs::AUTHORITY && 1383b07b77aSZbigniew Lukwinski installedCerts.size() >= AUTHORITY_CERTIFICATES_LIMIT) 1393b07b77aSZbigniew Lukwinski { 1403b07b77aSZbigniew Lukwinski elog<NotAllowed>(Reason("Certificates limit reached")); 1413b07b77aSZbigniew Lukwinski } 142ffad1ef1SMarri Devender Rao 143db029c95SKowalski, Kamil auto certObjectPath = objectPath + '/' + std::to_string(certIdCounter++); 144db029c95SKowalski, Kamil 145db029c95SKowalski, Kamil installedCerts.emplace_back(std::make_unique<Certificate>( 1468f80c35bSMarri Devender Rao bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath, 147a3bb38fbSZbigniew Kurzynski false, certWatchPtr, *this)); 14806a69d7bSZbigniew Kurzynski return certObjectPath; 149589159f2SJayanth Othayoth } 150ae70b3daSDeepak Kodihalli 151a3bb38fbSZbigniew Kurzynski void Manager::deleteAll() 152ae70b3daSDeepak Kodihalli { 1536ceec40bSMarri Devender Rao // TODO: #Issue 4 when a certificate is deleted system auto generates 1546ceec40bSMarri Devender Rao // certificate file. At present we are not supporting creation of 1556ceec40bSMarri Devender Rao // certificate object for the auto-generated certificate file as 1566ceec40bSMarri Devender Rao // deletion if only applicable for REST server and Bmcweb does not allow 1576ceec40bSMarri Devender Rao // deletion of certificates 158db029c95SKowalski, Kamil installedCerts.clear(); 159ae70b3daSDeepak Kodihalli } 160f4682712SMarri Devender Rao 161a3bb38fbSZbigniew Kurzynski void Manager::deleteCertificate(const std::string& certHash) 162a3bb38fbSZbigniew Kurzynski { 163a3bb38fbSZbigniew Kurzynski std::vector<std::unique_ptr<Certificate>>::iterator const& certIt = 164a3bb38fbSZbigniew Kurzynski std::find_if(installedCerts.begin(), installedCerts.end(), 165a3bb38fbSZbigniew Kurzynski [certHash](std::unique_ptr<Certificate> const& cert) { 166a3bb38fbSZbigniew Kurzynski return cert->getHash().compare(certHash) == 0; 167a3bb38fbSZbigniew Kurzynski }); 168a3bb38fbSZbigniew Kurzynski if (certIt != installedCerts.end()) 169a3bb38fbSZbigniew Kurzynski { 170a3bb38fbSZbigniew Kurzynski installedCerts.erase(certIt); 171a3bb38fbSZbigniew Kurzynski } 172a3bb38fbSZbigniew Kurzynski else 173a3bb38fbSZbigniew Kurzynski { 174a3bb38fbSZbigniew Kurzynski log<level::ERR>("Certificate does not exist", 175a3bb38fbSZbigniew Kurzynski entry("HASH=%s", certHash.c_str())); 176a3bb38fbSZbigniew Kurzynski elog<InternalFailure>(); 177a3bb38fbSZbigniew Kurzynski } 178a3bb38fbSZbigniew Kurzynski } 179a3bb38fbSZbigniew Kurzynski 180f4682712SMarri Devender Rao std::string Manager::generateCSR( 181f4682712SMarri Devender Rao std::vector<std::string> alternativeNames, std::string challengePassword, 182f4682712SMarri Devender Rao std::string city, std::string commonName, std::string contactPerson, 183f4682712SMarri Devender Rao std::string country, std::string email, std::string givenName, 184f4682712SMarri Devender Rao std::string initials, int64_t keyBitLength, std::string keyCurveId, 185f4682712SMarri Devender Rao std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 186f4682712SMarri Devender Rao std::string organization, std::string organizationalUnit, std::string state, 187f4682712SMarri Devender Rao std::string surname, std::string unstructuredName) 188f4682712SMarri Devender Rao { 189f4682712SMarri Devender Rao // We support only one CSR. 190f4682712SMarri Devender Rao csrPtr.reset(nullptr); 191f4682712SMarri Devender Rao auto pid = fork(); 192f4682712SMarri Devender Rao if (pid == -1) 193f4682712SMarri Devender Rao { 194f4682712SMarri Devender Rao log<level::ERR>("Error occurred during forking process"); 195f4682712SMarri Devender Rao report<InternalFailure>(); 196f4682712SMarri Devender Rao } 197f4682712SMarri Devender Rao else if (pid == 0) 198f4682712SMarri Devender Rao { 199f4682712SMarri Devender Rao try 200f4682712SMarri Devender Rao { 201f4682712SMarri Devender Rao generateCSRHelper(alternativeNames, challengePassword, city, 202f4682712SMarri Devender Rao commonName, contactPerson, country, email, 203f4682712SMarri Devender Rao givenName, initials, keyBitLength, keyCurveId, 204f4682712SMarri Devender Rao keyPairAlgorithm, keyUsage, organization, 205f4682712SMarri Devender Rao organizationalUnit, state, surname, 206f4682712SMarri Devender Rao unstructuredName); 207f4682712SMarri Devender Rao exit(EXIT_SUCCESS); 208f4682712SMarri Devender Rao } 209f4682712SMarri Devender Rao catch (const InternalFailure& e) 210f4682712SMarri Devender Rao { 211f4682712SMarri Devender Rao // commit the error reported in child process and exit 212f4682712SMarri Devender Rao // Callback method from SDEvent Loop looks for exit status 213f4682712SMarri Devender Rao exit(EXIT_FAILURE); 214f4682712SMarri Devender Rao commit<InternalFailure>(); 215f4682712SMarri Devender Rao } 216f4682712SMarri Devender Rao } 217f4682712SMarri Devender Rao else 218f4682712SMarri Devender Rao { 219f4682712SMarri Devender Rao using namespace sdeventplus::source; 220f4682712SMarri Devender Rao Child::Callback callback = [this](Child& eventSource, 221f4682712SMarri Devender Rao const siginfo_t* si) { 222f4682712SMarri Devender Rao eventSource.set_enabled(Enabled::On); 223f4682712SMarri Devender Rao if (si->si_status != 0) 224f4682712SMarri Devender Rao { 225f4682712SMarri Devender Rao this->createCSRObject(Status::FAILURE); 226f4682712SMarri Devender Rao } 227f4682712SMarri Devender Rao else 228f4682712SMarri Devender Rao { 229f4682712SMarri Devender Rao this->createCSRObject(Status::SUCCESS); 230f4682712SMarri Devender Rao } 231f4682712SMarri Devender Rao }; 232f4682712SMarri Devender Rao try 233f4682712SMarri Devender Rao { 234f4682712SMarri Devender Rao sigset_t ss; 235f4682712SMarri Devender Rao if (sigemptyset(&ss) < 0) 236f4682712SMarri Devender Rao { 237f4682712SMarri Devender Rao log<level::ERR>("Unable to initialize signal set"); 238f4682712SMarri Devender Rao elog<InternalFailure>(); 239f4682712SMarri Devender Rao } 240f4682712SMarri Devender Rao if (sigaddset(&ss, SIGCHLD) < 0) 241f4682712SMarri Devender Rao { 242f4682712SMarri Devender Rao log<level::ERR>("Unable to add signal to signal set"); 243f4682712SMarri Devender Rao elog<InternalFailure>(); 244f4682712SMarri Devender Rao } 245f4682712SMarri Devender Rao 246f4682712SMarri Devender Rao // Block SIGCHLD first, so that the event loop can handle it 247f4682712SMarri Devender Rao if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0) 248f4682712SMarri Devender Rao { 249f4682712SMarri Devender Rao log<level::ERR>("Unable to block signal"); 250f4682712SMarri Devender Rao elog<InternalFailure>(); 251f4682712SMarri Devender Rao } 252f4682712SMarri Devender Rao if (childPtr) 253f4682712SMarri Devender Rao { 254f4682712SMarri Devender Rao childPtr.reset(); 255f4682712SMarri Devender Rao } 256f4682712SMarri Devender Rao childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED, 257f4682712SMarri Devender Rao std::move(callback)); 258f4682712SMarri Devender Rao } 259f4682712SMarri Devender Rao catch (const InternalFailure& e) 260f4682712SMarri Devender Rao { 261f4682712SMarri Devender Rao commit<InternalFailure>(); 262f4682712SMarri Devender Rao } 263f4682712SMarri Devender Rao } 264f4682712SMarri Devender Rao auto csrObjectPath = objectPath + '/' + "csr"; 265f4682712SMarri Devender Rao return csrObjectPath; 266f4682712SMarri Devender Rao } 267f4682712SMarri Devender Rao 268db029c95SKowalski, Kamil std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates() 269ffad1ef1SMarri Devender Rao { 270db029c95SKowalski, Kamil return installedCerts; 271ffad1ef1SMarri Devender Rao } 272ffad1ef1SMarri Devender Rao 273f4682712SMarri Devender Rao void Manager::generateCSRHelper( 274f4682712SMarri Devender Rao std::vector<std::string> alternativeNames, std::string challengePassword, 275f4682712SMarri Devender Rao std::string city, std::string commonName, std::string contactPerson, 276f4682712SMarri Devender Rao std::string country, std::string email, std::string givenName, 277f4682712SMarri Devender Rao std::string initials, int64_t keyBitLength, std::string keyCurveId, 278f4682712SMarri Devender Rao std::string keyPairAlgorithm, std::vector<std::string> keyUsage, 279f4682712SMarri Devender Rao std::string organization, std::string organizationalUnit, std::string state, 280f4682712SMarri Devender Rao std::string surname, std::string unstructuredName) 281f4682712SMarri Devender Rao { 282f4682712SMarri Devender Rao int ret = 0; 283f4682712SMarri Devender Rao 284f4682712SMarri Devender Rao // set version of x509 req 285f4682712SMarri Devender Rao int nVersion = 1; 286f4682712SMarri Devender Rao // TODO: Issue#6 need to make version number configurable 287f4682712SMarri Devender Rao X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free); 288f4682712SMarri Devender Rao ret = X509_REQ_set_version(x509Req.get(), nVersion); 289f4682712SMarri Devender Rao if (ret == 0) 290f4682712SMarri Devender Rao { 291f4682712SMarri Devender Rao log<level::ERR>("Error occured during X509_REQ_set_version call"); 292f4682712SMarri Devender Rao elog<InternalFailure>(); 293f4682712SMarri Devender Rao } 294f4682712SMarri Devender Rao 295f4682712SMarri Devender Rao // set subject of x509 req 296f4682712SMarri Devender Rao X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get()); 297f4682712SMarri Devender Rao 298f4682712SMarri Devender Rao if (!alternativeNames.empty()) 299f4682712SMarri Devender Rao { 300f4682712SMarri Devender Rao for (auto& name : alternativeNames) 301f4682712SMarri Devender Rao { 302f4682712SMarri Devender Rao addEntry(x509Name, "subjectAltName", name); 303f4682712SMarri Devender Rao } 304f4682712SMarri Devender Rao } 305f4682712SMarri Devender Rao addEntry(x509Name, "challengePassword", challengePassword); 306f4682712SMarri Devender Rao addEntry(x509Name, "L", city); 307f4682712SMarri Devender Rao addEntry(x509Name, "CN", commonName); 308f4682712SMarri Devender Rao addEntry(x509Name, "name", contactPerson); 309f4682712SMarri Devender Rao addEntry(x509Name, "C", country); 310f4682712SMarri Devender Rao addEntry(x509Name, "emailAddress", email); 311f4682712SMarri Devender Rao addEntry(x509Name, "GN", givenName); 312f4682712SMarri Devender Rao addEntry(x509Name, "initials", initials); 313f4682712SMarri Devender Rao addEntry(x509Name, "algorithm", keyPairAlgorithm); 314f4682712SMarri Devender Rao if (!keyUsage.empty()) 315f4682712SMarri Devender Rao { 316f4682712SMarri Devender Rao for (auto& usage : keyUsage) 317f4682712SMarri Devender Rao { 3187641105dSMarri Devender Rao if (isExtendedKeyUsage(usage)) 3197641105dSMarri Devender Rao { 3207641105dSMarri Devender Rao addEntry(x509Name, "extendedKeyUsage", usage); 3217641105dSMarri Devender Rao } 3227641105dSMarri Devender Rao else 3237641105dSMarri Devender Rao { 324f4682712SMarri Devender Rao addEntry(x509Name, "keyUsage", usage); 325f4682712SMarri Devender Rao } 326f4682712SMarri Devender Rao } 3277641105dSMarri Devender Rao } 328f4682712SMarri Devender Rao addEntry(x509Name, "O", organization); 329f4682712SMarri Devender Rao addEntry(x509Name, "ST", state); 330f4682712SMarri Devender Rao addEntry(x509Name, "SN", surname); 331f4682712SMarri Devender Rao addEntry(x509Name, "unstructuredName", unstructuredName); 332f4682712SMarri Devender Rao 3338a09b52aSRamesh Iyyar EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free); 3348a09b52aSRamesh Iyyar 3358a09b52aSRamesh Iyyar log<level::INFO>("Given Key pair algorithm", 3368a09b52aSRamesh Iyyar entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str())); 3378a09b52aSRamesh Iyyar 3388a09b52aSRamesh Iyyar // Used EC algorithm as default if user did not give algorithm type. 3398a09b52aSRamesh Iyyar if (keyPairAlgorithm == "RSA") 340c6e58c7eSRamesh Iyyar pKey = getRSAKeyPair(keyBitLength); 3418a09b52aSRamesh Iyyar else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty())) 342c6e58c7eSRamesh Iyyar pKey = generateECKeyPair(keyCurveId); 3438a09b52aSRamesh Iyyar else 3448a09b52aSRamesh Iyyar { 3458a09b52aSRamesh Iyyar log<level::ERR>("Given Key pair algorithm is not supported. Supporting " 3468a09b52aSRamesh Iyyar "RSA and EC only"); 3478a09b52aSRamesh Iyyar elog<InvalidArgument>( 3488a09b52aSRamesh Iyyar Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"), 3498a09b52aSRamesh Iyyar Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str())); 3508a09b52aSRamesh Iyyar } 3518a09b52aSRamesh Iyyar 3528a09b52aSRamesh Iyyar ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get()); 3538a09b52aSRamesh Iyyar if (ret == 0) 3548a09b52aSRamesh Iyyar { 3558a09b52aSRamesh Iyyar log<level::ERR>("Error occured while setting Public key"); 3568a09b52aSRamesh Iyyar elog<InternalFailure>(); 3578a09b52aSRamesh Iyyar } 3588a09b52aSRamesh Iyyar 3598a09b52aSRamesh Iyyar // Write private key to file 360c6e58c7eSRamesh Iyyar writePrivateKey(pKey, PRIV_KEY_FILE_NAME); 361f4682712SMarri Devender Rao 362f4682712SMarri Devender Rao // set sign key of x509 req 363f4682712SMarri Devender Rao ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256()); 3648a09b52aSRamesh Iyyar if (ret == 0) 365f4682712SMarri Devender Rao { 366f4682712SMarri Devender Rao log<level::ERR>("Error occured while signing key of x509"); 367f4682712SMarri Devender Rao elog<InternalFailure>(); 368f4682712SMarri Devender Rao } 3698a09b52aSRamesh Iyyar 370f4682712SMarri Devender Rao log<level::INFO>("Writing CSR to file"); 371c6e58c7eSRamesh Iyyar fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME; 372c6e58c7eSRamesh Iyyar writeCSR(csrFilePath.string(), x509Req); 373f4682712SMarri Devender Rao } 374f4682712SMarri Devender Rao 3757641105dSMarri Devender Rao bool Manager::isExtendedKeyUsage(const std::string& usage) 3767641105dSMarri Devender Rao { 3777641105dSMarri Devender Rao const static std::array<const char*, 6> usageList = { 3787641105dSMarri Devender Rao "ServerAuthentication", "ClientAuthentication", "OCSPSigning", 3797641105dSMarri Devender Rao "Timestamping", "CodeSigning", "EmailProtection"}; 3807641105dSMarri Devender Rao auto it = std::find_if( 3817641105dSMarri Devender Rao usageList.begin(), usageList.end(), 3827641105dSMarri Devender Rao [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); }); 3837641105dSMarri Devender Rao return it != usageList.end(); 3847641105dSMarri Devender Rao } 3858a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength) 386f4682712SMarri Devender Rao { 387f4682712SMarri Devender Rao int ret = 0; 388f4682712SMarri Devender Rao // generate rsa key 389f4682712SMarri Devender Rao BIGNUM_Ptr bne(BN_new(), ::BN_free); 390f4682712SMarri Devender Rao ret = BN_set_word(bne.get(), RSA_F4); 391f4682712SMarri Devender Rao if (ret == 0) 392f4682712SMarri Devender Rao { 393f4682712SMarri Devender Rao log<level::ERR>("Error occured during BN_set_word call"); 394f4682712SMarri Devender Rao elog<InternalFailure>(); 395f4682712SMarri Devender Rao } 396f4682712SMarri Devender Rao 3978a09b52aSRamesh Iyyar int64_t keyBitLen = keyBitLength; 398f4682712SMarri Devender Rao // set keybit length to default value if not set 3998a09b52aSRamesh Iyyar if (keyBitLen <= 0) 400f4682712SMarri Devender Rao { 4018a09b52aSRamesh Iyyar constexpr auto DEFAULT_KEYBITLENGTH = 2048; 4028a09b52aSRamesh Iyyar log<level::INFO>( 4038a09b52aSRamesh Iyyar "KeyBitLength is not given.Hence, using default KeyBitLength", 4048a09b52aSRamesh Iyyar entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH)); 4058a09b52aSRamesh Iyyar keyBitLen = DEFAULT_KEYBITLENGTH; 406f4682712SMarri Devender Rao } 407f4682712SMarri Devender Rao RSA* rsa = RSA_new(); 4088a09b52aSRamesh Iyyar ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL); 409f4682712SMarri Devender Rao if (ret != 1) 410f4682712SMarri Devender Rao { 411f4682712SMarri Devender Rao free(rsa); 412f4682712SMarri Devender Rao log<level::ERR>("Error occured during RSA_generate_key_ex call", 4138a09b52aSRamesh Iyyar entry("KEYBITLENGTH=%PRIu64", keyBitLen)); 414f4682712SMarri Devender Rao elog<InternalFailure>(); 415f4682712SMarri Devender Rao } 416f4682712SMarri Devender Rao 417f4682712SMarri Devender Rao // set public key of x509 req 418f4682712SMarri Devender Rao EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 4198a09b52aSRamesh Iyyar ret = EVP_PKEY_assign_RSA(pKey.get(), rsa); 420f4682712SMarri Devender Rao if (ret == 0) 421f4682712SMarri Devender Rao { 4228a09b52aSRamesh Iyyar free(rsa); 4238a09b52aSRamesh Iyyar log<level::ERR>("Error occured during assign rsa key into EVP"); 424f4682712SMarri Devender Rao elog<InternalFailure>(); 425f4682712SMarri Devender Rao } 426f4682712SMarri Devender Rao 4278a09b52aSRamesh Iyyar return pKey; 4288a09b52aSRamesh Iyyar } 4298a09b52aSRamesh Iyyar 4308a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId) 4318a09b52aSRamesh Iyyar { 4328a09b52aSRamesh Iyyar std::string curId(curveId); 4338a09b52aSRamesh Iyyar 4348a09b52aSRamesh Iyyar if (curId.empty()) 4358a09b52aSRamesh Iyyar { 4368a09b52aSRamesh Iyyar // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349 4378a09b52aSRamesh Iyyar constexpr auto DEFAULT_KEYCURVEID = "secp224r1"; 4388a09b52aSRamesh Iyyar log<level::INFO>( 4398a09b52aSRamesh Iyyar "KeyCurveId is not given. Hence using default curve id", 4408a09b52aSRamesh Iyyar entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID)); 4418a09b52aSRamesh Iyyar curId = DEFAULT_KEYCURVEID; 4428a09b52aSRamesh Iyyar } 4438a09b52aSRamesh Iyyar 4448a09b52aSRamesh Iyyar int ecGrp = OBJ_txt2nid(curId.c_str()); 4458a09b52aSRamesh Iyyar 4468a09b52aSRamesh Iyyar if (ecGrp == NID_undef) 4478a09b52aSRamesh Iyyar { 4488a09b52aSRamesh Iyyar log<level::ERR>( 4498a09b52aSRamesh Iyyar "Error occured during convert the curve id string format into NID", 4508a09b52aSRamesh Iyyar entry("KEYCURVEID=%s", curId.c_str())); 4518a09b52aSRamesh Iyyar elog<InternalFailure>(); 4528a09b52aSRamesh Iyyar } 4538a09b52aSRamesh Iyyar 4548a09b52aSRamesh Iyyar EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp); 4558a09b52aSRamesh Iyyar 4568a09b52aSRamesh Iyyar if (ecKey == NULL) 4578a09b52aSRamesh Iyyar { 4588a09b52aSRamesh Iyyar log<level::ERR>( 4598a09b52aSRamesh Iyyar "Error occured during create the EC_Key object from NID", 4608a09b52aSRamesh Iyyar entry("ECGROUP=%d", ecGrp)); 4618a09b52aSRamesh Iyyar elog<InternalFailure>(); 4628a09b52aSRamesh Iyyar } 4638a09b52aSRamesh Iyyar 4648a09b52aSRamesh Iyyar // If you want to save a key and later load it with 4658a09b52aSRamesh Iyyar // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE 4668a09b52aSRamesh Iyyar // flag on the key. 4678a09b52aSRamesh Iyyar EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE); 4688a09b52aSRamesh Iyyar 4698a09b52aSRamesh Iyyar int ret = EC_KEY_generate_key(ecKey); 4708a09b52aSRamesh Iyyar 4718a09b52aSRamesh Iyyar if (ret == 0) 4728a09b52aSRamesh Iyyar { 4738a09b52aSRamesh Iyyar EC_KEY_free(ecKey); 4748a09b52aSRamesh Iyyar log<level::ERR>("Error occured during generate EC key"); 4758a09b52aSRamesh Iyyar elog<InternalFailure>(); 4768a09b52aSRamesh Iyyar } 4778a09b52aSRamesh Iyyar 4788a09b52aSRamesh Iyyar EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free); 4798a09b52aSRamesh Iyyar ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey); 4808a09b52aSRamesh Iyyar if (ret == 0) 4818a09b52aSRamesh Iyyar { 4828a09b52aSRamesh Iyyar EC_KEY_free(ecKey); 4838a09b52aSRamesh Iyyar log<level::ERR>("Error occured during assign EC Key into EVP"); 4848a09b52aSRamesh Iyyar elog<InternalFailure>(); 4858a09b52aSRamesh Iyyar } 4868a09b52aSRamesh Iyyar 4878a09b52aSRamesh Iyyar return pKey; 4888a09b52aSRamesh Iyyar } 4898a09b52aSRamesh Iyyar 490c6e58c7eSRamesh Iyyar void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey, 491c6e58c7eSRamesh Iyyar const std::string& privKeyFileName) 4928a09b52aSRamesh Iyyar { 4938a09b52aSRamesh Iyyar log<level::INFO>("Writing private key to file"); 494f4682712SMarri Devender Rao // write private key to file 495c6e58c7eSRamesh Iyyar fs::path privKeyPath = certParentInstallPath / privKeyFileName; 496f4682712SMarri Devender Rao 497f4682712SMarri Devender Rao FILE* fp = std::fopen(privKeyPath.c_str(), "w"); 498f4682712SMarri Devender Rao if (fp == NULL) 499f4682712SMarri Devender Rao { 500f4682712SMarri Devender Rao log<level::ERR>("Error occured creating private key file"); 501f4682712SMarri Devender Rao elog<InternalFailure>(); 502f4682712SMarri Devender Rao } 5038a09b52aSRamesh Iyyar int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL); 504f4682712SMarri Devender Rao std::fclose(fp); 505f4682712SMarri Devender Rao if (ret == 0) 506f4682712SMarri Devender Rao { 507f4682712SMarri Devender Rao log<level::ERR>("Error occured while writing private key to file"); 508f4682712SMarri Devender Rao elog<InternalFailure>(); 509f4682712SMarri Devender Rao } 510f4682712SMarri Devender Rao } 511f4682712SMarri Devender Rao 512f4682712SMarri Devender Rao void Manager::addEntry(X509_NAME* x509Name, const char* field, 513f4682712SMarri Devender Rao const std::string& bytes) 514f4682712SMarri Devender Rao { 515f4682712SMarri Devender Rao if (bytes.empty()) 516f4682712SMarri Devender Rao { 517f4682712SMarri Devender Rao return; 518f4682712SMarri Devender Rao } 519f4682712SMarri Devender Rao int ret = X509_NAME_add_entry_by_txt( 520f4682712SMarri Devender Rao x509Name, field, MBSTRING_ASC, 521f4682712SMarri Devender Rao reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0); 522f4682712SMarri Devender Rao if (ret != 1) 523f4682712SMarri Devender Rao { 524f4682712SMarri Devender Rao log<level::ERR>("Unable to set entry", entry("FIELD=%s", field), 525f4682712SMarri Devender Rao entry("VALUE=%s", bytes.c_str())); 526f4682712SMarri Devender Rao elog<InternalFailure>(); 527f4682712SMarri Devender Rao } 528f4682712SMarri Devender Rao } 529f4682712SMarri Devender Rao 530f4682712SMarri Devender Rao void Manager::createCSRObject(const Status& status) 531f4682712SMarri Devender Rao { 532f4682712SMarri Devender Rao if (csrPtr) 533f4682712SMarri Devender Rao { 534f4682712SMarri Devender Rao csrPtr.reset(nullptr); 535f4682712SMarri Devender Rao } 536f4682712SMarri Devender Rao auto csrObjectPath = objectPath + '/' + "csr"; 537f4682712SMarri Devender Rao csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(), 538f4682712SMarri Devender Rao certInstallPath.c_str(), status); 539f4682712SMarri Devender Rao } 540f4682712SMarri Devender Rao 541f4682712SMarri Devender Rao void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req) 542f4682712SMarri Devender Rao { 543f4682712SMarri Devender Rao if (fs::exists(filePath)) 544f4682712SMarri Devender Rao { 545f4682712SMarri Devender Rao log<level::INFO>("Removing the existing file", 546f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 547f4682712SMarri Devender Rao if (!fs::remove(filePath.c_str())) 548f4682712SMarri Devender Rao { 549f4682712SMarri Devender Rao log<level::ERR>("Unable to remove the file", 550f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 551f4682712SMarri Devender Rao elog<InternalFailure>(); 552f4682712SMarri Devender Rao } 553f4682712SMarri Devender Rao } 554f4682712SMarri Devender Rao 555f4682712SMarri Devender Rao FILE* fp = NULL; 556f4682712SMarri Devender Rao 557f4682712SMarri Devender Rao if ((fp = std::fopen(filePath.c_str(), "w")) == NULL) 558f4682712SMarri Devender Rao { 559f4682712SMarri Devender Rao log<level::ERR>("Error opening the file to write the CSR", 560f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 561f4682712SMarri Devender Rao elog<InternalFailure>(); 562f4682712SMarri Devender Rao } 563f4682712SMarri Devender Rao 564f4682712SMarri Devender Rao int rc = PEM_write_X509_REQ(fp, x509Req.get()); 565f4682712SMarri Devender Rao if (!rc) 566f4682712SMarri Devender Rao { 567f4682712SMarri Devender Rao log<level::ERR>("PEM write routine failed", 568f4682712SMarri Devender Rao entry("FILENAME=%s", filePath.c_str())); 569f4682712SMarri Devender Rao std::fclose(fp); 570f4682712SMarri Devender Rao elog<InternalFailure>(); 571f4682712SMarri Devender Rao } 572f4682712SMarri Devender Rao std::fclose(fp); 573f4682712SMarri Devender Rao } 574f4682712SMarri Devender Rao 575db029c95SKowalski, Kamil void Manager::createCertificates() 576db029c95SKowalski, Kamil { 577db029c95SKowalski, Kamil auto certObjectPath = objectPath + '/'; 578db029c95SKowalski, Kamil 579db029c95SKowalski, Kamil if (certType == phosphor::certs::AUTHORITY) 580db029c95SKowalski, Kamil { 581*fe590c4eSZbigniew Lukwinski // Check whether install path is a directory. 582db029c95SKowalski, Kamil if (!fs::is_directory(certInstallPath)) 583db029c95SKowalski, Kamil { 584db029c95SKowalski, Kamil log<level::ERR>("Certificate installation path exists and it is " 585db029c95SKowalski, Kamil "not a directory"); 586db029c95SKowalski, Kamil elog<InternalFailure>(); 587db029c95SKowalski, Kamil return; 588db029c95SKowalski, Kamil } 589db029c95SKowalski, Kamil 590db029c95SKowalski, Kamil for (auto& path : fs::directory_iterator(certInstallPath)) 591ffad1ef1SMarri Devender Rao { 592ffad1ef1SMarri Devender Rao try 593ffad1ef1SMarri Devender Rao { 594db029c95SKowalski, Kamil installedCerts.emplace_back(std::make_unique<Certificate>( 595db029c95SKowalski, Kamil bus, certObjectPath + std::to_string(certIdCounter++), 596db029c95SKowalski, Kamil certType, unitToRestart, certInstallPath, path.path(), true, 597a3bb38fbSZbigniew Kurzynski certWatchPtr, *this)); 598ffad1ef1SMarri Devender Rao } 599ffad1ef1SMarri Devender Rao catch (const InternalFailure& e) 600ffad1ef1SMarri Devender Rao { 601ffad1ef1SMarri Devender Rao report<InternalFailure>(); 602ffad1ef1SMarri Devender Rao } 603ffad1ef1SMarri Devender Rao catch (const InvalidCertificate& e) 604ffad1ef1SMarri Devender Rao { 605ffad1ef1SMarri Devender Rao report<InvalidCertificate>( 606ffad1ef1SMarri Devender Rao Reason("Existing certificate file is corrupted")); 607ffad1ef1SMarri Devender Rao } 608ffad1ef1SMarri Devender Rao } 609db029c95SKowalski, Kamil } 610db029c95SKowalski, Kamil else if (fs::exists(certInstallPath)) 611db029c95SKowalski, Kamil { 612db029c95SKowalski, Kamil try 613db029c95SKowalski, Kamil { 614db029c95SKowalski, Kamil installedCerts.emplace_back(std::make_unique<Certificate>( 615db029c95SKowalski, Kamil bus, certObjectPath + '1', certType, unitToRestart, 616a3bb38fbSZbigniew Kurzynski certInstallPath, certInstallPath, true, certWatchPtr, *this)); 617db029c95SKowalski, Kamil } 618db029c95SKowalski, Kamil catch (const InternalFailure& e) 619db029c95SKowalski, Kamil { 620db029c95SKowalski, Kamil report<InternalFailure>(); 621db029c95SKowalski, Kamil } 622db029c95SKowalski, Kamil catch (const InvalidCertificate& e) 623db029c95SKowalski, Kamil { 624db029c95SKowalski, Kamil report<InvalidCertificate>( 625db029c95SKowalski, Kamil Reason("Existing certificate file is corrupted")); 626db029c95SKowalski, Kamil } 627db029c95SKowalski, Kamil } 628db029c95SKowalski, Kamil } 629c6e58c7eSRamesh Iyyar 630c6e58c7eSRamesh Iyyar void Manager::createRSAPrivateKeyFile() 631c6e58c7eSRamesh Iyyar { 632c6e58c7eSRamesh Iyyar fs::path rsaPrivateKeyFileName = 633c6e58c7eSRamesh Iyyar certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 634c6e58c7eSRamesh Iyyar 635c6e58c7eSRamesh Iyyar try 636c6e58c7eSRamesh Iyyar { 637c6e58c7eSRamesh Iyyar if (!fs::exists(rsaPrivateKeyFileName)) 638c6e58c7eSRamesh Iyyar { 639c6e58c7eSRamesh Iyyar writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH), 640c6e58c7eSRamesh Iyyar RSA_PRIV_KEY_FILE_NAME); 641c6e58c7eSRamesh Iyyar } 642c6e58c7eSRamesh Iyyar } 643c6e58c7eSRamesh Iyyar catch (const InternalFailure& e) 644c6e58c7eSRamesh Iyyar { 645c6e58c7eSRamesh Iyyar report<InternalFailure>(); 646c6e58c7eSRamesh Iyyar } 647c6e58c7eSRamesh Iyyar } 648c6e58c7eSRamesh Iyyar 649c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength) 650c6e58c7eSRamesh Iyyar { 651c6e58c7eSRamesh Iyyar if (keyBitLength != SUPPORTED_KEYBITLENGTH) 652c6e58c7eSRamesh Iyyar { 653c6e58c7eSRamesh Iyyar log<level::ERR>( 654c6e58c7eSRamesh Iyyar "Given Key bit length is not supported", 655c6e58c7eSRamesh Iyyar entry("GIVENKEYBITLENGTH=%d", keyBitLength), 656c6e58c7eSRamesh Iyyar entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH)); 657c6e58c7eSRamesh Iyyar elog<InvalidArgument>( 658c6e58c7eSRamesh Iyyar Argument::ARGUMENT_NAME("KEYBITLENGTH"), 659c6e58c7eSRamesh Iyyar Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str())); 660c6e58c7eSRamesh Iyyar } 661c6e58c7eSRamesh Iyyar fs::path rsaPrivateKeyFileName = 662c6e58c7eSRamesh Iyyar certParentInstallPath / RSA_PRIV_KEY_FILE_NAME; 663c6e58c7eSRamesh Iyyar 664c6e58c7eSRamesh Iyyar FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r"); 665c6e58c7eSRamesh Iyyar if (!privateKeyFile) 666c6e58c7eSRamesh Iyyar { 667c6e58c7eSRamesh Iyyar log<level::ERR>("Unable to open RSA private key file to read", 668c6e58c7eSRamesh Iyyar entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()), 669c6e58c7eSRamesh Iyyar entry("ERRORREASON=%s", strerror(errno))); 670c6e58c7eSRamesh Iyyar elog<InternalFailure>(); 671c6e58c7eSRamesh Iyyar } 672c6e58c7eSRamesh Iyyar 673c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr privateKey( 674c6e58c7eSRamesh Iyyar PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr), 675c6e58c7eSRamesh Iyyar ::EVP_PKEY_free); 676c6e58c7eSRamesh Iyyar std::fclose(privateKeyFile); 677c6e58c7eSRamesh Iyyar 678c6e58c7eSRamesh Iyyar if (!privateKey) 679c6e58c7eSRamesh Iyyar { 680c6e58c7eSRamesh Iyyar log<level::ERR>("Error occured during PEM_read_PrivateKey call"); 681c6e58c7eSRamesh Iyyar elog<InternalFailure>(); 682c6e58c7eSRamesh Iyyar } 683c6e58c7eSRamesh Iyyar return privateKey; 684c6e58c7eSRamesh Iyyar } 685cfbc8dc8SJayanth Othayoth } // namespace certs 686cfbc8dc8SJayanth Othayoth } // namespace phosphor 687