blob: a070e0728aa40f5ac4f37ad9d9722b15dddb09cb [file] [log] [blame]
Marri Devender Raocd30c492019-06-12 01:40:17 -05001#include "config.h"
2
Marri Devender Rao6ceec402019-02-01 03:15:19 -06003#include "certificate.hpp"
4
5#include <openssl/bio.h>
6#include <openssl/crypto.h>
7#include <openssl/err.h>
8#include <openssl/evp.h>
9#include <openssl/pem.h>
10#include <openssl/x509v3.h>
11
12#include <fstream>
13#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050014#include <xyz/openbmc_project/Certs/error.hpp>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060015#include <xyz/openbmc_project/Common/error.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050016
Marri Devender Rao6ceec402019-02-01 03:15:19 -060017namespace phosphor
18{
19namespace certs
20{
21// RAII support for openSSL functions.
22using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
23using X509_STORE_CTX_Ptr =
24 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
25using X509_LOOKUP_Ptr =
26 std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060027using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060028using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
29using BUF_MEM_Ptr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
30using InternalFailure =
31 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
32using InvalidCertificate =
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050033 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
34using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060035
36// Trust chain related errors.`
37#define TRUST_CHAIN_ERR(errnum) \
38 ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || \
39 (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) || \
40 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) || \
41 (errnum == X509_V_ERR_CERT_UNTRUSTED) || \
42 (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
43
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060044// Refer to schema 2018.3
45// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
46// supported KeyUsage types in redfish
47// Refer to
48// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
49// key usage bit fields
50std::map<uint8_t, std::string> keyUsageToRfStr = {
51 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
52 {KU_NON_REPUDIATION, "NonRepudiation"},
53 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
54 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
55 {KU_KEY_AGREEMENT, "KeyAgreement"},
56 {KU_KEY_CERT_SIGN, "KeyCertSign"},
57 {KU_CRL_SIGN, "CRLSigning"},
58 {KU_ENCIPHER_ONLY, "EncipherOnly"},
59 {KU_DECIPHER_ONLY, "DecipherOnly"}};
60
61// Refer to schema 2018.3
62// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
63// supported Extended KeyUsage types in redfish
64std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
65 {NID_server_auth, "ServerAuthentication"},
66 {NID_client_auth, "ClientAuthentication"},
67 {NID_email_protect, "EmailProtection"},
68 {NID_OCSP_sign, "OCSPSigning"},
69 {NID_ad_timeStamping, "Timestamping"},
70 {NID_code_sign, "CodeSigning"}};
71
Kowalski, Kamildb029c92019-07-08 17:09:39 +020072std::string Certificate::getSubjectHash(const X509_STORE_CTX_Ptr& storeCtx)
73{
74 X509_Ptr cert(X509_STORE_CTX_get_current_cert(storeCtx.get()), ::X509_free);
75 unsigned long hash = X509_subject_name_hash(cert.get());
76
77 char hashBuf[9];
78
79 sprintf(hashBuf, "%08lx", hash);
80
81 return std::string(hashBuf);
82}
83
Marri Devender Rao6ceec402019-02-01 03:15:19 -060084Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
85 const CertificateType& type,
86 const UnitsToRestart& unit,
87 const CertInstallPath& installPath,
Marri Devender Rao8f80c352019-05-13 00:53:01 -050088 const CertUploadPath& uploadPath,
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050089 bool isSkipUnitReload,
90 const CertWatchPtr& certWatchPtr) :
Marri Devender Raoedd11312019-02-27 08:45:10 -060091 CertIfaces(bus, objPath.c_str(), true),
92 bus(bus), objectPath(objPath), certType(type), unitToRestart(unit),
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050093 certInstallPath(installPath), certWatchPtr(certWatchPtr)
Marri Devender Rao6ceec402019-02-01 03:15:19 -060094{
95 auto installHelper = [this](const auto& filePath) {
96 if (!compareKeys(filePath))
97 {
98 elog<InvalidCertificate>(
99 Reason("Private key does not match the Certificate"));
100 };
101 };
102 typeFuncMap[SERVER] = installHelper;
103 typeFuncMap[CLIENT] = installHelper;
104 typeFuncMap[AUTHORITY] = [](auto filePath) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500105
106 auto appendPrivateKey = [this](const std::string& filePath) {
107 checkAndAppendPrivateKey(filePath);
108 };
109
110 appendKeyMap[SERVER] = appendPrivateKey;
111 appendKeyMap[CLIENT] = appendPrivateKey;
112 appendKeyMap[AUTHORITY] = [](const std::string& filePath) {};
113
114 // install the certificate
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500115 install(uploadPath, isSkipUnitReload);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500116
Marri Devender Raoedd11312019-02-27 08:45:10 -0600117 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600118}
119
120Certificate::~Certificate()
121{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200122 std::string installPath = certInstallPath;
123
124 if (certType == phosphor::certs::AUTHORITY)
125 {
126 installPath += "/" + certHash + ".0";
127 }
128
129 if (!fs::remove(installPath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600130 {
131 log<level::INFO>("Certificate file not found!",
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200132 entry("PATH=%s", installPath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600133 }
134 else if (!unitToRestart.empty())
135 {
136 reloadOrReset(unitToRestart);
137 }
138}
139
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500140void Certificate::replace(const std::string filePath)
141{
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500142 install(filePath, false);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500143}
144
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500145void Certificate::install(const std::string& filePath, bool isSkipUnitReload)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600146{
147 log<level::INFO>("Certificate install ",
148 entry("FILEPATH=%s", filePath.c_str()));
149 auto errCode = X509_V_OK;
150
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500151 // stop watch for user initiated certificate install
152 if (certWatchPtr)
153 {
154 certWatchPtr->stopWatch();
155 }
156
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600157 // Verify the certificate file
158 fs::path file(filePath);
159 if (!fs::exists(file))
160 {
161 log<level::ERR>("File is Missing", entry("FILE=%s", filePath.c_str()));
162 elog<InternalFailure>();
163 }
164
165 try
166 {
167 if (fs::file_size(filePath) == 0)
168 {
169 // file is empty
170 log<level::ERR>("File is empty",
171 entry("FILE=%s", filePath.c_str()));
172 elog<InvalidCertificate>(Reason("File is empty"));
173 }
174 }
175 catch (const fs::filesystem_error& e)
176 {
177 // Log Error message
178 log<level::ERR>(e.what(), entry("FILE=%s", filePath.c_str()));
179 elog<InternalFailure>();
180 }
181
182 // Defining store object as RAW to avoid double free.
183 // X509_LOOKUP_free free up store object.
184 // Create an empty X509_STORE structure for certificate validation.
185 auto x509Store = X509_STORE_new();
186 if (!x509Store)
187 {
188 log<level::ERR>("Error occured during X509_STORE_new call");
189 elog<InternalFailure>();
190 }
191
192 OpenSSL_add_all_algorithms();
193
194 // ADD Certificate Lookup method.
195 X509_LOOKUP_Ptr lookup(X509_STORE_add_lookup(x509Store, X509_LOOKUP_file()),
196 ::X509_LOOKUP_free);
197 if (!lookup)
198 {
199 // Normally lookup cleanup function interanlly does X509Store cleanup
200 // Free up the X509Store.
201 X509_STORE_free(x509Store);
202 log<level::ERR>("Error occured during X509_STORE_add_lookup call");
203 elog<InternalFailure>();
204 }
205 // Load Certificate file.
206 errCode = X509_LOOKUP_load_file(lookup.get(), filePath.c_str(),
207 X509_FILETYPE_PEM);
208 if (errCode != 1)
209 {
210 log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
211 entry("FILE=%s", filePath.c_str()));
212 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
213 }
214
215 // Load Certificate file into the X509 structre.
216 X509_Ptr cert = std::move(loadCert(filePath));
217 X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
218 if (!storeCtx)
219 {
220 log<level::ERR>("Error occured during X509_STORE_CTX_new call",
221 entry("FILE=%s", filePath.c_str()));
222 elog<InternalFailure>();
223 }
224
225 errCode = X509_STORE_CTX_init(storeCtx.get(), x509Store, cert.get(), NULL);
226 if (errCode != 1)
227 {
228 log<level::ERR>("Error occured during X509_STORE_CTX_init call",
229 entry("FILE=%s", filePath.c_str()));
230 elog<InternalFailure>();
231 }
232
233 // Set time to current time.
234 auto locTime = time(nullptr);
235
236 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
237 locTime);
238
239 errCode = X509_verify_cert(storeCtx.get());
240 if (errCode == 1)
241 {
242 errCode = X509_V_OK;
243 }
244 else if (errCode == 0)
245 {
246 errCode = X509_STORE_CTX_get_error(storeCtx.get());
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500247 log<level::INFO>(
248 "Error occured during X509_verify_cert call, checking for known "
249 "error",
250 entry("FILE=%s", filePath.c_str()), entry("ERRCODE=%d", errCode),
251 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600252 }
253 else
254 {
255 log<level::ERR>("Error occured during X509_verify_cert call",
256 entry("FILE=%s", filePath.c_str()));
257 elog<InternalFailure>();
258 }
259
260 // Allow certificate upload, for "certificate is not yet valid" and
261 // trust chain related errors.
262 if (!((errCode == X509_V_OK) ||
263 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
264 TRUST_CHAIN_ERR(errCode)))
265 {
266 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
267 {
268 elog<InvalidCertificate>(Reason("Expired Certificate"));
269 }
270 // Loging general error here.
271 elog<InvalidCertificate>(Reason("Certificate validation failed"));
272 }
273
Marri Devender Raocd30c492019-06-12 01:40:17 -0500274 // Invoke type specific append private key function.
275 auto appendIter = appendKeyMap.find(certType);
276 if (appendIter == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600277 {
278 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
279 elog<InternalFailure>();
280 }
Marri Devender Raocd30c492019-06-12 01:40:17 -0500281 appendIter->second(filePath);
282
283 // Invoke type specific compare keys function.
284 auto compIter = typeFuncMap.find(certType);
285 if (compIter == typeFuncMap.end())
286 {
287 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
288 elog<InternalFailure>();
289 }
290 compIter->second(filePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600291
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200292 std::string newHash = getSubjectHash(storeCtx);
293 std::string installPath = certInstallPath;
294
295 if (certType == phosphor::certs::AUTHORITY)
296 {
297 // Save under OpenSSL lib acceptable name
298 installPath += "/" + newHash + ".0";
299
300 // Check if we are not trying to overwrite already existing certificate
301 if (certHash != newHash && fs::exists(installPath) &&
302 filePath != installPath)
303 {
304 using NotAllowed =
305 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
306 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
307
308 elog<NotAllowed>(Reason("Certificate already exist"));
309 }
310 }
311
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500312 // Copy the certificate to the installation path
313 // During bootup will be parsing existing file so no need to
314 // copy it.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200315 if (filePath != installPath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600316 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500317 std::ifstream inputCertFileStream;
318 std::ofstream outputCertFileStream;
319 inputCertFileStream.exceptions(std::ifstream::failbit |
320 std::ifstream::badbit |
321 std::ifstream::eofbit);
322 outputCertFileStream.exceptions(std::ofstream::failbit |
323 std::ofstream::badbit |
324 std::ofstream::eofbit);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200325
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500326 try
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600327 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500328 inputCertFileStream.open(filePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200329 outputCertFileStream.open(installPath, std::ios::out);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500330 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
331 inputCertFileStream.close();
332 outputCertFileStream.close();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600333 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500334 catch (const std::exception& e)
335 {
336 log<level::ERR>("Failed to copy certificate",
337 entry("ERR=%s", e.what()),
338 entry("SRC=%s", filePath.c_str()),
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200339 entry("DST=%s", installPath.c_str()));
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500340 elog<InternalFailure>();
341 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200342
343 if (certHash != newHash && !certHash.empty() &&
344 certType == phosphor::certs::AUTHORITY)
345 {
346 std::string oldPath = certInstallPath + "/" + certHash + ".0";
347
348 // Remove previous file
349 try
350 {
351 fs::remove(oldPath);
352 }
353 catch (const std::exception& e)
354 {
355 log<level::ERR>("Failed to remove old certificate",
356 entry("ERR=%s", e.what()),
357 entry("OLD=%s", oldPath.c_str()),
358 entry("NEW=%s", installPath.c_str()));
359 }
360 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600361 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500362
363 if (!isSkipUnitReload)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600364 {
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500365 // restart the units
366 if (!unitToRestart.empty())
367 {
368 reloadOrReset(unitToRestart);
369 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600370 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600371
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200372 // Store current hash
373 certHash = newHash;
374
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600375 // Parse the certificate file and populate properties
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200376 populateProperties(installPath);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500377
378 // restart watch
379 if (certWatchPtr)
380 {
381 certWatchPtr->startWatch();
382 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600383}
384
385void Certificate::populateProperties()
386{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200387 populateProperties(certInstallPath);
388}
389
390const std::string& Certificate::getHash() const
391{
392 return certHash;
393}
394
395void Certificate::populateProperties(const std::string& certPath)
396{
397 X509_Ptr cert = std::move(loadCert(certPath));
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600398 // Update properties if no error thrown
399 BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
400 PEM_write_bio_X509(certBio.get(), cert.get());
401 BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
402 BUF_MEM* buf = certBuf.get();
403 BIO_get_mem_ptr(certBio.get(), &buf);
404 std::string certStr(buf->data, buf->length);
405 CertificateIface::certificateString(certStr);
406
407 static const int maxKeySize = 4096;
408 char subBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500409 BIO_MEM_Ptr subBio(BIO_new(BIO_s_mem()), BIO_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600410 // This pointer cannot be freed independantly.
411 X509_NAME* sub = X509_get_subject_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500412 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
413 BIO_read(subBio.get(), subBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600414 CertificateIface::subject(subBuffer);
415
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600416 char issuerBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500417 BIO_MEM_Ptr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
418 // This pointer cannot be freed independantly.
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600419 X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500420 X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
421 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600422 CertificateIface::issuer(issuerBuffer);
423
424 std::vector<std::string> keyUsageList;
425 ASN1_BIT_STRING* usage;
426
427 // Go through each usage in the bit string and convert to
428 // corresponding string value
429 if ((usage = static_cast<ASN1_BIT_STRING*>(
430 X509_get_ext_d2i(cert.get(), NID_key_usage, NULL, NULL))))
431 {
432 for (auto i = 0; i < usage->length; ++i)
433 {
434 for (auto& x : keyUsageToRfStr)
435 {
436 if (x.first & usage->data[i])
437 {
438 keyUsageList.push_back(x.second);
439 break;
440 }
441 }
442 }
443 }
444
445 EXTENDED_KEY_USAGE* extUsage;
446 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
447 X509_get_ext_d2i(cert.get(), NID_ext_key_usage, NULL, NULL))))
448 {
449 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
450 {
451 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
452 sk_ASN1_OBJECT_value(extUsage, i))]);
453 }
454 }
455 CertificateIface::keyUsage(keyUsageList);
456
457 int days = 0;
458 int secs = 0;
459
460 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
461 // Set time to 12:00am GMT, Jan 1 1970
462 ASN1_TIME_set_string(epoch.get(), "700101120000Z");
463
464 static const int dayToSeconds = 24 * 60 * 60;
465 ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
466 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
467 CertificateIface::validNotAfter((days * dayToSeconds) + secs);
468
469 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
470 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
471 CertificateIface::validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600472}
473
474X509_Ptr Certificate::loadCert(const std::string& filePath)
475{
476 log<level::INFO>("Certificate loadCert",
477 entry("FILEPATH=%s", filePath.c_str()));
478 // Read Certificate file
479 X509_Ptr cert(X509_new(), ::X509_free);
480 if (!cert)
481 {
482 log<level::ERR>("Error occured during X509_new call",
483 entry("FILE=%s", filePath.c_str()),
484 entry("ERRCODE=%lu", ERR_get_error()));
485 elog<InternalFailure>();
486 }
487
488 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
489 if (!bioCert)
490 {
491 log<level::ERR>("Error occured during BIO_new_file call",
492 entry("FILE=%s", filePath.c_str()));
493 elog<InternalFailure>();
494 }
495
496 X509* x509 = cert.get();
497 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
498 {
499 log<level::ERR>("Error occured during PEM_read_bio_X509 call",
500 entry("FILE=%s", filePath.c_str()));
501 elog<InternalFailure>();
502 }
503 return cert;
504}
Marri Devender Raocd30c492019-06-12 01:40:17 -0500505
506void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
507{
508 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
509 if (!keyBio)
510 {
511 log<level::ERR>("Error occured during BIO_s_file call",
512 entry("FILE=%s", filePath.c_str()));
513 elog<InternalFailure>();
514 }
515 BIO_read_filename(keyBio.get(), filePath.c_str());
516
517 EVP_PKEY_Ptr priKey(
518 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
519 ::EVP_PKEY_free);
520 if (!priKey)
521 {
522 log<level::INFO>("Private key not present in file",
523 entry("FILE=%s", filePath.c_str()));
524 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
525 privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
526 if (!fs::exists(privateKeyFile))
527 {
528 log<level::ERR>("Private key file is not found",
529 entry("FILE=%s", privateKeyFile.c_str()));
530 elog<InternalFailure>();
531 }
532
533 std::ifstream privKeyFileStream;
534 std::ofstream certFileStream;
535 privKeyFileStream.exceptions(std::ifstream::failbit |
536 std::ifstream::badbit |
537 std::ifstream::eofbit);
538 certFileStream.exceptions(std::ofstream::failbit |
539 std::ofstream::badbit |
540 std::ofstream::eofbit);
541 try
542 {
543 privKeyFileStream.open(privateKeyFile);
544 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500545 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500546 certFileStream << privKeyFileStream.rdbuf() << std::flush;
547 privKeyFileStream.close();
548 certFileStream.close();
549 }
550 catch (const std::exception& e)
551 {
552 log<level::ERR>("Failed to append private key",
553 entry("ERR=%s", e.what()),
554 entry("SRC=%s", privateKeyFile.c_str()),
555 entry("DST=%s", filePath.c_str()));
556 elog<InternalFailure>();
557 }
558 }
559}
560
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600561bool Certificate::compareKeys(const std::string& filePath)
562{
563 log<level::INFO>("Certificate compareKeys",
564 entry("FILEPATH=%s", filePath.c_str()));
565 X509_Ptr cert(X509_new(), ::X509_free);
566 if (!cert)
567 {
568 log<level::ERR>("Error occured during X509_new call",
569 entry("FILE=%s", filePath.c_str()),
570 entry("ERRCODE=%lu", ERR_get_error()));
571 elog<InternalFailure>();
572 }
573
574 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
575 if (!bioCert)
576 {
577 log<level::ERR>("Error occured during BIO_new_file call",
578 entry("FILE=%s", filePath.c_str()));
579 elog<InternalFailure>();
580 }
581
582 X509* x509 = cert.get();
583 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
584
585 EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
586 if (!pubKey)
587 {
588 log<level::ERR>("Error occurred during X509_get_pubkey",
589 entry("FILE=%s", filePath.c_str()),
590 entry("ERRCODE=%lu", ERR_get_error()));
591 elog<InvalidCertificate>(Reason("Failed to get public key info"));
592 }
593
594 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
595 if (!keyBio)
596 {
597 log<level::ERR>("Error occured during BIO_s_file call",
598 entry("FILE=%s", filePath.c_str()));
599 elog<InternalFailure>();
600 }
601 BIO_read_filename(keyBio.get(), filePath.c_str());
602
603 EVP_PKEY_Ptr priKey(
604 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
605 ::EVP_PKEY_free);
606 if (!priKey)
607 {
608 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
609 entry("FILE=%s", filePath.c_str()),
610 entry("ERRCODE=%lu", ERR_get_error()));
611 elog<InvalidCertificate>(Reason("Failed to get private key info"));
612 }
613
614 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
615 if (rc != 1)
616 {
617 log<level::ERR>("Private key is not matching with Certificate",
618 entry("FILE=%s", filePath.c_str()),
619 entry("ERRCODE=%d", rc));
620 return false;
621 }
622 return true;
623}
624
625void Certificate::reloadOrReset(const UnitsToRestart& unit)
626{
627 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
628 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
629 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
630 try
631 {
632 auto method =
633 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
634 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
635 method.append(unit, "replace");
636 bus.call_noreply(method);
637 }
638 catch (const sdbusplus::exception::SdBusError& e)
639 {
640 log<level::ERR>("Failed to reload or restart service",
641 entry("ERR=%s", e.what()),
642 entry("UNIT=%s", unit.c_str()));
643 elog<InternalFailure>();
644 }
645}
646} // namespace certs
647} // namespace phosphor