blob: b189bb9dd98ebaa0936b15563a5dafbba8702561 [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
Marri Devender Rao6ceec402019-02-01 03:15:19 -06006#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -05007#include <xyz/openbmc_project/Certs/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05008#include <xyz/openbmc_project/Common/error.hpp>
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -05009namespace phosphor
10{
11namespace certs
12{
Marri Devender Rao13965112019-02-27 08:47:12 -060013using InternalFailure =
14 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050015using InvalidCertificate =
16 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
17using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050018
Marri Devender Raof4682712019-03-19 05:00:28 -050019using X509_REQ_Ptr = std::unique_ptr<X509_REQ, decltype(&::X509_REQ_free)>;
20using BIGNUM_Ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
21
22Manager::Manager(sdbusplus::bus::bus& bus, sdeventplus::Event& event,
23 const char* path, const CertificateType& type,
24 UnitsToRestart&& unit, CertInstallPath&& installPath) :
Marri Devender Rao6ceec402019-02-01 03:15:19 -060025 Ifaces(bus, path),
Marri Devender Raof4682712019-03-19 05:00:28 -050026 bus(bus), event(event), objectPath(path), certType(type),
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050027 unitToRestart(std::move(unit)), certInstallPath(std::move(installPath))
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050028{
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050029 // restore any existing certificates
Marri Devender Raobf7c5882019-02-27 08:41:07 -060030 if (fs::exists(certInstallPath))
31 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050032 createCertificate();
33 }
34
35 // watch is not required for authority certificates
36 if (certType != AUTHORITY)
37 {
38 // watch for certificate file create/replace
39 certWatchPtr = std::make_unique<
40 Watch>(event, certInstallPath, [this]() {
41 try
42 {
43 // if certificate file existing update it
44 if (certificatePtr != nullptr)
45 {
46 log<level::INFO>(
47 "Inotify callback to update certificate properties");
48 certificatePtr->populateProperties();
49 }
50 else
51 {
52 log<level::INFO>(
53 "Inotify callback to create certificate object");
54 createCertificate();
55 }
56 }
57 catch (const InternalFailure& e)
58 {
59 commit<InternalFailure>();
60 }
61 catch (const InvalidCertificate& e)
62 {
63 commit<InvalidCertificate>();
64 }
65 });
Marri Devender Raobf7c5882019-02-27 08:41:07 -060066 }
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050067}
68
Marri Devender Rao6ceec402019-02-01 03:15:19 -060069void Manager::install(const std::string filePath)
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -050070{
Marri Devender Rao13965112019-02-27 08:47:12 -060071 using NotAllowed =
72 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
73 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
74 // TODO: Issue#3 At present supporting only one certificate to be
75 // uploaded this need to be revisited to support multiple
76 // certificates
77 if (certificatePtr != nullptr)
78 {
79 elog<NotAllowed>(Reason("Certificate already exist"));
80 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050081
Marri Devender Rao13965112019-02-27 08:47:12 -060082 auto certObjectPath = objectPath + '/' + '1';
Marri Devender Rao8f80c352019-05-13 00:53:01 -050083 certificatePtr = std::make_unique<Certificate>(
84 bus, certObjectPath, certType, unitToRestart, certInstallPath, filePath,
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050085 false, certWatchPtr);
Jayanth Othayoth589159f2018-09-28 08:32:39 -050086}
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -050087
88void Manager::delete_()
89{
Marri Devender Rao6ceec402019-02-01 03:15:19 -060090 // TODO: #Issue 4 when a certificate is deleted system auto generates
91 // certificate file. At present we are not supporting creation of
92 // certificate object for the auto-generated certificate file as
93 // deletion if only applicable for REST server and Bmcweb does not allow
94 // deletion of certificates
95 if (certificatePtr != nullptr)
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -050096 {
Marri Devender Rao6ceec402019-02-01 03:15:19 -060097 certificatePtr.reset(nullptr);
Deepak Kodihalliae70b3d2018-09-30 05:42:00 -050098 }
99}
Marri Devender Raof4682712019-03-19 05:00:28 -0500100
101std::string Manager::generateCSR(
102 std::vector<std::string> alternativeNames, std::string challengePassword,
103 std::string city, std::string commonName, std::string contactPerson,
104 std::string country, std::string email, std::string givenName,
105 std::string initials, int64_t keyBitLength, std::string keyCurveId,
106 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
107 std::string organization, std::string organizationalUnit, std::string state,
108 std::string surname, std::string unstructuredName)
109{
110 // We support only one CSR.
111 csrPtr.reset(nullptr);
112 auto pid = fork();
113 if (pid == -1)
114 {
115 log<level::ERR>("Error occurred during forking process");
116 report<InternalFailure>();
117 }
118 else if (pid == 0)
119 {
120 try
121 {
122 generateCSRHelper(alternativeNames, challengePassword, city,
123 commonName, contactPerson, country, email,
124 givenName, initials, keyBitLength, keyCurveId,
125 keyPairAlgorithm, keyUsage, organization,
126 organizationalUnit, state, surname,
127 unstructuredName);
128 exit(EXIT_SUCCESS);
129 }
130 catch (const InternalFailure& e)
131 {
132 // commit the error reported in child process and exit
133 // Callback method from SDEvent Loop looks for exit status
134 exit(EXIT_FAILURE);
135 commit<InternalFailure>();
136 }
137 }
138 else
139 {
140 using namespace sdeventplus::source;
141 Child::Callback callback = [this](Child& eventSource,
142 const siginfo_t* si) {
143 eventSource.set_enabled(Enabled::On);
144 if (si->si_status != 0)
145 {
146 this->createCSRObject(Status::FAILURE);
147 }
148 else
149 {
150 this->createCSRObject(Status::SUCCESS);
151 }
152 };
153 try
154 {
155 sigset_t ss;
156 if (sigemptyset(&ss) < 0)
157 {
158 log<level::ERR>("Unable to initialize signal set");
159 elog<InternalFailure>();
160 }
161 if (sigaddset(&ss, SIGCHLD) < 0)
162 {
163 log<level::ERR>("Unable to add signal to signal set");
164 elog<InternalFailure>();
165 }
166
167 // Block SIGCHLD first, so that the event loop can handle it
168 if (sigprocmask(SIG_BLOCK, &ss, NULL) < 0)
169 {
170 log<level::ERR>("Unable to block signal");
171 elog<InternalFailure>();
172 }
173 if (childPtr)
174 {
175 childPtr.reset();
176 }
177 childPtr = std::make_unique<Child>(event, pid, WEXITED | WSTOPPED,
178 std::move(callback));
179 }
180 catch (const InternalFailure& e)
181 {
182 commit<InternalFailure>();
183 }
184 }
185 auto csrObjectPath = objectPath + '/' + "csr";
186 return csrObjectPath;
187}
188
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500189CertificatePtr& Manager::getCertificate()
190{
191 return certificatePtr;
192}
193
Marri Devender Raof4682712019-03-19 05:00:28 -0500194void Manager::generateCSRHelper(
195 std::vector<std::string> alternativeNames, std::string challengePassword,
196 std::string city, std::string commonName, std::string contactPerson,
197 std::string country, std::string email, std::string givenName,
198 std::string initials, int64_t keyBitLength, std::string keyCurveId,
199 std::string keyPairAlgorithm, std::vector<std::string> keyUsage,
200 std::string organization, std::string organizationalUnit, std::string state,
201 std::string surname, std::string unstructuredName)
202{
203 int ret = 0;
204
205 // set version of x509 req
206 int nVersion = 1;
207 // TODO: Issue#6 need to make version number configurable
208 X509_REQ_Ptr x509Req(X509_REQ_new(), ::X509_REQ_free);
209 ret = X509_REQ_set_version(x509Req.get(), nVersion);
210 if (ret == 0)
211 {
212 log<level::ERR>("Error occured during X509_REQ_set_version call");
213 elog<InternalFailure>();
214 }
215
216 // set subject of x509 req
217 X509_NAME* x509Name = X509_REQ_get_subject_name(x509Req.get());
218
219 if (!alternativeNames.empty())
220 {
221 for (auto& name : alternativeNames)
222 {
223 addEntry(x509Name, "subjectAltName", name);
224 }
225 }
226 addEntry(x509Name, "challengePassword", challengePassword);
227 addEntry(x509Name, "L", city);
228 addEntry(x509Name, "CN", commonName);
229 addEntry(x509Name, "name", contactPerson);
230 addEntry(x509Name, "C", country);
231 addEntry(x509Name, "emailAddress", email);
232 addEntry(x509Name, "GN", givenName);
233 addEntry(x509Name, "initials", initials);
234 addEntry(x509Name, "algorithm", keyPairAlgorithm);
235 if (!keyUsage.empty())
236 {
237 for (auto& usage : keyUsage)
238 {
239 addEntry(x509Name, "keyUsage", usage);
240 }
241 }
242 addEntry(x509Name, "O", organization);
243 addEntry(x509Name, "ST", state);
244 addEntry(x509Name, "SN", surname);
245 addEntry(x509Name, "unstructuredName", unstructuredName);
246
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500247 EVP_PKEY_Ptr pKey(nullptr, ::EVP_PKEY_free);
248
249 log<level::INFO>("Given Key pair algorithm",
250 entry("KEYPAIRALGORITHM=%s", keyPairAlgorithm.c_str()));
251
252 // Used EC algorithm as default if user did not give algorithm type.
253 if (keyPairAlgorithm == "RSA")
254 pKey = std::move(generateRSAKeyPair(keyBitLength));
255 else if ((keyPairAlgorithm == "EC") || (keyPairAlgorithm.empty()))
256 pKey = std::move(generateECKeyPair(keyCurveId));
257 else
258 {
259 using InvalidArgument =
260 sdbusplus::xyz::openbmc_project::Common::Error::InvalidArgument;
261 using Argument = xyz::openbmc_project::Common::InvalidArgument;
262
263 log<level::ERR>("Given Key pair algorithm is not supported. Supporting "
264 "RSA and EC only");
265 elog<InvalidArgument>(
266 Argument::ARGUMENT_NAME("KEYPAIRALGORITHM"),
267 Argument::ARGUMENT_VALUE(keyPairAlgorithm.c_str()));
268 }
269
270 ret = X509_REQ_set_pubkey(x509Req.get(), pKey.get());
271 if (ret == 0)
272 {
273 log<level::ERR>("Error occured while setting Public key");
274 elog<InternalFailure>();
275 }
276
277 // Write private key to file
278 writePrivateKey(pKey);
Marri Devender Raof4682712019-03-19 05:00:28 -0500279
280 // set sign key of x509 req
281 ret = X509_REQ_sign(x509Req.get(), pKey.get(), EVP_sha256());
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500282 if (ret == 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500283 {
284 log<level::ERR>("Error occured while signing key of x509");
285 elog<InternalFailure>();
286 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500287
Marri Devender Raof4682712019-03-19 05:00:28 -0500288 log<level::INFO>("Writing CSR to file");
289 std::string path = fs::path(certInstallPath).parent_path();
290 std::string csrFilePath = path + '/' + CSR_FILE_NAME;
291 writeCSR(csrFilePath, x509Req);
292}
293
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500294EVP_PKEY_Ptr Manager::generateRSAKeyPair(const int64_t keyBitLength)
Marri Devender Raof4682712019-03-19 05:00:28 -0500295{
296 int ret = 0;
297 // generate rsa key
298 BIGNUM_Ptr bne(BN_new(), ::BN_free);
299 ret = BN_set_word(bne.get(), RSA_F4);
300 if (ret == 0)
301 {
302 log<level::ERR>("Error occured during BN_set_word call");
303 elog<InternalFailure>();
304 }
305
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500306 int64_t keyBitLen = keyBitLength;
Marri Devender Raof4682712019-03-19 05:00:28 -0500307 // set keybit length to default value if not set
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500308 if (keyBitLen <= 0)
Marri Devender Raof4682712019-03-19 05:00:28 -0500309 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500310 constexpr auto DEFAULT_KEYBITLENGTH = 2048;
311 log<level::INFO>(
312 "KeyBitLength is not given.Hence, using default KeyBitLength",
313 entry("DEFAULTKEYBITLENGTH=%d", DEFAULT_KEYBITLENGTH));
314 keyBitLen = DEFAULT_KEYBITLENGTH;
Marri Devender Raof4682712019-03-19 05:00:28 -0500315 }
316 RSA* rsa = RSA_new();
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500317 ret = RSA_generate_key_ex(rsa, keyBitLen, bne.get(), NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500318 if (ret != 1)
319 {
320 free(rsa);
321 log<level::ERR>("Error occured during RSA_generate_key_ex call",
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500322 entry("KEYBITLENGTH=%PRIu64", keyBitLen));
Marri Devender Raof4682712019-03-19 05:00:28 -0500323 elog<InternalFailure>();
324 }
325
326 // set public key of x509 req
327 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500328 ret = EVP_PKEY_assign_RSA(pKey.get(), rsa);
Marri Devender Raof4682712019-03-19 05:00:28 -0500329 if (ret == 0)
330 {
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500331 free(rsa);
332 log<level::ERR>("Error occured during assign rsa key into EVP");
Marri Devender Raof4682712019-03-19 05:00:28 -0500333 elog<InternalFailure>();
334 }
335
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500336 return pKey;
337}
338
339EVP_PKEY_Ptr Manager::generateECKeyPair(const std::string& curveId)
340{
341 std::string curId(curveId);
342
343 if (curId.empty())
344 {
345 // secp224r1 is equal to RSA 2048 KeyBitLength. Refer RFC 5349
346 constexpr auto DEFAULT_KEYCURVEID = "secp224r1";
347 log<level::INFO>(
348 "KeyCurveId is not given. Hence using default curve id",
349 entry("DEFAULTKEYCURVEID=%s", DEFAULT_KEYCURVEID));
350 curId = DEFAULT_KEYCURVEID;
351 }
352
353 int ecGrp = OBJ_txt2nid(curId.c_str());
354
355 if (ecGrp == NID_undef)
356 {
357 log<level::ERR>(
358 "Error occured during convert the curve id string format into NID",
359 entry("KEYCURVEID=%s", curId.c_str()));
360 elog<InternalFailure>();
361 }
362
363 EC_KEY* ecKey = EC_KEY_new_by_curve_name(ecGrp);
364
365 if (ecKey == NULL)
366 {
367 log<level::ERR>(
368 "Error occured during create the EC_Key object from NID",
369 entry("ECGROUP=%d", ecGrp));
370 elog<InternalFailure>();
371 }
372
373 // If you want to save a key and later load it with
374 // SSL_CTX_use_PrivateKey_file, then you must set the OPENSSL_EC_NAMED_CURVE
375 // flag on the key.
376 EC_KEY_set_asn1_flag(ecKey, OPENSSL_EC_NAMED_CURVE);
377
378 int ret = EC_KEY_generate_key(ecKey);
379
380 if (ret == 0)
381 {
382 EC_KEY_free(ecKey);
383 log<level::ERR>("Error occured during generate EC key");
384 elog<InternalFailure>();
385 }
386
387 EVP_PKEY_Ptr pKey(EVP_PKEY_new(), ::EVP_PKEY_free);
388 ret = EVP_PKEY_assign_EC_KEY(pKey.get(), ecKey);
389 if (ret == 0)
390 {
391 EC_KEY_free(ecKey);
392 log<level::ERR>("Error occured during assign EC Key into EVP");
393 elog<InternalFailure>();
394 }
395
396 return pKey;
397}
398
399void Manager::writePrivateKey(const EVP_PKEY_Ptr& pKey)
400{
401 log<level::INFO>("Writing private key to file");
Marri Devender Raof4682712019-03-19 05:00:28 -0500402 // write private key to file
403 std::string path = fs::path(certInstallPath).parent_path();
404 std::string privKeyPath = path + '/' + PRIV_KEY_FILE_NAME;
405
406 FILE* fp = std::fopen(privKeyPath.c_str(), "w");
407 if (fp == NULL)
408 {
Marri Devender Raof4682712019-03-19 05:00:28 -0500409 log<level::ERR>("Error occured creating private key file");
410 elog<InternalFailure>();
411 }
Ramesh Iyyar8a09b522019-06-07 05:23:29 -0500412 int ret = PEM_write_PrivateKey(fp, pKey.get(), NULL, NULL, 0, 0, NULL);
Marri Devender Raof4682712019-03-19 05:00:28 -0500413 std::fclose(fp);
414 if (ret == 0)
415 {
416 log<level::ERR>("Error occured while writing private key to file");
417 elog<InternalFailure>();
418 }
Marri Devender Raof4682712019-03-19 05:00:28 -0500419}
420
421void Manager::addEntry(X509_NAME* x509Name, const char* field,
422 const std::string& bytes)
423{
424 if (bytes.empty())
425 {
426 return;
427 }
428 int ret = X509_NAME_add_entry_by_txt(
429 x509Name, field, MBSTRING_ASC,
430 reinterpret_cast<const unsigned char*>(bytes.c_str()), -1, -1, 0);
431 if (ret != 1)
432 {
433 log<level::ERR>("Unable to set entry", entry("FIELD=%s", field),
434 entry("VALUE=%s", bytes.c_str()));
435 elog<InternalFailure>();
436 }
437}
438
439void Manager::createCSRObject(const Status& status)
440{
441 if (csrPtr)
442 {
443 csrPtr.reset(nullptr);
444 }
445 auto csrObjectPath = objectPath + '/' + "csr";
446 csrPtr = std::make_unique<CSR>(bus, csrObjectPath.c_str(),
447 certInstallPath.c_str(), status);
448}
449
450void Manager::writeCSR(const std::string& filePath, const X509_REQ_Ptr& x509Req)
451{
452 if (fs::exists(filePath))
453 {
454 log<level::INFO>("Removing the existing file",
455 entry("FILENAME=%s", filePath.c_str()));
456 if (!fs::remove(filePath.c_str()))
457 {
458 log<level::ERR>("Unable to remove the file",
459 entry("FILENAME=%s", filePath.c_str()));
460 elog<InternalFailure>();
461 }
462 }
463
464 FILE* fp = NULL;
465
466 if ((fp = std::fopen(filePath.c_str(), "w")) == NULL)
467 {
468 log<level::ERR>("Error opening the file to write the CSR",
469 entry("FILENAME=%s", filePath.c_str()));
470 elog<InternalFailure>();
471 }
472
473 int rc = PEM_write_X509_REQ(fp, x509Req.get());
474 if (!rc)
475 {
476 log<level::ERR>("PEM write routine failed",
477 entry("FILENAME=%s", filePath.c_str()));
478 std::fclose(fp);
479 elog<InternalFailure>();
480 }
481 std::fclose(fp);
482}
483
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500484void Manager::createCertificate()
485{
486 try
487 {
488 // TODO: Issue#3 At present supporting only one certificate to be
489 // uploaded this need to be revisited to support multiple
490 // certificates
491 auto certObjectPath = objectPath + '/' + '1';
492 certificatePtr = std::make_unique<Certificate>(
493 bus, certObjectPath, certType, unitToRestart, certInstallPath,
494 certInstallPath, true, certWatchPtr);
495 }
496 catch (const InternalFailure& e)
497 {
498 report<InternalFailure>();
499 }
500 catch (const InvalidCertificate& e)
501 {
502 report<InvalidCertificate>(
503 Reason("Existing certificate file is corrupted"));
504 }
505}
Jayanth Othayothcfbc8dc2018-09-03 07:22:27 -0500506} // namespace certs
507} // namespace phosphor