blob: 038792175bc173c9c34165e26bfcef3f832829c4 [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>
Ravi Tejaf2646272023-09-30 13:00:55 -050021#include <phosphor-logging/lg2.hpp>
Patrick Williams223e4602023-05-10 07:51:11 -050022#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;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080043using InvalidCertificateError =
44 ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
45using ::phosphor::logging::xyz::openbmc_project::Certs::InvalidCertificate;
46using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
47
Marri Devender Rao6ceec402019-02-01 03:15:19 -060048// RAII support for openSSL functions.
Nan Zhoucf06ccd2021-12-28 16:25:45 -080049using BIOMemPtr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
50using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080051using ASN1TimePtr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
52using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
53using BufMemPtr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060054
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060055// Refer to schema 2018.3
56// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
57// supported KeyUsage types in redfish
58// Refer to
59// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
60// key usage bit fields
61std::map<uint8_t, std::string> keyUsageToRfStr = {
62 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
63 {KU_NON_REPUDIATION, "NonRepudiation"},
64 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
65 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
66 {KU_KEY_AGREEMENT, "KeyAgreement"},
67 {KU_KEY_CERT_SIGN, "KeyCertSign"},
68 {KU_CRL_SIGN, "CRLSigning"},
69 {KU_ENCIPHER_ONLY, "EncipherOnly"},
70 {KU_DECIPHER_ONLY, "DecipherOnly"}};
71
72// Refer to schema 2018.3
73// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
74// supported Extended KeyUsage types in redfish
75std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
76 {NID_server_auth, "ServerAuthentication"},
77 {NID_client_auth, "ClientAuthentication"},
78 {NID_email_protect, "EmailProtection"},
79 {NID_OCSP_sign, "OCSPSigning"},
80 {NID_ad_timeStamping, "Timestamping"},
81 {NID_code_sign, "CodeSigning"}};
82
Nan Zhoue869bb62021-12-30 11:34:42 -080083/**
Nan Zhou6ec13c82021-12-30 11:34:50 -080084 * @brief Dumps the PEM encoded certificate to installFilePath
Nan Zhoue869bb62021-12-30 11:34:42 -080085 *
Nan Zhou6ec13c82021-12-30 11:34:50 -080086 * @param[in] pem - PEM encoded X509 certificate buffer.
87 * @param[in] certFilePath - Path to the destination file.
Nan Zhoue869bb62021-12-30 11:34:42 -080088 *
89 * @return void
90 */
Nan Zhou6ec13c82021-12-30 11:34:50 -080091
92void dumpCertificate(const std::string& pem, const std::string& certFilePath)
93{
94 std::ofstream outputCertFileStream;
95
96 outputCertFileStream.exceptions(
97 std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
98
99 try
100 {
101 outputCertFileStream.open(certFilePath, std::ios::out);
102 outputCertFileStream << pem << "\n" << std::flush;
103 outputCertFileStream.close();
104 }
105 catch (const std::exception& e)
106 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500107 lg2::error(
108 "Failed to dump certificate, ERR:{ERR}, SRC_PEM:{SRC_PEM}, DST:{DST}",
109 "ERR", e, "SRC_PEM", pem, "DST", certFilePath);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800110 elog<InternalFailure>();
111 }
112}
113} // namespace
114
115void Certificate::copyCertificate(const std::string& certSrcFilePath,
116 const std::string& certFilePath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200117{
Nan Zhoue869bb62021-12-30 11:34:42 -0800118 // Copy the certificate to the installation path
Zac Tangcbbec8d2024-09-04 13:37:25 -0700119 // During bootup will be parsing existing file so no need to
Nan Zhoue869bb62021-12-30 11:34:42 -0800120 // copy it.
121 if (certSrcFilePath != certFilePath)
122 {
Zac Tangcbbec8d2024-09-04 13:37:25 -0700123 // -p flag preserves the file metadata when copying
124 // -f flag forces the copy
125 const std::string command =
126 std::format("cp -fp {} {}", certSrcFilePath, certFilePath);
127 int statusCode = std::system(command.c_str());
128
129 // Non-zero `status_code` indicates something went wrong with issuing
130 // the copy command.
131 if (statusCode != 0)
Nan Zhoue869bb62021-12-30 11:34:42 -0800132 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500133 lg2::error(
134 "Failed to copy certificate, ERR:{ERR}, SRC:{SRC}, DST:{DST}",
Zac Tangcbbec8d2024-09-04 13:37:25 -0700135 "ERR", statusCode, "SRC", certSrcFilePath, "DST", certFilePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800136 elog<InternalFailure>();
137 }
138 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100139}
140
141std::string
142 Certificate::generateUniqueFilePath(const std::string& directoryPath)
143{
Nan Zhoucfb58022021-12-28 11:02:26 -0800144 char* filePath = tempnam(directoryPath.c_str(), nullptr);
145 if (filePath == nullptr)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100146 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500147 lg2::error(
148 "Error occurred while creating random certificate file path, DIR:{DIR}",
149 "DIR", directoryPath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100150 elog<InternalFailure>();
151 }
152 std::string filePathStr(filePath);
153 free(filePath);
154 return filePathStr;
155}
156
157std::string Certificate::generateAuthCertFileX509Path(
158 const std::string& certSrcFilePath, const std::string& certDstDirPath)
159{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800160 const internal::X509Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200161 unsigned long hash = X509_subject_name_hash(cert.get());
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000162 static constexpr auto certHashLength = 9;
163 char hashBuf[certHashLength];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200164
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000165 snprintf(hashBuf, certHashLength, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200166
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100167 const std::string certHash(hashBuf);
Nan Zhou718eef32021-12-28 11:03:30 -0800168 for (size_t i = 0; i < maxNumAuthorityCertificates; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100169 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100170 const std::string certDstFileX509Path =
171 certDstDirPath + "/" + certHash + "." + std::to_string(i);
172 if (!fs::exists(certDstFileX509Path))
173 {
174 return certDstFileX509Path;
175 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100176 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200177
Ravi Tejaf2646272023-09-30 13:00:55 -0500178 lg2::error("Authority certificate x509 file path already used, DIR:{DIR}",
179 "DIR", certDstDirPath);
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100180 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100181}
182
183std::string
184 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
185{
186 // If there is a certificate file path (which means certificate replacement
187 // is doing) use it (do not create new one)
188 if (!certFilePath.empty())
189 {
190 return certFilePath;
191 }
192 // If source certificate file is located in the certificates directory use
193 // it (do not create new one)
194 else if (fs::path(certSrcFilePath).parent_path().string() ==
195 certInstallPath)
196 {
197 return certSrcFilePath;
198 }
199 // Otherwise generate new file name/path
200 else
201 {
202 return generateUniqueFilePath(certInstallPath);
203 }
204}
205
206std::string
207 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
208{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000209 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100210 {
211 return generateAuthCertFilePath(certSrcFilePath);
212 }
213 else
214 {
215 return certInstallPath;
216 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200217}
218
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500219Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800220 CertificateType type, const std::string& installPath,
221 const std::string& uploadPath, Watch* watch,
Willy Tu698a5742022-09-23 21:33:01 +0000222 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500223 internal::CertificateInterface(
224 bus, objPath.c_str(),
225 internal::CertificateInterface::action::defer_emit),
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800226 objectPath(objPath), certType(type), certInstallPath(installPath),
227 certWatch(watch), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600228{
229 auto installHelper = [this](const auto& filePath) {
230 if (!compareKeys(filePath))
231 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800232 elog<InvalidCertificateError>(InvalidCertificate::REASON(
233 "Private key does not match the Certificate"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600234 };
235 };
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000236 typeFuncMap[CertificateType::server] = installHelper;
237 typeFuncMap[CertificateType::client] = installHelper;
238 typeFuncMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500239
240 auto appendPrivateKey = [this](const std::string& filePath) {
241 checkAndAppendPrivateKey(filePath);
242 };
243
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000244 appendKeyMap[CertificateType::server] = appendPrivateKey;
245 appendKeyMap[CertificateType::client] = appendPrivateKey;
246 appendKeyMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500247
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100248 // Generate certificate file path
249 certFilePath = generateCertFilePath(uploadPath);
250
Marri Devender Raocd30c492019-06-12 01:40:17 -0500251 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000252 install(uploadPath, restore);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500253
Marri Devender Raoedd11312019-02-27 08:45:10 -0600254 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600255}
256
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500257Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhou6ec13c82021-12-30 11:34:50 -0800258 const CertificateType& type,
259 const std::string& installPath, X509_STORE& x509Store,
260 const std::string& pem, Watch* watchPtr,
Willy Tu698a5742022-09-23 21:33:01 +0000261 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500262 internal::CertificateInterface(
263 bus, objPath.c_str(),
264 internal::CertificateInterface::action::defer_emit),
Nan Zhou6ec13c82021-12-30 11:34:50 -0800265 objectPath(objPath), certType(type), certInstallPath(installPath),
266 certWatch(watchPtr), manager(parent)
267{
268 // Generate certificate file path
269 certFilePath = generateUniqueFilePath(installPath);
270
271 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000272 install(x509Store, pem, restore);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800273
274 this->emit_object_added();
275}
276
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600277Certificate::~Certificate()
278{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100279 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600280 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500281 lg2::info("Certificate file not found! PATH:{PATH}", "PATH",
282 certFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600283 }
284}
285
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500286void Certificate::replace(const std::string filePath)
287{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100288 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500289}
290
Willy Tu698a5742022-09-23 21:33:01 +0000291void Certificate::install(const std::string& certSrcFilePath, bool restore)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600292{
Willy Tu698a5742022-09-23 21:33:01 +0000293 if (restore)
294 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500295 lg2::debug("Certificate install, FILEPATH:{FILEPATH}", "FILEPATH",
296 certSrcFilePath);
Willy Tu698a5742022-09-23 21:33:01 +0000297 }
298 else
299 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500300 lg2::info("Certificate install, FILEPATH:{FILEPATH}", "FILEPATH",
301 certSrcFilePath);
Willy Tu698a5742022-09-23 21:33:01 +0000302 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600303
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500304 // stop watch for user initiated certificate install
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800305 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500306 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800307 certWatch->stopWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500308 }
309
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600310 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100311 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600312 if (!fs::exists(file))
313 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500314 lg2::error("File is Missing, FILE:{FILE}", "FILE", certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600315 elog<InternalFailure>();
316 }
317
318 try
319 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100320 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600321 {
322 // file is empty
Ravi Tejaf2646272023-09-30 13:00:55 -0500323 lg2::error("File is empty, FILE:{FILE}", "FILE", certSrcFilePath);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800324 elog<InvalidCertificateError>(
325 InvalidCertificate::REASON("File is empty"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600326 }
327 }
328 catch (const fs::filesystem_error& e)
329 {
330 // Log Error message
Ravi Tejaf2646272023-09-30 13:00:55 -0500331 lg2::error("File is empty, FILE:{FILE}, ERR:{ERR}", "FILE",
332 certSrcFilePath, "ERR", e);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600333 elog<InternalFailure>();
334 }
335
Nan Zhoue869bb62021-12-30 11:34:42 -0800336 X509StorePtr x509Store = getX509Store(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600337
Nan Zhoubf3cf752021-12-28 11:02:07 -0800338 // Load Certificate file into the X509 structure.
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800339 internal::X509Ptr cert = loadCert(certSrcFilePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800340
341 // Perform validation
342 validateCertificateAgainstStore(*x509Store, *cert);
343 validateCertificateStartDate(*cert);
344 validateCertificateInSSLContext(*cert);
345
346 // Invoke type specific append private key function.
347 if (auto it = appendKeyMap.find(certType); it == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600348 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500349 lg2::error("Unsupported Type, TYPE:{TYPE}", "TYPE",
350 certificateTypeToString(certType));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600351 elog<InternalFailure>();
352 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600353 else
354 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800355 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600356 }
357
Marri Devender Raocd30c492019-06-12 01:40:17 -0500358 // Invoke type specific compare keys function.
Nan Zhoue869bb62021-12-30 11:34:42 -0800359 if (auto it = typeFuncMap.find(certType); it == typeFuncMap.end())
Marri Devender Raocd30c492019-06-12 01:40:17 -0500360 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500361 lg2::error("Unsupported Type, TYPE:{TYPE}", "TYPE",
362 certificateTypeToString(certType));
Marri Devender Raocd30c492019-06-12 01:40:17 -0500363 elog<InternalFailure>();
364 }
Nan Zhoue869bb62021-12-30 11:34:42 -0800365 else
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600366 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800367 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600368 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500369
Nan Zhoue869bb62021-12-30 11:34:42 -0800370 copyCertificate(certSrcFilePath, certFilePath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100371 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600372
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100373 // Keep certificate ID
Nan Zhoue869bb62021-12-30 11:34:42 -0800374 certId = generateCertId(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200375
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600376 // Parse the certificate file and populate properties
Nan Zhoue869bb62021-12-30 11:34:42 -0800377 populateProperties(*cert);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500378
379 // restart watch
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800380 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500381 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800382 certWatch->startWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500383 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600384}
385
Willy Tu698a5742022-09-23 21:33:01 +0000386void Certificate::install(X509_STORE& x509Store, const std::string& pem,
387 bool restore)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800388{
Willy Tu698a5742022-09-23 21:33:01 +0000389 if (restore)
390 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500391 lg2::debug("Certificate install, PEM_STR:{PEM_STR}", "PEM_STR", pem);
Willy Tu698a5742022-09-23 21:33:01 +0000392 }
393 else
394 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500395 lg2::info("Certificate install, PEM_STR:{PEM_STR} ", "PEM_STR", pem);
Willy Tu698a5742022-09-23 21:33:01 +0000396 }
Nan Zhou6ec13c82021-12-30 11:34:50 -0800397
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000398 if (certType != CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800399 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500400 lg2::error("Bulk install error: Unsupported Type; only authority "
401 "supports bulk install, TYPE:{TYPE}",
402 "TYPE", certificateTypeToString(certType));
Nan Zhou6ec13c82021-12-30 11:34:50 -0800403 elog<InternalFailure>();
404 }
405
406 // stop watch for user initiated certificate install
407 if (certWatch)
408 {
409 certWatch->stopWatch();
410 }
411
412 // Load Certificate file into the X509 structure.
413 internal::X509Ptr cert = parseCert(pem);
414 // Perform validation; no type specific compare keys function
415 validateCertificateAgainstStore(x509Store, *cert);
416 validateCertificateStartDate(*cert);
417 validateCertificateInSSLContext(*cert);
418
419 // Copy the PEM to the installation path
420 dumpCertificate(pem, certFilePath);
421 storageUpdate();
422 // Keep certificate ID
423 certId = generateCertId(*cert);
424 // Parse the certificate file and populate properties
425 populateProperties(*cert);
426 // restart watch
427 if (certWatch)
428 {
429 certWatch->startWatch();
430 }
431}
432
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600433void Certificate::populateProperties()
434{
Nan Zhoue869bb62021-12-30 11:34:42 -0800435 internal::X509Ptr cert = loadCert(certInstallPath);
436 populateProperties(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200437}
438
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100439std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200440{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100441 return certId;
442}
443
444bool Certificate::isSame(const std::string& certPath)
445{
Nan Zhoue869bb62021-12-30 11:34:42 -0800446 internal::X509Ptr cert = loadCert(certPath);
447 return getCertId() == generateCertId(*cert);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100448}
449
450void Certificate::storageUpdate()
451{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000452 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100453 {
454 // Create symbolic link in the certificate directory
455 std::string certFileX509Path;
456 try
457 {
458 if (!certFilePath.empty() &&
459 fs::is_regular_file(fs::path(certFilePath)))
460 {
461 certFileX509Path =
462 generateAuthCertFileX509Path(certFilePath, certInstallPath);
463 fs::create_symlink(fs::path(certFilePath),
464 fs::path(certFileX509Path));
465 }
466 }
467 catch (const std::exception& e)
468 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500469 lg2::error("Failed to create symlink for certificate, ERR:{ERR},"
470 "FILE:{FILE}, SYMLINK:{SYMLINK}",
471 "ERR", e, "FILE", certFilePath, "SYMLINK",
472 certFileX509Path);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100473 elog<InternalFailure>();
474 }
475 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200476}
477
Nan Zhoue869bb62021-12-30 11:34:42 -0800478void Certificate::populateProperties(X509& cert)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200479{
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600480 // Update properties if no error thrown
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800481 BIOMemPtr certBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800482 PEM_write_bio_X509(certBio.get(), &cert);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800483 BufMemPtr certBuf(BUF_MEM_new(), BUF_MEM_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600484 BUF_MEM* buf = certBuf.get();
485 BIO_get_mem_ptr(certBio.get(), &buf);
486 std::string certStr(buf->data, buf->length);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800487 certificateString(certStr);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600488
489 static const int maxKeySize = 4096;
490 char subBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800491 BIOMemPtr subBio(BIO_new(BIO_s_mem()), BIO_free);
Manojkiran Eda5d4f7932024-06-17 11:49:21 +0530492 // This pointer cannot be freed independently.
Nan Zhoue869bb62021-12-30 11:34:42 -0800493 X509_NAME* sub = X509_get_subject_name(&cert);
Marri Devender Raodec58772019-06-11 03:10:00 -0500494 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
495 BIO_read(subBio.get(), subBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800496 subject(subBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600497
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600498 char issuerBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800499 BIOMemPtr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
Manojkiran Eda5d4f7932024-06-17 11:49:21 +0530500 // This pointer cannot be freed independently.
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000501 X509_NAME* issuerName = X509_get_issuer_name(&cert);
502 X509_NAME_print_ex(issuerBio.get(), issuerName, 0, XN_FLAG_SEP_COMMA_PLUS);
Marri Devender Raodec58772019-06-11 03:10:00 -0500503 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800504 issuer(issuerBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600505
506 std::vector<std::string> keyUsageList;
507 ASN1_BIT_STRING* usage;
508
509 // Go through each usage in the bit string and convert to
510 // corresponding string value
511 if ((usage = static_cast<ASN1_BIT_STRING*>(
Nan Zhoue869bb62021-12-30 11:34:42 -0800512 X509_get_ext_d2i(&cert, NID_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600513 {
514 for (auto i = 0; i < usage->length; ++i)
515 {
516 for (auto& x : keyUsageToRfStr)
517 {
518 if (x.first & usage->data[i])
519 {
520 keyUsageList.push_back(x.second);
521 break;
522 }
523 }
524 }
525 }
526
527 EXTENDED_KEY_USAGE* extUsage;
Nan Zhoue869bb62021-12-30 11:34:42 -0800528 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
529 X509_get_ext_d2i(&cert, NID_ext_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600530 {
531 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
532 {
533 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
534 sk_ASN1_OBJECT_value(extUsage, i))]);
535 }
536 }
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800537 keyUsage(keyUsageList);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600538
539 int days = 0;
540 int secs = 0;
541
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800542 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800543 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
544 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600545
Nan Zhoucf811c42021-12-02 14:56:17 -0800546 static const uint64_t dayToSeconds = 24 * 60 * 60;
Nan Zhoue869bb62021-12-30 11:34:42 -0800547 ASN1_TIME* notAfter = X509_get_notAfter(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600548 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800549 validNotAfter((days * dayToSeconds) + secs);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600550
Nan Zhoue869bb62021-12-30 11:34:42 -0800551 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600552 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800553 validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600554}
555
Marri Devender Raocd30c492019-06-12 01:40:17 -0500556void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
557{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800558 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500559 if (!keyBio)
560 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500561 lg2::error("Error occurred during BIO_s_file call, FILE:{FILE}", "FILE",
562 filePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500563 elog<InternalFailure>();
564 }
565 BIO_read_filename(keyBio.get(), filePath.c_str());
566
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800567 EVPPkeyPtr priKey(
Marri Devender Raocd30c492019-06-12 01:40:17 -0500568 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
569 ::EVP_PKEY_free);
570 if (!priKey)
571 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500572 lg2::info("Private key not present in file, FILE:{FILE}", "FILE",
573 filePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500574 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
Nan Zhou718eef32021-12-28 11:03:30 -0800575 privateKeyFile = privateKeyFile / defaultPrivateKeyFileName;
Marri Devender Raocd30c492019-06-12 01:40:17 -0500576 if (!fs::exists(privateKeyFile))
577 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500578 lg2::error("Private key file is not found, FILE:{FILE}", "FILE",
579 privateKeyFile);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500580 elog<InternalFailure>();
581 }
582
583 std::ifstream privKeyFileStream;
584 std::ofstream certFileStream;
Patrick Williamsa2f68d82024-08-16 15:21:36 -0400585 privKeyFileStream.exceptions(
586 std::ifstream::failbit | std::ifstream::badbit |
587 std::ifstream::eofbit);
588 certFileStream.exceptions(
589 std::ofstream::failbit | std::ofstream::badbit |
590 std::ofstream::eofbit);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500591 try
592 {
593 privKeyFileStream.open(privateKeyFile);
594 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500595 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500596 certFileStream << privKeyFileStream.rdbuf() << std::flush;
597 privKeyFileStream.close();
598 certFileStream.close();
599 }
600 catch (const std::exception& e)
601 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500602 lg2::error(
603 "Failed to append private key, ERR:{ERR}, SRC:{SRC}, DST:{DST}",
604 "ERR", e, "SRC", privateKeyFile, "DST", filePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500605 elog<InternalFailure>();
606 }
607 }
608}
609
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600610bool Certificate::compareKeys(const std::string& filePath)
611{
Ravi Tejaf2646272023-09-30 13:00:55 -0500612 lg2::info("Certificate compareKeys, FILEPATH:{FILEPATH}", "FILEPATH",
613 filePath);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800614 internal::X509Ptr cert(X509_new(), ::X509_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600615 if (!cert)
616 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500617 lg2::error(
618 "Error occurred during X509_new call, FILE:{FILE}, ERRCODE:{ERRCODE}",
619 "FILE", filePath, "ERRCODE", ERR_get_error());
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600620 elog<InternalFailure>();
621 }
622
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800623 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600624 if (!bioCert)
625 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500626 lg2::error("Error occurred during BIO_new_file call, FILE:{FILE}",
627 "FILE", filePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600628 elog<InternalFailure>();
629 }
630
631 X509* x509 = cert.get();
632 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
633
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800634 EVPPkeyPtr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600635 if (!pubKey)
636 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500637 lg2::error(
638 "Error occurred during X509_get_pubkey, FILE:{FILE}, ERRCODE:{ERRCODE}",
639 "FILE", filePath, "ERRCODE", ERR_get_error());
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800640 elog<InvalidCertificateError>(
641 InvalidCertificate::REASON("Failed to get public key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600642 }
643
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800644 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600645 if (!keyBio)
646 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500647 lg2::error("Error occurred during BIO_s_file call, FILE:{FILE}", "FILE",
648 filePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600649 elog<InternalFailure>();
650 }
651 BIO_read_filename(keyBio.get(), filePath.c_str());
652
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800653 EVPPkeyPtr priKey(
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600654 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
655 ::EVP_PKEY_free);
656 if (!priKey)
657 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500658 lg2::error(
659 "Error occurred during PEM_read_bio_PrivateKey, FILE:{FILE}, ERRCODE:{ERRCODE}",
660 "FILE", filePath, "ERRCODE", ERR_get_error());
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800661 elog<InvalidCertificateError>(
662 InvalidCertificate::REASON("Failed to get private key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600663 }
664
Patrick Williams55ceaa22021-12-14 06:52:26 -0600665#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600666 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
Patrick Williams55ceaa22021-12-14 06:52:26 -0600667#else
668 int32_t rc = EVP_PKEY_eq(priKey.get(), pubKey.get());
669#endif
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600670 if (rc != 1)
671 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500672 lg2::error(
673 "Private key is not matching with Certificate, FILE:{FILE}, ERRCODE:{ERRCODE}",
674 "FILE", filePath, "ERRCODE", rc);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600675 return false;
676 }
677 return true;
678}
679
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200680void Certificate::delete_()
681{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100682 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200683}
Nan Zhou6ec13c82021-12-30 11:34:50 -0800684
685std::string Certificate::getObjectPath()
686{
687 return objectPath;
688}
689
690std::string Certificate::getCertFilePath()
691{
692 return certFilePath;
693}
694
695void Certificate::setCertFilePath(const std::string& path)
696{
697 certFilePath = path;
698}
699
700void Certificate::setCertInstallPath(const std::string& path)
701{
702 certInstallPath = path;
703}
704
Nan Zhoue1289ad2021-12-28 11:02:56 -0800705} // namespace phosphor::certs