blob: 0303c9a5d53e270107d81787ab7d0c733d4b49ec [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
309 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
310 {
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();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500473 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
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
549 if (ecKey == NULL)
550 {
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");
641 if (fp == NULL)
642 {
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 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500646 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500647 std::fclose(fp);
648 if (ret == 0)
649 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800650 log<level::ERR>("Error occurred while writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500651 elog<InternalFailure>();
652 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500653}
654
655void Manager::addEntry(X509_NAME* x509Name, const char* field,
656 const std::string& bytes)
657{
658 if (bytes.empty())
659 {
660 return;
661 }
662 int ret = X509_NAME_add_entry_by_txt(
663 x509Name, field, MBSTRING_ASC,
664 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
665 if (ret != 1)
666 {
667 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
668 entry("VALUE=%s", bytes.c_str()));
669 elog<InternalFailure>();
670 }
671}
672
673void Manager::createCSRObject(const Status& status)
674{
675 if (csrPtr)
676 {
677 csrPtr.reset(nullptr);
678 }
679 auto csrObjectPath = objectPath + '/' + "csr";
680 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
681 certInstallPath.c_str(), status);
682}
683
684void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
685{
686 if (fs::exists(filePath))
687 {
688 log<level::INFO>("Removing the existing file",
689 entry("FILENAME=%s", filePath.c_str()));
690 if (!fs::remove(filePath.c_str()))
691 {
692 log<level::ERR>("Unable to remove the file",
693 entry("FILENAME=%s", filePath.c_str()));
694 elog<InternalFailure>();
695 }
696 }
697
698 FILE* fp = NULL;
699
700 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
701 {
702 log<level::ERR>("Error opening the file to write the CSR",
703 entry("FILENAME=%s", filePath.c_str()));
704 elog<InternalFailure>();
705 }
706
707 int rc = PEM_write_X509_REQ(fp, x509Req.get());
708 if (!rc)
709 {
710 log<level::ERR>("PEM write routine failed",
711 entry("FILENAME=%s", filePath.c_str()));
712 std::fclose(fp);
713 elog<InternalFailure>();
714 }
715 std::fclose(fp);
716}
717
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200718void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500719{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200720 auto certObjectPath = objectPath + '/';
721
722 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500723 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100724 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200725 if (!fs::is_directory(certInstallPath))
726 {
727 log<level::ERR>("Certificate installation path exists and it is "
728 "not a directory");
729 elog<InternalFailure>();
730 return;
731 }
732
733 for (auto& path : fs::directory_iterator(certInstallPath))
734 {
735 try
736 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100737 // Assume here any regular file located in certificate directory
738 // contains certificates body. Do not want to use soft links
739 // would add value.
740 if (fs::is_regular_file(path))
741 {
742 installedCerts.emplace_back(std::make_unique<Certificate>(
743 bus, certObjectPath + std::to_string(certIdCounter++),
744 certType, certInstallPath, path.path(), certWatchPtr,
745 *this));
746 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200747 }
748 catch (const InternalFailure& e)
749 {
750 report<InternalFailure>();
751 }
752 catch (const InvalidCertificate& e)
753 {
754 report<InvalidCertificate>(
755 Reason("Existing certificate file is corrupted"));
756 }
757 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500758 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200759 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500760 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200761 try
762 {
763 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100764 bus, certObjectPath + '1', certType, certInstallPath,
765 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200766 }
767 catch (const InternalFailure& e)
768 {
769 report<InternalFailure>();
770 }
771 catch (const InvalidCertificate& e)
772 {
773 report<InvalidCertificate>(
774 Reason("Existing certificate file is corrupted"));
775 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500776 }
777}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500778
779void Manager::createRSAPrivateKeyFile()
780{
781 fs::path rsaPrivateKeyFileName =
782 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
783
784 try
785 {
786 if (!fs::exists(rsaPrivateKeyFileName))
787 {
788 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
789 RSA_PRIV_KEY_FILE_NAME);
790 }
791 }
792 catch (const InternalFailure& e)
793 {
794 report<InternalFailure>();
795 }
796}
797
798EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
799{
800 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
801 {
802 log<level::ERR>(
803 "Given Key bit length is not supported",
804 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
805 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
806 elog<InvalidArgument>(
807 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
808 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
809 }
810 fs::path rsaPrivateKeyFileName =
811 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
812
813 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
814 if (!privateKeyFile)
815 {
816 log<level::ERR>("Unable to open RSA private key file to read",
817 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
818 entry("ERRORREASON=%s", strerror(errno)));
819 elog<InternalFailure>();
820 }
821
822 EVP_PKEY_Ptr privateKey(
823 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
824 ::EVP_PKEY_free);
825 std::fclose(privateKeyFile);
826
827 if (!privateKey)
828 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800829 log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500830 elog<InternalFailure>();
831 }
832 return privateKey;
833}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100834
835void Manager::storageUpdate()
836{
837 if (certType == phosphor::certs::AUTHORITY)
838 {
839 // Remove symbolic links in the certificate directory
840 for (auto& certPath : fs::directory_iterator(certInstallPath))
841 {
842 try
843 {
844 if (fs::is_symlink(certPath))
845 {
846 fs::remove(certPath);
847 }
848 }
849 catch (const std::exception& e)
850 {
851 log<level::ERR>(
852 "Failed to remove symlink for certificate",
853 entry("ERR=%s", e.what()),
854 entry("SYMLINK=%s", certPath.path().string().c_str()));
855 elog<InternalFailure>();
856 }
857 }
858 }
859
860 for (const auto& cert : installedCerts)
861 {
862 cert->storageUpdate();
863 }
864}
865
866void Manager::reloadOrReset(const UnitsToRestart& unit)
867{
868 if (!unit.empty())
869 {
870 try
871 {
872 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
873 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
874 constexpr auto SYSTEMD_INTERFACE =
875 "org.freedesktop.systemd1.Manager";
876
877 auto method =
878 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
879 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
880 method.append(unit, "replace");
881 bus.call_noreply(method);
882 }
Patrick Williamsca128112021-09-02 09:36:07 -0500883 catch (const sdbusplus::exception::exception& e)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100884 {
885 log<level::ERR>("Failed to reload or restart service",
886 entry("ERR=%s", e.what()),
887 entry("UNIT=%s", unit.c_str()));
888 elog<InternalFailure>();
889 }
890 }
891}
892
893bool Manager::isCertificateUnique(const std::string& filePath,
894 const Certificate* const certToDrop)
895{
896 if (std::any_of(
897 installedCerts.begin(), installedCerts.end(),
898 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
899 return cert.get() != certToDrop && cert->isSame(filePath);
900 }))
901 {
902 return false;
903 }
904 else
905 {
906 return true;
907 }
908}
909
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500910} // namespace certs
911} // namespace phosphor