blob: 1ac54262b4bd5015e1a3d5fa632697982e759840 [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
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010074std::string Certificate::generateCertId(const std::string& certPath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +020075{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010076 const X509_Ptr cert = loadCert(certPath);
77 unsigned long subjectNameHash = X509_subject_name_hash(cert.get());
78 static constexpr auto CERT_ID_LENGTH = 9;
79 char idBuff[CERT_ID_LENGTH];
80
81 snprintf(idBuff, CERT_ID_LENGTH, "%08lx", subjectNameHash);
82
83 return std::string(idBuff);
84}
85
86std::string
87 Certificate::generateUniqueFilePath(const std::string& directoryPath)
88{
89 char* filePath = tempnam(directoryPath.c_str(), NULL);
90 if (filePath == NULL)
91 {
92 log<level::ERR>(
93 "Error occured while creating random certificate file path",
94 entry("DIR=%s", directoryPath.c_str()));
95 elog<InternalFailure>();
96 }
97 std::string filePathStr(filePath);
98 free(filePath);
99 return filePathStr;
100}
101
102std::string Certificate::generateAuthCertFileX509Path(
103 const std::string& certSrcFilePath, const std::string& certDstDirPath)
104{
105 const X509_Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200106 unsigned long hash = X509_subject_name_hash(cert.get());
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100107 static constexpr auto CERT_HASH_LENGTH = 9;
108 char hashBuf[CERT_HASH_LENGTH];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200109
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100110 snprintf(hashBuf, CERT_HASH_LENGTH, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200111
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100112 const std::string certHash(hashBuf);
113 const std::string certDstFileX509Path =
114 certDstDirPath + "/" + certHash + ".0";
115 if (fs::exists(certDstFileX509Path))
116 {
117 log<level::ERR>("Authority certificate x509 file path already used",
118 entry("CERT=%s", certDstFileX509Path.c_str()));
119 elog<InternalFailure>();
120 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200121
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100122 return certDstFileX509Path;
123}
124
125std::string
126 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
127{
128 // If there is a certificate file path (which means certificate replacement
129 // is doing) use it (do not create new one)
130 if (!certFilePath.empty())
131 {
132 return certFilePath;
133 }
134 // If source certificate file is located in the certificates directory use
135 // it (do not create new one)
136 else if (fs::path(certSrcFilePath).parent_path().string() ==
137 certInstallPath)
138 {
139 return certSrcFilePath;
140 }
141 // Otherwise generate new file name/path
142 else
143 {
144 return generateUniqueFilePath(certInstallPath);
145 }
146}
147
148std::string
149 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
150{
151 if (certType == phosphor::certs::AUTHORITY)
152 {
153 return generateAuthCertFilePath(certSrcFilePath);
154 }
155 else
156 {
157 return certInstallPath;
158 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200159}
160
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600161Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
162 const CertificateType& type,
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600163 const CertInstallPath& installPath,
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500164 const CertUploadPath& uploadPath,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200165 const CertWatchPtr& certWatchPtr, Manager& parent) :
Marri Devender Raoedd11312019-02-27 08:45:10 -0600166 CertIfaces(bus, objPath.c_str(), true),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100167 bus(bus), objectPath(objPath), certType(type), certInstallPath(installPath),
168 certWatchPtr(certWatchPtr), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600169{
170 auto installHelper = [this](const auto& filePath) {
171 if (!compareKeys(filePath))
172 {
173 elog<InvalidCertificate>(
174 Reason("Private key does not match the Certificate"));
175 };
176 };
177 typeFuncMap[SERVER] = installHelper;
178 typeFuncMap[CLIENT] = installHelper;
179 typeFuncMap[AUTHORITY] = [](auto filePath) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500180
181 auto appendPrivateKey = [this](const std::string& filePath) {
182 checkAndAppendPrivateKey(filePath);
183 };
184
185 appendKeyMap[SERVER] = appendPrivateKey;
186 appendKeyMap[CLIENT] = appendPrivateKey;
187 appendKeyMap[AUTHORITY] = [](const std::string& filePath) {};
188
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100189 // Generate certificate file path
190 certFilePath = generateCertFilePath(uploadPath);
191
Marri Devender Raocd30c492019-06-12 01:40:17 -0500192 // install the certificate
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100193 install(uploadPath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500194
Marri Devender Raoedd11312019-02-27 08:45:10 -0600195 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600196}
197
198Certificate::~Certificate()
199{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100200 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600201 {
202 log<level::INFO>("Certificate file not found!",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100203 entry("PATH=%s", certFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600204 }
205}
206
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500207void Certificate::replace(const std::string filePath)
208{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100209 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500210}
211
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100212void Certificate::install(const std::string& certSrcFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600213{
214 log<level::INFO>("Certificate install ",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100215 entry("FILEPATH=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600216 auto errCode = X509_V_OK;
217
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500218 // stop watch for user initiated certificate install
219 if (certWatchPtr)
220 {
221 certWatchPtr->stopWatch();
222 }
223
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600224 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100225 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600226 if (!fs::exists(file))
227 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100228 log<level::ERR>("File is Missing",
229 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600230 elog<InternalFailure>();
231 }
232
233 try
234 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100235 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600236 {
237 // file is empty
238 log<level::ERR>("File is empty",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100239 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600240 elog<InvalidCertificate>(Reason("File is empty"));
241 }
242 }
243 catch (const fs::filesystem_error& e)
244 {
245 // Log Error message
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100246 log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600247 elog<InternalFailure>();
248 }
249
250 // Defining store object as RAW to avoid double free.
251 // X509_LOOKUP_free free up store object.
252 // Create an empty X509_STORE structure for certificate validation.
253 auto x509Store = X509_STORE_new();
254 if (!x509Store)
255 {
256 log<level::ERR>("Error occured during X509_STORE_new call");
257 elog<InternalFailure>();
258 }
259
260 OpenSSL_add_all_algorithms();
261
262 // ADD Certificate Lookup method.
263 X509_LOOKUP_Ptr lookup(X509_STORE_add_lookup(x509Store, X509_LOOKUP_file()),
264 ::X509_LOOKUP_free);
265 if (!lookup)
266 {
267 // Normally lookup cleanup function interanlly does X509Store cleanup
268 // Free up the X509Store.
269 X509_STORE_free(x509Store);
270 log<level::ERR>("Error occured during X509_STORE_add_lookup call");
271 elog<InternalFailure>();
272 }
273 // Load Certificate file.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100274 errCode = X509_LOOKUP_load_file(lookup.get(), certSrcFilePath.c_str(),
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600275 X509_FILETYPE_PEM);
276 if (errCode != 1)
277 {
278 log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100279 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600280 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
281 }
282
283 // Load Certificate file into the X509 structre.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100284 X509_Ptr cert = loadCert(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600285 X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
286 if (!storeCtx)
287 {
288 log<level::ERR>("Error occured during X509_STORE_CTX_new call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100289 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600290 elog<InternalFailure>();
291 }
292
293 errCode = X509_STORE_CTX_init(storeCtx.get(), x509Store, cert.get(), NULL);
294 if (errCode != 1)
295 {
296 log<level::ERR>("Error occured during X509_STORE_CTX_init call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100297 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600298 elog<InternalFailure>();
299 }
300
301 // Set time to current time.
302 auto locTime = time(nullptr);
303
304 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
305 locTime);
306
307 errCode = X509_verify_cert(storeCtx.get());
308 if (errCode == 1)
309 {
310 errCode = X509_V_OK;
311 }
312 else if (errCode == 0)
313 {
314 errCode = X509_STORE_CTX_get_error(storeCtx.get());
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500315 log<level::INFO>(
316 "Error occured during X509_verify_cert call, checking for known "
317 "error",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100318 entry("FILE=%s", certSrcFilePath.c_str()),
319 entry("ERRCODE=%d", errCode),
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500320 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600321 }
322 else
323 {
324 log<level::ERR>("Error occured during X509_verify_cert call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100325 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600326 elog<InternalFailure>();
327 }
328
329 // Allow certificate upload, for "certificate is not yet valid" and
330 // trust chain related errors.
331 if (!((errCode == X509_V_OK) ||
332 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
333 TRUST_CHAIN_ERR(errCode)))
334 {
335 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
336 {
337 elog<InvalidCertificate>(Reason("Expired Certificate"));
338 }
339 // Loging general error here.
340 elog<InvalidCertificate>(Reason("Certificate validation failed"));
341 }
342
Marri Devender Raocd30c492019-06-12 01:40:17 -0500343 // Invoke type specific append private key function.
344 auto appendIter = appendKeyMap.find(certType);
345 if (appendIter == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600346 {
347 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
348 elog<InternalFailure>();
349 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100350 appendIter->second(certSrcFilePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500351
352 // Invoke type specific compare keys function.
353 auto compIter = typeFuncMap.find(certType);
354 if (compIter == typeFuncMap.end())
355 {
356 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
357 elog<InternalFailure>();
358 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100359 compIter->second(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200360
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500361 // Copy the certificate to the installation path
362 // During bootup will be parsing existing file so no need to
363 // copy it.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100364 if (certSrcFilePath != certFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600365 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500366 std::ifstream inputCertFileStream;
367 std::ofstream outputCertFileStream;
368 inputCertFileStream.exceptions(std::ifstream::failbit |
369 std::ifstream::badbit |
370 std::ifstream::eofbit);
371 outputCertFileStream.exceptions(std::ofstream::failbit |
372 std::ofstream::badbit |
373 std::ofstream::eofbit);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200374
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500375 try
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600376 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100377 inputCertFileStream.open(certSrcFilePath);
378 outputCertFileStream.open(certFilePath, std::ios::out);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500379 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
380 inputCertFileStream.close();
381 outputCertFileStream.close();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600382 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500383 catch (const std::exception& e)
384 {
385 log<level::ERR>("Failed to copy certificate",
386 entry("ERR=%s", e.what()),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100387 entry("SRC=%s", certSrcFilePath.c_str()),
388 entry("DST=%s", certFilePath.c_str()));
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500389 elog<InternalFailure>();
390 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600391 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500392
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100393 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600394
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100395 // Keep certificate ID
396 certId = generateCertId(certFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200397
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600398 // Parse the certificate file and populate properties
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100399 populateProperties(certFilePath);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500400
401 // restart watch
402 if (certWatchPtr)
403 {
404 certWatchPtr->startWatch();
405 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600406}
407
408void Certificate::populateProperties()
409{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200410 populateProperties(certInstallPath);
411}
412
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100413std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200414{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100415 return certId;
416}
417
418bool Certificate::isSame(const std::string& certPath)
419{
420 return getCertId() == generateCertId(certPath);
421}
422
423void Certificate::storageUpdate()
424{
425 if (certType == phosphor::certs::AUTHORITY)
426 {
427 // Create symbolic link in the certificate directory
428 std::string certFileX509Path;
429 try
430 {
431 if (!certFilePath.empty() &&
432 fs::is_regular_file(fs::path(certFilePath)))
433 {
434 certFileX509Path =
435 generateAuthCertFileX509Path(certFilePath, certInstallPath);
436 fs::create_symlink(fs::path(certFilePath),
437 fs::path(certFileX509Path));
438 }
439 }
440 catch (const std::exception& e)
441 {
442 log<level::ERR>("Failed to create symlink for certificate",
443 entry("ERR=%s", e.what()),
444 entry("FILE=%s", certFilePath.c_str()),
445 entry("SYMLINK=%s", certFileX509Path.c_str()));
446 elog<InternalFailure>();
447 }
448 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200449}
450
451void Certificate::populateProperties(const std::string& certPath)
452{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100453 X509_Ptr cert = loadCert(certPath);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600454 // Update properties if no error thrown
455 BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
456 PEM_write_bio_X509(certBio.get(), cert.get());
457 BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
458 BUF_MEM* buf = certBuf.get();
459 BIO_get_mem_ptr(certBio.get(), &buf);
460 std::string certStr(buf->data, buf->length);
461 CertificateIface::certificateString(certStr);
462
463 static const int maxKeySize = 4096;
464 char subBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500465 BIO_MEM_Ptr subBio(BIO_new(BIO_s_mem()), BIO_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600466 // This pointer cannot be freed independantly.
467 X509_NAME* sub = X509_get_subject_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500468 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
469 BIO_read(subBio.get(), subBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600470 CertificateIface::subject(subBuffer);
471
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600472 char issuerBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500473 BIO_MEM_Ptr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
474 // This pointer cannot be freed independantly.
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600475 X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500476 X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
477 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600478 CertificateIface::issuer(issuerBuffer);
479
480 std::vector<std::string> keyUsageList;
481 ASN1_BIT_STRING* usage;
482
483 // Go through each usage in the bit string and convert to
484 // corresponding string value
485 if ((usage = static_cast<ASN1_BIT_STRING*>(
486 X509_get_ext_d2i(cert.get(), NID_key_usage, NULL, NULL))))
487 {
488 for (auto i = 0; i < usage->length; ++i)
489 {
490 for (auto& x : keyUsageToRfStr)
491 {
492 if (x.first & usage->data[i])
493 {
494 keyUsageList.push_back(x.second);
495 break;
496 }
497 }
498 }
499 }
500
501 EXTENDED_KEY_USAGE* extUsage;
502 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
503 X509_get_ext_d2i(cert.get(), NID_ext_key_usage, NULL, NULL))))
504 {
505 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
506 {
507 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
508 sk_ASN1_OBJECT_value(extUsage, i))]);
509 }
510 }
511 CertificateIface::keyUsage(keyUsageList);
512
513 int days = 0;
514 int secs = 0;
515
516 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
517 // Set time to 12:00am GMT, Jan 1 1970
518 ASN1_TIME_set_string(epoch.get(), "700101120000Z");
519
520 static const int dayToSeconds = 24 * 60 * 60;
521 ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
522 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
523 CertificateIface::validNotAfter((days * dayToSeconds) + secs);
524
525 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
526 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
527 CertificateIface::validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600528}
529
530X509_Ptr Certificate::loadCert(const std::string& filePath)
531{
532 log<level::INFO>("Certificate loadCert",
533 entry("FILEPATH=%s", filePath.c_str()));
534 // Read Certificate file
535 X509_Ptr cert(X509_new(), ::X509_free);
536 if (!cert)
537 {
538 log<level::ERR>("Error occured during X509_new call",
539 entry("FILE=%s", filePath.c_str()),
540 entry("ERRCODE=%lu", ERR_get_error()));
541 elog<InternalFailure>();
542 }
543
544 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
545 if (!bioCert)
546 {
547 log<level::ERR>("Error occured during BIO_new_file call",
548 entry("FILE=%s", filePath.c_str()));
549 elog<InternalFailure>();
550 }
551
552 X509* x509 = cert.get();
553 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
554 {
555 log<level::ERR>("Error occured during PEM_read_bio_X509 call",
556 entry("FILE=%s", filePath.c_str()));
557 elog<InternalFailure>();
558 }
559 return cert;
560}
Marri Devender Raocd30c492019-06-12 01:40:17 -0500561
562void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
563{
564 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
565 if (!keyBio)
566 {
567 log<level::ERR>("Error occured during BIO_s_file call",
568 entry("FILE=%s", filePath.c_str()));
569 elog<InternalFailure>();
570 }
571 BIO_read_filename(keyBio.get(), filePath.c_str());
572
573 EVP_PKEY_Ptr priKey(
574 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
575 ::EVP_PKEY_free);
576 if (!priKey)
577 {
578 log<level::INFO>("Private key not present in file",
579 entry("FILE=%s", filePath.c_str()));
580 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
581 privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
582 if (!fs::exists(privateKeyFile))
583 {
584 log<level::ERR>("Private key file is not found",
585 entry("FILE=%s", privateKeyFile.c_str()));
586 elog<InternalFailure>();
587 }
588
589 std::ifstream privKeyFileStream;
590 std::ofstream certFileStream;
591 privKeyFileStream.exceptions(std::ifstream::failbit |
592 std::ifstream::badbit |
593 std::ifstream::eofbit);
594 certFileStream.exceptions(std::ofstream::failbit |
595 std::ofstream::badbit |
596 std::ofstream::eofbit);
597 try
598 {
599 privKeyFileStream.open(privateKeyFile);
600 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500601 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500602 certFileStream << privKeyFileStream.rdbuf() << std::flush;
603 privKeyFileStream.close();
604 certFileStream.close();
605 }
606 catch (const std::exception& e)
607 {
608 log<level::ERR>("Failed to append private key",
609 entry("ERR=%s", e.what()),
610 entry("SRC=%s", privateKeyFile.c_str()),
611 entry("DST=%s", filePath.c_str()));
612 elog<InternalFailure>();
613 }
614 }
615}
616
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600617bool Certificate::compareKeys(const std::string& filePath)
618{
619 log<level::INFO>("Certificate compareKeys",
620 entry("FILEPATH=%s", filePath.c_str()));
621 X509_Ptr cert(X509_new(), ::X509_free);
622 if (!cert)
623 {
624 log<level::ERR>("Error occured during X509_new call",
625 entry("FILE=%s", filePath.c_str()),
626 entry("ERRCODE=%lu", ERR_get_error()));
627 elog<InternalFailure>();
628 }
629
630 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
631 if (!bioCert)
632 {
633 log<level::ERR>("Error occured during BIO_new_file call",
634 entry("FILE=%s", filePath.c_str()));
635 elog<InternalFailure>();
636 }
637
638 X509* x509 = cert.get();
639 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
640
641 EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
642 if (!pubKey)
643 {
644 log<level::ERR>("Error occurred during X509_get_pubkey",
645 entry("FILE=%s", filePath.c_str()),
646 entry("ERRCODE=%lu", ERR_get_error()));
647 elog<InvalidCertificate>(Reason("Failed to get public key info"));
648 }
649
650 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
651 if (!keyBio)
652 {
653 log<level::ERR>("Error occured during BIO_s_file call",
654 entry("FILE=%s", filePath.c_str()));
655 elog<InternalFailure>();
656 }
657 BIO_read_filename(keyBio.get(), filePath.c_str());
658
659 EVP_PKEY_Ptr priKey(
660 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
661 ::EVP_PKEY_free);
662 if (!priKey)
663 {
664 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
665 entry("FILE=%s", filePath.c_str()),
666 entry("ERRCODE=%lu", ERR_get_error()));
667 elog<InvalidCertificate>(Reason("Failed to get private key info"));
668 }
669
670 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
671 if (rc != 1)
672 {
673 log<level::ERR>("Private key is not matching with Certificate",
674 entry("FILE=%s", filePath.c_str()),
675 entry("ERRCODE=%d", rc));
676 return false;
677 }
678 return true;
679}
680
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200681void Certificate::delete_()
682{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100683 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200684}
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600685} // namespace certs
686} // namespace phosphor