blob: 2391267134fb50a650e0ed2f1c98af092299d96e [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 {
207 log<level::ERR>("Certificate already exist");
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200208 elog<InternalFailure>();
209 }
210}
211
Marri Devender Raof4682712019-03-19 05:00:28 -0500212std::string Manager::generateCSR(
213 std::vector<std::string> alternativeNames, std::string challengePassword,
214 std::string city, std::string commonName, std::string contactPerson,
215 std::string country, std::string email, std::string givenName,
216 std::string initials, int64_t keyBitLength, std::string keyCurveId,
217 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
218 std::string organization, std::string organizationalUnit, std::string state,
219 std::string surname, std::string unstructuredName)
220{
221 // We support only one CSR.
222 csrPtr.reset(nullptr);
223 auto pid = fork();
224 if (pid == -1)
225 {
226 log<level::ERR>("Error occurred during forking process");
227 report<InternalFailure>();
228 }
229 else if (pid == 0)
230 {
231 try
232 {
233 generateCSRHelper(alternativeNames, challengePassword, city,
234 commonName, contactPerson, country, email,
235 givenName, initials, keyBitLength, keyCurveId,
236 keyPairAlgorithm, keyUsage, organization,
237 organizationalUnit, state, surname,
238 unstructuredName);
239 exit(EXIT_SUCCESS);
240 }
241 catch (const InternalFailure& e)
242 {
243 // commit the error reported in child process and exit
244 // Callback method from SDEvent Loop looks for exit status
245 exit(EXIT_FAILURE);
246 commit<InternalFailure>();
247 }
248 }
249 else
250 {
251 using namespace sdeventplus::source;
252 Child::Callback callback = [this](Child& eventSource,
253 const siginfo_t* si) {
254 eventSource.set_enabled(Enabled::On);
255 if (si->si_status != 0)
256 {
257 this->createCSRObject(Status::FAILURE);
258 }
259 else
260 {
261 this->createCSRObject(Status::SUCCESS);
262 }
263 };
264 try
265 {
266 sigset_t ss;
267 if (sigemptyset(&ss) < 0)
268 {
269 log<level::ERR>("Unable to initialize signal set");
270 elog<InternalFailure>();
271 }
272 if (sigaddset(&ss, SIGCHLD) < 0)
273 {
274 log<level::ERR>("Unable to add signal to signal set");
275 elog<InternalFailure>();
276 }
277
278 // Block SIGCHLD first, so that the event loop can handle it
279 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
280 {
281 log<level::ERR>("Unable to block signal");
282 elog<InternalFailure>();
283 }
284 if (childPtr)
285 {
286 childPtr.reset();
287 }
288 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
289 std::move(callback));
290 }
291 catch (const InternalFailure& e)
292 {
293 commit<InternalFailure>();
294 }
295 }
296 auto csrObjectPath = objectPath + '/' + "csr";
297 return csrObjectPath;
298}
299
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200300std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500301{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200302 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500303}
304
Marri Devender Raof4682712019-03-19 05:00:28 -0500305void Manager::generateCSRHelper(
306 std::vector<std::string> alternativeNames, std::string challengePassword,
307 std::string city, std::string commonName, std::string contactPerson,
308 std::string country, std::string email, std::string givenName,
309 std::string initials, int64_t keyBitLength, std::string keyCurveId,
310 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
311 std::string organization, std::string organizationalUnit, std::string state,
312 std::string surname, std::string unstructuredName)
313{
314 int ret = 0;
315
316 // set version of x509 req
317 int nVersion = 1;
318 // TODO: Issue#6 need to make version number configurable
319 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
320 ret = X509_REQ_set_version(x509Req.get(), nVersion);
321 if (ret == 0)
322 {
323 log<level::ERR>("Error occured during X509_REQ_set_version call");
324 elog<InternalFailure>();
325 }
326
327 // set subject of x509 req
328 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
329
330 if (!alternativeNames.empty())
331 {
332 for (auto& name : alternativeNames)
333 {
334 addEntry(x509Name, "subjectAltName", name);
335 }
336 }
337 addEntry(x509Name, "challengePassword", challengePassword);
338 addEntry(x509Name, "L", city);
339 addEntry(x509Name, "CN", commonName);
340 addEntry(x509Name, "name", contactPerson);
341 addEntry(x509Name, "C", country);
342 addEntry(x509Name, "emailAddress", email);
343 addEntry(x509Name, "GN", givenName);
344 addEntry(x509Name, "initials", initials);
345 addEntry(x509Name, "algorithm", keyPairAlgorithm);
346 if (!keyUsage.empty())
347 {
348 for (auto& usage : keyUsage)
349 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500350 if (isExtendedKeyUsage(usage))
351 {
352 addEntry(x509Name, "extendedKeyUsage", usage);
353 }
354 else
355 {
356 addEntry(x509Name, "keyUsage", usage);
357 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500358 }
359 }
360 addEntry(x509Name, "O", organization);
361 addEntry(x509Name, "ST", state);
362 addEntry(x509Name, "SN", surname);
363 addEntry(x509Name, "unstructuredName", unstructuredName);
364
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500365 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
366
367 log<level::INFO>("Given Key pair algorithm",
368 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
369
370 // Used EC algorithm as default if user did not give algorithm type.
371 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500372 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500373 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500374 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500375 else
376 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500377 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
378 "RSA and EC only");
379 elog<InvalidArgument>(
380 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
381 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
382 }
383
384 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
385 if (ret == 0)
386 {
387 log<level::ERR>("Error occured while setting Public key");
388 elog<InternalFailure>();
389 }
390
391 // Write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500392 writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
Marri Devender Raof4682712019-03-19 05:00:28 -0500393
394 // set sign key of x509 req
395 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500396 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500397 {
398 log<level::ERR>("Error occured while signing key of x509");
399 elog<InternalFailure>();
400 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500401
Marri Devender Raof4682712019-03-19 05:00:28 -0500402 log<level::INFO>("Writing CSR to file");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500403 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
404 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500405}
406
Marri Devender Rao76411052019-08-07 01:25:07 -0500407bool Manager::isExtendedKeyUsage(const std::string& usage)
408{
409 const static std::array<const char*, 6> usageList = {
410 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
411 "Timestamping", "CodeSigning", "EmailProtection"};
412 auto it = std::find_if(
413 usageList.begin(), usageList.end(),
414 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
415 return it != usageList.end();
416}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500417EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500418{
419 int ret = 0;
420 // generate rsa key
421 BIGNUM_Ptr bne(BN_new(), ::BN_free);
422 ret = BN_set_word(bne.get(), RSA_F4);
423 if (ret == 0)
424 {
425 log<level::ERR>("Error occured during BN_set_word call");
426 elog<InternalFailure>();
427 }
428
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500429 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500430 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500431 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500432 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500433 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
434 log<level::INFO>(
435 "KeyBitLength is not given.Hence, using default KeyBitLength",
436 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
437 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500438 }
439 RSA* rsa = RSA_new();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500440 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500441 if (ret != 1)
442 {
443 free(rsa);
444 log<level::ERR>("Error occured during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500445 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500446 elog<InternalFailure>();
447 }
448
449 // set public key of x509 req
450 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500451 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500452 if (ret == 0)
453 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500454 free(rsa);
455 log<level::ERR>("Error occured during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500456 elog<InternalFailure>();
457 }
458
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500459 return pKey;
460}
461
462EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
463{
464 std::string curId(curveId);
465
466 if (curId.empty())
467 {
468 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
469 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
470 log<level::INFO>(
471 "KeyCurveId is not given. Hence using default curve id",
472 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
473 curId = DEFAULT_KEYCURVEID;
474 }
475
476 int ecGrp = OBJ_txt2nid(curId.c_str());
477
478 if (ecGrp == NID_undef)
479 {
480 log<level::ERR>(
481 "Error occured during convert the curve id string format into NID",
482 entry("KEYCURVEID=%s", curId.c_str()));
483 elog<InternalFailure>();
484 }
485
486 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
487
488 if (ecKey == NULL)
489 {
490 log<level::ERR>(
491 "Error occured during create the EC_Key object from NID",
492 entry("ECGROUP=%d", ecGrp));
493 elog<InternalFailure>();
494 }
495
496 // If you want to save a key and later load it with
497 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
498 // flag on the key.
499 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
500
501 int ret = EC_KEY_generate_key(ecKey);
502
503 if (ret == 0)
504 {
505 EC_KEY_free(ecKey);
506 log<level::ERR>("Error occured during generate EC key");
507 elog<InternalFailure>();
508 }
509
510 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
511 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
512 if (ret == 0)
513 {
514 EC_KEY_free(ecKey);
515 log<level::ERR>("Error occured during assign EC Key into EVP");
516 elog<InternalFailure>();
517 }
518
519 return pKey;
520}
521
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500522void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
523 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500524{
525 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500526 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500527 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500528
529 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
530 if (fp == NULL)
531 {
Marri Devender Raof4682712019-03-19 05:00:28 -0500532 log<level::ERR>("Error occured creating private key file");
533 elog<InternalFailure>();
534 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500535 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500536 std::fclose(fp);
537 if (ret == 0)
538 {
539 log<level::ERR>("Error occured while writing private key to file");
540 elog<InternalFailure>();
541 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500542}
543
544void Manager::addEntry(X509_NAME* x509Name, const char* field,
545 const std::string& bytes)
546{
547 if (bytes.empty())
548 {
549 return;
550 }
551 int ret = X509_NAME_add_entry_by_txt(
552 x509Name, field, MBSTRING_ASC,
553 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
554 if (ret != 1)
555 {
556 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
557 entry("VALUE=%s", bytes.c_str()));
558 elog<InternalFailure>();
559 }
560}
561
562void Manager::createCSRObject(const Status& status)
563{
564 if (csrPtr)
565 {
566 csrPtr.reset(nullptr);
567 }
568 auto csrObjectPath = objectPath + '/' + "csr";
569 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
570 certInstallPath.c_str(), status);
571}
572
573void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
574{
575 if (fs::exists(filePath))
576 {
577 log<level::INFO>("Removing the existing file",
578 entry("FILENAME=%s", filePath.c_str()));
579 if (!fs::remove(filePath.c_str()))
580 {
581 log<level::ERR>("Unable to remove the file",
582 entry("FILENAME=%s", filePath.c_str()));
583 elog<InternalFailure>();
584 }
585 }
586
587 FILE* fp = NULL;
588
589 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
590 {
591 log<level::ERR>("Error opening the file to write the CSR",
592 entry("FILENAME=%s", filePath.c_str()));
593 elog<InternalFailure>();
594 }
595
596 int rc = PEM_write_X509_REQ(fp, x509Req.get());
597 if (!rc)
598 {
599 log<level::ERR>("PEM write routine failed",
600 entry("FILENAME=%s", filePath.c_str()));
601 std::fclose(fp);
602 elog<InternalFailure>();
603 }
604 std::fclose(fp);
605}
606
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200607void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500608{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200609 auto certObjectPath = objectPath + '/';
610
611 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500612 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100613 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200614 if (!fs::is_directory(certInstallPath))
615 {
616 log<level::ERR>("Certificate installation path exists and it is "
617 "not a directory");
618 elog<InternalFailure>();
619 return;
620 }
621
622 for (auto& path : fs::directory_iterator(certInstallPath))
623 {
624 try
625 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100626 // Assume here any regular file located in certificate directory
627 // contains certificates body. Do not want to use soft links
628 // would add value.
629 if (fs::is_regular_file(path))
630 {
631 installedCerts.emplace_back(std::make_unique<Certificate>(
632 bus, certObjectPath + std::to_string(certIdCounter++),
633 certType, certInstallPath, path.path(), certWatchPtr,
634 *this));
635 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200636 }
637 catch (const InternalFailure& e)
638 {
639 report<InternalFailure>();
640 }
641 catch (const InvalidCertificate& e)
642 {
643 report<InvalidCertificate>(
644 Reason("Existing certificate file is corrupted"));
645 }
646 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500647 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200648 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500649 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200650 try
651 {
652 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100653 bus, certObjectPath + '1', certType, certInstallPath,
654 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200655 }
656 catch (const InternalFailure& e)
657 {
658 report<InternalFailure>();
659 }
660 catch (const InvalidCertificate& e)
661 {
662 report<InvalidCertificate>(
663 Reason("Existing certificate file is corrupted"));
664 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500665 }
666}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500667
668void Manager::createRSAPrivateKeyFile()
669{
670 fs::path rsaPrivateKeyFileName =
671 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
672
673 try
674 {
675 if (!fs::exists(rsaPrivateKeyFileName))
676 {
677 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
678 RSA_PRIV_KEY_FILE_NAME);
679 }
680 }
681 catch (const InternalFailure& e)
682 {
683 report<InternalFailure>();
684 }
685}
686
687EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
688{
689 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
690 {
691 log<level::ERR>(
692 "Given Key bit length is not supported",
693 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
694 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
695 elog<InvalidArgument>(
696 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
697 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
698 }
699 fs::path rsaPrivateKeyFileName =
700 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
701
702 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
703 if (!privateKeyFile)
704 {
705 log<level::ERR>("Unable to open RSA private key file to read",
706 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
707 entry("ERRORREASON=%s", strerror(errno)));
708 elog<InternalFailure>();
709 }
710
711 EVP_PKEY_Ptr privateKey(
712 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
713 ::EVP_PKEY_free);
714 std::fclose(privateKeyFile);
715
716 if (!privateKey)
717 {
718 log<level::ERR>("Error occured during PEM_read_PrivateKey call");
719 elog<InternalFailure>();
720 }
721 return privateKey;
722}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100723
724void Manager::storageUpdate()
725{
726 if (certType == phosphor::certs::AUTHORITY)
727 {
728 // Remove symbolic links in the certificate directory
729 for (auto& certPath : fs::directory_iterator(certInstallPath))
730 {
731 try
732 {
733 if (fs::is_symlink(certPath))
734 {
735 fs::remove(certPath);
736 }
737 }
738 catch (const std::exception& e)
739 {
740 log<level::ERR>(
741 "Failed to remove symlink for certificate",
742 entry("ERR=%s", e.what()),
743 entry("SYMLINK=%s", certPath.path().string().c_str()));
744 elog<InternalFailure>();
745 }
746 }
747 }
748
749 for (const auto& cert : installedCerts)
750 {
751 cert->storageUpdate();
752 }
753}
754
755void Manager::reloadOrReset(const UnitsToRestart& unit)
756{
757 if (!unit.empty())
758 {
759 try
760 {
761 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
762 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
763 constexpr auto SYSTEMD_INTERFACE =
764 "org.freedesktop.systemd1.Manager";
765
766 auto method =
767 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
768 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
769 method.append(unit, "replace");
770 bus.call_noreply(method);
771 }
772 catch (const sdbusplus::exception::SdBusError& e)
773 {
774 log<level::ERR>("Failed to reload or restart service",
775 entry("ERR=%s", e.what()),
776 entry("UNIT=%s", unit.c_str()));
777 elog<InternalFailure>();
778 }
779 }
780}
781
782bool Manager::isCertificateUnique(const std::string& filePath,
783 const Certificate* const certToDrop)
784{
785 if (std::any_of(
786 installedCerts.begin(), installedCerts.end(),
787 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
788 return cert.get() != certToDrop && cert->isSame(filePath);
789 }))
790 {
791 return false;
792 }
793 else
794 {
795 return true;
796 }
797}
798
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500799} // namespace certs
800} // namespace phosphor