xref: /openbmc/phosphor-certificate-manager/certs_manager.cpp (revision cf06ccdcbab548e8e75f5c1737569132d1bcb3c1)
1cfbc8dc8SJayanth Othayoth #include "certs_manager.hpp"
2cfbc8dc8SJayanth Othayoth 
326fb83efSPatrick Williams #include <openssl/evp.h>
4f4682712SMarri Devender Rao #include <openssl/pem.h>
5f4682712SMarri Devender Rao #include <unistd.h>
6f4682712SMarri Devender Rao 
7a3bb38fbSZbigniew Kurzynski #include <algorithm>
86ceec40bSMarri Devender Rao #include <phosphor-logging/elog-errors.hpp>
913bf74e4SMarri Devender Rao #include <xyz/openbmc_project/Certs/error.hpp>
10cfbc8dc8SJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp>
112f3563ccSZbigniew Lukwinski 
12e1289adfSNan Zhou namespace phosphor::certs
13cfbc8dc8SJayanth Othayoth {
14*cf06ccdcSNan Zhou namespace
15*cf06ccdcSNan Zhou {
16*cf06ccdcSNan Zhou namespace fs = std::filesystem;
17*cf06ccdcSNan Zhou using ::phosphor::logging::commit;
18*cf06ccdcSNan Zhou using ::phosphor::logging::elog;
19*cf06ccdcSNan Zhou using ::phosphor::logging::entry;
20*cf06ccdcSNan Zhou using ::phosphor::logging::level;
21*cf06ccdcSNan Zhou using ::phosphor::logging::log;
22*cf06ccdcSNan Zhou using ::phosphor::logging::report;
23cfbc8dc8SJayanth Othayoth 
24*cf06ccdcSNan Zhou using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
25*cf06ccdcSNan Zhou using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
26*cf06ccdcSNan Zhou using ::sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
27*cf06ccdcSNan Zhou using NotAllowedReason =
28*cf06ccdcSNan Zhou     ::phosphor::logging::xyz::openbmc_project::Common::NotAllowed::REASON;
29*cf06ccdcSNan Zhou using InvalidCertificateReason = ::phosphor::logging::xyz::openbmc_project::
30*cf06ccdcSNan Zhou     Certs::InvalidCertificate::REASON;
31*cf06ccdcSNan Zhou using ::sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
32*cf06ccdcSNan Zhou using Argument =
33*cf06ccdcSNan Zhou     ::phosphor::logging::xyz::openbmc_project::Common::InvalidArgument;
34c6e58c7eSRamesh Iyyar 
35*cf06ccdcSNan Zhou // RAII support for openSSL functions.
36*cf06ccdcSNan Zhou using X509ReqPtr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
37*cf06ccdcSNan Zhou using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
38*cf06ccdcSNan Zhou using BignumPtr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
39*cf06ccdcSNan Zhou 
40*cf06ccdcSNan Zhou constexpr int supportedKeyBitLength = 2048;
41*cf06ccdcSNan Zhou constexpr int defaultKeyBitLength = 2048;
42*cf06ccdcSNan Zhou // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
43*cf06ccdcSNan Zhou constexpr auto defaultKeyCurveID = "secp224r1";
44*cf06ccdcSNan Zhou } // namespace
45f4682712SMarri Devender Rao 
46f4682712SMarri Devender Rao Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
47*cf06ccdcSNan Zhou                  const char* path, CertificateType type,
48*cf06ccdcSNan Zhou                  const std::string& unit, const std::string& installPath) :
49*cf06ccdcSNan Zhou     internal::ManagerInterface(bus, path),
50f4682712SMarri Devender Rao     bus(bus), event(event), objectPath(path), certType(type),
51c6e58c7eSRamesh Iyyar     unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
52c6e58c7eSRamesh Iyyar     certParentInstallPath(fs::path(certInstallPath).parent_path())
53cfbc8dc8SJayanth Othayoth {
54db5c6fc8SMarri Devender Rao     try
55db5c6fc8SMarri Devender Rao     {
56fe590c4eSZbigniew Lukwinski         // Create certificate directory if not existing.
57bf3cf751SNan Zhou         // Set correct certificate directory permissions.
58fe590c4eSZbigniew Lukwinski         fs::path certDirectory;
59b57d75e2SMarri Devender Rao         try
60b57d75e2SMarri Devender Rao         {
61*cf06ccdcSNan Zhou             if (certType == CertificateType::Authority)
62b57d75e2SMarri Devender Rao             {
63fe590c4eSZbigniew Lukwinski                 certDirectory = certInstallPath;
64b57d75e2SMarri Devender Rao             }
65fe590c4eSZbigniew Lukwinski             else
66fe590c4eSZbigniew Lukwinski             {
67fe590c4eSZbigniew Lukwinski                 certDirectory = certParentInstallPath;
68fe590c4eSZbigniew Lukwinski             }
69fe590c4eSZbigniew Lukwinski 
70fe590c4eSZbigniew Lukwinski             if (!fs::exists(certDirectory))
71fe590c4eSZbigniew Lukwinski             {
72fe590c4eSZbigniew Lukwinski                 fs::create_directories(certDirectory);
73fe590c4eSZbigniew Lukwinski             }
74fe590c4eSZbigniew Lukwinski 
75667286e4SMarri Devender Rao             auto permission = fs::perms::owner_read | fs::perms::owner_write |
76667286e4SMarri Devender Rao                               fs::perms::owner_exec;
77db5c6fc8SMarri Devender Rao             fs::permissions(certDirectory, permission,
78db5c6fc8SMarri Devender Rao                             fs::perm_options::replace);
792f3563ccSZbigniew Lukwinski             storageUpdate();
80b57d75e2SMarri Devender Rao         }
8171957992SPatrick Williams         catch (const fs::filesystem_error& e)
82b57d75e2SMarri Devender Rao         {
83db5c6fc8SMarri Devender Rao             log<level::ERR>(
84db5c6fc8SMarri Devender Rao                 "Failed to create directory", entry("ERR=%s", e.what()),
85b57d75e2SMarri Devender Rao                 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
86b57d75e2SMarri Devender Rao             report<InternalFailure>();
87b57d75e2SMarri Devender Rao         }
88b57d75e2SMarri Devender Rao 
89c6e58c7eSRamesh Iyyar         // Generating RSA private key file if certificate type is server/client
90*cf06ccdcSNan Zhou         if (certType != CertificateType::Authority)
91c6e58c7eSRamesh Iyyar         {
92c6e58c7eSRamesh Iyyar             createRSAPrivateKeyFile();
93c6e58c7eSRamesh Iyyar         }
94c6e58c7eSRamesh Iyyar 
95ffad1ef1SMarri Devender Rao         // restore any existing certificates
96db029c95SKowalski, Kamil         createCertificates();
97ffad1ef1SMarri Devender Rao 
98ffad1ef1SMarri Devender Rao         // watch is not required for authority certificates
99*cf06ccdcSNan Zhou         if (certType != CertificateType::Authority)
100ffad1ef1SMarri Devender Rao         {
101ffad1ef1SMarri Devender Rao             // watch for certificate file create/replace
102ffad1ef1SMarri Devender Rao             certWatchPtr = std::make_unique<
103ffad1ef1SMarri Devender Rao                 Watch>(event, certInstallPath, [this]() {
104bf7c588cSMarri Devender Rao                 try
105bf7c588cSMarri Devender Rao                 {
106ffad1ef1SMarri Devender Rao                     // if certificate file existing update it
107db029c95SKowalski, Kamil                     if (!installedCerts.empty())
108ffad1ef1SMarri Devender Rao                     {
109db5c6fc8SMarri Devender Rao                         log<level::INFO>("Inotify callback to update "
110db5c6fc8SMarri Devender Rao                                          "certificate properties");
111db029c95SKowalski, Kamil                         installedCerts[0]->populateProperties();
112ffad1ef1SMarri Devender Rao                     }
113ffad1ef1SMarri Devender Rao                     else
114ffad1ef1SMarri Devender Rao                     {
115ffad1ef1SMarri Devender Rao                         log<level::INFO>(
116ffad1ef1SMarri Devender Rao                             "Inotify callback to create certificate object");
117db029c95SKowalski, Kamil                         createCertificates();
118ffad1ef1SMarri Devender Rao                     }
119bf7c588cSMarri Devender Rao                 }
120bf7c588cSMarri Devender Rao                 catch (const InternalFailure& e)
121bf7c588cSMarri Devender Rao                 {
122ffad1ef1SMarri Devender Rao                     commit<InternalFailure>();
123bf7c588cSMarri Devender Rao                 }
124bf7c588cSMarri Devender Rao                 catch (const InvalidCertificate& e)
125bf7c588cSMarri Devender Rao                 {
126ffad1ef1SMarri Devender Rao                     commit<InvalidCertificate>();
127bf7c588cSMarri Devender Rao                 }
128ffad1ef1SMarri Devender Rao             });
129bf7c588cSMarri Devender Rao         }
130db029c95SKowalski, Kamil         else
131db029c95SKowalski, Kamil         {
132db5c6fc8SMarri Devender Rao             try
133db5c6fc8SMarri Devender Rao             {
134db5c6fc8SMarri Devender Rao                 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
135db5c6fc8SMarri Devender Rao                 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
136db029c95SKowalski, Kamil                 {
137db029c95SKowalski, Kamil                     log<level::NOTICE>(
138db029c95SKowalski, Kamil                         "Legacy certificate detected, will be installed from: ",
139db5c6fc8SMarri Devender Rao                         entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
140db5c6fc8SMarri Devender Rao                     install(singleCertPath);
141db5c6fc8SMarri Devender Rao                     if (!fs::remove(singleCertPath))
142db029c95SKowalski, Kamil                     {
143db029c95SKowalski, Kamil                         log<level::ERR>(
144db029c95SKowalski, Kamil                             "Unable to remove old certificate from: ",
145db5c6fc8SMarri Devender Rao                             entry("SINGLE_CERTPATH=%s",
146db5c6fc8SMarri Devender Rao                                   singleCertPath.c_str()));
147db029c95SKowalski, Kamil                         elog<InternalFailure>();
148db029c95SKowalski, Kamil                     }
149db029c95SKowalski, Kamil                 }
150db029c95SKowalski, Kamil             }
151db5c6fc8SMarri Devender Rao             catch (const std::exception& ex)
152db5c6fc8SMarri Devender Rao             {
153db5c6fc8SMarri Devender Rao                 log<level::ERR>("Error in restoring legacy certificate",
154db5c6fc8SMarri Devender Rao                                 entry("ERROR_STR=%s", ex.what()));
155db5c6fc8SMarri Devender Rao             }
156db5c6fc8SMarri Devender Rao         }
157db5c6fc8SMarri Devender Rao     }
15871957992SPatrick Williams     catch (const std::exception& ex)
159db5c6fc8SMarri Devender Rao     {
160db5c6fc8SMarri Devender Rao         log<level::ERR>("Error in certificate manager constructor",
161db5c6fc8SMarri Devender Rao                         entry("ERROR_STR=%s", ex.what()));
162db5c6fc8SMarri Devender Rao     }
163dd74bd20SJayanth Othayoth }
164589159f2SJayanth Othayoth 
16506a69d7bSZbigniew Kurzynski std::string Manager::install(const std::string filePath)
166cfbc8dc8SJayanth Othayoth {
167*cf06ccdcSNan Zhou     if (certType != CertificateType::Authority && !installedCerts.empty())
1681396511dSMarri Devender Rao     {
169*cf06ccdcSNan Zhou         elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
1701396511dSMarri Devender Rao     }
171*cf06ccdcSNan Zhou     else if (certType == CertificateType::Authority &&
172718eef37SNan Zhou              installedCerts.size() >= maxNumAuthorityCertificates)
1733b07b77aSZbigniew Lukwinski     {
174*cf06ccdcSNan Zhou         elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
1753b07b77aSZbigniew Lukwinski     }
176ffad1ef1SMarri Devender Rao 
1772f3563ccSZbigniew Lukwinski     std::string certObjectPath;
1782f3563ccSZbigniew Lukwinski     if (isCertificateUnique(filePath))
1792f3563ccSZbigniew Lukwinski     {
1802f3563ccSZbigniew Lukwinski         certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
181db029c95SKowalski, Kamil         installedCerts.emplace_back(std::make_unique<Certificate>(
1822f3563ccSZbigniew Lukwinski             bus, certObjectPath, certType, certInstallPath, filePath,
183*cf06ccdcSNan Zhou             certWatchPtr.get(), *this));
1842f3563ccSZbigniew Lukwinski         reloadOrReset(unitToRestart);
1852f3563ccSZbigniew Lukwinski         certIdCounter++;
1862f3563ccSZbigniew Lukwinski     }
1872f3563ccSZbigniew Lukwinski     else
1882f3563ccSZbigniew Lukwinski     {
189*cf06ccdcSNan Zhou         elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
1902f3563ccSZbigniew Lukwinski     }
1912f3563ccSZbigniew Lukwinski 
19206a69d7bSZbigniew Kurzynski     return certObjectPath;
193589159f2SJayanth Othayoth }
194ae70b3daSDeepak Kodihalli 
195a3bb38fbSZbigniew Kurzynski void Manager::deleteAll()
196ae70b3daSDeepak Kodihalli {
1976ceec40bSMarri Devender Rao     // TODO: #Issue 4 when a certificate is deleted system auto generates
1986ceec40bSMarri Devender Rao     // certificate file. At present we are not supporting creation of
1996ceec40bSMarri Devender Rao     // certificate object for the auto-generated certificate file as
2006ceec40bSMarri Devender Rao     // deletion if only applicable for REST server and Bmcweb does not allow
2016ceec40bSMarri Devender Rao     // deletion of certificates
202db029c95SKowalski, Kamil     installedCerts.clear();
2032f3563ccSZbigniew Lukwinski     storageUpdate();
2042f3563ccSZbigniew Lukwinski     reloadOrReset(unitToRestart);
205ae70b3daSDeepak Kodihalli }
206f4682712SMarri Devender Rao 
2072f3563ccSZbigniew Lukwinski void Manager::deleteCertificate(const Certificate* const certificate)
208a3bb38fbSZbigniew Kurzynski {
209a3bb38fbSZbigniew Kurzynski     std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
210a3bb38fbSZbigniew Kurzynski         std::find_if(installedCerts.begin(), installedCerts.end(),
2112f3563ccSZbigniew Lukwinski                      [certificate](std::unique_ptr<Certificate> const& cert) {
2122f3563ccSZbigniew Lukwinski                          return (cert.get() == certificate);
213a3bb38fbSZbigniew Kurzynski                      });
214a3bb38fbSZbigniew Kurzynski     if (certIt != installedCerts.end())
215a3bb38fbSZbigniew Kurzynski     {
216a3bb38fbSZbigniew Kurzynski         installedCerts.erase(certIt);
2172f3563ccSZbigniew Lukwinski         storageUpdate();
2182f3563ccSZbigniew Lukwinski         reloadOrReset(unitToRestart);
219a3bb38fbSZbigniew Kurzynski     }
220a3bb38fbSZbigniew Kurzynski     else
221a3bb38fbSZbigniew Kurzynski     {
222a3bb38fbSZbigniew Kurzynski         log<level::ERR>("Certificate does not exist",
2232f3563ccSZbigniew Lukwinski                         entry("ID=%s", certificate->getCertId().c_str()));
2242f3563ccSZbigniew Lukwinski         elog<InternalFailure>();
2252f3563ccSZbigniew Lukwinski     }
2262f3563ccSZbigniew Lukwinski }
2272f3563ccSZbigniew Lukwinski 
2282f3563ccSZbigniew Lukwinski void Manager::replaceCertificate(Certificate* const certificate,
2292f3563ccSZbigniew Lukwinski                                  const std::string& filePath)
2302f3563ccSZbigniew Lukwinski {
2312f3563ccSZbigniew Lukwinski     if (isCertificateUnique(filePath, certificate))
2322f3563ccSZbigniew Lukwinski     {
2332f3563ccSZbigniew Lukwinski         certificate->install(filePath);
2342f3563ccSZbigniew Lukwinski         storageUpdate();
2352f3563ccSZbigniew Lukwinski         reloadOrReset(unitToRestart);
2362f3563ccSZbigniew Lukwinski     }
2372f3563ccSZbigniew Lukwinski     else
2382f3563ccSZbigniew Lukwinski     {
239*cf06ccdcSNan Zhou         elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
240a3bb38fbSZbigniew Kurzynski     }
241a3bb38fbSZbigniew Kurzynski }
242a3bb38fbSZbigniew Kurzynski 
243f4682712SMarri Devender Rao std::string Manager::generateCSR(
244f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
245f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
246f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
247f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
248f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
249f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
250f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
251f4682712SMarri Devender Rao {
252f4682712SMarri Devender Rao     // We support only one CSR.
253f4682712SMarri Devender Rao     csrPtr.reset(nullptr);
254f4682712SMarri Devender Rao     auto pid = fork();
255f4682712SMarri Devender Rao     if (pid == -1)
256f4682712SMarri Devender Rao     {
257f4682712SMarri Devender Rao         log<level::ERR>("Error occurred during forking process");
258f4682712SMarri Devender Rao         report<InternalFailure>();
259f4682712SMarri Devender Rao     }
260f4682712SMarri Devender Rao     else if (pid == 0)
261f4682712SMarri Devender Rao     {
262f4682712SMarri Devender Rao         try
263f4682712SMarri Devender Rao         {
264f4682712SMarri Devender Rao             generateCSRHelper(alternativeNames, challengePassword, city,
265f4682712SMarri Devender Rao                               commonName, contactPerson, country, email,
266f4682712SMarri Devender Rao                               givenName, initials, keyBitLength, keyCurveId,
267f4682712SMarri Devender Rao                               keyPairAlgorithm, keyUsage, organization,
268f4682712SMarri Devender Rao                               organizationalUnit, state, surname,
269f4682712SMarri Devender Rao                               unstructuredName);
270f4682712SMarri Devender Rao             exit(EXIT_SUCCESS);
271f4682712SMarri Devender Rao         }
272f4682712SMarri Devender Rao         catch (const InternalFailure& e)
273f4682712SMarri Devender Rao         {
274f4682712SMarri Devender Rao             // commit the error reported in child process and exit
275f4682712SMarri Devender Rao             // Callback method from SDEvent Loop looks for exit status
276f4682712SMarri Devender Rao             exit(EXIT_FAILURE);
277f4682712SMarri Devender Rao             commit<InternalFailure>();
278f4682712SMarri Devender Rao         }
279d2393f23SRamesh Iyyar         catch (const InvalidArgument& e)
280d2393f23SRamesh Iyyar         {
281d2393f23SRamesh Iyyar             // commit the error reported in child process and exit
282d2393f23SRamesh Iyyar             // Callback method from SDEvent Loop looks for exit status
283d2393f23SRamesh Iyyar             exit(EXIT_FAILURE);
284d2393f23SRamesh Iyyar             commit<InvalidArgument>();
285d2393f23SRamesh Iyyar         }
286f4682712SMarri Devender Rao     }
287f4682712SMarri Devender Rao     else
288f4682712SMarri Devender Rao     {
289f4682712SMarri Devender Rao         using namespace sdeventplus::source;
290f4682712SMarri Devender Rao         Child::Callback callback = [this](Child& eventSource,
291f4682712SMarri Devender Rao                                           const siginfo_t* si) {
292f4682712SMarri Devender Rao             eventSource.set_enabled(Enabled::On);
293f4682712SMarri Devender Rao             if (si->si_status != 0)
294f4682712SMarri Devender Rao             {
295f4682712SMarri Devender Rao                 this->createCSRObject(Status::FAILURE);
296f4682712SMarri Devender Rao             }
297f4682712SMarri Devender Rao             else
298f4682712SMarri Devender Rao             {
299f4682712SMarri Devender Rao                 this->createCSRObject(Status::SUCCESS);
300f4682712SMarri Devender Rao             }
301f4682712SMarri Devender Rao         };
302f4682712SMarri Devender Rao         try
303f4682712SMarri Devender Rao         {
304f4682712SMarri Devender Rao             sigset_t ss;
305f4682712SMarri Devender Rao             if (sigemptyset(&ss) < 0)
306f4682712SMarri Devender Rao             {
307f4682712SMarri Devender Rao                 log<level::ERR>("Unable to initialize signal set");
308f4682712SMarri Devender Rao                 elog<InternalFailure>();
309f4682712SMarri Devender Rao             }
310f4682712SMarri Devender Rao             if (sigaddset(&ss, SIGCHLD) < 0)
311f4682712SMarri Devender Rao             {
312f4682712SMarri Devender Rao                 log<level::ERR>("Unable to add signal to signal set");
313f4682712SMarri Devender Rao                 elog<InternalFailure>();
314f4682712SMarri Devender Rao             }
315f4682712SMarri Devender Rao 
316f4682712SMarri Devender Rao             // Block SIGCHLD first, so that the event loop can handle it
317cfb5802aSNan Zhou             if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
318f4682712SMarri Devender Rao             {
319f4682712SMarri Devender Rao                 log<level::ERR>("Unable to block signal");
320f4682712SMarri Devender Rao                 elog<InternalFailure>();
321f4682712SMarri Devender Rao             }
322f4682712SMarri Devender Rao             if (childPtr)
323f4682712SMarri Devender Rao             {
324f4682712SMarri Devender Rao                 childPtr.reset();
325f4682712SMarri Devender Rao             }
326f4682712SMarri Devender Rao             childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
327f4682712SMarri Devender Rao                                                std::move(callback));
328f4682712SMarri Devender Rao         }
329f4682712SMarri Devender Rao         catch (const InternalFailure& e)
330f4682712SMarri Devender Rao         {
331f4682712SMarri Devender Rao             commit<InternalFailure>();
332f4682712SMarri Devender Rao         }
333f4682712SMarri Devender Rao     }
334f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
335f4682712SMarri Devender Rao     return csrObjectPath;
336f4682712SMarri Devender Rao }
337f4682712SMarri Devender Rao 
338db029c95SKowalski, Kamil std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
339ffad1ef1SMarri Devender Rao {
340db029c95SKowalski, Kamil     return installedCerts;
341ffad1ef1SMarri Devender Rao }
342ffad1ef1SMarri Devender Rao 
343f4682712SMarri Devender Rao void Manager::generateCSRHelper(
344f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
345f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
346f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
347f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
348f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
349f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
350f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
351f4682712SMarri Devender Rao {
352f4682712SMarri Devender Rao     int ret = 0;
353f4682712SMarri Devender Rao 
354f4682712SMarri Devender Rao     // set version of x509 req
355f4682712SMarri Devender Rao     int nVersion = 1;
356f4682712SMarri Devender Rao     // TODO: Issue#6 need to make version number configurable
357*cf06ccdcSNan Zhou     X509ReqPtr x509Req(X509_REQ_new(), ::X509_REQ_free);
358f4682712SMarri Devender Rao     ret = X509_REQ_set_version(x509Req.get(), nVersion);
359f4682712SMarri Devender Rao     if (ret == 0)
360f4682712SMarri Devender Rao     {
361bf3cf751SNan Zhou         log<level::ERR>("Error occurred during X509_REQ_set_version call");
362f4682712SMarri Devender Rao         elog<InternalFailure>();
363f4682712SMarri Devender Rao     }
364f4682712SMarri Devender Rao 
365f4682712SMarri Devender Rao     // set subject of x509 req
366f4682712SMarri Devender Rao     X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
367f4682712SMarri Devender Rao 
368f4682712SMarri Devender Rao     if (!alternativeNames.empty())
369f4682712SMarri Devender Rao     {
370f4682712SMarri Devender Rao         for (auto& name : alternativeNames)
371f4682712SMarri Devender Rao         {
372f4682712SMarri Devender Rao             addEntry(x509Name, "subjectAltName", name);
373f4682712SMarri Devender Rao         }
374f4682712SMarri Devender Rao     }
375f4682712SMarri Devender Rao     addEntry(x509Name, "challengePassword", challengePassword);
376f4682712SMarri Devender Rao     addEntry(x509Name, "L", city);
377f4682712SMarri Devender Rao     addEntry(x509Name, "CN", commonName);
378f4682712SMarri Devender Rao     addEntry(x509Name, "name", contactPerson);
379f4682712SMarri Devender Rao     addEntry(x509Name, "C", country);
380f4682712SMarri Devender Rao     addEntry(x509Name, "emailAddress", email);
381f4682712SMarri Devender Rao     addEntry(x509Name, "GN", givenName);
382f4682712SMarri Devender Rao     addEntry(x509Name, "initials", initials);
383f4682712SMarri Devender Rao     addEntry(x509Name, "algorithm", keyPairAlgorithm);
384f4682712SMarri Devender Rao     if (!keyUsage.empty())
385f4682712SMarri Devender Rao     {
386f4682712SMarri Devender Rao         for (auto& usage : keyUsage)
387f4682712SMarri Devender Rao         {
3887641105dSMarri Devender Rao             if (isExtendedKeyUsage(usage))
3897641105dSMarri Devender Rao             {
3907641105dSMarri Devender Rao                 addEntry(x509Name, "extendedKeyUsage", usage);
3917641105dSMarri Devender Rao             }
3927641105dSMarri Devender Rao             else
3937641105dSMarri Devender Rao             {
394f4682712SMarri Devender Rao                 addEntry(x509Name, "keyUsage", usage);
395f4682712SMarri Devender Rao             }
396f4682712SMarri Devender Rao         }
3977641105dSMarri Devender Rao     }
398f4682712SMarri Devender Rao     addEntry(x509Name, "O", organization);
399dc91fb61SJayanth Othayoth     addEntry(x509Name, "OU", organizationalUnit);
400f4682712SMarri Devender Rao     addEntry(x509Name, "ST", state);
401f4682712SMarri Devender Rao     addEntry(x509Name, "SN", surname);
402f4682712SMarri Devender Rao     addEntry(x509Name, "unstructuredName", unstructuredName);
403f4682712SMarri Devender Rao 
404*cf06ccdcSNan Zhou     EVPPkeyPtr pKey(nullptr, ::EVP_PKEY_free);
4058a09b52aSRamesh Iyyar 
4068a09b52aSRamesh Iyyar     log<level::INFO>("Given Key pair algorithm",
4078a09b52aSRamesh Iyyar                      entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
4088a09b52aSRamesh Iyyar 
4098a09b52aSRamesh Iyyar     // Used EC algorithm as default if user did not give algorithm type.
4108a09b52aSRamesh Iyyar     if (keyPairAlgorithm == "RSA")
411c6e58c7eSRamesh Iyyar         pKey = getRSAKeyPair(keyBitLength);
4128a09b52aSRamesh Iyyar     else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
413c6e58c7eSRamesh Iyyar         pKey = generateECKeyPair(keyCurveId);
4148a09b52aSRamesh Iyyar     else
4158a09b52aSRamesh Iyyar     {
4168a09b52aSRamesh Iyyar         log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
4178a09b52aSRamesh Iyyar                         "RSA and EC only");
4188a09b52aSRamesh Iyyar         elog<InvalidArgument>(
4198a09b52aSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
4208a09b52aSRamesh Iyyar             Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
4218a09b52aSRamesh Iyyar     }
4228a09b52aSRamesh Iyyar 
4238a09b52aSRamesh Iyyar     ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
4248a09b52aSRamesh Iyyar     if (ret == 0)
4258a09b52aSRamesh Iyyar     {
426bf3cf751SNan Zhou         log<level::ERR>("Error occurred while setting Public key");
4278a09b52aSRamesh Iyyar         elog<InternalFailure>();
4288a09b52aSRamesh Iyyar     }
4298a09b52aSRamesh Iyyar 
4308a09b52aSRamesh Iyyar     // Write private key to file
431718eef37SNan Zhou     writePrivateKey(pKey, defaultPrivateKeyFileName);
432f4682712SMarri Devender Rao 
433f4682712SMarri Devender Rao     // set sign key of x509 req
434f4682712SMarri Devender Rao     ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
4358a09b52aSRamesh Iyyar     if (ret == 0)
436f4682712SMarri Devender Rao     {
437bf3cf751SNan Zhou         log<level::ERR>("Error occurred while signing key of x509");
438f4682712SMarri Devender Rao         elog<InternalFailure>();
439f4682712SMarri Devender Rao     }
4408a09b52aSRamesh Iyyar 
441f4682712SMarri Devender Rao     log<level::INFO>("Writing CSR to file");
442718eef37SNan Zhou     fs::path csrFilePath = certParentInstallPath / defaultCSRFileName;
443c6e58c7eSRamesh Iyyar     writeCSR(csrFilePath.string(), x509Req);
444f4682712SMarri Devender Rao }
445f4682712SMarri Devender Rao 
4467641105dSMarri Devender Rao bool Manager::isExtendedKeyUsage(const std::string& usage)
4477641105dSMarri Devender Rao {
4487641105dSMarri Devender Rao     const static std::array<const char*, 6> usageList = {
4497641105dSMarri Devender Rao         "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
4507641105dSMarri Devender Rao         "Timestamping",         "CodeSigning",          "EmailProtection"};
4517641105dSMarri Devender Rao     auto it = std::find_if(
4527641105dSMarri Devender Rao         usageList.begin(), usageList.end(),
4537641105dSMarri Devender Rao         [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
4547641105dSMarri Devender Rao     return it != usageList.end();
4557641105dSMarri Devender Rao }
456*cf06ccdcSNan Zhou EVPPkeyPtr Manager::generateRSAKeyPair(const int64_t keyBitLength)
457f4682712SMarri Devender Rao {
4588a09b52aSRamesh Iyyar     int64_t keyBitLen = keyBitLength;
459f4682712SMarri Devender Rao     // set keybit length to default value if not set
4608a09b52aSRamesh Iyyar     if (keyBitLen <= 0)
461f4682712SMarri Devender Rao     {
4628a09b52aSRamesh Iyyar         log<level::INFO>(
4638a09b52aSRamesh Iyyar             "KeyBitLength is not given.Hence, using default KeyBitLength",
464*cf06ccdcSNan Zhou             entry("DEFAULTKEYBITLENGTH=%d", defaultKeyBitLength));
465*cf06ccdcSNan Zhou         keyBitLen = defaultKeyBitLength;
466f4682712SMarri Devender Rao     }
46726fb83efSPatrick Williams 
46826fb83efSPatrick Williams #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
46926fb83efSPatrick Williams 
47026fb83efSPatrick Williams     // generate rsa key
471*cf06ccdcSNan Zhou     BignumPtr bne(BN_new(), ::BN_free);
47226fb83efSPatrick Williams     auto ret = BN_set_word(bne.get(), RSA_F4);
47326fb83efSPatrick Williams     if (ret == 0)
47426fb83efSPatrick Williams     {
475bf3cf751SNan Zhou         log<level::ERR>("Error occurred during BN_set_word call");
47626fb83efSPatrick Williams         elog<InternalFailure>();
47726fb83efSPatrick Williams     }
478762da74eSNan Zhou     using RSAPtr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
479762da74eSNan Zhou     RSAPtr rsa(RSA_new(), ::RSA_free);
480762da74eSNan Zhou     ret = RSA_generate_key_ex(rsa.get(), keyBitLen, bne.get(), nullptr);
481f4682712SMarri Devender Rao     if (ret != 1)
482f4682712SMarri Devender Rao     {
483bf3cf751SNan Zhou         log<level::ERR>("Error occurred during RSA_generate_key_ex call",
4848a09b52aSRamesh Iyyar                         entry("KEYBITLENGTH=%PRIu64", keyBitLen));
485f4682712SMarri Devender Rao         elog<InternalFailure>();
486f4682712SMarri Devender Rao     }
487f4682712SMarri Devender Rao 
488f4682712SMarri Devender Rao     // set public key of x509 req
489*cf06ccdcSNan Zhou     EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
490762da74eSNan Zhou     ret = EVP_PKEY_assign_RSA(pKey.get(), rsa.get());
491f4682712SMarri Devender Rao     if (ret == 0)
492f4682712SMarri Devender Rao     {
493bf3cf751SNan Zhou         log<level::ERR>("Error occurred during assign rsa key into EVP");
494f4682712SMarri Devender Rao         elog<InternalFailure>();
495f4682712SMarri Devender Rao     }
496762da74eSNan Zhou     // Now |rsa| is managed by |pKey|
497762da74eSNan Zhou     rsa.release();
4988a09b52aSRamesh Iyyar     return pKey;
49926fb83efSPatrick Williams 
50026fb83efSPatrick Williams #else
50126fb83efSPatrick Williams     auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
50226fb83efSPatrick Williams         EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
50326fb83efSPatrick Williams     if (!ctx)
50426fb83efSPatrick Williams     {
505bf3cf751SNan Zhou         log<level::ERR>("Error occurred creating EVP_PKEY_CTX from algorithm");
50626fb83efSPatrick Williams         elog<InternalFailure>();
50726fb83efSPatrick Williams     }
50826fb83efSPatrick Williams 
50926fb83efSPatrick Williams     if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
51026fb83efSPatrick Williams         (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
51126fb83efSPatrick Williams 
51226fb83efSPatrick Williams     {
513bf3cf751SNan Zhou         log<level::ERR>("Error occurred initializing keygen context");
51426fb83efSPatrick Williams         elog<InternalFailure>();
51526fb83efSPatrick Williams     }
51626fb83efSPatrick Williams 
51726fb83efSPatrick Williams     EVP_PKEY* pKey = nullptr;
51826fb83efSPatrick Williams     if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
51926fb83efSPatrick Williams     {
520bf3cf751SNan Zhou         log<level::ERR>("Error occurred during generate EC key");
52126fb83efSPatrick Williams         elog<InternalFailure>();
52226fb83efSPatrick Williams     }
52326fb83efSPatrick Williams 
52426fb83efSPatrick Williams     return {pKey, &::EVP_PKEY_free};
52526fb83efSPatrick Williams #endif
5268a09b52aSRamesh Iyyar }
5278a09b52aSRamesh Iyyar 
528*cf06ccdcSNan Zhou EVPPkeyPtr Manager::generateECKeyPair(const std::string& curveId)
5298a09b52aSRamesh Iyyar {
5308a09b52aSRamesh Iyyar     std::string curId(curveId);
5318a09b52aSRamesh Iyyar 
5328a09b52aSRamesh Iyyar     if (curId.empty())
5338a09b52aSRamesh Iyyar     {
5348a09b52aSRamesh Iyyar         log<level::INFO>(
5358a09b52aSRamesh Iyyar             "KeyCurveId is not given. Hence using default curve id",
536*cf06ccdcSNan Zhou             entry("DEFAULTKEYCURVEID=%s", defaultKeyCurveID));
537*cf06ccdcSNan Zhou         curId = defaultKeyCurveID;
5388a09b52aSRamesh Iyyar     }
5398a09b52aSRamesh Iyyar 
5408a09b52aSRamesh Iyyar     int ecGrp = OBJ_txt2nid(curId.c_str());
5418a09b52aSRamesh Iyyar     if (ecGrp == NID_undef)
5428a09b52aSRamesh Iyyar     {
5438a09b52aSRamesh Iyyar         log<level::ERR>(
544bf3cf751SNan Zhou             "Error occurred during convert the curve id string format into NID",
5458a09b52aSRamesh Iyyar             entry("KEYCURVEID=%s", curId.c_str()));
5468a09b52aSRamesh Iyyar         elog<InternalFailure>();
5478a09b52aSRamesh Iyyar     }
5488a09b52aSRamesh Iyyar 
54926fb83efSPatrick Williams #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
55026fb83efSPatrick Williams 
5518a09b52aSRamesh Iyyar     EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
5528a09b52aSRamesh Iyyar 
553cfb5802aSNan Zhou     if (ecKey == nullptr)
5548a09b52aSRamesh Iyyar     {
5558a09b52aSRamesh Iyyar         log<level::ERR>(
556bf3cf751SNan Zhou             "Error occurred during create the EC_Key object from NID",
5578a09b52aSRamesh Iyyar             entry("ECGROUP=%d", ecGrp));
5588a09b52aSRamesh Iyyar         elog<InternalFailure>();
5598a09b52aSRamesh Iyyar     }
5608a09b52aSRamesh Iyyar 
5618a09b52aSRamesh Iyyar     // If you want to save a key and later load it with
5628a09b52aSRamesh Iyyar     // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
5638a09b52aSRamesh Iyyar     // flag on the key.
5648a09b52aSRamesh Iyyar     EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
5658a09b52aSRamesh Iyyar 
5668a09b52aSRamesh Iyyar     int ret = EC_KEY_generate_key(ecKey);
5678a09b52aSRamesh Iyyar 
5688a09b52aSRamesh Iyyar     if (ret == 0)
5698a09b52aSRamesh Iyyar     {
5708a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
571bf3cf751SNan Zhou         log<level::ERR>("Error occurred during generate EC key");
5728a09b52aSRamesh Iyyar         elog<InternalFailure>();
5738a09b52aSRamesh Iyyar     }
5748a09b52aSRamesh Iyyar 
575*cf06ccdcSNan Zhou     EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
5768a09b52aSRamesh Iyyar     ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
5778a09b52aSRamesh Iyyar     if (ret == 0)
5788a09b52aSRamesh Iyyar     {
5798a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
580bf3cf751SNan Zhou         log<level::ERR>("Error occurred during assign EC Key into EVP");
5818a09b52aSRamesh Iyyar         elog<InternalFailure>();
5828a09b52aSRamesh Iyyar     }
5838a09b52aSRamesh Iyyar 
5848a09b52aSRamesh Iyyar     return pKey;
58526fb83efSPatrick Williams 
58626fb83efSPatrick Williams #else
58726fb83efSPatrick Williams     auto holder_of_key = [](EVP_PKEY* key) {
58826fb83efSPatrick Williams         return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
58926fb83efSPatrick Williams             key, &::EVP_PKEY_free};
59026fb83efSPatrick Williams     };
59126fb83efSPatrick Williams 
59226fb83efSPatrick Williams     // Create context to set up curve parameters.
59326fb83efSPatrick Williams     auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
59426fb83efSPatrick Williams         EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
59526fb83efSPatrick Williams     if (!ctx)
59626fb83efSPatrick Williams     {
597bf3cf751SNan Zhou         log<level::ERR>("Error occurred creating EVP_PKEY_CTX for params");
59826fb83efSPatrick Williams         elog<InternalFailure>();
59926fb83efSPatrick Williams     }
60026fb83efSPatrick Williams 
60126fb83efSPatrick Williams     // Set up curve parameters.
60226fb83efSPatrick Williams     EVP_PKEY* params = nullptr;
60326fb83efSPatrick Williams 
60426fb83efSPatrick Williams     if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
60526fb83efSPatrick Williams         (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
60626fb83efSPatrick Williams          0) ||
60726fb83efSPatrick Williams         (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
60826fb83efSPatrick Williams         (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
60926fb83efSPatrick Williams     {
610bf3cf751SNan Zhou         log<level::ERR>("Error occurred setting curve parameters");
61126fb83efSPatrick Williams         elog<InternalFailure>();
61226fb83efSPatrick Williams     }
61326fb83efSPatrick Williams 
61426fb83efSPatrick Williams     // Move parameters to RAII holder.
61526fb83efSPatrick Williams     auto pparms = holder_of_key(params);
61626fb83efSPatrick Williams 
61726fb83efSPatrick Williams     // Create new context for key.
61826fb83efSPatrick Williams     ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
61926fb83efSPatrick Williams 
62026fb83efSPatrick Williams     if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
62126fb83efSPatrick Williams     {
622bf3cf751SNan Zhou         log<level::ERR>("Error occurred initializing keygen context");
62326fb83efSPatrick Williams         elog<InternalFailure>();
62426fb83efSPatrick Williams     }
62526fb83efSPatrick Williams 
62626fb83efSPatrick Williams     EVP_PKEY* pKey = nullptr;
62726fb83efSPatrick Williams     if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
62826fb83efSPatrick Williams     {
629bf3cf751SNan Zhou         log<level::ERR>("Error occurred during generate EC key");
63026fb83efSPatrick Williams         elog<InternalFailure>();
63126fb83efSPatrick Williams     }
63226fb83efSPatrick Williams 
63326fb83efSPatrick Williams     return holder_of_key(pKey);
63426fb83efSPatrick Williams #endif
6358a09b52aSRamesh Iyyar }
6368a09b52aSRamesh Iyyar 
637*cf06ccdcSNan Zhou void Manager::writePrivateKey(const EVPPkeyPtr& pKey,
638c6e58c7eSRamesh Iyyar                               const std::string& privKeyFileName)
6398a09b52aSRamesh Iyyar {
6408a09b52aSRamesh Iyyar     log<level::INFO>("Writing private key to file");
641f4682712SMarri Devender Rao     // write private key to file
642c6e58c7eSRamesh Iyyar     fs::path privKeyPath = certParentInstallPath / privKeyFileName;
643f4682712SMarri Devender Rao 
644f4682712SMarri Devender Rao     FILE* fp = std::fopen(privKeyPath.c_str(), "w");
645cfb5802aSNan Zhou     if (fp == nullptr)
646f4682712SMarri Devender Rao     {
647bf3cf751SNan Zhou         log<level::ERR>("Error occurred creating private key file");
648f4682712SMarri Devender Rao         elog<InternalFailure>();
649f4682712SMarri Devender Rao     }
650cfb5802aSNan Zhou     int ret =
651cfb5802aSNan Zhou         PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0, nullptr);
652f4682712SMarri Devender Rao     std::fclose(fp);
653f4682712SMarri Devender Rao     if (ret == 0)
654f4682712SMarri Devender Rao     {
655bf3cf751SNan Zhou         log<level::ERR>("Error occurred while writing private key to file");
656f4682712SMarri Devender Rao         elog<InternalFailure>();
657f4682712SMarri Devender Rao     }
658f4682712SMarri Devender Rao }
659f4682712SMarri Devender Rao 
660f4682712SMarri Devender Rao void Manager::addEntry(X509_NAME* x509Name, const char* field,
661f4682712SMarri Devender Rao                        const std::string& bytes)
662f4682712SMarri Devender Rao {
663f4682712SMarri Devender Rao     if (bytes.empty())
664f4682712SMarri Devender Rao     {
665f4682712SMarri Devender Rao         return;
666f4682712SMarri Devender Rao     }
667f4682712SMarri Devender Rao     int ret = X509_NAME_add_entry_by_txt(
668f4682712SMarri Devender Rao         x509Name, field, MBSTRING_ASC,
669f4682712SMarri Devender Rao         reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
670f4682712SMarri Devender Rao     if (ret != 1)
671f4682712SMarri Devender Rao     {
672f4682712SMarri Devender Rao         log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
673f4682712SMarri Devender Rao                         entry("VALUE=%s", bytes.c_str()));
674f4682712SMarri Devender Rao         elog<InternalFailure>();
675f4682712SMarri Devender Rao     }
676f4682712SMarri Devender Rao }
677f4682712SMarri Devender Rao 
678f4682712SMarri Devender Rao void Manager::createCSRObject(const Status& status)
679f4682712SMarri Devender Rao {
680f4682712SMarri Devender Rao     if (csrPtr)
681f4682712SMarri Devender Rao     {
682f4682712SMarri Devender Rao         csrPtr.reset(nullptr);
683f4682712SMarri Devender Rao     }
684f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
685f4682712SMarri Devender Rao     csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
686f4682712SMarri Devender Rao                                    certInstallPath.c_str(), status);
687f4682712SMarri Devender Rao }
688f4682712SMarri Devender Rao 
689*cf06ccdcSNan Zhou void Manager::writeCSR(const std::string& filePath, const X509ReqPtr& x509Req)
690f4682712SMarri Devender Rao {
691f4682712SMarri Devender Rao     if (fs::exists(filePath))
692f4682712SMarri Devender Rao     {
693f4682712SMarri Devender Rao         log<level::INFO>("Removing the existing file",
694f4682712SMarri Devender Rao                          entry("FILENAME=%s", filePath.c_str()));
695f4682712SMarri Devender Rao         if (!fs::remove(filePath.c_str()))
696f4682712SMarri Devender Rao         {
697f4682712SMarri Devender Rao             log<level::ERR>("Unable to remove the file",
698f4682712SMarri Devender Rao                             entry("FILENAME=%s", filePath.c_str()));
699f4682712SMarri Devender Rao             elog<InternalFailure>();
700f4682712SMarri Devender Rao         }
701f4682712SMarri Devender Rao     }
702f4682712SMarri Devender Rao 
703cfb5802aSNan Zhou     FILE* fp = nullptr;
704f4682712SMarri Devender Rao 
705cfb5802aSNan Zhou     if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
706f4682712SMarri Devender Rao     {
707f4682712SMarri Devender Rao         log<level::ERR>("Error opening the file to write the CSR",
708f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
709f4682712SMarri Devender Rao         elog<InternalFailure>();
710f4682712SMarri Devender Rao     }
711f4682712SMarri Devender Rao 
712f4682712SMarri Devender Rao     int rc = PEM_write_X509_REQ(fp, x509Req.get());
713f4682712SMarri Devender Rao     if (!rc)
714f4682712SMarri Devender Rao     {
715f4682712SMarri Devender Rao         log<level::ERR>("PEM write routine failed",
716f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
717f4682712SMarri Devender Rao         std::fclose(fp);
718f4682712SMarri Devender Rao         elog<InternalFailure>();
719f4682712SMarri Devender Rao     }
720f4682712SMarri Devender Rao     std::fclose(fp);
721f4682712SMarri Devender Rao }
722f4682712SMarri Devender Rao 
723db029c95SKowalski, Kamil void Manager::createCertificates()
724db029c95SKowalski, Kamil {
725db029c95SKowalski, Kamil     auto certObjectPath = objectPath + '/';
726db029c95SKowalski, Kamil 
727*cf06ccdcSNan Zhou     if (certType == CertificateType::Authority)
728db029c95SKowalski, Kamil     {
729fe590c4eSZbigniew Lukwinski         // Check whether install path is a directory.
730db029c95SKowalski, Kamil         if (!fs::is_directory(certInstallPath))
731db029c95SKowalski, Kamil         {
732db029c95SKowalski, Kamil             log<level::ERR>("Certificate installation path exists and it is "
733db029c95SKowalski, Kamil                             "not a directory");
734db029c95SKowalski, Kamil             elog<InternalFailure>();
735db029c95SKowalski, Kamil             return;
736db029c95SKowalski, Kamil         }
737db029c95SKowalski, Kamil 
738db029c95SKowalski, Kamil         for (auto& path : fs::directory_iterator(certInstallPath))
739ffad1ef1SMarri Devender Rao         {
740ffad1ef1SMarri Devender Rao             try
741ffad1ef1SMarri Devender Rao             {
7422f3563ccSZbigniew Lukwinski                 // Assume here any regular file located in certificate directory
7432f3563ccSZbigniew Lukwinski                 // contains certificates body. Do not want to use soft links
7442f3563ccSZbigniew Lukwinski                 // would add value.
7452f3563ccSZbigniew Lukwinski                 if (fs::is_regular_file(path))
7462f3563ccSZbigniew Lukwinski                 {
747db029c95SKowalski, Kamil                     installedCerts.emplace_back(std::make_unique<Certificate>(
748db029c95SKowalski, Kamil                         bus, certObjectPath + std::to_string(certIdCounter++),
749*cf06ccdcSNan Zhou                         certType, certInstallPath, path.path(),
750*cf06ccdcSNan Zhou                         certWatchPtr.get(), *this));
7512f3563ccSZbigniew Lukwinski                 }
752ffad1ef1SMarri Devender Rao             }
753ffad1ef1SMarri Devender Rao             catch (const InternalFailure& e)
754ffad1ef1SMarri Devender Rao             {
755ffad1ef1SMarri Devender Rao                 report<InternalFailure>();
756ffad1ef1SMarri Devender Rao             }
757ffad1ef1SMarri Devender Rao             catch (const InvalidCertificate& e)
758ffad1ef1SMarri Devender Rao             {
759*cf06ccdcSNan Zhou                 report<InvalidCertificate>(InvalidCertificateReason(
760*cf06ccdcSNan Zhou                     "Existing certificate file is corrupted"));
761ffad1ef1SMarri Devender Rao             }
762ffad1ef1SMarri Devender Rao         }
763db029c95SKowalski, Kamil     }
764db029c95SKowalski, Kamil     else if (fs::exists(certInstallPath))
765db029c95SKowalski, Kamil     {
766db029c95SKowalski, Kamil         try
767db029c95SKowalski, Kamil         {
768db029c95SKowalski, Kamil             installedCerts.emplace_back(std::make_unique<Certificate>(
7692f3563ccSZbigniew Lukwinski                 bus, certObjectPath + '1', certType, certInstallPath,
770*cf06ccdcSNan Zhou                 certInstallPath, certWatchPtr.get(), *this));
771db029c95SKowalski, Kamil         }
772db029c95SKowalski, Kamil         catch (const InternalFailure& e)
773db029c95SKowalski, Kamil         {
774db029c95SKowalski, Kamil             report<InternalFailure>();
775db029c95SKowalski, Kamil         }
776db029c95SKowalski, Kamil         catch (const InvalidCertificate& e)
777db029c95SKowalski, Kamil         {
778*cf06ccdcSNan Zhou             report<InvalidCertificate>(InvalidCertificateReason(
779*cf06ccdcSNan Zhou                 "Existing certificate file is corrupted"));
780db029c95SKowalski, Kamil         }
781db029c95SKowalski, Kamil     }
782db029c95SKowalski, Kamil }
783c6e58c7eSRamesh Iyyar 
784c6e58c7eSRamesh Iyyar void Manager::createRSAPrivateKeyFile()
785c6e58c7eSRamesh Iyyar {
786c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
787718eef37SNan Zhou         certParentInstallPath / defaultRSAPrivateKeyFileName;
788c6e58c7eSRamesh Iyyar 
789c6e58c7eSRamesh Iyyar     try
790c6e58c7eSRamesh Iyyar     {
791c6e58c7eSRamesh Iyyar         if (!fs::exists(rsaPrivateKeyFileName))
792c6e58c7eSRamesh Iyyar         {
793*cf06ccdcSNan Zhou             writePrivateKey(generateRSAKeyPair(supportedKeyBitLength),
794718eef37SNan Zhou                             defaultRSAPrivateKeyFileName);
795c6e58c7eSRamesh Iyyar         }
796c6e58c7eSRamesh Iyyar     }
797c6e58c7eSRamesh Iyyar     catch (const InternalFailure& e)
798c6e58c7eSRamesh Iyyar     {
799c6e58c7eSRamesh Iyyar         report<InternalFailure>();
800c6e58c7eSRamesh Iyyar     }
801c6e58c7eSRamesh Iyyar }
802c6e58c7eSRamesh Iyyar 
803*cf06ccdcSNan Zhou EVPPkeyPtr Manager::getRSAKeyPair(const int64_t keyBitLength)
804c6e58c7eSRamesh Iyyar {
805*cf06ccdcSNan Zhou     if (keyBitLength != supportedKeyBitLength)
806c6e58c7eSRamesh Iyyar     {
807c6e58c7eSRamesh Iyyar         log<level::ERR>(
808c6e58c7eSRamesh Iyyar             "Given Key bit length is not supported",
809c6e58c7eSRamesh Iyyar             entry("GIVENKEYBITLENGTH=%d", keyBitLength),
810*cf06ccdcSNan Zhou             entry("SUPPORTEDKEYBITLENGTH=%d", supportedKeyBitLength));
811c6e58c7eSRamesh Iyyar         elog<InvalidArgument>(
812c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYBITLENGTH"),
813c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
814c6e58c7eSRamesh Iyyar     }
815c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
816718eef37SNan Zhou         certParentInstallPath / defaultRSAPrivateKeyFileName;
817c6e58c7eSRamesh Iyyar 
818c6e58c7eSRamesh Iyyar     FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
819c6e58c7eSRamesh Iyyar     if (!privateKeyFile)
820c6e58c7eSRamesh Iyyar     {
821c6e58c7eSRamesh Iyyar         log<level::ERR>("Unable to open RSA private key file to read",
822c6e58c7eSRamesh Iyyar                         entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
823c6e58c7eSRamesh Iyyar                         entry("ERRORREASON=%s", strerror(errno)));
824c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
825c6e58c7eSRamesh Iyyar     }
826c6e58c7eSRamesh Iyyar 
827*cf06ccdcSNan Zhou     EVPPkeyPtr privateKey(
828c6e58c7eSRamesh Iyyar         PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
829c6e58c7eSRamesh Iyyar         ::EVP_PKEY_free);
830c6e58c7eSRamesh Iyyar     std::fclose(privateKeyFile);
831c6e58c7eSRamesh Iyyar 
832c6e58c7eSRamesh Iyyar     if (!privateKey)
833c6e58c7eSRamesh Iyyar     {
834bf3cf751SNan Zhou         log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
835c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
836c6e58c7eSRamesh Iyyar     }
837c6e58c7eSRamesh Iyyar     return privateKey;
838c6e58c7eSRamesh Iyyar }
8392f3563ccSZbigniew Lukwinski 
8402f3563ccSZbigniew Lukwinski void Manager::storageUpdate()
8412f3563ccSZbigniew Lukwinski {
842*cf06ccdcSNan Zhou     if (certType == CertificateType::Authority)
8432f3563ccSZbigniew Lukwinski     {
8442f3563ccSZbigniew Lukwinski         // Remove symbolic links in the certificate directory
8452f3563ccSZbigniew Lukwinski         for (auto& certPath : fs::directory_iterator(certInstallPath))
8462f3563ccSZbigniew Lukwinski         {
8472f3563ccSZbigniew Lukwinski             try
8482f3563ccSZbigniew Lukwinski             {
8492f3563ccSZbigniew Lukwinski                 if (fs::is_symlink(certPath))
8502f3563ccSZbigniew Lukwinski                 {
8512f3563ccSZbigniew Lukwinski                     fs::remove(certPath);
8522f3563ccSZbigniew Lukwinski                 }
8532f3563ccSZbigniew Lukwinski             }
8542f3563ccSZbigniew Lukwinski             catch (const std::exception& e)
8552f3563ccSZbigniew Lukwinski             {
8562f3563ccSZbigniew Lukwinski                 log<level::ERR>(
8572f3563ccSZbigniew Lukwinski                     "Failed to remove symlink for certificate",
8582f3563ccSZbigniew Lukwinski                     entry("ERR=%s", e.what()),
8592f3563ccSZbigniew Lukwinski                     entry("SYMLINK=%s", certPath.path().string().c_str()));
8602f3563ccSZbigniew Lukwinski                 elog<InternalFailure>();
8612f3563ccSZbigniew Lukwinski             }
8622f3563ccSZbigniew Lukwinski         }
8632f3563ccSZbigniew Lukwinski     }
8642f3563ccSZbigniew Lukwinski 
8652f3563ccSZbigniew Lukwinski     for (const auto& cert : installedCerts)
8662f3563ccSZbigniew Lukwinski     {
8672f3563ccSZbigniew Lukwinski         cert->storageUpdate();
8682f3563ccSZbigniew Lukwinski     }
8692f3563ccSZbigniew Lukwinski }
8702f3563ccSZbigniew Lukwinski 
871*cf06ccdcSNan Zhou void Manager::reloadOrReset(const std::string& unit)
8722f3563ccSZbigniew Lukwinski {
8732f3563ccSZbigniew Lukwinski     if (!unit.empty())
8742f3563ccSZbigniew Lukwinski     {
8752f3563ccSZbigniew Lukwinski         try
8762f3563ccSZbigniew Lukwinski         {
877*cf06ccdcSNan Zhou             constexpr auto defaultSystemdService = "org.freedesktop.systemd1";
878*cf06ccdcSNan Zhou             constexpr auto defaultSystemdObjectPath =
879*cf06ccdcSNan Zhou                 "/org/freedesktop/systemd1";
880*cf06ccdcSNan Zhou             constexpr auto defaultSystemdInterface =
8812f3563ccSZbigniew Lukwinski                 "org.freedesktop.systemd1.Manager";
882*cf06ccdcSNan Zhou             auto method = bus.new_method_call(
883*cf06ccdcSNan Zhou                 defaultSystemdService, defaultSystemdObjectPath,
884*cf06ccdcSNan Zhou                 defaultSystemdInterface, "ReloadOrRestartUnit");
8852f3563ccSZbigniew Lukwinski             method.append(unit, "replace");
8862f3563ccSZbigniew Lukwinski             bus.call_noreply(method);
8872f3563ccSZbigniew Lukwinski         }
888ca128117SPatrick Williams         catch (const sdbusplus::exception::exception& e)
8892f3563ccSZbigniew Lukwinski         {
8902f3563ccSZbigniew Lukwinski             log<level::ERR>("Failed to reload or restart service",
8912f3563ccSZbigniew Lukwinski                             entry("ERR=%s", e.what()),
8922f3563ccSZbigniew Lukwinski                             entry("UNIT=%s", unit.c_str()));
8932f3563ccSZbigniew Lukwinski             elog<InternalFailure>();
8942f3563ccSZbigniew Lukwinski         }
8952f3563ccSZbigniew Lukwinski     }
8962f3563ccSZbigniew Lukwinski }
8972f3563ccSZbigniew Lukwinski 
8982f3563ccSZbigniew Lukwinski bool Manager::isCertificateUnique(const std::string& filePath,
8992f3563ccSZbigniew Lukwinski                                   const Certificate* const certToDrop)
9002f3563ccSZbigniew Lukwinski {
9012f3563ccSZbigniew Lukwinski     if (std::any_of(
9022f3563ccSZbigniew Lukwinski             installedCerts.begin(), installedCerts.end(),
9032f3563ccSZbigniew Lukwinski             [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
9042f3563ccSZbigniew Lukwinski                 return cert.get() != certToDrop && cert->isSame(filePath);
9052f3563ccSZbigniew Lukwinski             }))
9062f3563ccSZbigniew Lukwinski     {
9072f3563ccSZbigniew Lukwinski         return false;
9082f3563ccSZbigniew Lukwinski     }
9092f3563ccSZbigniew Lukwinski     else
9102f3563ccSZbigniew Lukwinski     {
9112f3563ccSZbigniew Lukwinski         return true;
9122f3563ccSZbigniew Lukwinski     }
9132f3563ccSZbigniew Lukwinski }
9142f3563ccSZbigniew Lukwinski 
915e1289adfSNan Zhou } // namespace phosphor::certs
916