blob: 2904f45690d0f90f90c4da7012103e42ec9e1629 [file] [log] [blame]
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05001#include "certs_manager.hpp"
2
Marri Devender Raof4682712019-03-19 05:00:28 -05003#include <openssl/pem.h>
4#include <unistd.h>
5
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +02006#include <algorithm>
Marri Devender Rao6ceec402019-02-01 03:15:19 -06007#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -05008#include <xyz/openbmc_project/Certs/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05009#include <xyz/openbmc_project/Common/error.hpp>
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010010
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050011namespace phosphor
12{
13namespace certs
14{
Marri Devender Rao13965112019-02-27 08:47:12 -060015using InternalFailure =
16 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050017using InvalidCertificate =
18 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
19using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050020
Marri Devender Raof4682712019-03-19 05:00:28 -050021using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
22using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050023using InvalidArgument =
24 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
25using Argument = xyz::openbmc_project::Common::InvalidArgument;
26
27constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
Marri Devender Raof4682712019-03-19 05:00:28 -050028
29Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
30 const char* path, const CertificateType& type,
31 UnitsToRestart&& unit, CertInstallPath&& installPath) :
Marri Devender Rao6ceec402019-02-01 03:15:19 -060032 Ifaces(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -050033 bus(bus), event(event), objectPath(path), certType(type),
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050034 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
35 certParentInstallPath(fs::path(certInstallPath).parent_path())
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050036{
Marri Devender Raob57d75e2019-07-25 04:56:21 -050037 try
38 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050039 // Create certificate directory if not existing.
40 // Set correct certificate directory permitions.
41 fs::path certDirectory;
42 try
Marri Devender Raob57d75e2019-07-25 04:56:21 -050043 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050044 if (certType == AUTHORITY)
45 {
46 certDirectory = certInstallPath;
47 }
48 else
49 {
50 certDirectory = certParentInstallPath;
51 }
52
53 if (!fs::exists(certDirectory))
54 {
55 fs::create_directories(certDirectory);
56 }
57
58 auto permission = fs::perms::owner_read | fs::perms::owner_write |
59 fs::perms::owner_exec;
60 fs::permissions(certDirectory, permission,
61 fs::perm_options::replace);
62 storageUpdate();
63 }
Patrick Williams71957992021-10-06 14:42:52 -050064 catch (const fs::filesystem_error& e)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050065 {
66 log<level::ERR>(
67 "Failed to create directory", entry("ERR=%s", e.what()),
68 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
69 report<InternalFailure>();
70 }
71
72 // Generating RSA private key file if certificate type is server/client
73 if (certType != AUTHORITY)
74 {
75 createRSAPrivateKeyFile();
76 }
77
78 // restore any existing certificates
79 createCertificates();
80
81 // watch is not required for authority certificates
82 if (certType != AUTHORITY)
83 {
84 // watch for certificate file create/replace
85 certWatchPtr = std::make_unique<
86 Watch>(event, certInstallPath, [this]() {
87 try
88 {
89 // if certificate file existing update it
90 if (!installedCerts.empty())
91 {
92 log<level::INFO>("Inotify callback to update "
93 "certificate properties");
94 installedCerts[0]->populateProperties();
95 }
96 else
97 {
98 log<level::INFO>(
99 "Inotify callback to create certificate object");
100 createCertificates();
101 }
102 }
103 catch (const InternalFailure& e)
104 {
105 commit<InternalFailure>();
106 }
107 catch (const InvalidCertificate& e)
108 {
109 commit<InvalidCertificate>();
110 }
111 });
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500112 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100113 else
114 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500115 try
116 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500117 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
118 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500119 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500120 log<level::NOTICE>(
121 "Legacy certificate detected, will be installed from: ",
122 entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
123 install(singleCertPath);
124 if (!fs::remove(singleCertPath))
125 {
126 log<level::ERR>(
127 "Unable to remove old certificate from: ",
128 entry("SINGLE_CERTPATH=%s",
129 singleCertPath.c_str()));
130 elog<InternalFailure>();
131 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500132 }
133 }
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500134 catch (const std::exception& ex)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500135 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500136 log<level::ERR>("Error in restoring legacy certificate",
137 entry("ERROR_STR=%s", ex.what()));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200138 }
139 }
140 }
Patrick Williams71957992021-10-06 14:42:52 -0500141 catch (const std::exception& ex)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500142 {
143 log<level::ERR>("Error in certificate manager constructor",
144 entry("ERROR_STR=%s", ex.what()));
145 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500146}
147
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200148std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500149{
Marri Devender Rao13965112019-02-27 08:47:12 -0600150 using NotAllowed =
151 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
152 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200153
154 if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600155 {
156 elog<NotAllowed>(Reason("Certificate already exist"));
157 }
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200158 else if (certType == phosphor::certs::AUTHORITY &&
159 installedCerts.size() >= AUTHORITY_CERTIFICATES_LIMIT)
160 {
161 elog<NotAllowed>(Reason("Certificates limit reached"));
162 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500163
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100164 std::string certObjectPath;
165 if (isCertificateUnique(filePath))
166 {
167 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
168 installedCerts.emplace_back(std::make_unique<Certificate>(
169 bus, certObjectPath, certType, certInstallPath, filePath,
170 certWatchPtr, *this));
171 reloadOrReset(unitToRestart);
172 certIdCounter++;
173 }
174 else
175 {
176 elog<NotAllowed>(Reason("Certificate already exist"));
177 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200178
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200179 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500180}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500181
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200182void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500183{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600184 // TODO: #Issue 4 when a certificate is deleted system auto generates
185 // certificate file. At present we are not supporting creation of
186 // certificate object for the auto-generated certificate file as
187 // deletion if only applicable for REST server and Bmcweb does not allow
188 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200189 installedCerts.clear();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100190 storageUpdate();
191 reloadOrReset(unitToRestart);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500192}
Marri Devender Raof4682712019-03-19 05:00:28 -0500193
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100194void Manager::deleteCertificate(const Certificate* const certificate)
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200195{
196 std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
197 std::find_if(installedCerts.begin(), installedCerts.end(),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100198 [certificate](std::unique_ptr<Certificate> const& cert) {
199 return (cert.get() == certificate);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200200 });
201 if (certIt != installedCerts.end())
202 {
203 installedCerts.erase(certIt);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100204 storageUpdate();
205 reloadOrReset(unitToRestart);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200206 }
207 else
208 {
209 log<level::ERR>("Certificate does not exist",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100210 entry("ID=%s", certificate->getCertId().c_str()));
211 elog<InternalFailure>();
212 }
213}
214
215void Manager::replaceCertificate(Certificate* const certificate,
216 const std::string& filePath)
217{
218 if (isCertificateUnique(filePath, certificate))
219 {
220 certificate->install(filePath);
221 storageUpdate();
222 reloadOrReset(unitToRestart);
223 }
224 else
225 {
Zbigniew Lukwinski15cbbec2020-01-16 13:50:45 +0100226 using NotAllowed =
227 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
228 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
229
230 elog<NotAllowed>(Reason("Certificate already exist"));
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200231 }
232}
233
Marri Devender Raof4682712019-03-19 05:00:28 -0500234std::string Manager::generateCSR(
235 std::vector<std::string> alternativeNames, std::string challengePassword,
236 std::string city, std::string commonName, std::string contactPerson,
237 std::string country, std::string email, std::string givenName,
238 std::string initials, int64_t keyBitLength, std::string keyCurveId,
239 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
240 std::string organization, std::string organizationalUnit, std::string state,
241 std::string surname, std::string unstructuredName)
242{
243 // We support only one CSR.
244 csrPtr.reset(nullptr);
245 auto pid = fork();
246 if (pid == -1)
247 {
248 log<level::ERR>("Error occurred during forking process");
249 report<InternalFailure>();
250 }
251 else if (pid == 0)
252 {
253 try
254 {
255 generateCSRHelper(alternativeNames, challengePassword, city,
256 commonName, contactPerson, country, email,
257 givenName, initials, keyBitLength, keyCurveId,
258 keyPairAlgorithm, keyUsage, organization,
259 organizationalUnit, state, surname,
260 unstructuredName);
261 exit(EXIT_SUCCESS);
262 }
263 catch (const InternalFailure& e)
264 {
265 // commit the error reported in child process and exit
266 // Callback method from SDEvent Loop looks for exit status
267 exit(EXIT_FAILURE);
268 commit<InternalFailure>();
269 }
Ramesh Iyyard2393f22020-10-29 09:46:51 -0500270 catch (const InvalidArgument& e)
271 {
272 // commit the error reported in child process and exit
273 // Callback method from SDEvent Loop looks for exit status
274 exit(EXIT_FAILURE);
275 commit<InvalidArgument>();
276 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500277 }
278 else
279 {
280 using namespace sdeventplus::source;
281 Child::Callback callback = [this](Child& eventSource,
282 const siginfo_t* si) {
283 eventSource.set_enabled(Enabled::On);
284 if (si->si_status != 0)
285 {
286 this->createCSRObject(Status::FAILURE);
287 }
288 else
289 {
290 this->createCSRObject(Status::SUCCESS);
291 }
292 };
293 try
294 {
295 sigset_t ss;
296 if (sigemptyset(&ss) < 0)
297 {
298 log<level::ERR>("Unable to initialize signal set");
299 elog<InternalFailure>();
300 }
301 if (sigaddset(&ss, SIGCHLD) < 0)
302 {
303 log<level::ERR>("Unable to add signal to signal set");
304 elog<InternalFailure>();
305 }
306
307 // Block SIGCHLD first, so that the event loop can handle it
308 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
309 {
310 log<level::ERR>("Unable to block signal");
311 elog<InternalFailure>();
312 }
313 if (childPtr)
314 {
315 childPtr.reset();
316 }
317 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
318 std::move(callback));
319 }
320 catch (const InternalFailure& e)
321 {
322 commit<InternalFailure>();
323 }
324 }
325 auto csrObjectPath = objectPath + '/' + "csr";
326 return csrObjectPath;
327}
328
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200329std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500330{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200331 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500332}
333
Marri Devender Raof4682712019-03-19 05:00:28 -0500334void Manager::generateCSRHelper(
335 std::vector<std::string> alternativeNames, std::string challengePassword,
336 std::string city, std::string commonName, std::string contactPerson,
337 std::string country, std::string email, std::string givenName,
338 std::string initials, int64_t keyBitLength, std::string keyCurveId,
339 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
340 std::string organization, std::string organizationalUnit, std::string state,
341 std::string surname, std::string unstructuredName)
342{
343 int ret = 0;
344
345 // set version of x509 req
346 int nVersion = 1;
347 // TODO: Issue#6 need to make version number configurable
348 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
349 ret = X509_REQ_set_version(x509Req.get(), nVersion);
350 if (ret == 0)
351 {
352 log<level::ERR>("Error occured during X509_REQ_set_version call");
353 elog<InternalFailure>();
354 }
355
356 // set subject of x509 req
357 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
358
359 if (!alternativeNames.empty())
360 {
361 for (auto& name : alternativeNames)
362 {
363 addEntry(x509Name, "subjectAltName", name);
364 }
365 }
366 addEntry(x509Name, "challengePassword", challengePassword);
367 addEntry(x509Name, "L", city);
368 addEntry(x509Name, "CN", commonName);
369 addEntry(x509Name, "name", contactPerson);
370 addEntry(x509Name, "C", country);
371 addEntry(x509Name, "emailAddress", email);
372 addEntry(x509Name, "GN", givenName);
373 addEntry(x509Name, "initials", initials);
374 addEntry(x509Name, "algorithm", keyPairAlgorithm);
375 if (!keyUsage.empty())
376 {
377 for (auto& usage : keyUsage)
378 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500379 if (isExtendedKeyUsage(usage))
380 {
381 addEntry(x509Name, "extendedKeyUsage", usage);
382 }
383 else
384 {
385 addEntry(x509Name, "keyUsage", usage);
386 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500387 }
388 }
389 addEntry(x509Name, "O", organization);
Jayanth Othayothdc91fb62021-05-04 23:17:47 -0500390 addEntry(x509Name, "OU", organizationalUnit);
Marri Devender Raof4682712019-03-19 05:00:28 -0500391 addEntry(x509Name, "ST", state);
392 addEntry(x509Name, "SN", surname);
393 addEntry(x509Name, "unstructuredName", unstructuredName);
394
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500395 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
396
397 log<level::INFO>("Given Key pair algorithm",
398 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
399
400 // Used EC algorithm as default if user did not give algorithm type.
401 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500402 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500403 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500404 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500405 else
406 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500407 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
408 "RSA and EC only");
409 elog<InvalidArgument>(
410 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
411 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
412 }
413
414 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
415 if (ret == 0)
416 {
417 log<level::ERR>("Error occured while setting Public key");
418 elog<InternalFailure>();
419 }
420
421 // Write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500422 writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
Marri Devender Raof4682712019-03-19 05:00:28 -0500423
424 // set sign key of x509 req
425 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500426 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500427 {
428 log<level::ERR>("Error occured while signing key of x509");
429 elog<InternalFailure>();
430 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500431
Marri Devender Raof4682712019-03-19 05:00:28 -0500432 log<level::INFO>("Writing CSR to file");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500433 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
434 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500435}
436
Marri Devender Rao76411052019-08-07 01:25:07 -0500437bool Manager::isExtendedKeyUsage(const std::string& usage)
438{
439 const static std::array<const char*, 6> usageList = {
440 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
441 "Timestamping", "CodeSigning", "EmailProtection"};
442 auto it = std::find_if(
443 usageList.begin(), usageList.end(),
444 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
445 return it != usageList.end();
446}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500447EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500448{
449 int ret = 0;
450 // generate rsa key
451 BIGNUM_Ptr bne(BN_new(), ::BN_free);
452 ret = BN_set_word(bne.get(), RSA_F4);
453 if (ret == 0)
454 {
455 log<level::ERR>("Error occured during BN_set_word call");
456 elog<InternalFailure>();
457 }
458
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500459 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500460 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500461 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500462 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500463 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
464 log<level::INFO>(
465 "KeyBitLength is not given.Hence, using default KeyBitLength",
466 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
467 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500468 }
469 RSA* rsa = RSA_new();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500470 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500471 if (ret != 1)
472 {
473 free(rsa);
474 log<level::ERR>("Error occured 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);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500481 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500482 if (ret == 0)
483 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500484 free(rsa);
485 log<level::ERR>("Error occured during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500486 elog<InternalFailure>();
487 }
488
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500489 return pKey;
490}
491
492EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
493{
494 std::string curId(curveId);
495
496 if (curId.empty())
497 {
498 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
499 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
500 log<level::INFO>(
501 "KeyCurveId is not given. Hence using default curve id",
502 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
503 curId = DEFAULT_KEYCURVEID;
504 }
505
506 int ecGrp = OBJ_txt2nid(curId.c_str());
507
508 if (ecGrp == NID_undef)
509 {
510 log<level::ERR>(
511 "Error occured during convert the curve id string format into NID",
512 entry("KEYCURVEID=%s", curId.c_str()));
513 elog<InternalFailure>();
514 }
515
516 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
517
518 if (ecKey == NULL)
519 {
520 log<level::ERR>(
521 "Error occured during create the EC_Key object from NID",
522 entry("ECGROUP=%d", ecGrp));
523 elog<InternalFailure>();
524 }
525
526 // If you want to save a key and later load it with
527 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
528 // flag on the key.
529 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
530
531 int ret = EC_KEY_generate_key(ecKey);
532
533 if (ret == 0)
534 {
535 EC_KEY_free(ecKey);
536 log<level::ERR>("Error occured during generate EC key");
537 elog<InternalFailure>();
538 }
539
540 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
541 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
542 if (ret == 0)
543 {
544 EC_KEY_free(ecKey);
545 log<level::ERR>("Error occured during assign EC Key into EVP");
546 elog<InternalFailure>();
547 }
548
549 return pKey;
550}
551
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500552void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
553 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500554{
555 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500556 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500557 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500558
559 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
560 if (fp == NULL)
561 {
Marri Devender Raof4682712019-03-19 05:00:28 -0500562 log<level::ERR>("Error occured creating private key file");
563 elog<InternalFailure>();
564 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500565 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500566 std::fclose(fp);
567 if (ret == 0)
568 {
569 log<level::ERR>("Error occured while writing private key to file");
570 elog<InternalFailure>();
571 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500572}
573
574void Manager::addEntry(X509_NAME* x509Name, const char* field,
575 const std::string& bytes)
576{
577 if (bytes.empty())
578 {
579 return;
580 }
581 int ret = X509_NAME_add_entry_by_txt(
582 x509Name, field, MBSTRING_ASC,
583 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
584 if (ret != 1)
585 {
586 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
587 entry("VALUE=%s", bytes.c_str()));
588 elog<InternalFailure>();
589 }
590}
591
592void Manager::createCSRObject(const Status& status)
593{
594 if (csrPtr)
595 {
596 csrPtr.reset(nullptr);
597 }
598 auto csrObjectPath = objectPath + '/' + "csr";
599 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
600 certInstallPath.c_str(), status);
601}
602
603void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
604{
605 if (fs::exists(filePath))
606 {
607 log<level::INFO>("Removing the existing file",
608 entry("FILENAME=%s", filePath.c_str()));
609 if (!fs::remove(filePath.c_str()))
610 {
611 log<level::ERR>("Unable to remove the file",
612 entry("FILENAME=%s", filePath.c_str()));
613 elog<InternalFailure>();
614 }
615 }
616
617 FILE* fp = NULL;
618
619 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
620 {
621 log<level::ERR>("Error opening the file to write the CSR",
622 entry("FILENAME=%s", filePath.c_str()));
623 elog<InternalFailure>();
624 }
625
626 int rc = PEM_write_X509_REQ(fp, x509Req.get());
627 if (!rc)
628 {
629 log<level::ERR>("PEM write routine failed",
630 entry("FILENAME=%s", filePath.c_str()));
631 std::fclose(fp);
632 elog<InternalFailure>();
633 }
634 std::fclose(fp);
635}
636
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200637void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500638{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200639 auto certObjectPath = objectPath + '/';
640
641 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500642 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100643 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200644 if (!fs::is_directory(certInstallPath))
645 {
646 log<level::ERR>("Certificate installation path exists and it is "
647 "not a directory");
648 elog<InternalFailure>();
649 return;
650 }
651
652 for (auto& path : fs::directory_iterator(certInstallPath))
653 {
654 try
655 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100656 // Assume here any regular file located in certificate directory
657 // contains certificates body. Do not want to use soft links
658 // would add value.
659 if (fs::is_regular_file(path))
660 {
661 installedCerts.emplace_back(std::make_unique<Certificate>(
662 bus, certObjectPath + std::to_string(certIdCounter++),
663 certType, certInstallPath, path.path(), certWatchPtr,
664 *this));
665 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200666 }
667 catch (const InternalFailure& e)
668 {
669 report<InternalFailure>();
670 }
671 catch (const InvalidCertificate& e)
672 {
673 report<InvalidCertificate>(
674 Reason("Existing certificate file is corrupted"));
675 }
676 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500677 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200678 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500679 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200680 try
681 {
682 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100683 bus, certObjectPath + '1', certType, certInstallPath,
684 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200685 }
686 catch (const InternalFailure& e)
687 {
688 report<InternalFailure>();
689 }
690 catch (const InvalidCertificate& e)
691 {
692 report<InvalidCertificate>(
693 Reason("Existing certificate file is corrupted"));
694 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500695 }
696}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500697
698void Manager::createRSAPrivateKeyFile()
699{
700 fs::path rsaPrivateKeyFileName =
701 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
702
703 try
704 {
705 if (!fs::exists(rsaPrivateKeyFileName))
706 {
707 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
708 RSA_PRIV_KEY_FILE_NAME);
709 }
710 }
711 catch (const InternalFailure& e)
712 {
713 report<InternalFailure>();
714 }
715}
716
717EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
718{
719 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
720 {
721 log<level::ERR>(
722 "Given Key bit length is not supported",
723 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
724 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
725 elog<InvalidArgument>(
726 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
727 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
728 }
729 fs::path rsaPrivateKeyFileName =
730 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
731
732 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
733 if (!privateKeyFile)
734 {
735 log<level::ERR>("Unable to open RSA private key file to read",
736 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
737 entry("ERRORREASON=%s", strerror(errno)));
738 elog<InternalFailure>();
739 }
740
741 EVP_PKEY_Ptr privateKey(
742 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
743 ::EVP_PKEY_free);
744 std::fclose(privateKeyFile);
745
746 if (!privateKey)
747 {
748 log<level::ERR>("Error occured during PEM_read_PrivateKey call");
749 elog<InternalFailure>();
750 }
751 return privateKey;
752}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100753
754void Manager::storageUpdate()
755{
756 if (certType == phosphor::certs::AUTHORITY)
757 {
758 // Remove symbolic links in the certificate directory
759 for (auto& certPath : fs::directory_iterator(certInstallPath))
760 {
761 try
762 {
763 if (fs::is_symlink(certPath))
764 {
765 fs::remove(certPath);
766 }
767 }
768 catch (const std::exception& e)
769 {
770 log<level::ERR>(
771 "Failed to remove symlink for certificate",
772 entry("ERR=%s", e.what()),
773 entry("SYMLINK=%s", certPath.path().string().c_str()));
774 elog<InternalFailure>();
775 }
776 }
777 }
778
779 for (const auto& cert : installedCerts)
780 {
781 cert->storageUpdate();
782 }
783}
784
785void Manager::reloadOrReset(const UnitsToRestart& unit)
786{
787 if (!unit.empty())
788 {
789 try
790 {
791 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
792 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
793 constexpr auto SYSTEMD_INTERFACE =
794 "org.freedesktop.systemd1.Manager";
795
796 auto method =
797 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
798 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
799 method.append(unit, "replace");
800 bus.call_noreply(method);
801 }
Patrick Williamsca128112021-09-02 09:36:07 -0500802 catch (const sdbusplus::exception::exception& e)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100803 {
804 log<level::ERR>("Failed to reload or restart service",
805 entry("ERR=%s", e.what()),
806 entry("UNIT=%s", unit.c_str()));
807 elog<InternalFailure>();
808 }
809 }
810}
811
812bool Manager::isCertificateUnique(const std::string& filePath,
813 const Certificate* const certToDrop)
814{
815 if (std::any_of(
816 installedCerts.begin(), installedCerts.end(),
817 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
818 return cert.get() != certToDrop && cert->isSame(filePath);
819 }))
820 {
821 return false;
822 }
823 else
824 {
825 return true;
826 }
827}
828
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500829} // namespace certs
830} // namespace phosphor