blob: 7872d817bc4b2c8ec21f8bfdccb24a465d4dad2b [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) || \
44 (errnum == X509_V_ERR_CERT_UNTRUSTED) || \
45 (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))
46
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060047// Refer to schema 2018.3
48// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
49// supported KeyUsage types in redfish
50// Refer to
51// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
52// key usage bit fields
53std::map<uint8_t, std::string> keyUsageToRfStr = {
54 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
55 {KU_NON_REPUDIATION, "NonRepudiation"},
56 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
57 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
58 {KU_KEY_AGREEMENT, "KeyAgreement"},
59 {KU_KEY_CERT_SIGN, "KeyCertSign"},
60 {KU_CRL_SIGN, "CRLSigning"},
61 {KU_ENCIPHER_ONLY, "EncipherOnly"},
62 {KU_DECIPHER_ONLY, "DecipherOnly"}};
63
64// Refer to schema 2018.3
65// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
66// supported Extended KeyUsage types in redfish
67std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
68 {NID_server_auth, "ServerAuthentication"},
69 {NID_client_auth, "ClientAuthentication"},
70 {NID_email_protect, "EmailProtection"},
71 {NID_OCSP_sign, "OCSPSigning"},
72 {NID_ad_timeStamping, "Timestamping"},
73 {NID_code_sign, "CodeSigning"}};
74
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010075std::string Certificate::generateCertId(const std::string& certPath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +020076{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010077 const X509_Ptr cert = loadCert(certPath);
78 unsigned long subjectNameHash = X509_subject_name_hash(cert.get());
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +010079 unsigned long issuerSerialHash = X509_issuer_and_serial_hash(cert.get());
80 static constexpr auto CERT_ID_LENGTH = 17;
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010081 char idBuff[CERT_ID_LENGTH];
82
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +010083 snprintf(idBuff, CERT_ID_LENGTH, "%08lx%08lx", subjectNameHash,
84 issuerSerialHash);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010085
86 return std::string(idBuff);
87}
88
89std::string
90 Certificate::generateUniqueFilePath(const std::string& directoryPath)
91{
92 char* filePath = tempnam(directoryPath.c_str(), NULL);
93 if (filePath == NULL)
94 {
95 log<level::ERR>(
96 "Error occured while creating random certificate file path",
97 entry("DIR=%s", directoryPath.c_str()));
98 elog<InternalFailure>();
99 }
100 std::string filePathStr(filePath);
101 free(filePath);
102 return filePathStr;
103}
104
105std::string Certificate::generateAuthCertFileX509Path(
106 const std::string& certSrcFilePath, const std::string& certDstDirPath)
107{
108 const X509_Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200109 unsigned long hash = X509_subject_name_hash(cert.get());
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100110 static constexpr auto CERT_HASH_LENGTH = 9;
111 char hashBuf[CERT_HASH_LENGTH];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200112
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100113 snprintf(hashBuf, CERT_HASH_LENGTH, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200114
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100115 const std::string certHash(hashBuf);
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100116 for (int i = 0; i < AUTHORITY_CERTIFICATES_LIMIT; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100117 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100118 const std::string certDstFileX509Path =
119 certDstDirPath + "/" + certHash + "." + std::to_string(i);
120 if (!fs::exists(certDstFileX509Path))
121 {
122 return certDstFileX509Path;
123 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100124 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200125
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100126 log<level::ERR>("Authority certificate x509 file path already used",
127 entry("DIR=%s", certDstDirPath.c_str()));
128 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100129}
130
131std::string
132 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
133{
134 // If there is a certificate file path (which means certificate replacement
135 // is doing) use it (do not create new one)
136 if (!certFilePath.empty())
137 {
138 return certFilePath;
139 }
140 // If source certificate file is located in the certificates directory use
141 // it (do not create new one)
142 else if (fs::path(certSrcFilePath).parent_path().string() ==
143 certInstallPath)
144 {
145 return certSrcFilePath;
146 }
147 // Otherwise generate new file name/path
148 else
149 {
150 return generateUniqueFilePath(certInstallPath);
151 }
152}
153
154std::string
155 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
156{
157 if (certType == phosphor::certs::AUTHORITY)
158 {
159 return generateAuthCertFilePath(certSrcFilePath);
160 }
161 else
162 {
163 return certInstallPath;
164 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200165}
166
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600167Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
168 const CertificateType& type,
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600169 const CertInstallPath& installPath,
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500170 const CertUploadPath& uploadPath,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200171 const CertWatchPtr& certWatchPtr, Manager& parent) :
Marri Devender Raoedd11312019-02-27 08:45:10 -0600172 CertIfaces(bus, objPath.c_str(), true),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100173 bus(bus), objectPath(objPath), certType(type), certInstallPath(installPath),
174 certWatchPtr(certWatchPtr), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600175{
176 auto installHelper = [this](const auto& filePath) {
177 if (!compareKeys(filePath))
178 {
179 elog<InvalidCertificate>(
180 Reason("Private key does not match the Certificate"));
181 };
182 };
183 typeFuncMap[SERVER] = installHelper;
184 typeFuncMap[CLIENT] = installHelper;
Patrick Williams7e2797e2021-12-03 13:37:07 -0600185 typeFuncMap[AUTHORITY] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500186
187 auto appendPrivateKey = [this](const std::string& filePath) {
188 checkAndAppendPrivateKey(filePath);
189 };
190
191 appendKeyMap[SERVER] = appendPrivateKey;
192 appendKeyMap[CLIENT] = appendPrivateKey;
Patrick Williams7e2797e2021-12-03 13:37:07 -0600193 appendKeyMap[AUTHORITY] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500194
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100195 // Generate certificate file path
196 certFilePath = generateCertFilePath(uploadPath);
197
Marri Devender Raocd30c492019-06-12 01:40:17 -0500198 // install the certificate
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100199 install(uploadPath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500200
Marri Devender Raoedd11312019-02-27 08:45:10 -0600201 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600202}
203
204Certificate::~Certificate()
205{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100206 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600207 {
208 log<level::INFO>("Certificate file not found!",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100209 entry("PATH=%s", certFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600210 }
211}
212
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500213void Certificate::replace(const std::string filePath)
214{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100215 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500216}
217
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100218void Certificate::install(const std::string& certSrcFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600219{
220 log<level::INFO>("Certificate install ",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100221 entry("FILEPATH=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600222 auto errCode = X509_V_OK;
223
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500224 // stop watch for user initiated certificate install
225 if (certWatchPtr)
226 {
227 certWatchPtr->stopWatch();
228 }
229
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600230 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100231 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600232 if (!fs::exists(file))
233 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100234 log<level::ERR>("File is Missing",
235 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600236 elog<InternalFailure>();
237 }
238
239 try
240 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100241 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600242 {
243 // file is empty
244 log<level::ERR>("File is empty",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100245 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600246 elog<InvalidCertificate>(Reason("File is empty"));
247 }
248 }
249 catch (const fs::filesystem_error& e)
250 {
251 // Log Error message
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100252 log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600253 elog<InternalFailure>();
254 }
255
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600256 // Create an empty X509_STORE structure for certificate validation.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600257 X509_STORE_Ptr x509Store(X509_STORE_new(), &X509_STORE_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600258 if (!x509Store)
259 {
260 log<level::ERR>("Error occured during X509_STORE_new call");
261 elog<InternalFailure>();
262 }
263
264 OpenSSL_add_all_algorithms();
265
266 // ADD Certificate Lookup method.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600267 // lookup will be cleaned up automatically when the holding Store goes away.
268 auto lookup = X509_STORE_add_lookup(x509Store.get(), X509_LOOKUP_file());
269
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600270 if (!lookup)
271 {
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600272 log<level::ERR>("Error occured during X509_STORE_add_lookup call");
273 elog<InternalFailure>();
274 }
275 // Load Certificate file.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600276 errCode = X509_LOOKUP_load_file(lookup, certSrcFilePath.c_str(),
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600277 X509_FILETYPE_PEM);
278 if (errCode != 1)
279 {
280 log<level::ERR>("Error occured during X509_LOOKUP_load_file call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100281 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600282 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
283 }
284
285 // Load Certificate file into the X509 structre.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100286 X509_Ptr cert = loadCert(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600287 X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
288 if (!storeCtx)
289 {
290 log<level::ERR>("Error occured during X509_STORE_CTX_new call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100291 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600292 elog<InternalFailure>();
293 }
294
Patrick Williams83e6eab2021-12-05 06:32:24 -0600295 errCode =
296 X509_STORE_CTX_init(storeCtx.get(), x509Store.get(), cert.get(), NULL);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600297 if (errCode != 1)
298 {
299 log<level::ERR>("Error occured during X509_STORE_CTX_init call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100300 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600301 elog<InternalFailure>();
302 }
303
304 // Set time to current time.
305 auto locTime = time(nullptr);
306
307 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
308 locTime);
309
310 errCode = X509_verify_cert(storeCtx.get());
311 if (errCode == 1)
312 {
313 errCode = X509_V_OK;
314 }
315 else if (errCode == 0)
316 {
317 errCode = X509_STORE_CTX_get_error(storeCtx.get());
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500318 log<level::INFO>(
319 "Error occured during X509_verify_cert call, checking for known "
320 "error",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100321 entry("FILE=%s", certSrcFilePath.c_str()),
322 entry("ERRCODE=%d", errCode),
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500323 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600324 }
325 else
326 {
327 log<level::ERR>("Error occured during X509_verify_cert call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100328 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600329 elog<InternalFailure>();
330 }
331
332 // Allow certificate upload, for "certificate is not yet valid" and
333 // trust chain related errors.
334 if (!((errCode == X509_V_OK) ||
335 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
336 TRUST_CHAIN_ERR(errCode)))
337 {
338 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
339 {
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500340 log<level::ERR>("Expired certificate ");
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600341 elog<InvalidCertificate>(Reason("Expired Certificate"));
342 }
343 // Loging general error here.
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500344 log<level::ERR>(
345 "Certificate validation failed", entry("ERRCODE=%d", errCode),
346 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600347 elog<InvalidCertificate>(Reason("Certificate validation failed"));
348 }
349
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500350 validateCertificateExpiryDate(cert);
351
Nidhin MS811a29e2021-05-13 12:54:32 +0530352 // Verify that the certificate can be used in a TLS context
353 const SSL_METHOD* method = TLS_method();
354 std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)> ctx(SSL_CTX_new(method),
355 SSL_CTX_free);
356 if (SSL_CTX_use_certificate(ctx.get(), cert.get()) != 1)
357 {
358 log<level::ERR>("Certificate is not usable",
359 entry("ERRCODE=%x", ERR_get_error()));
360 elog<InvalidCertificate>(Reason("Certificate is not usable"));
361 }
362
Marri Devender Raocd30c492019-06-12 01:40:17 -0500363 // Invoke type specific append private key function.
364 auto appendIter = appendKeyMap.find(certType);
365 if (appendIter == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600366 {
367 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
368 elog<InternalFailure>();
369 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100370 appendIter->second(certSrcFilePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500371
372 // Invoke type specific compare keys function.
373 auto compIter = typeFuncMap.find(certType);
374 if (compIter == typeFuncMap.end())
375 {
376 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
377 elog<InternalFailure>();
378 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100379 compIter->second(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200380
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500381 // Copy the certificate to the installation path
382 // During bootup will be parsing existing file so no need to
383 // copy it.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100384 if (certSrcFilePath != certFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600385 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500386 std::ifstream inputCertFileStream;
387 std::ofstream outputCertFileStream;
388 inputCertFileStream.exceptions(std::ifstream::failbit |
389 std::ifstream::badbit |
390 std::ifstream::eofbit);
391 outputCertFileStream.exceptions(std::ofstream::failbit |
392 std::ofstream::badbit |
393 std::ofstream::eofbit);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200394
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500395 try
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600396 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100397 inputCertFileStream.open(certSrcFilePath);
398 outputCertFileStream.open(certFilePath, std::ios::out);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500399 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
400 inputCertFileStream.close();
401 outputCertFileStream.close();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600402 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500403 catch (const std::exception& e)
404 {
405 log<level::ERR>("Failed to copy certificate",
406 entry("ERR=%s", e.what()),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100407 entry("SRC=%s", certSrcFilePath.c_str()),
408 entry("DST=%s", certFilePath.c_str()));
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500409 elog<InternalFailure>();
410 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600411 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500412
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100413 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600414
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100415 // Keep certificate ID
416 certId = generateCertId(certFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200417
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600418 // Parse the certificate file and populate properties
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100419 populateProperties(certFilePath);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500420
421 // restart watch
422 if (certWatchPtr)
423 {
424 certWatchPtr->startWatch();
425 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600426}
427
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500428void Certificate::validateCertificateExpiryDate(const X509_Ptr& cert)
429{
430 int days = 0;
431 int secs = 0;
432
433 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
434 // Set time to 12:00am GMT, Jan 1 1970
435 ASN1_TIME_set_string(epoch.get(), "700101120000Z");
436
437 ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
438 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
439
440 static const int dayToSeconds = 24 * 60 * 60;
441
442 // TODO #issue15 - allow only upto year 2038 which time_t supports for now
443 // time_t is defined as int32 so any expiry date greater than 2038 will
444 // cause the time_t variable overflow resulting in -ve number.
445 if (days > (INT_MAX - secs) / dayToSeconds)
446 {
447 log<level::ERR>("Certificate expiry date is beyond year 2038",
448 entry("DAYS=%d", days));
449 elog<InvalidCertificate>(Reason("Expiry date should be below 2038"));
450 }
451}
452
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600453void Certificate::populateProperties()
454{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200455 populateProperties(certInstallPath);
456}
457
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100458std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200459{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100460 return certId;
461}
462
463bool Certificate::isSame(const std::string& certPath)
464{
465 return getCertId() == generateCertId(certPath);
466}
467
468void Certificate::storageUpdate()
469{
470 if (certType == phosphor::certs::AUTHORITY)
471 {
472 // Create symbolic link in the certificate directory
473 std::string certFileX509Path;
474 try
475 {
476 if (!certFilePath.empty() &&
477 fs::is_regular_file(fs::path(certFilePath)))
478 {
479 certFileX509Path =
480 generateAuthCertFileX509Path(certFilePath, certInstallPath);
481 fs::create_symlink(fs::path(certFilePath),
482 fs::path(certFileX509Path));
483 }
484 }
485 catch (const std::exception& e)
486 {
487 log<level::ERR>("Failed to create symlink for certificate",
488 entry("ERR=%s", e.what()),
489 entry("FILE=%s", certFilePath.c_str()),
490 entry("SYMLINK=%s", certFileX509Path.c_str()));
491 elog<InternalFailure>();
492 }
493 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200494}
495
496void Certificate::populateProperties(const std::string& certPath)
497{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100498 X509_Ptr cert = loadCert(certPath);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600499 // Update properties if no error thrown
500 BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
501 PEM_write_bio_X509(certBio.get(), cert.get());
502 BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
503 BUF_MEM* buf = certBuf.get();
504 BIO_get_mem_ptr(certBio.get(), &buf);
505 std::string certStr(buf->data, buf->length);
506 CertificateIface::certificateString(certStr);
507
508 static const int maxKeySize = 4096;
509 char subBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500510 BIO_MEM_Ptr subBio(BIO_new(BIO_s_mem()), BIO_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600511 // This pointer cannot be freed independantly.
512 X509_NAME* sub = X509_get_subject_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500513 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
514 BIO_read(subBio.get(), subBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600515 CertificateIface::subject(subBuffer);
516
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600517 char issuerBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500518 BIO_MEM_Ptr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
519 // This pointer cannot be freed independantly.
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600520 X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500521 X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
522 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600523 CertificateIface::issuer(issuerBuffer);
524
525 std::vector<std::string> keyUsageList;
526 ASN1_BIT_STRING* usage;
527
528 // Go through each usage in the bit string and convert to
529 // corresponding string value
530 if ((usage = static_cast<ASN1_BIT_STRING*>(
531 X509_get_ext_d2i(cert.get(), NID_key_usage, NULL, NULL))))
532 {
533 for (auto i = 0; i < usage->length; ++i)
534 {
535 for (auto& x : keyUsageToRfStr)
536 {
537 if (x.first & usage->data[i])
538 {
539 keyUsageList.push_back(x.second);
540 break;
541 }
542 }
543 }
544 }
545
546 EXTENDED_KEY_USAGE* extUsage;
547 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
548 X509_get_ext_d2i(cert.get(), NID_ext_key_usage, NULL, NULL))))
549 {
550 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
551 {
552 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
553 sk_ASN1_OBJECT_value(extUsage, i))]);
554 }
555 }
556 CertificateIface::keyUsage(keyUsageList);
557
558 int days = 0;
559 int secs = 0;
560
561 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
562 // Set time to 12:00am GMT, Jan 1 1970
563 ASN1_TIME_set_string(epoch.get(), "700101120000Z");
564
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500565 static const uint32_t dayToSeconds = 24 * 60 * 60;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600566 ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
567 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
568 CertificateIface::validNotAfter((days * dayToSeconds) + secs);
569
570 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
571 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
572 CertificateIface::validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600573}
574
575X509_Ptr Certificate::loadCert(const std::string& filePath)
576{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600577 // Read Certificate file
578 X509_Ptr cert(X509_new(), ::X509_free);
579 if (!cert)
580 {
581 log<level::ERR>("Error occured during X509_new call",
582 entry("FILE=%s", filePath.c_str()),
583 entry("ERRCODE=%lu", ERR_get_error()));
584 elog<InternalFailure>();
585 }
586
587 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
588 if (!bioCert)
589 {
590 log<level::ERR>("Error occured during BIO_new_file call",
591 entry("FILE=%s", filePath.c_str()));
592 elog<InternalFailure>();
593 }
594
595 X509* x509 = cert.get();
596 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
597 {
598 log<level::ERR>("Error occured during PEM_read_bio_X509 call",
599 entry("FILE=%s", filePath.c_str()));
600 elog<InternalFailure>();
601 }
602 return cert;
603}
Marri Devender Raocd30c492019-06-12 01:40:17 -0500604
605void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
606{
607 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
608 if (!keyBio)
609 {
610 log<level::ERR>("Error occured during BIO_s_file call",
611 entry("FILE=%s", filePath.c_str()));
612 elog<InternalFailure>();
613 }
614 BIO_read_filename(keyBio.get(), filePath.c_str());
615
616 EVP_PKEY_Ptr priKey(
617 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
618 ::EVP_PKEY_free);
619 if (!priKey)
620 {
621 log<level::INFO>("Private key not present in file",
622 entry("FILE=%s", filePath.c_str()));
623 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
624 privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
625 if (!fs::exists(privateKeyFile))
626 {
627 log<level::ERR>("Private key file is not found",
628 entry("FILE=%s", privateKeyFile.c_str()));
629 elog<InternalFailure>();
630 }
631
632 std::ifstream privKeyFileStream;
633 std::ofstream certFileStream;
634 privKeyFileStream.exceptions(std::ifstream::failbit |
635 std::ifstream::badbit |
636 std::ifstream::eofbit);
637 certFileStream.exceptions(std::ofstream::failbit |
638 std::ofstream::badbit |
639 std::ofstream::eofbit);
640 try
641 {
642 privKeyFileStream.open(privateKeyFile);
643 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500644 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500645 certFileStream << privKeyFileStream.rdbuf() << std::flush;
646 privKeyFileStream.close();
647 certFileStream.close();
648 }
649 catch (const std::exception& e)
650 {
651 log<level::ERR>("Failed to append private key",
652 entry("ERR=%s", e.what()),
653 entry("SRC=%s", privateKeyFile.c_str()),
654 entry("DST=%s", filePath.c_str()));
655 elog<InternalFailure>();
656 }
657 }
658}
659
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600660bool Certificate::compareKeys(const std::string& filePath)
661{
662 log<level::INFO>("Certificate compareKeys",
663 entry("FILEPATH=%s", filePath.c_str()));
664 X509_Ptr cert(X509_new(), ::X509_free);
665 if (!cert)
666 {
667 log<level::ERR>("Error occured during X509_new call",
668 entry("FILE=%s", filePath.c_str()),
669 entry("ERRCODE=%lu", ERR_get_error()));
670 elog<InternalFailure>();
671 }
672
673 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
674 if (!bioCert)
675 {
676 log<level::ERR>("Error occured during BIO_new_file call",
677 entry("FILE=%s", filePath.c_str()));
678 elog<InternalFailure>();
679 }
680
681 X509* x509 = cert.get();
682 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
683
684 EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
685 if (!pubKey)
686 {
687 log<level::ERR>("Error occurred during X509_get_pubkey",
688 entry("FILE=%s", filePath.c_str()),
689 entry("ERRCODE=%lu", ERR_get_error()));
690 elog<InvalidCertificate>(Reason("Failed to get public key info"));
691 }
692
693 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
694 if (!keyBio)
695 {
696 log<level::ERR>("Error occured during BIO_s_file call",
697 entry("FILE=%s", filePath.c_str()));
698 elog<InternalFailure>();
699 }
700 BIO_read_filename(keyBio.get(), filePath.c_str());
701
702 EVP_PKEY_Ptr priKey(
703 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
704 ::EVP_PKEY_free);
705 if (!priKey)
706 {
707 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
708 entry("FILE=%s", filePath.c_str()),
709 entry("ERRCODE=%lu", ERR_get_error()));
710 elog<InvalidCertificate>(Reason("Failed to get private key info"));
711 }
712
713 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
714 if (rc != 1)
715 {
716 log<level::ERR>("Private key is not matching with Certificate",
717 entry("FILE=%s", filePath.c_str()),
718 entry("ERRCODE=%d", rc));
719 return false;
720 }
721 return true;
722}
723
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200724void Certificate::delete_()
725{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100726 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200727}
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600728} // namespace certs
729} // namespace phosphor