blob: de01f4c737634193c93a7525e3e039aa4962ac53 [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>
Nidhin MS811a29e2021-05-13 12:54:32 +053012#include <openssl/ssl.h>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060013#include <openssl/x509v3.h>
14
15#include <fstream>
16#include <phosphor-logging/elog-errors.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050017#include <xyz/openbmc_project/Certs/error.hpp>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060018#include <xyz/openbmc_project/Common/error.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050019
Marri Devender Rao6ceec402019-02-01 03:15:19 -060020namespace phosphor
21{
22namespace certs
23{
24// RAII support for openSSL functions.
25using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
Patrick Williams83e6eab2021-12-05 06:32:24 -060026using X509_STORE_Ptr =
27 std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060028using X509_STORE_CTX_Ptr =
29 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060030using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060031using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
32using BUF_MEM_Ptr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
33using InternalFailure =
34 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
35using InvalidCertificate =
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050036 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
37using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060038
39// Trust chain related errors.`
40#define TRUST_CHAIN_ERR(errnum) \
41 ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || \
42 (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) || \
43 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) || \
Nan Zhou63540132021-11-25 15:47:52 -080044 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) || \
Marri Devender Rao6ceec402019-02-01 03:15:19 -060045 (errnum == X509_V_ERR_CERT_UNTRUSTED) || \
46 (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
47
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060048// Refer to schema 2018.3
49// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
50// supported KeyUsage types in redfish
51// Refer to
52// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
53// key usage bit fields
54std::map<uint8_t, std::string> keyUsageToRfStr = {
55 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
56 {KU_NON_REPUDIATION, "NonRepudiation"},
57 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
58 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
59 {KU_KEY_AGREEMENT, "KeyAgreement"},
60 {KU_KEY_CERT_SIGN, "KeyCertSign"},
61 {KU_CRL_SIGN, "CRLSigning"},
62 {KU_ENCIPHER_ONLY, "EncipherOnly"},
63 {KU_DECIPHER_ONLY, "DecipherOnly"}};
64
65// Refer to schema 2018.3
66// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
67// supported Extended KeyUsage types in redfish
68std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
69 {NID_server_auth, "ServerAuthentication"},
70 {NID_client_auth, "ClientAuthentication"},
71 {NID_email_protect, "EmailProtection"},
72 {NID_OCSP_sign, "OCSPSigning"},
73 {NID_ad_timeStamping, "Timestamping"},
74 {NID_code_sign, "CodeSigning"}};
75
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010076std::string Certificate::generateCertId(const std::string& certPath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +020077{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010078 const X509_Ptr cert = loadCert(certPath);
79 unsigned long subjectNameHash = X509_subject_name_hash(cert.get());
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +010080 unsigned long issuerSerialHash = X509_issuer_and_serial_hash(cert.get());
81 static constexpr auto CERT_ID_LENGTH = 17;
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010082 char idBuff[CERT_ID_LENGTH];
83
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +010084 snprintf(idBuff, CERT_ID_LENGTH, "%08lx%08lx", subjectNameHash,
85 issuerSerialHash);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010086
87 return std::string(idBuff);
88}
89
90std::string
91 Certificate::generateUniqueFilePath(const std::string& directoryPath)
92{
93 char* filePath = tempnam(directoryPath.c_str(), NULL);
94 if (filePath == NULL)
95 {
96 log<level::ERR>(
97 "Error occured while creating random certificate file path",
98 entry("DIR=%s", directoryPath.c_str()));
99 elog<InternalFailure>();
100 }
101 std::string filePathStr(filePath);
102 free(filePath);
103 return filePathStr;
104}
105
106std::string Certificate::generateAuthCertFileX509Path(
107 const std::string& certSrcFilePath, const std::string& certDstDirPath)
108{
109 const X509_Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200110 unsigned long hash = X509_subject_name_hash(cert.get());
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100111 static constexpr auto CERT_HASH_LENGTH = 9;
112 char hashBuf[CERT_HASH_LENGTH];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200113
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100114 snprintf(hashBuf, CERT_HASH_LENGTH, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200115
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100116 const std::string certHash(hashBuf);
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100117 for (int i = 0; i < AUTHORITY_CERTIFICATES_LIMIT; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100118 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100119 const std::string certDstFileX509Path =
120 certDstDirPath + "/" + certHash + "." + std::to_string(i);
121 if (!fs::exists(certDstFileX509Path))
122 {
123 return certDstFileX509Path;
124 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100125 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200126
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100127 log<level::ERR>("Authority certificate x509 file path already used",
128 entry("DIR=%s", certDstDirPath.c_str()));
129 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100130}
131
132std::string
133 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
134{
135 // If there is a certificate file path (which means certificate replacement
136 // is doing) use it (do not create new one)
137 if (!certFilePath.empty())
138 {
139 return certFilePath;
140 }
141 // If source certificate file is located in the certificates directory use
142 // it (do not create new one)
143 else if (fs::path(certSrcFilePath).parent_path().string() ==
144 certInstallPath)
145 {
146 return certSrcFilePath;
147 }
148 // Otherwise generate new file name/path
149 else
150 {
151 return generateUniqueFilePath(certInstallPath);
152 }
153}
154
155std::string
156 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
157{
158 if (certType == phosphor::certs::AUTHORITY)
159 {
160 return generateAuthCertFilePath(certSrcFilePath);
161 }
162 else
163 {
164 return certInstallPath;
165 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200166}
167
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600168Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
169 const CertificateType& type,
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600170 const CertInstallPath& installPath,
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500171 const CertUploadPath& uploadPath,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200172 const CertWatchPtr& certWatchPtr, Manager& parent) :
Marri Devender Raoedd11312019-02-27 08:45:10 -0600173 CertIfaces(bus, objPath.c_str(), true),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100174 bus(bus), objectPath(objPath), certType(type), certInstallPath(installPath),
175 certWatchPtr(certWatchPtr), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600176{
177 auto installHelper = [this](const auto& filePath) {
178 if (!compareKeys(filePath))
179 {
180 elog<InvalidCertificate>(
181 Reason("Private key does not match the Certificate"));
182 };
183 };
184 typeFuncMap[SERVER] = installHelper;
185 typeFuncMap[CLIENT] = installHelper;
Patrick Williams7e2797e2021-12-03 13:37:07 -0600186 typeFuncMap[AUTHORITY] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500187
188 auto appendPrivateKey = [this](const std::string& filePath) {
189 checkAndAppendPrivateKey(filePath);
190 };
191
192 appendKeyMap[SERVER] = appendPrivateKey;
193 appendKeyMap[CLIENT] = appendPrivateKey;
Patrick Williams7e2797e2021-12-03 13:37:07 -0600194 appendKeyMap[AUTHORITY] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500195
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100196 // Generate certificate file path
197 certFilePath = generateCertFilePath(uploadPath);
198
Marri Devender Raocd30c492019-06-12 01:40:17 -0500199 // install the certificate
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100200 install(uploadPath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500201
Marri Devender Raoedd11312019-02-27 08:45:10 -0600202 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600203}
204
205Certificate::~Certificate()
206{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100207 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600208 {
209 log<level::INFO>("Certificate file not found!",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100210 entry("PATH=%s", certFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600211 }
212}
213
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500214void Certificate::replace(const std::string filePath)
215{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100216 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500217}
218
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100219void Certificate::install(const std::string& certSrcFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600220{
221 log<level::INFO>("Certificate install ",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100222 entry("FILEPATH=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600223 auto errCode = X509_V_OK;
224
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500225 // stop watch for user initiated certificate install
226 if (certWatchPtr)
227 {
228 certWatchPtr->stopWatch();
229 }
230
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600231 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100232 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600233 if (!fs::exists(file))
234 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100235 log<level::ERR>("File is Missing",
236 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600237 elog<InternalFailure>();
238 }
239
240 try
241 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100242 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600243 {
244 // file is empty
245 log<level::ERR>("File is empty",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100246 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600247 elog<InvalidCertificate>(Reason("File is empty"));
248 }
249 }
250 catch (const fs::filesystem_error& e)
251 {
252 // Log Error message
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100253 log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600254 elog<InternalFailure>();
255 }
256
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600257 // Create an empty X509_STORE structure for certificate validation.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600258 X509_STORE_Ptr x509Store(X509_STORE_new(), &X509_STORE_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600259 if (!x509Store)
260 {
261 log<level::ERR>("Error occured during X509_STORE_new call");
262 elog<InternalFailure>();
263 }
264
265 OpenSSL_add_all_algorithms();
266
267 // ADD Certificate Lookup method.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600268 // lookup will be cleaned up automatically when the holding Store goes away.
269 auto lookup = X509_STORE_add_lookup(x509Store.get(), X509_LOOKUP_file());
270
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600271 if (!lookup)
272 {
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600273 log<level::ERR>("Error occured during X509_STORE_add_lookup call");
274 elog<InternalFailure>();
275 }
276 // Load Certificate file.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600277 errCode = X509_LOOKUP_load_file(lookup, certSrcFilePath.c_str(),
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600278 X509_FILETYPE_PEM);
279 if (errCode != 1)
280 {
281 log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100282 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600283 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
284 }
285
286 // Load Certificate file into the X509 structre.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100287 X509_Ptr cert = loadCert(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600288 X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
289 if (!storeCtx)
290 {
291 log<level::ERR>("Error occured during X509_STORE_CTX_new call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100292 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600293 elog<InternalFailure>();
294 }
295
Patrick Williams83e6eab2021-12-05 06:32:24 -0600296 errCode =
297 X509_STORE_CTX_init(storeCtx.get(), x509Store.get(), cert.get(), NULL);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600298 if (errCode != 1)
299 {
300 log<level::ERR>("Error occured during X509_STORE_CTX_init call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100301 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600302 elog<InternalFailure>();
303 }
304
305 // Set time to current time.
306 auto locTime = time(nullptr);
307
308 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
309 locTime);
310
311 errCode = X509_verify_cert(storeCtx.get());
312 if (errCode == 1)
313 {
314 errCode = X509_V_OK;
315 }
316 else if (errCode == 0)
317 {
318 errCode = X509_STORE_CTX_get_error(storeCtx.get());
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500319 log<level::INFO>(
320 "Error occured during X509_verify_cert call, checking for known "
321 "error",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100322 entry("FILE=%s", certSrcFilePath.c_str()),
323 entry("ERRCODE=%d", errCode),
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500324 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600325 }
326 else
327 {
328 log<level::ERR>("Error occured during X509_verify_cert call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100329 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600330 elog<InternalFailure>();
331 }
332
333 // Allow certificate upload, for "certificate is not yet valid" and
334 // trust chain related errors.
335 if (!((errCode == X509_V_OK) ||
336 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
337 TRUST_CHAIN_ERR(errCode)))
338 {
339 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
340 {
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500341 log<level::ERR>("Expired certificate ");
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600342 elog<InvalidCertificate>(Reason("Expired Certificate"));
343 }
344 // Loging general error here.
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500345 log<level::ERR>(
346 "Certificate validation failed", entry("ERRCODE=%d", errCode),
347 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600348 elog<InvalidCertificate>(Reason("Certificate validation failed"));
349 }
350
Nan Zhoucf811c42021-12-02 14:56:17 -0800351 validateCertificateStartDate(cert);
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500352
Nidhin MS811a29e2021-05-13 12:54:32 +0530353 // Verify that the certificate can be used in a TLS context
354 const SSL_METHOD* method = TLS_method();
355 std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)> ctx(SSL_CTX_new(method),
356 SSL_CTX_free);
357 if (SSL_CTX_use_certificate(ctx.get(), cert.get()) != 1)
358 {
359 log<level::ERR>("Certificate is not usable",
360 entry("ERRCODE=%x", ERR_get_error()));
361 elog<InvalidCertificate>(Reason("Certificate is not usable"));
362 }
363
Marri Devender Raocd30c492019-06-12 01:40:17 -0500364 // Invoke type specific append private key function.
365 auto appendIter = appendKeyMap.find(certType);
366 if (appendIter == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600367 {
368 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
369 elog<InternalFailure>();
370 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100371 appendIter->second(certSrcFilePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500372
373 // Invoke type specific compare keys function.
374 auto compIter = typeFuncMap.find(certType);
375 if (compIter == typeFuncMap.end())
376 {
377 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
378 elog<InternalFailure>();
379 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100380 compIter->second(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200381
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500382 // Copy the certificate to the installation path
383 // During bootup will be parsing existing file so no need to
384 // copy it.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100385 if (certSrcFilePath != certFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600386 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500387 std::ifstream inputCertFileStream;
388 std::ofstream outputCertFileStream;
389 inputCertFileStream.exceptions(std::ifstream::failbit |
390 std::ifstream::badbit |
391 std::ifstream::eofbit);
392 outputCertFileStream.exceptions(std::ofstream::failbit |
393 std::ofstream::badbit |
394 std::ofstream::eofbit);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200395
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500396 try
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600397 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100398 inputCertFileStream.open(certSrcFilePath);
399 outputCertFileStream.open(certFilePath, std::ios::out);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500400 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
401 inputCertFileStream.close();
402 outputCertFileStream.close();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600403 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500404 catch (const std::exception& e)
405 {
406 log<level::ERR>("Failed to copy certificate",
407 entry("ERR=%s", e.what()),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100408 entry("SRC=%s", certSrcFilePath.c_str()),
409 entry("DST=%s", certFilePath.c_str()));
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500410 elog<InternalFailure>();
411 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600412 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500413
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100414 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600415
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100416 // Keep certificate ID
417 certId = generateCertId(certFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200418
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600419 // Parse the certificate file and populate properties
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100420 populateProperties(certFilePath);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500421
422 // restart watch
423 if (certWatchPtr)
424 {
425 certWatchPtr->startWatch();
426 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600427}
428
Nan Zhoucf811c42021-12-02 14:56:17 -0800429// Checks that notBefore is not earlier than the unix epoch given that
430// the corresponding DBus interface is uint64_t.
431void Certificate::validateCertificateStartDate(const X509_Ptr& cert)
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500432{
433 int days = 0;
434 int secs = 0;
435
436 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800437 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
438 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500439
Nan Zhoucf811c42021-12-02 14:56:17 -0800440 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
441 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500442
Nan Zhoucf811c42021-12-02 14:56:17 -0800443 if (days < 0 || secs < 0)
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500444 {
Nan Zhoucf811c42021-12-02 14:56:17 -0800445 log<level::ERR>("Certificate valid date starts before the Unix Epoch");
446 elog<InvalidCertificate>(
447 Reason("NotBefore should after 19700101000000Z"));
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500448 }
449}
450
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600451void Certificate::populateProperties()
452{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200453 populateProperties(certInstallPath);
454}
455
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100456std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200457{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100458 return certId;
459}
460
461bool Certificate::isSame(const std::string& certPath)
462{
463 return getCertId() == generateCertId(certPath);
464}
465
466void Certificate::storageUpdate()
467{
468 if (certType == phosphor::certs::AUTHORITY)
469 {
470 // Create symbolic link in the certificate directory
471 std::string certFileX509Path;
472 try
473 {
474 if (!certFilePath.empty() &&
475 fs::is_regular_file(fs::path(certFilePath)))
476 {
477 certFileX509Path =
478 generateAuthCertFileX509Path(certFilePath, certInstallPath);
479 fs::create_symlink(fs::path(certFilePath),
480 fs::path(certFileX509Path));
481 }
482 }
483 catch (const std::exception& e)
484 {
485 log<level::ERR>("Failed to create symlink for certificate",
486 entry("ERR=%s", e.what()),
487 entry("FILE=%s", certFilePath.c_str()),
488 entry("SYMLINK=%s", certFileX509Path.c_str()));
489 elog<InternalFailure>();
490 }
491 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200492}
493
494void Certificate::populateProperties(const std::string& certPath)
495{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100496 X509_Ptr cert = loadCert(certPath);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600497 // Update properties if no error thrown
498 BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
499 PEM_write_bio_X509(certBio.get(), cert.get());
500 BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
501 BUF_MEM* buf = certBuf.get();
502 BIO_get_mem_ptr(certBio.get(), &buf);
503 std::string certStr(buf->data, buf->length);
504 CertificateIface::certificateString(certStr);
505
506 static const int maxKeySize = 4096;
507 char subBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500508 BIO_MEM_Ptr subBio(BIO_new(BIO_s_mem()), BIO_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600509 // This pointer cannot be freed independantly.
510 X509_NAME* sub = X509_get_subject_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500511 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
512 BIO_read(subBio.get(), subBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600513 CertificateIface::subject(subBuffer);
514
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600515 char issuerBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500516 BIO_MEM_Ptr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
517 // This pointer cannot be freed independantly.
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600518 X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500519 X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
520 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600521 CertificateIface::issuer(issuerBuffer);
522
523 std::vector<std::string> keyUsageList;
524 ASN1_BIT_STRING* usage;
525
526 // Go through each usage in the bit string and convert to
527 // corresponding string value
528 if ((usage = static_cast<ASN1_BIT_STRING*>(
529 X509_get_ext_d2i(cert.get(), NID_key_usage, NULL, NULL))))
530 {
531 for (auto i = 0; i < usage->length; ++i)
532 {
533 for (auto& x : keyUsageToRfStr)
534 {
535 if (x.first & usage->data[i])
536 {
537 keyUsageList.push_back(x.second);
538 break;
539 }
540 }
541 }
542 }
543
544 EXTENDED_KEY_USAGE* extUsage;
545 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
546 X509_get_ext_d2i(cert.get(), NID_ext_key_usage, NULL, NULL))))
547 {
548 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
549 {
550 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
551 sk_ASN1_OBJECT_value(extUsage, i))]);
552 }
553 }
554 CertificateIface::keyUsage(keyUsageList);
555
556 int days = 0;
557 int secs = 0;
558
559 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800560 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
561 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600562
Nan Zhoucf811c42021-12-02 14:56:17 -0800563 static const uint64_t dayToSeconds = 24 * 60 * 60;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600564 ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
565 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
566 CertificateIface::validNotAfter((days * dayToSeconds) + secs);
567
568 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
569 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
570 CertificateIface::validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600571}
572
573X509_Ptr Certificate::loadCert(const std::string& filePath)
574{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600575 // Read Certificate file
576 X509_Ptr cert(X509_new(), ::X509_free);
577 if (!cert)
578 {
579 log<level::ERR>("Error occured during X509_new call",
580 entry("FILE=%s", filePath.c_str()),
581 entry("ERRCODE=%lu", ERR_get_error()));
582 elog<InternalFailure>();
583 }
584
585 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
586 if (!bioCert)
587 {
588 log<level::ERR>("Error occured during BIO_new_file call",
589 entry("FILE=%s", filePath.c_str()));
590 elog<InternalFailure>();
591 }
592
593 X509* x509 = cert.get();
594 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
595 {
596 log<level::ERR>("Error occured during PEM_read_bio_X509 call",
597 entry("FILE=%s", filePath.c_str()));
598 elog<InternalFailure>();
599 }
600 return cert;
601}
Marri Devender Raocd30c492019-06-12 01:40:17 -0500602
603void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
604{
605 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
606 if (!keyBio)
607 {
608 log<level::ERR>("Error occured during BIO_s_file call",
609 entry("FILE=%s", filePath.c_str()));
610 elog<InternalFailure>();
611 }
612 BIO_read_filename(keyBio.get(), filePath.c_str());
613
614 EVP_PKEY_Ptr priKey(
615 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
616 ::EVP_PKEY_free);
617 if (!priKey)
618 {
619 log<level::INFO>("Private key not present in file",
620 entry("FILE=%s", filePath.c_str()));
621 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
622 privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
623 if (!fs::exists(privateKeyFile))
624 {
625 log<level::ERR>("Private key file is not found",
626 entry("FILE=%s", privateKeyFile.c_str()));
627 elog<InternalFailure>();
628 }
629
630 std::ifstream privKeyFileStream;
631 std::ofstream certFileStream;
632 privKeyFileStream.exceptions(std::ifstream::failbit |
633 std::ifstream::badbit |
634 std::ifstream::eofbit);
635 certFileStream.exceptions(std::ofstream::failbit |
636 std::ofstream::badbit |
637 std::ofstream::eofbit);
638 try
639 {
640 privKeyFileStream.open(privateKeyFile);
641 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500642 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500643 certFileStream << privKeyFileStream.rdbuf() << std::flush;
644 privKeyFileStream.close();
645 certFileStream.close();
646 }
647 catch (const std::exception& e)
648 {
649 log<level::ERR>("Failed to append private key",
650 entry("ERR=%s", e.what()),
651 entry("SRC=%s", privateKeyFile.c_str()),
652 entry("DST=%s", filePath.c_str()));
653 elog<InternalFailure>();
654 }
655 }
656}
657
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600658bool Certificate::compareKeys(const std::string& filePath)
659{
660 log<level::INFO>("Certificate compareKeys",
661 entry("FILEPATH=%s", filePath.c_str()));
662 X509_Ptr cert(X509_new(), ::X509_free);
663 if (!cert)
664 {
665 log<level::ERR>("Error occured during X509_new call",
666 entry("FILE=%s", filePath.c_str()),
667 entry("ERRCODE=%lu", ERR_get_error()));
668 elog<InternalFailure>();
669 }
670
671 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
672 if (!bioCert)
673 {
674 log<level::ERR>("Error occured during BIO_new_file call",
675 entry("FILE=%s", filePath.c_str()));
676 elog<InternalFailure>();
677 }
678
679 X509* x509 = cert.get();
680 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
681
682 EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
683 if (!pubKey)
684 {
685 log<level::ERR>("Error occurred during X509_get_pubkey",
686 entry("FILE=%s", filePath.c_str()),
687 entry("ERRCODE=%lu", ERR_get_error()));
688 elog<InvalidCertificate>(Reason("Failed to get public key info"));
689 }
690
691 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
692 if (!keyBio)
693 {
694 log<level::ERR>("Error occured during BIO_s_file call",
695 entry("FILE=%s", filePath.c_str()));
696 elog<InternalFailure>();
697 }
698 BIO_read_filename(keyBio.get(), filePath.c_str());
699
700 EVP_PKEY_Ptr priKey(
701 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
702 ::EVP_PKEY_free);
703 if (!priKey)
704 {
705 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
706 entry("FILE=%s", filePath.c_str()),
707 entry("ERRCODE=%lu", ERR_get_error()));
708 elog<InvalidCertificate>(Reason("Failed to get private key info"));
709 }
710
Patrick Williams55ceaa22021-12-14 06:52:26 -0600711#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600712 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
Patrick Williams55ceaa22021-12-14 06:52:26 -0600713#else
714 int32_t rc = EVP_PKEY_eq(priKey.get(), pubKey.get());
715#endif
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600716 if (rc != 1)
717 {
718 log<level::ERR>("Private key is not matching with Certificate",
719 entry("FILE=%s", filePath.c_str()),
720 entry("ERRCODE=%d", rc));
721 return false;
722 }
723 return true;
724}
725
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200726void Certificate::delete_()
727{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100728 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200729}
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600730} // namespace certs
731} // namespace phosphor