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