blob: 9a81991d0516c1b1137040abf994f890530c8f2c [file] [log] [blame]
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05001#include "certs_manager.hpp"
2
Patrick Williams26fb83e2021-12-14 14:08:28 -06003#include <openssl/evp.h>
Marri Devender Raof4682712019-03-19 05:00:28 -05004#include <openssl/pem.h>
5#include <unistd.h>
6
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +02007#include <algorithm>
Marri Devender Rao6ceec402019-02-01 03:15:19 -06008#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -05009#include <xyz/openbmc_project/Certs/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050010#include <xyz/openbmc_project/Common/error.hpp>
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010011
Nan Zhoue1289ad2021-12-28 11:02:56 -080012namespace phosphor::certs
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050013{
Marri Devender Rao13965112019-02-27 08:47:12 -060014using InternalFailure =
15 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050016using InvalidCertificate =
17 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
18using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050019
Marri Devender Raof4682712019-03-19 05:00:28 -050020using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
21using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050022using InvalidArgument =
23 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
24using Argument = xyz::openbmc_project::Common::InvalidArgument;
25
26constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
Marri Devender Raof4682712019-03-19 05:00:28 -050027
28Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
29 const char* path, const CertificateType& type,
30 UnitsToRestart&& unit, CertInstallPath&& installPath) :
Marri Devender Rao6ceec402019-02-01 03:15:19 -060031 Ifaces(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -050032 bus(bus), event(event), objectPath(path), certType(type),
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050033 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
34 certParentInstallPath(fs::path(certInstallPath).parent_path())
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050035{
Marri Devender Raob57d75e2019-07-25 04:56:21 -050036 try
37 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050038 // Create certificate directory if not existing.
Nan Zhoubf3cf752021-12-28 11:02:07 -080039 // Set correct certificate directory permissions.
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050040 fs::path certDirectory;
41 try
Marri Devender Raob57d75e2019-07-25 04:56:21 -050042 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050043 if (certType == AUTHORITY)
44 {
45 certDirectory = certInstallPath;
46 }
47 else
48 {
49 certDirectory = certParentInstallPath;
50 }
51
52 if (!fs::exists(certDirectory))
53 {
54 fs::create_directories(certDirectory);
55 }
56
57 auto permission = fs::perms::owner_read | fs::perms::owner_write |
58 fs::perms::owner_exec;
59 fs::permissions(certDirectory, permission,
60 fs::perm_options::replace);
61 storageUpdate();
62 }
Patrick Williams71957992021-10-06 14:42:52 -050063 catch (const fs::filesystem_error& e)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050064 {
65 log<level::ERR>(
66 "Failed to create directory", entry("ERR=%s", e.what()),
67 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
68 report<InternalFailure>();
69 }
70
71 // Generating RSA private key file if certificate type is server/client
72 if (certType != AUTHORITY)
73 {
74 createRSAPrivateKeyFile();
75 }
76
77 // restore any existing certificates
78 createCertificates();
79
80 // watch is not required for authority certificates
81 if (certType != AUTHORITY)
82 {
83 // watch for certificate file create/replace
84 certWatchPtr = std::make_unique<
85 Watch>(event, certInstallPath, [this]() {
86 try
87 {
88 // if certificate file existing update it
89 if (!installedCerts.empty())
90 {
91 log<level::INFO>("Inotify callback to update "
92 "certificate properties");
93 installedCerts[0]->populateProperties();
94 }
95 else
96 {
97 log<level::INFO>(
98 "Inotify callback to create certificate object");
99 createCertificates();
100 }
101 }
102 catch (const InternalFailure& e)
103 {
104 commit<InternalFailure>();
105 }
106 catch (const InvalidCertificate& e)
107 {
108 commit<InvalidCertificate>();
109 }
110 });
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500111 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100112 else
113 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500114 try
115 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500116 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
117 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500118 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500119 log<level::NOTICE>(
120 "Legacy certificate detected, will be installed from: ",
121 entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
122 install(singleCertPath);
123 if (!fs::remove(singleCertPath))
124 {
125 log<level::ERR>(
126 "Unable to remove old certificate from: ",
127 entry("SINGLE_CERTPATH=%s",
128 singleCertPath.c_str()));
129 elog<InternalFailure>();
130 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500131 }
132 }
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500133 catch (const std::exception& ex)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500134 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500135 log<level::ERR>("Error in restoring legacy certificate",
136 entry("ERROR_STR=%s", ex.what()));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200137 }
138 }
139 }
Patrick Williams71957992021-10-06 14:42:52 -0500140 catch (const std::exception& ex)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500141 {
142 log<level::ERR>("Error in certificate manager constructor",
143 entry("ERROR_STR=%s", ex.what()));
144 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500145}
146
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200147std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500148{
Marri Devender Rao13965112019-02-27 08:47:12 -0600149 using NotAllowed =
150 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
151 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200152
153 if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600154 {
155 elog<NotAllowed>(Reason("Certificate already exist"));
156 }
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200157 else if (certType == phosphor::certs::AUTHORITY &&
Nan Zhou718eef32021-12-28 11:03:30 -0800158 installedCerts.size() >= maxNumAuthorityCertificates)
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200159 {
160 elog<NotAllowed>(Reason("Certificates limit reached"));
161 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500162
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100163 std::string certObjectPath;
164 if (isCertificateUnique(filePath))
165 {
166 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
167 installedCerts.emplace_back(std::make_unique<Certificate>(
168 bus, certObjectPath, certType, certInstallPath, filePath,
169 certWatchPtr, *this));
170 reloadOrReset(unitToRestart);
171 certIdCounter++;
172 }
173 else
174 {
175 elog<NotAllowed>(Reason("Certificate already exist"));
176 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200177
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200178 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500179}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500180
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200181void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500182{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600183 // TODO: #Issue 4 when a certificate is deleted system auto generates
184 // certificate file. At present we are not supporting creation of
185 // certificate object for the auto-generated certificate file as
186 // deletion if only applicable for REST server and Bmcweb does not allow
187 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200188 installedCerts.clear();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100189 storageUpdate();
190 reloadOrReset(unitToRestart);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500191}
Marri Devender Raof4682712019-03-19 05:00:28 -0500192
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100193void Manager::deleteCertificate(const Certificate* const certificate)
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200194{
195 std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
196 std::find_if(installedCerts.begin(), installedCerts.end(),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100197 [certificate](std::unique_ptr<Certificate> const& cert) {
198 return (cert.get() == certificate);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200199 });
200 if (certIt != installedCerts.end())
201 {
202 installedCerts.erase(certIt);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100203 storageUpdate();
204 reloadOrReset(unitToRestart);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200205 }
206 else
207 {
208 log<level::ERR>("Certificate does not exist",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100209 entry("ID=%s", certificate->getCertId().c_str()));
210 elog<InternalFailure>();
211 }
212}
213
214void Manager::replaceCertificate(Certificate* const certificate,
215 const std::string& filePath)
216{
217 if (isCertificateUnique(filePath, certificate))
218 {
219 certificate->install(filePath);
220 storageUpdate();
221 reloadOrReset(unitToRestart);
222 }
223 else
224 {
Zbigniew Lukwinski15cbbec2020-01-16 13:50:45 +0100225 using NotAllowed =
226 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
227 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
228
229 elog<NotAllowed>(Reason("Certificate already exist"));
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200230 }
231}
232
Marri Devender Raof4682712019-03-19 05:00:28 -0500233std::string Manager::generateCSR(
234 std::vector<std::string> alternativeNames, std::string challengePassword,
235 std::string city, std::string commonName, std::string contactPerson,
236 std::string country, std::string email, std::string givenName,
237 std::string initials, int64_t keyBitLength, std::string keyCurveId,
238 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
239 std::string organization, std::string organizationalUnit, std::string state,
240 std::string surname, std::string unstructuredName)
241{
242 // We support only one CSR.
243 csrPtr.reset(nullptr);
244 auto pid = fork();
245 if (pid == -1)
246 {
247 log<level::ERR>("Error occurred during forking process");
248 report<InternalFailure>();
249 }
250 else if (pid == 0)
251 {
252 try
253 {
254 generateCSRHelper(alternativeNames, challengePassword, city,
255 commonName, contactPerson, country, email,
256 givenName, initials, keyBitLength, keyCurveId,
257 keyPairAlgorithm, keyUsage, organization,
258 organizationalUnit, state, surname,
259 unstructuredName);
260 exit(EXIT_SUCCESS);
261 }
262 catch (const InternalFailure& e)
263 {
264 // commit the error reported in child process and exit
265 // Callback method from SDEvent Loop looks for exit status
266 exit(EXIT_FAILURE);
267 commit<InternalFailure>();
268 }
Ramesh Iyyard2393f22020-10-29 09:46:51 -0500269 catch (const InvalidArgument& e)
270 {
271 // commit the error reported in child process and exit
272 // Callback method from SDEvent Loop looks for exit status
273 exit(EXIT_FAILURE);
274 commit<InvalidArgument>();
275 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500276 }
277 else
278 {
279 using namespace sdeventplus::source;
280 Child::Callback callback = [this](Child& eventSource,
281 const siginfo_t* si) {
282 eventSource.set_enabled(Enabled::On);
283 if (si->si_status != 0)
284 {
285 this->createCSRObject(Status::FAILURE);
286 }
287 else
288 {
289 this->createCSRObject(Status::SUCCESS);
290 }
291 };
292 try
293 {
294 sigset_t ss;
295 if (sigemptyset(&ss) < 0)
296 {
297 log<level::ERR>("Unable to initialize signal set");
298 elog<InternalFailure>();
299 }
300 if (sigaddset(&ss, SIGCHLD) < 0)
301 {
302 log<level::ERR>("Unable to add signal to signal set");
303 elog<InternalFailure>();
304 }
305
306 // Block SIGCHLD first, so that the event loop can handle it
Nan Zhoucfb58022021-12-28 11:02:26 -0800307 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500308 {
309 log<level::ERR>("Unable to block signal");
310 elog<InternalFailure>();
311 }
312 if (childPtr)
313 {
314 childPtr.reset();
315 }
316 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
317 std::move(callback));
318 }
319 catch (const InternalFailure& e)
320 {
321 commit<InternalFailure>();
322 }
323 }
324 auto csrObjectPath = objectPath + '/' + "csr";
325 return csrObjectPath;
326}
327
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200328std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500329{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200330 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500331}
332
Marri Devender Raof4682712019-03-19 05:00:28 -0500333void Manager::generateCSRHelper(
334 std::vector<std::string> alternativeNames, std::string challengePassword,
335 std::string city, std::string commonName, std::string contactPerson,
336 std::string country, std::string email, std::string givenName,
337 std::string initials, int64_t keyBitLength, std::string keyCurveId,
338 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
339 std::string organization, std::string organizationalUnit, std::string state,
340 std::string surname, std::string unstructuredName)
341{
342 int ret = 0;
343
344 // set version of x509 req
345 int nVersion = 1;
346 // TODO: Issue#6 need to make version number configurable
347 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
348 ret = X509_REQ_set_version(x509Req.get(), nVersion);
349 if (ret == 0)
350 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800351 log<level::ERR>("Error occurred during X509_REQ_set_version call");
Marri Devender Raof4682712019-03-19 05:00:28 -0500352 elog<InternalFailure>();
353 }
354
355 // set subject of x509 req
356 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
357
358 if (!alternativeNames.empty())
359 {
360 for (auto& name : alternativeNames)
361 {
362 addEntry(x509Name, "subjectAltName", name);
363 }
364 }
365 addEntry(x509Name, "challengePassword", challengePassword);
366 addEntry(x509Name, "L", city);
367 addEntry(x509Name, "CN", commonName);
368 addEntry(x509Name, "name", contactPerson);
369 addEntry(x509Name, "C", country);
370 addEntry(x509Name, "emailAddress", email);
371 addEntry(x509Name, "GN", givenName);
372 addEntry(x509Name, "initials", initials);
373 addEntry(x509Name, "algorithm", keyPairAlgorithm);
374 if (!keyUsage.empty())
375 {
376 for (auto& usage : keyUsage)
377 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500378 if (isExtendedKeyUsage(usage))
379 {
380 addEntry(x509Name, "extendedKeyUsage", usage);
381 }
382 else
383 {
384 addEntry(x509Name, "keyUsage", usage);
385 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500386 }
387 }
388 addEntry(x509Name, "O", organization);
Jayanth Othayothdc91fb62021-05-04 23:17:47 -0500389 addEntry(x509Name, "OU", organizationalUnit);
Marri Devender Raof4682712019-03-19 05:00:28 -0500390 addEntry(x509Name, "ST", state);
391 addEntry(x509Name, "SN", surname);
392 addEntry(x509Name, "unstructuredName", unstructuredName);
393
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500394 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
395
396 log<level::INFO>("Given Key pair algorithm",
397 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
398
399 // Used EC algorithm as default if user did not give algorithm type.
400 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500401 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500402 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500403 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500404 else
405 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500406 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
407 "RSA and EC only");
408 elog<InvalidArgument>(
409 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
410 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
411 }
412
413 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
414 if (ret == 0)
415 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800416 log<level::ERR>("Error occurred while setting Public key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500417 elog<InternalFailure>();
418 }
419
420 // Write private key to file
Nan Zhou718eef32021-12-28 11:03:30 -0800421 writePrivateKey(pKey, defaultPrivateKeyFileName);
Marri Devender Raof4682712019-03-19 05:00:28 -0500422
423 // set sign key of x509 req
424 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500425 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500426 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800427 log<level::ERR>("Error occurred while signing key of x509");
Marri Devender Raof4682712019-03-19 05:00:28 -0500428 elog<InternalFailure>();
429 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500430
Marri Devender Raof4682712019-03-19 05:00:28 -0500431 log<level::INFO>("Writing CSR to file");
Nan Zhou718eef32021-12-28 11:03:30 -0800432 fs::path csrFilePath = certParentInstallPath / defaultCSRFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500433 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500434}
435
Marri Devender Rao76411052019-08-07 01:25:07 -0500436bool Manager::isExtendedKeyUsage(const std::string& usage)
437{
438 const static std::array<const char*, 6> usageList = {
439 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
440 "Timestamping", "CodeSigning", "EmailProtection"};
441 auto it = std::find_if(
442 usageList.begin(), usageList.end(),
443 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
444 return it != usageList.end();
445}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500446EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500447{
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500448 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500449 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500450 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500451 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500452 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
453 log<level::INFO>(
454 "KeyBitLength is not given.Hence, using default KeyBitLength",
455 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
456 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500457 }
Patrick Williams26fb83e2021-12-14 14:08:28 -0600458
459#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
460
461 // generate rsa key
462 BIGNUM_Ptr bne(BN_new(), ::BN_free);
463 auto ret = BN_set_word(bne.get(), RSA_F4);
464 if (ret == 0)
465 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800466 log<level::ERR>("Error occurred during BN_set_word call");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600467 elog<InternalFailure>();
468 }
Nan Zhou762da742022-01-14 17:21:44 -0800469 using RSAPtr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
470 RSAPtr rsa(RSA_new(), ::RSA_free);
471 ret = RSA_generate_key_ex(rsa.get(), keyBitLen, bne.get(), nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500472 if (ret != 1)
473 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800474 log<level::ERR>("Error occurred during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500475 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500476 elog<InternalFailure>();
477 }
478
479 // set public key of x509 req
480 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Nan Zhou762da742022-01-14 17:21:44 -0800481 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa.get());
Marri Devender Raof4682712019-03-19 05:00:28 -0500482 if (ret == 0)
483 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800484 log<level::ERR>("Error occurred during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500485 elog<InternalFailure>();
486 }
Nan Zhou762da742022-01-14 17:21:44 -0800487 // Now |rsa| is managed by |pKey|
488 rsa.release();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500489 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600490
491#else
492 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
493 EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
494 if (!ctx)
495 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800496 log<level::ERR>("Error occurred creating EVP_PKEY_CTX from algorithm");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600497 elog<InternalFailure>();
498 }
499
500 if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
501 (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
502
503 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800504 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600505 elog<InternalFailure>();
506 }
507
508 EVP_PKEY* pKey = nullptr;
509 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
510 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800511 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600512 elog<InternalFailure>();
513 }
514
515 return {pKey, &::EVP_PKEY_free};
516#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500517}
518
519EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
520{
521 std::string curId(curveId);
522
523 if (curId.empty())
524 {
525 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
526 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
527 log<level::INFO>(
528 "KeyCurveId is not given. Hence using default curve id",
529 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
530 curId = DEFAULT_KEYCURVEID;
531 }
532
533 int ecGrp = OBJ_txt2nid(curId.c_str());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500534 if (ecGrp == NID_undef)
535 {
536 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800537 "Error occurred during convert the curve id string format into NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500538 entry("KEYCURVEID=%s", curId.c_str()));
539 elog<InternalFailure>();
540 }
541
Patrick Williams26fb83e2021-12-14 14:08:28 -0600542#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
543
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500544 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
545
Nan Zhoucfb58022021-12-28 11:02:26 -0800546 if (ecKey == nullptr)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500547 {
548 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800549 "Error occurred during create the EC_Key object from NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500550 entry("ECGROUP=%d", ecGrp));
551 elog<InternalFailure>();
552 }
553
554 // If you want to save a key and later load it with
555 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
556 // flag on the key.
557 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
558
559 int ret = EC_KEY_generate_key(ecKey);
560
561 if (ret == 0)
562 {
563 EC_KEY_free(ecKey);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800564 log<level::ERR>("Error occurred during generate EC key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500565 elog<InternalFailure>();
566 }
567
568 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
569 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
570 if (ret == 0)
571 {
572 EC_KEY_free(ecKey);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800573 log<level::ERR>("Error occurred during assign EC Key into EVP");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500574 elog<InternalFailure>();
575 }
576
577 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600578
579#else
580 auto holder_of_key = [](EVP_PKEY* key) {
581 return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
582 key, &::EVP_PKEY_free};
583 };
584
585 // Create context to set up curve parameters.
586 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
587 EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
588 if (!ctx)
589 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800590 log<level::ERR>("Error occurred creating EVP_PKEY_CTX for params");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600591 elog<InternalFailure>();
592 }
593
594 // Set up curve parameters.
595 EVP_PKEY* params = nullptr;
596
597 if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
598 (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
599 0) ||
600 (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
601 (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
602 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800603 log<level::ERR>("Error occurred setting curve parameters");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600604 elog<InternalFailure>();
605 }
606
607 // Move parameters to RAII holder.
608 auto pparms = holder_of_key(params);
609
610 // Create new context for key.
611 ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
612
613 if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
614 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800615 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600616 elog<InternalFailure>();
617 }
618
619 EVP_PKEY* pKey = nullptr;
620 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
621 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800622 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600623 elog<InternalFailure>();
624 }
625
626 return holder_of_key(pKey);
627#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500628}
629
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500630void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
631 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500632{
633 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500634 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500635 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500636
637 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
Nan Zhoucfb58022021-12-28 11:02:26 -0800638 if (fp == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500639 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800640 log<level::ERR>("Error occurred creating private key file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500641 elog<InternalFailure>();
642 }
Nan Zhoucfb58022021-12-28 11:02:26 -0800643 int ret =
644 PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0, nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500645 std::fclose(fp);
646 if (ret == 0)
647 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800648 log<level::ERR>("Error occurred while writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500649 elog<InternalFailure>();
650 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500651}
652
653void Manager::addEntry(X509_NAME* x509Name, const char* field,
654 const std::string& bytes)
655{
656 if (bytes.empty())
657 {
658 return;
659 }
660 int ret = X509_NAME_add_entry_by_txt(
661 x509Name, field, MBSTRING_ASC,
662 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
663 if (ret != 1)
664 {
665 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
666 entry("VALUE=%s", bytes.c_str()));
667 elog<InternalFailure>();
668 }
669}
670
671void Manager::createCSRObject(const Status& status)
672{
673 if (csrPtr)
674 {
675 csrPtr.reset(nullptr);
676 }
677 auto csrObjectPath = objectPath + '/' + "csr";
678 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
679 certInstallPath.c_str(), status);
680}
681
682void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
683{
684 if (fs::exists(filePath))
685 {
686 log<level::INFO>("Removing the existing file",
687 entry("FILENAME=%s", filePath.c_str()));
688 if (!fs::remove(filePath.c_str()))
689 {
690 log<level::ERR>("Unable to remove the file",
691 entry("FILENAME=%s", filePath.c_str()));
692 elog<InternalFailure>();
693 }
694 }
695
Nan Zhoucfb58022021-12-28 11:02:26 -0800696 FILE* fp = nullptr;
Marri Devender Raof4682712019-03-19 05:00:28 -0500697
Nan Zhoucfb58022021-12-28 11:02:26 -0800698 if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500699 {
700 log<level::ERR>("Error opening the file to write the CSR",
701 entry("FILENAME=%s", filePath.c_str()));
702 elog<InternalFailure>();
703 }
704
705 int rc = PEM_write_X509_REQ(fp, x509Req.get());
706 if (!rc)
707 {
708 log<level::ERR>("PEM write routine failed",
709 entry("FILENAME=%s", filePath.c_str()));
710 std::fclose(fp);
711 elog<InternalFailure>();
712 }
713 std::fclose(fp);
714}
715
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200716void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500717{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200718 auto certObjectPath = objectPath + '/';
719
720 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500721 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100722 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200723 if (!fs::is_directory(certInstallPath))
724 {
725 log<level::ERR>("Certificate installation path exists and it is "
726 "not a directory");
727 elog<InternalFailure>();
728 return;
729 }
730
731 for (auto& path : fs::directory_iterator(certInstallPath))
732 {
733 try
734 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100735 // Assume here any regular file located in certificate directory
736 // contains certificates body. Do not want to use soft links
737 // would add value.
738 if (fs::is_regular_file(path))
739 {
740 installedCerts.emplace_back(std::make_unique<Certificate>(
741 bus, certObjectPath + std::to_string(certIdCounter++),
742 certType, certInstallPath, path.path(), certWatchPtr,
743 *this));
744 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200745 }
746 catch (const InternalFailure& e)
747 {
748 report<InternalFailure>();
749 }
750 catch (const InvalidCertificate& e)
751 {
752 report<InvalidCertificate>(
753 Reason("Existing certificate file is corrupted"));
754 }
755 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500756 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200757 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500758 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200759 try
760 {
761 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100762 bus, certObjectPath + '1', certType, certInstallPath,
763 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200764 }
765 catch (const InternalFailure& e)
766 {
767 report<InternalFailure>();
768 }
769 catch (const InvalidCertificate& e)
770 {
771 report<InvalidCertificate>(
772 Reason("Existing certificate file is corrupted"));
773 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500774 }
775}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500776
777void Manager::createRSAPrivateKeyFile()
778{
779 fs::path rsaPrivateKeyFileName =
Nan Zhou718eef32021-12-28 11:03:30 -0800780 certParentInstallPath / defaultRSAPrivateKeyFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500781
782 try
783 {
784 if (!fs::exists(rsaPrivateKeyFileName))
785 {
786 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
Nan Zhou718eef32021-12-28 11:03:30 -0800787 defaultRSAPrivateKeyFileName);
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500788 }
789 }
790 catch (const InternalFailure& e)
791 {
792 report<InternalFailure>();
793 }
794}
795
796EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
797{
798 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
799 {
800 log<level::ERR>(
801 "Given Key bit length is not supported",
802 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
803 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
804 elog<InvalidArgument>(
805 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
806 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
807 }
808 fs::path rsaPrivateKeyFileName =
Nan Zhou718eef32021-12-28 11:03:30 -0800809 certParentInstallPath / defaultRSAPrivateKeyFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500810
811 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
812 if (!privateKeyFile)
813 {
814 log<level::ERR>("Unable to open RSA private key file to read",
815 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
816 entry("ERRORREASON=%s", strerror(errno)));
817 elog<InternalFailure>();
818 }
819
820 EVP_PKEY_Ptr privateKey(
821 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
822 ::EVP_PKEY_free);
823 std::fclose(privateKeyFile);
824
825 if (!privateKey)
826 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800827 log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500828 elog<InternalFailure>();
829 }
830 return privateKey;
831}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100832
833void Manager::storageUpdate()
834{
835 if (certType == phosphor::certs::AUTHORITY)
836 {
837 // Remove symbolic links in the certificate directory
838 for (auto& certPath : fs::directory_iterator(certInstallPath))
839 {
840 try
841 {
842 if (fs::is_symlink(certPath))
843 {
844 fs::remove(certPath);
845 }
846 }
847 catch (const std::exception& e)
848 {
849 log<level::ERR>(
850 "Failed to remove symlink for certificate",
851 entry("ERR=%s", e.what()),
852 entry("SYMLINK=%s", certPath.path().string().c_str()));
853 elog<InternalFailure>();
854 }
855 }
856 }
857
858 for (const auto& cert : installedCerts)
859 {
860 cert->storageUpdate();
861 }
862}
863
864void Manager::reloadOrReset(const UnitsToRestart& unit)
865{
866 if (!unit.empty())
867 {
868 try
869 {
870 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
871 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
872 constexpr auto SYSTEMD_INTERFACE =
873 "org.freedesktop.systemd1.Manager";
874
875 auto method =
876 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
877 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
878 method.append(unit, "replace");
879 bus.call_noreply(method);
880 }
Patrick Williamsca128112021-09-02 09:36:07 -0500881 catch (const sdbusplus::exception::exception& e)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100882 {
883 log<level::ERR>("Failed to reload or restart service",
884 entry("ERR=%s", e.what()),
885 entry("UNIT=%s", unit.c_str()));
886 elog<InternalFailure>();
887 }
888 }
889}
890
891bool Manager::isCertificateUnique(const std::string& filePath,
892 const Certificate* const certToDrop)
893{
894 if (std::any_of(
895 installedCerts.begin(), installedCerts.end(),
896 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
897 return cert.get() != certToDrop && cert->isSame(filePath);
898 }))
899 {
900 return false;
901 }
902 else
903 {
904 return true;
905 }
906}
907
Nan Zhoue1289ad2021-12-28 11:02:56 -0800908} // namespace phosphor::certs