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