xref: /openbmc/phosphor-certificate-manager/certs_manager.cpp (revision 7641105d8c6a325d520a8f107a627638298962fc)
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
57bf7c588cSMarri Devender Rao     if (fs::exists(certInstallPath))
58bf7c588cSMarri Devender Rao     {
59ffad1ef1SMarri Devender Rao         createCertificate();
60ffad1ef1SMarri Devender Rao     }
61ffad1ef1SMarri Devender Rao 
62ffad1ef1SMarri Devender Rao     // watch is not required for authority certificates
63ffad1ef1SMarri Devender Rao     if (certType != AUTHORITY)
64ffad1ef1SMarri Devender Rao     {
65ffad1ef1SMarri Devender Rao         // watch for certificate file create/replace
66ffad1ef1SMarri Devender Rao         certWatchPtr = std::make_unique<
67ffad1ef1SMarri Devender Rao             Watch>(event, certInstallPath, [this]() {
68bf7c588cSMarri Devender Rao             try
69bf7c588cSMarri Devender Rao             {
70ffad1ef1SMarri Devender Rao                 // if certificate file existing update it
71ffad1ef1SMarri Devender Rao                 if (certificatePtr != nullptr)
72ffad1ef1SMarri Devender Rao                 {
73ffad1ef1SMarri Devender Rao                     log<level::INFO>(
74ffad1ef1SMarri Devender Rao                         "Inotify callback to update certificate properties");
75ffad1ef1SMarri Devender Rao                     certificatePtr->populateProperties();
76ffad1ef1SMarri Devender Rao                 }
77ffad1ef1SMarri Devender Rao                 else
78ffad1ef1SMarri Devender Rao                 {
79ffad1ef1SMarri Devender Rao                     log<level::INFO>(
80ffad1ef1SMarri Devender Rao                         "Inotify callback to create certificate object");
81ffad1ef1SMarri Devender Rao                     createCertificate();
82ffad1ef1SMarri Devender Rao                 }
83bf7c588cSMarri Devender Rao             }
84bf7c588cSMarri Devender Rao             catch (const InternalFailure& e)
85bf7c588cSMarri Devender Rao             {
86ffad1ef1SMarri Devender Rao                 commit<InternalFailure>();
87bf7c588cSMarri Devender Rao             }
88bf7c588cSMarri Devender Rao             catch (const InvalidCertificate& e)
89bf7c588cSMarri Devender Rao             {
90ffad1ef1SMarri Devender Rao                 commit<InvalidCertificate>();
91bf7c588cSMarri Devender Rao             }
92ffad1ef1SMarri Devender Rao         });
93bf7c588cSMarri Devender Rao     }
94dd74bd20SJayanth Othayoth }
95589159f2SJayanth Othayoth 
966ceec40bSMarri Devender Rao void Manager::install(const std::string filePath)
97cfbc8dc8SJayanth Othayoth {
981396511dSMarri Devender Rao     using NotAllowed =
991396511dSMarri Devender Rao         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
1001396511dSMarri Devender Rao     using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
1011396511dSMarri Devender Rao     // TODO: Issue#3 At present supporting only one certificate to be
1021396511dSMarri Devender Rao     // uploaded this need to be revisited to support multiple
1031396511dSMarri Devender Rao     // certificates
1041396511dSMarri Devender Rao     if (certificatePtr != nullptr)
1051396511dSMarri Devender Rao     {
1061396511dSMarri Devender Rao         elog<NotAllowed>(Reason("Certificate already exist"));
1071396511dSMarri Devender Rao     }
108ffad1ef1SMarri Devender Rao 
1091396511dSMarri Devender Rao     auto certObjectPath = objectPath + '/' + '1';
1108f80c35bSMarri Devender Rao     certificatePtr = std::make_unique<Certificate>(
1118f80c35bSMarri Devender Rao         bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath,
112ffad1ef1SMarri Devender Rao         false, certWatchPtr);
113589159f2SJayanth Othayoth }
114ae70b3daSDeepak Kodihalli 
115ae70b3daSDeepak Kodihalli void Manager::delete_()
116ae70b3daSDeepak Kodihalli {
1176ceec40bSMarri Devender Rao     // TODO: #Issue 4 when a certificate is deleted system auto generates
1186ceec40bSMarri Devender Rao     // certificate file. At present we are not supporting creation of
1196ceec40bSMarri Devender Rao     // certificate object for the auto-generated certificate file as
1206ceec40bSMarri Devender Rao     // deletion if only applicable for REST server and Bmcweb does not allow
1216ceec40bSMarri Devender Rao     // deletion of certificates
1226ceec40bSMarri Devender Rao     if (certificatePtr != nullptr)
123ae70b3daSDeepak Kodihalli     {
1246ceec40bSMarri Devender Rao         certificatePtr.reset(nullptr);
125ae70b3daSDeepak Kodihalli     }
126ae70b3daSDeepak Kodihalli }
127f4682712SMarri Devender Rao 
128f4682712SMarri Devender Rao std::string Manager::generateCSR(
129f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
130f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
131f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
132f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
133f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
134f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
135f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
136f4682712SMarri Devender Rao {
137f4682712SMarri Devender Rao     // We support only one CSR.
138f4682712SMarri Devender Rao     csrPtr.reset(nullptr);
139f4682712SMarri Devender Rao     auto pid = fork();
140f4682712SMarri Devender Rao     if (pid == -1)
141f4682712SMarri Devender Rao     {
142f4682712SMarri Devender Rao         log<level::ERR>("Error occurred during forking process");
143f4682712SMarri Devender Rao         report<InternalFailure>();
144f4682712SMarri Devender Rao     }
145f4682712SMarri Devender Rao     else if (pid == 0)
146f4682712SMarri Devender Rao     {
147f4682712SMarri Devender Rao         try
148f4682712SMarri Devender Rao         {
149f4682712SMarri Devender Rao             generateCSRHelper(alternativeNames, challengePassword, city,
150f4682712SMarri Devender Rao                               commonName, contactPerson, country, email,
151f4682712SMarri Devender Rao                               givenName, initials, keyBitLength, keyCurveId,
152f4682712SMarri Devender Rao                               keyPairAlgorithm, keyUsage, organization,
153f4682712SMarri Devender Rao                               organizationalUnit, state, surname,
154f4682712SMarri Devender Rao                               unstructuredName);
155f4682712SMarri Devender Rao             exit(EXIT_SUCCESS);
156f4682712SMarri Devender Rao         }
157f4682712SMarri Devender Rao         catch (const InternalFailure& e)
158f4682712SMarri Devender Rao         {
159f4682712SMarri Devender Rao             // commit the error reported in child process and exit
160f4682712SMarri Devender Rao             // Callback method from SDEvent Loop looks for exit status
161f4682712SMarri Devender Rao             exit(EXIT_FAILURE);
162f4682712SMarri Devender Rao             commit<InternalFailure>();
163f4682712SMarri Devender Rao         }
164f4682712SMarri Devender Rao     }
165f4682712SMarri Devender Rao     else
166f4682712SMarri Devender Rao     {
167f4682712SMarri Devender Rao         using namespace sdeventplus::source;
168f4682712SMarri Devender Rao         Child::Callback callback = [this](Child& eventSource,
169f4682712SMarri Devender Rao                                           const siginfo_t* si) {
170f4682712SMarri Devender Rao             eventSource.set_enabled(Enabled::On);
171f4682712SMarri Devender Rao             if (si->si_status != 0)
172f4682712SMarri Devender Rao             {
173f4682712SMarri Devender Rao                 this->createCSRObject(Status::FAILURE);
174f4682712SMarri Devender Rao             }
175f4682712SMarri Devender Rao             else
176f4682712SMarri Devender Rao             {
177f4682712SMarri Devender Rao                 this->createCSRObject(Status::SUCCESS);
178f4682712SMarri Devender Rao             }
179f4682712SMarri Devender Rao         };
180f4682712SMarri Devender Rao         try
181f4682712SMarri Devender Rao         {
182f4682712SMarri Devender Rao             sigset_t ss;
183f4682712SMarri Devender Rao             if (sigemptyset(&ss) < 0)
184f4682712SMarri Devender Rao             {
185f4682712SMarri Devender Rao                 log<level::ERR>("Unable to initialize signal set");
186f4682712SMarri Devender Rao                 elog<InternalFailure>();
187f4682712SMarri Devender Rao             }
188f4682712SMarri Devender Rao             if (sigaddset(&ss, SIGCHLD) < 0)
189f4682712SMarri Devender Rao             {
190f4682712SMarri Devender Rao                 log<level::ERR>("Unable to add signal to signal set");
191f4682712SMarri Devender Rao                 elog<InternalFailure>();
192f4682712SMarri Devender Rao             }
193f4682712SMarri Devender Rao 
194f4682712SMarri Devender Rao             // Block SIGCHLD first, so that the event loop can handle it
195f4682712SMarri Devender Rao             if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
196f4682712SMarri Devender Rao             {
197f4682712SMarri Devender Rao                 log<level::ERR>("Unable to block signal");
198f4682712SMarri Devender Rao                 elog<InternalFailure>();
199f4682712SMarri Devender Rao             }
200f4682712SMarri Devender Rao             if (childPtr)
201f4682712SMarri Devender Rao             {
202f4682712SMarri Devender Rao                 childPtr.reset();
203f4682712SMarri Devender Rao             }
204f4682712SMarri Devender Rao             childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
205f4682712SMarri Devender Rao                                                std::move(callback));
206f4682712SMarri Devender Rao         }
207f4682712SMarri Devender Rao         catch (const InternalFailure& e)
208f4682712SMarri Devender Rao         {
209f4682712SMarri Devender Rao             commit<InternalFailure>();
210f4682712SMarri Devender Rao         }
211f4682712SMarri Devender Rao     }
212f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
213f4682712SMarri Devender Rao     return csrObjectPath;
214f4682712SMarri Devender Rao }
215f4682712SMarri Devender Rao 
216ffad1ef1SMarri Devender Rao CertificatePtr& Manager::getCertificate()
217ffad1ef1SMarri Devender Rao {
218ffad1ef1SMarri Devender Rao     return certificatePtr;
219ffad1ef1SMarri Devender Rao }
220ffad1ef1SMarri Devender Rao 
221f4682712SMarri Devender Rao void Manager::generateCSRHelper(
222f4682712SMarri Devender Rao     std::vector<std::string> alternativeNames, std::string challengePassword,
223f4682712SMarri Devender Rao     std::string city, std::string commonName, std::string contactPerson,
224f4682712SMarri Devender Rao     std::string country, std::string email, std::string givenName,
225f4682712SMarri Devender Rao     std::string initials, int64_t keyBitLength, std::string keyCurveId,
226f4682712SMarri Devender Rao     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
227f4682712SMarri Devender Rao     std::string organization, std::string organizationalUnit, std::string state,
228f4682712SMarri Devender Rao     std::string surname, std::string unstructuredName)
229f4682712SMarri Devender Rao {
230f4682712SMarri Devender Rao     int ret = 0;
231f4682712SMarri Devender Rao 
232f4682712SMarri Devender Rao     // set version of x509 req
233f4682712SMarri Devender Rao     int nVersion = 1;
234f4682712SMarri Devender Rao     // TODO: Issue#6 need to make version number configurable
235f4682712SMarri Devender Rao     X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
236f4682712SMarri Devender Rao     ret = X509_REQ_set_version(x509Req.get(), nVersion);
237f4682712SMarri Devender Rao     if (ret == 0)
238f4682712SMarri Devender Rao     {
239f4682712SMarri Devender Rao         log<level::ERR>("Error occured during X509_REQ_set_version call");
240f4682712SMarri Devender Rao         elog<InternalFailure>();
241f4682712SMarri Devender Rao     }
242f4682712SMarri Devender Rao 
243f4682712SMarri Devender Rao     // set subject of x509 req
244f4682712SMarri Devender Rao     X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
245f4682712SMarri Devender Rao 
246f4682712SMarri Devender Rao     if (!alternativeNames.empty())
247f4682712SMarri Devender Rao     {
248f4682712SMarri Devender Rao         for (auto& name : alternativeNames)
249f4682712SMarri Devender Rao         {
250f4682712SMarri Devender Rao             addEntry(x509Name, "subjectAltName", name);
251f4682712SMarri Devender Rao         }
252f4682712SMarri Devender Rao     }
253f4682712SMarri Devender Rao     addEntry(x509Name, "challengePassword", challengePassword);
254f4682712SMarri Devender Rao     addEntry(x509Name, "L", city);
255f4682712SMarri Devender Rao     addEntry(x509Name, "CN", commonName);
256f4682712SMarri Devender Rao     addEntry(x509Name, "name", contactPerson);
257f4682712SMarri Devender Rao     addEntry(x509Name, "C", country);
258f4682712SMarri Devender Rao     addEntry(x509Name, "emailAddress", email);
259f4682712SMarri Devender Rao     addEntry(x509Name, "GN", givenName);
260f4682712SMarri Devender Rao     addEntry(x509Name, "initials", initials);
261f4682712SMarri Devender Rao     addEntry(x509Name, "algorithm", keyPairAlgorithm);
262f4682712SMarri Devender Rao     if (!keyUsage.empty())
263f4682712SMarri Devender Rao     {
264f4682712SMarri Devender Rao         for (auto& usage : keyUsage)
265f4682712SMarri Devender Rao         {
266*7641105dSMarri Devender Rao             if (isExtendedKeyUsage(usage))
267*7641105dSMarri Devender Rao             {
268*7641105dSMarri Devender Rao                 addEntry(x509Name, "extendedKeyUsage", usage);
269*7641105dSMarri Devender Rao             }
270*7641105dSMarri Devender Rao             else
271*7641105dSMarri Devender Rao             {
272f4682712SMarri Devender Rao                 addEntry(x509Name, "keyUsage", usage);
273f4682712SMarri Devender Rao             }
274f4682712SMarri Devender Rao         }
275*7641105dSMarri Devender Rao     }
276f4682712SMarri Devender Rao     addEntry(x509Name, "O", organization);
277f4682712SMarri Devender Rao     addEntry(x509Name, "ST", state);
278f4682712SMarri Devender Rao     addEntry(x509Name, "SN", surname);
279f4682712SMarri Devender Rao     addEntry(x509Name, "unstructuredName", unstructuredName);
280f4682712SMarri Devender Rao 
2818a09b52aSRamesh Iyyar     EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
2828a09b52aSRamesh Iyyar 
2838a09b52aSRamesh Iyyar     log<level::INFO>("Given Key pair algorithm",
2848a09b52aSRamesh Iyyar                      entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
2858a09b52aSRamesh Iyyar 
2868a09b52aSRamesh Iyyar     // Used EC algorithm as default if user did not give algorithm type.
2878a09b52aSRamesh Iyyar     if (keyPairAlgorithm == "RSA")
288c6e58c7eSRamesh Iyyar         pKey = getRSAKeyPair(keyBitLength);
2898a09b52aSRamesh Iyyar     else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
290c6e58c7eSRamesh Iyyar         pKey = generateECKeyPair(keyCurveId);
2918a09b52aSRamesh Iyyar     else
2928a09b52aSRamesh Iyyar     {
2938a09b52aSRamesh Iyyar         log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
2948a09b52aSRamesh Iyyar                         "RSA and EC only");
2958a09b52aSRamesh Iyyar         elog<InvalidArgument>(
2968a09b52aSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
2978a09b52aSRamesh Iyyar             Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
2988a09b52aSRamesh Iyyar     }
2998a09b52aSRamesh Iyyar 
3008a09b52aSRamesh Iyyar     ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
3018a09b52aSRamesh Iyyar     if (ret == 0)
3028a09b52aSRamesh Iyyar     {
3038a09b52aSRamesh Iyyar         log<level::ERR>("Error occured while setting Public key");
3048a09b52aSRamesh Iyyar         elog<InternalFailure>();
3058a09b52aSRamesh Iyyar     }
3068a09b52aSRamesh Iyyar 
3078a09b52aSRamesh Iyyar     // Write private key to file
308c6e58c7eSRamesh Iyyar     writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
309f4682712SMarri Devender Rao 
310f4682712SMarri Devender Rao     // set sign key of x509 req
311f4682712SMarri Devender Rao     ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
3128a09b52aSRamesh Iyyar     if (ret == 0)
313f4682712SMarri Devender Rao     {
314f4682712SMarri Devender Rao         log<level::ERR>("Error occured while signing key of x509");
315f4682712SMarri Devender Rao         elog<InternalFailure>();
316f4682712SMarri Devender Rao     }
3178a09b52aSRamesh Iyyar 
318f4682712SMarri Devender Rao     log<level::INFO>("Writing CSR to file");
319c6e58c7eSRamesh Iyyar     fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
320c6e58c7eSRamesh Iyyar     writeCSR(csrFilePath.string(), x509Req);
321f4682712SMarri Devender Rao }
322f4682712SMarri Devender Rao 
323*7641105dSMarri Devender Rao bool Manager::isExtendedKeyUsage(const std::string& usage)
324*7641105dSMarri Devender Rao {
325*7641105dSMarri Devender Rao     const static std::array<const char*, 6> usageList = {
326*7641105dSMarri Devender Rao         "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
327*7641105dSMarri Devender Rao         "Timestamping",         "CodeSigning",          "EmailProtection"};
328*7641105dSMarri Devender Rao     auto it = std::find_if(
329*7641105dSMarri Devender Rao         usageList.begin(), usageList.end(),
330*7641105dSMarri Devender Rao         [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
331*7641105dSMarri Devender Rao     return it != usageList.end();
332*7641105dSMarri Devender Rao }
3338a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
334f4682712SMarri Devender Rao {
335f4682712SMarri Devender Rao     int ret = 0;
336f4682712SMarri Devender Rao     // generate rsa key
337f4682712SMarri Devender Rao     BIGNUM_Ptr bne(BN_new(), ::BN_free);
338f4682712SMarri Devender Rao     ret = BN_set_word(bne.get(), RSA_F4);
339f4682712SMarri Devender Rao     if (ret == 0)
340f4682712SMarri Devender Rao     {
341f4682712SMarri Devender Rao         log<level::ERR>("Error occured during BN_set_word call");
342f4682712SMarri Devender Rao         elog<InternalFailure>();
343f4682712SMarri Devender Rao     }
344f4682712SMarri Devender Rao 
3458a09b52aSRamesh Iyyar     int64_t keyBitLen = keyBitLength;
346f4682712SMarri Devender Rao     // set keybit length to default value if not set
3478a09b52aSRamesh Iyyar     if (keyBitLen <= 0)
348f4682712SMarri Devender Rao     {
3498a09b52aSRamesh Iyyar         constexpr auto DEFAULT_KEYBITLENGTH = 2048;
3508a09b52aSRamesh Iyyar         log<level::INFO>(
3518a09b52aSRamesh Iyyar             "KeyBitLength is not given.Hence, using default KeyBitLength",
3528a09b52aSRamesh Iyyar             entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
3538a09b52aSRamesh Iyyar         keyBitLen = DEFAULT_KEYBITLENGTH;
354f4682712SMarri Devender Rao     }
355f4682712SMarri Devender Rao     RSA* rsa = RSA_new();
3568a09b52aSRamesh Iyyar     ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
357f4682712SMarri Devender Rao     if (ret != 1)
358f4682712SMarri Devender Rao     {
359f4682712SMarri Devender Rao         free(rsa);
360f4682712SMarri Devender Rao         log<level::ERR>("Error occured during RSA_generate_key_ex call",
3618a09b52aSRamesh Iyyar                         entry("KEYBITLENGTH=%PRIu64", keyBitLen));
362f4682712SMarri Devender Rao         elog<InternalFailure>();
363f4682712SMarri Devender Rao     }
364f4682712SMarri Devender Rao 
365f4682712SMarri Devender Rao     // set public key of x509 req
366f4682712SMarri Devender Rao     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
3678a09b52aSRamesh Iyyar     ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
368f4682712SMarri Devender Rao     if (ret == 0)
369f4682712SMarri Devender Rao     {
3708a09b52aSRamesh Iyyar         free(rsa);
3718a09b52aSRamesh Iyyar         log<level::ERR>("Error occured during assign rsa key into EVP");
372f4682712SMarri Devender Rao         elog<InternalFailure>();
373f4682712SMarri Devender Rao     }
374f4682712SMarri Devender Rao 
3758a09b52aSRamesh Iyyar     return pKey;
3768a09b52aSRamesh Iyyar }
3778a09b52aSRamesh Iyyar 
3788a09b52aSRamesh Iyyar EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
3798a09b52aSRamesh Iyyar {
3808a09b52aSRamesh Iyyar     std::string curId(curveId);
3818a09b52aSRamesh Iyyar 
3828a09b52aSRamesh Iyyar     if (curId.empty())
3838a09b52aSRamesh Iyyar     {
3848a09b52aSRamesh Iyyar         // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
3858a09b52aSRamesh Iyyar         constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
3868a09b52aSRamesh Iyyar         log<level::INFO>(
3878a09b52aSRamesh Iyyar             "KeyCurveId is not given. Hence using default curve id",
3888a09b52aSRamesh Iyyar             entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
3898a09b52aSRamesh Iyyar         curId = DEFAULT_KEYCURVEID;
3908a09b52aSRamesh Iyyar     }
3918a09b52aSRamesh Iyyar 
3928a09b52aSRamesh Iyyar     int ecGrp = OBJ_txt2nid(curId.c_str());
3938a09b52aSRamesh Iyyar 
3948a09b52aSRamesh Iyyar     if (ecGrp == NID_undef)
3958a09b52aSRamesh Iyyar     {
3968a09b52aSRamesh Iyyar         log<level::ERR>(
3978a09b52aSRamesh Iyyar             "Error occured during convert the curve id string format into NID",
3988a09b52aSRamesh Iyyar             entry("KEYCURVEID=%s", curId.c_str()));
3998a09b52aSRamesh Iyyar         elog<InternalFailure>();
4008a09b52aSRamesh Iyyar     }
4018a09b52aSRamesh Iyyar 
4028a09b52aSRamesh Iyyar     EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
4038a09b52aSRamesh Iyyar 
4048a09b52aSRamesh Iyyar     if (ecKey == NULL)
4058a09b52aSRamesh Iyyar     {
4068a09b52aSRamesh Iyyar         log<level::ERR>(
4078a09b52aSRamesh Iyyar             "Error occured during create the EC_Key object from NID",
4088a09b52aSRamesh Iyyar             entry("ECGROUP=%d", ecGrp));
4098a09b52aSRamesh Iyyar         elog<InternalFailure>();
4108a09b52aSRamesh Iyyar     }
4118a09b52aSRamesh Iyyar 
4128a09b52aSRamesh Iyyar     // If you want to save a key and later load it with
4138a09b52aSRamesh Iyyar     // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
4148a09b52aSRamesh Iyyar     // flag on the key.
4158a09b52aSRamesh Iyyar     EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
4168a09b52aSRamesh Iyyar 
4178a09b52aSRamesh Iyyar     int ret = EC_KEY_generate_key(ecKey);
4188a09b52aSRamesh Iyyar 
4198a09b52aSRamesh Iyyar     if (ret == 0)
4208a09b52aSRamesh Iyyar     {
4218a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
4228a09b52aSRamesh Iyyar         log<level::ERR>("Error occured during generate EC key");
4238a09b52aSRamesh Iyyar         elog<InternalFailure>();
4248a09b52aSRamesh Iyyar     }
4258a09b52aSRamesh Iyyar 
4268a09b52aSRamesh Iyyar     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
4278a09b52aSRamesh Iyyar     ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
4288a09b52aSRamesh Iyyar     if (ret == 0)
4298a09b52aSRamesh Iyyar     {
4308a09b52aSRamesh Iyyar         EC_KEY_free(ecKey);
4318a09b52aSRamesh Iyyar         log<level::ERR>("Error occured during assign EC Key into EVP");
4328a09b52aSRamesh Iyyar         elog<InternalFailure>();
4338a09b52aSRamesh Iyyar     }
4348a09b52aSRamesh Iyyar 
4358a09b52aSRamesh Iyyar     return pKey;
4368a09b52aSRamesh Iyyar }
4378a09b52aSRamesh Iyyar 
438c6e58c7eSRamesh Iyyar void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
439c6e58c7eSRamesh Iyyar                               const std::string& privKeyFileName)
4408a09b52aSRamesh Iyyar {
4418a09b52aSRamesh Iyyar     log<level::INFO>("Writing private key to file");
442f4682712SMarri Devender Rao     // write private key to file
443c6e58c7eSRamesh Iyyar     fs::path privKeyPath = certParentInstallPath / privKeyFileName;
444f4682712SMarri Devender Rao 
445f4682712SMarri Devender Rao     FILE* fp = std::fopen(privKeyPath.c_str(), "w");
446f4682712SMarri Devender Rao     if (fp == NULL)
447f4682712SMarri Devender Rao     {
448f4682712SMarri Devender Rao         log<level::ERR>("Error occured creating private key file");
449f4682712SMarri Devender Rao         elog<InternalFailure>();
450f4682712SMarri Devender Rao     }
4518a09b52aSRamesh Iyyar     int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
452f4682712SMarri Devender Rao     std::fclose(fp);
453f4682712SMarri Devender Rao     if (ret == 0)
454f4682712SMarri Devender Rao     {
455f4682712SMarri Devender Rao         log<level::ERR>("Error occured while writing private key to file");
456f4682712SMarri Devender Rao         elog<InternalFailure>();
457f4682712SMarri Devender Rao     }
458f4682712SMarri Devender Rao }
459f4682712SMarri Devender Rao 
460f4682712SMarri Devender Rao void Manager::addEntry(X509_NAME* x509Name, const char* field,
461f4682712SMarri Devender Rao                        const std::string& bytes)
462f4682712SMarri Devender Rao {
463f4682712SMarri Devender Rao     if (bytes.empty())
464f4682712SMarri Devender Rao     {
465f4682712SMarri Devender Rao         return;
466f4682712SMarri Devender Rao     }
467f4682712SMarri Devender Rao     int ret = X509_NAME_add_entry_by_txt(
468f4682712SMarri Devender Rao         x509Name, field, MBSTRING_ASC,
469f4682712SMarri Devender Rao         reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
470f4682712SMarri Devender Rao     if (ret != 1)
471f4682712SMarri Devender Rao     {
472f4682712SMarri Devender Rao         log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
473f4682712SMarri Devender Rao                         entry("VALUE=%s", bytes.c_str()));
474f4682712SMarri Devender Rao         elog<InternalFailure>();
475f4682712SMarri Devender Rao     }
476f4682712SMarri Devender Rao }
477f4682712SMarri Devender Rao 
478f4682712SMarri Devender Rao void Manager::createCSRObject(const Status& status)
479f4682712SMarri Devender Rao {
480f4682712SMarri Devender Rao     if (csrPtr)
481f4682712SMarri Devender Rao     {
482f4682712SMarri Devender Rao         csrPtr.reset(nullptr);
483f4682712SMarri Devender Rao     }
484f4682712SMarri Devender Rao     auto csrObjectPath = objectPath + '/' + "csr";
485f4682712SMarri Devender Rao     csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
486f4682712SMarri Devender Rao                                    certInstallPath.c_str(), status);
487f4682712SMarri Devender Rao }
488f4682712SMarri Devender Rao 
489f4682712SMarri Devender Rao void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
490f4682712SMarri Devender Rao {
491f4682712SMarri Devender Rao     if (fs::exists(filePath))
492f4682712SMarri Devender Rao     {
493f4682712SMarri Devender Rao         log<level::INFO>("Removing the existing file",
494f4682712SMarri Devender Rao                          entry("FILENAME=%s", filePath.c_str()));
495f4682712SMarri Devender Rao         if (!fs::remove(filePath.c_str()))
496f4682712SMarri Devender Rao         {
497f4682712SMarri Devender Rao             log<level::ERR>("Unable to remove the file",
498f4682712SMarri Devender Rao                             entry("FILENAME=%s", filePath.c_str()));
499f4682712SMarri Devender Rao             elog<InternalFailure>();
500f4682712SMarri Devender Rao         }
501f4682712SMarri Devender Rao     }
502f4682712SMarri Devender Rao 
503f4682712SMarri Devender Rao     FILE* fp = NULL;
504f4682712SMarri Devender Rao 
505f4682712SMarri Devender Rao     if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
506f4682712SMarri Devender Rao     {
507f4682712SMarri Devender Rao         log<level::ERR>("Error opening the file to write the CSR",
508f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
509f4682712SMarri Devender Rao         elog<InternalFailure>();
510f4682712SMarri Devender Rao     }
511f4682712SMarri Devender Rao 
512f4682712SMarri Devender Rao     int rc = PEM_write_X509_REQ(fp, x509Req.get());
513f4682712SMarri Devender Rao     if (!rc)
514f4682712SMarri Devender Rao     {
515f4682712SMarri Devender Rao         log<level::ERR>("PEM write routine failed",
516f4682712SMarri Devender Rao                         entry("FILENAME=%s", filePath.c_str()));
517f4682712SMarri Devender Rao         std::fclose(fp);
518f4682712SMarri Devender Rao         elog<InternalFailure>();
519f4682712SMarri Devender Rao     }
520f4682712SMarri Devender Rao     std::fclose(fp);
521f4682712SMarri Devender Rao }
522f4682712SMarri Devender Rao 
523ffad1ef1SMarri Devender Rao void Manager::createCertificate()
524ffad1ef1SMarri Devender Rao {
525ffad1ef1SMarri Devender Rao     try
526ffad1ef1SMarri Devender Rao     {
527ffad1ef1SMarri Devender Rao         // TODO: Issue#3 At present supporting only one certificate to be
528ffad1ef1SMarri Devender Rao         // uploaded this need to be revisited to support multiple
529ffad1ef1SMarri Devender Rao         // certificates
530ffad1ef1SMarri Devender Rao         auto certObjectPath = objectPath + '/' + '1';
531ffad1ef1SMarri Devender Rao         certificatePtr = std::make_unique<Certificate>(
532ffad1ef1SMarri Devender Rao             bus, certObjectPath, certType, unitToRestart, certInstallPath,
533ffad1ef1SMarri Devender Rao             certInstallPath, true, certWatchPtr);
534ffad1ef1SMarri Devender Rao     }
535ffad1ef1SMarri Devender Rao     catch (const InternalFailure& e)
536ffad1ef1SMarri Devender Rao     {
537ffad1ef1SMarri Devender Rao         report<InternalFailure>();
538ffad1ef1SMarri Devender Rao     }
539ffad1ef1SMarri Devender Rao     catch (const InvalidCertificate& e)
540ffad1ef1SMarri Devender Rao     {
541ffad1ef1SMarri Devender Rao         report<InvalidCertificate>(
542ffad1ef1SMarri Devender Rao             Reason("Existing certificate file is corrupted"));
543ffad1ef1SMarri Devender Rao     }
544ffad1ef1SMarri Devender Rao }
545c6e58c7eSRamesh Iyyar 
546c6e58c7eSRamesh Iyyar void Manager::createRSAPrivateKeyFile()
547c6e58c7eSRamesh Iyyar {
548c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
549c6e58c7eSRamesh Iyyar         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
550c6e58c7eSRamesh Iyyar 
551c6e58c7eSRamesh Iyyar     try
552c6e58c7eSRamesh Iyyar     {
553c6e58c7eSRamesh Iyyar         if (!fs::exists(rsaPrivateKeyFileName))
554c6e58c7eSRamesh Iyyar         {
555c6e58c7eSRamesh Iyyar             writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
556c6e58c7eSRamesh Iyyar                             RSA_PRIV_KEY_FILE_NAME);
557c6e58c7eSRamesh Iyyar         }
558c6e58c7eSRamesh Iyyar     }
559c6e58c7eSRamesh Iyyar     catch (const InternalFailure& e)
560c6e58c7eSRamesh Iyyar     {
561c6e58c7eSRamesh Iyyar         report<InternalFailure>();
562c6e58c7eSRamesh Iyyar     }
563c6e58c7eSRamesh Iyyar }
564c6e58c7eSRamesh Iyyar 
565c6e58c7eSRamesh Iyyar EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
566c6e58c7eSRamesh Iyyar {
567c6e58c7eSRamesh Iyyar     if (keyBitLength != SUPPORTED_KEYBITLENGTH)
568c6e58c7eSRamesh Iyyar     {
569c6e58c7eSRamesh Iyyar         log<level::ERR>(
570c6e58c7eSRamesh Iyyar             "Given Key bit length is not supported",
571c6e58c7eSRamesh Iyyar             entry("GIVENKEYBITLENGTH=%d", keyBitLength),
572c6e58c7eSRamesh Iyyar             entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
573c6e58c7eSRamesh Iyyar         elog<InvalidArgument>(
574c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_NAME("KEYBITLENGTH"),
575c6e58c7eSRamesh Iyyar             Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
576c6e58c7eSRamesh Iyyar     }
577c6e58c7eSRamesh Iyyar     fs::path rsaPrivateKeyFileName =
578c6e58c7eSRamesh Iyyar         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
579c6e58c7eSRamesh Iyyar 
580c6e58c7eSRamesh Iyyar     FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
581c6e58c7eSRamesh Iyyar     if (!privateKeyFile)
582c6e58c7eSRamesh Iyyar     {
583c6e58c7eSRamesh Iyyar         log<level::ERR>("Unable to open RSA private key file to read",
584c6e58c7eSRamesh Iyyar                         entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
585c6e58c7eSRamesh Iyyar                         entry("ERRORREASON=%s", strerror(errno)));
586c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
587c6e58c7eSRamesh Iyyar     }
588c6e58c7eSRamesh Iyyar 
589c6e58c7eSRamesh Iyyar     EVP_PKEY_Ptr privateKey(
590c6e58c7eSRamesh Iyyar         PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
591c6e58c7eSRamesh Iyyar         ::EVP_PKEY_free);
592c6e58c7eSRamesh Iyyar     std::fclose(privateKeyFile);
593c6e58c7eSRamesh Iyyar 
594c6e58c7eSRamesh Iyyar     if (!privateKey)
595c6e58c7eSRamesh Iyyar     {
596c6e58c7eSRamesh Iyyar         log<level::ERR>("Error occured during PEM_read_PrivateKey call");
597c6e58c7eSRamesh Iyyar         elog<InternalFailure>();
598c6e58c7eSRamesh Iyyar     }
599c6e58c7eSRamesh Iyyar     return privateKey;
600c6e58c7eSRamesh Iyyar }
601cfbc8dc8SJayanth Othayoth } // namespace certs
602cfbc8dc8SJayanth Othayoth } // namespace phosphor
603