blob: 6e310914c1d5bb242e6b2206c19fbd83a1d52e61 [file] [log] [blame]
Nan Zhou014be0b2021-12-28 18:00:14 -08001#include "config.h"
2
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05003#include "certs_manager.hpp"
4
Nan Zhou6ec13c82021-12-30 11:34:50 -08005#include "x509_utils.hpp"
6
Nan Zhou014be0b2021-12-28 18:00:14 -08007#include <openssl/asn1.h>
8#include <openssl/bn.h>
9#include <openssl/ec.h>
Patrick Williams26fb83e2021-12-14 14:08:28 -060010#include <openssl/evp.h>
Nan Zhou014be0b2021-12-28 18:00:14 -080011#include <openssl/obj_mac.h>
12#include <openssl/objects.h>
13#include <openssl/opensslv.h>
Marri Devender Raof4682712019-03-19 05:00:28 -050014#include <openssl/pem.h>
Nan Zhou014be0b2021-12-28 18:00:14 -080015#include <openssl/rsa.h>
Marri Devender Raof4682712019-03-19 05:00:28 -050016#include <unistd.h>
17
Patrick Williams223e4602023-05-10 07:51:11 -050018#include <phosphor-logging/elog-errors.hpp>
19#include <phosphor-logging/elog.hpp>
20#include <phosphor-logging/log.hpp>
21#include <sdbusplus/bus.hpp>
22#include <sdbusplus/exception.hpp>
23#include <sdbusplus/message.hpp>
24#include <sdeventplus/source/base.hpp>
25#include <sdeventplus/source/child.hpp>
26#include <xyz/openbmc_project/Certs/error.hpp>
27#include <xyz/openbmc_project/Common/error.hpp>
28
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +020029#include <algorithm>
Nan Zhou014be0b2021-12-28 18:00:14 -080030#include <array>
31#include <cerrno>
32#include <chrono>
33#include <csignal>
34#include <cstdio>
35#include <cstdlib>
36#include <cstring>
37#include <exception>
Nan Zhou6ec13c82021-12-30 11:34:50 -080038#include <fstream>
Nan Zhou014be0b2021-12-28 18:00:14 -080039#include <utility>
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010040
Nan Zhoue1289ad2021-12-28 11:02:56 -080041namespace phosphor::certs
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050042{
Nan Zhoucf06ccd2021-12-28 16:25:45 -080043namespace
44{
45namespace fs = std::filesystem;
46using ::phosphor::logging::commit;
47using ::phosphor::logging::elog;
48using ::phosphor::logging::entry;
49using ::phosphor::logging::level;
50using ::phosphor::logging::log;
51using ::phosphor::logging::report;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050052
Nan Zhoucf06ccd2021-12-28 16:25:45 -080053using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
54using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
55using ::sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
56using NotAllowedReason =
57 ::phosphor::logging::xyz::openbmc_project::Common::NotAllowed::REASON;
58using InvalidCertificateReason = ::phosphor::logging::xyz::openbmc_project::
59 Certs::InvalidCertificate::REASON;
60using ::sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
61using Argument =
62 ::phosphor::logging::xyz::openbmc_project::Common::InvalidArgument;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050063
Nan Zhoucf06ccd2021-12-28 16:25:45 -080064// RAII support for openSSL functions.
65using X509ReqPtr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
66using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
67using BignumPtr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
Nan Zhou6ec13c82021-12-30 11:34:50 -080068using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080069
70constexpr int supportedKeyBitLength = 2048;
71constexpr int defaultKeyBitLength = 2048;
72// secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
73constexpr auto defaultKeyCurveID = "secp224r1";
Nan Zhou6ec13c82021-12-30 11:34:50 -080074// PEM certificate block markers, defined in go/rfc/7468.
75constexpr std::string_view beginCertificate = "-----BEGIN CERTIFICATE-----";
76constexpr std::string_view endCertificate = "-----END CERTIFICATE-----";
77
78/**
79 * @brief Splits the given authorities list file and returns an array of
80 * individual PEM encoded x509 certificate.
81 *
82 * @param[in] sourceFilePath - Path to the authorities list file.
83 *
84 * @return An array of individual PEM encoded x509 certificate
85 */
86std::vector<std::string> splitCertificates(const std::string& sourceFilePath)
87{
88 std::ifstream inputCertFileStream;
89 inputCertFileStream.exceptions(
90 std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit);
91
92 std::stringstream pemStream;
93 std::vector<std::string> certificatesList;
94 try
95 {
96 inputCertFileStream.open(sourceFilePath);
97 pemStream << inputCertFileStream.rdbuf();
98 inputCertFileStream.close();
99 }
100 catch (const std::exception& e)
101 {
102 log<level::ERR>("Failed to read certificates list",
103 entry("ERR=%s", e.what()),
104 entry("SRC=%s", sourceFilePath.c_str()));
105 elog<InternalFailure>();
106 }
107 std::string pem = pemStream.str();
108 size_t begin = 0;
109 // |begin| points to the current start position for searching the next
110 // |beginCertificate| block. When we find the beginning of the certificate,
111 // we extract the content between the beginning and the end of the current
112 // certificate. And finally we move |begin| to the end of the current
113 // certificate to start searching the next potential certificate.
114 for (begin = pem.find(beginCertificate, begin); begin != std::string::npos;
115 begin = pem.find(beginCertificate, begin))
116 {
117 size_t end = pem.find(endCertificate, begin);
118 if (end == std::string::npos)
119 {
120 log<level::ERR>(
121 "invalid PEM contains a BEGIN identifier without an END");
122 elog<InvalidCertificate>(InvalidCertificateReason(
123 "invalid PEM contains a BEGIN identifier without an END"));
124 }
125 end += endCertificate.size();
126 certificatesList.emplace_back(pem.substr(begin, end - begin));
127 begin = end;
128 }
129 return certificatesList;
130}
131
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800132} // namespace
Marri Devender Raof4682712019-03-19 05:00:28 -0500133
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500134Manager::Manager(sdbusplus::bus_t& bus, sdeventplus::Event& event,
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800135 const char* path, CertificateType type,
136 const std::string& unit, const std::string& installPath) :
137 internal::ManagerInterface(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -0500138 bus(bus), event(event), objectPath(path), certType(type),
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500139 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
140 certParentInstallPath(fs::path(certInstallPath).parent_path())
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500141{
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500142 try
143 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500144 // Create certificate directory if not existing.
Nan Zhoubf3cf752021-12-28 11:02:07 -0800145 // Set correct certificate directory permissions.
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500146 fs::path certDirectory;
147 try
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500148 {
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000149 if (certType == CertificateType::authority)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500150 {
151 certDirectory = certInstallPath;
152 }
153 else
154 {
155 certDirectory = certParentInstallPath;
156 }
157
158 if (!fs::exists(certDirectory))
159 {
160 fs::create_directories(certDirectory);
161 }
162
163 auto permission = fs::perms::owner_read | fs::perms::owner_write |
164 fs::perms::owner_exec;
165 fs::permissions(certDirectory, permission,
166 fs::perm_options::replace);
167 storageUpdate();
168 }
Patrick Williams71957992021-10-06 14:42:52 -0500169 catch (const fs::filesystem_error& e)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500170 {
171 log<level::ERR>(
172 "Failed to create directory", entry("ERR=%s", e.what()),
173 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
174 report<InternalFailure>();
175 }
176
177 // Generating RSA private key file if certificate type is server/client
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000178 if (certType != CertificateType::authority)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500179 {
180 createRSAPrivateKeyFile();
181 }
182
183 // restore any existing certificates
184 createCertificates();
185
186 // watch is not required for authority certificates
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000187 if (certType != CertificateType::authority)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500188 {
189 // watch for certificate file create/replace
Patrick Williams223e4602023-05-10 07:51:11 -0500190 certWatchPtr = std::make_unique<Watch>(event, certInstallPath,
191 [this]() {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500192 try
193 {
194 // if certificate file existing update it
195 if (!installedCerts.empty())
196 {
197 log<level::INFO>("Inotify callback to update "
198 "certificate properties");
199 installedCerts[0]->populateProperties();
200 }
201 else
202 {
203 log<level::INFO>(
204 "Inotify callback to create certificate object");
205 createCertificates();
206 }
207 }
208 catch (const InternalFailure& e)
209 {
210 commit<InternalFailure>();
211 }
212 catch (const InvalidCertificate& e)
213 {
214 commit<InvalidCertificate>();
215 }
216 });
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500217 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100218 else
219 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500220 try
221 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500222 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
223 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500224 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500225 log<level::NOTICE>(
226 "Legacy certificate detected, will be installed from: ",
227 entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
228 install(singleCertPath);
229 if (!fs::remove(singleCertPath))
230 {
231 log<level::ERR>(
232 "Unable to remove old certificate from: ",
233 entry("SINGLE_CERTPATH=%s",
234 singleCertPath.c_str()));
235 elog<InternalFailure>();
236 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500237 }
238 }
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500239 catch (const std::exception& ex)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500240 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500241 log<level::ERR>("Error in restoring legacy certificate",
242 entry("ERROR_STR=%s", ex.what()));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200243 }
244 }
245 }
Patrick Williams71957992021-10-06 14:42:52 -0500246 catch (const std::exception& ex)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500247 {
248 log<level::ERR>("Error in certificate manager constructor",
249 entry("ERROR_STR=%s", ex.what()));
250 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500251}
252
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200253std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500254{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000255 if (certType != CertificateType::authority && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600256 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800257 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
Marri Devender Rao13965112019-02-27 08:47:12 -0600258 }
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000259 else if (certType == CertificateType::authority &&
Nan Zhou718eef32021-12-28 11:03:30 -0800260 installedCerts.size() >= maxNumAuthorityCertificates)
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200261 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800262 elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200263 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500264
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100265 std::string certObjectPath;
266 if (isCertificateUnique(filePath))
267 {
268 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
269 installedCerts.emplace_back(std::make_unique<Certificate>(
270 bus, certObjectPath, certType, certInstallPath, filePath,
Willy Tu698a5742022-09-23 21:33:01 +0000271 certWatchPtr.get(), *this, /*restore=*/false));
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100272 reloadOrReset(unitToRestart);
273 certIdCounter++;
274 }
275 else
276 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800277 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100278 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200279
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200280 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500281}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500282
Nan Zhou6ec13c82021-12-30 11:34:50 -0800283std::vector<sdbusplus::message::object_path>
284 Manager::installAll(const std::string filePath)
285{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000286 if (certType != CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800287 {
288 elog<NotAllowed>(
289 NotAllowedReason("The InstallAll interface is only allowed for "
290 "Authority certificates"));
291 }
292
293 if (!installedCerts.empty())
294 {
295 elog<NotAllowed>(NotAllowedReason(
296 "There are already root certificates; Call DeleteAll then "
297 "InstallAll, or use ReplaceAll"));
298 }
299
300 fs::path sourceFile(filePath);
301 if (!fs::exists(sourceFile))
302 {
303 log<level::ERR>("File is Missing", entry("FILE=%s", filePath.c_str()));
304 elog<InternalFailure>();
305 }
306 std::vector<std::string> authorities = splitCertificates(sourceFile);
307 if (authorities.size() > maxNumAuthorityCertificates)
308 {
309 elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
310 }
311
Nan Zhou78357b02022-06-09 22:33:35 +0000312 log<level::INFO>("Starts authority list install");
313
Nan Zhou6ec13c82021-12-30 11:34:50 -0800314 fs::path authorityStore(certInstallPath);
Patrick Williams223e4602023-05-10 07:51:11 -0500315 fs::path authoritiesListFile = authorityStore /
316 defaultAuthoritiesListFileName;
Nan Zhou6ec13c82021-12-30 11:34:50 -0800317
318 // Atomically install all the certificates
319 fs::path tempPath = Certificate::generateUniqueFilePath(authorityStore);
320 fs::create_directory(tempPath);
321 // Copies the authorities list
322 Certificate::copyCertificate(sourceFile,
323 tempPath / defaultAuthoritiesListFileName);
324 std::vector<std::unique_ptr<Certificate>> tempCertificates;
325 uint64_t tempCertIdCounter = certIdCounter;
326 X509StorePtr x509Store = getX509Store(sourceFile);
327 for (const auto& authority : authorities)
328 {
Patrick Williams223e4602023-05-10 07:51:11 -0500329 std::string certObjectPath = objectPath + '/' +
330 std::to_string(tempCertIdCounter);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800331 tempCertificates.emplace_back(std::make_unique<Certificate>(
332 bus, certObjectPath, certType, tempPath, *x509Store, authority,
Willy Tu698a5742022-09-23 21:33:01 +0000333 certWatchPtr.get(), *this, /*restore=*/false));
Nan Zhou6ec13c82021-12-30 11:34:50 -0800334 tempCertIdCounter++;
335 }
336
337 // We are good now, issue swap
338 installedCerts = std::move(tempCertificates);
339 certIdCounter = tempCertIdCounter;
340 // Rename all the certificates including the authorities list
341 for (const fs::path& f : fs::directory_iterator(tempPath))
342 {
343 if (fs::is_symlink(f))
344 {
345 continue;
346 }
347 fs::rename(/*from=*/f, /*to=*/certInstallPath / f.filename());
348 }
349 // Update file locations and create symbol links
350 for (const auto& cert : installedCerts)
351 {
352 cert->setCertInstallPath(certInstallPath);
353 cert->setCertFilePath(certInstallPath /
354 fs::path(cert->getCertFilePath()).filename());
355 cert->storageUpdate();
356 }
357 // Remove the temporary folder
358 fs::remove_all(tempPath);
359
360 std::vector<sdbusplus::message::object_path> objects;
361 for (const auto& certificate : installedCerts)
362 {
363 objects.emplace_back(certificate->getObjectPath());
364 }
365
Nan Zhou78357b02022-06-09 22:33:35 +0000366 log<level::INFO>("Finishes authority list install; reload units starts");
Nan Zhou6ec13c82021-12-30 11:34:50 -0800367 reloadOrReset(unitToRestart);
368 return objects;
369}
370
371std::vector<sdbusplus::message::object_path>
372 Manager::replaceAll(std::string filePath)
373{
374 installedCerts.clear();
375 certIdCounter = 1;
376 storageUpdate();
377 return installAll(std::move(filePath));
378}
379
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200380void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500381{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600382 // TODO: #Issue 4 when a certificate is deleted system auto generates
383 // certificate file. At present we are not supporting creation of
384 // certificate object for the auto-generated certificate file as
385 // deletion if only applicable for REST server and Bmcweb does not allow
386 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200387 installedCerts.clear();
Nan Zhou6ec13c82021-12-30 11:34:50 -0800388 // If the authorities list exists, delete it as well
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000389 if (certType == CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800390 {
Patrick Williams223e4602023-05-10 07:51:11 -0500391 if (fs::path authoritiesList = fs::path(certInstallPath) /
392 defaultAuthoritiesListFileName;
Nan Zhou6ec13c82021-12-30 11:34:50 -0800393 fs::exists(authoritiesList))
394 {
395 fs::remove(authoritiesList);
396 }
397 }
398 certIdCounter = 1;
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100399 storageUpdate();
400 reloadOrReset(unitToRestart);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500401}
Marri Devender Raof4682712019-03-19 05:00:28 -0500402
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100403void Manager::deleteCertificate(const Certificate* const certificate)
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200404{
Patrick Williams223e4602023-05-10 07:51:11 -0500405 const std::vector<std::unique_ptr<Certificate>>::iterator& certIt =
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200406 std::find_if(installedCerts.begin(), installedCerts.end(),
Patrick Williams223e4602023-05-10 07:51:11 -0500407 [certificate](const std::unique_ptr<Certificate>& cert) {
408 return (cert.get() == certificate);
409 });
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200410 if (certIt != installedCerts.end())
411 {
412 installedCerts.erase(certIt);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100413 storageUpdate();
414 reloadOrReset(unitToRestart);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200415 }
416 else
417 {
418 log<level::ERR>("Certificate does not exist",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100419 entry("ID=%s", certificate->getCertId().c_str()));
420 elog<InternalFailure>();
421 }
422}
423
424void Manager::replaceCertificate(Certificate* const certificate,
425 const std::string& filePath)
426{
427 if (isCertificateUnique(filePath, certificate))
428 {
Willy Tu698a5742022-09-23 21:33:01 +0000429 certificate->install(filePath, false);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100430 storageUpdate();
431 reloadOrReset(unitToRestart);
432 }
433 else
434 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800435 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200436 }
437}
438
Marri Devender Raof4682712019-03-19 05:00:28 -0500439std::string Manager::generateCSR(
440 std::vector<std::string> alternativeNames, std::string challengePassword,
441 std::string city, std::string commonName, std::string contactPerson,
442 std::string country, std::string email, std::string givenName,
443 std::string initials, int64_t keyBitLength, std::string keyCurveId,
444 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
445 std::string organization, std::string organizationalUnit, std::string state,
446 std::string surname, std::string unstructuredName)
447{
448 // We support only one CSR.
449 csrPtr.reset(nullptr);
450 auto pid = fork();
451 if (pid == -1)
452 {
453 log<level::ERR>("Error occurred during forking process");
454 report<InternalFailure>();
455 }
456 else if (pid == 0)
457 {
458 try
459 {
460 generateCSRHelper(alternativeNames, challengePassword, city,
461 commonName, contactPerson, country, email,
462 givenName, initials, keyBitLength, keyCurveId,
463 keyPairAlgorithm, keyUsage, organization,
464 organizationalUnit, state, surname,
465 unstructuredName);
466 exit(EXIT_SUCCESS);
467 }
468 catch (const InternalFailure& e)
469 {
470 // commit the error reported in child process and exit
471 // Callback method from SDEvent Loop looks for exit status
472 exit(EXIT_FAILURE);
473 commit<InternalFailure>();
474 }
Ramesh Iyyard2393f22020-10-29 09:46:51 -0500475 catch (const InvalidArgument& e)
476 {
477 // commit the error reported in child process and exit
478 // Callback method from SDEvent Loop looks for exit status
479 exit(EXIT_FAILURE);
480 commit<InvalidArgument>();
481 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500482 }
483 else
484 {
485 using namespace sdeventplus::source;
Patrick Williams223e4602023-05-10 07:51:11 -0500486 Child::Callback callback =
487 [this](Child& eventSource, const siginfo_t* si) {
Marri Devender Raof4682712019-03-19 05:00:28 -0500488 eventSource.set_enabled(Enabled::On);
489 if (si->si_status != 0)
490 {
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000491 this->createCSRObject(Status::failure);
Marri Devender Raof4682712019-03-19 05:00:28 -0500492 }
493 else
494 {
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000495 this->createCSRObject(Status::success);
Marri Devender Raof4682712019-03-19 05:00:28 -0500496 }
497 };
498 try
499 {
500 sigset_t ss;
501 if (sigemptyset(&ss) < 0)
502 {
503 log<level::ERR>("Unable to initialize signal set");
504 elog<InternalFailure>();
505 }
506 if (sigaddset(&ss, SIGCHLD) < 0)
507 {
508 log<level::ERR>("Unable to add signal to signal set");
509 elog<InternalFailure>();
510 }
511
512 // Block SIGCHLD first, so that the event loop can handle it
Nan Zhoucfb58022021-12-28 11:02:26 -0800513 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500514 {
515 log<level::ERR>("Unable to block signal");
516 elog<InternalFailure>();
517 }
518 if (childPtr)
519 {
520 childPtr.reset();
521 }
522 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
523 std::move(callback));
524 }
525 catch (const InternalFailure& e)
526 {
527 commit<InternalFailure>();
528 }
529 }
530 auto csrObjectPath = objectPath + '/' + "csr";
531 return csrObjectPath;
532}
533
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200534std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500535{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200536 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500537}
538
Marri Devender Raof4682712019-03-19 05:00:28 -0500539void Manager::generateCSRHelper(
540 std::vector<std::string> alternativeNames, std::string challengePassword,
541 std::string city, std::string commonName, std::string contactPerson,
542 std::string country, std::string email, std::string givenName,
543 std::string initials, int64_t keyBitLength, std::string keyCurveId,
544 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
545 std::string organization, std::string organizationalUnit, std::string state,
546 std::string surname, std::string unstructuredName)
547{
548 int ret = 0;
549
550 // set version of x509 req
551 int nVersion = 1;
552 // TODO: Issue#6 need to make version number configurable
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800553 X509ReqPtr x509Req(X509_REQ_new(), ::X509_REQ_free);
Marri Devender Raof4682712019-03-19 05:00:28 -0500554 ret = X509_REQ_set_version(x509Req.get(), nVersion);
555 if (ret == 0)
556 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800557 log<level::ERR>("Error occurred during X509_REQ_set_version call");
Marri Devender Raof4682712019-03-19 05:00:28 -0500558 elog<InternalFailure>();
559 }
560
561 // set subject of x509 req
562 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
563
564 if (!alternativeNames.empty())
565 {
566 for (auto& name : alternativeNames)
567 {
568 addEntry(x509Name, "subjectAltName", name);
569 }
570 }
571 addEntry(x509Name, "challengePassword", challengePassword);
572 addEntry(x509Name, "L", city);
573 addEntry(x509Name, "CN", commonName);
574 addEntry(x509Name, "name", contactPerson);
575 addEntry(x509Name, "C", country);
576 addEntry(x509Name, "emailAddress", email);
577 addEntry(x509Name, "GN", givenName);
578 addEntry(x509Name, "initials", initials);
579 addEntry(x509Name, "algorithm", keyPairAlgorithm);
580 if (!keyUsage.empty())
581 {
582 for (auto& usage : keyUsage)
583 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500584 if (isExtendedKeyUsage(usage))
585 {
586 addEntry(x509Name, "extendedKeyUsage", usage);
587 }
588 else
589 {
590 addEntry(x509Name, "keyUsage", usage);
591 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500592 }
593 }
594 addEntry(x509Name, "O", organization);
Jayanth Othayothdc91fb62021-05-04 23:17:47 -0500595 addEntry(x509Name, "OU", organizationalUnit);
Marri Devender Raof4682712019-03-19 05:00:28 -0500596 addEntry(x509Name, "ST", state);
597 addEntry(x509Name, "SN", surname);
598 addEntry(x509Name, "unstructuredName", unstructuredName);
599
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800600 EVPPkeyPtr pKey(nullptr, ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500601
602 log<level::INFO>("Given Key pair algorithm",
603 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
604
605 // Used EC algorithm as default if user did not give algorithm type.
606 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500607 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500608 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500609 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500610 else
611 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500612 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
613 "RSA and EC only");
614 elog<InvalidArgument>(
615 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
616 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
617 }
618
619 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
620 if (ret == 0)
621 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800622 log<level::ERR>("Error occurred while setting Public key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500623 elog<InternalFailure>();
624 }
625
626 // Write private key to file
Nan Zhou718eef32021-12-28 11:03:30 -0800627 writePrivateKey(pKey, defaultPrivateKeyFileName);
Marri Devender Raof4682712019-03-19 05:00:28 -0500628
629 // set sign key of x509 req
630 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500631 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500632 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800633 log<level::ERR>("Error occurred while signing key of x509");
Marri Devender Raof4682712019-03-19 05:00:28 -0500634 elog<InternalFailure>();
635 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500636
Marri Devender Raof4682712019-03-19 05:00:28 -0500637 log<level::INFO>("Writing CSR to file");
Nan Zhou718eef32021-12-28 11:03:30 -0800638 fs::path csrFilePath = certParentInstallPath / defaultCSRFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500639 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500640}
641
Marri Devender Rao76411052019-08-07 01:25:07 -0500642bool Manager::isExtendedKeyUsage(const std::string& usage)
643{
644 const static std::array<const char*, 6> usageList = {
645 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
646 "Timestamping", "CodeSigning", "EmailProtection"};
Patrick Williams223e4602023-05-10 07:51:11 -0500647 auto it = std::find_if(usageList.begin(), usageList.end(),
648 [&usage](const char* s) {
649 return (strcmp(s, usage.c_str()) == 0);
650 });
Marri Devender Rao76411052019-08-07 01:25:07 -0500651 return it != usageList.end();
652}
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800653EVPPkeyPtr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500654{
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500655 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500656 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500657 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500658 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500659 log<level::INFO>(
660 "KeyBitLength is not given.Hence, using default KeyBitLength",
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800661 entry("DEFAULTKEYBITLENGTH=%d", defaultKeyBitLength));
662 keyBitLen = defaultKeyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500663 }
Patrick Williams26fb83e2021-12-14 14:08:28 -0600664
665#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
666
667 // generate rsa key
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800668 BignumPtr bne(BN_new(), ::BN_free);
Patrick Williams26fb83e2021-12-14 14:08:28 -0600669 auto ret = BN_set_word(bne.get(), RSA_F4);
670 if (ret == 0)
671 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800672 log<level::ERR>("Error occurred during BN_set_word call");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600673 elog<InternalFailure>();
674 }
Nan Zhou762da742022-01-14 17:21:44 -0800675 using RSAPtr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
676 RSAPtr rsa(RSA_new(), ::RSA_free);
677 ret = RSA_generate_key_ex(rsa.get(), keyBitLen, bne.get(), nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500678 if (ret != 1)
679 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800680 log<level::ERR>("Error occurred during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500681 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500682 elog<InternalFailure>();
683 }
684
685 // set public key of x509 req
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800686 EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Nan Zhou762da742022-01-14 17:21:44 -0800687 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa.get());
Marri Devender Raof4682712019-03-19 05:00:28 -0500688 if (ret == 0)
689 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800690 log<level::ERR>("Error occurred during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500691 elog<InternalFailure>();
692 }
Nan Zhou762da742022-01-14 17:21:44 -0800693 // Now |rsa| is managed by |pKey|
694 rsa.release();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500695 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600696
697#else
698 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
699 EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
700 if (!ctx)
701 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800702 log<level::ERR>("Error occurred creating EVP_PKEY_CTX from algorithm");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600703 elog<InternalFailure>();
704 }
705
706 if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
707 (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
708
709 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800710 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600711 elog<InternalFailure>();
712 }
713
714 EVP_PKEY* pKey = nullptr;
715 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
716 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800717 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600718 elog<InternalFailure>();
719 }
720
721 return {pKey, &::EVP_PKEY_free};
722#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500723}
724
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800725EVPPkeyPtr Manager::generateECKeyPair(const std::string& curveId)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500726{
727 std::string curId(curveId);
728
729 if (curId.empty())
730 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500731 log<level::INFO>(
732 "KeyCurveId is not given. Hence using default curve id",
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800733 entry("DEFAULTKEYCURVEID=%s", defaultKeyCurveID));
734 curId = defaultKeyCurveID;
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500735 }
736
737 int ecGrp = OBJ_txt2nid(curId.c_str());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500738 if (ecGrp == NID_undef)
739 {
740 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800741 "Error occurred during convert the curve id string format into NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500742 entry("KEYCURVEID=%s", curId.c_str()));
743 elog<InternalFailure>();
744 }
745
Patrick Williams26fb83e2021-12-14 14:08:28 -0600746#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
747
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500748 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
749
Nan Zhoucfb58022021-12-28 11:02:26 -0800750 if (ecKey == nullptr)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500751 {
752 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800753 "Error occurred during create the EC_Key object from NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500754 entry("ECGROUP=%d", ecGrp));
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);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800768 log<level::ERR>("Error occurred during generate EC key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500769 elog<InternalFailure>();
770 }
771
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800772 EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500773 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
774 if (ret == 0)
775 {
776 EC_KEY_free(ecKey);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800777 log<level::ERR>("Error occurred during assign EC Key into EVP");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500778 elog<InternalFailure>();
779 }
780
781 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600782
783#else
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000784 auto holderOfKey = [](EVP_PKEY* key) {
Patrick Williams26fb83e2021-12-14 14:08:28 -0600785 return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
786 key, &::EVP_PKEY_free};
787 };
788
789 // Create context to set up curve parameters.
790 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
791 EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
792 if (!ctx)
793 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800794 log<level::ERR>("Error occurred creating EVP_PKEY_CTX for params");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600795 elog<InternalFailure>();
796 }
797
798 // Set up curve parameters.
799 EVP_PKEY* params = nullptr;
800
801 if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
802 (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
803 0) ||
804 (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
805 (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
806 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800807 log<level::ERR>("Error occurred setting curve parameters");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600808 elog<InternalFailure>();
809 }
810
811 // Move parameters to RAII holder.
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000812 auto pparms = holderOfKey(params);
Patrick Williams26fb83e2021-12-14 14:08:28 -0600813
814 // Create new context for key.
815 ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
816
817 if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
818 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800819 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600820 elog<InternalFailure>();
821 }
822
823 EVP_PKEY* pKey = nullptr;
824 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
825 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800826 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600827 elog<InternalFailure>();
828 }
829
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000830 return holderOfKey(pKey);
Patrick Williams26fb83e2021-12-14 14:08:28 -0600831#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500832}
833
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800834void Manager::writePrivateKey(const EVPPkeyPtr& pKey,
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500835 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500836{
837 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500838 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500839 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500840
841 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
Nan Zhoucfb58022021-12-28 11:02:26 -0800842 if (fp == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500843 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800844 log<level::ERR>("Error occurred creating private key file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500845 elog<InternalFailure>();
846 }
Patrick Williams223e4602023-05-10 07:51:11 -0500847 int ret = PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0,
848 nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500849 std::fclose(fp);
850 if (ret == 0)
851 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800852 log<level::ERR>("Error occurred while writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500853 elog<InternalFailure>();
854 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500855}
856
857void Manager::addEntry(X509_NAME* x509Name, const char* field,
858 const std::string& bytes)
859{
860 if (bytes.empty())
861 {
862 return;
863 }
864 int ret = X509_NAME_add_entry_by_txt(
865 x509Name, field, MBSTRING_ASC,
866 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
867 if (ret != 1)
868 {
869 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
870 entry("VALUE=%s", bytes.c_str()));
871 elog<InternalFailure>();
872 }
873}
874
875void Manager::createCSRObject(const Status& status)
876{
877 if (csrPtr)
878 {
879 csrPtr.reset(nullptr);
880 }
881 auto csrObjectPath = objectPath + '/' + "csr";
882 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
883 certInstallPath.c_str(), status);
884}
885
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800886void Manager::writeCSR(const std::string& filePath, const X509ReqPtr& x509Req)
Marri Devender Raof4682712019-03-19 05:00:28 -0500887{
888 if (fs::exists(filePath))
889 {
890 log<level::INFO>("Removing the existing file",
891 entry("FILENAME=%s", filePath.c_str()));
892 if (!fs::remove(filePath.c_str()))
893 {
894 log<level::ERR>("Unable to remove the file",
895 entry("FILENAME=%s", filePath.c_str()));
896 elog<InternalFailure>();
897 }
898 }
899
Nan Zhoucfb58022021-12-28 11:02:26 -0800900 FILE* fp = nullptr;
Marri Devender Raof4682712019-03-19 05:00:28 -0500901
Nan Zhoucfb58022021-12-28 11:02:26 -0800902 if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500903 {
904 log<level::ERR>("Error opening the file to write the CSR",
905 entry("FILENAME=%s", filePath.c_str()));
906 elog<InternalFailure>();
907 }
908
909 int rc = PEM_write_X509_REQ(fp, x509Req.get());
910 if (!rc)
911 {
912 log<level::ERR>("PEM write routine failed",
913 entry("FILENAME=%s", filePath.c_str()));
914 std::fclose(fp);
915 elog<InternalFailure>();
916 }
917 std::fclose(fp);
918}
919
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200920void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500921{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200922 auto certObjectPath = objectPath + '/';
923
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000924 if (certType == CertificateType::authority)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500925 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100926 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200927 if (!fs::is_directory(certInstallPath))
928 {
929 log<level::ERR>("Certificate installation path exists and it is "
930 "not a directory");
931 elog<InternalFailure>();
Nan Zhou6ec13c82021-12-30 11:34:50 -0800932 }
933
934 // If the authorities list exists, recover from it and return
Patrick Williams223e4602023-05-10 07:51:11 -0500935 if (fs::path authoritiesListFilePath = fs::path(certInstallPath) /
936 defaultAuthoritiesListFileName;
Nan Zhou6ec13c82021-12-30 11:34:50 -0800937 fs::exists(authoritiesListFilePath))
938 {
939 // remove all other files and directories
940 for (auto& path : fs::directory_iterator(certInstallPath))
941 {
942 if (path.path() != authoritiesListFilePath)
943 {
944 fs::remove_all(path);
945 }
946 }
947 installAll(authoritiesListFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200948 return;
949 }
950
951 for (auto& path : fs::directory_iterator(certInstallPath))
952 {
953 try
954 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100955 // Assume here any regular file located in certificate directory
956 // contains certificates body. Do not want to use soft links
957 // would add value.
958 if (fs::is_regular_file(path))
959 {
960 installedCerts.emplace_back(std::make_unique<Certificate>(
961 bus, certObjectPath + std::to_string(certIdCounter++),
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800962 certType, certInstallPath, path.path(),
Willy Tu698a5742022-09-23 21:33:01 +0000963 certWatchPtr.get(), *this, /*restore=*/true));
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100964 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200965 }
966 catch (const InternalFailure& e)
967 {
968 report<InternalFailure>();
969 }
970 catch (const InvalidCertificate& e)
971 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800972 report<InvalidCertificate>(InvalidCertificateReason(
973 "Existing certificate file is corrupted"));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200974 }
975 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500976 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200977 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500978 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200979 try
980 {
981 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100982 bus, certObjectPath + '1', certType, certInstallPath,
Willy Tu698a5742022-09-23 21:33:01 +0000983 certInstallPath, certWatchPtr.get(), *this, /*restore=*/false));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200984 }
985 catch (const InternalFailure& e)
986 {
987 report<InternalFailure>();
988 }
989 catch (const InvalidCertificate& e)
990 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800991 report<InvalidCertificate>(InvalidCertificateReason(
992 "Existing certificate file is corrupted"));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200993 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500994 }
995}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500996
997void Manager::createRSAPrivateKeyFile()
998{
Patrick Williams223e4602023-05-10 07:51:11 -0500999 fs::path rsaPrivateKeyFileName = certParentInstallPath /
1000 defaultRSAPrivateKeyFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001001
1002 try
1003 {
1004 if (!fs::exists(rsaPrivateKeyFileName))
1005 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001006 writePrivateKey(generateRSAKeyPair(supportedKeyBitLength),
Nan Zhou718eef32021-12-28 11:03:30 -08001007 defaultRSAPrivateKeyFileName);
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001008 }
1009 }
1010 catch (const InternalFailure& e)
1011 {
1012 report<InternalFailure>();
1013 }
1014}
1015
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001016EVPPkeyPtr Manager::getRSAKeyPair(const int64_t keyBitLength)
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001017{
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001018 if (keyBitLength != supportedKeyBitLength)
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001019 {
1020 log<level::ERR>(
1021 "Given Key bit length is not supported",
1022 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001023 entry("SUPPORTEDKEYBITLENGTH=%d", supportedKeyBitLength));
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001024 elog<InvalidArgument>(
1025 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
1026 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
1027 }
Patrick Williams223e4602023-05-10 07:51:11 -05001028 fs::path rsaPrivateKeyFileName = certParentInstallPath /
1029 defaultRSAPrivateKeyFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001030
1031 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
1032 if (!privateKeyFile)
1033 {
1034 log<level::ERR>("Unable to open RSA private key file to read",
1035 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
1036 entry("ERRORREASON=%s", strerror(errno)));
1037 elog<InternalFailure>();
1038 }
1039
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001040 EVPPkeyPtr privateKey(
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001041 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
1042 ::EVP_PKEY_free);
1043 std::fclose(privateKeyFile);
1044
1045 if (!privateKey)
1046 {
Nan Zhoubf3cf752021-12-28 11:02:07 -08001047 log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001048 elog<InternalFailure>();
1049 }
1050 return privateKey;
1051}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001052
1053void Manager::storageUpdate()
1054{
Nan Zhoue3d47cd2022-09-16 03:41:53 +00001055 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001056 {
1057 // Remove symbolic links in the certificate directory
1058 for (auto& certPath : fs::directory_iterator(certInstallPath))
1059 {
1060 try
1061 {
1062 if (fs::is_symlink(certPath))
1063 {
1064 fs::remove(certPath);
1065 }
1066 }
1067 catch (const std::exception& e)
1068 {
1069 log<level::ERR>(
1070 "Failed to remove symlink for certificate",
1071 entry("ERR=%s", e.what()),
1072 entry("SYMLINK=%s", certPath.path().string().c_str()));
1073 elog<InternalFailure>();
1074 }
1075 }
1076 }
1077
1078 for (const auto& cert : installedCerts)
1079 {
1080 cert->storageUpdate();
1081 }
1082}
1083
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001084void Manager::reloadOrReset(const std::string& unit)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001085{
1086 if (!unit.empty())
1087 {
1088 try
1089 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001090 constexpr auto defaultSystemdService = "org.freedesktop.systemd1";
1091 constexpr auto defaultSystemdObjectPath =
1092 "/org/freedesktop/systemd1";
1093 constexpr auto defaultSystemdInterface =
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001094 "org.freedesktop.systemd1.Manager";
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001095 auto method = bus.new_method_call(
1096 defaultSystemdService, defaultSystemdObjectPath,
1097 defaultSystemdInterface, "ReloadOrRestartUnit");
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001098 method.append(unit, "replace");
1099 bus.call_noreply(method);
1100 }
Patrick Williamsb3dbfb32022-07-22 19:26:57 -05001101 catch (const sdbusplus::exception_t& e)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001102 {
1103 log<level::ERR>("Failed to reload or restart service",
1104 entry("ERR=%s", e.what()),
1105 entry("UNIT=%s", unit.c_str()));
1106 elog<InternalFailure>();
1107 }
1108 }
1109}
1110
1111bool Manager::isCertificateUnique(const std::string& filePath,
1112 const Certificate* const certToDrop)
1113{
1114 if (std::any_of(
1115 installedCerts.begin(), installedCerts.end(),
Patrick Williams223e4602023-05-10 07:51:11 -05001116 [&filePath, certToDrop](const std::unique_ptr<Certificate>& cert) {
1117 return cert.get() != certToDrop && cert->isSame(filePath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001118 }))
1119 {
1120 return false;
1121 }
1122 else
1123 {
1124 return true;
1125 }
1126}
1127
Nan Zhoue1289ad2021-12-28 11:02:56 -08001128} // namespace phosphor::certs