blob: 2d1afeda078fac2eaddff4a64cabfb7dfaada62b [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{
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010037 // Create certificate directory if not existing.
38 // Set correct certificate directory permitions.
39 fs::path certDirectory;
Marri Devender Raob57d75e2019-07-25 04:56:21 -050040 try
41 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010042 if (certType == AUTHORITY)
Marri Devender Raob57d75e2019-07-25 04:56:21 -050043 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010044 certDirectory = certInstallPath;
Marri Devender Raob57d75e2019-07-25 04:56:21 -050045 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010046 else
47 {
48 certDirectory = certParentInstallPath;
49 }
50
51 if (!fs::exists(certDirectory))
52 {
53 fs::create_directories(certDirectory);
54 }
55
Marri Devender Rao667286e2019-10-29 03:22:46 -050056 auto permission = fs::perms::owner_read | fs::perms::owner_write |
57 fs::perms::owner_exec;
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010058 fs::permissions(certDirectory, permission, fs::perm_options::replace);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010059 storageUpdate();
Marri Devender Raob57d75e2019-07-25 04:56:21 -050060 }
61 catch (fs::filesystem_error& e)
62 {
63 log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()),
64 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
65 report<InternalFailure>();
66 }
67
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050068 // Generating RSA private key file if certificate type is server/client
69 if (certType != AUTHORITY)
70 {
71 createRSAPrivateKeyFile();
72 }
73
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050074 // restore any existing certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +020075 createCertificates();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050076
77 // watch is not required for authority certificates
78 if (certType != AUTHORITY)
79 {
80 // watch for certificate file create/replace
81 certWatchPtr = std::make_unique<
82 Watch>(event, certInstallPath, [this]() {
83 try
84 {
85 // if certificate file existing update it
Kowalski, Kamildb029c92019-07-08 17:09:39 +020086 if (!installedCerts.empty())
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050087 {
88 log<level::INFO>(
89 "Inotify callback to update certificate properties");
Kowalski, Kamildb029c92019-07-08 17:09:39 +020090 installedCerts[0]->populateProperties();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050091 }
92 else
93 {
94 log<level::INFO>(
95 "Inotify callback to create certificate object");
Kowalski, Kamildb029c92019-07-08 17:09:39 +020096 createCertificates();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050097 }
98 }
99 catch (const InternalFailure& e)
100 {
101 commit<InternalFailure>();
102 }
103 catch (const InvalidCertificate& e)
104 {
105 commit<InvalidCertificate>();
106 }
107 });
Marri Devender Raobf7c5882019-02-27 08:41:07 -0600108 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200109 else
110 {
111 const std::string signleCertPath = "/etc/ssl/certs/Root-CA.pem";
112 if (fs::exists(signleCertPath) && !fs::is_empty(signleCertPath))
113 {
114 log<level::NOTICE>(
115 "Legacy certificate detected, will be installed from: ",
116 entry("SINGLE_CERTPATH=%s", signleCertPath.c_str()));
117 install(signleCertPath);
118 if (!fs::remove(signleCertPath))
119 {
120 log<level::ERR>(
121 "Unable to remove old certificate from: ",
122 entry("SINGLE_CERTPATH=%s", signleCertPath.c_str()));
123 elog<InternalFailure>();
124 }
125 }
126 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500127}
128
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200129std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500130{
Marri Devender Rao13965112019-02-27 08:47:12 -0600131 using NotAllowed =
132 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
133 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200134
135 if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600136 {
137 elog<NotAllowed>(Reason("Certificate already exist"));
138 }
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200139 else if (certType == phosphor::certs::AUTHORITY &&
140 installedCerts.size() >= AUTHORITY_CERTIFICATES_LIMIT)
141 {
142 elog<NotAllowed>(Reason("Certificates limit reached"));
143 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500144
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100145 std::string certObjectPath;
146 if (isCertificateUnique(filePath))
147 {
148 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
149 installedCerts.emplace_back(std::make_unique<Certificate>(
150 bus, certObjectPath, certType, certInstallPath, filePath,
151 certWatchPtr, *this));
152 reloadOrReset(unitToRestart);
153 certIdCounter++;
154 }
155 else
156 {
157 elog<NotAllowed>(Reason("Certificate already exist"));
158 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200159
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200160 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500161}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500162
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200163void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500164{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600165 // TODO: #Issue 4 when a certificate is deleted system auto generates
166 // certificate file. At present we are not supporting creation of
167 // certificate object for the auto-generated certificate file as
168 // deletion if only applicable for REST server and Bmcweb does not allow
169 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200170 installedCerts.clear();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100171 storageUpdate();
172 reloadOrReset(unitToRestart);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500173}
Marri Devender Raof4682712019-03-19 05:00:28 -0500174
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100175void Manager::deleteCertificate(const Certificate* const certificate)
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200176{
177 std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
178 std::find_if(installedCerts.begin(), installedCerts.end(),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100179 [certificate](std::unique_ptr<Certificate> const& cert) {
180 return (cert.get() == certificate);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200181 });
182 if (certIt != installedCerts.end())
183 {
184 installedCerts.erase(certIt);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100185 storageUpdate();
186 reloadOrReset(unitToRestart);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200187 }
188 else
189 {
190 log<level::ERR>("Certificate does not exist",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100191 entry("ID=%s", certificate->getCertId().c_str()));
192 elog<InternalFailure>();
193 }
194}
195
196void Manager::replaceCertificate(Certificate* const certificate,
197 const std::string& filePath)
198{
199 if (isCertificateUnique(filePath, certificate))
200 {
201 certificate->install(filePath);
202 storageUpdate();
203 reloadOrReset(unitToRestart);
204 }
205 else
206 {
Zbigniew Lukwinski15cbbec2020-01-16 13:50:45 +0100207 using NotAllowed =
208 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
209 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
210
211 elog<NotAllowed>(Reason("Certificate already exist"));
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200212 }
213}
214
Marri Devender Raof4682712019-03-19 05:00:28 -0500215std::string Manager::generateCSR(
216 std::vector<std::string> alternativeNames, std::string challengePassword,
217 std::string city, std::string commonName, std::string contactPerson,
218 std::string country, std::string email, std::string givenName,
219 std::string initials, int64_t keyBitLength, std::string keyCurveId,
220 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
221 std::string organization, std::string organizationalUnit, std::string state,
222 std::string surname, std::string unstructuredName)
223{
224 // We support only one CSR.
225 csrPtr.reset(nullptr);
226 auto pid = fork();
227 if (pid == -1)
228 {
229 log<level::ERR>("Error occurred during forking process");
230 report<InternalFailure>();
231 }
232 else if (pid == 0)
233 {
234 try
235 {
236 generateCSRHelper(alternativeNames, challengePassword, city,
237 commonName, contactPerson, country, email,
238 givenName, initials, keyBitLength, keyCurveId,
239 keyPairAlgorithm, keyUsage, organization,
240 organizationalUnit, state, surname,
241 unstructuredName);
242 exit(EXIT_SUCCESS);
243 }
244 catch (const InternalFailure& e)
245 {
246 // commit the error reported in child process and exit
247 // Callback method from SDEvent Loop looks for exit status
248 exit(EXIT_FAILURE);
249 commit<InternalFailure>();
250 }
251 }
252 else
253 {
254 using namespace sdeventplus::source;
255 Child::Callback callback = [this](Child& eventSource,
256 const siginfo_t* si) {
257 eventSource.set_enabled(Enabled::On);
258 if (si->si_status != 0)
259 {
260 this->createCSRObject(Status::FAILURE);
261 }
262 else
263 {
264 this->createCSRObject(Status::SUCCESS);
265 }
266 };
267 try
268 {
269 sigset_t ss;
270 if (sigemptyset(&ss) < 0)
271 {
272 log<level::ERR>("Unable to initialize signal set");
273 elog<InternalFailure>();
274 }
275 if (sigaddset(&ss, SIGCHLD) < 0)
276 {
277 log<level::ERR>("Unable to add signal to signal set");
278 elog<InternalFailure>();
279 }
280
281 // Block SIGCHLD first, so that the event loop can handle it
282 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
283 {
284 log<level::ERR>("Unable to block signal");
285 elog<InternalFailure>();
286 }
287 if (childPtr)
288 {
289 childPtr.reset();
290 }
291 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
292 std::move(callback));
293 }
294 catch (const InternalFailure& e)
295 {
296 commit<InternalFailure>();
297 }
298 }
299 auto csrObjectPath = objectPath + '/' + "csr";
300 return csrObjectPath;
301}
302
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200303std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500304{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200305 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500306}
307
Marri Devender Raof4682712019-03-19 05:00:28 -0500308void Manager::generateCSRHelper(
309 std::vector<std::string> alternativeNames, std::string challengePassword,
310 std::string city, std::string commonName, std::string contactPerson,
311 std::string country, std::string email, std::string givenName,
312 std::string initials, int64_t keyBitLength, std::string keyCurveId,
313 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
314 std::string organization, std::string organizationalUnit, std::string state,
315 std::string surname, std::string unstructuredName)
316{
317 int ret = 0;
318
319 // set version of x509 req
320 int nVersion = 1;
321 // TODO: Issue#6 need to make version number configurable
322 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
323 ret = X509_REQ_set_version(x509Req.get(), nVersion);
324 if (ret == 0)
325 {
326 log<level::ERR>("Error occured during X509_REQ_set_version call");
327 elog<InternalFailure>();
328 }
329
330 // set subject of x509 req
331 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
332
333 if (!alternativeNames.empty())
334 {
335 for (auto& name : alternativeNames)
336 {
337 addEntry(x509Name, "subjectAltName", name);
338 }
339 }
340 addEntry(x509Name, "challengePassword", challengePassword);
341 addEntry(x509Name, "L", city);
342 addEntry(x509Name, "CN", commonName);
343 addEntry(x509Name, "name", contactPerson);
344 addEntry(x509Name, "C", country);
345 addEntry(x509Name, "emailAddress", email);
346 addEntry(x509Name, "GN", givenName);
347 addEntry(x509Name, "initials", initials);
348 addEntry(x509Name, "algorithm", keyPairAlgorithm);
349 if (!keyUsage.empty())
350 {
351 for (auto& usage : keyUsage)
352 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500353 if (isExtendedKeyUsage(usage))
354 {
355 addEntry(x509Name, "extendedKeyUsage", usage);
356 }
357 else
358 {
359 addEntry(x509Name, "keyUsage", usage);
360 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500361 }
362 }
363 addEntry(x509Name, "O", organization);
364 addEntry(x509Name, "ST", state);
365 addEntry(x509Name, "SN", surname);
366 addEntry(x509Name, "unstructuredName", unstructuredName);
367
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500368 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
369
370 log<level::INFO>("Given Key pair algorithm",
371 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
372
373 // Used EC algorithm as default if user did not give algorithm type.
374 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500375 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500376 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500377 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500378 else
379 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500380 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
381 "RSA and EC only");
382 elog<InvalidArgument>(
383 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
384 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
385 }
386
387 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
388 if (ret == 0)
389 {
390 log<level::ERR>("Error occured while setting Public key");
391 elog<InternalFailure>();
392 }
393
394 // Write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500395 writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
Marri Devender Raof4682712019-03-19 05:00:28 -0500396
397 // set sign key of x509 req
398 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500399 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500400 {
401 log<level::ERR>("Error occured while signing key of x509");
402 elog<InternalFailure>();
403 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500404
Marri Devender Raof4682712019-03-19 05:00:28 -0500405 log<level::INFO>("Writing CSR to file");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500406 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
407 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500408}
409
Marri Devender Rao76411052019-08-07 01:25:07 -0500410bool Manager::isExtendedKeyUsage(const std::string& usage)
411{
412 const static std::array<const char*, 6> usageList = {
413 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
414 "Timestamping", "CodeSigning", "EmailProtection"};
415 auto it = std::find_if(
416 usageList.begin(), usageList.end(),
417 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
418 return it != usageList.end();
419}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500420EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500421{
422 int ret = 0;
423 // generate rsa key
424 BIGNUM_Ptr bne(BN_new(), ::BN_free);
425 ret = BN_set_word(bne.get(), RSA_F4);
426 if (ret == 0)
427 {
428 log<level::ERR>("Error occured during BN_set_word call");
429 elog<InternalFailure>();
430 }
431
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500432 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500433 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500434 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500435 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500436 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
437 log<level::INFO>(
438 "KeyBitLength is not given.Hence, using default KeyBitLength",
439 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
440 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500441 }
442 RSA* rsa = RSA_new();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500443 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500444 if (ret != 1)
445 {
446 free(rsa);
447 log<level::ERR>("Error occured during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500448 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500449 elog<InternalFailure>();
450 }
451
452 // set public key of x509 req
453 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500454 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500455 if (ret == 0)
456 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500457 free(rsa);
458 log<level::ERR>("Error occured during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500459 elog<InternalFailure>();
460 }
461
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500462 return pKey;
463}
464
465EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
466{
467 std::string curId(curveId);
468
469 if (curId.empty())
470 {
471 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
472 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
473 log<level::INFO>(
474 "KeyCurveId is not given. Hence using default curve id",
475 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
476 curId = DEFAULT_KEYCURVEID;
477 }
478
479 int ecGrp = OBJ_txt2nid(curId.c_str());
480
481 if (ecGrp == NID_undef)
482 {
483 log<level::ERR>(
484 "Error occured during convert the curve id string format into NID",
485 entry("KEYCURVEID=%s", curId.c_str()));
486 elog<InternalFailure>();
487 }
488
489 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
490
491 if (ecKey == NULL)
492 {
493 log<level::ERR>(
494 "Error occured during create the EC_Key object from NID",
495 entry("ECGROUP=%d", ecGrp));
496 elog<InternalFailure>();
497 }
498
499 // If you want to save a key and later load it with
500 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
501 // flag on the key.
502 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
503
504 int ret = EC_KEY_generate_key(ecKey);
505
506 if (ret == 0)
507 {
508 EC_KEY_free(ecKey);
509 log<level::ERR>("Error occured during generate EC key");
510 elog<InternalFailure>();
511 }
512
513 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
514 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
515 if (ret == 0)
516 {
517 EC_KEY_free(ecKey);
518 log<level::ERR>("Error occured during assign EC Key into EVP");
519 elog<InternalFailure>();
520 }
521
522 return pKey;
523}
524
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500525void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
526 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500527{
528 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500529 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500530 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500531
532 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
533 if (fp == NULL)
534 {
Marri Devender Raof4682712019-03-19 05:00:28 -0500535 log<level::ERR>("Error occured creating private key file");
536 elog<InternalFailure>();
537 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500538 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500539 std::fclose(fp);
540 if (ret == 0)
541 {
542 log<level::ERR>("Error occured while writing private key to file");
543 elog<InternalFailure>();
544 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500545}
546
547void Manager::addEntry(X509_NAME* x509Name, const char* field,
548 const std::string& bytes)
549{
550 if (bytes.empty())
551 {
552 return;
553 }
554 int ret = X509_NAME_add_entry_by_txt(
555 x509Name, field, MBSTRING_ASC,
556 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
557 if (ret != 1)
558 {
559 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
560 entry("VALUE=%s", bytes.c_str()));
561 elog<InternalFailure>();
562 }
563}
564
565void Manager::createCSRObject(const Status& status)
566{
567 if (csrPtr)
568 {
569 csrPtr.reset(nullptr);
570 }
571 auto csrObjectPath = objectPath + '/' + "csr";
572 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
573 certInstallPath.c_str(), status);
574}
575
576void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
577{
578 if (fs::exists(filePath))
579 {
580 log<level::INFO>("Removing the existing file",
581 entry("FILENAME=%s", filePath.c_str()));
582 if (!fs::remove(filePath.c_str()))
583 {
584 log<level::ERR>("Unable to remove the file",
585 entry("FILENAME=%s", filePath.c_str()));
586 elog<InternalFailure>();
587 }
588 }
589
590 FILE* fp = NULL;
591
592 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
593 {
594 log<level::ERR>("Error opening the file to write the CSR",
595 entry("FILENAME=%s", filePath.c_str()));
596 elog<InternalFailure>();
597 }
598
599 int rc = PEM_write_X509_REQ(fp, x509Req.get());
600 if (!rc)
601 {
602 log<level::ERR>("PEM write routine failed",
603 entry("FILENAME=%s", filePath.c_str()));
604 std::fclose(fp);
605 elog<InternalFailure>();
606 }
607 std::fclose(fp);
608}
609
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200610void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500611{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200612 auto certObjectPath = objectPath + '/';
613
614 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500615 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100616 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200617 if (!fs::is_directory(certInstallPath))
618 {
619 log<level::ERR>("Certificate installation path exists and it is "
620 "not a directory");
621 elog<InternalFailure>();
622 return;
623 }
624
625 for (auto& path : fs::directory_iterator(certInstallPath))
626 {
627 try
628 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100629 // Assume here any regular file located in certificate directory
630 // contains certificates body. Do not want to use soft links
631 // would add value.
632 if (fs::is_regular_file(path))
633 {
634 installedCerts.emplace_back(std::make_unique<Certificate>(
635 bus, certObjectPath + std::to_string(certIdCounter++),
636 certType, certInstallPath, path.path(), certWatchPtr,
637 *this));
638 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200639 }
640 catch (const InternalFailure& e)
641 {
642 report<InternalFailure>();
643 }
644 catch (const InvalidCertificate& e)
645 {
646 report<InvalidCertificate>(
647 Reason("Existing certificate file is corrupted"));
648 }
649 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500650 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200651 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500652 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200653 try
654 {
655 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100656 bus, certObjectPath + '1', certType, certInstallPath,
657 certInstallPath, certWatchPtr, *this));
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 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500668 }
669}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500670
671void Manager::createRSAPrivateKeyFile()
672{
673 fs::path rsaPrivateKeyFileName =
674 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
675
676 try
677 {
678 if (!fs::exists(rsaPrivateKeyFileName))
679 {
680 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
681 RSA_PRIV_KEY_FILE_NAME);
682 }
683 }
684 catch (const InternalFailure& e)
685 {
686 report<InternalFailure>();
687 }
688}
689
690EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
691{
692 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
693 {
694 log<level::ERR>(
695 "Given Key bit length is not supported",
696 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
697 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
698 elog<InvalidArgument>(
699 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
700 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
701 }
702 fs::path rsaPrivateKeyFileName =
703 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
704
705 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
706 if (!privateKeyFile)
707 {
708 log<level::ERR>("Unable to open RSA private key file to read",
709 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
710 entry("ERRORREASON=%s", strerror(errno)));
711 elog<InternalFailure>();
712 }
713
714 EVP_PKEY_Ptr privateKey(
715 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
716 ::EVP_PKEY_free);
717 std::fclose(privateKeyFile);
718
719 if (!privateKey)
720 {
721 log<level::ERR>("Error occured during PEM_read_PrivateKey call");
722 elog<InternalFailure>();
723 }
724 return privateKey;
725}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100726
727void Manager::storageUpdate()
728{
729 if (certType == phosphor::certs::AUTHORITY)
730 {
731 // Remove symbolic links in the certificate directory
732 for (auto& certPath : fs::directory_iterator(certInstallPath))
733 {
734 try
735 {
736 if (fs::is_symlink(certPath))
737 {
738 fs::remove(certPath);
739 }
740 }
741 catch (const std::exception& e)
742 {
743 log<level::ERR>(
744 "Failed to remove symlink for certificate",
745 entry("ERR=%s", e.what()),
746 entry("SYMLINK=%s", certPath.path().string().c_str()));
747 elog<InternalFailure>();
748 }
749 }
750 }
751
752 for (const auto& cert : installedCerts)
753 {
754 cert->storageUpdate();
755 }
756}
757
758void Manager::reloadOrReset(const UnitsToRestart& unit)
759{
760 if (!unit.empty())
761 {
762 try
763 {
764 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
765 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
766 constexpr auto SYSTEMD_INTERFACE =
767 "org.freedesktop.systemd1.Manager";
768
769 auto method =
770 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
771 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
772 method.append(unit, "replace");
773 bus.call_noreply(method);
774 }
775 catch (const sdbusplus::exception::SdBusError& e)
776 {
777 log<level::ERR>("Failed to reload or restart service",
778 entry("ERR=%s", e.what()),
779 entry("UNIT=%s", unit.c_str()));
780 elog<InternalFailure>();
781 }
782 }
783}
784
785bool Manager::isCertificateUnique(const std::string& filePath,
786 const Certificate* const certToDrop)
787{
788 if (std::any_of(
789 installedCerts.begin(), installedCerts.end(),
790 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
791 return cert.get() != certToDrop && cert->isSame(filePath);
792 }))
793 {
794 return false;
795 }
796 else
797 {
798 return true;
799 }
800}
801
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500802} // namespace certs
803} // namespace phosphor