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