blob: f833a95ec7e03e3292ef18e5925957ee9861dc10 [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 }
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);
390 addEntry(x509Name, "ST", state);
391 addEntry(x509Name, "SN", surname);
392 addEntry(x509Name, "unstructuredName", unstructuredName);
393
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500394 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
395
396 log<level::INFO>("Given Key pair algorithm",
397 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
398
399 // Used EC algorithm as default if user did not give algorithm type.
400 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500401 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500402 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500403 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500404 else
405 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500406 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
407 "RSA and EC only");
408 elog<InvalidArgument>(
409 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
410 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
411 }
412
413 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
414 if (ret == 0)
415 {
416 log<level::ERR>("Error occured while setting Public key");
417 elog<InternalFailure>();
418 }
419
420 // Write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500421 writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
Marri Devender Raof4682712019-03-19 05:00:28 -0500422
423 // set sign key of x509 req
424 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500425 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500426 {
427 log<level::ERR>("Error occured while signing key of x509");
428 elog<InternalFailure>();
429 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500430
Marri Devender Raof4682712019-03-19 05:00:28 -0500431 log<level::INFO>("Writing CSR to file");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500432 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
433 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500434}
435
Marri Devender Rao76411052019-08-07 01:25:07 -0500436bool Manager::isExtendedKeyUsage(const std::string& usage)
437{
438 const static std::array<const char*, 6> usageList = {
439 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
440 "Timestamping", "CodeSigning", "EmailProtection"};
441 auto it = std::find_if(
442 usageList.begin(), usageList.end(),
443 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
444 return it != usageList.end();
445}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500446EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500447{
448 int ret = 0;
449 // generate rsa key
450 BIGNUM_Ptr bne(BN_new(), ::BN_free);
451 ret = BN_set_word(bne.get(), RSA_F4);
452 if (ret == 0)
453 {
454 log<level::ERR>("Error occured during BN_set_word call");
455 elog<InternalFailure>();
456 }
457
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500458 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500459 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500460 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500461 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500462 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
463 log<level::INFO>(
464 "KeyBitLength is not given.Hence, using default KeyBitLength",
465 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
466 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500467 }
468 RSA* rsa = RSA_new();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500469 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500470 if (ret != 1)
471 {
472 free(rsa);
473 log<level::ERR>("Error occured during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500474 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500475 elog<InternalFailure>();
476 }
477
478 // set public key of x509 req
479 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500480 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500481 if (ret == 0)
482 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500483 free(rsa);
484 log<level::ERR>("Error occured during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500485 elog<InternalFailure>();
486 }
487
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500488 return pKey;
489}
490
491EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
492{
493 std::string curId(curveId);
494
495 if (curId.empty())
496 {
497 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
498 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
499 log<level::INFO>(
500 "KeyCurveId is not given. Hence using default curve id",
501 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
502 curId = DEFAULT_KEYCURVEID;
503 }
504
505 int ecGrp = OBJ_txt2nid(curId.c_str());
506
507 if (ecGrp == NID_undef)
508 {
509 log<level::ERR>(
510 "Error occured during convert the curve id string format into NID",
511 entry("KEYCURVEID=%s", curId.c_str()));
512 elog<InternalFailure>();
513 }
514
515 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
516
517 if (ecKey == NULL)
518 {
519 log<level::ERR>(
520 "Error occured during create the EC_Key object from NID",
521 entry("ECGROUP=%d", ecGrp));
522 elog<InternalFailure>();
523 }
524
525 // If you want to save a key and later load it with
526 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
527 // flag on the key.
528 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
529
530 int ret = EC_KEY_generate_key(ecKey);
531
532 if (ret == 0)
533 {
534 EC_KEY_free(ecKey);
535 log<level::ERR>("Error occured during generate EC key");
536 elog<InternalFailure>();
537 }
538
539 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
540 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
541 if (ret == 0)
542 {
543 EC_KEY_free(ecKey);
544 log<level::ERR>("Error occured during assign EC Key into EVP");
545 elog<InternalFailure>();
546 }
547
548 return pKey;
549}
550
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500551void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
552 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500553{
554 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500555 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500556 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500557
558 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
559 if (fp == NULL)
560 {
Marri Devender Raof4682712019-03-19 05:00:28 -0500561 log<level::ERR>("Error occured creating private key file");
562 elog<InternalFailure>();
563 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500564 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500565 std::fclose(fp);
566 if (ret == 0)
567 {
568 log<level::ERR>("Error occured while writing private key to file");
569 elog<InternalFailure>();
570 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500571}
572
573void Manager::addEntry(X509_NAME* x509Name, const char* field,
574 const std::string& bytes)
575{
576 if (bytes.empty())
577 {
578 return;
579 }
580 int ret = X509_NAME_add_entry_by_txt(
581 x509Name, field, MBSTRING_ASC,
582 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
583 if (ret != 1)
584 {
585 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
586 entry("VALUE=%s", bytes.c_str()));
587 elog<InternalFailure>();
588 }
589}
590
591void Manager::createCSRObject(const Status& status)
592{
593 if (csrPtr)
594 {
595 csrPtr.reset(nullptr);
596 }
597 auto csrObjectPath = objectPath + '/' + "csr";
598 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
599 certInstallPath.c_str(), status);
600}
601
602void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
603{
604 if (fs::exists(filePath))
605 {
606 log<level::INFO>("Removing the existing file",
607 entry("FILENAME=%s", filePath.c_str()));
608 if (!fs::remove(filePath.c_str()))
609 {
610 log<level::ERR>("Unable to remove the file",
611 entry("FILENAME=%s", filePath.c_str()));
612 elog<InternalFailure>();
613 }
614 }
615
616 FILE* fp = NULL;
617
618 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
619 {
620 log<level::ERR>("Error opening the file to write the CSR",
621 entry("FILENAME=%s", filePath.c_str()));
622 elog<InternalFailure>();
623 }
624
625 int rc = PEM_write_X509_REQ(fp, x509Req.get());
626 if (!rc)
627 {
628 log<level::ERR>("PEM write routine failed",
629 entry("FILENAME=%s", filePath.c_str()));
630 std::fclose(fp);
631 elog<InternalFailure>();
632 }
633 std::fclose(fp);
634}
635
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200636void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500637{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200638 auto certObjectPath = objectPath + '/';
639
640 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500641 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100642 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200643 if (!fs::is_directory(certInstallPath))
644 {
645 log<level::ERR>("Certificate installation path exists and it is "
646 "not a directory");
647 elog<InternalFailure>();
648 return;
649 }
650
651 for (auto& path : fs::directory_iterator(certInstallPath))
652 {
653 try
654 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100655 // Assume here any regular file located in certificate directory
656 // contains certificates body. Do not want to use soft links
657 // would add value.
658 if (fs::is_regular_file(path))
659 {
660 installedCerts.emplace_back(std::make_unique<Certificate>(
661 bus, certObjectPath + std::to_string(certIdCounter++),
662 certType, certInstallPath, path.path(), certWatchPtr,
663 *this));
664 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200665 }
666 catch (const InternalFailure& e)
667 {
668 report<InternalFailure>();
669 }
670 catch (const InvalidCertificate& e)
671 {
672 report<InvalidCertificate>(
673 Reason("Existing certificate file is corrupted"));
674 }
675 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500676 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200677 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500678 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200679 try
680 {
681 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100682 bus, certObjectPath + '1', certType, certInstallPath,
683 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200684 }
685 catch (const InternalFailure& e)
686 {
687 report<InternalFailure>();
688 }
689 catch (const InvalidCertificate& e)
690 {
691 report<InvalidCertificate>(
692 Reason("Existing certificate file is corrupted"));
693 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500694 }
695}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500696
697void Manager::createRSAPrivateKeyFile()
698{
699 fs::path rsaPrivateKeyFileName =
700 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
701
702 try
703 {
704 if (!fs::exists(rsaPrivateKeyFileName))
705 {
706 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
707 RSA_PRIV_KEY_FILE_NAME);
708 }
709 }
710 catch (const InternalFailure& e)
711 {
712 report<InternalFailure>();
713 }
714}
715
716EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
717{
718 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
719 {
720 log<level::ERR>(
721 "Given Key bit length is not supported",
722 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
723 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
724 elog<InvalidArgument>(
725 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
726 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
727 }
728 fs::path rsaPrivateKeyFileName =
729 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
730
731 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
732 if (!privateKeyFile)
733 {
734 log<level::ERR>("Unable to open RSA private key file to read",
735 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
736 entry("ERRORREASON=%s", strerror(errno)));
737 elog<InternalFailure>();
738 }
739
740 EVP_PKEY_Ptr privateKey(
741 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
742 ::EVP_PKEY_free);
743 std::fclose(privateKeyFile);
744
745 if (!privateKey)
746 {
747 log<level::ERR>("Error occured during PEM_read_PrivateKey call");
748 elog<InternalFailure>();
749 }
750 return privateKey;
751}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100752
753void Manager::storageUpdate()
754{
755 if (certType == phosphor::certs::AUTHORITY)
756 {
757 // Remove symbolic links in the certificate directory
758 for (auto& certPath : fs::directory_iterator(certInstallPath))
759 {
760 try
761 {
762 if (fs::is_symlink(certPath))
763 {
764 fs::remove(certPath);
765 }
766 }
767 catch (const std::exception& e)
768 {
769 log<level::ERR>(
770 "Failed to remove symlink for certificate",
771 entry("ERR=%s", e.what()),
772 entry("SYMLINK=%s", certPath.path().string().c_str()));
773 elog<InternalFailure>();
774 }
775 }
776 }
777
778 for (const auto& cert : installedCerts)
779 {
780 cert->storageUpdate();
781 }
782}
783
784void Manager::reloadOrReset(const UnitsToRestart& unit)
785{
786 if (!unit.empty())
787 {
788 try
789 {
790 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
791 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
792 constexpr auto SYSTEMD_INTERFACE =
793 "org.freedesktop.systemd1.Manager";
794
795 auto method =
796 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
797 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
798 method.append(unit, "replace");
799 bus.call_noreply(method);
800 }
801 catch (const sdbusplus::exception::SdBusError& e)
802 {
803 log<level::ERR>("Failed to reload or restart service",
804 entry("ERR=%s", e.what()),
805 entry("UNIT=%s", unit.c_str()));
806 elog<InternalFailure>();
807 }
808 }
809}
810
811bool Manager::isCertificateUnique(const std::string& filePath,
812 const Certificate* const certToDrop)
813{
814 if (std::any_of(
815 installedCerts.begin(), installedCerts.end(),
816 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
817 return cert.get() != certToDrop && cert->isSame(filePath);
818 }))
819 {
820 return false;
821 }
822 else
823 {
824 return true;
825 }
826}
827
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500828} // namespace certs
829} // namespace phosphor