blob: 06822160427e52ec9f0c945244cbb0a1eb674b7a [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
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +02005#include "certs_manager.hpp"
6
Marri Devender Rao6ceec402019-02-01 03:15:19 -06007#include <openssl/bio.h>
8#include <openssl/crypto.h>
9#include <openssl/err.h>
10#include <openssl/evp.h>
11#include <openssl/pem.h>
12#include <openssl/x509v3.h>
13
14#include <fstream>
15#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050016#include <xyz/openbmc_project/Certs/error.hpp>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060017#include <xyz/openbmc_project/Common/error.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050018
Marri Devender Rao6ceec402019-02-01 03:15:19 -060019namespace phosphor
20{
21namespace certs
22{
23// RAII support for openSSL functions.
24using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
25using X509_STORE_CTX_Ptr =
26 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
27using X509_LOOKUP_Ptr =
28 std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060029using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060030using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
31using BUF_MEM_Ptr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
32using InternalFailure =
33 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
34using InvalidCertificate =
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050035 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
36using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060037
38// Trust chain related errors.`
39#define TRUST_CHAIN_ERR(errnum) \
40 ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || \
41 (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) || \
42 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) || \
43 (errnum == X509_V_ERR_CERT_UNTRUSTED) || \
44 (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
45
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060046// Refer to schema 2018.3
47// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
48// supported KeyUsage types in redfish
49// Refer to
50// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
51// key usage bit fields
52std::map<uint8_t, std::string> keyUsageToRfStr = {
53 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
54 {KU_NON_REPUDIATION, "NonRepudiation"},
55 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
56 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
57 {KU_KEY_AGREEMENT, "KeyAgreement"},
58 {KU_KEY_CERT_SIGN, "KeyCertSign"},
59 {KU_CRL_SIGN, "CRLSigning"},
60 {KU_ENCIPHER_ONLY, "EncipherOnly"},
61 {KU_DECIPHER_ONLY, "DecipherOnly"}};
62
63// Refer to schema 2018.3
64// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
65// supported Extended KeyUsage types in redfish
66std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
67 {NID_server_auth, "ServerAuthentication"},
68 {NID_client_auth, "ClientAuthentication"},
69 {NID_email_protect, "EmailProtection"},
70 {NID_OCSP_sign, "OCSPSigning"},
71 {NID_ad_timeStamping, "Timestamping"},
72 {NID_code_sign, "CodeSigning"}};
73
Kowalski, Kamildb029c92019-07-08 17:09:39 +020074std::string Certificate::getSubjectHash(const X509_STORE_CTX_Ptr& storeCtx)
75{
76 X509_Ptr cert(X509_STORE_CTX_get_current_cert(storeCtx.get()), ::X509_free);
77 unsigned long hash = X509_subject_name_hash(cert.get());
78
79 char hashBuf[9];
80
81 sprintf(hashBuf, "%08lx", hash);
82
83 return std::string(hashBuf);
84}
85
Marri Devender Rao6ceec402019-02-01 03:15:19 -060086Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
87 const CertificateType& type,
88 const UnitsToRestart& unit,
89 const CertInstallPath& installPath,
Marri Devender Rao8f80c352019-05-13 00:53:01 -050090 const CertUploadPath& uploadPath,
Marri Devender Raoffad1ef2019-06-03 04:54:12 -050091 bool isSkipUnitReload,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +020092 const CertWatchPtr& certWatchPtr, Manager& parent) :
Marri Devender Raoedd11312019-02-27 08:45:10 -060093 CertIfaces(bus, objPath.c_str(), true),
94 bus(bus), objectPath(objPath), certType(type), unitToRestart(unit),
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +020095 certInstallPath(installPath), certWatchPtr(certWatchPtr), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -060096{
97 auto installHelper = [this](const auto& filePath) {
98 if (!compareKeys(filePath))
99 {
100 elog<InvalidCertificate>(
101 Reason("Private key does not match the Certificate"));
102 };
103 };
104 typeFuncMap[SERVER] = installHelper;
105 typeFuncMap[CLIENT] = installHelper;
106 typeFuncMap[AUTHORITY] = [](auto filePath) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500107
108 auto appendPrivateKey = [this](const std::string& filePath) {
109 checkAndAppendPrivateKey(filePath);
110 };
111
112 appendKeyMap[SERVER] = appendPrivateKey;
113 appendKeyMap[CLIENT] = appendPrivateKey;
114 appendKeyMap[AUTHORITY] = [](const std::string& filePath) {};
115
116 // install the certificate
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500117 install(uploadPath, isSkipUnitReload);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500118
Marri Devender Raoedd11312019-02-27 08:45:10 -0600119 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600120}
121
122Certificate::~Certificate()
123{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200124 std::string installPath = certInstallPath;
125
126 if (certType == phosphor::certs::AUTHORITY)
127 {
128 installPath += "/" + certHash + ".0";
129 }
130
131 if (!fs::remove(installPath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600132 {
133 log<level::INFO>("Certificate file not found!",
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200134 entry("PATH=%s", installPath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600135 }
136 else if (!unitToRestart.empty())
137 {
138 reloadOrReset(unitToRestart);
139 }
140}
141
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500142void Certificate::replace(const std::string filePath)
143{
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500144 install(filePath, false);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500145}
146
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500147void Certificate::install(const std::string& filePath, bool isSkipUnitReload)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600148{
149 log<level::INFO>("Certificate install ",
150 entry("FILEPATH=%s", filePath.c_str()));
151 auto errCode = X509_V_OK;
152
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500153 // stop watch for user initiated certificate install
154 if (certWatchPtr)
155 {
156 certWatchPtr->stopWatch();
157 }
158
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600159 // Verify the certificate file
160 fs::path file(filePath);
161 if (!fs::exists(file))
162 {
163 log<level::ERR>("File is Missing", entry("FILE=%s", filePath.c_str()));
164 elog<InternalFailure>();
165 }
166
167 try
168 {
169 if (fs::file_size(filePath) == 0)
170 {
171 // file is empty
172 log<level::ERR>("File is empty",
173 entry("FILE=%s", filePath.c_str()));
174 elog<InvalidCertificate>(Reason("File is empty"));
175 }
176 }
177 catch (const fs::filesystem_error& e)
178 {
179 // Log Error message
180 log<level::ERR>(e.what(), entry("FILE=%s", filePath.c_str()));
181 elog<InternalFailure>();
182 }
183
184 // Defining store object as RAW to avoid double free.
185 // X509_LOOKUP_free free up store object.
186 // Create an empty X509_STORE structure for certificate validation.
187 auto x509Store = X509_STORE_new();
188 if (!x509Store)
189 {
190 log<level::ERR>("Error occured during X509_STORE_new call");
191 elog<InternalFailure>();
192 }
193
194 OpenSSL_add_all_algorithms();
195
196 // ADD Certificate Lookup method.
197 X509_LOOKUP_Ptr lookup(X509_STORE_add_lookup(x509Store, X509_LOOKUP_file()),
198 ::X509_LOOKUP_free);
199 if (!lookup)
200 {
201 // Normally lookup cleanup function interanlly does X509Store cleanup
202 // Free up the X509Store.
203 X509_STORE_free(x509Store);
204 log<level::ERR>("Error occured during X509_STORE_add_lookup call");
205 elog<InternalFailure>();
206 }
207 // Load Certificate file.
208 errCode = X509_LOOKUP_load_file(lookup.get(), filePath.c_str(),
209 X509_FILETYPE_PEM);
210 if (errCode != 1)
211 {
212 log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
213 entry("FILE=%s", filePath.c_str()));
214 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
215 }
216
217 // Load Certificate file into the X509 structre.
218 X509_Ptr cert = std::move(loadCert(filePath));
219 X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
220 if (!storeCtx)
221 {
222 log<level::ERR>("Error occured during X509_STORE_CTX_new call",
223 entry("FILE=%s", filePath.c_str()));
224 elog<InternalFailure>();
225 }
226
227 errCode = X509_STORE_CTX_init(storeCtx.get(), x509Store, cert.get(), NULL);
228 if (errCode != 1)
229 {
230 log<level::ERR>("Error occured during X509_STORE_CTX_init call",
231 entry("FILE=%s", filePath.c_str()));
232 elog<InternalFailure>();
233 }
234
235 // Set time to current time.
236 auto locTime = time(nullptr);
237
238 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
239 locTime);
240
241 errCode = X509_verify_cert(storeCtx.get());
242 if (errCode == 1)
243 {
244 errCode = X509_V_OK;
245 }
246 else if (errCode == 0)
247 {
248 errCode = X509_STORE_CTX_get_error(storeCtx.get());
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500249 log<level::INFO>(
250 "Error occured during X509_verify_cert call, checking for known "
251 "error",
252 entry("FILE=%s", filePath.c_str()), entry("ERRCODE=%d", errCode),
253 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600254 }
255 else
256 {
257 log<level::ERR>("Error occured during X509_verify_cert call",
258 entry("FILE=%s", filePath.c_str()));
259 elog<InternalFailure>();
260 }
261
262 // Allow certificate upload, for "certificate is not yet valid" and
263 // trust chain related errors.
264 if (!((errCode == X509_V_OK) ||
265 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
266 TRUST_CHAIN_ERR(errCode)))
267 {
268 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
269 {
270 elog<InvalidCertificate>(Reason("Expired Certificate"));
271 }
272 // Loging general error here.
273 elog<InvalidCertificate>(Reason("Certificate validation failed"));
274 }
275
Marri Devender Raocd30c492019-06-12 01:40:17 -0500276 // Invoke type specific append private key function.
277 auto appendIter = appendKeyMap.find(certType);
278 if (appendIter == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600279 {
280 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
281 elog<InternalFailure>();
282 }
Marri Devender Raocd30c492019-06-12 01:40:17 -0500283 appendIter->second(filePath);
284
285 // Invoke type specific compare keys function.
286 auto compIter = typeFuncMap.find(certType);
287 if (compIter == typeFuncMap.end())
288 {
289 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
290 elog<InternalFailure>();
291 }
292 compIter->second(filePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600293
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200294 std::string newHash = getSubjectHash(storeCtx);
295 std::string installPath = certInstallPath;
296
297 if (certType == phosphor::certs::AUTHORITY)
298 {
299 // Save under OpenSSL lib acceptable name
300 installPath += "/" + newHash + ".0";
301
302 // Check if we are not trying to overwrite already existing certificate
303 if (certHash != newHash && fs::exists(installPath) &&
304 filePath != installPath)
305 {
306 using NotAllowed =
307 sdbusplus::xyz::openbmc_project::Common::Error::NotAllowed;
308 using Reason = xyz::openbmc_project::Common::NotAllowed::REASON;
309
310 elog<NotAllowed>(Reason("Certificate already exist"));
311 }
312 }
313
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500314 // Copy the certificate to the installation path
315 // During bootup will be parsing existing file so no need to
316 // copy it.
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200317 if (filePath != installPath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600318 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500319 std::ifstream inputCertFileStream;
320 std::ofstream outputCertFileStream;
321 inputCertFileStream.exceptions(std::ifstream::failbit |
322 std::ifstream::badbit |
323 std::ifstream::eofbit);
324 outputCertFileStream.exceptions(std::ofstream::failbit |
325 std::ofstream::badbit |
326 std::ofstream::eofbit);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200327
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500328 try
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600329 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500330 inputCertFileStream.open(filePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200331 outputCertFileStream.open(installPath, std::ios::out);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500332 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
333 inputCertFileStream.close();
334 outputCertFileStream.close();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600335 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500336 catch (const std::exception& e)
337 {
338 log<level::ERR>("Failed to copy certificate",
339 entry("ERR=%s", e.what()),
340 entry("SRC=%s", filePath.c_str()),
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200341 entry("DST=%s", installPath.c_str()));
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500342 elog<InternalFailure>();
343 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200344
345 if (certHash != newHash && !certHash.empty() &&
346 certType == phosphor::certs::AUTHORITY)
347 {
348 std::string oldPath = certInstallPath + "/" + certHash + ".0";
349
350 // Remove previous file
351 try
352 {
353 fs::remove(oldPath);
354 }
355 catch (const std::exception& e)
356 {
357 log<level::ERR>("Failed to remove old certificate",
358 entry("ERR=%s", e.what()),
359 entry("OLD=%s", oldPath.c_str()),
360 entry("NEW=%s", installPath.c_str()));
361 }
362 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600363 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500364
365 if (!isSkipUnitReload)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600366 {
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500367 // restart the units
368 if (!unitToRestart.empty())
369 {
370 reloadOrReset(unitToRestart);
371 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600372 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600373
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200374 // Store current hash
375 certHash = newHash;
376
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600377 // Parse the certificate file and populate properties
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200378 populateProperties(installPath);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500379
380 // restart watch
381 if (certWatchPtr)
382 {
383 certWatchPtr->startWatch();
384 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600385}
386
387void Certificate::populateProperties()
388{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200389 populateProperties(certInstallPath);
390}
391
392const std::string& Certificate::getHash() const
393{
394 return certHash;
395}
396
397void Certificate::populateProperties(const std::string& certPath)
398{
399 X509_Ptr cert = std::move(loadCert(certPath));
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600400 // Update properties if no error thrown
401 BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
402 PEM_write_bio_X509(certBio.get(), cert.get());
403 BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
404 BUF_MEM* buf = certBuf.get();
405 BIO_get_mem_ptr(certBio.get(), &buf);
406 std::string certStr(buf->data, buf->length);
407 CertificateIface::certificateString(certStr);
408
409 static const int maxKeySize = 4096;
410 char subBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500411 BIO_MEM_Ptr subBio(BIO_new(BIO_s_mem()), BIO_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600412 // This pointer cannot be freed independantly.
413 X509_NAME* sub = X509_get_subject_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500414 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
415 BIO_read(subBio.get(), subBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600416 CertificateIface::subject(subBuffer);
417
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600418 char issuerBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500419 BIO_MEM_Ptr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
420 // This pointer cannot be freed independantly.
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600421 X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500422 X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
423 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600424 CertificateIface::issuer(issuerBuffer);
425
426 std::vector<std::string> keyUsageList;
427 ASN1_BIT_STRING* usage;
428
429 // Go through each usage in the bit string and convert to
430 // corresponding string value
431 if ((usage = static_cast<ASN1_BIT_STRING*>(
432 X509_get_ext_d2i(cert.get(), NID_key_usage, NULL, NULL))))
433 {
434 for (auto i = 0; i < usage->length; ++i)
435 {
436 for (auto& x : keyUsageToRfStr)
437 {
438 if (x.first & usage->data[i])
439 {
440 keyUsageList.push_back(x.second);
441 break;
442 }
443 }
444 }
445 }
446
447 EXTENDED_KEY_USAGE* extUsage;
448 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
449 X509_get_ext_d2i(cert.get(), NID_ext_key_usage, NULL, NULL))))
450 {
451 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
452 {
453 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
454 sk_ASN1_OBJECT_value(extUsage, i))]);
455 }
456 }
457 CertificateIface::keyUsage(keyUsageList);
458
459 int days = 0;
460 int secs = 0;
461
462 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
463 // Set time to 12:00am GMT, Jan 1 1970
464 ASN1_TIME_set_string(epoch.get(), "700101120000Z");
465
466 static const int dayToSeconds = 24 * 60 * 60;
467 ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
468 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
469 CertificateIface::validNotAfter((days * dayToSeconds) + secs);
470
471 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
472 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
473 CertificateIface::validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600474}
475
476X509_Ptr Certificate::loadCert(const std::string& filePath)
477{
478 log<level::INFO>("Certificate loadCert",
479 entry("FILEPATH=%s", filePath.c_str()));
480 // Read Certificate file
481 X509_Ptr cert(X509_new(), ::X509_free);
482 if (!cert)
483 {
484 log<level::ERR>("Error occured during X509_new call",
485 entry("FILE=%s", filePath.c_str()),
486 entry("ERRCODE=%lu", ERR_get_error()));
487 elog<InternalFailure>();
488 }
489
490 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
491 if (!bioCert)
492 {
493 log<level::ERR>("Error occured during BIO_new_file call",
494 entry("FILE=%s", filePath.c_str()));
495 elog<InternalFailure>();
496 }
497
498 X509* x509 = cert.get();
499 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
500 {
501 log<level::ERR>("Error occured during PEM_read_bio_X509 call",
502 entry("FILE=%s", filePath.c_str()));
503 elog<InternalFailure>();
504 }
505 return cert;
506}
Marri Devender Raocd30c492019-06-12 01:40:17 -0500507
508void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
509{
510 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
511 if (!keyBio)
512 {
513 log<level::ERR>("Error occured during BIO_s_file call",
514 entry("FILE=%s", filePath.c_str()));
515 elog<InternalFailure>();
516 }
517 BIO_read_filename(keyBio.get(), filePath.c_str());
518
519 EVP_PKEY_Ptr priKey(
520 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
521 ::EVP_PKEY_free);
522 if (!priKey)
523 {
524 log<level::INFO>("Private key not present in file",
525 entry("FILE=%s", filePath.c_str()));
526 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
527 privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
528 if (!fs::exists(privateKeyFile))
529 {
530 log<level::ERR>("Private key file is not found",
531 entry("FILE=%s", privateKeyFile.c_str()));
532 elog<InternalFailure>();
533 }
534
535 std::ifstream privKeyFileStream;
536 std::ofstream certFileStream;
537 privKeyFileStream.exceptions(std::ifstream::failbit |
538 std::ifstream::badbit |
539 std::ifstream::eofbit);
540 certFileStream.exceptions(std::ofstream::failbit |
541 std::ofstream::badbit |
542 std::ofstream::eofbit);
543 try
544 {
545 privKeyFileStream.open(privateKeyFile);
546 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500547 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500548 certFileStream << privKeyFileStream.rdbuf() << std::flush;
549 privKeyFileStream.close();
550 certFileStream.close();
551 }
552 catch (const std::exception& e)
553 {
554 log<level::ERR>("Failed to append private key",
555 entry("ERR=%s", e.what()),
556 entry("SRC=%s", privateKeyFile.c_str()),
557 entry("DST=%s", filePath.c_str()));
558 elog<InternalFailure>();
559 }
560 }
561}
562
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600563bool Certificate::compareKeys(const std::string& filePath)
564{
565 log<level::INFO>("Certificate compareKeys",
566 entry("FILEPATH=%s", filePath.c_str()));
567 X509_Ptr cert(X509_new(), ::X509_free);
568 if (!cert)
569 {
570 log<level::ERR>("Error occured during X509_new call",
571 entry("FILE=%s", filePath.c_str()),
572 entry("ERRCODE=%lu", ERR_get_error()));
573 elog<InternalFailure>();
574 }
575
576 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
577 if (!bioCert)
578 {
579 log<level::ERR>("Error occured during BIO_new_file call",
580 entry("FILE=%s", filePath.c_str()));
581 elog<InternalFailure>();
582 }
583
584 X509* x509 = cert.get();
585 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
586
587 EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
588 if (!pubKey)
589 {
590 log<level::ERR>("Error occurred during X509_get_pubkey",
591 entry("FILE=%s", filePath.c_str()),
592 entry("ERRCODE=%lu", ERR_get_error()));
593 elog<InvalidCertificate>(Reason("Failed to get public key info"));
594 }
595
596 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
597 if (!keyBio)
598 {
599 log<level::ERR>("Error occured during BIO_s_file call",
600 entry("FILE=%s", filePath.c_str()));
601 elog<InternalFailure>();
602 }
603 BIO_read_filename(keyBio.get(), filePath.c_str());
604
605 EVP_PKEY_Ptr priKey(
606 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
607 ::EVP_PKEY_free);
608 if (!priKey)
609 {
610 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
611 entry("FILE=%s", filePath.c_str()),
612 entry("ERRCODE=%lu", ERR_get_error()));
613 elog<InvalidCertificate>(Reason("Failed to get private key info"));
614 }
615
616 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
617 if (rc != 1)
618 {
619 log<level::ERR>("Private key is not matching with Certificate",
620 entry("FILE=%s", filePath.c_str()),
621 entry("ERRCODE=%d", rc));
622 return false;
623 }
624 return true;
625}
626
627void Certificate::reloadOrReset(const UnitsToRestart& unit)
628{
629 constexpr auto SYSTEMD_SERVICE = "org.freedesktop.systemd1";
630 constexpr auto SYSTEMD_OBJ_PATH = "/org/freedesktop/systemd1";
631 constexpr auto SYSTEMD_INTERFACE = "org.freedesktop.systemd1.Manager";
632 try
633 {
634 auto method =
635 bus.new_method_call(SYSTEMD_SERVICE, SYSTEMD_OBJ_PATH,
636 SYSTEMD_INTERFACE, "ReloadOrRestartUnit");
637 method.append(unit, "replace");
638 bus.call_noreply(method);
639 }
640 catch (const sdbusplus::exception::SdBusError& e)
641 {
642 log<level::ERR>("Failed to reload or restart service",
643 entry("ERR=%s", e.what()),
644 entry("UNIT=%s", unit.c_str()));
645 elog<InternalFailure>();
646 }
647}
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200648
649void Certificate::delete_()
650{
651 manager.deleteCertificate(getHash());
652}
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600653} // namespace certs
654} // namespace phosphor