blob: de7a455d4524fdca4a0c2118e4539227b17a5461 [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
Nan Zhoue1289ad2021-12-28 11:02:56 -080020namespace phosphor::certs
Marri Devender Rao6ceec402019-02-01 03:15:19 -060021{
22// RAII support for openSSL functions.
23using BIO_MEM_Ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
Patrick Williams83e6eab2021-12-05 06:32:24 -060024using X509_STORE_Ptr =
25 std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060026using X509_STORE_CTX_Ptr =
27 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060028using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060029using EVP_PKEY_Ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
30using BUF_MEM_Ptr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
31using InternalFailure =
32 sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
33using InvalidCertificate =
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050034 sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
35using Reason = xyz::openbmc_project::Certs::InvalidCertificate::REASON;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060036
37// Trust chain related errors.`
38#define TRUST_CHAIN_ERR(errnum) \
39 ((errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || \
40 (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) || \
41 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) || \
Nan Zhou63540132021-11-25 15:47:52 -080042 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) || \
Marri Devender Rao6ceec402019-02-01 03:15:19 -060043 (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());
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +010078 unsigned long issuerSerialHash = X509_issuer_and_serial_hash(cert.get());
79 static constexpr auto CERT_ID_LENGTH = 17;
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010080 char idBuff[CERT_ID_LENGTH];
81
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +010082 snprintf(idBuff, CERT_ID_LENGTH, "%08lx%08lx", subjectNameHash,
83 issuerSerialHash);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010084
85 return std::string(idBuff);
86}
87
88std::string
89 Certificate::generateUniqueFilePath(const std::string& directoryPath)
90{
Nan Zhoucfb58022021-12-28 11:02:26 -080091 char* filePath = tempnam(directoryPath.c_str(), nullptr);
92 if (filePath == nullptr)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010093 {
94 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -080095 "Error occurred while creating random certificate file path",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +010096 entry("DIR=%s", directoryPath.c_str()));
97 elog<InternalFailure>();
98 }
99 std::string filePathStr(filePath);
100 free(filePath);
101 return filePathStr;
102}
103
104std::string Certificate::generateAuthCertFileX509Path(
105 const std::string& certSrcFilePath, const std::string& certDstDirPath)
106{
107 const X509_Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200108 unsigned long hash = X509_subject_name_hash(cert.get());
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100109 static constexpr auto CERT_HASH_LENGTH = 9;
110 char hashBuf[CERT_HASH_LENGTH];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200111
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100112 snprintf(hashBuf, CERT_HASH_LENGTH, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200113
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100114 const std::string certHash(hashBuf);
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100115 for (int i = 0; i < AUTHORITY_CERTIFICATES_LIMIT; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100116 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100117 const std::string certDstFileX509Path =
118 certDstDirPath + "/" + certHash + "." + std::to_string(i);
119 if (!fs::exists(certDstFileX509Path))
120 {
121 return certDstFileX509Path;
122 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100123 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200124
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100125 log<level::ERR>("Authority certificate x509 file path already used",
126 entry("DIR=%s", certDstDirPath.c_str()));
127 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100128}
129
130std::string
131 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
132{
133 // If there is a certificate file path (which means certificate replacement
134 // is doing) use it (do not create new one)
135 if (!certFilePath.empty())
136 {
137 return certFilePath;
138 }
139 // If source certificate file is located in the certificates directory use
140 // it (do not create new one)
141 else if (fs::path(certSrcFilePath).parent_path().string() ==
142 certInstallPath)
143 {
144 return certSrcFilePath;
145 }
146 // Otherwise generate new file name/path
147 else
148 {
149 return generateUniqueFilePath(certInstallPath);
150 }
151}
152
153std::string
154 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
155{
156 if (certType == phosphor::certs::AUTHORITY)
157 {
158 return generateAuthCertFilePath(certSrcFilePath);
159 }
160 else
161 {
162 return certInstallPath;
163 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200164}
165
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600166Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
167 const CertificateType& type,
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600168 const CertInstallPath& installPath,
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500169 const CertUploadPath& uploadPath,
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200170 const CertWatchPtr& certWatchPtr, Manager& parent) :
Marri Devender Raoedd11312019-02-27 08:45:10 -0600171 CertIfaces(bus, objPath.c_str(), true),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100172 bus(bus), objectPath(objPath), certType(type), certInstallPath(installPath),
173 certWatchPtr(certWatchPtr), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600174{
175 auto installHelper = [this](const auto& filePath) {
176 if (!compareKeys(filePath))
177 {
178 elog<InvalidCertificate>(
179 Reason("Private key does not match the Certificate"));
180 };
181 };
182 typeFuncMap[SERVER] = installHelper;
183 typeFuncMap[CLIENT] = installHelper;
Patrick Williams7e2797e2021-12-03 13:37:07 -0600184 typeFuncMap[AUTHORITY] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500185
186 auto appendPrivateKey = [this](const std::string& filePath) {
187 checkAndAppendPrivateKey(filePath);
188 };
189
190 appendKeyMap[SERVER] = appendPrivateKey;
191 appendKeyMap[CLIENT] = appendPrivateKey;
Patrick Williams7e2797e2021-12-03 13:37:07 -0600192 appendKeyMap[AUTHORITY] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500193
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100194 // Generate certificate file path
195 certFilePath = generateCertFilePath(uploadPath);
196
Marri Devender Raocd30c492019-06-12 01:40:17 -0500197 // install the certificate
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100198 install(uploadPath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500199
Marri Devender Raoedd11312019-02-27 08:45:10 -0600200 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600201}
202
203Certificate::~Certificate()
204{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100205 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600206 {
207 log<level::INFO>("Certificate file not found!",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100208 entry("PATH=%s", certFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600209 }
210}
211
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500212void Certificate::replace(const std::string filePath)
213{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100214 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500215}
216
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100217void Certificate::install(const std::string& certSrcFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600218{
219 log<level::INFO>("Certificate install ",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100220 entry("FILEPATH=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600221 auto errCode = X509_V_OK;
222
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500223 // stop watch for user initiated certificate install
224 if (certWatchPtr)
225 {
226 certWatchPtr->stopWatch();
227 }
228
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600229 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100230 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600231 if (!fs::exists(file))
232 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100233 log<level::ERR>("File is Missing",
234 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600235 elog<InternalFailure>();
236 }
237
238 try
239 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100240 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600241 {
242 // file is empty
243 log<level::ERR>("File is empty",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100244 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600245 elog<InvalidCertificate>(Reason("File is empty"));
246 }
247 }
248 catch (const fs::filesystem_error& e)
249 {
250 // Log Error message
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100251 log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600252 elog<InternalFailure>();
253 }
254
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600255 // Create an empty X509_STORE structure for certificate validation.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600256 X509_STORE_Ptr x509Store(X509_STORE_new(), &X509_STORE_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600257 if (!x509Store)
258 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800259 log<level::ERR>("Error occurred during X509_STORE_new call");
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600260 elog<InternalFailure>();
261 }
262
263 OpenSSL_add_all_algorithms();
264
265 // ADD Certificate Lookup method.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600266 // lookup will be cleaned up automatically when the holding Store goes away.
267 auto lookup = X509_STORE_add_lookup(x509Store.get(), X509_LOOKUP_file());
268
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600269 if (!lookup)
270 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800271 log<level::ERR>("Error occurred during X509_STORE_add_lookup call");
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600272 elog<InternalFailure>();
273 }
274 // Load Certificate file.
Patrick Williams83e6eab2021-12-05 06:32:24 -0600275 errCode = X509_LOOKUP_load_file(lookup, certSrcFilePath.c_str(),
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600276 X509_FILETYPE_PEM);
277 if (errCode != 1)
278 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800279 log<level::ERR>("Error occurred during X509_LOOKUP_load_file call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100280 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600281 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
282 }
283
Nan Zhoubf3cf752021-12-28 11:02:07 -0800284 // Load Certificate file into the X509 structure.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100285 X509_Ptr cert = loadCert(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600286 X509_STORE_CTX_Ptr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
287 if (!storeCtx)
288 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800289 log<level::ERR>("Error occurred during X509_STORE_CTX_new call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100290 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600291 elog<InternalFailure>();
292 }
293
Nan Zhoucfb58022021-12-28 11:02:26 -0800294 errCode = X509_STORE_CTX_init(storeCtx.get(), x509Store.get(), cert.get(),
295 nullptr);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600296 if (errCode != 1)
297 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800298 log<level::ERR>("Error occurred during X509_STORE_CTX_init call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100299 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600300 elog<InternalFailure>();
301 }
302
303 // Set time to current time.
304 auto locTime = time(nullptr);
305
306 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
307 locTime);
308
309 errCode = X509_verify_cert(storeCtx.get());
310 if (errCode == 1)
311 {
312 errCode = X509_V_OK;
313 }
314 else if (errCode == 0)
315 {
316 errCode = X509_STORE_CTX_get_error(storeCtx.get());
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500317 log<level::INFO>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800318 "Error occurred during X509_verify_cert call, checking for known "
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500319 "error",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100320 entry("FILE=%s", certSrcFilePath.c_str()),
321 entry("ERRCODE=%d", errCode),
Marri Devender Rao2e8c3a52019-08-09 01:26:35 -0500322 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600323 }
324 else
325 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800326 log<level::ERR>("Error occurred during X509_verify_cert call",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100327 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600328 elog<InternalFailure>();
329 }
330
331 // Allow certificate upload, for "certificate is not yet valid" and
332 // trust chain related errors.
333 if (!((errCode == X509_V_OK) ||
334 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
335 TRUST_CHAIN_ERR(errCode)))
336 {
337 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
338 {
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500339 log<level::ERR>("Expired certificate ");
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600340 elog<InvalidCertificate>(Reason("Expired Certificate"));
341 }
Nan Zhoubf3cf752021-12-28 11:02:07 -0800342 // Logging general error here.
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500343 log<level::ERR>(
344 "Certificate validation failed", entry("ERRCODE=%d", errCode),
345 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600346 elog<InvalidCertificate>(Reason("Certificate validation failed"));
347 }
348
Nan Zhoucf811c42021-12-02 14:56:17 -0800349 validateCertificateStartDate(cert);
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500350
Nidhin MS811a29e2021-05-13 12:54:32 +0530351 // Verify that the certificate can be used in a TLS context
352 const SSL_METHOD* method = TLS_method();
353 std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)> ctx(SSL_CTX_new(method),
354 SSL_CTX_free);
355 if (SSL_CTX_use_certificate(ctx.get(), cert.get()) != 1)
356 {
357 log<level::ERR>("Certificate is not usable",
358 entry("ERRCODE=%x", ERR_get_error()));
359 elog<InvalidCertificate>(Reason("Certificate is not usable"));
360 }
361
Marri Devender Raocd30c492019-06-12 01:40:17 -0500362 // Invoke type specific append private key function.
363 auto appendIter = appendKeyMap.find(certType);
364 if (appendIter == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600365 {
366 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
367 elog<InternalFailure>();
368 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100369 appendIter->second(certSrcFilePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500370
371 // Invoke type specific compare keys function.
372 auto compIter = typeFuncMap.find(certType);
373 if (compIter == typeFuncMap.end())
374 {
375 log<level::ERR>("Unsupported Type", entry("TYPE=%s", certType.c_str()));
376 elog<InternalFailure>();
377 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100378 compIter->second(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200379
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500380 // Copy the certificate to the installation path
381 // During bootup will be parsing existing file so no need to
382 // copy it.
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100383 if (certSrcFilePath != certFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600384 {
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500385 std::ifstream inputCertFileStream;
386 std::ofstream outputCertFileStream;
387 inputCertFileStream.exceptions(std::ifstream::failbit |
388 std::ifstream::badbit |
389 std::ifstream::eofbit);
390 outputCertFileStream.exceptions(std::ofstream::failbit |
391 std::ofstream::badbit |
392 std::ofstream::eofbit);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200393
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500394 try
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600395 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100396 inputCertFileStream.open(certSrcFilePath);
397 outputCertFileStream.open(certFilePath, std::ios::out);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500398 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
399 inputCertFileStream.close();
400 outputCertFileStream.close();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600401 }
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500402 catch (const std::exception& e)
403 {
404 log<level::ERR>("Failed to copy certificate",
405 entry("ERR=%s", e.what()),
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100406 entry("SRC=%s", certSrcFilePath.c_str()),
407 entry("DST=%s", certFilePath.c_str()));
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500408 elog<InternalFailure>();
409 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600410 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500411
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100412 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600413
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100414 // Keep certificate ID
415 certId = generateCertId(certFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200416
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600417 // Parse the certificate file and populate properties
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100418 populateProperties(certFilePath);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500419
420 // restart watch
421 if (certWatchPtr)
422 {
423 certWatchPtr->startWatch();
424 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600425}
426
Nan Zhoucf811c42021-12-02 14:56:17 -0800427// Checks that notBefore is not earlier than the unix epoch given that
428// the corresponding DBus interface is uint64_t.
429void Certificate::validateCertificateStartDate(const X509_Ptr& cert)
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500430{
431 int days = 0;
432 int secs = 0;
433
434 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800435 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
436 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500437
Nan Zhoucf811c42021-12-02 14:56:17 -0800438 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
439 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500440
Nan Zhoucf811c42021-12-02 14:56:17 -0800441 if (days < 0 || secs < 0)
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500442 {
Nan Zhoucf811c42021-12-02 14:56:17 -0800443 log<level::ERR>("Certificate valid date starts before the Unix Epoch");
444 elog<InvalidCertificate>(
445 Reason("NotBefore should after 19700101000000Z"));
Marri Devender Raoc4522d22020-03-12 06:50:17 -0500446 }
447}
448
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600449void Certificate::populateProperties()
450{
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200451 populateProperties(certInstallPath);
452}
453
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100454std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200455{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100456 return certId;
457}
458
459bool Certificate::isSame(const std::string& certPath)
460{
461 return getCertId() == generateCertId(certPath);
462}
463
464void Certificate::storageUpdate()
465{
466 if (certType == phosphor::certs::AUTHORITY)
467 {
468 // Create symbolic link in the certificate directory
469 std::string certFileX509Path;
470 try
471 {
472 if (!certFilePath.empty() &&
473 fs::is_regular_file(fs::path(certFilePath)))
474 {
475 certFileX509Path =
476 generateAuthCertFileX509Path(certFilePath, certInstallPath);
477 fs::create_symlink(fs::path(certFilePath),
478 fs::path(certFileX509Path));
479 }
480 }
481 catch (const std::exception& e)
482 {
483 log<level::ERR>("Failed to create symlink for certificate",
484 entry("ERR=%s", e.what()),
485 entry("FILE=%s", certFilePath.c_str()),
486 entry("SYMLINK=%s", certFileX509Path.c_str()));
487 elog<InternalFailure>();
488 }
489 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200490}
491
492void Certificate::populateProperties(const std::string& certPath)
493{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100494 X509_Ptr cert = loadCert(certPath);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600495 // Update properties if no error thrown
496 BIO_MEM_Ptr certBio(BIO_new(BIO_s_mem()), BIO_free);
497 PEM_write_bio_X509(certBio.get(), cert.get());
498 BUF_MEM_Ptr certBuf(BUF_MEM_new(), BUF_MEM_free);
499 BUF_MEM* buf = certBuf.get();
500 BIO_get_mem_ptr(certBio.get(), &buf);
501 std::string certStr(buf->data, buf->length);
502 CertificateIface::certificateString(certStr);
503
504 static const int maxKeySize = 4096;
505 char subBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500506 BIO_MEM_Ptr subBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800507 // This pointer cannot be freed independently.
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600508 X509_NAME* sub = X509_get_subject_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500509 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
510 BIO_read(subBio.get(), subBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600511 CertificateIface::subject(subBuffer);
512
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600513 char issuerBuffer[maxKeySize] = {0};
Marri Devender Raodec58772019-06-11 03:10:00 -0500514 BIO_MEM_Ptr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoubf3cf752021-12-28 11:02:07 -0800515 // This pointer cannot be freed independently.
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600516 X509_NAME* issuer_name = X509_get_issuer_name(cert.get());
Marri Devender Raodec58772019-06-11 03:10:00 -0500517 X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
518 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600519 CertificateIface::issuer(issuerBuffer);
520
521 std::vector<std::string> keyUsageList;
522 ASN1_BIT_STRING* usage;
523
524 // Go through each usage in the bit string and convert to
525 // corresponding string value
526 if ((usage = static_cast<ASN1_BIT_STRING*>(
Nan Zhoucfb58022021-12-28 11:02:26 -0800527 X509_get_ext_d2i(cert.get(), NID_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600528 {
529 for (auto i = 0; i < usage->length; ++i)
530 {
531 for (auto& x : keyUsageToRfStr)
532 {
533 if (x.first & usage->data[i])
534 {
535 keyUsageList.push_back(x.second);
536 break;
537 }
538 }
539 }
540 }
541
542 EXTENDED_KEY_USAGE* extUsage;
Nan Zhoucfb58022021-12-28 11:02:26 -0800543 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(X509_get_ext_d2i(
544 cert.get(), NID_ext_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600545 {
546 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
547 {
548 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
549 sk_ASN1_OBJECT_value(extUsage, i))]);
550 }
551 }
552 CertificateIface::keyUsage(keyUsageList);
553
554 int days = 0;
555 int secs = 0;
556
557 ASN1_TIME_ptr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800558 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
559 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600560
Nan Zhoucf811c42021-12-02 14:56:17 -0800561 static const uint64_t dayToSeconds = 24 * 60 * 60;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600562 ASN1_TIME* notAfter = X509_get_notAfter(cert.get());
563 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
564 CertificateIface::validNotAfter((days * dayToSeconds) + secs);
565
566 ASN1_TIME* notBefore = X509_get_notBefore(cert.get());
567 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
568 CertificateIface::validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600569}
570
571X509_Ptr Certificate::loadCert(const std::string& filePath)
572{
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600573 // Read Certificate file
574 X509_Ptr cert(X509_new(), ::X509_free);
575 if (!cert)
576 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800577 log<level::ERR>("Error occurred during X509_new call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600578 entry("FILE=%s", filePath.c_str()),
579 entry("ERRCODE=%lu", ERR_get_error()));
580 elog<InternalFailure>();
581 }
582
583 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
584 if (!bioCert)
585 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800586 log<level::ERR>("Error occurred during BIO_new_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600587 entry("FILE=%s", filePath.c_str()));
588 elog<InternalFailure>();
589 }
590
591 X509* x509 = cert.get();
592 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
593 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800594 log<level::ERR>("Error occurred during PEM_read_bio_X509 call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600595 entry("FILE=%s", filePath.c_str()));
596 elog<InternalFailure>();
597 }
598 return cert;
599}
Marri Devender Raocd30c492019-06-12 01:40:17 -0500600
601void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
602{
603 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
604 if (!keyBio)
605 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800606 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Raocd30c492019-06-12 01:40:17 -0500607 entry("FILE=%s", filePath.c_str()));
608 elog<InternalFailure>();
609 }
610 BIO_read_filename(keyBio.get(), filePath.c_str());
611
612 EVP_PKEY_Ptr priKey(
613 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
614 ::EVP_PKEY_free);
615 if (!priKey)
616 {
617 log<level::INFO>("Private key not present in file",
618 entry("FILE=%s", filePath.c_str()));
619 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
620 privateKeyFile = privateKeyFile / PRIV_KEY_FILE_NAME;
621 if (!fs::exists(privateKeyFile))
622 {
623 log<level::ERR>("Private key file is not found",
624 entry("FILE=%s", privateKeyFile.c_str()));
625 elog<InternalFailure>();
626 }
627
628 std::ifstream privKeyFileStream;
629 std::ofstream certFileStream;
630 privKeyFileStream.exceptions(std::ifstream::failbit |
631 std::ifstream::badbit |
632 std::ifstream::eofbit);
633 certFileStream.exceptions(std::ofstream::failbit |
634 std::ofstream::badbit |
635 std::ofstream::eofbit);
636 try
637 {
638 privKeyFileStream.open(privateKeyFile);
639 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500640 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500641 certFileStream << privKeyFileStream.rdbuf() << std::flush;
642 privKeyFileStream.close();
643 certFileStream.close();
644 }
645 catch (const std::exception& e)
646 {
647 log<level::ERR>("Failed to append private key",
648 entry("ERR=%s", e.what()),
649 entry("SRC=%s", privateKeyFile.c_str()),
650 entry("DST=%s", filePath.c_str()));
651 elog<InternalFailure>();
652 }
653 }
654}
655
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600656bool Certificate::compareKeys(const std::string& filePath)
657{
658 log<level::INFO>("Certificate compareKeys",
659 entry("FILEPATH=%s", filePath.c_str()));
660 X509_Ptr cert(X509_new(), ::X509_free);
661 if (!cert)
662 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800663 log<level::ERR>("Error occurred during X509_new call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600664 entry("FILE=%s", filePath.c_str()),
665 entry("ERRCODE=%lu", ERR_get_error()));
666 elog<InternalFailure>();
667 }
668
669 BIO_MEM_Ptr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
670 if (!bioCert)
671 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800672 log<level::ERR>("Error occurred during BIO_new_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600673 entry("FILE=%s", filePath.c_str()));
674 elog<InternalFailure>();
675 }
676
677 X509* x509 = cert.get();
678 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
679
680 EVP_PKEY_Ptr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
681 if (!pubKey)
682 {
683 log<level::ERR>("Error occurred during X509_get_pubkey",
684 entry("FILE=%s", filePath.c_str()),
685 entry("ERRCODE=%lu", ERR_get_error()));
686 elog<InvalidCertificate>(Reason("Failed to get public key info"));
687 }
688
689 BIO_MEM_Ptr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
690 if (!keyBio)
691 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800692 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600693 entry("FILE=%s", filePath.c_str()));
694 elog<InternalFailure>();
695 }
696 BIO_read_filename(keyBio.get(), filePath.c_str());
697
698 EVP_PKEY_Ptr priKey(
699 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
700 ::EVP_PKEY_free);
701 if (!priKey)
702 {
703 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
704 entry("FILE=%s", filePath.c_str()),
705 entry("ERRCODE=%lu", ERR_get_error()));
706 elog<InvalidCertificate>(Reason("Failed to get private key info"));
707 }
708
Patrick Williams55ceaa22021-12-14 06:52:26 -0600709#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600710 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
Patrick Williams55ceaa22021-12-14 06:52:26 -0600711#else
712 int32_t rc = EVP_PKEY_eq(priKey.get(), pubKey.get());
713#endif
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600714 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}
Nan Zhoue1289ad2021-12-28 11:02:56 -0800728} // namespace phosphor::certs