xref: /openbmc/phosphor-certificate-manager/certs_manager.cpp (revision e1289adf5bf18a55abad1a5a032e13a7f564b269)
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 
12*e1289adfSNan Zhou namespace phosphor::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 {
36db5c6fc8SMarri Devender Rao     try
37db5c6fc8SMarri Devender Rao     {
38fe590c4eSZbigniew Lukwinski         // Create certificate directory if not existing.
39bf3cf751SNan Zhou         // Set correct certificate directory permissions.
40fe590c4eSZbigniew Lukwinski         fs::path certDirectory;
41b57d75e2SMarri Devender Rao         try
42b57d75e2SMarri Devender Rao         {
43fe590c4eSZbigniew Lukwinski             if (certType == AUTHORITY)
44b57d75e2SMarri Devender Rao             {
45fe590c4eSZbigniew Lukwinski                 certDirectory = certInstallPath;
46b57d75e2SMarri Devender Rao             }
47fe590c4eSZbigniew Lukwinski             else
48fe590c4eSZbigniew Lukwinski             {
49fe590c4eSZbigniew Lukwinski                 certDirectory = certParentInstallPath;
50fe590c4eSZbigniew Lukwinski             }
51fe590c4eSZbigniew Lukwinski 
52fe590c4eSZbigniew Lukwinski             if (!fs::exists(certDirectory))
53fe590c4eSZbigniew Lukwinski             {
54fe590c4eSZbigniew Lukwinski                 fs::create_directories(certDirectory);
55fe590c4eSZbigniew Lukwinski             }
56fe590c4eSZbigniew Lukwinski 
57667286e4SMarri Devender Rao             auto permission = fs::perms::owner_read | fs::perms::owner_write |
58667286e4SMarri Devender Rao                               fs::perms::owner_exec;
59db5c6fc8SMarri Devender Rao             fs::permissions(certDirectory, permission,
60db5c6fc8SMarri Devender Rao                             fs::perm_options::replace);
612f3563ccSZbigniew Lukwinski             storageUpdate();
62b57d75e2SMarri Devender Rao         }
6371957992SPatrick Williams         catch (const fs::filesystem_error& e)
64b57d75e2SMarri Devender Rao         {
65db5c6fc8SMarri Devender Rao             log<level::ERR>(
66db5c6fc8SMarri Devender Rao                 "Failed to create directory", entry("ERR=%s", e.what()),
67b57d75e2SMarri Devender Rao                 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
68b57d75e2SMarri Devender Rao             report<InternalFailure>();
69b57d75e2SMarri Devender Rao         }
70b57d75e2SMarri Devender Rao 
71c6e58c7eSRamesh Iyyar         // Generating RSA private key file if certificate type is server/client
72c6e58c7eSRamesh Iyyar         if (certType != AUTHORITY)
73c6e58c7eSRamesh Iyyar         {
74c6e58c7eSRamesh Iyyar             createRSAPrivateKeyFile();
75c6e58c7eSRamesh Iyyar         }
76c6e58c7eSRamesh Iyyar 
77ffad1ef1SMarri Devender Rao         // restore any existing certificates
78db029c95SKowalski, Kamil         createCertificates();
79ffad1ef1SMarri Devender Rao 
80ffad1ef1SMarri Devender Rao         // watch is not required for authority certificates
81ffad1ef1SMarri Devender Rao         if (certType != AUTHORITY)
82ffad1ef1SMarri Devender Rao         {
83ffad1ef1SMarri Devender Rao             // watch for certificate file create/replace
84ffad1ef1SMarri Devender Rao             certWatchPtr = std::make_unique<
85ffad1ef1SMarri Devender Rao                 Watch>(event, certInstallPath, [this]() {
86bf7c588cSMarri Devender Rao                 try
87bf7c588cSMarri Devender Rao                 {
88ffad1ef1SMarri Devender Rao                     // if certificate file existing update it
89db029c95SKowalski, Kamil                     if (!installedCerts.empty())
90ffad1ef1SMarri Devender Rao                     {
91db5c6fc8SMarri Devender Rao                         log<level::INFO>("Inotify callback to update "
92db5c6fc8SMarri Devender Rao                                          "certificate properties");
93db029c95SKowalski, Kamil                         installedCerts[0]->populateProperties();
94ffad1ef1SMarri Devender Rao                     }
95ffad1ef1SMarri Devender Rao                     else
96ffad1ef1SMarri Devender Rao                     {
97ffad1ef1SMarri Devender Rao                         log<level::INFO>(
98ffad1ef1SMarri Devender Rao                             "Inotify callback to create certificate object");
99db029c95SKowalski, Kamil                         createCertificates();
100ffad1ef1SMarri Devender Rao                     }
101bf7c588cSMarri Devender Rao                 }
102bf7c588cSMarri Devender Rao                 catch (const InternalFailure& e)
103bf7c588cSMarri Devender Rao                 {
104ffad1ef1SMarri Devender Rao                     commit<InternalFailure>();
105bf7c588cSMarri Devender Rao                 }
106bf7c588cSMarri Devender Rao                 catch (const InvalidCertificate& e)
107bf7c588cSMarri Devender Rao                 {
108ffad1ef1SMarri Devender Rao                     commit<InvalidCertificate>();
109bf7c588cSMarri Devender Rao                 }
110ffad1ef1SMarri Devender Rao             });
111bf7c588cSMarri Devender Rao         }
112db029c95SKowalski, Kamil         else
113db029c95SKowalski, Kamil         {
114db5c6fc8SMarri Devender Rao             try
115db5c6fc8SMarri Devender Rao             {
116db5c6fc8SMarri Devender Rao                 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
117db5c6fc8SMarri Devender Rao                 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
118db029c95SKowalski, Kamil                 {
119db029c95SKowalski, Kamil                     log<level::NOTICE>(
120db029c95SKowalski, Kamil                         "Legacy certificate detected, will be installed from: ",
121db5c6fc8SMarri Devender Rao                         entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
122db5c6fc8SMarri Devender Rao                     install(singleCertPath);
123db5c6fc8SMarri Devender Rao                     if (!fs::remove(singleCertPath))
124db029c95SKowalski, Kamil                     {
125db029c95SKowalski, Kamil                         log<level::ERR>(
126db029c95SKowalski, Kamil                             "Unable to remove old certificate from: ",
127db5c6fc8SMarri Devender Rao                             entry("SINGLE_CERTPATH=%s",
128db5c6fc8SMarri Devender Rao                                   singleCertPath.c_str()));
129db029c95SKowalski, Kamil                         elog<InternalFailure>();
130db029c95SKowalski, Kamil                     }
131db029c95SKowalski, Kamil                 }
132db029c95SKowalski, Kamil             }
133db5c6fc8SMarri Devender Rao             catch (const std::exception& ex)
134db5c6fc8SMarri Devender Rao             {
135db5c6fc8SMarri Devender Rao                 log<level::ERR>("Error in restoring legacy certificate",
136db5c6fc8SMarri Devender Rao                                 entry("ERROR_STR=%s", ex.what()));
137db5c6fc8SMarri Devender Rao             }
138db5c6fc8SMarri Devender Rao         }
139db5c6fc8SMarri Devender Rao     }
14071957992SPatrick Williams     catch (const std::exception& ex)
141db5c6fc8SMarri Devender Rao     {
142db5c6fc8SMarri Devender Rao         log<level::ERR>("Error in certificate manager constructor",
143db5c6fc8SMarri Devender Rao                         entry("ERROR_STR=%s", ex.what()));
144db5c6fc8SMarri Devender Rao     }
145dd74bd20SJayanth Othayoth }
146589159f2SJayanth Othayoth 
14706a69d7bSZbigniew Kurzynski std::string Manager::install(const std::string filePath)
148cfbc8dc8SJayanth Othayoth {
1491396511dSMarri Devender Rao     using NotAllowed =
1501396511dSMarri Devender Rao         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
1511396511dSMarri Devender Rao     using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
152db029c95SKowalski, Kamil 
153db029c95SKowalski, Kamil     if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
1541396511dSMarri Devender Rao     {
1551396511dSMarri Devender Rao         elog<NotAllowed>(Reason("Certificate already exist"));
1561396511dSMarri Devender Rao     }
1573b07b77aSZbigniew Lukwinski     else if (certType == phosphor::certs::AUTHORITY &&
1583b07b77aSZbigniew Lukwinski              installedCerts.size() >= AUTHORITY_CERTIFICATES_LIMIT)
1593b07b77aSZbigniew Lukwinski     {
1603b07b77aSZbigniew Lukwinski         elog<NotAllowed>(Reason("Certificates limit reached"));
1613b07b77aSZbigniew Lukwinski     }
162ffad1ef1SMarri Devender Rao 
1632f3563ccSZbigniew Lukwinski     std::string certObjectPath;
1642f3563ccSZbigniew Lukwinski     if (isCertificateUnique(filePath))
1652f3563ccSZbigniew Lukwinski     {
1662f3563ccSZbigniew Lukwinski         certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
167db029c95SKowalski, Kamil         installedCerts.emplace_back(std::make_unique<Certificate>(
1682f3563ccSZbigniew Lukwinski             bus, certObjectPath, certType, certInstallPath, filePath,
1692f3563ccSZbigniew Lukwinski             certWatchPtr, *this));
1702f3563ccSZbigniew Lukwinski         reloadOrReset(unitToRestart);
1712f3563ccSZbigniew Lukwinski         certIdCounter++;
1722f3563ccSZbigniew Lukwinski     }
1732f3563ccSZbigniew Lukwinski     else
1742f3563ccSZbigniew Lukwinski     {
1752f3563ccSZbigniew Lukwinski         elog<NotAllowed>(Reason("Certificate already exist"));
1762f3563ccSZbigniew Lukwinski     }
1772f3563ccSZbigniew Lukwinski 
17806a69d7bSZbigniew Kurzynski     return certObjectPath;
179589159f2SJayanth Othayoth }
180ae70b3daSDeepak Kodihalli 
181a3bb38fbSZbigniew Kurzynski void Manager::deleteAll()
182ae70b3daSDeepak Kodihalli {
1836ceec40bSMarri Devender Rao     // TODO: #Issue 4 when a certificate is deleted system auto generates
1846ceec40bSMarri Devender Rao     // certificate file. At present we are not supporting creation of
1856ceec40bSMarri Devender Rao     // certificate object for the auto-generated certificate file as
1866ceec40bSMarri Devender Rao     // deletion if only applicable for REST server and Bmcweb does not allow
1876ceec40bSMarri Devender Rao     // deletion of certificates
188db029c95SKowalski, Kamil     installedCerts.clear();
1892f3563ccSZbigniew Lukwinski     storageUpdate();
1902f3563ccSZbigniew Lukwinski     reloadOrReset(unitToRestart);
191ae70b3daSDeepak Kodihalli }
192f4682712SMarri Devender Rao 
1932f3563ccSZbigniew Lukwinski void Manager::deleteCertificate(const Certificate* const certificate)
194a3bb38fbSZbigniew Kurzynski {
195a3bb38fbSZbigniew Kurzynski     std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
196a3bb38fbSZbigniew Kurzynski         std::find_if(installedCerts.begin(), installedCerts.end(),
1972f3563ccSZbigniew Lukwinski                      [certificate](std::unique_ptr<Certificate> const& cert) {
1982f3563ccSZbigniew Lukwinski                          return (cert.get() == certificate);
199a3bb38fbSZbigniew Kurzynski                      });
200a3bb38fbSZbigniew Kurzynski     if (certIt != installedCerts.end())
201a3bb38fbSZbigniew Kurzynski     {
202a3bb38fbSZbigniew Kurzynski         installedCerts.erase(certIt);
2032f3563ccSZbigniew Lukwinski         storageUpdate();
2042f3563ccSZbigniew Lukwinski         reloadOrReset(unitToRestart);
205a3bb38fbSZbigniew Kurzynski     }
206a3bb38fbSZbigniew Kurzynski     else
207a3bb38fbSZbigniew Kurzynski     {
208a3bb38fbSZbigniew Kurzynski         log<level::ERR>("Certificate does not exist",
2092f3563ccSZbigniew Lukwinski                         entry("ID=%s", certificate->getCertId().c_str()));
2102f3563ccSZbigniew Lukwinski         elog<InternalFailure>();
2112f3563ccSZbigniew Lukwinski     }
2122f3563ccSZbigniew Lukwinski }
2132f3563ccSZbigniew Lukwinski 
2142f3563ccSZbigniew Lukwinski void Manager::replaceCertificate(Certificate* const certificate,
2152f3563ccSZbigniew Lukwinski                                  const std::string& filePath)
2162f3563ccSZbigniew Lukwinski {
2172f3563ccSZbigniew Lukwinski     if (isCertificateUnique(filePath, certificate))
2182f3563ccSZbigniew Lukwinski     {
2192f3563ccSZbigniew Lukwinski         certificate->install(filePath);
2202f3563ccSZbigniew Lukwinski         storageUpdate();
2212f3563ccSZbigniew Lukwinski         reloadOrReset(unitToRestart);
2222f3563ccSZbigniew Lukwinski     }
2232f3563ccSZbigniew Lukwinski     else
2242f3563ccSZbigniew Lukwinski     {
22515cbbec2SZbigniew Lukwinski         using NotAllowed =
22615cbbec2SZbigniew Lukwinski             sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
22715cbbec2SZbigniew Lukwinski         using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
22815cbbec2SZbigniew Lukwinski 
22915cbbec2SZbigniew Lukwinski         elog<NotAllowed>(Reason("Certificate already exist"));
230a3bb38fbSZbigniew Kurzynski     }
231a3bb38fbSZbigniew Kurzynski }
232a3bb38fbSZbigniew Kurzynski 
233f4682712SMarri Devender Rao std::string Manager::generateCSR(
234f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
235f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
236f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
237f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
238f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
239f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
240f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
241f4682712SMarri Devender Rao {
242f4682712SMarri Devender Rao     // We support only one CSR.
243f4682712SMarri Devender Rao     csrPtr.reset(nullptr);
244f4682712SMarri Devender Rao     auto pid = fork();
245f4682712SMarri Devender Rao     if (pid == -1)
246f4682712SMarri Devender Rao     {
247f4682712SMarri Devender Rao         log<level::ERR>("Error occurred during forking process");
248f4682712SMarri Devender Rao         report<InternalFailure>();
249f4682712SMarri Devender Rao     }
250f4682712SMarri Devender Rao     else if (pid == 0)
251f4682712SMarri Devender Rao     {
252f4682712SMarri Devender Rao         try
253f4682712SMarri Devender Rao         {
254f4682712SMarri Devender Rao             generateCSRHelper(alternativeNames, challengePassword, city,
255f4682712SMarri Devender Rao                               commonName, contactPerson, country, email,
256f4682712SMarri Devender Rao                               givenName, initials, keyBitLength, keyCurveId,
257f4682712SMarri Devender Rao                               keyPairAlgorithm, keyUsage, organization,
258f4682712SMarri Devender Rao                               organizationalUnit, state, surname,
259f4682712SMarri Devender Rao                               unstructuredName);
260f4682712SMarri Devender Rao             exit(EXIT_SUCCESS);
261f4682712SMarri Devender Rao         }
262f4682712SMarri Devender Rao         catch (const InternalFailure& e)
263f4682712SMarri Devender Rao         {
264f4682712SMarri Devender Rao             // commit the error reported in child process and exit
265f4682712SMarri Devender Rao             // Callback method from SDEvent Loop looks for exit status
266f4682712SMarri Devender Rao             exit(EXIT_FAILURE);
267f4682712SMarri Devender Rao             commit<InternalFailure>();
268f4682712SMarri Devender Rao         }
269d2393f23SRamesh Iyyar         catch (const InvalidArgument& e)
270d2393f23SRamesh Iyyar         {
271d2393f23SRamesh Iyyar             // commit the error reported in child process and exit
272d2393f23SRamesh Iyyar             // Callback method from SDEvent Loop looks for exit status
273d2393f23SRamesh Iyyar             exit(EXIT_FAILURE);
274d2393f23SRamesh Iyyar             commit<InvalidArgument>();
275d2393f23SRamesh Iyyar         }
276f4682712SMarri Devender Rao     }
277f4682712SMarri Devender Rao     else
278f4682712SMarri Devender Rao     {
279f4682712SMarri Devender Rao         using namespace sdeventplus::source;
280f4682712SMarri Devender Rao         Child::Callback callback = [this](Child& eventSource,
281f4682712SMarri Devender Rao                                           const siginfo_t* si) {
282f4682712SMarri Devender Rao             eventSource.set_enabled(Enabled::On);
283f4682712SMarri Devender Rao             if (si->si_status != 0)
284f4682712SMarri Devender Rao             {
285f4682712SMarri Devender Rao                 this->createCSRObject(Status::FAILURE);
286f4682712SMarri Devender Rao             }
287f4682712SMarri Devender Rao             else
288f4682712SMarri Devender Rao             {
289f4682712SMarri Devender Rao                 this->createCSRObject(Status::SUCCESS);
290f4682712SMarri Devender Rao             }
291f4682712SMarri Devender Rao         };
292f4682712SMarri Devender Rao         try
293f4682712SMarri Devender Rao         {
294f4682712SMarri Devender Rao             sigset_t ss;
295f4682712SMarri Devender Rao             if (sigemptyset(&ss) < 0)
296f4682712SMarri Devender Rao             {
297f4682712SMarri Devender Rao                 log<level::ERR>("Unable to initialize signal set");
298f4682712SMarri Devender Rao                 elog<InternalFailure>();
299f4682712SMarri Devender Rao             }
300f4682712SMarri Devender Rao             if (sigaddset(&ss, SIGCHLD) < 0)
301f4682712SMarri Devender Rao             {
302f4682712SMarri Devender Rao                 log<level::ERR>("Unable to add signal to signal set");
303f4682712SMarri Devender Rao                 elog<InternalFailure>();
304f4682712SMarri Devender Rao             }
305f4682712SMarri Devender Rao 
306f4682712SMarri Devender Rao             // Block SIGCHLD first, so that the event loop can handle it
307cfb5802aSNan Zhou             if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
308f4682712SMarri Devender Rao             {
309f4682712SMarri Devender Rao                 log<level::ERR>("Unable to block signal");
310f4682712SMarri Devender Rao                 elog<InternalFailure>();
311f4682712SMarri Devender Rao             }
312f4682712SMarri Devender Rao             if (childPtr)
313f4682712SMarri Devender Rao             {
314f4682712SMarri Devender Rao                 childPtr.reset();
315f4682712SMarri Devender Rao             }
316f4682712SMarri Devender Rao             childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
317f4682712SMarri Devender Rao                                                std::move(callback));
318f4682712SMarri Devender Rao         }
319f4682712SMarri Devender Rao         catch (const InternalFailure& e)
320f4682712SMarri Devender Rao         {
321f4682712SMarri Devender Rao             commit<InternalFailure>();
322f4682712SMarri Devender Rao         }
323f4682712SMarri Devender Rao     }
324f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
325f4682712SMarri Devender Rao     return csrObjectPath;
326f4682712SMarri Devender Rao }
327f4682712SMarri Devender Rao 
328db029c95SKowalski, Kamil std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
329ffad1ef1SMarri Devender Rao {
330db029c95SKowalski, Kamil     return installedCerts;
331ffad1ef1SMarri Devender Rao }
332ffad1ef1SMarri Devender Rao 
333f4682712SMarri Devender Rao void Manager::generateCSRHelper(
334f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
335f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
336f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
337f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
338f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
339f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
340f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
341f4682712SMarri Devender Rao {
342f4682712SMarri Devender Rao     int ret = 0;
343f4682712SMarri Devender Rao 
344f4682712SMarri Devender Rao     // set version of x509 req
345f4682712SMarri Devender Rao     int nVersion = 1;
346f4682712SMarri Devender Rao     // TODO: Issue#6 need to make version number configurable
347f4682712SMarri Devender Rao     X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
348f4682712SMarri Devender Rao     ret = X509_REQ_set_version(x509Req.get(), nVersion);
349f4682712SMarri Devender Rao     if (ret == 0)
350f4682712SMarri Devender Rao     {
351bf3cf751SNan Zhou         log<level::ERR>("Error occurred during X509_REQ_set_version call");
352f4682712SMarri Devender Rao         elog<InternalFailure>();
353f4682712SMarri Devender Rao     }
354f4682712SMarri Devender Rao 
355f4682712SMarri Devender Rao     // set subject of x509 req
356f4682712SMarri Devender Rao     X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
357f4682712SMarri Devender Rao 
358f4682712SMarri Devender Rao     if (!alternativeNames.empty())
359f4682712SMarri Devender Rao     {
360f4682712SMarri Devender Rao         for (auto& name : alternativeNames)
361f4682712SMarri Devender Rao         {
362f4682712SMarri Devender Rao             addEntry(x509Name, "subjectAltName", name);
363f4682712SMarri Devender Rao         }
364f4682712SMarri Devender Rao     }
365f4682712SMarri Devender Rao     addEntry(x509Name, "challengePassword", challengePassword);
366f4682712SMarri Devender Rao     addEntry(x509Name, "L", city);
367f4682712SMarri Devender Rao     addEntry(x509Name, "CN", commonName);
368f4682712SMarri Devender Rao     addEntry(x509Name, "name", contactPerson);
369f4682712SMarri Devender Rao     addEntry(x509Name, "C", country);
370f4682712SMarri Devender Rao     addEntry(x509Name, "emailAddress", email);
371f4682712SMarri Devender Rao     addEntry(x509Name, "GN", givenName);
372f4682712SMarri Devender Rao     addEntry(x509Name, "initials", initials);
373f4682712SMarri Devender Rao     addEntry(x509Name, "algorithm", keyPairAlgorithm);
374f4682712SMarri Devender Rao     if (!keyUsage.empty())
375f4682712SMarri Devender Rao     {
376f4682712SMarri Devender Rao         for (auto& usage : keyUsage)
377f4682712SMarri Devender Rao         {
3787641105dSMarri Devender Rao             if (isExtendedKeyUsage(usage))
3797641105dSMarri Devender Rao             {
3807641105dSMarri Devender Rao                 addEntry(x509Name, "extendedKeyUsage", usage);
3817641105dSMarri Devender Rao             }
3827641105dSMarri Devender Rao             else
3837641105dSMarri Devender Rao             {
384f4682712SMarri Devender Rao                 addEntry(x509Name, "keyUsage", usage);
385f4682712SMarri Devender Rao             }
386f4682712SMarri Devender Rao         }
3877641105dSMarri Devender Rao     }
388f4682712SMarri Devender Rao     addEntry(x509Name, "O", organization);
389dc91fb61SJayanth Othayoth     addEntry(x509Name, "OU", organizationalUnit);
390f4682712SMarri Devender Rao     addEntry(x509Name, "ST", state);
391f4682712SMarri Devender Rao     addEntry(x509Name, "SN", surname);
392f4682712SMarri Devender Rao     addEntry(x509Name, "unstructuredName", unstructuredName);
393f4682712SMarri Devender Rao 
3948a09b52aSRamesh Iyyar     EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
3958a09b52aSRamesh Iyyar 
3968a09b52aSRamesh Iyyar     log<level::INFO>("Given Key pair algorithm",
3978a09b52aSRamesh Iyyar                      entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
3988a09b52aSRamesh Iyyar 
3998a09b52aSRamesh Iyyar     // Used EC algorithm as default if user did not give algorithm type.
4008a09b52aSRamesh Iyyar     if (keyPairAlgorithm == "RSA")
401c6e58c7eSRamesh Iyyar         pKey = getRSAKeyPair(keyBitLength);
4028a09b52aSRamesh Iyyar     else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
403c6e58c7eSRamesh Iyyar         pKey = generateECKeyPair(keyCurveId);
4048a09b52aSRamesh Iyyar     else
4058a09b52aSRamesh Iyyar     {
4068a09b52aSRamesh Iyyar         log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
4078a09b52aSRamesh Iyyar                         "RSA and EC only");
4088a09b52aSRamesh Iyyar         elog<InvalidArgument>(
4098a09b52aSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
4108a09b52aSRamesh Iyyar             Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
4118a09b52aSRamesh Iyyar     }
4128a09b52aSRamesh Iyyar 
4138a09b52aSRamesh Iyyar     ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
4148a09b52aSRamesh Iyyar     if (ret == 0)
4158a09b52aSRamesh Iyyar     {
416bf3cf751SNan Zhou         log<level::ERR>("Error occurred while setting Public key");
4178a09b52aSRamesh Iyyar         elog<InternalFailure>();
4188a09b52aSRamesh Iyyar     }
4198a09b52aSRamesh Iyyar 
4208a09b52aSRamesh Iyyar     // Write private key to file
421c6e58c7eSRamesh Iyyar     writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
422f4682712SMarri Devender Rao 
423f4682712SMarri Devender Rao     // set sign key of x509 req
424f4682712SMarri Devender Rao     ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
4258a09b52aSRamesh Iyyar     if (ret == 0)
426f4682712SMarri Devender Rao     {
427bf3cf751SNan Zhou         log<level::ERR>("Error occurred while signing key of x509");
428f4682712SMarri Devender Rao         elog<InternalFailure>();
429f4682712SMarri Devender Rao     }
4308a09b52aSRamesh Iyyar 
431f4682712SMarri Devender Rao     log<level::INFO>("Writing CSR to file");
432c6e58c7eSRamesh Iyyar     fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
433c6e58c7eSRamesh Iyyar     writeCSR(csrFilePath.string(), x509Req);
434f4682712SMarri Devender Rao }
435f4682712SMarri Devender Rao 
4367641105dSMarri Devender Rao bool Manager::isExtendedKeyUsage(const std::string& usage)
4377641105dSMarri Devender Rao {
4387641105dSMarri Devender Rao     const static std::array<const char*, 6> usageList = {
4397641105dSMarri Devender Rao         "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
4407641105dSMarri Devender Rao         "Timestamping",         "CodeSigning",          "EmailProtection"};
4417641105dSMarri Devender Rao     auto it = std::find_if(
4427641105dSMarri Devender Rao         usageList.begin(), usageList.end(),
4437641105dSMarri Devender Rao         [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
4447641105dSMarri Devender Rao     return it != usageList.end();
4457641105dSMarri Devender Rao }
4468a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
447f4682712SMarri Devender Rao {
4488a09b52aSRamesh Iyyar     int64_t keyBitLen = keyBitLength;
449f4682712SMarri Devender Rao     // set keybit length to default value if not set
4508a09b52aSRamesh Iyyar     if (keyBitLen <= 0)
451f4682712SMarri Devender Rao     {
4528a09b52aSRamesh Iyyar         constexpr auto DEFAULT_KEYBITLENGTH = 2048;
4538a09b52aSRamesh Iyyar         log<level::INFO>(
4548a09b52aSRamesh Iyyar             "KeyBitLength is not given.Hence, using default KeyBitLength",
4558a09b52aSRamesh Iyyar             entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
4568a09b52aSRamesh Iyyar         keyBitLen = DEFAULT_KEYBITLENGTH;
457f4682712SMarri Devender Rao     }
45826fb83efSPatrick Williams 
45926fb83efSPatrick Williams #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
46026fb83efSPatrick Williams 
46126fb83efSPatrick Williams     // generate rsa key
46226fb83efSPatrick Williams     BIGNUM_Ptr bne(BN_new(), ::BN_free);
46326fb83efSPatrick Williams     auto ret = BN_set_word(bne.get(), RSA_F4);
46426fb83efSPatrick Williams     if (ret == 0)
46526fb83efSPatrick Williams     {
466bf3cf751SNan Zhou         log<level::ERR>("Error occurred during BN_set_word call");
46726fb83efSPatrick Williams         elog<InternalFailure>();
46826fb83efSPatrick Williams     }
46926fb83efSPatrick Williams 
470f4682712SMarri Devender Rao     RSA* rsa = RSA_new();
471cfb5802aSNan Zhou     ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), nullptr);
472f4682712SMarri Devender Rao     if (ret != 1)
473f4682712SMarri Devender Rao     {
474f4682712SMarri Devender Rao         free(rsa);
475bf3cf751SNan Zhou         log<level::ERR>("Error occurred during RSA_generate_key_ex call",
4768a09b52aSRamesh Iyyar                         entry("KEYBITLENGTH=%PRIu64", keyBitLen));
477f4682712SMarri Devender Rao         elog<InternalFailure>();
478f4682712SMarri Devender Rao     }
479f4682712SMarri Devender Rao 
480f4682712SMarri Devender Rao     // set public key of x509 req
481f4682712SMarri Devender Rao     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
4828a09b52aSRamesh Iyyar     ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
483f4682712SMarri Devender Rao     if (ret == 0)
484f4682712SMarri Devender Rao     {
4858a09b52aSRamesh Iyyar         free(rsa);
486bf3cf751SNan Zhou         log<level::ERR>("Error occurred during assign rsa key into EVP");
487f4682712SMarri Devender Rao         elog<InternalFailure>();
488f4682712SMarri Devender Rao     }
489f4682712SMarri Devender Rao 
4908a09b52aSRamesh Iyyar     return pKey;
49126fb83efSPatrick Williams 
49226fb83efSPatrick Williams #else
49326fb83efSPatrick Williams     auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
49426fb83efSPatrick Williams         EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
49526fb83efSPatrick Williams     if (!ctx)
49626fb83efSPatrick Williams     {
497bf3cf751SNan Zhou         log<level::ERR>("Error occurred creating EVP_PKEY_CTX from algorithm");
49826fb83efSPatrick Williams         elog<InternalFailure>();
49926fb83efSPatrick Williams     }
50026fb83efSPatrick Williams 
50126fb83efSPatrick Williams     if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
50226fb83efSPatrick Williams         (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
50326fb83efSPatrick Williams 
50426fb83efSPatrick Williams     {
505bf3cf751SNan Zhou         log<level::ERR>("Error occurred initializing keygen context");
50626fb83efSPatrick Williams         elog<InternalFailure>();
50726fb83efSPatrick Williams     }
50826fb83efSPatrick Williams 
50926fb83efSPatrick Williams     EVP_PKEY* pKey = nullptr;
51026fb83efSPatrick Williams     if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
51126fb83efSPatrick Williams     {
512bf3cf751SNan Zhou         log<level::ERR>("Error occurred during generate EC key");
51326fb83efSPatrick Williams         elog<InternalFailure>();
51426fb83efSPatrick Williams     }
51526fb83efSPatrick Williams 
51626fb83efSPatrick Williams     return {pKey, &::EVP_PKEY_free};
51726fb83efSPatrick Williams #endif
5188a09b52aSRamesh Iyyar }
5198a09b52aSRamesh Iyyar 
5208a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
5218a09b52aSRamesh Iyyar {
5228a09b52aSRamesh Iyyar     std::string curId(curveId);
5238a09b52aSRamesh Iyyar 
5248a09b52aSRamesh Iyyar     if (curId.empty())
5258a09b52aSRamesh Iyyar     {
5268a09b52aSRamesh Iyyar         // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
5278a09b52aSRamesh Iyyar         constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
5288a09b52aSRamesh Iyyar         log<level::INFO>(
5298a09b52aSRamesh Iyyar             "KeyCurveId is not given. Hence using default curve id",
5308a09b52aSRamesh Iyyar             entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
5318a09b52aSRamesh Iyyar         curId = DEFAULT_KEYCURVEID;
5328a09b52aSRamesh Iyyar     }
5338a09b52aSRamesh Iyyar 
5348a09b52aSRamesh Iyyar     int ecGrp = OBJ_txt2nid(curId.c_str());
5358a09b52aSRamesh Iyyar     if (ecGrp == NID_undef)
5368a09b52aSRamesh Iyyar     {
5378a09b52aSRamesh Iyyar         log<level::ERR>(
538bf3cf751SNan Zhou             "Error occurred during convert the curve id string format into NID",
5398a09b52aSRamesh Iyyar             entry("KEYCURVEID=%s", curId.c_str()));
5408a09b52aSRamesh Iyyar         elog<InternalFailure>();
5418a09b52aSRamesh Iyyar     }
5428a09b52aSRamesh Iyyar 
54326fb83efSPatrick Williams #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
54426fb83efSPatrick Williams 
5458a09b52aSRamesh Iyyar     EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
5468a09b52aSRamesh Iyyar 
547cfb5802aSNan Zhou     if (ecKey == nullptr)
5488a09b52aSRamesh Iyyar     {
5498a09b52aSRamesh Iyyar         log<level::ERR>(
550bf3cf751SNan Zhou             "Error occurred during create the EC_Key object from NID",
5518a09b52aSRamesh Iyyar             entry("ECGROUP=%d", ecGrp));
5528a09b52aSRamesh Iyyar         elog<InternalFailure>();
5538a09b52aSRamesh Iyyar     }
5548a09b52aSRamesh Iyyar 
5558a09b52aSRamesh Iyyar     // If you want to save a key and later load it with
5568a09b52aSRamesh Iyyar     // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
5578a09b52aSRamesh Iyyar     // flag on the key.
5588a09b52aSRamesh Iyyar     EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
5598a09b52aSRamesh Iyyar 
5608a09b52aSRamesh Iyyar     int ret = EC_KEY_generate_key(ecKey);
5618a09b52aSRamesh Iyyar 
5628a09b52aSRamesh Iyyar     if (ret == 0)
5638a09b52aSRamesh Iyyar     {
5648a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
565bf3cf751SNan Zhou         log<level::ERR>("Error occurred during generate EC key");
5668a09b52aSRamesh Iyyar         elog<InternalFailure>();
5678a09b52aSRamesh Iyyar     }
5688a09b52aSRamesh Iyyar 
5698a09b52aSRamesh Iyyar     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
5708a09b52aSRamesh Iyyar     ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
5718a09b52aSRamesh Iyyar     if (ret == 0)
5728a09b52aSRamesh Iyyar     {
5738a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
574bf3cf751SNan Zhou         log<level::ERR>("Error occurred during assign EC Key into EVP");
5758a09b52aSRamesh Iyyar         elog<InternalFailure>();
5768a09b52aSRamesh Iyyar     }
5778a09b52aSRamesh Iyyar 
5788a09b52aSRamesh Iyyar     return pKey;
57926fb83efSPatrick Williams 
58026fb83efSPatrick Williams #else
58126fb83efSPatrick Williams     auto holder_of_key = [](EVP_PKEY* key) {
58226fb83efSPatrick Williams         return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
58326fb83efSPatrick Williams             key, &::EVP_PKEY_free};
58426fb83efSPatrick Williams     };
58526fb83efSPatrick Williams 
58626fb83efSPatrick Williams     // Create context to set up curve parameters.
58726fb83efSPatrick Williams     auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
58826fb83efSPatrick Williams         EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
58926fb83efSPatrick Williams     if (!ctx)
59026fb83efSPatrick Williams     {
591bf3cf751SNan Zhou         log<level::ERR>("Error occurred creating EVP_PKEY_CTX for params");
59226fb83efSPatrick Williams         elog<InternalFailure>();
59326fb83efSPatrick Williams     }
59426fb83efSPatrick Williams 
59526fb83efSPatrick Williams     // Set up curve parameters.
59626fb83efSPatrick Williams     EVP_PKEY* params = nullptr;
59726fb83efSPatrick Williams 
59826fb83efSPatrick Williams     if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
59926fb83efSPatrick Williams         (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
60026fb83efSPatrick Williams          0) ||
60126fb83efSPatrick Williams         (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
60226fb83efSPatrick Williams         (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
60326fb83efSPatrick Williams     {
604bf3cf751SNan Zhou         log<level::ERR>("Error occurred setting curve parameters");
60526fb83efSPatrick Williams         elog<InternalFailure>();
60626fb83efSPatrick Williams     }
60726fb83efSPatrick Williams 
60826fb83efSPatrick Williams     // Move parameters to RAII holder.
60926fb83efSPatrick Williams     auto pparms = holder_of_key(params);
61026fb83efSPatrick Williams 
61126fb83efSPatrick Williams     // Create new context for key.
61226fb83efSPatrick Williams     ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
61326fb83efSPatrick Williams 
61426fb83efSPatrick Williams     if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
61526fb83efSPatrick Williams     {
616bf3cf751SNan Zhou         log<level::ERR>("Error occurred initializing keygen context");
61726fb83efSPatrick Williams         elog<InternalFailure>();
61826fb83efSPatrick Williams     }
61926fb83efSPatrick Williams 
62026fb83efSPatrick Williams     EVP_PKEY* pKey = nullptr;
62126fb83efSPatrick Williams     if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
62226fb83efSPatrick Williams     {
623bf3cf751SNan Zhou         log<level::ERR>("Error occurred during generate EC key");
62426fb83efSPatrick Williams         elog<InternalFailure>();
62526fb83efSPatrick Williams     }
62626fb83efSPatrick Williams 
62726fb83efSPatrick Williams     return holder_of_key(pKey);
62826fb83efSPatrick Williams #endif
6298a09b52aSRamesh Iyyar }
6308a09b52aSRamesh Iyyar 
631c6e58c7eSRamesh Iyyar void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
632c6e58c7eSRamesh Iyyar                               const std::string& privKeyFileName)
6338a09b52aSRamesh Iyyar {
6348a09b52aSRamesh Iyyar     log<level::INFO>("Writing private key to file");
635f4682712SMarri Devender Rao     // write private key to file
636c6e58c7eSRamesh Iyyar     fs::path privKeyPath = certParentInstallPath / privKeyFileName;
637f4682712SMarri Devender Rao 
638f4682712SMarri Devender Rao     FILE* fp = std::fopen(privKeyPath.c_str(), "w");
639cfb5802aSNan Zhou     if (fp == nullptr)
640f4682712SMarri Devender Rao     {
641bf3cf751SNan Zhou         log<level::ERR>("Error occurred creating private key file");
642f4682712SMarri Devender Rao         elog<InternalFailure>();
643f4682712SMarri Devender Rao     }
644cfb5802aSNan Zhou     int ret =
645cfb5802aSNan Zhou         PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0, nullptr);
646f4682712SMarri Devender Rao     std::fclose(fp);
647f4682712SMarri Devender Rao     if (ret == 0)
648f4682712SMarri Devender Rao     {
649bf3cf751SNan Zhou         log<level::ERR>("Error occurred while writing private key to file");
650f4682712SMarri Devender Rao         elog<InternalFailure>();
651f4682712SMarri Devender Rao     }
652f4682712SMarri Devender Rao }
653f4682712SMarri Devender Rao 
654f4682712SMarri Devender Rao void Manager::addEntry(X509_NAME* x509Name, const char* field,
655f4682712SMarri Devender Rao                        const std::string& bytes)
656f4682712SMarri Devender Rao {
657f4682712SMarri Devender Rao     if (bytes.empty())
658f4682712SMarri Devender Rao     {
659f4682712SMarri Devender Rao         return;
660f4682712SMarri Devender Rao     }
661f4682712SMarri Devender Rao     int ret = X509_NAME_add_entry_by_txt(
662f4682712SMarri Devender Rao         x509Name, field, MBSTRING_ASC,
663f4682712SMarri Devender Rao         reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
664f4682712SMarri Devender Rao     if (ret != 1)
665f4682712SMarri Devender Rao     {
666f4682712SMarri Devender Rao         log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
667f4682712SMarri Devender Rao                         entry("VALUE=%s", bytes.c_str()));
668f4682712SMarri Devender Rao         elog<InternalFailure>();
669f4682712SMarri Devender Rao     }
670f4682712SMarri Devender Rao }
671f4682712SMarri Devender Rao 
672f4682712SMarri Devender Rao void Manager::createCSRObject(const Status& status)
673f4682712SMarri Devender Rao {
674f4682712SMarri Devender Rao     if (csrPtr)
675f4682712SMarri Devender Rao     {
676f4682712SMarri Devender Rao         csrPtr.reset(nullptr);
677f4682712SMarri Devender Rao     }
678f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
679f4682712SMarri Devender Rao     csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
680f4682712SMarri Devender Rao                                    certInstallPath.c_str(), status);
681f4682712SMarri Devender Rao }
682f4682712SMarri Devender Rao 
683f4682712SMarri Devender Rao void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
684f4682712SMarri Devender Rao {
685f4682712SMarri Devender Rao     if (fs::exists(filePath))
686f4682712SMarri Devender Rao     {
687f4682712SMarri Devender Rao         log<level::INFO>("Removing the existing file",
688f4682712SMarri Devender Rao                          entry("FILENAME=%s", filePath.c_str()));
689f4682712SMarri Devender Rao         if (!fs::remove(filePath.c_str()))
690f4682712SMarri Devender Rao         {
691f4682712SMarri Devender Rao             log<level::ERR>("Unable to remove the file",
692f4682712SMarri Devender Rao                             entry("FILENAME=%s", filePath.c_str()));
693f4682712SMarri Devender Rao             elog<InternalFailure>();
694f4682712SMarri Devender Rao         }
695f4682712SMarri Devender Rao     }
696f4682712SMarri Devender Rao 
697cfb5802aSNan Zhou     FILE* fp = nullptr;
698f4682712SMarri Devender Rao 
699cfb5802aSNan Zhou     if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
700f4682712SMarri Devender Rao     {
701f4682712SMarri Devender Rao         log<level::ERR>("Error opening the file to write the CSR",
702f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
703f4682712SMarri Devender Rao         elog<InternalFailure>();
704f4682712SMarri Devender Rao     }
705f4682712SMarri Devender Rao 
706f4682712SMarri Devender Rao     int rc = PEM_write_X509_REQ(fp, x509Req.get());
707f4682712SMarri Devender Rao     if (!rc)
708f4682712SMarri Devender Rao     {
709f4682712SMarri Devender Rao         log<level::ERR>("PEM write routine failed",
710f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
711f4682712SMarri Devender Rao         std::fclose(fp);
712f4682712SMarri Devender Rao         elog<InternalFailure>();
713f4682712SMarri Devender Rao     }
714f4682712SMarri Devender Rao     std::fclose(fp);
715f4682712SMarri Devender Rao }
716f4682712SMarri Devender Rao 
717db029c95SKowalski, Kamil void Manager::createCertificates()
718db029c95SKowalski, Kamil {
719db029c95SKowalski, Kamil     auto certObjectPath = objectPath + '/';
720db029c95SKowalski, Kamil 
721db029c95SKowalski, Kamil     if (certType == phosphor::certs::AUTHORITY)
722db029c95SKowalski, Kamil     {
723fe590c4eSZbigniew Lukwinski         // Check whether install path is a directory.
724db029c95SKowalski, Kamil         if (!fs::is_directory(certInstallPath))
725db029c95SKowalski, Kamil         {
726db029c95SKowalski, Kamil             log<level::ERR>("Certificate installation path exists and it is "
727db029c95SKowalski, Kamil                             "not a directory");
728db029c95SKowalski, Kamil             elog<InternalFailure>();
729db029c95SKowalski, Kamil             return;
730db029c95SKowalski, Kamil         }
731db029c95SKowalski, Kamil 
732db029c95SKowalski, Kamil         for (auto& path : fs::directory_iterator(certInstallPath))
733ffad1ef1SMarri Devender Rao         {
734ffad1ef1SMarri Devender Rao             try
735ffad1ef1SMarri Devender Rao             {
7362f3563ccSZbigniew Lukwinski                 // Assume here any regular file located in certificate directory
7372f3563ccSZbigniew Lukwinski                 // contains certificates body. Do not want to use soft links
7382f3563ccSZbigniew Lukwinski                 // would add value.
7392f3563ccSZbigniew Lukwinski                 if (fs::is_regular_file(path))
7402f3563ccSZbigniew Lukwinski                 {
741db029c95SKowalski, Kamil                     installedCerts.emplace_back(std::make_unique<Certificate>(
742db029c95SKowalski, Kamil                         bus, certObjectPath + std::to_string(certIdCounter++),
7432f3563ccSZbigniew Lukwinski                         certType, certInstallPath, path.path(), certWatchPtr,
7442f3563ccSZbigniew Lukwinski                         *this));
7452f3563ccSZbigniew Lukwinski                 }
746ffad1ef1SMarri Devender Rao             }
747ffad1ef1SMarri Devender Rao             catch (const InternalFailure& e)
748ffad1ef1SMarri Devender Rao             {
749ffad1ef1SMarri Devender Rao                 report<InternalFailure>();
750ffad1ef1SMarri Devender Rao             }
751ffad1ef1SMarri Devender Rao             catch (const InvalidCertificate& e)
752ffad1ef1SMarri Devender Rao             {
753ffad1ef1SMarri Devender Rao                 report<InvalidCertificate>(
754ffad1ef1SMarri Devender Rao                     Reason("Existing certificate file is corrupted"));
755ffad1ef1SMarri Devender Rao             }
756ffad1ef1SMarri Devender Rao         }
757db029c95SKowalski, Kamil     }
758db029c95SKowalski, Kamil     else if (fs::exists(certInstallPath))
759db029c95SKowalski, Kamil     {
760db029c95SKowalski, Kamil         try
761db029c95SKowalski, Kamil         {
762db029c95SKowalski, Kamil             installedCerts.emplace_back(std::make_unique<Certificate>(
7632f3563ccSZbigniew Lukwinski                 bus, certObjectPath + '1', certType, certInstallPath,
7642f3563ccSZbigniew Lukwinski                 certInstallPath, certWatchPtr, *this));
765db029c95SKowalski, Kamil         }
766db029c95SKowalski, Kamil         catch (const InternalFailure& e)
767db029c95SKowalski, Kamil         {
768db029c95SKowalski, Kamil             report<InternalFailure>();
769db029c95SKowalski, Kamil         }
770db029c95SKowalski, Kamil         catch (const InvalidCertificate& e)
771db029c95SKowalski, Kamil         {
772db029c95SKowalski, Kamil             report<InvalidCertificate>(
773db029c95SKowalski, Kamil                 Reason("Existing certificate file is corrupted"));
774db029c95SKowalski, Kamil         }
775db029c95SKowalski, Kamil     }
776db029c95SKowalski, Kamil }
777c6e58c7eSRamesh Iyyar 
778c6e58c7eSRamesh Iyyar void Manager::createRSAPrivateKeyFile()
779c6e58c7eSRamesh Iyyar {
780c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
781c6e58c7eSRamesh Iyyar         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
782c6e58c7eSRamesh Iyyar 
783c6e58c7eSRamesh Iyyar     try
784c6e58c7eSRamesh Iyyar     {
785c6e58c7eSRamesh Iyyar         if (!fs::exists(rsaPrivateKeyFileName))
786c6e58c7eSRamesh Iyyar         {
787c6e58c7eSRamesh Iyyar             writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
788c6e58c7eSRamesh Iyyar                             RSA_PRIV_KEY_FILE_NAME);
789c6e58c7eSRamesh Iyyar         }
790c6e58c7eSRamesh Iyyar     }
791c6e58c7eSRamesh Iyyar     catch (const InternalFailure& e)
792c6e58c7eSRamesh Iyyar     {
793c6e58c7eSRamesh Iyyar         report<InternalFailure>();
794c6e58c7eSRamesh Iyyar     }
795c6e58c7eSRamesh Iyyar }
796c6e58c7eSRamesh Iyyar 
797c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
798c6e58c7eSRamesh Iyyar {
799c6e58c7eSRamesh Iyyar     if (keyBitLength != SUPPORTED_KEYBITLENGTH)
800c6e58c7eSRamesh Iyyar     {
801c6e58c7eSRamesh Iyyar         log<level::ERR>(
802c6e58c7eSRamesh Iyyar             "Given Key bit length is not supported",
803c6e58c7eSRamesh Iyyar             entry("GIVENKEYBITLENGTH=%d", keyBitLength),
804c6e58c7eSRamesh Iyyar             entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
805c6e58c7eSRamesh Iyyar         elog<InvalidArgument>(
806c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYBITLENGTH"),
807c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
808c6e58c7eSRamesh Iyyar     }
809c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
810c6e58c7eSRamesh Iyyar         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
811c6e58c7eSRamesh Iyyar 
812c6e58c7eSRamesh Iyyar     FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
813c6e58c7eSRamesh Iyyar     if (!privateKeyFile)
814c6e58c7eSRamesh Iyyar     {
815c6e58c7eSRamesh Iyyar         log<level::ERR>("Unable to open RSA private key file to read",
816c6e58c7eSRamesh Iyyar                         entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
817c6e58c7eSRamesh Iyyar                         entry("ERRORREASON=%s", strerror(errno)));
818c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
819c6e58c7eSRamesh Iyyar     }
820c6e58c7eSRamesh Iyyar 
821c6e58c7eSRamesh Iyyar     EVP_PKEY_Ptr privateKey(
822c6e58c7eSRamesh Iyyar         PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
823c6e58c7eSRamesh Iyyar         ::EVP_PKEY_free);
824c6e58c7eSRamesh Iyyar     std::fclose(privateKeyFile);
825c6e58c7eSRamesh Iyyar 
826c6e58c7eSRamesh Iyyar     if (!privateKey)
827c6e58c7eSRamesh Iyyar     {
828bf3cf751SNan Zhou         log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
829c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
830c6e58c7eSRamesh Iyyar     }
831c6e58c7eSRamesh Iyyar     return privateKey;
832c6e58c7eSRamesh Iyyar }
8332f3563ccSZbigniew Lukwinski 
8342f3563ccSZbigniew Lukwinski void Manager::storageUpdate()
8352f3563ccSZbigniew Lukwinski {
8362f3563ccSZbigniew Lukwinski     if (certType == phosphor::certs::AUTHORITY)
8372f3563ccSZbigniew Lukwinski     {
8382f3563ccSZbigniew Lukwinski         // Remove symbolic links in the certificate directory
8392f3563ccSZbigniew Lukwinski         for (auto& certPath : fs::directory_iterator(certInstallPath))
8402f3563ccSZbigniew Lukwinski         {
8412f3563ccSZbigniew Lukwinski             try
8422f3563ccSZbigniew Lukwinski             {
8432f3563ccSZbigniew Lukwinski                 if (fs::is_symlink(certPath))
8442f3563ccSZbigniew Lukwinski                 {
8452f3563ccSZbigniew Lukwinski                     fs::remove(certPath);
8462f3563ccSZbigniew Lukwinski                 }
8472f3563ccSZbigniew Lukwinski             }
8482f3563ccSZbigniew Lukwinski             catch (const std::exception& e)
8492f3563ccSZbigniew Lukwinski             {
8502f3563ccSZbigniew Lukwinski                 log<level::ERR>(
8512f3563ccSZbigniew Lukwinski                     "Failed to remove symlink for certificate",
8522f3563ccSZbigniew Lukwinski                     entry("ERR=%s", e.what()),
8532f3563ccSZbigniew Lukwinski                     entry("SYMLINK=%s", certPath.path().string().c_str()));
8542f3563ccSZbigniew Lukwinski                 elog<InternalFailure>();
8552f3563ccSZbigniew Lukwinski             }
8562f3563ccSZbigniew Lukwinski         }
8572f3563ccSZbigniew Lukwinski     }
8582f3563ccSZbigniew Lukwinski 
8592f3563ccSZbigniew Lukwinski     for (const auto& cert : installedCerts)
8602f3563ccSZbigniew Lukwinski     {
8612f3563ccSZbigniew Lukwinski         cert->storageUpdate();
8622f3563ccSZbigniew Lukwinski     }
8632f3563ccSZbigniew Lukwinski }
8642f3563ccSZbigniew Lukwinski 
8652f3563ccSZbigniew Lukwinski void Manager::reloadOrReset(const UnitsToRestart& unit)
8662f3563ccSZbigniew Lukwinski {
8672f3563ccSZbigniew Lukwinski     if (!unit.empty())
8682f3563ccSZbigniew Lukwinski     {
8692f3563ccSZbigniew Lukwinski         try
8702f3563ccSZbigniew Lukwinski         {
8712f3563ccSZbigniew Lukwinski             constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
8722f3563ccSZbigniew Lukwinski             constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
8732f3563ccSZbigniew Lukwinski             constexpr auto SYSTEMD_INTERFACE =
8742f3563ccSZbigniew Lukwinski                 "org.freedesktop.systemd1.Manager";
8752f3563ccSZbigniew Lukwinski 
8762f3563ccSZbigniew Lukwinski             auto method =
8772f3563ccSZbigniew Lukwinski                 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
8782f3563ccSZbigniew Lukwinski                                     SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
8792f3563ccSZbigniew Lukwinski             method.append(unit, "replace");
8802f3563ccSZbigniew Lukwinski             bus.call_noreply(method);
8812f3563ccSZbigniew Lukwinski         }
882ca128117SPatrick Williams         catch (const sdbusplus::exception::exception& e)
8832f3563ccSZbigniew Lukwinski         {
8842f3563ccSZbigniew Lukwinski             log<level::ERR>("Failed to reload or restart service",
8852f3563ccSZbigniew Lukwinski                             entry("ERR=%s", e.what()),
8862f3563ccSZbigniew Lukwinski                             entry("UNIT=%s", unit.c_str()));
8872f3563ccSZbigniew Lukwinski             elog<InternalFailure>();
8882f3563ccSZbigniew Lukwinski         }
8892f3563ccSZbigniew Lukwinski     }
8902f3563ccSZbigniew Lukwinski }
8912f3563ccSZbigniew Lukwinski 
8922f3563ccSZbigniew Lukwinski bool Manager::isCertificateUnique(const std::string& filePath,
8932f3563ccSZbigniew Lukwinski                                   const Certificate* const certToDrop)
8942f3563ccSZbigniew Lukwinski {
8952f3563ccSZbigniew Lukwinski     if (std::any_of(
8962f3563ccSZbigniew Lukwinski             installedCerts.begin(), installedCerts.end(),
8972f3563ccSZbigniew Lukwinski             [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
8982f3563ccSZbigniew Lukwinski                 return cert.get() != certToDrop && cert->isSame(filePath);
8992f3563ccSZbigniew Lukwinski             }))
9002f3563ccSZbigniew Lukwinski     {
9012f3563ccSZbigniew Lukwinski         return false;
9022f3563ccSZbigniew Lukwinski     }
9032f3563ccSZbigniew Lukwinski     else
9042f3563ccSZbigniew Lukwinski     {
9052f3563ccSZbigniew Lukwinski         return true;
9062f3563ccSZbigniew Lukwinski     }
9072f3563ccSZbigniew Lukwinski }
9082f3563ccSZbigniew Lukwinski 
909*e1289adfSNan Zhou } // namespace phosphor::certs
910