blob: 85416565f94a9b90504f466c801bdd81bf3e073c [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 }
64 catch (fs::filesystem_error& e)
65 {
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 }
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500141 catch (std::exception& ex)
142 {
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 }
270 }
271 else
272 {
273 using namespace sdeventplus::source;
274 Child::Callback callback = [this](Child& eventSource,
275 const siginfo_t* si) {
276 eventSource.set_enabled(Enabled::On);
277 if (si->si_status != 0)
278 {
279 this->createCSRObject(Status::FAILURE);
280 }
281 else
282 {
283 this->createCSRObject(Status::SUCCESS);
284 }
285 };
286 try
287 {
288 sigset_t ss;
289 if (sigemptyset(&ss) < 0)
290 {
291 log<level::ERR>("Unable to initialize signal set");
292 elog<InternalFailure>();
293 }
294 if (sigaddset(&ss, SIGCHLD) < 0)
295 {
296 log<level::ERR>("Unable to add signal to signal set");
297 elog<InternalFailure>();
298 }
299
300 // Block SIGCHLD first, so that the event loop can handle it
301 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
302 {
303 log<level::ERR>("Unable to block signal");
304 elog<InternalFailure>();
305 }
306 if (childPtr)
307 {
308 childPtr.reset();
309 }
310 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
311 std::move(callback));
312 }
313 catch (const InternalFailure& e)
314 {
315 commit<InternalFailure>();
316 }
317 }
318 auto csrObjectPath = objectPath + '/' + "csr";
319 return csrObjectPath;
320}
321
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200322std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500323{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200324 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500325}
326
Marri Devender Raof4682712019-03-19 05:00:28 -0500327void Manager::generateCSRHelper(
328 std::vector<std::string> alternativeNames, std::string challengePassword,
329 std::string city, std::string commonName, std::string contactPerson,
330 std::string country, std::string email, std::string givenName,
331 std::string initials, int64_t keyBitLength, std::string keyCurveId,
332 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
333 std::string organization, std::string organizationalUnit, std::string state,
334 std::string surname, std::string unstructuredName)
335{
336 int ret = 0;
337
338 // set version of x509 req
339 int nVersion = 1;
340 // TODO: Issue#6 need to make version number configurable
341 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
342 ret = X509_REQ_set_version(x509Req.get(), nVersion);
343 if (ret == 0)
344 {
345 log<level::ERR>("Error occured during X509_REQ_set_version call");
346 elog<InternalFailure>();
347 }
348
349 // set subject of x509 req
350 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
351
352 if (!alternativeNames.empty())
353 {
354 for (auto& name : alternativeNames)
355 {
356 addEntry(x509Name, "subjectAltName", name);
357 }
358 }
359 addEntry(x509Name, "challengePassword", challengePassword);
360 addEntry(x509Name, "L", city);
361 addEntry(x509Name, "CN", commonName);
362 addEntry(x509Name, "name", contactPerson);
363 addEntry(x509Name, "C", country);
364 addEntry(x509Name, "emailAddress", email);
365 addEntry(x509Name, "GN", givenName);
366 addEntry(x509Name, "initials", initials);
367 addEntry(x509Name, "algorithm", keyPairAlgorithm);
368 if (!keyUsage.empty())
369 {
370 for (auto& usage : keyUsage)
371 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500372 if (isExtendedKeyUsage(usage))
373 {
374 addEntry(x509Name, "extendedKeyUsage", usage);
375 }
376 else
377 {
378 addEntry(x509Name, "keyUsage", usage);
379 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500380 }
381 }
382 addEntry(x509Name, "O", organization);
383 addEntry(x509Name, "ST", state);
384 addEntry(x509Name, "SN", surname);
385 addEntry(x509Name, "unstructuredName", unstructuredName);
386
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500387 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
388
389 log<level::INFO>("Given Key pair algorithm",
390 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
391
392 // Used EC algorithm as default if user did not give algorithm type.
393 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500394 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500395 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500396 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500397 else
398 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500399 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
400 "RSA and EC only");
401 elog<InvalidArgument>(
402 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
403 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
404 }
405
406 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
407 if (ret == 0)
408 {
409 log<level::ERR>("Error occured while setting Public key");
410 elog<InternalFailure>();
411 }
412
413 // Write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500414 writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
Marri Devender Raof4682712019-03-19 05:00:28 -0500415
416 // set sign key of x509 req
417 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500418 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500419 {
420 log<level::ERR>("Error occured while signing key of x509");
421 elog<InternalFailure>();
422 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500423
Marri Devender Raof4682712019-03-19 05:00:28 -0500424 log<level::INFO>("Writing CSR to file");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500425 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
426 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500427}
428
Marri Devender Rao76411052019-08-07 01:25:07 -0500429bool Manager::isExtendedKeyUsage(const std::string& usage)
430{
431 const static std::array<const char*, 6> usageList = {
432 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
433 "Timestamping", "CodeSigning", "EmailProtection"};
434 auto it = std::find_if(
435 usageList.begin(), usageList.end(),
436 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
437 return it != usageList.end();
438}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500439EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500440{
441 int ret = 0;
442 // generate rsa key
443 BIGNUM_Ptr bne(BN_new(), ::BN_free);
444 ret = BN_set_word(bne.get(), RSA_F4);
445 if (ret == 0)
446 {
447 log<level::ERR>("Error occured during BN_set_word call");
448 elog<InternalFailure>();
449 }
450
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500451 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500452 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500453 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500454 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500455 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
456 log<level::INFO>(
457 "KeyBitLength is not given.Hence, using default KeyBitLength",
458 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
459 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500460 }
461 RSA* rsa = RSA_new();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500462 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500463 if (ret != 1)
464 {
465 free(rsa);
466 log<level::ERR>("Error occured during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500467 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500468 elog<InternalFailure>();
469 }
470
471 // set public key of x509 req
472 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500473 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500474 if (ret == 0)
475 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500476 free(rsa);
477 log<level::ERR>("Error occured during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500478 elog<InternalFailure>();
479 }
480
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500481 return pKey;
482}
483
484EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
485{
486 std::string curId(curveId);
487
488 if (curId.empty())
489 {
490 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
491 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
492 log<level::INFO>(
493 "KeyCurveId is not given. Hence using default curve id",
494 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
495 curId = DEFAULT_KEYCURVEID;
496 }
497
498 int ecGrp = OBJ_txt2nid(curId.c_str());
499
500 if (ecGrp == NID_undef)
501 {
502 log<level::ERR>(
503 "Error occured during convert the curve id string format into NID",
504 entry("KEYCURVEID=%s", curId.c_str()));
505 elog<InternalFailure>();
506 }
507
508 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
509
510 if (ecKey == NULL)
511 {
512 log<level::ERR>(
513 "Error occured during create the EC_Key object from NID",
514 entry("ECGROUP=%d", ecGrp));
515 elog<InternalFailure>();
516 }
517
518 // If you want to save a key and later load it with
519 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
520 // flag on the key.
521 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
522
523 int ret = EC_KEY_generate_key(ecKey);
524
525 if (ret == 0)
526 {
527 EC_KEY_free(ecKey);
528 log<level::ERR>("Error occured during generate EC key");
529 elog<InternalFailure>();
530 }
531
532 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
533 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
534 if (ret == 0)
535 {
536 EC_KEY_free(ecKey);
537 log<level::ERR>("Error occured during assign EC Key into EVP");
538 elog<InternalFailure>();
539 }
540
541 return pKey;
542}
543
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500544void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
545 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500546{
547 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500548 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500549 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500550
551 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
552 if (fp == NULL)
553 {
Marri Devender Raof4682712019-03-19 05:00:28 -0500554 log<level::ERR>("Error occured creating private key file");
555 elog<InternalFailure>();
556 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500557 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500558 std::fclose(fp);
559 if (ret == 0)
560 {
561 log<level::ERR>("Error occured while writing private key to file");
562 elog<InternalFailure>();
563 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500564}
565
566void Manager::addEntry(X509_NAME* x509Name, const char* field,
567 const std::string& bytes)
568{
569 if (bytes.empty())
570 {
571 return;
572 }
573 int ret = X509_NAME_add_entry_by_txt(
574 x509Name, field, MBSTRING_ASC,
575 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
576 if (ret != 1)
577 {
578 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
579 entry("VALUE=%s", bytes.c_str()));
580 elog<InternalFailure>();
581 }
582}
583
584void Manager::createCSRObject(const Status& status)
585{
586 if (csrPtr)
587 {
588 csrPtr.reset(nullptr);
589 }
590 auto csrObjectPath = objectPath + '/' + "csr";
591 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
592 certInstallPath.c_str(), status);
593}
594
595void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
596{
597 if (fs::exists(filePath))
598 {
599 log<level::INFO>("Removing the existing file",
600 entry("FILENAME=%s", filePath.c_str()));
601 if (!fs::remove(filePath.c_str()))
602 {
603 log<level::ERR>("Unable to remove the file",
604 entry("FILENAME=%s", filePath.c_str()));
605 elog<InternalFailure>();
606 }
607 }
608
609 FILE* fp = NULL;
610
611 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
612 {
613 log<level::ERR>("Error opening the file to write the CSR",
614 entry("FILENAME=%s", filePath.c_str()));
615 elog<InternalFailure>();
616 }
617
618 int rc = PEM_write_X509_REQ(fp, x509Req.get());
619 if (!rc)
620 {
621 log<level::ERR>("PEM write routine failed",
622 entry("FILENAME=%s", filePath.c_str()));
623 std::fclose(fp);
624 elog<InternalFailure>();
625 }
626 std::fclose(fp);
627}
628
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200629void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500630{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200631 auto certObjectPath = objectPath + '/';
632
633 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500634 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100635 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200636 if (!fs::is_directory(certInstallPath))
637 {
638 log<level::ERR>("Certificate installation path exists and it is "
639 "not a directory");
640 elog<InternalFailure>();
641 return;
642 }
643
644 for (auto& path : fs::directory_iterator(certInstallPath))
645 {
646 try
647 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100648 // Assume here any regular file located in certificate directory
649 // contains certificates body. Do not want to use soft links
650 // would add value.
651 if (fs::is_regular_file(path))
652 {
653 installedCerts.emplace_back(std::make_unique<Certificate>(
654 bus, certObjectPath + std::to_string(certIdCounter++),
655 certType, certInstallPath, path.path(), certWatchPtr,
656 *this));
657 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200658 }
659 catch (const InternalFailure& e)
660 {
661 report<InternalFailure>();
662 }
663 catch (const InvalidCertificate& e)
664 {
665 report<InvalidCertificate>(
666 Reason("Existing certificate file is corrupted"));
667 }
668 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500669 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200670 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500671 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200672 try
673 {
674 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100675 bus, certObjectPath + '1', certType, certInstallPath,
676 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200677 }
678 catch (const InternalFailure& e)
679 {
680 report<InternalFailure>();
681 }
682 catch (const InvalidCertificate& e)
683 {
684 report<InvalidCertificate>(
685 Reason("Existing certificate file is corrupted"));
686 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500687 }
688}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500689
690void Manager::createRSAPrivateKeyFile()
691{
692 fs::path rsaPrivateKeyFileName =
693 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
694
695 try
696 {
697 if (!fs::exists(rsaPrivateKeyFileName))
698 {
699 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
700 RSA_PRIV_KEY_FILE_NAME);
701 }
702 }
703 catch (const InternalFailure& e)
704 {
705 report<InternalFailure>();
706 }
707}
708
709EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
710{
711 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
712 {
713 log<level::ERR>(
714 "Given Key bit length is not supported",
715 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
716 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
717 elog<InvalidArgument>(
718 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
719 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
720 }
721 fs::path rsaPrivateKeyFileName =
722 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
723
724 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
725 if (!privateKeyFile)
726 {
727 log<level::ERR>("Unable to open RSA private key file to read",
728 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
729 entry("ERRORREASON=%s", strerror(errno)));
730 elog<InternalFailure>();
731 }
732
733 EVP_PKEY_Ptr privateKey(
734 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
735 ::EVP_PKEY_free);
736 std::fclose(privateKeyFile);
737
738 if (!privateKey)
739 {
740 log<level::ERR>("Error occured during PEM_read_PrivateKey call");
741 elog<InternalFailure>();
742 }
743 return privateKey;
744}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100745
746void Manager::storageUpdate()
747{
748 if (certType == phosphor::certs::AUTHORITY)
749 {
750 // Remove symbolic links in the certificate directory
751 for (auto& certPath : fs::directory_iterator(certInstallPath))
752 {
753 try
754 {
755 if (fs::is_symlink(certPath))
756 {
757 fs::remove(certPath);
758 }
759 }
760 catch (const std::exception& e)
761 {
762 log<level::ERR>(
763 "Failed to remove symlink for certificate",
764 entry("ERR=%s", e.what()),
765 entry("SYMLINK=%s", certPath.path().string().c_str()));
766 elog<InternalFailure>();
767 }
768 }
769 }
770
771 for (const auto& cert : installedCerts)
772 {
773 cert->storageUpdate();
774 }
775}
776
777void Manager::reloadOrReset(const UnitsToRestart& unit)
778{
779 if (!unit.empty())
780 {
781 try
782 {
783 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
784 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
785 constexpr auto SYSTEMD_INTERFACE =
786 "org.freedesktop.systemd1.Manager";
787
788 auto method =
789 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
790 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
791 method.append(unit, "replace");
792 bus.call_noreply(method);
793 }
794 catch (const sdbusplus::exception::SdBusError& e)
795 {
796 log<level::ERR>("Failed to reload or restart service",
797 entry("ERR=%s", e.what()),
798 entry("UNIT=%s", unit.c_str()));
799 elog<InternalFailure>();
800 }
801 }
802}
803
804bool Manager::isCertificateUnique(const std::string& filePath,
805 const Certificate* const certToDrop)
806{
807 if (std::any_of(
808 installedCerts.begin(), installedCerts.end(),
809 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
810 return cert.get() != certToDrop && cert->isSame(filePath);
811 }))
812 {
813 return false;
814 }
815 else
816 {
817 return true;
818 }
819}
820
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500821} // namespace certs
822} // namespace phosphor