xref: /openbmc/phosphor-certificate-manager/certs_manager.cpp (revision 718eef375b27325da3084794b72dcd511c034049)
1 #include "certs_manager.hpp"
2 
3 #include <openssl/evp.h>
4 #include <openssl/pem.h>
5 #include <unistd.h>
6 
7 #include <algorithm>
8 #include <phosphor-logging/elog-errors.hpp>
9 #include <xyz/openbmc_project/Certs/error.hpp>
10 #include <xyz/openbmc_project/Common/error.hpp>
11 
12 namespace phosphor::certs
13 {
14 using InternalFailure =
15     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
16 using InvalidCertificate =
17     sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
18 using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
19 
20 using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
21 using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
22 using InvalidArgument =
23     sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
24 using Argument = xyz::openbmc_project::Common::InvalidArgument;
25 
26 constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
27 
28 Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
29                  const char* path, const CertificateType& type,
30                  UnitsToRestart&& unit, CertInstallPath&& installPath) :
31     Ifaces(bus, path),
32     bus(bus), event(event), objectPath(path), certType(type),
33     unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
34     certParentInstallPath(fs::path(certInstallPath).parent_path())
35 {
36     try
37     {
38         // Create certificate directory if not existing.
39         // Set correct certificate directory permissions.
40         fs::path certDirectory;
41         try
42         {
43             if (certType == AUTHORITY)
44             {
45                 certDirectory = certInstallPath;
46             }
47             else
48             {
49                 certDirectory = certParentInstallPath;
50             }
51 
52             if (!fs::exists(certDirectory))
53             {
54                 fs::create_directories(certDirectory);
55             }
56 
57             auto permission = fs::perms::owner_read | fs::perms::owner_write |
58                               fs::perms::owner_exec;
59             fs::permissions(certDirectory, permission,
60                             fs::perm_options::replace);
61             storageUpdate();
62         }
63         catch (const fs::filesystem_error& e)
64         {
65             log<level::ERR>(
66                 "Failed to create directory", entry("ERR=%s", e.what()),
67                 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
68             report<InternalFailure>();
69         }
70 
71         // Generating RSA private key file if certificate type is server/client
72         if (certType != AUTHORITY)
73         {
74             createRSAPrivateKeyFile();
75         }
76 
77         // restore any existing certificates
78         createCertificates();
79 
80         // watch is not required for authority certificates
81         if (certType != AUTHORITY)
82         {
83             // watch for certificate file create/replace
84             certWatchPtr = std::make_unique<
85                 Watch>(event, certInstallPath, [this]() {
86                 try
87                 {
88                     // if certificate file existing update it
89                     if (!installedCerts.empty())
90                     {
91                         log<level::INFO>("Inotify callback to update "
92                                          "certificate properties");
93                         installedCerts[0]->populateProperties();
94                     }
95                     else
96                     {
97                         log<level::INFO>(
98                             "Inotify callback to create certificate object");
99                         createCertificates();
100                     }
101                 }
102                 catch (const InternalFailure& e)
103                 {
104                     commit<InternalFailure>();
105                 }
106                 catch (const InvalidCertificate& e)
107                 {
108                     commit<InvalidCertificate>();
109                 }
110             });
111         }
112         else
113         {
114             try
115             {
116                 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
117                 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
118                 {
119                     log<level::NOTICE>(
120                         "Legacy certificate detected, will be installed from: ",
121                         entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
122                     install(singleCertPath);
123                     if (!fs::remove(singleCertPath))
124                     {
125                         log<level::ERR>(
126                             "Unable to remove old certificate from: ",
127                             entry("SINGLE_CERTPATH=%s",
128                                   singleCertPath.c_str()));
129                         elog<InternalFailure>();
130                     }
131                 }
132             }
133             catch (const std::exception& ex)
134             {
135                 log<level::ERR>("Error in restoring legacy certificate",
136                                 entry("ERROR_STR=%s", ex.what()));
137             }
138         }
139     }
140     catch (const std::exception& ex)
141     {
142         log<level::ERR>("Error in certificate manager constructor",
143                         entry("ERROR_STR=%s", ex.what()));
144     }
145 }
146 
147 std::string Manager::install(const std::string filePath)
148 {
149     using NotAllowed =
150         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
151     using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
152 
153     if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
154     {
155         elog<NotAllowed>(Reason("Certificate already exist"));
156     }
157     else if (certType == phosphor::certs::AUTHORITY &&
158              installedCerts.size() >= maxNumAuthorityCertificates)
159     {
160         elog<NotAllowed>(Reason("Certificates limit reached"));
161     }
162 
163     std::string certObjectPath;
164     if (isCertificateUnique(filePath))
165     {
166         certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
167         installedCerts.emplace_back(std::make_unique<Certificate>(
168             bus, certObjectPath, certType, certInstallPath, filePath,
169             certWatchPtr, *this));
170         reloadOrReset(unitToRestart);
171         certIdCounter++;
172     }
173     else
174     {
175         elog<NotAllowed>(Reason("Certificate already exist"));
176     }
177 
178     return certObjectPath;
179 }
180 
181 void Manager::deleteAll()
182 {
183     // TODO: #Issue 4 when a certificate is deleted system auto generates
184     // certificate file. At present we are not supporting creation of
185     // certificate object for the auto-generated certificate file as
186     // deletion if only applicable for REST server and Bmcweb does not allow
187     // deletion of certificates
188     installedCerts.clear();
189     storageUpdate();
190     reloadOrReset(unitToRestart);
191 }
192 
193 void Manager::deleteCertificate(const Certificate* const certificate)
194 {
195     std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
196         std::find_if(installedCerts.begin(), installedCerts.end(),
197                      [certificate](std::unique_ptr<Certificate> const& cert) {
198                          return (cert.get() == certificate);
199                      });
200     if (certIt != installedCerts.end())
201     {
202         installedCerts.erase(certIt);
203         storageUpdate();
204         reloadOrReset(unitToRestart);
205     }
206     else
207     {
208         log<level::ERR>("Certificate does not exist",
209                         entry("ID=%s", certificate->getCertId().c_str()));
210         elog<InternalFailure>();
211     }
212 }
213 
214 void Manager::replaceCertificate(Certificate* const certificate,
215                                  const std::string& filePath)
216 {
217     if (isCertificateUnique(filePath, certificate))
218     {
219         certificate->install(filePath);
220         storageUpdate();
221         reloadOrReset(unitToRestart);
222     }
223     else
224     {
225         using NotAllowed =
226             sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
227         using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
228 
229         elog<NotAllowed>(Reason("Certificate already exist"));
230     }
231 }
232 
233 std::string Manager::generateCSR(
234     std::vector<std::string> alternativeNames, std::string challengePassword,
235     std::string city, std::string commonName, std::string contactPerson,
236     std::string country, std::string email, std::string givenName,
237     std::string initials, int64_t keyBitLength, std::string keyCurveId,
238     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
239     std::string organization, std::string organizationalUnit, std::string state,
240     std::string surname, std::string unstructuredName)
241 {
242     // We support only one CSR.
243     csrPtr.reset(nullptr);
244     auto pid = fork();
245     if (pid == -1)
246     {
247         log<level::ERR>("Error occurred during forking process");
248         report<InternalFailure>();
249     }
250     else if (pid == 0)
251     {
252         try
253         {
254             generateCSRHelper(alternativeNames, challengePassword, city,
255                               commonName, contactPerson, country, email,
256                               givenName, initials, keyBitLength, keyCurveId,
257                               keyPairAlgorithm, keyUsage, organization,
258                               organizationalUnit, state, surname,
259                               unstructuredName);
260             exit(EXIT_SUCCESS);
261         }
262         catch (const InternalFailure& e)
263         {
264             // commit the error reported in child process and exit
265             // Callback method from SDEvent Loop looks for exit status
266             exit(EXIT_FAILURE);
267             commit<InternalFailure>();
268         }
269         catch (const InvalidArgument& e)
270         {
271             // commit the error reported in child process and exit
272             // Callback method from SDEvent Loop looks for exit status
273             exit(EXIT_FAILURE);
274             commit<InvalidArgument>();
275         }
276     }
277     else
278     {
279         using namespace sdeventplus::source;
280         Child::Callback callback = [this](Child& eventSource,
281                                           const siginfo_t* si) {
282             eventSource.set_enabled(Enabled::On);
283             if (si->si_status != 0)
284             {
285                 this->createCSRObject(Status::FAILURE);
286             }
287             else
288             {
289                 this->createCSRObject(Status::SUCCESS);
290             }
291         };
292         try
293         {
294             sigset_t ss;
295             if (sigemptyset(&ss) < 0)
296             {
297                 log<level::ERR>("Unable to initialize signal set");
298                 elog<InternalFailure>();
299             }
300             if (sigaddset(&ss, SIGCHLD) < 0)
301             {
302                 log<level::ERR>("Unable to add signal to signal set");
303                 elog<InternalFailure>();
304             }
305 
306             // Block SIGCHLD first, so that the event loop can handle it
307             if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
308             {
309                 log<level::ERR>("Unable to block signal");
310                 elog<InternalFailure>();
311             }
312             if (childPtr)
313             {
314                 childPtr.reset();
315             }
316             childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
317                                                std::move(callback));
318         }
319         catch (const InternalFailure& e)
320         {
321             commit<InternalFailure>();
322         }
323     }
324     auto csrObjectPath = objectPath + '/' + "csr";
325     return csrObjectPath;
326 }
327 
328 std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
329 {
330     return installedCerts;
331 }
332 
333 void Manager::generateCSRHelper(
334     std::vector<std::string> alternativeNames, std::string challengePassword,
335     std::string city, std::string commonName, std::string contactPerson,
336     std::string country, std::string email, std::string givenName,
337     std::string initials, int64_t keyBitLength, std::string keyCurveId,
338     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
339     std::string organization, std::string organizationalUnit, std::string state,
340     std::string surname, std::string unstructuredName)
341 {
342     int ret = 0;
343 
344     // set version of x509 req
345     int nVersion = 1;
346     // TODO: Issue#6 need to make version number configurable
347     X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
348     ret = X509_REQ_set_version(x509Req.get(), nVersion);
349     if (ret == 0)
350     {
351         log<level::ERR>("Error occurred during X509_REQ_set_version call");
352         elog<InternalFailure>();
353     }
354 
355     // set subject of x509 req
356     X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
357 
358     if (!alternativeNames.empty())
359     {
360         for (auto& name : alternativeNames)
361         {
362             addEntry(x509Name, "subjectAltName", name);
363         }
364     }
365     addEntry(x509Name, "challengePassword", challengePassword);
366     addEntry(x509Name, "L", city);
367     addEntry(x509Name, "CN", commonName);
368     addEntry(x509Name, "name", contactPerson);
369     addEntry(x509Name, "C", country);
370     addEntry(x509Name, "emailAddress", email);
371     addEntry(x509Name, "GN", givenName);
372     addEntry(x509Name, "initials", initials);
373     addEntry(x509Name, "algorithm", keyPairAlgorithm);
374     if (!keyUsage.empty())
375     {
376         for (auto& usage : keyUsage)
377         {
378             if (isExtendedKeyUsage(usage))
379             {
380                 addEntry(x509Name, "extendedKeyUsage", usage);
381             }
382             else
383             {
384                 addEntry(x509Name, "keyUsage", usage);
385             }
386         }
387     }
388     addEntry(x509Name, "O", organization);
389     addEntry(x509Name, "OU", organizationalUnit);
390     addEntry(x509Name, "ST", state);
391     addEntry(x509Name, "SN", surname);
392     addEntry(x509Name, "unstructuredName", unstructuredName);
393 
394     EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
395 
396     log<level::INFO>("Given Key pair algorithm",
397                      entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
398 
399     // Used EC algorithm as default if user did not give algorithm type.
400     if (keyPairAlgorithm == "RSA")
401         pKey = getRSAKeyPair(keyBitLength);
402     else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
403         pKey = generateECKeyPair(keyCurveId);
404     else
405     {
406         log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
407                         "RSA and EC only");
408         elog<InvalidArgument>(
409             Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
410             Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
411     }
412 
413     ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
414     if (ret == 0)
415     {
416         log<level::ERR>("Error occurred while setting Public key");
417         elog<InternalFailure>();
418     }
419 
420     // Write private key to file
421     writePrivateKey(pKey, defaultPrivateKeyFileName);
422 
423     // set sign key of x509 req
424     ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
425     if (ret == 0)
426     {
427         log<level::ERR>("Error occurred while signing key of x509");
428         elog<InternalFailure>();
429     }
430 
431     log<level::INFO>("Writing CSR to file");
432     fs::path csrFilePath = certParentInstallPath / defaultCSRFileName;
433     writeCSR(csrFilePath.string(), x509Req);
434 }
435 
436 bool Manager::isExtendedKeyUsage(const std::string& usage)
437 {
438     const static std::array<const char*, 6> usageList = {
439         "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
440         "Timestamping",         "CodeSigning",          "EmailProtection"};
441     auto it = std::find_if(
442         usageList.begin(), usageList.end(),
443         [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
444     return it != usageList.end();
445 }
446 EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
447 {
448     int64_t keyBitLen = keyBitLength;
449     // set keybit length to default value if not set
450     if (keyBitLen <= 0)
451     {
452         constexpr auto DEFAULT_KEYBITLENGTH = 2048;
453         log<level::INFO>(
454             "KeyBitLength is not given.Hence, using default KeyBitLength",
455             entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
456         keyBitLen = DEFAULT_KEYBITLENGTH;
457     }
458 
459 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
460 
461     // generate rsa key
462     BIGNUM_Ptr bne(BN_new(), ::BN_free);
463     auto ret = BN_set_word(bne.get(), RSA_F4);
464     if (ret == 0)
465     {
466         log<level::ERR>("Error occurred during BN_set_word call");
467         elog<InternalFailure>();
468     }
469 
470     RSA* rsa = RSA_new();
471     ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), nullptr);
472     if (ret != 1)
473     {
474         free(rsa);
475         log<level::ERR>("Error occurred during RSA_generate_key_ex call",
476                         entry("KEYBITLENGTH=%PRIu64", keyBitLen));
477         elog<InternalFailure>();
478     }
479 
480     // set public key of x509 req
481     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
482     ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
483     if (ret == 0)
484     {
485         free(rsa);
486         log<level::ERR>("Error occurred during assign rsa key into EVP");
487         elog<InternalFailure>();
488     }
489 
490     return pKey;
491 
492 #else
493     auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
494         EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
495     if (!ctx)
496     {
497         log<level::ERR>("Error occurred creating EVP_PKEY_CTX from algorithm");
498         elog<InternalFailure>();
499     }
500 
501     if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
502         (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
503 
504     {
505         log<level::ERR>("Error occurred initializing keygen context");
506         elog<InternalFailure>();
507     }
508 
509     EVP_PKEY* pKey = nullptr;
510     if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
511     {
512         log<level::ERR>("Error occurred during generate EC key");
513         elog<InternalFailure>();
514     }
515 
516     return {pKey, &::EVP_PKEY_free};
517 #endif
518 }
519 
520 EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
521 {
522     std::string curId(curveId);
523 
524     if (curId.empty())
525     {
526         // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
527         constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
528         log<level::INFO>(
529             "KeyCurveId is not given. Hence using default curve id",
530             entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
531         curId = DEFAULT_KEYCURVEID;
532     }
533 
534     int ecGrp = OBJ_txt2nid(curId.c_str());
535     if (ecGrp == NID_undef)
536     {
537         log<level::ERR>(
538             "Error occurred during convert the curve id string format into NID",
539             entry("KEYCURVEID=%s", curId.c_str()));
540         elog<InternalFailure>();
541     }
542 
543 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
544 
545     EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
546 
547     if (ecKey == nullptr)
548     {
549         log<level::ERR>(
550             "Error occurred during create the EC_Key object from NID",
551             entry("ECGROUP=%d", ecGrp));
552         elog<InternalFailure>();
553     }
554 
555     // If you want to save a key and later load it with
556     // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
557     // flag on the key.
558     EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
559 
560     int ret = EC_KEY_generate_key(ecKey);
561 
562     if (ret == 0)
563     {
564         EC_KEY_free(ecKey);
565         log<level::ERR>("Error occurred during generate EC key");
566         elog<InternalFailure>();
567     }
568 
569     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
570     ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
571     if (ret == 0)
572     {
573         EC_KEY_free(ecKey);
574         log<level::ERR>("Error occurred during assign EC Key into EVP");
575         elog<InternalFailure>();
576     }
577 
578     return pKey;
579 
580 #else
581     auto holder_of_key = [](EVP_PKEY* key) {
582         return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
583             key, &::EVP_PKEY_free};
584     };
585 
586     // Create context to set up curve parameters.
587     auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
588         EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
589     if (!ctx)
590     {
591         log<level::ERR>("Error occurred creating EVP_PKEY_CTX for params");
592         elog<InternalFailure>();
593     }
594 
595     // Set up curve parameters.
596     EVP_PKEY* params = nullptr;
597 
598     if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
599         (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
600          0) ||
601         (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
602         (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
603     {
604         log<level::ERR>("Error occurred setting curve parameters");
605         elog<InternalFailure>();
606     }
607 
608     // Move parameters to RAII holder.
609     auto pparms = holder_of_key(params);
610 
611     // Create new context for key.
612     ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
613 
614     if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
615     {
616         log<level::ERR>("Error occurred initializing keygen context");
617         elog<InternalFailure>();
618     }
619 
620     EVP_PKEY* pKey = nullptr;
621     if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
622     {
623         log<level::ERR>("Error occurred during generate EC key");
624         elog<InternalFailure>();
625     }
626 
627     return holder_of_key(pKey);
628 #endif
629 }
630 
631 void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
632                               const std::string& privKeyFileName)
633 {
634     log<level::INFO>("Writing private key to file");
635     // write private key to file
636     fs::path privKeyPath = certParentInstallPath / privKeyFileName;
637 
638     FILE* fp = std::fopen(privKeyPath.c_str(), "w");
639     if (fp == nullptr)
640     {
641         log<level::ERR>("Error occurred creating private key file");
642         elog<InternalFailure>();
643     }
644     int ret =
645         PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0, nullptr);
646     std::fclose(fp);
647     if (ret == 0)
648     {
649         log<level::ERR>("Error occurred while writing private key to file");
650         elog<InternalFailure>();
651     }
652 }
653 
654 void Manager::addEntry(X509_NAME* x509Name, const char* field,
655                        const std::string& bytes)
656 {
657     if (bytes.empty())
658     {
659         return;
660     }
661     int ret = X509_NAME_add_entry_by_txt(
662         x509Name, field, MBSTRING_ASC,
663         reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
664     if (ret != 1)
665     {
666         log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
667                         entry("VALUE=%s", bytes.c_str()));
668         elog<InternalFailure>();
669     }
670 }
671 
672 void Manager::createCSRObject(const Status& status)
673 {
674     if (csrPtr)
675     {
676         csrPtr.reset(nullptr);
677     }
678     auto csrObjectPath = objectPath + '/' + "csr";
679     csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
680                                    certInstallPath.c_str(), status);
681 }
682 
683 void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
684 {
685     if (fs::exists(filePath))
686     {
687         log<level::INFO>("Removing the existing file",
688                          entry("FILENAME=%s", filePath.c_str()));
689         if (!fs::remove(filePath.c_str()))
690         {
691             log<level::ERR>("Unable to remove the file",
692                             entry("FILENAME=%s", filePath.c_str()));
693             elog<InternalFailure>();
694         }
695     }
696 
697     FILE* fp = nullptr;
698 
699     if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
700     {
701         log<level::ERR>("Error opening the file to write the CSR",
702                         entry("FILENAME=%s", filePath.c_str()));
703         elog<InternalFailure>();
704     }
705 
706     int rc = PEM_write_X509_REQ(fp, x509Req.get());
707     if (!rc)
708     {
709         log<level::ERR>("PEM write routine failed",
710                         entry("FILENAME=%s", filePath.c_str()));
711         std::fclose(fp);
712         elog<InternalFailure>();
713     }
714     std::fclose(fp);
715 }
716 
717 void Manager::createCertificates()
718 {
719     auto certObjectPath = objectPath + '/';
720 
721     if (certType == phosphor::certs::AUTHORITY)
722     {
723         // Check whether install path is a directory.
724         if (!fs::is_directory(certInstallPath))
725         {
726             log<level::ERR>("Certificate installation path exists and it is "
727                             "not a directory");
728             elog<InternalFailure>();
729             return;
730         }
731 
732         for (auto& path : fs::directory_iterator(certInstallPath))
733         {
734             try
735             {
736                 // Assume here any regular file located in certificate directory
737                 // contains certificates body. Do not want to use soft links
738                 // would add value.
739                 if (fs::is_regular_file(path))
740                 {
741                     installedCerts.emplace_back(std::make_unique<Certificate>(
742                         bus, certObjectPath + std::to_string(certIdCounter++),
743                         certType, certInstallPath, path.path(), certWatchPtr,
744                         *this));
745                 }
746             }
747             catch (const InternalFailure& e)
748             {
749                 report<InternalFailure>();
750             }
751             catch (const InvalidCertificate& e)
752             {
753                 report<InvalidCertificate>(
754                     Reason("Existing certificate file is corrupted"));
755             }
756         }
757     }
758     else if (fs::exists(certInstallPath))
759     {
760         try
761         {
762             installedCerts.emplace_back(std::make_unique<Certificate>(
763                 bus, certObjectPath + '1', certType, certInstallPath,
764                 certInstallPath, certWatchPtr, *this));
765         }
766         catch (const InternalFailure& e)
767         {
768             report<InternalFailure>();
769         }
770         catch (const InvalidCertificate& e)
771         {
772             report<InvalidCertificate>(
773                 Reason("Existing certificate file is corrupted"));
774         }
775     }
776 }
777 
778 void Manager::createRSAPrivateKeyFile()
779 {
780     fs::path rsaPrivateKeyFileName =
781         certParentInstallPath / defaultRSAPrivateKeyFileName;
782 
783     try
784     {
785         if (!fs::exists(rsaPrivateKeyFileName))
786         {
787             writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
788                             defaultRSAPrivateKeyFileName);
789         }
790     }
791     catch (const InternalFailure& e)
792     {
793         report<InternalFailure>();
794     }
795 }
796 
797 EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
798 {
799     if (keyBitLength != SUPPORTED_KEYBITLENGTH)
800     {
801         log<level::ERR>(
802             "Given Key bit length is not supported",
803             entry("GIVENKEYBITLENGTH=%d", keyBitLength),
804             entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
805         elog<InvalidArgument>(
806             Argument::ARGUMENT_NAME("KEYBITLENGTH"),
807             Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
808     }
809     fs::path rsaPrivateKeyFileName =
810         certParentInstallPath / defaultRSAPrivateKeyFileName;
811 
812     FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
813     if (!privateKeyFile)
814     {
815         log<level::ERR>("Unable to open RSA private key file to read",
816                         entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
817                         entry("ERRORREASON=%s", strerror(errno)));
818         elog<InternalFailure>();
819     }
820 
821     EVP_PKEY_Ptr privateKey(
822         PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
823         ::EVP_PKEY_free);
824     std::fclose(privateKeyFile);
825 
826     if (!privateKey)
827     {
828         log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
829         elog<InternalFailure>();
830     }
831     return privateKey;
832 }
833 
834 void Manager::storageUpdate()
835 {
836     if (certType == phosphor::certs::AUTHORITY)
837     {
838         // Remove symbolic links in the certificate directory
839         for (auto& certPath : fs::directory_iterator(certInstallPath))
840         {
841             try
842             {
843                 if (fs::is_symlink(certPath))
844                 {
845                     fs::remove(certPath);
846                 }
847             }
848             catch (const std::exception& e)
849             {
850                 log<level::ERR>(
851                     "Failed to remove symlink for certificate",
852                     entry("ERR=%s", e.what()),
853                     entry("SYMLINK=%s", certPath.path().string().c_str()));
854                 elog<InternalFailure>();
855             }
856         }
857     }
858 
859     for (const auto& cert : installedCerts)
860     {
861         cert->storageUpdate();
862     }
863 }
864 
865 void Manager::reloadOrReset(const UnitsToRestart& unit)
866 {
867     if (!unit.empty())
868     {
869         try
870         {
871             constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
872             constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
873             constexpr auto SYSTEMD_INTERFACE =
874                 "org.freedesktop.systemd1.Manager";
875 
876             auto method =
877                 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
878                                     SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
879             method.append(unit, "replace");
880             bus.call_noreply(method);
881         }
882         catch (const sdbusplus::exception::exception& e)
883         {
884             log<level::ERR>("Failed to reload or restart service",
885                             entry("ERR=%s", e.what()),
886                             entry("UNIT=%s", unit.c_str()));
887             elog<InternalFailure>();
888         }
889     }
890 }
891 
892 bool Manager::isCertificateUnique(const std::string& filePath,
893                                   const Certificate* const certToDrop)
894 {
895     if (std::any_of(
896             installedCerts.begin(), installedCerts.end(),
897             [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
898                 return cert.get() != certToDrop && cert->isSame(filePath);
899             }))
900     {
901         return false;
902     }
903     else
904     {
905         return true;
906     }
907 }
908 
909 } // namespace phosphor::certs
910