blob: ca6f35650f21106fd3b5d70190584cad371e069d [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>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050010namespace phosphor
11{
12namespace certs
13{
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{
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010036 // Create certificate directory if not existing.
37 // Set correct certificate directory permitions.
38 fs::path certDirectory;
Marri Devender Raob57d75e2019-07-25 04:56:21 -050039 try
40 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010041 if (certType == AUTHORITY)
Marri Devender Raob57d75e2019-07-25 04:56:21 -050042 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010043 certDirectory = certInstallPath;
Marri Devender Raob57d75e2019-07-25 04:56:21 -050044 }
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010045 else
46 {
47 certDirectory = certParentInstallPath;
48 }
49
50 if (!fs::exists(certDirectory))
51 {
52 fs::create_directories(certDirectory);
53 }
54
Marri Devender Rao667286e2019-10-29 03:22:46 -050055 auto permission = fs::perms::owner_read | fs::perms::owner_write |
56 fs::perms::owner_exec;
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +010057 fs::permissions(certDirectory, permission, fs::perm_options::replace);
Marri Devender Raob57d75e2019-07-25 04:56:21 -050058 }
59 catch (fs::filesystem_error& e)
60 {
61 log<level::ERR>("Failed to create directory", entry("ERR=%s", e.what()),
62 entry("DIRECTORY=%s", certParentInstallPath.c_str()));
63 report<InternalFailure>();
64 }
65
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -050066 // Generating RSA private key file if certificate type is server/client
67 if (certType != AUTHORITY)
68 {
69 createRSAPrivateKeyFile();
70 }
71
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050072 // restore any existing certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +020073 createCertificates();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050074
75 // watch is not required for authority certificates
76 if (certType != AUTHORITY)
77 {
78 // watch for certificate file create/replace
79 certWatchPtr = std::make_unique<
80 Watch>(event, certInstallPath, [this]() {
81 try
82 {
83 // if certificate file existing update it
Kowalski, Kamildb029c92019-07-08 17:09:39 +020084 if (!installedCerts.empty())
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050085 {
86 log<level::INFO>(
87 "Inotify callback to update certificate properties");
Kowalski, Kamildb029c92019-07-08 17:09:39 +020088 installedCerts[0]->populateProperties();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050089 }
90 else
91 {
92 log<level::INFO>(
93 "Inotify callback to create certificate object");
Kowalski, Kamildb029c92019-07-08 17:09:39 +020094 createCertificates();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050095 }
96 }
97 catch (const InternalFailure& e)
98 {
99 commit<InternalFailure>();
100 }
101 catch (const InvalidCertificate& e)
102 {
103 commit<InvalidCertificate>();
104 }
105 });
Marri Devender Raobf7c5882019-02-27 08:41:07 -0600106 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200107 else
108 {
109 const std::string signleCertPath = "/etc/ssl/certs/Root-CA.pem";
110 if (fs::exists(signleCertPath) && !fs::is_empty(signleCertPath))
111 {
112 log<level::NOTICE>(
113 "Legacy certificate detected, will be installed from: ",
114 entry("SINGLE_CERTPATH=%s", signleCertPath.c_str()));
115 install(signleCertPath);
116 if (!fs::remove(signleCertPath))
117 {
118 log<level::ERR>(
119 "Unable to remove old certificate from: ",
120 entry("SINGLE_CERTPATH=%s", signleCertPath.c_str()));
121 elog<InternalFailure>();
122 }
123 }
124 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500125}
126
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200127std::string Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500128{
Marri Devender Rao13965112019-02-27 08:47:12 -0600129 using NotAllowed =
130 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
131 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200132
133 if (certType != phosphor::certs::AUTHORITY && !installedCerts.empty())
Marri Devender Rao13965112019-02-27 08:47:12 -0600134 {
135 elog<NotAllowed>(Reason("Certificate already exist"));
136 }
Zbigniew Lukwinski3b07b772019-10-09 11:43:34 +0200137 else if (certType == phosphor::certs::AUTHORITY &&
138 installedCerts.size() >= AUTHORITY_CERTIFICATES_LIMIT)
139 {
140 elog<NotAllowed>(Reason("Certificates limit reached"));
141 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500142
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200143 auto certObjectPath = objectPath + '/' + std::to_string(certIdCounter++);
144
145 installedCerts.emplace_back(std::make_unique<Certificate>(
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500146 bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200147 false, certWatchPtr, *this));
Zbigniew Kurzynski06a69d72019-09-27 10:57:38 +0200148 return certObjectPath;
Jayanth Othayoth589159f2018-09-28 08:32:39 -0500149}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500150
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200151void Manager::deleteAll()
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500152{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600153 // TODO: #Issue 4 when a certificate is deleted system auto generates
154 // certificate file. At present we are not supporting creation of
155 // certificate object for the auto-generated certificate file as
156 // deletion if only applicable for REST server and Bmcweb does not allow
157 // deletion of certificates
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200158 installedCerts.clear();
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -0500159}
Marri Devender Raof4682712019-03-19 05:00:28 -0500160
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200161void Manager::deleteCertificate(const std::string& certHash)
162{
163 std::vector<std::unique_ptr<Certificate>>::iterator const& certIt =
164 std::find_if(installedCerts.begin(), installedCerts.end(),
165 [certHash](std::unique_ptr<Certificate> const& cert) {
166 return cert->getHash().compare(certHash) == 0;
167 });
168 if (certIt != installedCerts.end())
169 {
170 installedCerts.erase(certIt);
171 }
172 else
173 {
174 log<level::ERR>("Certificate does not exist",
175 entry("HASH=%s", certHash.c_str()));
176 elog<InternalFailure>();
177 }
178}
179
Marri Devender Raof4682712019-03-19 05:00:28 -0500180std::string Manager::generateCSR(
181 std::vector<std::string> alternativeNames, std::string challengePassword,
182 std::string city, std::string commonName, std::string contactPerson,
183 std::string country, std::string email, std::string givenName,
184 std::string initials, int64_t keyBitLength, std::string keyCurveId,
185 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
186 std::string organization, std::string organizationalUnit, std::string state,
187 std::string surname, std::string unstructuredName)
188{
189 // We support only one CSR.
190 csrPtr.reset(nullptr);
191 auto pid = fork();
192 if (pid == -1)
193 {
194 log<level::ERR>("Error occurred during forking process");
195 report<InternalFailure>();
196 }
197 else if (pid == 0)
198 {
199 try
200 {
201 generateCSRHelper(alternativeNames, challengePassword, city,
202 commonName, contactPerson, country, email,
203 givenName, initials, keyBitLength, keyCurveId,
204 keyPairAlgorithm, keyUsage, organization,
205 organizationalUnit, state, surname,
206 unstructuredName);
207 exit(EXIT_SUCCESS);
208 }
209 catch (const InternalFailure& e)
210 {
211 // commit the error reported in child process and exit
212 // Callback method from SDEvent Loop looks for exit status
213 exit(EXIT_FAILURE);
214 commit<InternalFailure>();
215 }
216 }
217 else
218 {
219 using namespace sdeventplus::source;
220 Child::Callback callback = [this](Child& eventSource,
221 const siginfo_t* si) {
222 eventSource.set_enabled(Enabled::On);
223 if (si->si_status != 0)
224 {
225 this->createCSRObject(Status::FAILURE);
226 }
227 else
228 {
229 this->createCSRObject(Status::SUCCESS);
230 }
231 };
232 try
233 {
234 sigset_t ss;
235 if (sigemptyset(&ss) < 0)
236 {
237 log<level::ERR>("Unable to initialize signal set");
238 elog<InternalFailure>();
239 }
240 if (sigaddset(&ss, SIGCHLD) < 0)
241 {
242 log<level::ERR>("Unable to add signal to signal set");
243 elog<InternalFailure>();
244 }
245
246 // Block SIGCHLD first, so that the event loop can handle it
247 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
248 {
249 log<level::ERR>("Unable to block signal");
250 elog<InternalFailure>();
251 }
252 if (childPtr)
253 {
254 childPtr.reset();
255 }
256 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
257 std::move(callback));
258 }
259 catch (const InternalFailure& e)
260 {
261 commit<InternalFailure>();
262 }
263 }
264 auto csrObjectPath = objectPath + '/' + "csr";
265 return csrObjectPath;
266}
267
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200268std::vector<std::unique_ptr<Certificate>>& Manager::getCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500269{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200270 return installedCerts;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500271}
272
Marri Devender Raof4682712019-03-19 05:00:28 -0500273void Manager::generateCSRHelper(
274 std::vector<std::string> alternativeNames, std::string challengePassword,
275 std::string city, std::string commonName, std::string contactPerson,
276 std::string country, std::string email, std::string givenName,
277 std::string initials, int64_t keyBitLength, std::string keyCurveId,
278 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
279 std::string organization, std::string organizationalUnit, std::string state,
280 std::string surname, std::string unstructuredName)
281{
282 int ret = 0;
283
284 // set version of x509 req
285 int nVersion = 1;
286 // TODO: Issue#6 need to make version number configurable
287 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
288 ret = X509_REQ_set_version(x509Req.get(), nVersion);
289 if (ret == 0)
290 {
291 log<level::ERR>("Error occured during X509_REQ_set_version call");
292 elog<InternalFailure>();
293 }
294
295 // set subject of x509 req
296 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
297
298 if (!alternativeNames.empty())
299 {
300 for (auto& name : alternativeNames)
301 {
302 addEntry(x509Name, "subjectAltName", name);
303 }
304 }
305 addEntry(x509Name, "challengePassword", challengePassword);
306 addEntry(x509Name, "L", city);
307 addEntry(x509Name, "CN", commonName);
308 addEntry(x509Name, "name", contactPerson);
309 addEntry(x509Name, "C", country);
310 addEntry(x509Name, "emailAddress", email);
311 addEntry(x509Name, "GN", givenName);
312 addEntry(x509Name, "initials", initials);
313 addEntry(x509Name, "algorithm", keyPairAlgorithm);
314 if (!keyUsage.empty())
315 {
316 for (auto& usage : keyUsage)
317 {
Marri Devender Rao76411052019-08-07 01:25:07 -0500318 if (isExtendedKeyUsage(usage))
319 {
320 addEntry(x509Name, "extendedKeyUsage", usage);
321 }
322 else
323 {
324 addEntry(x509Name, "keyUsage", usage);
325 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500326 }
327 }
328 addEntry(x509Name, "O", organization);
329 addEntry(x509Name, "ST", state);
330 addEntry(x509Name, "SN", surname);
331 addEntry(x509Name, "unstructuredName", unstructuredName);
332
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500333 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
334
335 log<level::INFO>("Given Key pair algorithm",
336 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
337
338 // Used EC algorithm as default if user did not give algorithm type.
339 if (keyPairAlgorithm == "RSA")
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500340 pKey = getRSAKeyPair(keyBitLength);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500341 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500342 pKey = generateECKeyPair(keyCurveId);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500343 else
344 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500345 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
346 "RSA and EC only");
347 elog<InvalidArgument>(
348 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
349 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
350 }
351
352 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
353 if (ret == 0)
354 {
355 log<level::ERR>("Error occured while setting Public key");
356 elog<InternalFailure>();
357 }
358
359 // Write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500360 writePrivateKey(pKey, PRIV_KEY_FILE_NAME);
Marri Devender Raof4682712019-03-19 05:00:28 -0500361
362 // set sign key of x509 req
363 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500364 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500365 {
366 log<level::ERR>("Error occured while signing key of x509");
367 elog<InternalFailure>();
368 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500369
Marri Devender Raof4682712019-03-19 05:00:28 -0500370 log<level::INFO>("Writing CSR to file");
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500371 fs::path csrFilePath = certParentInstallPath / CSR_FILE_NAME;
372 writeCSR(csrFilePath.string(), x509Req);
Marri Devender Raof4682712019-03-19 05:00:28 -0500373}
374
Marri Devender Rao76411052019-08-07 01:25:07 -0500375bool Manager::isExtendedKeyUsage(const std::string& usage)
376{
377 const static std::array<const char*, 6> usageList = {
378 "ServerAuthentication", "ClientAuthentication", "OCSPSigning",
379 "Timestamping", "CodeSigning", "EmailProtection"};
380 auto it = std::find_if(
381 usageList.begin(), usageList.end(),
382 [&usage](const char* s) { return (strcmp(s, usage.c_str()) == 0); });
383 return it != usageList.end();
384}
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500385EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500386{
387 int ret = 0;
388 // generate rsa key
389 BIGNUM_Ptr bne(BN_new(), ::BN_free);
390 ret = BN_set_word(bne.get(), RSA_F4);
391 if (ret == 0)
392 {
393 log<level::ERR>("Error occured during BN_set_word call");
394 elog<InternalFailure>();
395 }
396
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500397 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500398 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500399 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500400 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500401 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
402 log<level::INFO>(
403 "KeyBitLength is not given.Hence, using default KeyBitLength",
404 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
405 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500406 }
407 RSA* rsa = RSA_new();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500408 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500409 if (ret != 1)
410 {
411 free(rsa);
412 log<level::ERR>("Error occured during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500413 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500414 elog<InternalFailure>();
415 }
416
417 // set public key of x509 req
418 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500419 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500420 if (ret == 0)
421 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500422 free(rsa);
423 log<level::ERR>("Error occured during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500424 elog<InternalFailure>();
425 }
426
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500427 return pKey;
428}
429
430EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
431{
432 std::string curId(curveId);
433
434 if (curId.empty())
435 {
436 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
437 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
438 log<level::INFO>(
439 "KeyCurveId is not given. Hence using default curve id",
440 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
441 curId = DEFAULT_KEYCURVEID;
442 }
443
444 int ecGrp = OBJ_txt2nid(curId.c_str());
445
446 if (ecGrp == NID_undef)
447 {
448 log<level::ERR>(
449 "Error occured during convert the curve id string format into NID",
450 entry("KEYCURVEID=%s", curId.c_str()));
451 elog<InternalFailure>();
452 }
453
454 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
455
456 if (ecKey == NULL)
457 {
458 log<level::ERR>(
459 "Error occured during create the EC_Key object from NID",
460 entry("ECGROUP=%d", ecGrp));
461 elog<InternalFailure>();
462 }
463
464 // If you want to save a key and later load it with
465 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
466 // flag on the key.
467 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
468
469 int ret = EC_KEY_generate_key(ecKey);
470
471 if (ret == 0)
472 {
473 EC_KEY_free(ecKey);
474 log<level::ERR>("Error occured during generate EC key");
475 elog<InternalFailure>();
476 }
477
478 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
479 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
480 if (ret == 0)
481 {
482 EC_KEY_free(ecKey);
483 log<level::ERR>("Error occured during assign EC Key into EVP");
484 elog<InternalFailure>();
485 }
486
487 return pKey;
488}
489
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500490void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey,
491 const std::string& privKeyFileName)
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500492{
493 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500494 // write private key to file
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500495 fs::path privKeyPath = certParentInstallPath / privKeyFileName;
Marri Devender Raof4682712019-03-19 05:00:28 -0500496
497 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
498 if (fp == NULL)
499 {
Marri Devender Raof4682712019-03-19 05:00:28 -0500500 log<level::ERR>("Error occured creating private key file");
501 elog<InternalFailure>();
502 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500503 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500504 std::fclose(fp);
505 if (ret == 0)
506 {
507 log<level::ERR>("Error occured while writing private key to file");
508 elog<InternalFailure>();
509 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500510}
511
512void Manager::addEntry(X509_NAME* x509Name, const char* field,
513 const std::string& bytes)
514{
515 if (bytes.empty())
516 {
517 return;
518 }
519 int ret = X509_NAME_add_entry_by_txt(
520 x509Name, field, MBSTRING_ASC,
521 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
522 if (ret != 1)
523 {
524 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
525 entry("VALUE=%s", bytes.c_str()));
526 elog<InternalFailure>();
527 }
528}
529
530void Manager::createCSRObject(const Status& status)
531{
532 if (csrPtr)
533 {
534 csrPtr.reset(nullptr);
535 }
536 auto csrObjectPath = objectPath + '/' + "csr";
537 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
538 certInstallPath.c_str(), status);
539}
540
541void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
542{
543 if (fs::exists(filePath))
544 {
545 log<level::INFO>("Removing the existing file",
546 entry("FILENAME=%s", filePath.c_str()));
547 if (!fs::remove(filePath.c_str()))
548 {
549 log<level::ERR>("Unable to remove the file",
550 entry("FILENAME=%s", filePath.c_str()));
551 elog<InternalFailure>();
552 }
553 }
554
555 FILE* fp = NULL;
556
557 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
558 {
559 log<level::ERR>("Error opening the file to write the CSR",
560 entry("FILENAME=%s", filePath.c_str()));
561 elog<InternalFailure>();
562 }
563
564 int rc = PEM_write_X509_REQ(fp, x509Req.get());
565 if (!rc)
566 {
567 log<level::ERR>("PEM write routine failed",
568 entry("FILENAME=%s", filePath.c_str()));
569 std::fclose(fp);
570 elog<InternalFailure>();
571 }
572 std::fclose(fp);
573}
574
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200575void Manager::createCertificates()
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500576{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200577 auto certObjectPath = objectPath + '/';
578
579 if (certType == phosphor::certs::AUTHORITY)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500580 {
Zbigniew Lukwinskife590c42019-12-10 12:33:50 +0100581 // Check whether install path is a directory.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200582 if (!fs::is_directory(certInstallPath))
583 {
584 log<level::ERR>("Certificate installation path exists and it is "
585 "not a directory");
586 elog<InternalFailure>();
587 return;
588 }
589
590 for (auto& path : fs::directory_iterator(certInstallPath))
591 {
592 try
593 {
594 installedCerts.emplace_back(std::make_unique<Certificate>(
595 bus, certObjectPath + std::to_string(certIdCounter++),
596 certType, unitToRestart, certInstallPath, path.path(), true,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200597 certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200598 }
599 catch (const InternalFailure& e)
600 {
601 report<InternalFailure>();
602 }
603 catch (const InvalidCertificate& e)
604 {
605 report<InvalidCertificate>(
606 Reason("Existing certificate file is corrupted"));
607 }
608 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500609 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200610 else if (fs::exists(certInstallPath))
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500611 {
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200612 try
613 {
614 installedCerts.emplace_back(std::make_unique<Certificate>(
615 bus, certObjectPath + '1', certType, unitToRestart,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200616 certInstallPath, certInstallPath, true, certWatchPtr, *this));
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200617 }
618 catch (const InternalFailure& e)
619 {
620 report<InternalFailure>();
621 }
622 catch (const InvalidCertificate& e)
623 {
624 report<InvalidCertificate>(
625 Reason("Existing certificate file is corrupted"));
626 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500627 }
628}
Ramesh Iyyarc6e58c72019-07-16 08:52:47 -0500629
630void Manager::createRSAPrivateKeyFile()
631{
632 fs::path rsaPrivateKeyFileName =
633 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
634
635 try
636 {
637 if (!fs::exists(rsaPrivateKeyFileName))
638 {
639 writePrivateKey(generateRSAKeyPair(SUPPORTED_KEYBITLENGTH),
640 RSA_PRIV_KEY_FILE_NAME);
641 }
642 }
643 catch (const InternalFailure& e)
644 {
645 report<InternalFailure>();
646 }
647}
648
649EVP_PKEY_Ptr Manager::getRSAKeyPair(const int64_t keyBitLength)
650{
651 if (keyBitLength != SUPPORTED_KEYBITLENGTH)
652 {
653 log<level::ERR>(
654 "Given Key bit length is not supported",
655 entry("GIVENKEYBITLENGTH=%d", keyBitLength),
656 entry("SUPPORTEDKEYBITLENGTH=%d", SUPPORTED_KEYBITLENGTH));
657 elog<InvalidArgument>(
658 Argument::ARGUMENT_NAME("KEYBITLENGTH"),
659 Argument::ARGUMENT_VALUE(std::to_string(keyBitLength).c_str()));
660 }
661 fs::path rsaPrivateKeyFileName =
662 certParentInstallPath / RSA_PRIV_KEY_FILE_NAME;
663
664 FILE* privateKeyFile = std::fopen(rsaPrivateKeyFileName.c_str(), "r");
665 if (!privateKeyFile)
666 {
667 log<level::ERR>("Unable to open RSA private key file to read",
668 entry("RSAKEYFILE=%s", rsaPrivateKeyFileName.c_str()),
669 entry("ERRORREASON=%s", strerror(errno)));
670 elog<InternalFailure>();
671 }
672
673 EVP_PKEY_Ptr privateKey(
674 PEM_read_PrivateKey(privateKeyFile, nullptr, nullptr, nullptr),
675 ::EVP_PKEY_free);
676 std::fclose(privateKeyFile);
677
678 if (!privateKey)
679 {
680 log<level::ERR>("Error occured during PEM_read_PrivateKey call");
681 elog<InternalFailure>();
682 }
683 return privateKey;
684}
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500685} // namespace certs
686} // namespace phosphor