blob: be1b98f21c551df57b5748902d78954d4f71f54e [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>
Ravi Tejaf2646272023-09-30 13:00:55 -050020#include <phosphor-logging/lg2.hpp>
Patrick Williams223e4602023-05-10 07:51:11 -050021#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;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080048using ::phosphor::logging::report;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050049
Nan Zhoucf06ccd2021-12-28 16:25:45 -080050using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
51using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
52using ::sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
53using NotAllowedReason =
54 ::phosphor::logging::xyz::openbmc_project::Common::NotAllowed::REASON;
55using InvalidCertificateReason = ::phosphor::logging::xyz::openbmc_project::
56 Certs::InvalidCertificate::REASON;
57using ::sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
58using Argument =
59 ::phosphor::logging::xyz::openbmc_project::Common::InvalidArgument;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050060
Nan Zhoucf06ccd2021-12-28 16:25:45 -080061// RAII support for openSSL functions.
62using X509ReqPtr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
63using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
64using BignumPtr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
Nan Zhou6ec13c82021-12-30 11:34:50 -080065using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080066
67constexpr int supportedKeyBitLength = 2048;
68constexpr int defaultKeyBitLength = 2048;
69// secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
70constexpr auto defaultKeyCurveID = "secp224r1";
Nan Zhou6ec13c82021-12-30 11:34:50 -080071// PEM certificate block markers, defined in go/rfc/7468.
72constexpr std::string_view beginCertificate = "-----BEGIN CERTIFICATE-----";
73constexpr std::string_view endCertificate = "-----END CERTIFICATE-----";
74
75/**
76 * @brief Splits the given authorities list file and returns an array of
77 * individual PEM encoded x509 certificate.
78 *
79 * @param[in] sourceFilePath - Path to the authorities list file.
80 *
81 * @return An array of individual PEM encoded x509 certificate
82 */
83std::vector<std::string> splitCertificates(const std::string& sourceFilePath)
84{
85 std::ifstream inputCertFileStream;
86 inputCertFileStream.exceptions(
87 std::ifstream::failbit | std::ifstream::badbit | std::ifstream::eofbit);
88
89 std::stringstream pemStream;
90 std::vector<std::string> certificatesList;
91 try
92 {
93 inputCertFileStream.open(sourceFilePath);
94 pemStream << inputCertFileStream.rdbuf();
95 inputCertFileStream.close();
96 }
97 catch (const std::exception& e)
98 {
Ravi Tejaf2646272023-09-30 13:00:55 -050099 lg2::error("Failed to read certificates list, ERR:{ERR}, SRC:{SRC}",
100 "ERR", e, "SRC", sourceFilePath);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800101 elog<InternalFailure>();
102 }
103 std::string pem = pemStream.str();
104 size_t begin = 0;
105 // |begin| points to the current start position for searching the next
106 // |beginCertificate| block. When we find the beginning of the certificate,
107 // we extract the content between the beginning and the end of the current
108 // certificate. And finally we move |begin| to the end of the current
109 // certificate to start searching the next potential certificate.
110 for (begin = pem.find(beginCertificate, begin); begin != std::string::npos;
111 begin = pem.find(beginCertificate, begin))
112 {
113 size_t end = pem.find(endCertificate, begin);
114 if (end == std::string::npos)
115 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500116 lg2::error(
Nan Zhou6ec13c82021-12-30 11:34:50 -0800117 "invalid PEM contains a BEGIN identifier without an END");
118 elog<InvalidCertificate>(InvalidCertificateReason(
119 "invalid PEM contains a BEGIN identifier without an END"));
120 }
121 end += endCertificate.size();
122 certificatesList.emplace_back(pem.substr(begin, end - begin));
123 begin = end;
124 }
125 return certificatesList;
126}
127
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800128} // namespace
Marri Devender Raof4682712019-03-19 05:00:28 -0500129
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500130Manager::Manager(sdbusplus::bus_t& bus, sdeventplus::Event& event,
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800131 const char* path, CertificateType type,
132 const std::string& unit, const std::string& installPath) :
133 internal::ManagerInterface(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -0500134 bus(bus), event(event), objectPath(path), certType(type),
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500135 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
136 certParentInstallPath(fs::path(certInstallPath).parent_path())
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500137{
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500138 try
139 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500140 // Create certificate directory if not existing.
Nan Zhoubf3cf752021-12-28 11:02:07 -0800141 // Set correct certificate directory permissions.
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500142 fs::path certDirectory;
143 try
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500144 {
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000145 if (certType == CertificateType::authority)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500146 {
147 certDirectory = certInstallPath;
148 }
149 else
150 {
151 certDirectory = certParentInstallPath;
152 }
153
154 if (!fs::exists(certDirectory))
155 {
156 fs::create_directories(certDirectory);
157 }
158
159 auto permission = fs::perms::owner_read | fs::perms::owner_write |
160 fs::perms::owner_exec;
161 fs::permissions(certDirectory, permission,
162 fs::perm_options::replace);
163 storageUpdate();
164 }
Patrick Williams71957992021-10-06 14:42:52 -0500165 catch (const fs::filesystem_error& e)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500166 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500167 lg2::error(
168 "Failed to create directory, ERR:{ERR}, DIRECTORY:{DIRECTORY}",
169 "ERR", e, "DIRECTORY", certParentInstallPath);
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500170 report<InternalFailure>();
171 }
172
173 // Generating RSA private key file if certificate type is server/client
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000174 if (certType != CertificateType::authority)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500175 {
176 createRSAPrivateKeyFile();
177 }
178
179 // restore any existing certificates
180 createCertificates();
181
182 // watch is not required for authority certificates
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000183 if (certType != CertificateType::authority)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500184 {
185 // watch for certificate file create/replace
Patrick Williams223e4602023-05-10 07:51:11 -0500186 certWatchPtr = std::make_unique<Watch>(event, certInstallPath,
187 [this]() {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500188 try
189 {
190 // if certificate file existing update it
191 if (!installedCerts.empty())
192 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500193 lg2::info("Inotify callback to update "
194 "certificate properties");
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500195 installedCerts[0]->populateProperties();
196 }
197 else
198 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500199 lg2::info(
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500200 "Inotify callback to create certificate object");
201 createCertificates();
202 }
203 }
204 catch (const InternalFailure& e)
205 {
206 commit<InternalFailure>();
207 }
208 catch (const InvalidCertificate& e)
209 {
210 commit<InvalidCertificate>();
211 }
212 });
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500213 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100214 else
215 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500216 try
217 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500218 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
219 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500220 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500221 lg2::notice(
222 "Legacy certificate detected, will be installed from,"
223 "SINGLE_CERTPATH:{SINGLE_CERTPATH}",
224 "SINGLE_CERTPATH", singleCertPath);
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500225 install(singleCertPath);
226 if (!fs::remove(singleCertPath))
227 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500228 lg2::error("Unable to remove old certificate from,"
229 "SINGLE_CERTPATH:{SINGLE_CERTPATH}",
230 "SINGLE_CERTPATH", singleCertPath);
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500231 elog<InternalFailure>();
232 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500233 }
234 }
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500235 catch (const std::exception& ex)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500236 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500237 lg2::error(
238 "Error in restoring legacy certificate, ERROR_STR:{ERROR_STR}",
239 "ERROR_STR", ex);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200240 }
241 }
242 }
Patrick Williams71957992021-10-06 14:42:52 -0500243 catch (const std::exception& ex)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500244 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500245 lg2::error(
246 "Error in certificate manager constructor, ERROR_STR:{ERROR_STR}",
247 "ERROR_STR", ex);
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500248 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500249}
250
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200251std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500252{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000253 if (certType != CertificateType::authority && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600254 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800255 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
Marri Devender Rao13965112019-02-27 08:47:12 -0600256 }
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000257 else if (certType == CertificateType::authority &&
Nan Zhou718eef32021-12-28 11:03:30 -0800258 installedCerts.size() >= maxNumAuthorityCertificates)
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200259 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800260 elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200261 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500262
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100263 std::string certObjectPath;
264 if (isCertificateUnique(filePath))
265 {
266 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
267 installedCerts.emplace_back(std::make_unique<Certificate>(
268 bus, certObjectPath, certType, certInstallPath, filePath,
Willy Tu698a5742022-09-23 21:33:01 +0000269 certWatchPtr.get(), *this, /*restore=*/false));
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100270 reloadOrReset(unitToRestart);
271 certIdCounter++;
272 }
273 else
274 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800275 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100276 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200277
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200278 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500279}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500280
Nan Zhou6ec13c82021-12-30 11:34:50 -0800281std::vector<sdbusplus::message::object_path>
282 Manager::installAll(const std::string filePath)
283{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000284 if (certType != CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800285 {
286 elog<NotAllowed>(
287 NotAllowedReason("The InstallAll interface is only allowed for "
288 "Authority certificates"));
289 }
290
291 if (!installedCerts.empty())
292 {
293 elog<NotAllowed>(NotAllowedReason(
294 "There are already root certificates; Call DeleteAll then "
295 "InstallAll, or use ReplaceAll"));
296 }
297
298 fs::path sourceFile(filePath);
299 if (!fs::exists(sourceFile))
300 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500301 lg2::error("File is Missing, FILE:{FILE}", "FILE", filePath);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800302 elog<InternalFailure>();
303 }
304 std::vector<std::string> authorities = splitCertificates(sourceFile);
305 if (authorities.size() > maxNumAuthorityCertificates)
306 {
307 elog<NotAllowed>(NotAllowedReason("Certificates limit reached"));
308 }
309
Ravi Tejaf2646272023-09-30 13:00:55 -0500310 lg2::info("Starts authority list install");
Nan Zhou78357b02022-06-09 22:33:35 +0000311
Nan Zhou6ec13c82021-12-30 11:34:50 -0800312 fs::path authorityStore(certInstallPath);
Patrick Williams223e4602023-05-10 07:51:11 -0500313 fs::path authoritiesListFile = authorityStore /
314 defaultAuthoritiesListFileName;
Nan Zhou6ec13c82021-12-30 11:34:50 -0800315
316 // Atomically install all the certificates
317 fs::path tempPath = Certificate::generateUniqueFilePath(authorityStore);
318 fs::create_directory(tempPath);
319 // Copies the authorities list
320 Certificate::copyCertificate(sourceFile,
321 tempPath / defaultAuthoritiesListFileName);
322 std::vector<std::unique_ptr<Certificate>> tempCertificates;
323 uint64_t tempCertIdCounter = certIdCounter;
324 X509StorePtr x509Store = getX509Store(sourceFile);
325 for (const auto& authority : authorities)
326 {
Patrick Williams223e4602023-05-10 07:51:11 -0500327 std::string certObjectPath = objectPath + '/' +
328 std::to_string(tempCertIdCounter);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800329 tempCertificates.emplace_back(std::make_unique<Certificate>(
330 bus, certObjectPath, certType, tempPath, *x509Store, authority,
Willy Tu698a5742022-09-23 21:33:01 +0000331 certWatchPtr.get(), *this, /*restore=*/false));
Nan Zhou6ec13c82021-12-30 11:34:50 -0800332 tempCertIdCounter++;
333 }
334
335 // We are good now, issue swap
336 installedCerts = std::move(tempCertificates);
337 certIdCounter = tempCertIdCounter;
338 // Rename all the certificates including the authorities list
339 for (const fs::path& f : fs::directory_iterator(tempPath))
340 {
341 if (fs::is_symlink(f))
342 {
343 continue;
344 }
345 fs::rename(/*from=*/f, /*to=*/certInstallPath / f.filename());
346 }
347 // Update file locations and create symbol links
348 for (const auto& cert : installedCerts)
349 {
350 cert->setCertInstallPath(certInstallPath);
351 cert->setCertFilePath(certInstallPath /
352 fs::path(cert->getCertFilePath()).filename());
353 cert->storageUpdate();
354 }
355 // Remove the temporary folder
356 fs::remove_all(tempPath);
357
358 std::vector<sdbusplus::message::object_path> objects;
359 for (const auto& certificate : installedCerts)
360 {
361 objects.emplace_back(certificate->getObjectPath());
362 }
363
Ravi Tejaf2646272023-09-30 13:00:55 -0500364 lg2::info("Finishes authority list install; reload units starts");
Nan Zhou6ec13c82021-12-30 11:34:50 -0800365 reloadOrReset(unitToRestart);
366 return objects;
367}
368
369std::vector<sdbusplus::message::object_path>
370 Manager::replaceAll(std::string filePath)
371{
372 installedCerts.clear();
373 certIdCounter = 1;
374 storageUpdate();
375 return installAll(std::move(filePath));
376}
377
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200378void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500379{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600380 // TODO: #Issue 4 when a certificate is deleted system auto generates
381 // certificate file. At present we are not supporting creation of
382 // certificate object for the auto-generated certificate file as
383 // deletion if only applicable for REST server and Bmcweb does not allow
384 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200385 installedCerts.clear();
Nan Zhou6ec13c82021-12-30 11:34:50 -0800386 // If the authorities list exists, delete it as well
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000387 if (certType == CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800388 {
Patrick Williams223e4602023-05-10 07:51:11 -0500389 if (fs::path authoritiesList = fs::path(certInstallPath) /
390 defaultAuthoritiesListFileName;
Nan Zhou6ec13c82021-12-30 11:34:50 -0800391 fs::exists(authoritiesList))
392 {
393 fs::remove(authoritiesList);
394 }
395 }
396 certIdCounter = 1;
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100397 storageUpdate();
398 reloadOrReset(unitToRestart);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500399}
Marri Devender Raof4682712019-03-19 05:00:28 -0500400
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100401void Manager::deleteCertificate(const Certificate* const certificate)
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200402{
Patrick Williams223e4602023-05-10 07:51:11 -0500403 const std::vector<std::unique_ptr<Certificate>>::iterator& certIt =
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200404 std::find_if(installedCerts.begin(), installedCerts.end(),
Patrick Williams223e4602023-05-10 07:51:11 -0500405 [certificate](const std::unique_ptr<Certificate>& cert) {
406 return (cert.get() == certificate);
Patrick Williamsd96b81c2023-10-20 11:19:39 -0500407 });
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200408 if (certIt != installedCerts.end())
409 {
410 installedCerts.erase(certIt);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100411 storageUpdate();
412 reloadOrReset(unitToRestart);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200413 }
414 else
415 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500416 lg2::error("Certificate does not exist, ID:{ID}", "ID",
417 certificate->getCertId());
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100418 elog<InternalFailure>();
419 }
420}
421
422void Manager::replaceCertificate(Certificate* const certificate,
423 const std::string& filePath)
424{
425 if (isCertificateUnique(filePath, certificate))
426 {
Willy Tu698a5742022-09-23 21:33:01 +0000427 certificate->install(filePath, false);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100428 storageUpdate();
429 reloadOrReset(unitToRestart);
430 }
431 else
432 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800433 elog<NotAllowed>(NotAllowedReason("Certificate already exist"));
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200434 }
435}
436
Marri Devender Raof4682712019-03-19 05:00:28 -0500437std::string Manager::generateCSR(
438 std::vector<std::string> alternativeNames, std::string challengePassword,
439 std::string city, std::string commonName, std::string contactPerson,
440 std::string country, std::string email, std::string givenName,
441 std::string initials, int64_t keyBitLength, std::string keyCurveId,
442 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
443 std::string organization, std::string organizationalUnit, std::string state,
444 std::string surname, std::string unstructuredName)
445{
446 // We support only one CSR.
447 csrPtr.reset(nullptr);
448 auto pid = fork();
449 if (pid == -1)
450 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500451 lg2::error("Error occurred during forking process");
Marri Devender Raof4682712019-03-19 05:00:28 -0500452 report<InternalFailure>();
453 }
454 else if (pid == 0)
455 {
456 try
457 {
458 generateCSRHelper(alternativeNames, challengePassword, city,
459 commonName, contactPerson, country, email,
460 givenName, initials, keyBitLength, keyCurveId,
461 keyPairAlgorithm, keyUsage, organization,
462 organizationalUnit, state, surname,
463 unstructuredName);
464 exit(EXIT_SUCCESS);
465 }
466 catch (const InternalFailure& e)
467 {
468 // commit the error reported in child process and exit
469 // Callback method from SDEvent Loop looks for exit status
470 exit(EXIT_FAILURE);
471 commit<InternalFailure>();
472 }
Ramesh Iyyard2393f22020-10-29 09:46:51 -0500473 catch (const InvalidArgument& e)
474 {
475 // commit the error reported in child process and exit
476 // Callback method from SDEvent Loop looks for exit status
477 exit(EXIT_FAILURE);
478 commit<InvalidArgument>();
479 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500480 }
481 else
482 {
483 using namespace sdeventplus::source;
Patrick Williamsd96b81c2023-10-20 11:19:39 -0500484 Child::Callback callback = [this](Child& eventSource,
485 const siginfo_t* si) {
Marri Devender Raof4682712019-03-19 05:00:28 -0500486 eventSource.set_enabled(Enabled::On);
487 if (si->si_status != 0)
488 {
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000489 this->createCSRObject(Status::failure);
Marri Devender Raof4682712019-03-19 05:00:28 -0500490 }
491 else
492 {
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000493 this->createCSRObject(Status::success);
Marri Devender Raof4682712019-03-19 05:00:28 -0500494 }
495 };
496 try
497 {
498 sigset_t ss;
499 if (sigemptyset(&ss) < 0)
500 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500501 lg2::error("Unable to initialize signal set");
Marri Devender Raof4682712019-03-19 05:00:28 -0500502 elog<InternalFailure>();
503 }
504 if (sigaddset(&ss, SIGCHLD) < 0)
505 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500506 lg2::error("Unable to add signal to signal set");
Marri Devender Raof4682712019-03-19 05:00:28 -0500507 elog<InternalFailure>();
508 }
509
510 // Block SIGCHLD first, so that the event loop can handle it
Nan Zhoucfb58022021-12-28 11:02:26 -0800511 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500512 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500513 lg2::error("Unable to block signal");
Marri Devender Raof4682712019-03-19 05:00:28 -0500514 elog<InternalFailure>();
515 }
516 if (childPtr)
517 {
518 childPtr.reset();
519 }
520 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
521 std::move(callback));
522 }
523 catch (const InternalFailure& e)
524 {
525 commit<InternalFailure>();
526 }
527 }
528 auto csrObjectPath = objectPath + '/' + "csr";
529 return csrObjectPath;
530}
531
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200532std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500533{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200534 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500535}
536
Marri Devender Raof4682712019-03-19 05:00:28 -0500537void Manager::generateCSRHelper(
538 std::vector<std::string> alternativeNames, std::string challengePassword,
539 std::string city, std::string commonName, std::string contactPerson,
540 std::string country, std::string email, std::string givenName,
541 std::string initials, int64_t keyBitLength, std::string keyCurveId,
542 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
543 std::string organization, std::string organizationalUnit, std::string state,
544 std::string surname, std::string unstructuredName)
545{
546 int ret = 0;
547
548 // set version of x509 req
549 int nVersion = 1;
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800550 X509ReqPtr x509Req(X509_REQ_new(), ::X509_REQ_free);
Marri Devender Raof4682712019-03-19 05:00:28 -0500551 ret = X509_REQ_set_version(x509Req.get(), nVersion);
552 if (ret == 0)
553 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500554 lg2::error("Error occurred during X509_REQ_set_version call");
Marri Devender Raof4682712019-03-19 05:00:28 -0500555 elog<InternalFailure>();
556 }
557
558 // set subject of x509 req
559 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
560
561 if (!alternativeNames.empty())
562 {
563 for (auto& name : alternativeNames)
564 {
565 addEntry(x509Name, "subjectAltName", name);
566 }
567 }
568 addEntry(x509Name, "challengePassword", challengePassword);
569 addEntry(x509Name, "L", city);
570 addEntry(x509Name, "CN", commonName);
571 addEntry(x509Name, "name", contactPerson);
572 addEntry(x509Name, "C", country);
573 addEntry(x509Name, "emailAddress", email);
574 addEntry(x509Name, "GN", givenName);
575 addEntry(x509Name, "initials", initials);
576 addEntry(x509Name, "algorithm", keyPairAlgorithm);
577 if (!keyUsage.empty())
578 {
579 for (auto& usage : keyUsage)
580 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500581 if (isExtendedKeyUsage(usage))
582 {
583 addEntry(x509Name, "extendedKeyUsage", usage);
584 }
585 else
586 {
587 addEntry(x509Name, "keyUsage", usage);
588 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500589 }
590 }
591 addEntry(x509Name, "O", organization);
Jayanth Othayothdc91fb62021-05-04 23:17:47 -0500592 addEntry(x509Name, "OU", organizationalUnit);
Marri Devender Raof4682712019-03-19 05:00:28 -0500593 addEntry(x509Name, "ST", state);
594 addEntry(x509Name, "SN", surname);
595 addEntry(x509Name, "unstructuredName", unstructuredName);
596
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800597 EVPPkeyPtr pKey(nullptr, ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500598
Ravi Tejaf2646272023-09-30 13:00:55 -0500599 lg2::info("Given Key pair algorithm, KEYPAIRALGORITHM:{KEYPAIRALGORITHM}",
600 "KEYPAIRALGORITHM", keyPairAlgorithm);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500601
602 // Used EC algorithm as default if user did not give algorithm type.
603 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500604 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500605 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500606 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500607 else
608 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500609 lg2::error("Given Key pair algorithm is not supported. Supporting "
610 "RSA and EC only");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500611 elog<InvalidArgument>(
612 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
613 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
614 }
615
616 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
617 if (ret == 0)
618 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500619 lg2::error("Error occurred while setting Public key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500620 elog<InternalFailure>();
621 }
622
623 // Write private key to file
Nan Zhou718eef32021-12-28 11:03:30 -0800624 writePrivateKey(pKey, defaultPrivateKeyFileName);
Marri Devender Raof4682712019-03-19 05:00:28 -0500625
626 // set sign key of x509 req
627 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500628 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500629 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500630 lg2::error("Error occurred while signing key of x509");
Marri Devender Raof4682712019-03-19 05:00:28 -0500631 elog<InternalFailure>();
632 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500633
Ravi Tejaf2646272023-09-30 13:00:55 -0500634 lg2::info("Writing CSR to file");
Nan Zhou718eef32021-12-28 11:03:30 -0800635 fs::path csrFilePath = certParentInstallPath / defaultCSRFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500636 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500637}
638
Marri Devender Rao76411052019-08-07 01:25:07 -0500639bool Manager::isExtendedKeyUsage(const std::string& usage)
640{
641 const static std::array<const char*, 6> usageList = {
642 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
643 "Timestamping", "CodeSigning", "EmailProtection"};
Patrick Williamsd96b81c2023-10-20 11:19:39 -0500644 auto it = std::find_if(
645 usageList.begin(), usageList.end(),
646 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
Marri Devender Rao76411052019-08-07 01:25:07 -0500647 return it != usageList.end();
648}
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800649EVPPkeyPtr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500650{
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500651 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500652 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500653 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500654 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500655 lg2::info("KeyBitLength is not given.Hence, using default KeyBitLength:"
656 "{DEFAULTKEYBITLENGTH}",
657 "DEFAULTKEYBITLENGTH", defaultKeyBitLength);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800658 keyBitLen = defaultKeyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500659 }
Patrick Williams26fb83e2021-12-14 14:08:28 -0600660
661#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
662
663 // generate rsa key
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800664 BignumPtr bne(BN_new(), ::BN_free);
Patrick Williams26fb83e2021-12-14 14:08:28 -0600665 auto ret = BN_set_word(bne.get(), RSA_F4);
666 if (ret == 0)
667 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500668 lg2::error("Error occurred during BN_set_word call");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600669 elog<InternalFailure>();
670 }
Nan Zhou762da742022-01-14 17:21:44 -0800671 using RSAPtr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
672 RSAPtr rsa(RSA_new(), ::RSA_free);
673 ret = RSA_generate_key_ex(rsa.get(), keyBitLen, bne.get(), nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500674 if (ret != 1)
675 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500676 lg2::error(
677 "Error occurred during RSA_generate_key_ex call: {KEYBITLENGTH}",
678 "KEYBITLENGTH", keyBitLen);
Marri Devender Raof4682712019-03-19 05:00:28 -0500679 elog<InternalFailure>();
680 }
681
682 // set public key of x509 req
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800683 EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Nan Zhou762da742022-01-14 17:21:44 -0800684 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa.get());
Marri Devender Raof4682712019-03-19 05:00:28 -0500685 if (ret == 0)
686 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500687 lg2::error("Error occurred during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500688 elog<InternalFailure>();
689 }
Nan Zhou762da742022-01-14 17:21:44 -0800690 // Now |rsa| is managed by |pKey|
691 rsa.release();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500692 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600693
694#else
695 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
696 EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
697 if (!ctx)
698 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500699 lg2::error("Error occurred creating EVP_PKEY_CTX from algorithm");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600700 elog<InternalFailure>();
701 }
702
703 if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
704 (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
705
706 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500707 lg2::error("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600708 elog<InternalFailure>();
709 }
710
711 EVP_PKEY* pKey = nullptr;
712 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
713 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500714 lg2::error("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600715 elog<InternalFailure>();
716 }
717
718 return {pKey, &::EVP_PKEY_free};
719#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500720}
721
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800722EVPPkeyPtr Manager::generateECKeyPair(const std::string& curveId)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500723{
724 std::string curId(curveId);
725
726 if (curId.empty())
727 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500728 lg2::info("KeyCurveId is not given. Hence using default curve id,"
729 "DEFAULTKEYCURVEID:{DEFAULTKEYCURVEID}",
730 "DEFAULTKEYCURVEID", defaultKeyCurveID);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800731 curId = defaultKeyCurveID;
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500732 }
733
734 int ecGrp = OBJ_txt2nid(curId.c_str());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500735 if (ecGrp == NID_undef)
736 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500737 lg2::error(
738 "Error occurred during convert the curve id string format into NID,"
739 "KEYCURVEID:{KEYCURVEID}",
740 "KEYCURVEID", curId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500741 elog<InternalFailure>();
742 }
743
Patrick Williams26fb83e2021-12-14 14:08:28 -0600744#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
745
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500746 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
747
Nan Zhoucfb58022021-12-28 11:02:26 -0800748 if (ecKey == nullptr)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500749 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500750 lg2::error(
751 "Error occurred during create the EC_Key object from NID, ECGROUP:{ECGROUP}",
752 "ECGROUP", ecGrp);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500753 elog<InternalFailure>();
754 }
755
756 // If you want to save a key and later load it with
757 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
758 // flag on the key.
759 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
760
761 int ret = EC_KEY_generate_key(ecKey);
762
763 if (ret == 0)
764 {
765 EC_KEY_free(ecKey);
Ravi Tejaf2646272023-09-30 13:00:55 -0500766 lg2::error("Error occurred during generate EC key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500767 elog<InternalFailure>();
768 }
769
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800770 EVPPkeyPtr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500771 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
772 if (ret == 0)
773 {
774 EC_KEY_free(ecKey);
Ravi Tejaf2646272023-09-30 13:00:55 -0500775 lg2::error("Error occurred during assign EC Key into EVP");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500776 elog<InternalFailure>();
777 }
778
779 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600780
781#else
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000782 auto holderOfKey = [](EVP_PKEY* key) {
Patrick Williams26fb83e2021-12-14 14:08:28 -0600783 return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
784 key, &::EVP_PKEY_free};
785 };
786
787 // Create context to set up curve parameters.
788 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
789 EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
790 if (!ctx)
791 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500792 lg2::error("Error occurred creating EVP_PKEY_CTX for params");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600793 elog<InternalFailure>();
794 }
795
796 // Set up curve parameters.
797 EVP_PKEY* params = nullptr;
798
799 if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
800 (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
801 0) ||
802 (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
803 (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
804 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500805 lg2::error("Error occurred setting curve parameters");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600806 elog<InternalFailure>();
807 }
808
809 // Move parameters to RAII holder.
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000810 auto pparms = holderOfKey(params);
Patrick Williams26fb83e2021-12-14 14:08:28 -0600811
812 // Create new context for key.
813 ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
814
815 if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
816 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500817 lg2::error("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600818 elog<InternalFailure>();
819 }
820
821 EVP_PKEY* pKey = nullptr;
822 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
823 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500824 lg2::error("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600825 elog<InternalFailure>();
826 }
827
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000828 return holderOfKey(pKey);
Patrick Williams26fb83e2021-12-14 14:08:28 -0600829#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500830}
831
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800832void Manager::writePrivateKey(const EVPPkeyPtr& pKey,
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500833 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500834{
Ravi Tejaf2646272023-09-30 13:00:55 -0500835 lg2::info("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500836 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500837 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500838
839 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
Nan Zhoucfb58022021-12-28 11:02:26 -0800840 if (fp == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500841 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500842 lg2::error("Error occurred creating private key file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500843 elog<InternalFailure>();
844 }
Patrick Williams223e4602023-05-10 07:51:11 -0500845 int ret = PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0,
846 nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500847 std::fclose(fp);
848 if (ret == 0)
849 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500850 lg2::error("Error occurred while writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500851 elog<InternalFailure>();
852 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500853}
854
855void Manager::addEntry(X509_NAME* x509Name, const char* field,
856 const std::string& bytes)
857{
858 if (bytes.empty())
859 {
860 return;
861 }
862 int ret = X509_NAME_add_entry_by_txt(
863 x509Name, field, MBSTRING_ASC,
864 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
865 if (ret != 1)
866 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500867 lg2::error("Unable to set entry, FIELD:{FIELD}, VALUE:{VALUE}", "FIELD",
868 field, "VALUE", bytes);
Marri Devender Raof4682712019-03-19 05:00:28 -0500869 elog<InternalFailure>();
870 }
871}
872
873void Manager::createCSRObject(const Status& status)
874{
875 if (csrPtr)
876 {
877 csrPtr.reset(nullptr);
878 }
879 auto csrObjectPath = objectPath + '/' + "csr";
880 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
881 certInstallPath.c_str(), status);
882}
883
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800884void Manager::writeCSR(const std::string& filePath, const X509ReqPtr& x509Req)
Marri Devender Raof4682712019-03-19 05:00:28 -0500885{
886 if (fs::exists(filePath))
887 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500888 lg2::info("Removing the existing file, FILENAME:{FILENAME}", "FILENAME",
889 filePath);
Marri Devender Raof4682712019-03-19 05:00:28 -0500890 if (!fs::remove(filePath.c_str()))
891 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500892 lg2::error("Unable to remove the file, FILENAME:{FILENAME}",
893 "FILENAME", filePath);
Marri Devender Raof4682712019-03-19 05:00:28 -0500894 elog<InternalFailure>();
895 }
896 }
897
Nan Zhoucfb58022021-12-28 11:02:26 -0800898 FILE* fp = nullptr;
Marri Devender Raof4682712019-03-19 05:00:28 -0500899
Nan Zhoucfb58022021-12-28 11:02:26 -0800900 if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500901 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500902 lg2::error(
903 "Error opening the file to write the CSR, FILENAME:{FILENAME}",
904 "FILENAME", filePath);
Marri Devender Raof4682712019-03-19 05:00:28 -0500905 elog<InternalFailure>();
906 }
907
908 int rc = PEM_write_X509_REQ(fp, x509Req.get());
909 if (!rc)
910 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500911 lg2::error("PEM write routine failed, FILENAME:{FILENAME}", "FILENAME",
912 filePath);
Marri Devender Raof4682712019-03-19 05:00:28 -0500913 std::fclose(fp);
914 elog<InternalFailure>();
915 }
916 std::fclose(fp);
917}
918
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200919void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500920{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200921 auto certObjectPath = objectPath + '/';
922
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000923 if (certType == CertificateType::authority)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500924 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100925 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200926 if (!fs::is_directory(certInstallPath))
927 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500928 lg2::error("Certificate installation path exists and it is "
929 "not a directory");
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200930 elog<InternalFailure>();
Nan Zhou6ec13c82021-12-30 11:34:50 -0800931 }
932
933 // If the authorities list exists, recover from it and return
Patrick Williams223e4602023-05-10 07:51:11 -0500934 if (fs::path authoritiesListFilePath = fs::path(certInstallPath) /
935 defaultAuthoritiesListFileName;
Nan Zhou6ec13c82021-12-30 11:34:50 -0800936 fs::exists(authoritiesListFilePath))
937 {
938 // remove all other files and directories
939 for (auto& path : fs::directory_iterator(certInstallPath))
940 {
941 if (path.path() != authoritiesListFilePath)
942 {
943 fs::remove_all(path);
944 }
945 }
946 installAll(authoritiesListFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200947 return;
948 }
949
950 for (auto& path : fs::directory_iterator(certInstallPath))
951 {
952 try
953 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100954 // Assume here any regular file located in certificate directory
955 // contains certificates body. Do not want to use soft links
956 // would add value.
957 if (fs::is_regular_file(path))
958 {
959 installedCerts.emplace_back(std::make_unique<Certificate>(
960 bus, certObjectPath + std::to_string(certIdCounter++),
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800961 certType, certInstallPath, path.path(),
Willy Tu698a5742022-09-23 21:33:01 +0000962 certWatchPtr.get(), *this, /*restore=*/true));
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100963 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200964 }
965 catch (const InternalFailure& e)
966 {
967 report<InternalFailure>();
968 }
969 catch (const InvalidCertificate& e)
970 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800971 report<InvalidCertificate>(InvalidCertificateReason(
972 "Existing certificate file is corrupted"));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200973 }
974 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500975 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200976 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500977 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200978 try
979 {
980 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100981 bus, certObjectPath + '1', certType, certInstallPath,
Willy Tu698a5742022-09-23 21:33:01 +0000982 certInstallPath, certWatchPtr.get(), *this, /*restore=*/false));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200983 }
984 catch (const InternalFailure& e)
985 {
986 report<InternalFailure>();
987 }
988 catch (const InvalidCertificate& e)
989 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800990 report<InvalidCertificate>(InvalidCertificateReason(
991 "Existing certificate file is corrupted"));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200992 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500993 }
994}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500995
996void Manager::createRSAPrivateKeyFile()
997{
Patrick Williams223e4602023-05-10 07:51:11 -0500998 fs::path rsaPrivateKeyFileName = certParentInstallPath /
999 defaultRSAPrivateKeyFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001000
1001 try
1002 {
1003 if (!fs::exists(rsaPrivateKeyFileName))
1004 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001005 writePrivateKey(generateRSAKeyPair(supportedKeyBitLength),
Nan Zhou718eef32021-12-28 11:03:30 -08001006 defaultRSAPrivateKeyFileName);
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001007 }
1008 }
1009 catch (const InternalFailure& e)
1010 {
1011 report<InternalFailure>();
1012 }
1013}
1014
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001015EVPPkeyPtr Manager::getRSAKeyPair(const int64_t keyBitLength)
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001016{
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001017 if (keyBitLength != supportedKeyBitLength)
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001018 {
Ravi Tejaf2646272023-09-30 13:00:55 -05001019 lg2::error(
1020 "Given Key bit length is not supported, GIVENKEYBITLENGTH:"
1021 "{GIVENKEYBITLENGTH}, SUPPORTEDKEYBITLENGTH:{SUPPORTEDKEYBITLENGTH}",
1022 "GIVENKEYBITLENGTH", keyBitLength, "SUPPORTEDKEYBITLENGTH",
1023 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 {
Ravi Tejaf2646272023-09-30 13:00:55 -05001034 lg2::error(
1035 "Unable to open RSA private key file to read, RSAKEYFILE:{RSAKEYFILE},"
1036 "ERRORREASON:{ERRORREASON}",
1037 "RSAKEYFILE", rsaPrivateKeyFileName, "ERRORREASON",
1038 strerror(errno));
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001039 elog<InternalFailure>();
1040 }
1041
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001042 EVPPkeyPtr privateKey(
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001043 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
1044 ::EVP_PKEY_free);
1045 std::fclose(privateKeyFile);
1046
1047 if (!privateKey)
1048 {
Ravi Tejaf2646272023-09-30 13:00:55 -05001049 lg2::error("Error occurred during PEM_read_PrivateKey call");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -05001050 elog<InternalFailure>();
1051 }
1052 return privateKey;
1053}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001054
1055void Manager::storageUpdate()
1056{
Nan Zhoue3d47cd2022-09-16 03:41:53 +00001057 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001058 {
1059 // Remove symbolic links in the certificate directory
1060 for (auto& certPath : fs::directory_iterator(certInstallPath))
1061 {
1062 try
1063 {
1064 if (fs::is_symlink(certPath))
1065 {
1066 fs::remove(certPath);
1067 }
1068 }
1069 catch (const std::exception& e)
1070 {
Ravi Tejaf2646272023-09-30 13:00:55 -05001071 lg2::error(
1072 "Failed to remove symlink for certificate, ERR:{ERR} SYMLINK:{SYMLINK}",
1073 "ERR", e, "SYMLINK", certPath.path().string());
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001074 elog<InternalFailure>();
1075 }
1076 }
1077 }
1078
1079 for (const auto& cert : installedCerts)
1080 {
1081 cert->storageUpdate();
1082 }
1083}
1084
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001085void Manager::reloadOrReset(const std::string& unit)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001086{
1087 if (!unit.empty())
1088 {
1089 try
1090 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001091 constexpr auto defaultSystemdService = "org.freedesktop.systemd1";
1092 constexpr auto defaultSystemdObjectPath =
1093 "/org/freedesktop/systemd1";
1094 constexpr auto defaultSystemdInterface =
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001095 "org.freedesktop.systemd1.Manager";
Nan Zhoucf06ccd2021-12-28 16:25:45 -08001096 auto method = bus.new_method_call(
1097 defaultSystemdService, defaultSystemdObjectPath,
1098 defaultSystemdInterface, "ReloadOrRestartUnit");
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001099 method.append(unit, "replace");
1100 bus.call_noreply(method);
1101 }
Patrick Williamsb3dbfb32022-07-22 19:26:57 -05001102 catch (const sdbusplus::exception_t& e)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001103 {
Ravi Tejaf2646272023-09-30 13:00:55 -05001104 lg2::error(
1105 "Failed to reload or restart service, ERR:{ERR}, UNIT:{UNIT}",
1106 "ERR", e, "UNIT", unit);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001107 elog<InternalFailure>();
1108 }
1109 }
1110}
1111
1112bool Manager::isCertificateUnique(const std::string& filePath,
1113 const Certificate* const certToDrop)
1114{
1115 if (std::any_of(
1116 installedCerts.begin(), installedCerts.end(),
Patrick Williams223e4602023-05-10 07:51:11 -05001117 [&filePath, certToDrop](const std::unique_ptr<Certificate>& cert) {
1118 return cert.get() != certToDrop && cert->isSame(filePath);
Patrick Williamsd96b81c2023-10-20 11:19:39 -05001119 }))
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +01001120 {
1121 return false;
1122 }
1123 else
1124 {
1125 return true;
1126 }
1127}
1128
Nan Zhoue1289ad2021-12-28 11:02:56 -08001129} // namespace phosphor::certs