xref: /openbmc/phosphor-certificate-manager/certs_manager.cpp (revision 3b07b77a58820b27c32981e36b0ce500dffaa94c)
1cfbc8dc8SJayanth Othayoth #include "certs_manager.hpp"
2cfbc8dc8SJayanth Othayoth 
3f4682712SMarri Devender Rao #include <openssl/pem.h>
4f4682712SMarri Devender Rao #include <unistd.h>
5f4682712SMarri Devender Rao 
66ceec40bSMarri Devender Rao #include <phosphor-logging/elog-errors.hpp>
713bf74e4SMarri Devender Rao #include <xyz/openbmc_project/Certs/error.hpp>
8cfbc8dc8SJayanth Othayoth #include <xyz/openbmc_project/Common/error.hpp>
9cfbc8dc8SJayanth Othayoth namespace phosphor
10cfbc8dc8SJayanth Othayoth {
11cfbc8dc8SJayanth Othayoth namespace certs
12cfbc8dc8SJayanth Othayoth {
131396511dSMarri Devender Rao using InternalFailure =
141396511dSMarri Devender Rao     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
15ffad1ef1SMarri Devender Rao using InvalidCertificate =
16ffad1ef1SMarri Devender Rao     sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
17ffad1ef1SMarri Devender Rao using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
18cfbc8dc8SJayanth Othayoth 
19f4682712SMarri Devender Rao using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
20f4682712SMarri Devender Rao using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
21c6e58c7eSRamesh Iyyar using InvalidArgument =
22c6e58c7eSRamesh Iyyar     sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
23c6e58c7eSRamesh Iyyar using Argument = xyz::openbmc_project::Common::InvalidArgument;
24c6e58c7eSRamesh Iyyar 
25c6e58c7eSRamesh Iyyar constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
26f4682712SMarri Devender Rao 
27f4682712SMarri Devender Rao Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
28f4682712SMarri Devender Rao                  const char* path, const CertificateType& type,
29f4682712SMarri Devender Rao                  UnitsToRestart&& unit, CertInstallPath&& installPath) :
306ceec40bSMarri Devender Rao     Ifaces(bus, path),
31f4682712SMarri Devender Rao     bus(bus), event(event), objectPath(path), certType(type),
32c6e58c7eSRamesh Iyyar     unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
33c6e58c7eSRamesh Iyyar     certParentInstallPath(fs::path(certInstallPath).parent_path())
34cfbc8dc8SJayanth Othayoth {
35b57d75e2SMarri Devender Rao     // create parent certificate path if not existing
36b57d75e2SMarri Devender Rao     try
37b57d75e2SMarri Devender Rao     {
38b57d75e2SMarri Devender Rao         if (!fs::exists(certParentInstallPath))
39b57d75e2SMarri Devender Rao         {
40b57d75e2SMarri Devender Rao             fs::create_directories(certParentInstallPath);
41b57d75e2SMarri Devender Rao         }
42b57d75e2SMarri Devender Rao     }
43b57d75e2SMarri Devender Rao     catch (fs::filesystem_error& e)
44b57d75e2SMarri Devender Rao     {
45b57d75e2SMarri Devender Rao         log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()),
46b57d75e2SMarri Devender Rao                         entry("DIRECTORY=%s", certParentInstallPath.c_str()));
47b57d75e2SMarri Devender Rao         report<InternalFailure>();
48b57d75e2SMarri Devender Rao     }
49b57d75e2SMarri Devender Rao 
50c6e58c7eSRamesh Iyyar     // Generating RSA private key file if certificate type is server/client
51c6e58c7eSRamesh Iyyar     if (certType != AUTHORITY)
52c6e58c7eSRamesh Iyyar     {
53c6e58c7eSRamesh Iyyar         createRSAPrivateKeyFile();
54c6e58c7eSRamesh Iyyar     }
55c6e58c7eSRamesh Iyyar 
56ffad1ef1SMarri Devender Rao     // restore any existing certificates
57db029c95SKowalski, Kamil     createCertificates();
58ffad1ef1SMarri Devender Rao 
59ffad1ef1SMarri Devender Rao     // watch is not required for authority certificates
60ffad1ef1SMarri Devender Rao     if (certType != AUTHORITY)
61ffad1ef1SMarri Devender Rao     {
62ffad1ef1SMarri Devender Rao         // watch for certificate file create/replace
63ffad1ef1SMarri Devender Rao         certWatchPtr = std::make_unique<
64ffad1ef1SMarri Devender Rao             Watch>(event, certInstallPath, [this]() {
65bf7c588cSMarri Devender Rao             try
66bf7c588cSMarri Devender Rao             {
67ffad1ef1SMarri Devender Rao                 // if certificate file existing update it
68db029c95SKowalski, Kamil                 if (!installedCerts.empty())
69ffad1ef1SMarri Devender Rao                 {
70ffad1ef1SMarri Devender Rao                     log<level::INFO>(
71ffad1ef1SMarri Devender Rao                         "Inotify callback to update certificate properties");
72db029c95SKowalski, Kamil                     installedCerts[0]->populateProperties();
73ffad1ef1SMarri Devender Rao                 }
74ffad1ef1SMarri Devender Rao                 else
75ffad1ef1SMarri Devender Rao                 {
76ffad1ef1SMarri Devender Rao                     log<level::INFO>(
77ffad1ef1SMarri Devender Rao                         "Inotify callback to create certificate object");
78db029c95SKowalski, Kamil                     createCertificates();
79ffad1ef1SMarri Devender Rao                 }
80bf7c588cSMarri Devender Rao             }
81bf7c588cSMarri Devender Rao             catch (const InternalFailure& e)
82bf7c588cSMarri Devender Rao             {
83ffad1ef1SMarri Devender Rao                 commit<InternalFailure>();
84bf7c588cSMarri Devender Rao             }
85bf7c588cSMarri Devender Rao             catch (const InvalidCertificate& e)
86bf7c588cSMarri Devender Rao             {
87ffad1ef1SMarri Devender Rao                 commit<InvalidCertificate>();
88bf7c588cSMarri Devender Rao             }
89ffad1ef1SMarri Devender Rao         });
90bf7c588cSMarri Devender Rao     }
91db029c95SKowalski, Kamil     else
92db029c95SKowalski, Kamil     {
93db029c95SKowalski, Kamil         const std::string signleCertPath = "/etc/ssl/certs/Root-CA.pem";
94db029c95SKowalski, Kamil         if (fs::exists(signleCertPath) && !fs::is_empty(signleCertPath))
95db029c95SKowalski, Kamil         {
96db029c95SKowalski, Kamil             log<level::NOTICE>(
97db029c95SKowalski, Kamil                 "Legacy certificate detected, will be installed from: ",
98db029c95SKowalski, Kamil                 entry("SINGLE_CERTPATH=%s", signleCertPath.c_str()));
99db029c95SKowalski, Kamil             install(signleCertPath);
100db029c95SKowalski, Kamil             if (!fs::remove(signleCertPath))
101db029c95SKowalski, Kamil             {
102db029c95SKowalski, Kamil                 log<level::ERR>(
103db029c95SKowalski, Kamil                     "Unable to remove old certificate from: ",
104db029c95SKowalski, Kamil                     entry("SINGLE_CERTPATH=%s", signleCertPath.c_str()));
105db029c95SKowalski, Kamil                 elog<InternalFailure>();
106db029c95SKowalski, Kamil             }
107db029c95SKowalski, Kamil         }
108db029c95SKowalski, Kamil     }
109dd74bd20SJayanth Othayoth }
110589159f2SJayanth Othayoth 
11106a69d7bSZbigniew Kurzynski std::string Manager::install(const std::string filePath)
112cfbc8dc8SJayanth Othayoth {
1131396511dSMarri Devender Rao     using NotAllowed =
1141396511dSMarri Devender Rao         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
1151396511dSMarri Devender Rao     using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
116db029c95SKowalski, Kamil 
117db029c95SKowalski, Kamil     if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
1181396511dSMarri Devender Rao     {
1191396511dSMarri Devender Rao         elog<NotAllowed>(Reason("Certificate already exist"));
1201396511dSMarri Devender Rao     }
121*3b07b77aSZbigniew Lukwinski     else if (certType == phosphor::certs::AUTHORITY &&
122*3b07b77aSZbigniew Lukwinski              installedCerts.size() >= AUTHORITY_CERTIFICATES_LIMIT)
123*3b07b77aSZbigniew Lukwinski     {
124*3b07b77aSZbigniew Lukwinski         elog<NotAllowed>(Reason("Certificates limit reached"));
125*3b07b77aSZbigniew Lukwinski     }
126ffad1ef1SMarri Devender Rao 
127db029c95SKowalski, Kamil     auto certObjectPath = objectPath + '/' + std::to_string(certIdCounter++);
128db029c95SKowalski, Kamil 
129db029c95SKowalski, Kamil     installedCerts.emplace_back(std::make_unique<Certificate>(
1308f80c35bSMarri Devender Rao         bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath,
131db029c95SKowalski, Kamil         false, certWatchPtr));
13206a69d7bSZbigniew Kurzynski     return certObjectPath;
133589159f2SJayanth Othayoth }
134ae70b3daSDeepak Kodihalli 
135ae70b3daSDeepak Kodihalli void Manager::delete_()
136ae70b3daSDeepak Kodihalli {
1376ceec40bSMarri Devender Rao     // TODO: #Issue 4 when a certificate is deleted system auto generates
1386ceec40bSMarri Devender Rao     // certificate file. At present we are not supporting creation of
1396ceec40bSMarri Devender Rao     // certificate object for the auto-generated certificate file as
1406ceec40bSMarri Devender Rao     // deletion if only applicable for REST server and Bmcweb does not allow
1416ceec40bSMarri Devender Rao     // deletion of certificates
142db029c95SKowalski, Kamil     installedCerts.clear();
143ae70b3daSDeepak Kodihalli }
144f4682712SMarri Devender Rao 
145f4682712SMarri Devender Rao std::string Manager::generateCSR(
146f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
147f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
148f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
149f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
150f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
151f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
152f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
153f4682712SMarri Devender Rao {
154f4682712SMarri Devender Rao     // We support only one CSR.
155f4682712SMarri Devender Rao     csrPtr.reset(nullptr);
156f4682712SMarri Devender Rao     auto pid = fork();
157f4682712SMarri Devender Rao     if (pid == -1)
158f4682712SMarri Devender Rao     {
159f4682712SMarri Devender Rao         log<level::ERR>("Error occurred during forking process");
160f4682712SMarri Devender Rao         report<InternalFailure>();
161f4682712SMarri Devender Rao     }
162f4682712SMarri Devender Rao     else if (pid == 0)
163f4682712SMarri Devender Rao     {
164f4682712SMarri Devender Rao         try
165f4682712SMarri Devender Rao         {
166f4682712SMarri Devender Rao             generateCSRHelper(alternativeNames, challengePassword, city,
167f4682712SMarri Devender Rao                               commonName, contactPerson, country, email,
168f4682712SMarri Devender Rao                               givenName, initials, keyBitLength, keyCurveId,
169f4682712SMarri Devender Rao                               keyPairAlgorithm, keyUsage, organization,
170f4682712SMarri Devender Rao                               organizationalUnit, state, surname,
171f4682712SMarri Devender Rao                               unstructuredName);
172f4682712SMarri Devender Rao             exit(EXIT_SUCCESS);
173f4682712SMarri Devender Rao         }
174f4682712SMarri Devender Rao         catch (const InternalFailure& e)
175f4682712SMarri Devender Rao         {
176f4682712SMarri Devender Rao             // commit the error reported in child process and exit
177f4682712SMarri Devender Rao             // Callback method from SDEvent Loop looks for exit status
178f4682712SMarri Devender Rao             exit(EXIT_FAILURE);
179f4682712SMarri Devender Rao             commit<InternalFailure>();
180f4682712SMarri Devender Rao         }
181f4682712SMarri Devender Rao     }
182f4682712SMarri Devender Rao     else
183f4682712SMarri Devender Rao     {
184f4682712SMarri Devender Rao         using namespace sdeventplus::source;
185f4682712SMarri Devender Rao         Child::Callback callback = [this](Child& eventSource,
186f4682712SMarri Devender Rao                                           const siginfo_t* si) {
187f4682712SMarri Devender Rao             eventSource.set_enabled(Enabled::On);
188f4682712SMarri Devender Rao             if (si->si_status != 0)
189f4682712SMarri Devender Rao             {
190f4682712SMarri Devender Rao                 this->createCSRObject(Status::FAILURE);
191f4682712SMarri Devender Rao             }
192f4682712SMarri Devender Rao             else
193f4682712SMarri Devender Rao             {
194f4682712SMarri Devender Rao                 this->createCSRObject(Status::SUCCESS);
195f4682712SMarri Devender Rao             }
196f4682712SMarri Devender Rao         };
197f4682712SMarri Devender Rao         try
198f4682712SMarri Devender Rao         {
199f4682712SMarri Devender Rao             sigset_t ss;
200f4682712SMarri Devender Rao             if (sigemptyset(&ss) < 0)
201f4682712SMarri Devender Rao             {
202f4682712SMarri Devender Rao                 log<level::ERR>("Unable to initialize signal set");
203f4682712SMarri Devender Rao                 elog<InternalFailure>();
204f4682712SMarri Devender Rao             }
205f4682712SMarri Devender Rao             if (sigaddset(&ss, SIGCHLD) < 0)
206f4682712SMarri Devender Rao             {
207f4682712SMarri Devender Rao                 log<level::ERR>("Unable to add signal to signal set");
208f4682712SMarri Devender Rao                 elog<InternalFailure>();
209f4682712SMarri Devender Rao             }
210f4682712SMarri Devender Rao 
211f4682712SMarri Devender Rao             // Block SIGCHLD first, so that the event loop can handle it
212f4682712SMarri Devender Rao             if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
213f4682712SMarri Devender Rao             {
214f4682712SMarri Devender Rao                 log<level::ERR>("Unable to block signal");
215f4682712SMarri Devender Rao                 elog<InternalFailure>();
216f4682712SMarri Devender Rao             }
217f4682712SMarri Devender Rao             if (childPtr)
218f4682712SMarri Devender Rao             {
219f4682712SMarri Devender Rao                 childPtr.reset();
220f4682712SMarri Devender Rao             }
221f4682712SMarri Devender Rao             childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
222f4682712SMarri Devender Rao                                                std::move(callback));
223f4682712SMarri Devender Rao         }
224f4682712SMarri Devender Rao         catch (const InternalFailure& e)
225f4682712SMarri Devender Rao         {
226f4682712SMarri Devender Rao             commit<InternalFailure>();
227f4682712SMarri Devender Rao         }
228f4682712SMarri Devender Rao     }
229f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
230f4682712SMarri Devender Rao     return csrObjectPath;
231f4682712SMarri Devender Rao }
232f4682712SMarri Devender Rao 
233db029c95SKowalski, Kamil std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
234ffad1ef1SMarri Devender Rao {
235db029c95SKowalski, Kamil     return installedCerts;
236ffad1ef1SMarri Devender Rao }
237ffad1ef1SMarri Devender Rao 
238f4682712SMarri Devender Rao void Manager::generateCSRHelper(
239f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
240f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
241f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
242f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
243f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
244f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
245f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
246f4682712SMarri Devender Rao {
247f4682712SMarri Devender Rao     int ret = 0;
248f4682712SMarri Devender Rao 
249f4682712SMarri Devender Rao     // set version of x509 req
250f4682712SMarri Devender Rao     int nVersion = 1;
251f4682712SMarri Devender Rao     // TODO: Issue#6 need to make version number configurable
252f4682712SMarri Devender Rao     X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
253f4682712SMarri Devender Rao     ret = X509_REQ_set_version(x509Req.get(), nVersion);
254f4682712SMarri Devender Rao     if (ret == 0)
255f4682712SMarri Devender Rao     {
256f4682712SMarri Devender Rao         log<level::ERR>("Error occured during X509_REQ_set_version call");
257f4682712SMarri Devender Rao         elog<InternalFailure>();
258f4682712SMarri Devender Rao     }
259f4682712SMarri Devender Rao 
260f4682712SMarri Devender Rao     // set subject of x509 req
261f4682712SMarri Devender Rao     X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
262f4682712SMarri Devender Rao 
263f4682712SMarri Devender Rao     if (!alternativeNames.empty())
264f4682712SMarri Devender Rao     {
265f4682712SMarri Devender Rao         for (auto& name : alternativeNames)
266f4682712SMarri Devender Rao         {
267f4682712SMarri Devender Rao             addEntry(x509Name, "subjectAltName", name);
268f4682712SMarri Devender Rao         }
269f4682712SMarri Devender Rao     }
270f4682712SMarri Devender Rao     addEntry(x509Name, "challengePassword", challengePassword);
271f4682712SMarri Devender Rao     addEntry(x509Name, "L", city);
272f4682712SMarri Devender Rao     addEntry(x509Name, "CN", commonName);
273f4682712SMarri Devender Rao     addEntry(x509Name, "name", contactPerson);
274f4682712SMarri Devender Rao     addEntry(x509Name, "C", country);
275f4682712SMarri Devender Rao     addEntry(x509Name, "emailAddress", email);
276f4682712SMarri Devender Rao     addEntry(x509Name, "GN", givenName);
277f4682712SMarri Devender Rao     addEntry(x509Name, "initials", initials);
278f4682712SMarri Devender Rao     addEntry(x509Name, "algorithm", keyPairAlgorithm);
279f4682712SMarri Devender Rao     if (!keyUsage.empty())
280f4682712SMarri Devender Rao     {
281f4682712SMarri Devender Rao         for (auto& usage : keyUsage)
282f4682712SMarri Devender Rao         {
2837641105dSMarri Devender Rao             if (isExtendedKeyUsage(usage))
2847641105dSMarri Devender Rao             {
2857641105dSMarri Devender Rao                 addEntry(x509Name, "extendedKeyUsage", usage);
2867641105dSMarri Devender Rao             }
2877641105dSMarri Devender Rao             else
2887641105dSMarri Devender Rao             {
289f4682712SMarri Devender Rao                 addEntry(x509Name, "keyUsage", usage);
290f4682712SMarri Devender Rao             }
291f4682712SMarri Devender Rao         }
2927641105dSMarri Devender Rao     }
293f4682712SMarri Devender Rao     addEntry(x509Name, "O", organization);
294f4682712SMarri Devender Rao     addEntry(x509Name, "ST", state);
295f4682712SMarri Devender Rao     addEntry(x509Name, "SN", surname);
296f4682712SMarri Devender Rao     addEntry(x509Name, "unstructuredName", unstructuredName);
297f4682712SMarri Devender Rao 
2988a09b52aSRamesh Iyyar     EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
2998a09b52aSRamesh Iyyar 
3008a09b52aSRamesh Iyyar     log<level::INFO>("Given Key pair algorithm",
3018a09b52aSRamesh Iyyar                      entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
3028a09b52aSRamesh Iyyar 
3038a09b52aSRamesh Iyyar     // Used EC algorithm as default if user did not give algorithm type.
3048a09b52aSRamesh Iyyar     if (keyPairAlgorithm == "RSA")
305c6e58c7eSRamesh Iyyar         pKey = getRSAKeyPair(keyBitLength);
3068a09b52aSRamesh Iyyar     else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
307c6e58c7eSRamesh Iyyar         pKey = generateECKeyPair(keyCurveId);
3088a09b52aSRamesh Iyyar     else
3098a09b52aSRamesh Iyyar     {
3108a09b52aSRamesh Iyyar         log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
3118a09b52aSRamesh Iyyar                         "RSA and EC only");
3128a09b52aSRamesh Iyyar         elog<InvalidArgument>(
3138a09b52aSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
3148a09b52aSRamesh Iyyar             Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
3158a09b52aSRamesh Iyyar     }
3168a09b52aSRamesh Iyyar 
3178a09b52aSRamesh Iyyar     ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
3188a09b52aSRamesh Iyyar     if (ret == 0)
3198a09b52aSRamesh Iyyar     {
3208a09b52aSRamesh Iyyar         log<level::ERR>("Error occured while setting Public key");
3218a09b52aSRamesh Iyyar         elog<InternalFailure>();
3228a09b52aSRamesh Iyyar     }
3238a09b52aSRamesh Iyyar 
3248a09b52aSRamesh Iyyar     // Write private key to file
325c6e58c7eSRamesh Iyyar     writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
326f4682712SMarri Devender Rao 
327f4682712SMarri Devender Rao     // set sign key of x509 req
328f4682712SMarri Devender Rao     ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
3298a09b52aSRamesh Iyyar     if (ret == 0)
330f4682712SMarri Devender Rao     {
331f4682712SMarri Devender Rao         log<level::ERR>("Error occured while signing key of x509");
332f4682712SMarri Devender Rao         elog<InternalFailure>();
333f4682712SMarri Devender Rao     }
3348a09b52aSRamesh Iyyar 
335f4682712SMarri Devender Rao     log<level::INFO>("Writing CSR to file");
336c6e58c7eSRamesh Iyyar     fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
337c6e58c7eSRamesh Iyyar     writeCSR(csrFilePath.string(), x509Req);
338f4682712SMarri Devender Rao }
339f4682712SMarri Devender Rao 
3407641105dSMarri Devender Rao bool Manager::isExtendedKeyUsage(const std::string& usage)
3417641105dSMarri Devender Rao {
3427641105dSMarri Devender Rao     const static std::array<const char*, 6> usageList = {
3437641105dSMarri Devender Rao         "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
3447641105dSMarri Devender Rao         "Timestamping",         "CodeSigning",          "EmailProtection"};
3457641105dSMarri Devender Rao     auto it = std::find_if(
3467641105dSMarri Devender Rao         usageList.begin(), usageList.end(),
3477641105dSMarri Devender Rao         [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
3487641105dSMarri Devender Rao     return it != usageList.end();
3497641105dSMarri Devender Rao }
3508a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
351f4682712SMarri Devender Rao {
352f4682712SMarri Devender Rao     int ret = 0;
353f4682712SMarri Devender Rao     // generate rsa key
354f4682712SMarri Devender Rao     BIGNUM_Ptr bne(BN_new(), ::BN_free);
355f4682712SMarri Devender Rao     ret = BN_set_word(bne.get(), RSA_F4);
356f4682712SMarri Devender Rao     if (ret == 0)
357f4682712SMarri Devender Rao     {
358f4682712SMarri Devender Rao         log<level::ERR>("Error occured during BN_set_word call");
359f4682712SMarri Devender Rao         elog<InternalFailure>();
360f4682712SMarri Devender Rao     }
361f4682712SMarri Devender Rao 
3628a09b52aSRamesh Iyyar     int64_t keyBitLen = keyBitLength;
363f4682712SMarri Devender Rao     // set keybit length to default value if not set
3648a09b52aSRamesh Iyyar     if (keyBitLen <= 0)
365f4682712SMarri Devender Rao     {
3668a09b52aSRamesh Iyyar         constexpr auto DEFAULT_KEYBITLENGTH = 2048;
3678a09b52aSRamesh Iyyar         log<level::INFO>(
3688a09b52aSRamesh Iyyar             "KeyBitLength is not given.Hence, using default KeyBitLength",
3698a09b52aSRamesh Iyyar             entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
3708a09b52aSRamesh Iyyar         keyBitLen = DEFAULT_KEYBITLENGTH;
371f4682712SMarri Devender Rao     }
372f4682712SMarri Devender Rao     RSA* rsa = RSA_new();
3738a09b52aSRamesh Iyyar     ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
374f4682712SMarri Devender Rao     if (ret != 1)
375f4682712SMarri Devender Rao     {
376f4682712SMarri Devender Rao         free(rsa);
377f4682712SMarri Devender Rao         log<level::ERR>("Error occured during RSA_generate_key_ex call",
3788a09b52aSRamesh Iyyar                         entry("KEYBITLENGTH=%PRIu64", keyBitLen));
379f4682712SMarri Devender Rao         elog<InternalFailure>();
380f4682712SMarri Devender Rao     }
381f4682712SMarri Devender Rao 
382f4682712SMarri Devender Rao     // set public key of x509 req
383f4682712SMarri Devender Rao     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
3848a09b52aSRamesh Iyyar     ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
385f4682712SMarri Devender Rao     if (ret == 0)
386f4682712SMarri Devender Rao     {
3878a09b52aSRamesh Iyyar         free(rsa);
3888a09b52aSRamesh Iyyar         log<level::ERR>("Error occured during assign rsa key into EVP");
389f4682712SMarri Devender Rao         elog<InternalFailure>();
390f4682712SMarri Devender Rao     }
391f4682712SMarri Devender Rao 
3928a09b52aSRamesh Iyyar     return pKey;
3938a09b52aSRamesh Iyyar }
3948a09b52aSRamesh Iyyar 
3958a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
3968a09b52aSRamesh Iyyar {
3978a09b52aSRamesh Iyyar     std::string curId(curveId);
3988a09b52aSRamesh Iyyar 
3998a09b52aSRamesh Iyyar     if (curId.empty())
4008a09b52aSRamesh Iyyar     {
4018a09b52aSRamesh Iyyar         // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
4028a09b52aSRamesh Iyyar         constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
4038a09b52aSRamesh Iyyar         log<level::INFO>(
4048a09b52aSRamesh Iyyar             "KeyCurveId is not given. Hence using default curve id",
4058a09b52aSRamesh Iyyar             entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
4068a09b52aSRamesh Iyyar         curId = DEFAULT_KEYCURVEID;
4078a09b52aSRamesh Iyyar     }
4088a09b52aSRamesh Iyyar 
4098a09b52aSRamesh Iyyar     int ecGrp = OBJ_txt2nid(curId.c_str());
4108a09b52aSRamesh Iyyar 
4118a09b52aSRamesh Iyyar     if (ecGrp == NID_undef)
4128a09b52aSRamesh Iyyar     {
4138a09b52aSRamesh Iyyar         log<level::ERR>(
4148a09b52aSRamesh Iyyar             "Error occured during convert the curve id string format into NID",
4158a09b52aSRamesh Iyyar             entry("KEYCURVEID=%s", curId.c_str()));
4168a09b52aSRamesh Iyyar         elog<InternalFailure>();
4178a09b52aSRamesh Iyyar     }
4188a09b52aSRamesh Iyyar 
4198a09b52aSRamesh Iyyar     EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
4208a09b52aSRamesh Iyyar 
4218a09b52aSRamesh Iyyar     if (ecKey == NULL)
4228a09b52aSRamesh Iyyar     {
4238a09b52aSRamesh Iyyar         log<level::ERR>(
4248a09b52aSRamesh Iyyar             "Error occured during create the EC_Key object from NID",
4258a09b52aSRamesh Iyyar             entry("ECGROUP=%d", ecGrp));
4268a09b52aSRamesh Iyyar         elog<InternalFailure>();
4278a09b52aSRamesh Iyyar     }
4288a09b52aSRamesh Iyyar 
4298a09b52aSRamesh Iyyar     // If you want to save a key and later load it with
4308a09b52aSRamesh Iyyar     // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
4318a09b52aSRamesh Iyyar     // flag on the key.
4328a09b52aSRamesh Iyyar     EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
4338a09b52aSRamesh Iyyar 
4348a09b52aSRamesh Iyyar     int ret = EC_KEY_generate_key(ecKey);
4358a09b52aSRamesh Iyyar 
4368a09b52aSRamesh Iyyar     if (ret == 0)
4378a09b52aSRamesh Iyyar     {
4388a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
4398a09b52aSRamesh Iyyar         log<level::ERR>("Error occured during generate EC key");
4408a09b52aSRamesh Iyyar         elog<InternalFailure>();
4418a09b52aSRamesh Iyyar     }
4428a09b52aSRamesh Iyyar 
4438a09b52aSRamesh Iyyar     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
4448a09b52aSRamesh Iyyar     ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
4458a09b52aSRamesh Iyyar     if (ret == 0)
4468a09b52aSRamesh Iyyar     {
4478a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
4488a09b52aSRamesh Iyyar         log<level::ERR>("Error occured during assign EC Key into EVP");
4498a09b52aSRamesh Iyyar         elog<InternalFailure>();
4508a09b52aSRamesh Iyyar     }
4518a09b52aSRamesh Iyyar 
4528a09b52aSRamesh Iyyar     return pKey;
4538a09b52aSRamesh Iyyar }
4548a09b52aSRamesh Iyyar 
455c6e58c7eSRamesh Iyyar void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
456c6e58c7eSRamesh Iyyar                               const std::string& privKeyFileName)
4578a09b52aSRamesh Iyyar {
4588a09b52aSRamesh Iyyar     log<level::INFO>("Writing private key to file");
459f4682712SMarri Devender Rao     // write private key to file
460c6e58c7eSRamesh Iyyar     fs::path privKeyPath = certParentInstallPath / privKeyFileName;
461f4682712SMarri Devender Rao 
462f4682712SMarri Devender Rao     FILE* fp = std::fopen(privKeyPath.c_str(), "w");
463f4682712SMarri Devender Rao     if (fp == NULL)
464f4682712SMarri Devender Rao     {
465f4682712SMarri Devender Rao         log<level::ERR>("Error occured creating private key file");
466f4682712SMarri Devender Rao         elog<InternalFailure>();
467f4682712SMarri Devender Rao     }
4688a09b52aSRamesh Iyyar     int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
469f4682712SMarri Devender Rao     std::fclose(fp);
470f4682712SMarri Devender Rao     if (ret == 0)
471f4682712SMarri Devender Rao     {
472f4682712SMarri Devender Rao         log<level::ERR>("Error occured while writing private key to file");
473f4682712SMarri Devender Rao         elog<InternalFailure>();
474f4682712SMarri Devender Rao     }
475f4682712SMarri Devender Rao }
476f4682712SMarri Devender Rao 
477f4682712SMarri Devender Rao void Manager::addEntry(X509_NAME* x509Name, const char* field,
478f4682712SMarri Devender Rao                        const std::string& bytes)
479f4682712SMarri Devender Rao {
480f4682712SMarri Devender Rao     if (bytes.empty())
481f4682712SMarri Devender Rao     {
482f4682712SMarri Devender Rao         return;
483f4682712SMarri Devender Rao     }
484f4682712SMarri Devender Rao     int ret = X509_NAME_add_entry_by_txt(
485f4682712SMarri Devender Rao         x509Name, field, MBSTRING_ASC,
486f4682712SMarri Devender Rao         reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
487f4682712SMarri Devender Rao     if (ret != 1)
488f4682712SMarri Devender Rao     {
489f4682712SMarri Devender Rao         log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
490f4682712SMarri Devender Rao                         entry("VALUE=%s", bytes.c_str()));
491f4682712SMarri Devender Rao         elog<InternalFailure>();
492f4682712SMarri Devender Rao     }
493f4682712SMarri Devender Rao }
494f4682712SMarri Devender Rao 
495f4682712SMarri Devender Rao void Manager::createCSRObject(const Status& status)
496f4682712SMarri Devender Rao {
497f4682712SMarri Devender Rao     if (csrPtr)
498f4682712SMarri Devender Rao     {
499f4682712SMarri Devender Rao         csrPtr.reset(nullptr);
500f4682712SMarri Devender Rao     }
501f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
502f4682712SMarri Devender Rao     csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
503f4682712SMarri Devender Rao                                    certInstallPath.c_str(), status);
504f4682712SMarri Devender Rao }
505f4682712SMarri Devender Rao 
506f4682712SMarri Devender Rao void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
507f4682712SMarri Devender Rao {
508f4682712SMarri Devender Rao     if (fs::exists(filePath))
509f4682712SMarri Devender Rao     {
510f4682712SMarri Devender Rao         log<level::INFO>("Removing the existing file",
511f4682712SMarri Devender Rao                          entry("FILENAME=%s", filePath.c_str()));
512f4682712SMarri Devender Rao         if (!fs::remove(filePath.c_str()))
513f4682712SMarri Devender Rao         {
514f4682712SMarri Devender Rao             log<level::ERR>("Unable to remove the file",
515f4682712SMarri Devender Rao                             entry("FILENAME=%s", filePath.c_str()));
516f4682712SMarri Devender Rao             elog<InternalFailure>();
517f4682712SMarri Devender Rao         }
518f4682712SMarri Devender Rao     }
519f4682712SMarri Devender Rao 
520f4682712SMarri Devender Rao     FILE* fp = NULL;
521f4682712SMarri Devender Rao 
522f4682712SMarri Devender Rao     if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
523f4682712SMarri Devender Rao     {
524f4682712SMarri Devender Rao         log<level::ERR>("Error opening the file to write the CSR",
525f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
526f4682712SMarri Devender Rao         elog<InternalFailure>();
527f4682712SMarri Devender Rao     }
528f4682712SMarri Devender Rao 
529f4682712SMarri Devender Rao     int rc = PEM_write_X509_REQ(fp, x509Req.get());
530f4682712SMarri Devender Rao     if (!rc)
531f4682712SMarri Devender Rao     {
532f4682712SMarri Devender Rao         log<level::ERR>("PEM write routine failed",
533f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
534f4682712SMarri Devender Rao         std::fclose(fp);
535f4682712SMarri Devender Rao         elog<InternalFailure>();
536f4682712SMarri Devender Rao     }
537f4682712SMarri Devender Rao     std::fclose(fp);
538f4682712SMarri Devender Rao }
539f4682712SMarri Devender Rao 
540db029c95SKowalski, Kamil void Manager::createCertificates()
541db029c95SKowalski, Kamil {
542db029c95SKowalski, Kamil     auto certObjectPath = objectPath + '/';
543db029c95SKowalski, Kamil 
544db029c95SKowalski, Kamil     if (certType == phosphor::certs::AUTHORITY)
545db029c95SKowalski, Kamil     {
546db029c95SKowalski, Kamil         // Create directory
547db029c95SKowalski, Kamil         fs::create_directories(certInstallPath);
548db029c95SKowalski, Kamil 
549db029c95SKowalski, Kamil         // Check if above created proper path
550db029c95SKowalski, Kamil         if (!fs::is_directory(certInstallPath))
551db029c95SKowalski, Kamil         {
552db029c95SKowalski, Kamil             log<level::ERR>("Certificate installation path exists and it is "
553db029c95SKowalski, Kamil                             "not a directory");
554db029c95SKowalski, Kamil             elog<InternalFailure>();
555db029c95SKowalski, Kamil             return;
556db029c95SKowalski, Kamil         }
557db029c95SKowalski, Kamil 
558db029c95SKowalski, Kamil         for (auto& path : fs::directory_iterator(certInstallPath))
559ffad1ef1SMarri Devender Rao         {
560ffad1ef1SMarri Devender Rao             try
561ffad1ef1SMarri Devender Rao             {
562db029c95SKowalski, Kamil                 installedCerts.emplace_back(std::make_unique<Certificate>(
563db029c95SKowalski, Kamil                     bus, certObjectPath + std::to_string(certIdCounter++),
564db029c95SKowalski, Kamil                     certType, unitToRestart, certInstallPath, path.path(), true,
565db029c95SKowalski, Kamil                     certWatchPtr));
566ffad1ef1SMarri Devender Rao             }
567ffad1ef1SMarri Devender Rao             catch (const InternalFailure& e)
568ffad1ef1SMarri Devender Rao             {
569ffad1ef1SMarri Devender Rao                 report<InternalFailure>();
570ffad1ef1SMarri Devender Rao             }
571ffad1ef1SMarri Devender Rao             catch (const InvalidCertificate& e)
572ffad1ef1SMarri Devender Rao             {
573ffad1ef1SMarri Devender Rao                 report<InvalidCertificate>(
574ffad1ef1SMarri Devender Rao                     Reason("Existing certificate file is corrupted"));
575ffad1ef1SMarri Devender Rao             }
576ffad1ef1SMarri Devender Rao         }
577db029c95SKowalski, Kamil     }
578db029c95SKowalski, Kamil     else if (fs::exists(certInstallPath))
579db029c95SKowalski, Kamil     {
580db029c95SKowalski, Kamil         try
581db029c95SKowalski, Kamil         {
582db029c95SKowalski, Kamil             installedCerts.emplace_back(std::make_unique<Certificate>(
583db029c95SKowalski, Kamil                 bus, certObjectPath + '1', certType, unitToRestart,
584db029c95SKowalski, Kamil                 certInstallPath, certInstallPath, true, certWatchPtr));
585db029c95SKowalski, Kamil         }
586db029c95SKowalski, Kamil         catch (const InternalFailure& e)
587db029c95SKowalski, Kamil         {
588db029c95SKowalski, Kamil             report<InternalFailure>();
589db029c95SKowalski, Kamil         }
590db029c95SKowalski, Kamil         catch (const InvalidCertificate& e)
591db029c95SKowalski, Kamil         {
592db029c95SKowalski, Kamil             report<InvalidCertificate>(
593db029c95SKowalski, Kamil                 Reason("Existing certificate file is corrupted"));
594db029c95SKowalski, Kamil         }
595db029c95SKowalski, Kamil     }
596db029c95SKowalski, Kamil }
597c6e58c7eSRamesh Iyyar 
598c6e58c7eSRamesh Iyyar void Manager::createRSAPrivateKeyFile()
599c6e58c7eSRamesh Iyyar {
600c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
601c6e58c7eSRamesh Iyyar         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
602c6e58c7eSRamesh Iyyar 
603c6e58c7eSRamesh Iyyar     try
604c6e58c7eSRamesh Iyyar     {
605c6e58c7eSRamesh Iyyar         if (!fs::exists(rsaPrivateKeyFileName))
606c6e58c7eSRamesh Iyyar         {
607c6e58c7eSRamesh Iyyar             writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
608c6e58c7eSRamesh Iyyar                             RSA_PRIV_KEY_FILE_NAME);
609c6e58c7eSRamesh Iyyar         }
610c6e58c7eSRamesh Iyyar     }
611c6e58c7eSRamesh Iyyar     catch (const InternalFailure& e)
612c6e58c7eSRamesh Iyyar     {
613c6e58c7eSRamesh Iyyar         report<InternalFailure>();
614c6e58c7eSRamesh Iyyar     }
615c6e58c7eSRamesh Iyyar }
616c6e58c7eSRamesh Iyyar 
617c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
618c6e58c7eSRamesh Iyyar {
619c6e58c7eSRamesh Iyyar     if (keyBitLength != SUPPORTED_KEYBITLENGTH)
620c6e58c7eSRamesh Iyyar     {
621c6e58c7eSRamesh Iyyar         log<level::ERR>(
622c6e58c7eSRamesh Iyyar             "Given Key bit length is not supported",
623c6e58c7eSRamesh Iyyar             entry("GIVENKEYBITLENGTH=%d", keyBitLength),
624c6e58c7eSRamesh Iyyar             entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
625c6e58c7eSRamesh Iyyar         elog<InvalidArgument>(
626c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYBITLENGTH"),
627c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
628c6e58c7eSRamesh Iyyar     }
629c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
630c6e58c7eSRamesh Iyyar         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
631c6e58c7eSRamesh Iyyar 
632c6e58c7eSRamesh Iyyar     FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
633c6e58c7eSRamesh Iyyar     if (!privateKeyFile)
634c6e58c7eSRamesh Iyyar     {
635c6e58c7eSRamesh Iyyar         log<level::ERR>("Unable to open RSA private key file to read",
636c6e58c7eSRamesh Iyyar                         entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
637c6e58c7eSRamesh Iyyar                         entry("ERRORREASON=%s", strerror(errno)));
638c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
639c6e58c7eSRamesh Iyyar     }
640c6e58c7eSRamesh Iyyar 
641c6e58c7eSRamesh Iyyar     EVP_PKEY_Ptr privateKey(
642c6e58c7eSRamesh Iyyar         PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
643c6e58c7eSRamesh Iyyar         ::EVP_PKEY_free);
644c6e58c7eSRamesh Iyyar     std::fclose(privateKeyFile);
645c6e58c7eSRamesh Iyyar 
646c6e58c7eSRamesh Iyyar     if (!privateKey)
647c6e58c7eSRamesh Iyyar     {
648c6e58c7eSRamesh Iyyar         log<level::ERR>("Error occured during PEM_read_PrivateKey call");
649c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
650c6e58c7eSRamesh Iyyar     }
651c6e58c7eSRamesh Iyyar     return privateKey;
652c6e58c7eSRamesh Iyyar }
653cfbc8dc8SJayanth Othayoth } // namespace certs
654cfbc8dc8SJayanth Othayoth } // namespace phosphor
655