blob: 52c3c2a081ef8a402f5cb8196e62c512e4837f17 [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"
Nan Zhoue869bb62021-12-30 11:34:42 -08006#include "x509_utils.hpp"
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +02007
Nan Zhou014be0b2021-12-28 18:00:14 -08008#include <openssl/asn1.h>
Marri Devender Rao6ceec402019-02-01 03:15:19 -06009#include <openssl/bio.h>
Nan Zhou014be0b2021-12-28 18:00:14 -080010#include <openssl/buffer.h>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060011#include <openssl/err.h>
12#include <openssl/evp.h>
Nan Zhou014be0b2021-12-28 18:00:14 -080013#include <openssl/obj_mac.h>
14#include <openssl/objects.h>
15#include <openssl/opensslv.h>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060016#include <openssl/pem.h>
17#include <openssl/x509v3.h>
18
Patrick Williams223e4602023-05-10 07:51:11 -050019#include <phosphor-logging/elog-errors.hpp>
20#include <phosphor-logging/elog.hpp>
21#include <phosphor-logging/log.hpp>
22#include <watch.hpp>
23#include <xyz/openbmc_project/Certs/error.hpp>
24#include <xyz/openbmc_project/Common/error.hpp>
25
Nan Zhou014be0b2021-12-28 18:00:14 -080026#include <cstdint>
27#include <cstdio>
28#include <cstdlib>
Nan Zhou014be0b2021-12-28 18:00:14 -080029#include <exception>
30#include <filesystem>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060031#include <fstream>
Nan Zhou014be0b2021-12-28 18:00:14 -080032#include <map>
Nan Zhou014be0b2021-12-28 18:00:14 -080033#include <utility>
34#include <vector>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050035
Nan Zhoue1289ad2021-12-28 11:02:56 -080036namespace phosphor::certs
Marri Devender Rao6ceec402019-02-01 03:15:19 -060037{
Nan Zhoucf06ccd2021-12-28 16:25:45 -080038
39namespace
40{
41namespace fs = std::filesystem;
42using ::phosphor::logging::elog;
43using ::phosphor::logging::entry;
44using ::phosphor::logging::level;
45using ::phosphor::logging::log;
46using ::phosphor::logging::report;
47using InvalidCertificateError =
48 ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
49using ::phosphor::logging::xyz::openbmc_project::Certs::InvalidCertificate;
50using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
51
Marri Devender Rao6ceec402019-02-01 03:15:19 -060052// RAII support for openSSL functions.
Nan Zhoucf06ccd2021-12-28 16:25:45 -080053using BIOMemPtr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
54using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080055using ASN1TimePtr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
56using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
57using BufMemPtr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060058
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060059// Refer to schema 2018.3
60// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
61// supported KeyUsage types in redfish
62// Refer to
63// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
64// key usage bit fields
65std::map<uint8_t, std::string> keyUsageToRfStr = {
66 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
67 {KU_NON_REPUDIATION, "NonRepudiation"},
68 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
69 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
70 {KU_KEY_AGREEMENT, "KeyAgreement"},
71 {KU_KEY_CERT_SIGN, "KeyCertSign"},
72 {KU_CRL_SIGN, "CRLSigning"},
73 {KU_ENCIPHER_ONLY, "EncipherOnly"},
74 {KU_DECIPHER_ONLY, "DecipherOnly"}};
75
76// Refer to schema 2018.3
77// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
78// supported Extended KeyUsage types in redfish
79std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
80 {NID_server_auth, "ServerAuthentication"},
81 {NID_client_auth, "ClientAuthentication"},
82 {NID_email_protect, "EmailProtection"},
83 {NID_OCSP_sign, "OCSPSigning"},
84 {NID_ad_timeStamping, "Timestamping"},
85 {NID_code_sign, "CodeSigning"}};
86
Nan Zhoue869bb62021-12-30 11:34:42 -080087/**
Nan Zhou6ec13c82021-12-30 11:34:50 -080088 * @brief Dumps the PEM encoded certificate to installFilePath
Nan Zhoue869bb62021-12-30 11:34:42 -080089 *
Nan Zhou6ec13c82021-12-30 11:34:50 -080090 * @param[in] pem - PEM encoded X509 certificate buffer.
91 * @param[in] certFilePath - Path to the destination file.
Nan Zhoue869bb62021-12-30 11:34:42 -080092 *
93 * @return void
94 */
Nan Zhou6ec13c82021-12-30 11:34:50 -080095
96void dumpCertificate(const std::string& pem, const std::string& certFilePath)
97{
98 std::ofstream outputCertFileStream;
99
100 outputCertFileStream.exceptions(
101 std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
102
103 try
104 {
105 outputCertFileStream.open(certFilePath, std::ios::out);
106 outputCertFileStream << pem << "\n" << std::flush;
107 outputCertFileStream.close();
108 }
109 catch (const std::exception& e)
110 {
111 log<level::ERR>("Failed to dump certificate", entry("ERR=%s", e.what()),
112 entry("SRC_PEM=%s", pem.c_str()),
113 entry("DST=%s", certFilePath.c_str()));
114 elog<InternalFailure>();
115 }
116}
117} // namespace
118
119void Certificate::copyCertificate(const std::string& certSrcFilePath,
120 const std::string& certFilePath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200121{
Nan Zhoue869bb62021-12-30 11:34:42 -0800122 // Copy the certificate to the installation path
123 // During bootup will be parsing existing file so no need to
124 // copy it.
125 if (certSrcFilePath != certFilePath)
126 {
127 std::ifstream inputCertFileStream;
128 std::ofstream outputCertFileStream;
129 inputCertFileStream.exceptions(std::ifstream::failbit |
130 std::ifstream::badbit |
131 std::ifstream::eofbit);
132 outputCertFileStream.exceptions(std::ofstream::failbit |
133 std::ofstream::badbit |
134 std::ofstream::eofbit);
135 try
136 {
137 inputCertFileStream.open(certSrcFilePath);
138 outputCertFileStream.open(certFilePath, std::ios::out);
139 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
140 inputCertFileStream.close();
141 outputCertFileStream.close();
142 }
143 catch (const std::exception& e)
144 {
145 log<level::ERR>("Failed to copy certificate",
146 entry("ERR=%s", e.what()),
147 entry("SRC=%s", certSrcFilePath.c_str()),
148 entry("DST=%s", certFilePath.c_str()));
149 elog<InternalFailure>();
150 }
151 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100152}
153
154std::string
155 Certificate::generateUniqueFilePath(const std::string& directoryPath)
156{
Nan Zhoucfb58022021-12-28 11:02:26 -0800157 char* filePath = tempnam(directoryPath.c_str(), nullptr);
158 if (filePath == nullptr)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100159 {
160 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800161 "Error occurred while creating random certificate file path",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100162 entry("DIR=%s", directoryPath.c_str()));
163 elog<InternalFailure>();
164 }
165 std::string filePathStr(filePath);
166 free(filePath);
167 return filePathStr;
168}
169
170std::string Certificate::generateAuthCertFileX509Path(
171 const std::string& certSrcFilePath, const std::string& certDstDirPath)
172{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800173 const internal::X509Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200174 unsigned long hash = X509_subject_name_hash(cert.get());
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000175 static constexpr auto certHashLength = 9;
176 char hashBuf[certHashLength];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200177
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000178 snprintf(hashBuf, certHashLength, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200179
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100180 const std::string certHash(hashBuf);
Nan Zhou718eef32021-12-28 11:03:30 -0800181 for (size_t i = 0; i < maxNumAuthorityCertificates; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100182 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100183 const std::string certDstFileX509Path =
184 certDstDirPath + "/" + certHash + "." + std::to_string(i);
185 if (!fs::exists(certDstFileX509Path))
186 {
187 return certDstFileX509Path;
188 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100189 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200190
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100191 log<level::ERR>("Authority certificate x509 file path already used",
192 entry("DIR=%s", certDstDirPath.c_str()));
193 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100194}
195
196std::string
197 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
198{
199 // If there is a certificate file path (which means certificate replacement
200 // is doing) use it (do not create new one)
201 if (!certFilePath.empty())
202 {
203 return certFilePath;
204 }
205 // If source certificate file is located in the certificates directory use
206 // it (do not create new one)
207 else if (fs::path(certSrcFilePath).parent_path().string() ==
208 certInstallPath)
209 {
210 return certSrcFilePath;
211 }
212 // Otherwise generate new file name/path
213 else
214 {
215 return generateUniqueFilePath(certInstallPath);
216 }
217}
218
219std::string
220 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
221{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000222 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100223 {
224 return generateAuthCertFilePath(certSrcFilePath);
225 }
226 else
227 {
228 return certInstallPath;
229 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200230}
231
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500232Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800233 CertificateType type, const std::string& installPath,
234 const std::string& uploadPath, Watch* watch,
Willy Tu698a5742022-09-23 21:33:01 +0000235 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500236 internal::CertificateInterface(
237 bus, objPath.c_str(),
238 internal::CertificateInterface::action::defer_emit),
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800239 objectPath(objPath), certType(type), certInstallPath(installPath),
240 certWatch(watch), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600241{
242 auto installHelper = [this](const auto& filePath) {
243 if (!compareKeys(filePath))
244 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800245 elog<InvalidCertificateError>(InvalidCertificate::REASON(
246 "Private key does not match the Certificate"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600247 };
248 };
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000249 typeFuncMap[CertificateType::server] = installHelper;
250 typeFuncMap[CertificateType::client] = installHelper;
251 typeFuncMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500252
253 auto appendPrivateKey = [this](const std::string& filePath) {
254 checkAndAppendPrivateKey(filePath);
255 };
256
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000257 appendKeyMap[CertificateType::server] = appendPrivateKey;
258 appendKeyMap[CertificateType::client] = appendPrivateKey;
259 appendKeyMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500260
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100261 // Generate certificate file path
262 certFilePath = generateCertFilePath(uploadPath);
263
Marri Devender Raocd30c492019-06-12 01:40:17 -0500264 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000265 install(uploadPath, restore);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500266
Marri Devender Raoedd11312019-02-27 08:45:10 -0600267 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600268}
269
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500270Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhou6ec13c82021-12-30 11:34:50 -0800271 const CertificateType& type,
272 const std::string& installPath, X509_STORE& x509Store,
273 const std::string& pem, Watch* watchPtr,
Willy Tu698a5742022-09-23 21:33:01 +0000274 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500275 internal::CertificateInterface(
276 bus, objPath.c_str(),
277 internal::CertificateInterface::action::defer_emit),
Nan Zhou6ec13c82021-12-30 11:34:50 -0800278 objectPath(objPath), certType(type), certInstallPath(installPath),
279 certWatch(watchPtr), manager(parent)
280{
281 // Generate certificate file path
282 certFilePath = generateUniqueFilePath(installPath);
283
284 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000285 install(x509Store, pem, restore);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800286
287 this->emit_object_added();
288}
289
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600290Certificate::~Certificate()
291{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100292 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600293 {
294 log<level::INFO>("Certificate file not found!",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100295 entry("PATH=%s", certFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600296 }
297}
298
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500299void Certificate::replace(const std::string filePath)
300{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100301 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500302}
303
Willy Tu698a5742022-09-23 21:33:01 +0000304void Certificate::install(const std::string& certSrcFilePath, bool restore)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600305{
Willy Tu698a5742022-09-23 21:33:01 +0000306 if (restore)
307 {
308 log<level::DEBUG>("Certificate install ",
309 entry("FILEPATH=%s", certSrcFilePath.c_str()));
310 }
311 else
312 {
313 log<level::INFO>("Certificate install ",
314 entry("FILEPATH=%s", certSrcFilePath.c_str()));
315 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600316
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500317 // stop watch for user initiated certificate install
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800318 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500319 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800320 certWatch->stopWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500321 }
322
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600323 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100324 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600325 if (!fs::exists(file))
326 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100327 log<level::ERR>("File is Missing",
328 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600329 elog<InternalFailure>();
330 }
331
332 try
333 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100334 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600335 {
336 // file is empty
337 log<level::ERR>("File is empty",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100338 entry("FILE=%s", certSrcFilePath.c_str()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800339 elog<InvalidCertificateError>(
340 InvalidCertificate::REASON("File is empty"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600341 }
342 }
343 catch (const fs::filesystem_error& e)
344 {
345 // Log Error message
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100346 log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600347 elog<InternalFailure>();
348 }
349
Nan Zhoue869bb62021-12-30 11:34:42 -0800350 X509StorePtr x509Store = getX509Store(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600351
Nan Zhoubf3cf752021-12-28 11:02:07 -0800352 // Load Certificate file into the X509 structure.
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800353 internal::X509Ptr cert = loadCert(certSrcFilePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800354
355 // Perform validation
356 validateCertificateAgainstStore(*x509Store, *cert);
357 validateCertificateStartDate(*cert);
358 validateCertificateInSSLContext(*cert);
359
360 // Invoke type specific append private key function.
361 if (auto it = appendKeyMap.find(certType); it == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600362 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800363 log<level::ERR>("Unsupported Type",
364 entry("TYPE=%s", certificateTypeToString(certType)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600365 elog<InternalFailure>();
366 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600367 else
368 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800369 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600370 }
371
Marri Devender Raocd30c492019-06-12 01:40:17 -0500372 // Invoke type specific compare keys function.
Nan Zhoue869bb62021-12-30 11:34:42 -0800373 if (auto it = typeFuncMap.find(certType); it == typeFuncMap.end())
Marri Devender Raocd30c492019-06-12 01:40:17 -0500374 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800375 log<level::ERR>("Unsupported Type",
376 entry("TYPE=%s", certificateTypeToString(certType)));
Marri Devender Raocd30c492019-06-12 01:40:17 -0500377 elog<InternalFailure>();
378 }
Nan Zhoue869bb62021-12-30 11:34:42 -0800379 else
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600380 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800381 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600382 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500383
Nan Zhoue869bb62021-12-30 11:34:42 -0800384 copyCertificate(certSrcFilePath, certFilePath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100385 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600386
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100387 // Keep certificate ID
Nan Zhoue869bb62021-12-30 11:34:42 -0800388 certId = generateCertId(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200389
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600390 // Parse the certificate file and populate properties
Nan Zhoue869bb62021-12-30 11:34:42 -0800391 populateProperties(*cert);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500392
393 // restart watch
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800394 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500395 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800396 certWatch->startWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500397 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600398}
399
Willy Tu698a5742022-09-23 21:33:01 +0000400void Certificate::install(X509_STORE& x509Store, const std::string& pem,
401 bool restore)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800402{
Willy Tu698a5742022-09-23 21:33:01 +0000403 if (restore)
404 {
405 log<level::DEBUG>("Certificate install ",
406 entry("PEM_STR=%s", pem.data()));
407 }
408 else
409 {
410 log<level::INFO>("Certificate install ",
411 entry("PEM_STR=%s", pem.data()));
412 }
Nan Zhou6ec13c82021-12-30 11:34:50 -0800413
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000414 if (certType != CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800415 {
416 log<level::ERR>("Bulk install error: Unsupported Type; only authority "
417 "supports bulk install",
418 entry("TYPE=%s", certificateTypeToString(certType)));
419 elog<InternalFailure>();
420 }
421
422 // stop watch for user initiated certificate install
423 if (certWatch)
424 {
425 certWatch->stopWatch();
426 }
427
428 // Load Certificate file into the X509 structure.
429 internal::X509Ptr cert = parseCert(pem);
430 // Perform validation; no type specific compare keys function
431 validateCertificateAgainstStore(x509Store, *cert);
432 validateCertificateStartDate(*cert);
433 validateCertificateInSSLContext(*cert);
434
435 // Copy the PEM to the installation path
436 dumpCertificate(pem, certFilePath);
437 storageUpdate();
438 // Keep certificate ID
439 certId = generateCertId(*cert);
440 // Parse the certificate file and populate properties
441 populateProperties(*cert);
442 // restart watch
443 if (certWatch)
444 {
445 certWatch->startWatch();
446 }
447}
448
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600449void Certificate::populateProperties()
450{
Nan Zhoue869bb62021-12-30 11:34:42 -0800451 internal::X509Ptr cert = loadCert(certInstallPath);
452 populateProperties(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200453}
454
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100455std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200456{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100457 return certId;
458}
459
460bool Certificate::isSame(const std::string& certPath)
461{
Nan Zhoue869bb62021-12-30 11:34:42 -0800462 internal::X509Ptr cert = loadCert(certPath);
463 return getCertId() == generateCertId(*cert);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100464}
465
466void Certificate::storageUpdate()
467{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000468 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100469 {
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
Nan Zhoue869bb62021-12-30 11:34:42 -0800494void Certificate::populateProperties(X509& cert)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200495{
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600496 // Update properties if no error thrown
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800497 BIOMemPtr certBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800498 PEM_write_bio_X509(certBio.get(), &cert);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800499 BufMemPtr certBuf(BUF_MEM_new(), BUF_MEM_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600500 BUF_MEM* buf = certBuf.get();
501 BIO_get_mem_ptr(certBio.get(), &buf);
502 std::string certStr(buf->data, buf->length);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800503 certificateString(certStr);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600504
505 static const int maxKeySize = 4096;
506 char subBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800507 BIOMemPtr subBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800508 // This pointer cannot be freed independantly.
509 X509_NAME* sub = X509_get_subject_name(&cert);
Marri Devender Raodec58772019-06-11 03:10:00 -0500510 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
511 BIO_read(subBio.get(), subBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800512 subject(subBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600513
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600514 char issuerBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800515 BIOMemPtr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800516 // This pointer cannot be freed independantly.
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000517 X509_NAME* issuerName = X509_get_issuer_name(&cert);
518 X509_NAME_print_ex(issuerBio.get(), issuerName, 0, XN_FLAG_SEP_COMMA_PLUS);
Marri Devender Raodec58772019-06-11 03:10:00 -0500519 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800520 issuer(issuerBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600521
522 std::vector<std::string> keyUsageList;
523 ASN1_BIT_STRING* usage;
524
525 // Go through each usage in the bit string and convert to
526 // corresponding string value
527 if ((usage = static_cast<ASN1_BIT_STRING*>(
Nan Zhoue869bb62021-12-30 11:34:42 -0800528 X509_get_ext_d2i(&cert, NID_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600529 {
530 for (auto i = 0; i < usage->length; ++i)
531 {
532 for (auto& x : keyUsageToRfStr)
533 {
534 if (x.first & usage->data[i])
535 {
536 keyUsageList.push_back(x.second);
537 break;
538 }
539 }
540 }
541 }
542
543 EXTENDED_KEY_USAGE* extUsage;
Nan Zhoue869bb62021-12-30 11:34:42 -0800544 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
545 X509_get_ext_d2i(&cert, NID_ext_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600546 {
547 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
548 {
549 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
550 sk_ASN1_OBJECT_value(extUsage, i))]);
551 }
552 }
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800553 keyUsage(keyUsageList);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600554
555 int days = 0;
556 int secs = 0;
557
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800558 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800559 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
560 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600561
Nan Zhoucf811c42021-12-02 14:56:17 -0800562 static const uint64_t dayToSeconds = 24 * 60 * 60;
Nan Zhoue869bb62021-12-30 11:34:42 -0800563 ASN1_TIME* notAfter = X509_get_notAfter(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600564 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800565 validNotAfter((days * dayToSeconds) + secs);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600566
Nan Zhoue869bb62021-12-30 11:34:42 -0800567 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600568 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800569 validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600570}
571
Marri Devender Raocd30c492019-06-12 01:40:17 -0500572void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
573{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800574 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500575 if (!keyBio)
576 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800577 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Raocd30c492019-06-12 01:40:17 -0500578 entry("FILE=%s", filePath.c_str()));
579 elog<InternalFailure>();
580 }
581 BIO_read_filename(keyBio.get(), filePath.c_str());
582
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800583 EVPPkeyPtr priKey(
Marri Devender Raocd30c492019-06-12 01:40:17 -0500584 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
585 ::EVP_PKEY_free);
586 if (!priKey)
587 {
588 log<level::INFO>("Private key not present in file",
589 entry("FILE=%s", filePath.c_str()));
590 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
Nan Zhou718eef32021-12-28 11:03:30 -0800591 privateKeyFile = privateKeyFile / defaultPrivateKeyFileName;
Marri Devender Raocd30c492019-06-12 01:40:17 -0500592 if (!fs::exists(privateKeyFile))
593 {
594 log<level::ERR>("Private key file is not found",
595 entry("FILE=%s", privateKeyFile.c_str()));
596 elog<InternalFailure>();
597 }
598
599 std::ifstream privKeyFileStream;
600 std::ofstream certFileStream;
601 privKeyFileStream.exceptions(std::ifstream::failbit |
602 std::ifstream::badbit |
603 std::ifstream::eofbit);
604 certFileStream.exceptions(std::ofstream::failbit |
605 std::ofstream::badbit |
606 std::ofstream::eofbit);
607 try
608 {
609 privKeyFileStream.open(privateKeyFile);
610 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500611 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500612 certFileStream << privKeyFileStream.rdbuf() << std::flush;
613 privKeyFileStream.close();
614 certFileStream.close();
615 }
616 catch (const std::exception& e)
617 {
618 log<level::ERR>("Failed to append private key",
619 entry("ERR=%s", e.what()),
620 entry("SRC=%s", privateKeyFile.c_str()),
621 entry("DST=%s", filePath.c_str()));
622 elog<InternalFailure>();
623 }
624 }
625}
626
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600627bool Certificate::compareKeys(const std::string& filePath)
628{
629 log<level::INFO>("Certificate compareKeys",
630 entry("FILEPATH=%s", filePath.c_str()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800631 internal::X509Ptr cert(X509_new(), ::X509_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600632 if (!cert)
633 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800634 log<level::ERR>("Error occurred during X509_new call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600635 entry("FILE=%s", filePath.c_str()),
636 entry("ERRCODE=%lu", ERR_get_error()));
637 elog<InternalFailure>();
638 }
639
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800640 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600641 if (!bioCert)
642 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800643 log<level::ERR>("Error occurred during BIO_new_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600644 entry("FILE=%s", filePath.c_str()));
645 elog<InternalFailure>();
646 }
647
648 X509* x509 = cert.get();
649 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
650
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800651 EVPPkeyPtr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600652 if (!pubKey)
653 {
654 log<level::ERR>("Error occurred during X509_get_pubkey",
655 entry("FILE=%s", filePath.c_str()),
656 entry("ERRCODE=%lu", ERR_get_error()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800657 elog<InvalidCertificateError>(
658 InvalidCertificate::REASON("Failed to get public key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600659 }
660
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800661 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600662 if (!keyBio)
663 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800664 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600665 entry("FILE=%s", filePath.c_str()));
666 elog<InternalFailure>();
667 }
668 BIO_read_filename(keyBio.get(), filePath.c_str());
669
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800670 EVPPkeyPtr priKey(
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600671 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
672 ::EVP_PKEY_free);
673 if (!priKey)
674 {
675 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
676 entry("FILE=%s", filePath.c_str()),
677 entry("ERRCODE=%lu", ERR_get_error()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800678 elog<InvalidCertificateError>(
679 InvalidCertificate::REASON("Failed to get private key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600680 }
681
Patrick Williams55ceaa22021-12-14 06:52:26 -0600682#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600683 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
Patrick Williams55ceaa22021-12-14 06:52:26 -0600684#else
685 int32_t rc = EVP_PKEY_eq(priKey.get(), pubKey.get());
686#endif
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600687 if (rc != 1)
688 {
689 log<level::ERR>("Private key is not matching with Certificate",
690 entry("FILE=%s", filePath.c_str()),
691 entry("ERRCODE=%d", rc));
692 return false;
693 }
694 return true;
695}
696
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200697void Certificate::delete_()
698{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100699 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200700}
Nan Zhou6ec13c82021-12-30 11:34:50 -0800701
702std::string Certificate::getObjectPath()
703{
704 return objectPath;
705}
706
707std::string Certificate::getCertFilePath()
708{
709 return certFilePath;
710}
711
712void Certificate::setCertFilePath(const std::string& path)
713{
714 certFilePath = path;
715}
716
717void Certificate::setCertInstallPath(const std::string& path)
718{
719 certInstallPath = path;
720}
721
Nan Zhoue1289ad2021-12-28 11:02:56 -0800722} // namespace phosphor::certs