blob: 9adca200bde9e5bda6dae4adf2298e5baedebeef [file] [log] [blame]
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05001#include "certs_manager.hpp"
2
Patrick Williams26fb83e2021-12-14 14:08:28 -06003#include <openssl/evp.h>
Marri Devender Raof4682712019-03-19 05:00:28 -05004#include <openssl/pem.h>
5#include <unistd.h>
6
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +02007#include <algorithm>
Marri Devender Rao6ceec402019-02-01 03:15:19 -06008#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -05009#include <xyz/openbmc_project/Certs/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050010#include <xyz/openbmc_project/Common/error.hpp>
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010011
Nan Zhoue1289ad2021-12-28 11:02:56 -080012namespace phosphor::certs
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050013{
Marri Devender Rao13965112019-02-27 08:47:12 -060014using InternalFailure =
15 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050016using InvalidCertificate =
17 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
18using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050019
Marri Devender Raof4682712019-03-19 05:00:28 -050020using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
21using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050022using InvalidArgument =
23 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
24using Argument = xyz::openbmc_project::Common::InvalidArgument;
25
26constexpr auto SUPPORTED_KEYBITLENGTH = 2048;
Marri Devender Raof4682712019-03-19 05:00:28 -050027
28Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
29 const char* path, const CertificateType& type,
30 UnitsToRestart&& unit, CertInstallPath&& installPath) :
Marri Devender Rao6ceec402019-02-01 03:15:19 -060031 Ifaces(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -050032 bus(bus), event(event), objectPath(path), certType(type),
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050033 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath)),
34 certParentInstallPath(fs::path(certInstallPath).parent_path())
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050035{
Marri Devender Raob57d75e2019-07-25 04:56:21 -050036 try
37 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050038 // Create certificate directory if not existing.
Nan Zhoubf3cf752021-12-28 11:02:07 -080039 // Set correct certificate directory permissions.
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050040 fs::path certDirectory;
41 try
Marri Devender Raob57d75e2019-07-25 04:56:21 -050042 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050043 if (certType == AUTHORITY)
44 {
45 certDirectory = certInstallPath;
46 }
47 else
48 {
49 certDirectory = certParentInstallPath;
50 }
51
52 if (!fs::exists(certDirectory))
53 {
54 fs::create_directories(certDirectory);
55 }
56
57 auto permission = fs::perms::owner_read | fs::perms::owner_write |
58 fs::perms::owner_exec;
59 fs::permissions(certDirectory, permission,
60 fs::perm_options::replace);
61 storageUpdate();
62 }
Patrick Williams71957992021-10-06 14:42:52 -050063 catch (const fs::filesystem_error& e)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -050064 {
65 log<level::ERR>(
66 "Failed to create directory", entry("ERR=%s", e.what()),
67 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
68 report<InternalFailure>();
69 }
70
71 // Generating RSA private key file if certificate type is server/client
72 if (certType != AUTHORITY)
73 {
74 createRSAPrivateKeyFile();
75 }
76
77 // restore any existing certificates
78 createCertificates();
79
80 // watch is not required for authority certificates
81 if (certType != AUTHORITY)
82 {
83 // watch for certificate file create/replace
84 certWatchPtr = std::make_unique<
85 Watch>(event, certInstallPath, [this]() {
86 try
87 {
88 // if certificate file existing update it
89 if (!installedCerts.empty())
90 {
91 log<level::INFO>("Inotify callback to update "
92 "certificate properties");
93 installedCerts[0]->populateProperties();
94 }
95 else
96 {
97 log<level::INFO>(
98 "Inotify callback to create certificate object");
99 createCertificates();
100 }
101 }
102 catch (const InternalFailure& e)
103 {
104 commit<InternalFailure>();
105 }
106 catch (const InvalidCertificate& e)
107 {
108 commit<InvalidCertificate>();
109 }
110 });
Marri Devender Raob57d75e2019-07-25 04:56:21 -0500111 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100112 else
113 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500114 try
115 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500116 const std::string singleCertPath = "/etc/ssl/certs/Root-CA.pem";
117 if (fs::exists(singleCertPath) && !fs::is_empty(singleCertPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500118 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500119 log<level::NOTICE>(
120 "Legacy certificate detected, will be installed from: ",
121 entry("SINGLE_CERTPATH=%s", singleCertPath.c_str()));
122 install(singleCertPath);
123 if (!fs::remove(singleCertPath))
124 {
125 log<level::ERR>(
126 "Unable to remove old certificate from: ",
127 entry("SINGLE_CERTPATH=%s",
128 singleCertPath.c_str()));
129 elog<InternalFailure>();
130 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500131 }
132 }
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500133 catch (const std::exception& ex)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500134 {
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500135 log<level::ERR>("Error in restoring legacy certificate",
136 entry("ERROR_STR=%s", ex.what()));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200137 }
138 }
139 }
Patrick Williams71957992021-10-06 14:42:52 -0500140 catch (const std::exception& ex)
Marri Devender Raodb5c6fc2020-03-10 13:27:49 -0500141 {
142 log<level::ERR>("Error in certificate manager constructor",
143 entry("ERROR_STR=%s", ex.what()));
144 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500145}
146
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200147std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500148{
Marri Devender Rao13965112019-02-27 08:47:12 -0600149 using NotAllowed =
150 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
151 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200152
153 if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600154 {
155 elog<NotAllowed>(Reason("Certificate already exist"));
156 }
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200157 else if (certType == phosphor::certs::AUTHORITY &&
Nan Zhou718eef32021-12-28 11:03:30 -0800158 installedCerts.size() >= maxNumAuthorityCertificates)
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200159 {
160 elog<NotAllowed>(Reason("Certificates limit reached"));
161 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500162
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100163 std::string certObjectPath;
164 if (isCertificateUnique(filePath))
165 {
166 certObjectPath = objectPath + '/' + std::to_string(certIdCounter);
167 installedCerts.emplace_back(std::make_unique<Certificate>(
168 bus, certObjectPath, certType, certInstallPath, filePath,
169 certWatchPtr, *this));
170 reloadOrReset(unitToRestart);
171 certIdCounter++;
172 }
173 else
174 {
175 elog<NotAllowed>(Reason("Certificate already exist"));
176 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200177
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200178 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500179}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500180
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200181void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500182{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600183 // TODO: #Issue 4 when a certificate is deleted system auto generates
184 // certificate file. At present we are not supporting creation of
185 // certificate object for the auto-generated certificate file as
186 // deletion if only applicable for REST server and Bmcweb does not allow
187 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200188 installedCerts.clear();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100189 storageUpdate();
190 reloadOrReset(unitToRestart);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500191}
Marri Devender Raof4682712019-03-19 05:00:28 -0500192
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100193void Manager::deleteCertificate(const Certificate* const certificate)
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200194{
195 std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
196 std::find_if(installedCerts.begin(), installedCerts.end(),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100197 [certificate](std::unique_ptr<Certificate> const& cert) {
198 return (cert.get() == certificate);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200199 });
200 if (certIt != installedCerts.end())
201 {
202 installedCerts.erase(certIt);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100203 storageUpdate();
204 reloadOrReset(unitToRestart);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200205 }
206 else
207 {
208 log<level::ERR>("Certificate does not exist",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100209 entry("ID=%s", certificate->getCertId().c_str()));
210 elog<InternalFailure>();
211 }
212}
213
214void Manager::replaceCertificate(Certificate* const certificate,
215 const std::string& filePath)
216{
217 if (isCertificateUnique(filePath, certificate))
218 {
219 certificate->install(filePath);
220 storageUpdate();
221 reloadOrReset(unitToRestart);
222 }
223 else
224 {
Zbigniew Lukwinski15cbbec2020-01-16 13:50:45 +0100225 using NotAllowed =
226 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
227 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
228
229 elog<NotAllowed>(Reason("Certificate already exist"));
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200230 }
231}
232
Marri Devender Raof4682712019-03-19 05:00:28 -0500233std::string Manager::generateCSR(
234 std::vector<std::string> alternativeNames, std::string challengePassword,
235 std::string city, std::string commonName, std::string contactPerson,
236 std::string country, std::string email, std::string givenName,
237 std::string initials, int64_t keyBitLength, std::string keyCurveId,
238 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
239 std::string organization, std::string organizationalUnit, std::string state,
240 std::string surname, std::string unstructuredName)
241{
242 // We support only one CSR.
243 csrPtr.reset(nullptr);
244 auto pid = fork();
245 if (pid == -1)
246 {
247 log<level::ERR>("Error occurred during forking process");
248 report<InternalFailure>();
249 }
250 else if (pid == 0)
251 {
252 try
253 {
254 generateCSRHelper(alternativeNames, challengePassword, city,
255 commonName, contactPerson, country, email,
256 givenName, initials, keyBitLength, keyCurveId,
257 keyPairAlgorithm, keyUsage, organization,
258 organizationalUnit, state, surname,
259 unstructuredName);
260 exit(EXIT_SUCCESS);
261 }
262 catch (const InternalFailure& e)
263 {
264 // commit the error reported in child process and exit
265 // Callback method from SDEvent Loop looks for exit status
266 exit(EXIT_FAILURE);
267 commit<InternalFailure>();
268 }
Ramesh Iyyard2393f22020-10-29 09:46:51 -0500269 catch (const InvalidArgument& e)
270 {
271 // commit the error reported in child process and exit
272 // Callback method from SDEvent Loop looks for exit status
273 exit(EXIT_FAILURE);
274 commit<InvalidArgument>();
275 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500276 }
277 else
278 {
279 using namespace sdeventplus::source;
280 Child::Callback callback = [this](Child& eventSource,
281 const siginfo_t* si) {
282 eventSource.set_enabled(Enabled::On);
283 if (si->si_status != 0)
284 {
285 this->createCSRObject(Status::FAILURE);
286 }
287 else
288 {
289 this->createCSRObject(Status::SUCCESS);
290 }
291 };
292 try
293 {
294 sigset_t ss;
295 if (sigemptyset(&ss) < 0)
296 {
297 log<level::ERR>("Unable to initialize signal set");
298 elog<InternalFailure>();
299 }
300 if (sigaddset(&ss, SIGCHLD) < 0)
301 {
302 log<level::ERR>("Unable to add signal to signal set");
303 elog<InternalFailure>();
304 }
305
306 // Block SIGCHLD first, so that the event loop can handle it
Nan Zhoucfb58022021-12-28 11:02:26 -0800307 if (sigprocmask(SIG_BLOCK, &ss, nullptr) < 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500308 {
309 log<level::ERR>("Unable to block signal");
310 elog<InternalFailure>();
311 }
312 if (childPtr)
313 {
314 childPtr.reset();
315 }
316 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
317 std::move(callback));
318 }
319 catch (const InternalFailure& e)
320 {
321 commit<InternalFailure>();
322 }
323 }
324 auto csrObjectPath = objectPath + '/' + "csr";
325 return csrObjectPath;
326}
327
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200328std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500329{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200330 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500331}
332
Marri Devender Raof4682712019-03-19 05:00:28 -0500333void Manager::generateCSRHelper(
334 std::vector<std::string> alternativeNames, std::string challengePassword,
335 std::string city, std::string commonName, std::string contactPerson,
336 std::string country, std::string email, std::string givenName,
337 std::string initials, int64_t keyBitLength, std::string keyCurveId,
338 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
339 std::string organization, std::string organizationalUnit, std::string state,
340 std::string surname, std::string unstructuredName)
341{
342 int ret = 0;
343
344 // set version of x509 req
345 int nVersion = 1;
346 // TODO: Issue#6 need to make version number configurable
347 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
348 ret = X509_REQ_set_version(x509Req.get(), nVersion);
349 if (ret == 0)
350 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800351 log<level::ERR>("Error occurred during X509_REQ_set_version call");
Marri Devender Raof4682712019-03-19 05:00:28 -0500352 elog<InternalFailure>();
353 }
354
355 // set subject of x509 req
356 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
357
358 if (!alternativeNames.empty())
359 {
360 for (auto& name : alternativeNames)
361 {
362 addEntry(x509Name, "subjectAltName", name);
363 }
364 }
365 addEntry(x509Name, "challengePassword", challengePassword);
366 addEntry(x509Name, "L", city);
367 addEntry(x509Name, "CN", commonName);
368 addEntry(x509Name, "name", contactPerson);
369 addEntry(x509Name, "C", country);
370 addEntry(x509Name, "emailAddress", email);
371 addEntry(x509Name, "GN", givenName);
372 addEntry(x509Name, "initials", initials);
373 addEntry(x509Name, "algorithm", keyPairAlgorithm);
374 if (!keyUsage.empty())
375 {
376 for (auto& usage : keyUsage)
377 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500378 if (isExtendedKeyUsage(usage))
379 {
380 addEntry(x509Name, "extendedKeyUsage", usage);
381 }
382 else
383 {
384 addEntry(x509Name, "keyUsage", usage);
385 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500386 }
387 }
388 addEntry(x509Name, "O", organization);
Jayanth Othayothdc91fb62021-05-04 23:17:47 -0500389 addEntry(x509Name, "OU", organizationalUnit);
Marri Devender Raof4682712019-03-19 05:00:28 -0500390 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 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800416 log<level::ERR>("Error occurred while setting Public key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500417 elog<InternalFailure>();
418 }
419
420 // Write private key to file
Nan Zhou718eef32021-12-28 11:03:30 -0800421 writePrivateKey(pKey, defaultPrivateKeyFileName);
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 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800427 log<level::ERR>("Error occurred while signing key of x509");
Marri Devender Raof4682712019-03-19 05:00:28 -0500428 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");
Nan Zhou718eef32021-12-28 11:03:30 -0800432 fs::path csrFilePath = certParentInstallPath / defaultCSRFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500433 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{
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500448 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500449 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500450 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500451 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500452 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
453 log<level::INFO>(
454 "KeyBitLength is not given.Hence, using default KeyBitLength",
455 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
456 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500457 }
Patrick Williams26fb83e2021-12-14 14:08:28 -0600458
459#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
460
461 // generate rsa key
462 BIGNUM_Ptr bne(BN_new(), ::BN_free);
463 auto ret = BN_set_word(bne.get(), RSA_F4);
464 if (ret == 0)
465 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800466 log<level::ERR>("Error occurred during BN_set_word call");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600467 elog<InternalFailure>();
468 }
469
Marri Devender Raof4682712019-03-19 05:00:28 -0500470 RSA* rsa = RSA_new();
Nan Zhoucfb58022021-12-28 11:02:26 -0800471 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500472 if (ret != 1)
473 {
474 free(rsa);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800475 log<level::ERR>("Error occurred during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500476 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500477 elog<InternalFailure>();
478 }
479
480 // set public key of x509 req
481 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500482 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500483 if (ret == 0)
484 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500485 free(rsa);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800486 log<level::ERR>("Error occurred during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500487 elog<InternalFailure>();
488 }
489
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500490 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600491
492#else
493 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
494 EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, nullptr), &::EVP_PKEY_CTX_free);
495 if (!ctx)
496 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800497 log<level::ERR>("Error occurred creating EVP_PKEY_CTX from algorithm");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600498 elog<InternalFailure>();
499 }
500
501 if ((EVP_PKEY_keygen_init(ctx.get()) <= 0) ||
502 (EVP_PKEY_CTX_set_rsa_keygen_bits(ctx.get(), keyBitLen) <= 0))
503
504 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800505 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600506 elog<InternalFailure>();
507 }
508
509 EVP_PKEY* pKey = nullptr;
510 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
511 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800512 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600513 elog<InternalFailure>();
514 }
515
516 return {pKey, &::EVP_PKEY_free};
517#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500518}
519
520EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
521{
522 std::string curId(curveId);
523
524 if (curId.empty())
525 {
526 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
527 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
528 log<level::INFO>(
529 "KeyCurveId is not given. Hence using default curve id",
530 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
531 curId = DEFAULT_KEYCURVEID;
532 }
533
534 int ecGrp = OBJ_txt2nid(curId.c_str());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500535 if (ecGrp == NID_undef)
536 {
537 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800538 "Error occurred during convert the curve id string format into NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500539 entry("KEYCURVEID=%s", curId.c_str()));
540 elog<InternalFailure>();
541 }
542
Patrick Williams26fb83e2021-12-14 14:08:28 -0600543#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
544
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500545 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
546
Nan Zhoucfb58022021-12-28 11:02:26 -0800547 if (ecKey == nullptr)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500548 {
549 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800550 "Error occurred during create the EC_Key object from NID",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500551 entry("ECGROUP=%d", ecGrp));
552 elog<InternalFailure>();
553 }
554
555 // If you want to save a key and later load it with
556 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
557 // flag on the key.
558 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
559
560 int ret = EC_KEY_generate_key(ecKey);
561
562 if (ret == 0)
563 {
564 EC_KEY_free(ecKey);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800565 log<level::ERR>("Error occurred during generate EC key");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500566 elog<InternalFailure>();
567 }
568
569 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
570 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
571 if (ret == 0)
572 {
573 EC_KEY_free(ecKey);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800574 log<level::ERR>("Error occurred during assign EC Key into EVP");
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500575 elog<InternalFailure>();
576 }
577
578 return pKey;
Patrick Williams26fb83e2021-12-14 14:08:28 -0600579
580#else
581 auto holder_of_key = [](EVP_PKEY* key) {
582 return std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>{
583 key, &::EVP_PKEY_free};
584 };
585
586 // Create context to set up curve parameters.
587 auto ctx = std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)>(
588 EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free);
589 if (!ctx)
590 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800591 log<level::ERR>("Error occurred creating EVP_PKEY_CTX for params");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600592 elog<InternalFailure>();
593 }
594
595 // Set up curve parameters.
596 EVP_PKEY* params = nullptr;
597
598 if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
599 (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
600 0) ||
601 (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), ecGrp) <= 0) ||
602 (EVP_PKEY_paramgen(ctx.get(), &params) <= 0))
603 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800604 log<level::ERR>("Error occurred setting curve parameters");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600605 elog<InternalFailure>();
606 }
607
608 // Move parameters to RAII holder.
609 auto pparms = holder_of_key(params);
610
611 // Create new context for key.
612 ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
613
614 if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
615 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800616 log<level::ERR>("Error occurred initializing keygen context");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600617 elog<InternalFailure>();
618 }
619
620 EVP_PKEY* pKey = nullptr;
621 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
622 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800623 log<level::ERR>("Error occurred during generate EC key");
Patrick Williams26fb83e2021-12-14 14:08:28 -0600624 elog<InternalFailure>();
625 }
626
627 return holder_of_key(pKey);
628#endif
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500629}
630
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500631void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
632 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500633{
634 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500635 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500636 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500637
638 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
Nan Zhoucfb58022021-12-28 11:02:26 -0800639 if (fp == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500640 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800641 log<level::ERR>("Error occurred creating private key file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500642 elog<InternalFailure>();
643 }
Nan Zhoucfb58022021-12-28 11:02:26 -0800644 int ret =
645 PEM_write_PrivateKey(fp, pKey.get(), nullptr, nullptr, 0, 0, nullptr);
Marri Devender Raof4682712019-03-19 05:00:28 -0500646 std::fclose(fp);
647 if (ret == 0)
648 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800649 log<level::ERR>("Error occurred while writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500650 elog<InternalFailure>();
651 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500652}
653
654void Manager::addEntry(X509_NAME* x509Name, const char* field,
655 const std::string& bytes)
656{
657 if (bytes.empty())
658 {
659 return;
660 }
661 int ret = X509_NAME_add_entry_by_txt(
662 x509Name, field, MBSTRING_ASC,
663 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
664 if (ret != 1)
665 {
666 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
667 entry("VALUE=%s", bytes.c_str()));
668 elog<InternalFailure>();
669 }
670}
671
672void Manager::createCSRObject(const Status& status)
673{
674 if (csrPtr)
675 {
676 csrPtr.reset(nullptr);
677 }
678 auto csrObjectPath = objectPath + '/' + "csr";
679 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
680 certInstallPath.c_str(), status);
681}
682
683void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
684{
685 if (fs::exists(filePath))
686 {
687 log<level::INFO>("Removing the existing file",
688 entry("FILENAME=%s", filePath.c_str()));
689 if (!fs::remove(filePath.c_str()))
690 {
691 log<level::ERR>("Unable to remove the file",
692 entry("FILENAME=%s", filePath.c_str()));
693 elog<InternalFailure>();
694 }
695 }
696
Nan Zhoucfb58022021-12-28 11:02:26 -0800697 FILE* fp = nullptr;
Marri Devender Raof4682712019-03-19 05:00:28 -0500698
Nan Zhoucfb58022021-12-28 11:02:26 -0800699 if ((fp = std::fopen(filePath.c_str(), "w")) == nullptr)
Marri Devender Raof4682712019-03-19 05:00:28 -0500700 {
701 log<level::ERR>("Error opening the file to write the CSR",
702 entry("FILENAME=%s", filePath.c_str()));
703 elog<InternalFailure>();
704 }
705
706 int rc = PEM_write_X509_REQ(fp, x509Req.get());
707 if (!rc)
708 {
709 log<level::ERR>("PEM write routine failed",
710 entry("FILENAME=%s", filePath.c_str()));
711 std::fclose(fp);
712 elog<InternalFailure>();
713 }
714 std::fclose(fp);
715}
716
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200717void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500718{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200719 auto certObjectPath = objectPath + '/';
720
721 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500722 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100723 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200724 if (!fs::is_directory(certInstallPath))
725 {
726 log<level::ERR>("Certificate installation path exists and it is "
727 "not a directory");
728 elog<InternalFailure>();
729 return;
730 }
731
732 for (auto& path : fs::directory_iterator(certInstallPath))
733 {
734 try
735 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100736 // Assume here any regular file located in certificate directory
737 // contains certificates body. Do not want to use soft links
738 // would add value.
739 if (fs::is_regular_file(path))
740 {
741 installedCerts.emplace_back(std::make_unique<Certificate>(
742 bus, certObjectPath + std::to_string(certIdCounter++),
743 certType, certInstallPath, path.path(), certWatchPtr,
744 *this));
745 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200746 }
747 catch (const InternalFailure& e)
748 {
749 report<InternalFailure>();
750 }
751 catch (const InvalidCertificate& e)
752 {
753 report<InvalidCertificate>(
754 Reason("Existing certificate file is corrupted"));
755 }
756 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500757 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200758 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500759 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200760 try
761 {
762 installedCerts.emplace_back(std::make_unique<Certificate>(
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100763 bus, certObjectPath + '1', certType, certInstallPath,
764 certInstallPath, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200765 }
766 catch (const InternalFailure& e)
767 {
768 report<InternalFailure>();
769 }
770 catch (const InvalidCertificate& e)
771 {
772 report<InvalidCertificate>(
773 Reason("Existing certificate file is corrupted"));
774 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500775 }
776}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500777
778void Manager::createRSAPrivateKeyFile()
779{
780 fs::path rsaPrivateKeyFileName =
Nan Zhou718eef32021-12-28 11:03:30 -0800781 certParentInstallPath / defaultRSAPrivateKeyFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500782
783 try
784 {
785 if (!fs::exists(rsaPrivateKeyFileName))
786 {
787 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
Nan Zhou718eef32021-12-28 11:03:30 -0800788 defaultRSAPrivateKeyFileName);
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500789 }
790 }
791 catch (const InternalFailure& e)
792 {
793 report<InternalFailure>();
794 }
795}
796
797EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
798{
799 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
800 {
801 log<level::ERR>(
802 "Given Key bit length is not supported",
803 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
804 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
805 elog<InvalidArgument>(
806 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
807 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
808 }
809 fs::path rsaPrivateKeyFileName =
Nan Zhou718eef32021-12-28 11:03:30 -0800810 certParentInstallPath / defaultRSAPrivateKeyFileName;
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500811
812 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
813 if (!privateKeyFile)
814 {
815 log<level::ERR>("Unable to open RSA private key file to read",
816 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
817 entry("ERRORREASON=%s", strerror(errno)));
818 elog<InternalFailure>();
819 }
820
821 EVP_PKEY_Ptr privateKey(
822 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
823 ::EVP_PKEY_free);
824 std::fclose(privateKeyFile);
825
826 if (!privateKey)
827 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800828 log<level::ERR>("Error occurred during PEM_read_PrivateKey call");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500829 elog<InternalFailure>();
830 }
831 return privateKey;
832}
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100833
834void Manager::storageUpdate()
835{
836 if (certType == phosphor::certs::AUTHORITY)
837 {
838 // Remove symbolic links in the certificate directory
839 for (auto& certPath : fs::directory_iterator(certInstallPath))
840 {
841 try
842 {
843 if (fs::is_symlink(certPath))
844 {
845 fs::remove(certPath);
846 }
847 }
848 catch (const std::exception& e)
849 {
850 log<level::ERR>(
851 "Failed to remove symlink for certificate",
852 entry("ERR=%s", e.what()),
853 entry("SYMLINK=%s", certPath.path().string().c_str()));
854 elog<InternalFailure>();
855 }
856 }
857 }
858
859 for (const auto& cert : installedCerts)
860 {
861 cert->storageUpdate();
862 }
863}
864
865void Manager::reloadOrReset(const UnitsToRestart& unit)
866{
867 if (!unit.empty())
868 {
869 try
870 {
871 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
872 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
873 constexpr auto SYSTEMD_INTERFACE =
874 "org.freedesktop.systemd1.Manager";
875
876 auto method =
877 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
878 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
879 method.append(unit, "replace");
880 bus.call_noreply(method);
881 }
Patrick Williamsca128112021-09-02 09:36:07 -0500882 catch (const sdbusplus::exception::exception& e)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100883 {
884 log<level::ERR>("Failed to reload or restart service",
885 entry("ERR=%s", e.what()),
886 entry("UNIT=%s", unit.c_str()));
887 elog<InternalFailure>();
888 }
889 }
890}
891
892bool Manager::isCertificateUnique(const std::string& filePath,
893 const Certificate* const certToDrop)
894{
895 if (std::any_of(
896 installedCerts.begin(), installedCerts.end(),
897 [&filePath, certToDrop](std::unique_ptr<Certificate> const& cert) {
898 return cert.get() != certToDrop && cert->isSame(filePath);
899 }))
900 {
901 return false;
902 }
903 else
904 {
905 return true;
906 }
907}
908
Nan Zhoue1289ad2021-12-28 11:02:56 -0800909} // namespace phosphor::certs