1 #include "config.h"
2
3 #include "certs_manager.hpp"
4
5 #include "x509_utils.hpp"
6
7 #include <openssl/asn1.h>
8 #include <openssl/bn.h>
9 #include <openssl/ec.h>
10 #include <openssl/err.h>
11 #include <openssl/evp.h>
12 #include <openssl/obj_mac.h>
13 #include <openssl/objects.h>
14 #include <openssl/opensslv.h>
15 #include <openssl/pem.h>
16 #include <openssl/rsa.h>
17 #include <unistd.h>
18
19 #include <phosphor-logging/elog-errors.hpp>
20 #include <phosphor-logging/elog.hpp>
21 #include <phosphor-logging/lg2.hpp>
22 #include <sdbusplus/bus.hpp>
23 #include <sdbusplus/exception.hpp>
24 #include <sdbusplus/message.hpp>
25 #include <sdeventplus/source/base.hpp>
26 #include <sdeventplus/source/child.hpp>
27 #include <xyz/openbmc_project/Certs/error.hpp>
28 #include <xyz/openbmc_project/Common/error.hpp>
29
30 #include <algorithm>
31 #include <array>
32 #include <cerrno>
33 #include <chrono>
34 #include <csignal>
35 #include <cstdio>
36 #include <cstdlib>
37 #include <cstring>
38 #include <exception>
39 #include <fstream>
40 #include <utility>
41
42 namespace phosphor::certs
43 {
44 namespace
45 {
46 namespace fs = std::filesystem;
47 using ::phosphor::logging::commit;
48 using ::phosphor::logging::elog;
49 using ::phosphor::logging::report;
50
51 using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
52 using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
53 using ::sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
54 using NotAllowedReason =
55 ::phosphor::logging::xyz::openbmc_project::Common::NotAllowed::REASON;
56 using InvalidCertificateReason = ::phosphor::logging::xyz::openbmc_project::
57 Certs::InvalidCertificate::REASON;
58 using ::sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
59 using Argument =
60 ::phosphor::logging::xyz::openbmc_project::Common::InvalidArgument;
61
62 // RAII support for openSSL functions.
63 using X509ReqPtr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
64 using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
65 using BignumPtr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
66 using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
67
68 constexpr int supportedKeyBitLength = 2048;
69 constexpr int defaultKeyBitLength = 2048;
70 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
71 constexpr auto defaultKeyCurveID = "secp224r1";
72 // PEM certificate block markers, defined in go/rfc/7468.
73 constexpr std::string_view beginCertificate = "-----BEGIN CERTIFICATE-----";
74 constexpr std::string_view endCertificate = "-----END CERTIFICATE-----";
75
76 /**
77 * @brief Splits the given authorities list file and returns an array of
78 * individual PEM encoded x509 certificate.
79 *
80 * @param[in] sourceFilePath - Path to the authorities list file.
81 *
82 * @return An array of individual PEM encoded x509 certificate
83 */
splitCertificates(const std::string & sourceFilePath)84 std::vector<std::string> splitCertificates(const std::string& sourceFilePath)
85 {
86 std::ifstream inputCertFileStream;
87 inputCertFileStream.exceptions(
88 std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit);
89
90 std::stringstream pemStream;
91 std::vector<std::string> certificatesList;
92 try
93 {
94 inputCertFileStream.open(sourceFilePath);
95 pemStream << inputCertFileStream.rdbuf();
96 inputCertFileStream.close();
97 }
98 catch (const std::exception& e)
99 {
100 lg2::error("Failed to read certificates list, ERR:{ERR}, SRC:{SRC}",
101 "ERR", e, "SRC", sourceFilePath);
102 elog<InternalFailure>();
103 }
104 std::string pem = pemStream.str();
105 size_t begin = 0;
106 // |begin| points to the current start position for searching the next
107 // |beginCertificate| block. When we find the beginning of the certificate,
108 // we extract the content between the beginning and the end of the current
109 // certificate. And finally we move |begin| to the end of the current
110 // certificate to start searching the next potential certificate.
111 for (begin = pem.find(beginCertificate, begin); begin != std::string::npos;
112 begin = pem.find(beginCertificate, begin))
113 {
114 size_t end = pem.find(endCertificate, begin);
115 if (end == std::string::npos)
116 {
117 lg2::error(
118 "invalid PEM contains a BEGIN identifier without an END");
119 elog<InvalidCertificate>(InvalidCertificateReason(
120 "invalid PEM contains a BEGIN identifier without an END"));
121 }
122 end += endCertificate.size();
123 certificatesList.emplace_back(pem.substr(begin, end - begin));
124 begin = end;
125 }
126 return certificatesList;
127 }
128
129 } // namespace
130
Manager(sdbusplus::bus_t & bus,sdeventplus::Event & event,const char * path,CertificateType type,const std::string & unit,const std::string & installPath)131 Manager::Manager(sdbusplus::bus_t& bus, sdeventplus::Event& event,
132 const char* path, CertificateType type,
133 const std::string& unit, const std::string& installPath) :
134 internal::ManagerInterface(bus, path), bus(bus), event(event),
135 objectPath(path), certType(type), unitToRestart(std::move(unit)),
136 certInstallPath(std::move(installPath)),
137 certParentInstallPath(fs::path(certInstallPath).parent_path())
138 {
139 try
140 {
141 // Create certificate directory if not existing.
142 // Set correct certificate directory permissions.
143 fs::path certDirectory;
144 try
145 {
146 if (certType == CertificateType::authority)
147 {
148 certDirectory = certInstallPath;
149 }
150 else
151 {
152 certDirectory = certParentInstallPath;
153 }
154
155 if (!fs::exists(certDirectory))
156 {
157 fs::create_directories(certDirectory);
158 }
159
160 auto permission = fs::perms::owner_read | fs::perms::owner_write |
161 fs::perms::owner_exec;
162 fs::permissions(certDirectory, permission,
163 fs::perm_options::replace);
164 storageUpdate();
165 }
166 catch (const fs::filesystem_error& e)
167 {
168 lg2::error(
169 "Failed to create directory, ERR:{ERR}, DIRECTORY:{DIRECTORY}",
170 "ERR", e, "DIRECTORY", certParentInstallPath);
171 report<InternalFailure>();
172 }
173
174 // Generating RSA private key file if certificate type is server/client
175 if (certType != CertificateType::authority)
176 {
177 createRSAPrivateKeyFile();
178 }
179
180 // restore any existing certificates
181 createCertificates();
182
183 // watch is not required for authority certificates
184 if (certType != CertificateType::authority)
185 {
186 // watch for certificate file create/replace
187 certWatchPtr = std::make_unique<
188 Watch>(event, certInstallPath, [this]() {
189 try
190 {
191 // if certificate file existing update it
192 if (!installedCerts.empty())
193 {
194 lg2::info("Inotify callback to update "
195 "certificate properties");
196 installedCerts[0]->populateProperties();
197 }
198 else
199 {
200 lg2::info(
201 "Inotify callback to create certificate object");
202 createCertificates();
203 }
204 }
205 catch (const InternalFailure& e)
206 {
207 commit<InternalFailure>();
208 }
209 catch (const InvalidCertificate& e)
210 {
211 commit<InvalidCertificate>();
212 }
213 });
214 }
215 else
216 {
217 try
218 {
219 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
220 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
221 {
222 lg2::notice(
223 "Legacy certificate detected, will be installed from,"
224 "SINGLE_CERTPATH:{SINGLE_CERTPATH}",
225 "SINGLE_CERTPATH", singleCertPath);
226 install(singleCertPath);
227 if (!fs::remove(singleCertPath))
228 {
229 lg2::error("Unable to remove old certificate from,"
230 "SINGLE_CERTPATH:{SINGLE_CERTPATH}",
231 "SINGLE_CERTPATH", singleCertPath);
232 elog<InternalFailure>();
233 }
234 }
235 }
236 catch (const std::exception& ex)
237 {
238 lg2::error(
239 "Error in restoring legacy certificate, ERROR_STR:{ERROR_STR}",
240 "ERROR_STR", ex);
241 }
242 }
243 }
244 catch (const std::exception& ex)
245 {
246 lg2::error(
247 "Error in certificate manager constructor, ERROR_STR:{ERROR_STR}",
248 "ERROR_STR", ex);
249 }
250 }
251
install(const std::string filePath)252 std::string Manager::install(const std::string filePath)
253 {
254 if (certType != CertificateType::authority && !installedCerts.empty())
255 {
256 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
257 }
258 else if (certType == CertificateType::authority &&
259 installedCerts.size() >= maxNumAuthorityCertificates)
260 {
261 elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
262 }
263
264 std::string certObjectPath;
265 if (isCertificateUnique(filePath))
266 {
267 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
268 installedCerts.emplace_back(std::make_unique<Certificate>(
269 bus, certObjectPath, certType, certInstallPath, filePath,
270 certWatchPtr.get(), *this, /*restore=*/false));
271 reloadOrReset(unitToRestart);
272 certIdCounter++;
273 }
274 else
275 {
276 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
277 }
278
279 return certObjectPath;
280 }
281
282 std::vector<sdbusplus::message::object_path>
installAll(const std::string filePath)283 Manager::installAll(const std::string filePath)
284 {
285 if (certType != CertificateType::authority)
286 {
287 elog<NotAllowed>(NotAllowedReason(
288 "The InstallAll interface is only allowed for "
289 "Authority certificates"));
290 }
291
292 if (!installedCerts.empty())
293 {
294 elog<NotAllowed>(NotAllowedReason(
295 "There are already root certificates; Call DeleteAll then "
296 "InstallAll, or use ReplaceAll"));
297 }
298
299 fs::path sourceFile(filePath);
300 if (!fs::exists(sourceFile))
301 {
302 lg2::error("File is Missing, FILE:{FILE}", "FILE", filePath);
303 elog<InternalFailure>();
304 }
305 std::vector<std::string> authorities = splitCertificates(sourceFile);
306 if (authorities.size() > maxNumAuthorityCertificates)
307 {
308 elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
309 }
310
311 lg2::info("Starts authority list install");
312
313 fs::path authorityStore(certInstallPath);
314 fs::path authoritiesListFile =
315 authorityStore / defaultAuthoritiesListFileName;
316
317 // Atomically install all the certificates
318 fs::path tempPath = Certificate::generateUniqueFilePath(authorityStore);
319 fs::create_directory(tempPath);
320 // Copies the authorities list
321 Certificate::copyCertificate(sourceFile,
322 tempPath / defaultAuthoritiesListFileName);
323 std::vector<std::unique_ptr<Certificate>> tempCertificates;
324 uint64_t tempCertIdCounter = certIdCounter;
325 X509StorePtr x509Store = getX509Store(sourceFile);
326 for (const auto& authority : authorities)
327 {
328 std::string certObjectPath =
329 objectPath + '/' + std::to_string(tempCertIdCounter);
330 tempCertificates.emplace_back(std::make_unique<Certificate>(
331 bus, certObjectPath, certType, tempPath, *x509Store, authority,
332 certWatchPtr.get(), *this, /*restore=*/false));
333 tempCertIdCounter++;
334 }
335
336 // We are good now, issue swap
337 installedCerts = std::move(tempCertificates);
338 certIdCounter = tempCertIdCounter;
339 // Rename all the certificates including the authorities list
340 for (const fs::path& f : fs::directory_iterator(tempPath))
341 {
342 if (fs::is_symlink(f))
343 {
344 continue;
345 }
346 fs::rename(/*from=*/f, /*to=*/certInstallPath / f.filename());
347 }
348 // Update file locations and create symbol links
349 for (const auto& cert : installedCerts)
350 {
351 cert->setCertInstallPath(certInstallPath);
352 cert->setCertFilePath(
353 certInstallPath / fs::path(cert->getCertFilePath()).filename());
354 cert->storageUpdate();
355 }
356 // Remove the temporary folder
357 fs::remove_all(tempPath);
358
359 std::vector<sdbusplus::message::object_path> objects;
360 for (const auto& certificate : installedCerts)
361 {
362 objects.emplace_back(certificate->getObjectPath());
363 }
364
365 lg2::info("Finishes authority list install; reload units starts");
366 reloadOrReset(unitToRestart);
367 return objects;
368 }
369
370 std::vector<sdbusplus::message::object_path>
replaceAll(std::string filePath)371 Manager::replaceAll(std::string filePath)
372 {
373 installedCerts.clear();
374 certIdCounter = 1;
375 storageUpdate();
376 return installAll(std::move(filePath));
377 }
378
deleteAll()379 void Manager::deleteAll()
380 {
381 // TODO: #Issue 4 when a certificate is deleted system auto generates
382 // certificate file. At present we are not supporting creation of
383 // certificate object for the auto-generated certificate file as
384 // deletion if only applicable for REST server and Bmcweb does not allow
385 // deletion of certificates
386 installedCerts.clear();
387 // If the authorities list exists, delete it as well
388 if (certType == CertificateType::authority)
389 {
390 if (fs::path authoritiesList =
391 fs::path(certInstallPath) / defaultAuthoritiesListFileName;
392 fs::exists(authoritiesList))
393 {
394 fs::remove(authoritiesList);
395 }
396 }
397 certIdCounter = 1;
398 storageUpdate();
399 reloadOrReset(unitToRestart);
400 }
401
deleteCertificate(const Certificate * const certificate)402 void Manager::deleteCertificate(const Certificate* const certificate)
403 {
404 const std::vector<std::unique_ptr<Certificate>>::iterator& certIt =
405 std::find_if(installedCerts.begin(), installedCerts.end(),
406 [certificate](const std::unique_ptr<Certificate>& cert) {
407 return (cert.get() == certificate);
408 });
409 if (certIt != installedCerts.end())
410 {
411 installedCerts.erase(certIt);
412 storageUpdate();
413 reloadOrReset(unitToRestart);
414 }
415 else
416 {
417 lg2::error("Certificate does not exist, ID:{ID}", "ID",
418 certificate->getCertId());
419 elog<InternalFailure>();
420 }
421 }
422
replaceCertificate(Certificate * const certificate,const std::string & filePath)423 void Manager::replaceCertificate(Certificate* const certificate,
424 const std::string& filePath)
425 {
426 if (isCertificateUnique(filePath, certificate))
427 {
428 certificate->install(filePath, false);
429 storageUpdate();
430 reloadOrReset(unitToRestart);
431 }
432 else
433 {
434 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
435 }
436 }
437
generateCSR(std::vector<std::string> alternativeNames,std::string challengePassword,std::string city,std::string commonName,std::string contactPerson,std::string country,std::string email,std::string givenName,std::string initials,int64_t keyBitLength,std::string keyCurveId,std::string keyPairAlgorithm,std::vector<std::string> keyUsage,std::string organization,std::string organizationalUnit,std::string state,std::string surname,std::string unstructuredName)438 std::string Manager::generateCSR(
439 std::vector<std::string> alternativeNames, std::string challengePassword,
440 std::string city, std::string commonName, std::string contactPerson,
441 std::string country, std::string email, std::string givenName,
442 std::string initials, int64_t keyBitLength, std::string keyCurveId,
443 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
444 std::string organization, std::string organizationalUnit, std::string state,
445 std::string surname, std::string unstructuredName)
446 {
447 // We support only one CSR.
448 csrPtr.reset(nullptr);
449 auto pid = fork();
450 if (pid == -1)
451 {
452 lg2::error("Error occurred during forking process");
453 report<InternalFailure>();
454 }
455 else if (pid == 0)
456 {
457 try
458 {
459 generateCSRHelper(
460 alternativeNames, challengePassword, city, commonName,
461 contactPerson, country, email, givenName, initials,
462 keyBitLength, keyCurveId, keyPairAlgorithm, keyUsage,
463 organization, organizationalUnit, state, surname,
464 unstructuredName);
465 exit(EXIT_SUCCESS);
466 }
467 catch (const InternalFailure& e)
468 {
469 // commit the error reported in child process and exit
470 // Callback method from SDEvent Loop looks for exit status
471 exit(EXIT_FAILURE);
472 commit<InternalFailure>();
473 }
474 catch (const InvalidArgument& e)
475 {
476 // commit the error reported in child process and exit
477 // Callback method from SDEvent Loop looks for exit status
478 exit(EXIT_FAILURE);
479 commit<InvalidArgument>();
480 }
481 }
482 else
483 {
484 using namespace sdeventplus::source;
485 Child::Callback callback =
486 [this](Child& eventSource, const siginfo_t* si) {
487 eventSource.set_enabled(Enabled::On);
488 if (si->si_status != 0)
489 {
490 this->createCSRObject(Status::failure);
491 }
492 else
493 {
494 this->createCSRObject(Status::success);
495 }
496 };
497 try
498 {
499 sigset_t ss;
500 if (sigemptyset(&ss) < 0)
501 {
502 lg2::error("Unable to initialize signal set");
503 elog<InternalFailure>();
504 }
505 if (sigaddset(&ss, SIGCHLD) < 0)
506 {
507 lg2::error("Unable to add signal to signal set");
508 elog<InternalFailure>();
509 }
510
511 // Block SIGCHLD first, so that the event loop can handle it
512 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
513 {
514 lg2::error("Unable to block signal");
515 elog<InternalFailure>();
516 }
517 if (childPtr)
518 {
519 childPtr.reset();
520 }
521 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
522 std::move(callback));
523 }
524 catch (const InternalFailure& e)
525 {
526 commit<InternalFailure>();
527 }
528 }
529 auto csrObjectPath = objectPath + '/' + "csr";
530 return csrObjectPath;
531 }
532
getCertificates()533 std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
534 {
535 return installedCerts;
536 }
537
generateCSRHelper(std::vector<std::string> alternativeNames,std::string challengePassword,std::string city,std::string commonName,std::string contactPerson,std::string country,std::string email,std::string givenName,std::string initials,int64_t keyBitLength,std::string keyCurveId,std::string keyPairAlgorithm,std::vector<std::string> keyUsage,std::string organization,std::string organizationalUnit,std::string state,std::string surname,std::string unstructuredName)538 void Manager::generateCSRHelper(
539 std::vector<std::string> alternativeNames, std::string challengePassword,
540 std::string city, std::string commonName, std::string contactPerson,
541 std::string country, std::string email, std::string givenName,
542 std::string initials, int64_t keyBitLength, std::string keyCurveId,
543 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
544 std::string organization, std::string organizationalUnit, std::string state,
545 std::string surname, std::string unstructuredName)
546 {
547 int ret = 0;
548
549 X509ReqPtr x509Req(X509_REQ_new(), ::X509_REQ_free);
550
551 // set subject of x509 req
552 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
553
554 if (!alternativeNames.empty())
555 {
556 for (auto& name : alternativeNames)
557 {
558 addEntry(x509Name, "subjectAltName", name);
559 }
560 }
561 addEntry(x509Name, "challengePassword", challengePassword);
562 addEntry(x509Name, "L", city);
563 addEntry(x509Name, "CN", commonName);
564 addEntry(x509Name, "name", contactPerson);
565 addEntry(x509Name, "C", country);
566 addEntry(x509Name, "emailAddress", email);
567 addEntry(x509Name, "GN", givenName);
568 addEntry(x509Name, "initials", initials);
569 addEntry(x509Name, "algorithm", keyPairAlgorithm);
570 if (!keyUsage.empty())
571 {
572 for (auto& usage : keyUsage)
573 {
574 if (isExtendedKeyUsage(usage))
575 {
576 addEntry(x509Name, "extendedKeyUsage", usage);
577 }
578 else
579 {
580 addEntry(x509Name, "keyUsage", usage);
581 }
582 }
583 }
584 addEntry(x509Name, "O", organization);
585 addEntry(x509Name, "OU", organizationalUnit);
586 addEntry(x509Name, "ST", state);
587 addEntry(x509Name, "SN", surname);
588 addEntry(x509Name, "unstructuredName", unstructuredName);
589
590 EVPPkeyPtr pKey(nullptr, ::EVP_PKEY_free);
591
592 lg2::info("Given Key pair algorithm, KEYPAIRALGORITHM:{KEYPAIRALGORITHM}",
593 "KEYPAIRALGORITHM", keyPairAlgorithm);
594
595 // Used EC algorithm as default if user did not give algorithm type.
596 if (keyPairAlgorithm == "RSA")
597 pKey = getRSAKeyPair(keyBitLength);
598 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
599 pKey = generateECKeyPair(keyCurveId);
600 else
601 {
602 lg2::error("Given Key pair algorithm is not supported. Supporting "
603 "RSA and EC only");
604 elog<InvalidArgument>(
605 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
606 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
607 }
608
609 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
610 if (ret == 0)
611 {
612 lg2::error("Error occurred while setting Public key");
613 ERR_print_errors_fp(stderr);
614 elog<InternalFailure>();
615 }
616
617 // Write private key to file
618 writePrivateKey(pKey, defaultPrivateKeyFileName);
619
620 // set sign key of x509 req
621 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
622 if (ret == 0)
623 {
624 lg2::error("Error occurred while signing key of x509");
625 ERR_print_errors_fp(stderr);
626 elog<InternalFailure>();
627 }
628
629 lg2::info("Writing CSR to file");
630 fs::path csrFilePath = certParentInstallPath / defaultCSRFileName;
631 writeCSR(csrFilePath.string(), x509Req);
632 }
633
isExtendedKeyUsage(const std::string & usage)634 bool Manager::isExtendedKeyUsage(const std::string& usage)
635 {
636 const static std::array<const char*, 6> usageList = {
637 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
638 "Timestamping", "CodeSigning", "EmailProtection"};
639 auto it = std::find_if(
640 usageList.begin(), usageList.end(),
641 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
642 return it != usageList.end();
643 }
generateRSAKeyPair(const int64_t keyBitLength)644 EVPPkeyPtr Manager::generateRSAKeyPair(const int64_t keyBitLength)
645 {
646 int64_t keyBitLen = keyBitLength;
647 // set keybit length to default value if not set
648 if (keyBitLen <= 0)
649 {
650 lg2::info("KeyBitLength is not given.Hence, using default KeyBitLength:"
651 "{DEFAULTKEYBITLENGTH}",
652 "DEFAULTKEYBITLENGTH", defaultKeyBitLength);
653 keyBitLen = defaultKeyBitLength;
654 }
655
656 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
657
658 // generate rsa key
659 BignumPtr bne(BN_new(), ::BN_free);
660 auto ret = BN_set_word(bne.get(), RSA_F4);
661 if (ret == 0)
662 {
663 lg2::error("Error occurred during BN_set_word call");
664 ERR_print_errors_fp(stderr);
665 elog<InternalFailure>();
666 }
667 using RSAPtr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
668 RSAPtr rsa(RSA_new(), ::RSA_free);
669 ret = RSA_generate_key_ex(rsa.get(), keyBitLen, bne.get(), nullptr);
670 if (ret != 1)
671 {
672 lg2::error(
673 "Error occurred during RSA_generate_key_ex call: {KEYBITLENGTH}",
674 "KEYBITLENGTH", keyBitLen);
675 ERR_print_errors_fp(stderr);
676 elog<InternalFailure>();
677 }
678
679 // set public key of x509 req
680 EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
681 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa.get());
682 if (ret == 0)
683 {
684 lg2::error("Error occurred during assign rsa key into EVP");
685 ERR_print_errors_fp(stderr);
686 elog<InternalFailure>();
687 }
688 // Now |rsa| is managed by |pKey|
689 rsa.release();
690 return pKey;
691
692 #else
693 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
694 EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
695 if (!ctx)
696 {
697 lg2::error("Error occurred creating EVP_PKEY_CTX from algorithm");
698 ERR_print_errors_fp(stderr);
699 elog<InternalFailure>();
700 }
701
702 if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
703 (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
704
705 {
706 lg2::error("Error occurred initializing keygen context");
707 ERR_print_errors_fp(stderr);
708 elog<InternalFailure>();
709 }
710
711 EVP_PKEY* pKey = nullptr;
712 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
713 {
714 lg2::error("Error occurred during generate EC key");
715 ERR_print_errors_fp(stderr);
716 elog<InternalFailure>();
717 }
718
719 return {pKey, &::EVP_PKEY_free};
720 #endif
721 }
722
generateECKeyPair(const std::string & curveId)723 EVPPkeyPtr Manager::generateECKeyPair(const std::string& curveId)
724 {
725 std::string curId(curveId);
726
727 if (curId.empty())
728 {
729 lg2::info("KeyCurveId is not given. Hence using default curve id,"
730 "DEFAULTKEYCURVEID:{DEFAULTKEYCURVEID}",
731 "DEFAULTKEYCURVEID", defaultKeyCurveID);
732 curId = defaultKeyCurveID;
733 }
734
735 int ecGrp = OBJ_txt2nid(curId.c_str());
736 if (ecGrp == NID_undef)
737 {
738 lg2::error(
739 "Error occurred during convert the curve id string format into NID,"
740 "KEYCURVEID:{KEYCURVEID}",
741 "KEYCURVEID", curId);
742 elog<InternalFailure>();
743 }
744
745 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
746
747 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
748
749 if (ecKey == nullptr)
750 {
751 lg2::error(
752 "Error occurred during create the EC_Key object from NID, ECGROUP:{ECGROUP}",
753 "ECGROUP", ecGrp);
754 ERR_print_errors_fp(stderr);
755 elog<InternalFailure>();
756 }
757
758 // If you want to save a key and later load it with
759 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
760 // flag on the key.
761 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
762
763 int ret = EC_KEY_generate_key(ecKey);
764
765 if (ret == 0)
766 {
767 EC_KEY_free(ecKey);
768 lg2::error("Error occurred during generate EC key");
769 ERR_print_errors_fp(stderr);
770 elog<InternalFailure>();
771 }
772
773 EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
774 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
775 if (ret == 0)
776 {
777 EC_KEY_free(ecKey);
778 lg2::error("Error occurred during assign EC Key into EVP");
779 ERR_print_errors_fp(stderr);
780 elog<InternalFailure>();
781 }
782
783 return pKey;
784
785 #else
786 auto holderOfKey = [](EVP_PKEY* key) {
787 return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
788 key, &::EVP_PKEY_free};
789 };
790
791 // Create context to set up curve parameters.
792 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
793 EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
794 if (!ctx)
795 {
796 lg2::error("Error occurred creating EVP_PKEY_CTX for params");
797 ERR_print_errors_fp(stderr);
798 elog<InternalFailure>();
799 }
800
801 // Set up curve parameters.
802 EVP_PKEY* params = nullptr;
803
804 if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
805 (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
806 0) ||
807 (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
808 (EVP_PKEY_paramgen(ctx.get(), ¶ms) <= 0))
809 {
810 lg2::error("Error occurred setting curve parameters");
811 ERR_print_errors_fp(stderr);
812 elog<InternalFailure>();
813 }
814
815 // Move parameters to RAII holder.
816 auto pparms = holderOfKey(params);
817
818 // Create new context for key.
819 ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
820
821 if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
822 {
823 lg2::error("Error occurred initializing keygen context");
824 ERR_print_errors_fp(stderr);
825 elog<InternalFailure>();
826 }
827
828 EVP_PKEY* pKey = nullptr;
829 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
830 {
831 lg2::error("Error occurred during generate EC key");
832 ERR_print_errors_fp(stderr);
833 elog<InternalFailure>();
834 }
835
836 return holderOfKey(pKey);
837 #endif
838 }
839
writePrivateKey(const EVPPkeyPtr & pKey,const std::string & privKeyFileName)840 void Manager::writePrivateKey(const EVPPkeyPtr& pKey,
841 const std::string& privKeyFileName)
842 {
843 lg2::info("Writing private key to file");
844 // write private key to file
845 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
846
847 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
848 if (fp == nullptr)
849 {
850 lg2::error("Error occurred creating private key file");
851 elog<InternalFailure>();
852 }
853 int ret =
854 PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0, nullptr);
855 std::fclose(fp);
856 if (ret == 0)
857 {
858 lg2::error("Error occurred while writing private key to file");
859 elog<InternalFailure>();
860 }
861 }
862
addEntry(X509_NAME * x509Name,const char * field,const std::string & bytes)863 void Manager::addEntry(X509_NAME* x509Name, const char* field,
864 const std::string& bytes)
865 {
866 if (bytes.empty())
867 {
868 return;
869 }
870 int ret = X509_NAME_add_entry_by_txt(
871 x509Name, field, MBSTRING_ASC,
872 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
873 if (ret != 1)
874 {
875 lg2::error("Unable to set entry, FIELD:{FIELD}, VALUE:{VALUE}", "FIELD",
876 field, "VALUE", bytes);
877 ERR_print_errors_fp(stderr);
878 elog<InternalFailure>();
879 }
880 }
881
createCSRObject(const Status & status)882 void Manager::createCSRObject(const Status& status)
883 {
884 if (csrPtr)
885 {
886 csrPtr.reset(nullptr);
887 }
888 auto csrObjectPath = objectPath + '/' + "csr";
889 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
890 certInstallPath.c_str(), status);
891 }
892
writeCSR(const std::string & filePath,const X509ReqPtr & x509Req)893 void Manager::writeCSR(const std::string& filePath, const X509ReqPtr& x509Req)
894 {
895 if (fs::exists(filePath))
896 {
897 lg2::info("Removing the existing file, FILENAME:{FILENAME}", "FILENAME",
898 filePath);
899 if (!fs::remove(filePath.c_str()))
900 {
901 lg2::error("Unable to remove the file, FILENAME:{FILENAME}",
902 "FILENAME", filePath);
903 elog<InternalFailure>();
904 }
905 }
906
907 FILE* fp = nullptr;
908
909 if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
910 {
911 lg2::error(
912 "Error opening the file to write the CSR, FILENAME:{FILENAME}",
913 "FILENAME", filePath);
914 elog<InternalFailure>();
915 }
916
917 int rc = PEM_write_X509_REQ(fp, x509Req.get());
918 if (!rc)
919 {
920 lg2::error("PEM write routine failed, FILENAME:{FILENAME}", "FILENAME",
921 filePath);
922 std::fclose(fp);
923 elog<InternalFailure>();
924 }
925 std::fclose(fp);
926 }
927
createCertificates()928 void Manager::createCertificates()
929 {
930 auto certObjectPath = objectPath + '/';
931
932 if (certType == CertificateType::authority)
933 {
934 // Check whether install path is a directory.
935 if (!fs::is_directory(certInstallPath))
936 {
937 lg2::error("Certificate installation path exists and it is "
938 "not a directory");
939 elog<InternalFailure>();
940 }
941
942 // If the authorities list exists, recover from it and return
943 if (fs::path authoritiesListFilePath =
944 fs::path(certInstallPath) / defaultAuthoritiesListFileName;
945 fs::exists(authoritiesListFilePath))
946 {
947 // remove all other files and directories
948 for (auto& path : fs::directory_iterator(certInstallPath))
949 {
950 if (path.path() != authoritiesListFilePath)
951 {
952 fs::remove_all(path);
953 }
954 }
955 installAll(authoritiesListFilePath);
956 return;
957 }
958
959 for (auto& path : fs::directory_iterator(certInstallPath))
960 {
961 try
962 {
963 // Assume here any regular file located in certificate directory
964 // contains certificates body. Do not want to use soft links
965 // would add value.
966 if (fs::is_regular_file(path))
967 {
968 installedCerts.emplace_back(std::make_unique<Certificate>(
969 bus, certObjectPath + std::to_string(certIdCounter++),
970 certType, certInstallPath, path.path(),
971 certWatchPtr.get(), *this, /*restore=*/true));
972 }
973 }
974 catch (const InternalFailure& e)
975 {
976 report<InternalFailure>();
977 }
978 catch (const InvalidCertificate& e)
979 {
980 report<InvalidCertificate>(InvalidCertificateReason(
981 "Existing certificate file is corrupted"));
982 }
983 }
984 }
985 else if (fs::exists(certInstallPath))
986 {
987 try
988 {
989 installedCerts.emplace_back(std::make_unique<Certificate>(
990 bus, certObjectPath + '1', certType, certInstallPath,
991 certInstallPath, certWatchPtr.get(), *this, /*restore=*/false));
992 }
993 catch (const InternalFailure& e)
994 {
995 report<InternalFailure>();
996 }
997 catch (const InvalidCertificate& e)
998 {
999 report<InvalidCertificate>(InvalidCertificateReason(
1000 "Existing certificate file is corrupted"));
1001 }
1002 }
1003 }
1004
createRSAPrivateKeyFile()1005 void Manager::createRSAPrivateKeyFile()
1006 {
1007 fs::path rsaPrivateKeyFileName =
1008 certParentInstallPath / defaultRSAPrivateKeyFileName;
1009
1010 try
1011 {
1012 if (!fs::exists(rsaPrivateKeyFileName))
1013 {
1014 writePrivateKey(generateRSAKeyPair(supportedKeyBitLength),
1015 defaultRSAPrivateKeyFileName);
1016 }
1017 }
1018 catch (const InternalFailure& e)
1019 {
1020 report<InternalFailure>();
1021 }
1022 }
1023
getRSAKeyPair(const int64_t keyBitLength)1024 EVPPkeyPtr Manager::getRSAKeyPair(const int64_t keyBitLength)
1025 {
1026 if (keyBitLength != supportedKeyBitLength)
1027 {
1028 lg2::error(
1029 "Given Key bit length is not supported, GIVENKEYBITLENGTH:"
1030 "{GIVENKEYBITLENGTH}, SUPPORTEDKEYBITLENGTH:{SUPPORTEDKEYBITLENGTH}",
1031 "GIVENKEYBITLENGTH", keyBitLength, "SUPPORTEDKEYBITLENGTH",
1032 supportedKeyBitLength);
1033 elog<InvalidArgument>(
1034 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
1035 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
1036 }
1037 fs::path rsaPrivateKeyFileName =
1038 certParentInstallPath / defaultRSAPrivateKeyFileName;
1039
1040 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
1041 if (!privateKeyFile)
1042 {
1043 lg2::error(
1044 "Unable to open RSA private key file to read, RSAKEYFILE:{RSAKEYFILE},"
1045 "ERRORREASON:{ERRORREASON}",
1046 "RSAKEYFILE", rsaPrivateKeyFileName, "ERRORREASON",
1047 strerror(errno));
1048 elog<InternalFailure>();
1049 }
1050
1051 EVPPkeyPtr privateKey(
1052 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
1053 ::EVP_PKEY_free);
1054 std::fclose(privateKeyFile);
1055
1056 if (!privateKey)
1057 {
1058 lg2::error("Error occurred during PEM_read_PrivateKey call");
1059 elog<InternalFailure>();
1060 }
1061 return privateKey;
1062 }
1063
storageUpdate()1064 void Manager::storageUpdate()
1065 {
1066 if (certType == CertificateType::authority)
1067 {
1068 // Remove symbolic links in the certificate directory
1069 for (auto& certPath : fs::directory_iterator(certInstallPath))
1070 {
1071 try
1072 {
1073 if (fs::is_symlink(certPath))
1074 {
1075 fs::remove(certPath);
1076 }
1077 }
1078 catch (const std::exception& e)
1079 {
1080 lg2::error(
1081 "Failed to remove symlink for certificate, ERR:{ERR} SYMLINK:{SYMLINK}",
1082 "ERR", e, "SYMLINK", certPath.path().string());
1083 elog<InternalFailure>();
1084 }
1085 }
1086 }
1087
1088 for (const auto& cert : installedCerts)
1089 {
1090 cert->storageUpdate();
1091 }
1092 }
1093
reloadOrReset(const std::string & unit)1094 void Manager::reloadOrReset(const std::string& unit)
1095 {
1096 if (!unit.empty())
1097 {
1098 try
1099 {
1100 constexpr auto defaultSystemdService = "org.freedesktop.systemd1";
1101 constexpr auto defaultSystemdObjectPath =
1102 "/org/freedesktop/systemd1";
1103 constexpr auto defaultSystemdInterface =
1104 "org.freedesktop.systemd1.Manager";
1105 auto method = bus.new_method_call(
1106 defaultSystemdService, defaultSystemdObjectPath,
1107 defaultSystemdInterface, "ReloadOrRestartUnit");
1108 method.append(unit, "replace");
1109 bus.call_noreply(method);
1110 }
1111 catch (const sdbusplus::exception_t& e)
1112 {
1113 lg2::error(
1114 "Failed to reload or restart service, ERR:{ERR}, UNIT:{UNIT}",
1115 "ERR", e, "UNIT", unit);
1116 elog<InternalFailure>();
1117 }
1118 }
1119 }
1120
isCertificateUnique(const std::string & filePath,const Certificate * const certToDrop)1121 bool Manager::isCertificateUnique(const std::string& filePath,
1122 const Certificate* const certToDrop)
1123 {
1124 if (std::any_of(
1125 installedCerts.begin(), installedCerts.end(),
1126 [&filePath, certToDrop](const std::unique_ptr<Certificate>& cert) {
1127 return cert.get() != certToDrop && cert->isSame(filePath);
1128 }))
1129 {
1130 return false;
1131 }
1132 else
1133 {
1134 return true;
1135 }
1136 }
1137
1138 } // namespace phosphor::certs
1139