blob: 6aca6af002ecb1822c1b4422ae7afc9ddab01353 [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
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050012namespace phosphor
13{
14namespace certs
15{
Marri Devender Rao13965112019-02-27 08:47:12 -060016using InternalFailure =
17 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050018using InvalidCertificate =
19 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
20using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050021
Marri Devender Raof4682712019-03-19 05:00:28 -050022using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
23using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050024using InvalidArgument =
25 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
26using Argument = xyz::openbmc_project::Common::InvalidArgument;
27
28constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
Marri Devender Raof4682712019-03-19 05:00:28 -050029
30Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
31 const char* path, const CertificateType& type,
32 UnitsToRestart&& unit, CertInstallPath&& installPath) :
Marri Devender Rao6ceec402019-02-01 03:15:19 -060033 Ifaces(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -050034 bus(bus), event(event), objectPath(path), certType(type),
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050035 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
36 certParentInstallPath(fs::path(certInstallPath).parent_path())
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050037{
Marri Devender Raob57d75e2019-07-25 04:56:21 -050038 try
39 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050040 // Create certificate directory if not existing.
Nan Zhoubf3cf752021-12-28 11:02:07 -080041 // Set correct certificate directory permissions.
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050042 fs::path certDirectory;
43 try
Marri Devender Raob57d75e2019-07-25 04:56:21 -050044 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050045 if (certType == AUTHORITY)
46 {
47 certDirectory = certInstallPath;
48 }
49 else
50 {
51 certDirectory = certParentInstallPath;
52 }
53
54 if (!fs::exists(certDirectory))
55 {
56 fs::create_directories(certDirectory);
57 }
58
59 auto permission = fs::perms::owner_read | fs::perms::owner_write |
60 fs::perms::owner_exec;
61 fs::permissions(certDirectory, permission,
62 fs::perm_options::replace);
63 storageUpdate();
64 }
Patrick Williams71957992021-10-06 14:42:52 -050065 catch (const fs::filesystem_error& e)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050066 {
67 log<level::ERR>(
68 "Failed to create directory", entry("ERR=%s", e.what()),
69 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
70 report<InternalFailure>();
71 }
72
73 // Generating RSA private key file if certificate type is server/client
74 if (certType != AUTHORITY)
75 {
76 createRSAPrivateKeyFile();
77 }
78
79 // restore any existing certificates
80 createCertificates();
81
82 // watch is not required for authority certificates
83 if (certType != AUTHORITY)
84 {
85 // watch for certificate file create/replace
86 certWatchPtr = std::make_unique<
87 Watch>(event, certInstallPath, [this]() {
88 try
89 {
90 // if certificate file existing update it
91 if (!installedCerts.empty())
92 {
93 log<level::INFO>("Inotify callback to update "
94 "certificate properties");
95 installedCerts[0]->populateProperties();
96 }
97 else
98 {
99 log<level::INFO>(
100 "Inotify callback to create certificate object");
101 createCertificates();
102 }
103 }
104 catch (const InternalFailure& e)
105 {
106 commit<InternalFailure>();
107 }
108 catch (const InvalidCertificate& e)
109 {
110 commit<InvalidCertificate>();
111 }
112 });
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500113 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100114 else
115 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500116 try
117 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500118 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
119 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500120 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500121 log<level::NOTICE>(
122 "Legacy certificate detected, will be installed from: ",
123 entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
124 install(singleCertPath);
125 if (!fs::remove(singleCertPath))
126 {
127 log<level::ERR>(
128 "Unable to remove old certificate from: ",
129 entry("SINGLE_CERTPATH=%s",
130 singleCertPath.c_str()));
131 elog<InternalFailure>();
132 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500133 }
134 }
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500135 catch (const std::exception& ex)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500136 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500137 log<level::ERR>("Error in restoring legacy certificate",
138 entry("ERROR_STR=%s", ex.what()));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200139 }
140 }
141 }
Patrick Williams71957992021-10-06 14:42:52 -0500142 catch (const std::exception& ex)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500143 {
144 log<level::ERR>("Error in certificate manager constructor",
145 entry("ERROR_STR=%s", ex.what()));
146 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500147}
148
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200149std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500150{
Marri Devender Rao13965112019-02-27 08:47:12 -0600151 using NotAllowed =
152 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
153 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200154
155 if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600156 {
157 elog<NotAllowed>(Reason("Certificate already exist"));
158 }
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200159 else if (certType == phosphor::certs::AUTHORITY &&
160 installedCerts.size() >= AUTHORITY_CERTIFICATES_LIMIT)
161 {
162 elog<NotAllowed>(Reason("Certificates limit reached"));
163 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500164
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100165 std::string certObjectPath;
166 if (isCertificateUnique(filePath))
167 {
168 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
169 installedCerts.emplace_back(std::make_unique<Certificate>(
170 bus, certObjectPath, certType, certInstallPath, filePath,
171 certWatchPtr, *this));
172 reloadOrReset(unitToRestart);
173 certIdCounter++;
174 }
175 else
176 {
177 elog<NotAllowed>(Reason("Certificate already exist"));
178 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200179
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200180 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500181}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500182
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200183void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500184{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600185 // TODO: #Issue 4 when a certificate is deleted system auto generates
186 // certificate file. At present we are not supporting creation of
187 // certificate object for the auto-generated certificate file as
188 // deletion if only applicable for REST server and Bmcweb does not allow
189 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200190 installedCerts.clear();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100191 storageUpdate();
192 reloadOrReset(unitToRestart);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500193}
Marri Devender Raof4682712019-03-19 05:00:28 -0500194
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100195void Manager::deleteCertificate(const Certificate* const certificate)
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200196{
197 std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
198 std::find_if(installedCerts.begin(), installedCerts.end(),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100199 [certificate](std::unique_ptr<Certificate> const& cert) {
200 return (cert.get() == certificate);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200201 });
202 if (certIt != installedCerts.end())
203 {
204 installedCerts.erase(certIt);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100205 storageUpdate();
206 reloadOrReset(unitToRestart);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200207 }
208 else
209 {
210 log<level::ERR>("Certificate does not exist",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100211 entry("ID=%s", certificate->getCertId().c_str()));
212 elog<InternalFailure>();
213 }
214}
215
216void Manager::replaceCertificate(Certificate* const certificate,
217 const std::string& filePath)
218{
219 if (isCertificateUnique(filePath, certificate))
220 {
221 certificate->install(filePath);
222 storageUpdate();
223 reloadOrReset(unitToRestart);
224 }
225 else
226 {
Zbigniew Lukwinski15cbbec2020-01-16 13:50:45 +0100227 using NotAllowed =
228 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
229 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
230
231 elog<NotAllowed>(Reason("Certificate already exist"));
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200232 }
233}
234
Marri Devender Raof4682712019-03-19 05:00:28 -0500235std::string Manager::generateCSR(
236 std::vector<std::string> alternativeNames, std::string challengePassword,
237 std::string city, std::string commonName, std::string contactPerson,
238 std::string country, std::string email, std::string givenName,
239 std::string initials, int64_t keyBitLength, std::string keyCurveId,
240 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
241 std::string organization, std::string organizationalUnit, std::string state,
242 std::string surname, std::string unstructuredName)
243{
244 // We support only one CSR.
245 csrPtr.reset(nullptr);
246 auto pid = fork();
247 if (pid == -1)
248 {
249 log<level::ERR>("Error occurred during forking process");
250 report<InternalFailure>();
251 }
252 else if (pid == 0)
253 {
254 try
255 {
256 generateCSRHelper(alternativeNames, challengePassword, city,
257 commonName, contactPerson, country, email,
258 givenName, initials, keyBitLength, keyCurveId,
259 keyPairAlgorithm, keyUsage, organization,
260 organizationalUnit, state, surname,
261 unstructuredName);
262 exit(EXIT_SUCCESS);
263 }
264 catch (const InternalFailure& e)
265 {
266 // commit the error reported in child process and exit
267 // Callback method from SDEvent Loop looks for exit status
268 exit(EXIT_FAILURE);
269 commit<InternalFailure>();
270 }
Ramesh Iyyard2393f22020-10-29 09:46:51 -0500271 catch (const InvalidArgument& e)
272 {
273 // commit the error reported in child process and exit
274 // Callback method from SDEvent Loop looks for exit status
275 exit(EXIT_FAILURE);
276 commit<InvalidArgument>();
277 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500278 }
279 else
280 {
281 using namespace sdeventplus::source;
282 Child::Callback callback = [this](Child& eventSource,
283 const siginfo_t* si) {
284 eventSource.set_enabled(Enabled::On);
285 if (si->si_status != 0)
286 {
287 this->createCSRObject(Status::FAILURE);
288 }
289 else
290 {
291 this->createCSRObject(Status::SUCCESS);
292 }
293 };
294 try
295 {
296 sigset_t ss;
297 if (sigemptyset(&ss) < 0)
298 {
299 log<level::ERR>("Unable to initialize signal set");
300 elog<InternalFailure>();
301 }
302 if (sigaddset(&ss, SIGCHLD) < 0)
303 {
304 log<level::ERR>("Unable to add signal to signal set");
305 elog<InternalFailure>();
306 }
307
308 // Block SIGCHLD first, so that the event loop can handle it
Nan Zhoucfb58022021-12-28 11:02:26 -0800309 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500310 {
311 log<level::ERR>("Unable to block signal");
312 elog<InternalFailure>();
313 }
314 if (childPtr)
315 {
316 childPtr.reset();
317 }
318 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
319 std::move(callback));
320 }
321 catch (const InternalFailure& e)
322 {
323 commit<InternalFailure>();
324 }
325 }
326 auto csrObjectPath = objectPath + '/' + "csr";
327 return csrObjectPath;
328}
329
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200330std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500331{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200332 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500333}
334
Marri Devender Raof4682712019-03-19 05:00:28 -0500335void Manager::generateCSRHelper(
336 std::vector<std::string> alternativeNames, std::string challengePassword,
337 std::string city, std::string commonName, std::string contactPerson,
338 std::string country, std::string email, std::string givenName,
339 std::string initials, int64_t keyBitLength, std::string keyCurveId,
340 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
341 std::string organization, std::string organizationalUnit, std::string state,
342 std::string surname, std::string unstructuredName)
343{
344 int ret = 0;
345
346 // set version of x509 req
347 int nVersion = 1;
348 // TODO: Issue#6 need to make version number configurable
349 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
350 ret = X509_REQ_set_version(x509Req.get(), nVersion);
351 if (ret == 0)
352 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800353 log<level::ERR>("Error occurred during X509_REQ_set_version call");
Marri Devender Raof4682712019-03-19 05:00:28 -0500354 elog<InternalFailure>();
355 }
356
357 // set subject of x509 req
358 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
359
360 if (!alternativeNames.empty())
361 {
362 for (auto& name : alternativeNames)
363 {
364 addEntry(x509Name, "subjectAltName", name);
365 }
366 }
367 addEntry(x509Name, "challengePassword", challengePassword);
368 addEntry(x509Name, "L", city);
369 addEntry(x509Name, "CN", commonName);
370 addEntry(x509Name, "name", contactPerson);
371 addEntry(x509Name, "C", country);
372 addEntry(x509Name, "emailAddress", email);
373 addEntry(x509Name, "GN", givenName);
374 addEntry(x509Name, "initials", initials);
375 addEntry(x509Name, "algorithm", keyPairAlgorithm);
376 if (!keyUsage.empty())
377 {
378 for (auto& usage : keyUsage)
379 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500380 if (isExtendedKeyUsage(usage))
381 {
382 addEntry(x509Name, "extendedKeyUsage", usage);
383 }
384 else
385 {
386 addEntry(x509Name, "keyUsage", usage);
387 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500388 }
389 }
390 addEntry(x509Name, "O", organization);
Jayanth Othayothdc91fb62021-05-04 23:17:47 -0500391 addEntry(x509Name, "OU", organizationalUnit);
Marri Devender Raof4682712019-03-19 05:00:28 -0500392 addEntry(x509Name, "ST", state);
393 addEntry(x509Name, "SN", surname);
394 addEntry(x509Name, "unstructuredName", unstructuredName);
395
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500396 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
397
398 log<level::INFO>("Given Key pair algorithm",
399 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
400
401 // Used EC algorithm as default if user did not give algorithm type.
402 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500403 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500404 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500405 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500406 else
407 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500408 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
409 "RSA and EC only");
410 elog<InvalidArgument>(
411 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
412 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
413 }
414
415 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
416 if (ret == 0)
417 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800418 log<level::ERR>("Error occurred while setting Public key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500419 elog<InternalFailure>();
420 }
421
422 // Write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500423 writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
Marri Devender Raof4682712019-03-19 05:00:28 -0500424
425 // set sign key of x509 req
426 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500427 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500428 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800429 log<level::ERR>("Error occurred while signing key of x509");
Marri Devender Raof4682712019-03-19 05:00:28 -0500430 elog<InternalFailure>();
431 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500432
Marri Devender Raof4682712019-03-19 05:00:28 -0500433 log<level::INFO>("Writing CSR to file");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500434 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
435 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500436}
437
Marri Devender Rao76411052019-08-07 01:25:07 -0500438bool Manager::isExtendedKeyUsage(const std::string& usage)
439{
440 const static std::array<const char*, 6> usageList = {
441 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
442 "Timestamping", "CodeSigning", "EmailProtection"};
443 auto it = std::find_if(
444 usageList.begin(), usageList.end(),
445 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
446 return it != usageList.end();
447}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500448EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500449{
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500450 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500451 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500452 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500453 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500454 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
455 log<level::INFO>(
456 "KeyBitLength is not given.Hence, using default KeyBitLength",
457 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
458 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500459 }
Patrick Williams26fb83e2021-12-14 14:08:28 -0600460
461#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
462
463 // generate rsa key
464 BIGNUM_Ptr bne(BN_new(), ::BN_free);
465 auto ret = BN_set_word(bne.get(), RSA_F4);
466 if (ret == 0)
467 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800468 log<level::ERR>("Error occurred during BN_set_word call");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600469 elog<InternalFailure>();
470 }
471
Marri Devender Raof4682712019-03-19 05:00:28 -0500472 RSA* rsa = RSA_new();
Nan Zhoucfb58022021-12-28 11:02:26 -0800473 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500474 if (ret != 1)
475 {
476 free(rsa);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800477 log<level::ERR>("Error occurred during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500478 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500479 elog<InternalFailure>();
480 }
481
482 // set public key of x509 req
483 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500484 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500485 if (ret == 0)
486 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500487 free(rsa);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800488 log<level::ERR>("Error occurred during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500489 elog<InternalFailure>();
490 }
491
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500492 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600493
494#else
495 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
496 EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
497 if (!ctx)
498 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800499 log<level::ERR>("Error occurred creating EVP_PKEY_CTX from algorithm");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600500 elog<InternalFailure>();
501 }
502
503 if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
504 (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
505
506 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800507 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600508 elog<InternalFailure>();
509 }
510
511 EVP_PKEY* pKey = nullptr;
512 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
513 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800514 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600515 elog<InternalFailure>();
516 }
517
518 return {pKey, &::EVP_PKEY_free};
519#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500520}
521
522EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
523{
524 std::string curId(curveId);
525
526 if (curId.empty())
527 {
528 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
529 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
530 log<level::INFO>(
531 "KeyCurveId is not given. Hence using default curve id",
532 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
533 curId = DEFAULT_KEYCURVEID;
534 }
535
536 int ecGrp = OBJ_txt2nid(curId.c_str());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500537 if (ecGrp == NID_undef)
538 {
539 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800540 "Error occurred during convert the curve id string format into NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500541 entry("KEYCURVEID=%s", curId.c_str()));
542 elog<InternalFailure>();
543 }
544
Patrick Williams26fb83e2021-12-14 14:08:28 -0600545#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
546
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500547 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
548
Nan Zhoucfb58022021-12-28 11:02:26 -0800549 if (ecKey == nullptr)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500550 {
551 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800552 "Error occurred during create the EC_Key object from NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500553 entry("ECGROUP=%d", ecGrp));
554 elog<InternalFailure>();
555 }
556
557 // If you want to save a key and later load it with
558 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
559 // flag on the key.
560 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
561
562 int ret = EC_KEY_generate_key(ecKey);
563
564 if (ret == 0)
565 {
566 EC_KEY_free(ecKey);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800567 log<level::ERR>("Error occurred during generate EC key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500568 elog<InternalFailure>();
569 }
570
571 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
572 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
573 if (ret == 0)
574 {
575 EC_KEY_free(ecKey);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800576 log<level::ERR>("Error occurred during assign EC Key into EVP");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500577 elog<InternalFailure>();
578 }
579
580 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600581
582#else
583 auto holder_of_key = [](EVP_PKEY* key) {
584 return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
585 key, &::EVP_PKEY_free};
586 };
587
588 // Create context to set up curve parameters.
589 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
590 EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
591 if (!ctx)
592 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800593 log<level::ERR>("Error occurred creating EVP_PKEY_CTX for params");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600594 elog<InternalFailure>();
595 }
596
597 // Set up curve parameters.
598 EVP_PKEY* params = nullptr;
599
600 if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
601 (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
602 0) ||
603 (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
604 (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
605 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800606 log<level::ERR>("Error occurred setting curve parameters");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600607 elog<InternalFailure>();
608 }
609
610 // Move parameters to RAII holder.
611 auto pparms = holder_of_key(params);
612
613 // Create new context for key.
614 ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
615
616 if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
617 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800618 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600619 elog<InternalFailure>();
620 }
621
622 EVP_PKEY* pKey = nullptr;
623 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
624 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800625 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600626 elog<InternalFailure>();
627 }
628
629 return holder_of_key(pKey);
630#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500631}
632
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500633void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
634 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500635{
636 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500637 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500638 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500639
640 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
Nan Zhoucfb58022021-12-28 11:02:26 -0800641 if (fp == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500642 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800643 log<level::ERR>("Error occurred creating private key file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500644 elog<InternalFailure>();
645 }
Nan Zhoucfb58022021-12-28 11:02:26 -0800646 int ret =
647 PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0, nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500648 std::fclose(fp);
649 if (ret == 0)
650 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800651 log<level::ERR>("Error occurred while writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500652 elog<InternalFailure>();
653 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500654}
655
656void Manager::addEntry(X509_NAME* x509Name, const char* field,
657 const std::string& bytes)
658{
659 if (bytes.empty())
660 {
661 return;
662 }
663 int ret = X509_NAME_add_entry_by_txt(
664 x509Name, field, MBSTRING_ASC,
665 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
666 if (ret != 1)
667 {
668 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
669 entry("VALUE=%s", bytes.c_str()));
670 elog<InternalFailure>();
671 }
672}
673
674void Manager::createCSRObject(const Status& status)
675{
676 if (csrPtr)
677 {
678 csrPtr.reset(nullptr);
679 }
680 auto csrObjectPath = objectPath + '/' + "csr";
681 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
682 certInstallPath.c_str(), status);
683}
684
685void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
686{
687 if (fs::exists(filePath))
688 {
689 log<level::INFO>("Removing the existing file",
690 entry("FILENAME=%s", filePath.c_str()));
691 if (!fs::remove(filePath.c_str()))
692 {
693 log<level::ERR>("Unable to remove the file",
694 entry("FILENAME=%s", filePath.c_str()));
695 elog<InternalFailure>();
696 }
697 }
698
Nan Zhoucfb58022021-12-28 11:02:26 -0800699 FILE* fp = nullptr;
Marri Devender Raof4682712019-03-19 05:00:28 -0500700
Nan Zhoucfb58022021-12-28 11:02:26 -0800701 if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500702 {
703 log<level::ERR>("Error opening the file to write the CSR",
704 entry("FILENAME=%s", filePath.c_str()));
705 elog<InternalFailure>();
706 }
707
708 int rc = PEM_write_X509_REQ(fp, x509Req.get());
709 if (!rc)
710 {
711 log<level::ERR>("PEM write routine failed",
712 entry("FILENAME=%s", filePath.c_str()));
713 std::fclose(fp);
714 elog<InternalFailure>();
715 }
716 std::fclose(fp);
717}
718
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200719void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500720{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200721 auto certObjectPath = objectPath + '/';
722
723 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500724 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100725 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200726 if (!fs::is_directory(certInstallPath))
727 {
728 log<level::ERR>("Certificate installation path exists and it is "
729 "not a directory");
730 elog<InternalFailure>();
731 return;
732 }
733
734 for (auto& path : fs::directory_iterator(certInstallPath))
735 {
736 try
737 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100738 // Assume here any regular file located in certificate directory
739 // contains certificates body. Do not want to use soft links
740 // would add value.
741 if (fs::is_regular_file(path))
742 {
743 installedCerts.emplace_back(std::make_unique<Certificate>(
744 bus, certObjectPath + std::to_string(certIdCounter++),
745 certType, certInstallPath, path.path(), certWatchPtr,
746 *this));
747 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200748 }
749 catch (const InternalFailure& e)
750 {
751 report<InternalFailure>();
752 }
753 catch (const InvalidCertificate& e)
754 {
755 report<InvalidCertificate>(
756 Reason("Existing certificate file is corrupted"));
757 }
758 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500759 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200760 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500761 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200762 try
763 {
764 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100765 bus, certObjectPath + '1', certType, certInstallPath,
766 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200767 }
768 catch (const InternalFailure& e)
769 {
770 report<InternalFailure>();
771 }
772 catch (const InvalidCertificate& e)
773 {
774 report<InvalidCertificate>(
775 Reason("Existing certificate file is corrupted"));
776 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500777 }
778}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500779
780void Manager::createRSAPrivateKeyFile()
781{
782 fs::path rsaPrivateKeyFileName =
783 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
784
785 try
786 {
787 if (!fs::exists(rsaPrivateKeyFileName))
788 {
789 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
790 RSA_PRIV_KEY_FILE_NAME);
791 }
792 }
793 catch (const InternalFailure& e)
794 {
795 report<InternalFailure>();
796 }
797}
798
799EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
800{
801 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
802 {
803 log<level::ERR>(
804 "Given Key bit length is not supported",
805 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
806 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
807 elog<InvalidArgument>(
808 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
809 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
810 }
811 fs::path rsaPrivateKeyFileName =
812 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
813
814 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
815 if (!privateKeyFile)
816 {
817 log<level::ERR>("Unable to open RSA private key file to read",
818 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
819 entry("ERRORREASON=%s", strerror(errno)));
820 elog<InternalFailure>();
821 }
822
823 EVP_PKEY_Ptr privateKey(
824 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
825 ::EVP_PKEY_free);
826 std::fclose(privateKeyFile);
827
828 if (!privateKey)
829 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800830 log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500831 elog<InternalFailure>();
832 }
833 return privateKey;
834}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100835
836void Manager::storageUpdate()
837{
838 if (certType == phosphor::certs::AUTHORITY)
839 {
840 // Remove symbolic links in the certificate directory
841 for (auto& certPath : fs::directory_iterator(certInstallPath))
842 {
843 try
844 {
845 if (fs::is_symlink(certPath))
846 {
847 fs::remove(certPath);
848 }
849 }
850 catch (const std::exception& e)
851 {
852 log<level::ERR>(
853 "Failed to remove symlink for certificate",
854 entry("ERR=%s", e.what()),
855 entry("SYMLINK=%s", certPath.path().string().c_str()));
856 elog<InternalFailure>();
857 }
858 }
859 }
860
861 for (const auto& cert : installedCerts)
862 {
863 cert->storageUpdate();
864 }
865}
866
867void Manager::reloadOrReset(const UnitsToRestart& unit)
868{
869 if (!unit.empty())
870 {
871 try
872 {
873 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
874 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
875 constexpr auto SYSTEMD_INTERFACE =
876 "org.freedesktop.systemd1.Manager";
877
878 auto method =
879 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
880 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
881 method.append(unit, "replace");
882 bus.call_noreply(method);
883 }
Patrick Williamsca128112021-09-02 09:36:07 -0500884 catch (const sdbusplus::exception::exception& e)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100885 {
886 log<level::ERR>("Failed to reload or restart service",
887 entry("ERR=%s", e.what()),
888 entry("UNIT=%s", unit.c_str()));
889 elog<InternalFailure>();
890 }
891 }
892}
893
894bool Manager::isCertificateUnique(const std::string& filePath,
895 const Certificate* const certToDrop)
896{
897 if (std::any_of(
898 installedCerts.begin(), installedCerts.end(),
899 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
900 return cert.get() != certToDrop && cert->isSame(filePath);
901 }))
902 {
903 return false;
904 }
905 else
906 {
907 return true;
908 }
909}
910
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500911} // namespace certs
912} // namespace phosphor