xref: /openbmc/phosphor-certificate-manager/certs_manager.cpp (revision c6e58c7e7fade369e64c6eaee23a5b72f37a7ace)
1 #include "certs_manager.hpp"
2 
3 #include <openssl/pem.h>
4 #include <unistd.h>
5 
6 #include <phosphor-logging/elog-errors.hpp>
7 #include <xyz/openbmc_project/Certs/error.hpp>
8 #include <xyz/openbmc_project/Common/error.hpp>
9 namespace phosphor
10 {
11 namespace certs
12 {
13 using InternalFailure =
14     sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
15 using InvalidCertificate =
16     sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
17 using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
18 
19 using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
20 using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
21 using InvalidArgument =
22     sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
23 using Argument = xyz::openbmc_project::Common::InvalidArgument;
24 
25 constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
26 
27 Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
28                  const char* path, const CertificateType& type,
29                  UnitsToRestart&& unit, CertInstallPath&& installPath) :
30     Ifaces(bus, path),
31     bus(bus), event(event), objectPath(path), certType(type),
32     unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
33     certParentInstallPath(fs::path(certInstallPath).parent_path())
34 {
35     // Generating RSA private key file if certificate type is server/client
36     if (certType != AUTHORITY)
37     {
38         createRSAPrivateKeyFile();
39     }
40 
41     // restore any existing certificates
42     if (fs::exists(certInstallPath))
43     {
44         createCertificate();
45     }
46 
47     // watch is not required for authority certificates
48     if (certType != AUTHORITY)
49     {
50         // watch for certificate file create/replace
51         certWatchPtr = std::make_unique<
52             Watch>(event, certInstallPath, [this]() {
53             try
54             {
55                 // if certificate file existing update it
56                 if (certificatePtr != nullptr)
57                 {
58                     log<level::INFO>(
59                         "Inotify callback to update certificate properties");
60                     certificatePtr->populateProperties();
61                 }
62                 else
63                 {
64                     log<level::INFO>(
65                         "Inotify callback to create certificate object");
66                     createCertificate();
67                 }
68             }
69             catch (const InternalFailure& e)
70             {
71                 commit<InternalFailure>();
72             }
73             catch (const InvalidCertificate& e)
74             {
75                 commit<InvalidCertificate>();
76             }
77         });
78     }
79 }
80 
81 void Manager::install(const std::string filePath)
82 {
83     using NotAllowed =
84         sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
85     using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
86     // TODO: Issue#3 At present supporting only one certificate to be
87     // uploaded this need to be revisited to support multiple
88     // certificates
89     if (certificatePtr != nullptr)
90     {
91         elog<NotAllowed>(Reason("Certificate already exist"));
92     }
93 
94     auto certObjectPath = objectPath + '/' + '1';
95     certificatePtr = std::make_unique<Certificate>(
96         bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath,
97         false, certWatchPtr);
98 }
99 
100 void Manager::delete_()
101 {
102     // TODO: #Issue 4 when a certificate is deleted system auto generates
103     // certificate file. At present we are not supporting creation of
104     // certificate object for the auto-generated certificate file as
105     // deletion if only applicable for REST server and Bmcweb does not allow
106     // deletion of certificates
107     if (certificatePtr != nullptr)
108     {
109         certificatePtr.reset(nullptr);
110     }
111 }
112 
113 std::string Manager::generateCSR(
114     std::vector<std::string> alternativeNames, std::string challengePassword,
115     std::string city, std::string commonName, std::string contactPerson,
116     std::string country, std::string email, std::string givenName,
117     std::string initials, int64_t keyBitLength, std::string keyCurveId,
118     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
119     std::string organization, std::string organizationalUnit, std::string state,
120     std::string surname, std::string unstructuredName)
121 {
122     // We support only one CSR.
123     csrPtr.reset(nullptr);
124     auto pid = fork();
125     if (pid == -1)
126     {
127         log<level::ERR>("Error occurred during forking process");
128         report<InternalFailure>();
129     }
130     else if (pid == 0)
131     {
132         try
133         {
134             generateCSRHelper(alternativeNames, challengePassword, city,
135                               commonName, contactPerson, country, email,
136                               givenName, initials, keyBitLength, keyCurveId,
137                               keyPairAlgorithm, keyUsage, organization,
138                               organizationalUnit, state, surname,
139                               unstructuredName);
140             exit(EXIT_SUCCESS);
141         }
142         catch (const InternalFailure& e)
143         {
144             // commit the error reported in child process and exit
145             // Callback method from SDEvent Loop looks for exit status
146             exit(EXIT_FAILURE);
147             commit<InternalFailure>();
148         }
149     }
150     else
151     {
152         using namespace sdeventplus::source;
153         Child::Callback callback = [this](Child& eventSource,
154                                           const siginfo_t* si) {
155             eventSource.set_enabled(Enabled::On);
156             if (si->si_status != 0)
157             {
158                 this->createCSRObject(Status::FAILURE);
159             }
160             else
161             {
162                 this->createCSRObject(Status::SUCCESS);
163             }
164         };
165         try
166         {
167             sigset_t ss;
168             if (sigemptyset(&ss) < 0)
169             {
170                 log<level::ERR>("Unable to initialize signal set");
171                 elog<InternalFailure>();
172             }
173             if (sigaddset(&ss, SIGCHLD) < 0)
174             {
175                 log<level::ERR>("Unable to add signal to signal set");
176                 elog<InternalFailure>();
177             }
178 
179             // Block SIGCHLD first, so that the event loop can handle it
180             if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
181             {
182                 log<level::ERR>("Unable to block signal");
183                 elog<InternalFailure>();
184             }
185             if (childPtr)
186             {
187                 childPtr.reset();
188             }
189             childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
190                                                std::move(callback));
191         }
192         catch (const InternalFailure& e)
193         {
194             commit<InternalFailure>();
195         }
196     }
197     auto csrObjectPath = objectPath + '/' + "csr";
198     return csrObjectPath;
199 }
200 
201 CertificatePtr& Manager::getCertificate()
202 {
203     return certificatePtr;
204 }
205 
206 void Manager::generateCSRHelper(
207     std::vector<std::string> alternativeNames, std::string challengePassword,
208     std::string city, std::string commonName, std::string contactPerson,
209     std::string country, std::string email, std::string givenName,
210     std::string initials, int64_t keyBitLength, std::string keyCurveId,
211     std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
212     std::string organization, std::string organizationalUnit, std::string state,
213     std::string surname, std::string unstructuredName)
214 {
215     int ret = 0;
216 
217     // set version of x509 req
218     int nVersion = 1;
219     // TODO: Issue#6 need to make version number configurable
220     X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
221     ret = X509_REQ_set_version(x509Req.get(), nVersion);
222     if (ret == 0)
223     {
224         log<level::ERR>("Error occured during X509_REQ_set_version call");
225         elog<InternalFailure>();
226     }
227 
228     // set subject of x509 req
229     X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
230 
231     if (!alternativeNames.empty())
232     {
233         for (auto& name : alternativeNames)
234         {
235             addEntry(x509Name, "subjectAltName", name);
236         }
237     }
238     addEntry(x509Name, "challengePassword", challengePassword);
239     addEntry(x509Name, "L", city);
240     addEntry(x509Name, "CN", commonName);
241     addEntry(x509Name, "name", contactPerson);
242     addEntry(x509Name, "C", country);
243     addEntry(x509Name, "emailAddress", email);
244     addEntry(x509Name, "GN", givenName);
245     addEntry(x509Name, "initials", initials);
246     addEntry(x509Name, "algorithm", keyPairAlgorithm);
247     if (!keyUsage.empty())
248     {
249         for (auto& usage : keyUsage)
250         {
251             addEntry(x509Name, "keyUsage", usage);
252         }
253     }
254     addEntry(x509Name, "O", organization);
255     addEntry(x509Name, "ST", state);
256     addEntry(x509Name, "SN", surname);
257     addEntry(x509Name, "unstructuredName", unstructuredName);
258 
259     EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
260 
261     log<level::INFO>("Given Key pair algorithm",
262                      entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
263 
264     // Used EC algorithm as default if user did not give algorithm type.
265     if (keyPairAlgorithm == "RSA")
266         pKey = getRSAKeyPair(keyBitLength);
267     else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
268         pKey = generateECKeyPair(keyCurveId);
269     else
270     {
271         log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
272                         "RSA and EC only");
273         elog<InvalidArgument>(
274             Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
275             Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
276     }
277 
278     ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
279     if (ret == 0)
280     {
281         log<level::ERR>("Error occured while setting Public key");
282         elog<InternalFailure>();
283     }
284 
285     // Write private key to file
286     writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
287 
288     // set sign key of x509 req
289     ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
290     if (ret == 0)
291     {
292         log<level::ERR>("Error occured while signing key of x509");
293         elog<InternalFailure>();
294     }
295 
296     log<level::INFO>("Writing CSR to file");
297     fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
298     writeCSR(csrFilePath.string(), x509Req);
299 }
300 
301 EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
302 {
303     int ret = 0;
304     // generate rsa key
305     BIGNUM_Ptr bne(BN_new(), ::BN_free);
306     ret = BN_set_word(bne.get(), RSA_F4);
307     if (ret == 0)
308     {
309         log<level::ERR>("Error occured during BN_set_word call");
310         elog<InternalFailure>();
311     }
312 
313     int64_t keyBitLen = keyBitLength;
314     // set keybit length to default value if not set
315     if (keyBitLen <= 0)
316     {
317         constexpr auto DEFAULT_KEYBITLENGTH = 2048;
318         log<level::INFO>(
319             "KeyBitLength is not given.Hence, using default KeyBitLength",
320             entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
321         keyBitLen = DEFAULT_KEYBITLENGTH;
322     }
323     RSA* rsa = RSA_new();
324     ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
325     if (ret != 1)
326     {
327         free(rsa);
328         log<level::ERR>("Error occured during RSA_generate_key_ex call",
329                         entry("KEYBITLENGTH=%PRIu64", keyBitLen));
330         elog<InternalFailure>();
331     }
332 
333     // set public key of x509 req
334     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
335     ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
336     if (ret == 0)
337     {
338         free(rsa);
339         log<level::ERR>("Error occured during assign rsa key into EVP");
340         elog<InternalFailure>();
341     }
342 
343     return pKey;
344 }
345 
346 EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
347 {
348     std::string curId(curveId);
349 
350     if (curId.empty())
351     {
352         // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
353         constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
354         log<level::INFO>(
355             "KeyCurveId is not given. Hence using default curve id",
356             entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
357         curId = DEFAULT_KEYCURVEID;
358     }
359 
360     int ecGrp = OBJ_txt2nid(curId.c_str());
361 
362     if (ecGrp == NID_undef)
363     {
364         log<level::ERR>(
365             "Error occured during convert the curve id string format into NID",
366             entry("KEYCURVEID=%s", curId.c_str()));
367         elog<InternalFailure>();
368     }
369 
370     EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
371 
372     if (ecKey == NULL)
373     {
374         log<level::ERR>(
375             "Error occured during create the EC_Key object from NID",
376             entry("ECGROUP=%d", ecGrp));
377         elog<InternalFailure>();
378     }
379 
380     // If you want to save a key and later load it with
381     // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
382     // flag on the key.
383     EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
384 
385     int ret = EC_KEY_generate_key(ecKey);
386 
387     if (ret == 0)
388     {
389         EC_KEY_free(ecKey);
390         log<level::ERR>("Error occured during generate EC key");
391         elog<InternalFailure>();
392     }
393 
394     EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
395     ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
396     if (ret == 0)
397     {
398         EC_KEY_free(ecKey);
399         log<level::ERR>("Error occured during assign EC Key into EVP");
400         elog<InternalFailure>();
401     }
402 
403     return pKey;
404 }
405 
406 void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
407                               const std::string& privKeyFileName)
408 {
409     log<level::INFO>("Writing private key to file");
410     // write private key to file
411     fs::path privKeyPath = certParentInstallPath / privKeyFileName;
412 
413     FILE* fp = std::fopen(privKeyPath.c_str(), "w");
414     if (fp == NULL)
415     {
416         log<level::ERR>("Error occured creating private key file");
417         elog<InternalFailure>();
418     }
419     int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
420     std::fclose(fp);
421     if (ret == 0)
422     {
423         log<level::ERR>("Error occured while writing private key to file");
424         elog<InternalFailure>();
425     }
426 }
427 
428 void Manager::addEntry(X509_NAME* x509Name, const char* field,
429                        const std::string& bytes)
430 {
431     if (bytes.empty())
432     {
433         return;
434     }
435     int ret = X509_NAME_add_entry_by_txt(
436         x509Name, field, MBSTRING_ASC,
437         reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
438     if (ret != 1)
439     {
440         log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
441                         entry("VALUE=%s", bytes.c_str()));
442         elog<InternalFailure>();
443     }
444 }
445 
446 void Manager::createCSRObject(const Status& status)
447 {
448     if (csrPtr)
449     {
450         csrPtr.reset(nullptr);
451     }
452     auto csrObjectPath = objectPath + '/' + "csr";
453     csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
454                                    certInstallPath.c_str(), status);
455 }
456 
457 void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
458 {
459     if (fs::exists(filePath))
460     {
461         log<level::INFO>("Removing the existing file",
462                          entry("FILENAME=%s", filePath.c_str()));
463         if (!fs::remove(filePath.c_str()))
464         {
465             log<level::ERR>("Unable to remove the file",
466                             entry("FILENAME=%s", filePath.c_str()));
467             elog<InternalFailure>();
468         }
469     }
470 
471     FILE* fp = NULL;
472 
473     if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
474     {
475         log<level::ERR>("Error opening the file to write the CSR",
476                         entry("FILENAME=%s", filePath.c_str()));
477         elog<InternalFailure>();
478     }
479 
480     int rc = PEM_write_X509_REQ(fp, x509Req.get());
481     if (!rc)
482     {
483         log<level::ERR>("PEM write routine failed",
484                         entry("FILENAME=%s", filePath.c_str()));
485         std::fclose(fp);
486         elog<InternalFailure>();
487     }
488     std::fclose(fp);
489 }
490 
491 void Manager::createCertificate()
492 {
493     try
494     {
495         // TODO: Issue#3 At present supporting only one certificate to be
496         // uploaded this need to be revisited to support multiple
497         // certificates
498         auto certObjectPath = objectPath + '/' + '1';
499         certificatePtr = std::make_unique<Certificate>(
500             bus, certObjectPath, certType, unitToRestart, certInstallPath,
501             certInstallPath, true, certWatchPtr);
502     }
503     catch (const InternalFailure& e)
504     {
505         report<InternalFailure>();
506     }
507     catch (const InvalidCertificate& e)
508     {
509         report<InvalidCertificate>(
510             Reason("Existing certificate file is corrupted"));
511     }
512 }
513 
514 void Manager::createRSAPrivateKeyFile()
515 {
516     fs::path rsaPrivateKeyFileName =
517         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
518 
519     try
520     {
521         if (!fs::exists(rsaPrivateKeyFileName))
522         {
523             writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
524                             RSA_PRIV_KEY_FILE_NAME);
525         }
526     }
527     catch (const InternalFailure& e)
528     {
529         report<InternalFailure>();
530     }
531 }
532 
533 EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
534 {
535     if (keyBitLength != SUPPORTED_KEYBITLENGTH)
536     {
537         log<level::ERR>(
538             "Given Key bit length is not supported",
539             entry("GIVENKEYBITLENGTH=%d", keyBitLength),
540             entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
541         elog<InvalidArgument>(
542             Argument::ARGUMENT_NAME("KEYBITLENGTH"),
543             Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
544     }
545     fs::path rsaPrivateKeyFileName =
546         certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
547 
548     FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
549     if (!privateKeyFile)
550     {
551         log<level::ERR>("Unable to open RSA private key file to read",
552                         entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
553                         entry("ERRORREASON=%s", strerror(errno)));
554         elog<InternalFailure>();
555     }
556 
557     EVP_PKEY_Ptr privateKey(
558         PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
559         ::EVP_PKEY_free);
560     std::fclose(privateKeyFile);
561 
562     if (!privateKey)
563     {
564         log<level::ERR>("Error occured during PEM_read_PrivateKey call");
565         elog<InternalFailure>();
566     }
567     return privateKey;
568 }
569 } // namespace certs
570 } // namespace phosphor
571