blob: 5e4ef9c287dbbbac32a6cb9a81e3fc5ea81ec08e [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>
Jayanth Othayothcd24c232024-11-24 09:10:15 -060033#include <random>
Nan Zhou014be0b2021-12-28 18:00:14 -080034#include <utility>
35#include <vector>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050036
Nan Zhoue1289ad2021-12-28 11:02:56 -080037namespace phosphor::certs
Marri Devender Rao6ceec402019-02-01 03:15:19 -060038{
Nan Zhoucf06ccd2021-12-28 16:25:45 -080039
40namespace
41{
42namespace fs = std::filesystem;
43using ::phosphor::logging::elog;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080044using InvalidCertificateError =
45 ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
46using ::phosphor::logging::xyz::openbmc_project::Certs::InvalidCertificate;
47using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
48
Marri Devender Rao6ceec402019-02-01 03:15:19 -060049// RAII support for openSSL functions.
Nan Zhoucf06ccd2021-12-28 16:25:45 -080050using BIOMemPtr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
51using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080052using ASN1TimePtr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
53using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
54using BufMemPtr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060055
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060056// Refer to schema 2018.3
57// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
58// supported KeyUsage types in redfish
59// Refer to
60// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
61// key usage bit fields
62std::map<uint8_t, std::string> keyUsageToRfStr = {
63 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
64 {KU_NON_REPUDIATION, "NonRepudiation"},
65 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
66 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
67 {KU_KEY_AGREEMENT, "KeyAgreement"},
68 {KU_KEY_CERT_SIGN, "KeyCertSign"},
69 {KU_CRL_SIGN, "CRLSigning"},
70 {KU_ENCIPHER_ONLY, "EncipherOnly"},
71 {KU_DECIPHER_ONLY, "DecipherOnly"}};
72
73// Refer to schema 2018.3
74// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
75// supported Extended KeyUsage types in redfish
76std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
77 {NID_server_auth, "ServerAuthentication"},
78 {NID_client_auth, "ClientAuthentication"},
79 {NID_email_protect, "EmailProtection"},
80 {NID_OCSP_sign, "OCSPSigning"},
81 {NID_ad_timeStamping, "Timestamping"},
82 {NID_code_sign, "CodeSigning"}};
83
Nan Zhoue869bb62021-12-30 11:34:42 -080084/**
Nan Zhou6ec13c82021-12-30 11:34:50 -080085 * @brief Dumps the PEM encoded certificate to installFilePath
Nan Zhoue869bb62021-12-30 11:34:42 -080086 *
Nan Zhou6ec13c82021-12-30 11:34:50 -080087 * @param[in] pem - PEM encoded X509 certificate buffer.
88 * @param[in] certFilePath - Path to the destination file.
Nan Zhoue869bb62021-12-30 11:34:42 -080089 *
90 * @return void
91 */
Nan Zhou6ec13c82021-12-30 11:34:50 -080092
93void dumpCertificate(const std::string& pem, const std::string& certFilePath)
94{
95 std::ofstream outputCertFileStream;
96
97 outputCertFileStream.exceptions(
98 std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
99
100 try
101 {
102 outputCertFileStream.open(certFilePath, std::ios::out);
103 outputCertFileStream << pem << "\n" << std::flush;
104 outputCertFileStream.close();
105 }
106 catch (const std::exception& e)
107 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500108 lg2::error(
109 "Failed to dump certificate, ERR:{ERR}, SRC_PEM:{SRC_PEM}, DST:{DST}",
110 "ERR", e, "SRC_PEM", pem, "DST", certFilePath);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800111 elog<InternalFailure>();
112 }
113}
114} // namespace
115
116void Certificate::copyCertificate(const std::string& certSrcFilePath,
117 const std::string& certFilePath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200118{
Nan Zhoue869bb62021-12-30 11:34:42 -0800119 // Copy the certificate to the installation path
Zac Tangcbbec8d2024-09-04 13:37:25 -0700120 // During bootup will be parsing existing file so no need to
Nan Zhoue869bb62021-12-30 11:34:42 -0800121 // copy it.
122 if (certSrcFilePath != certFilePath)
123 {
Zac Tangcbbec8d2024-09-04 13:37:25 -0700124 // -p flag preserves the file metadata when copying
125 // -f flag forces the copy
126 const std::string command =
127 std::format("cp -fp {} {}", certSrcFilePath, certFilePath);
128 int statusCode = std::system(command.c_str());
129
130 // Non-zero `status_code` indicates something went wrong with issuing
131 // the copy command.
132 if (statusCode != 0)
Nan Zhoue869bb62021-12-30 11:34:42 -0800133 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500134 lg2::error(
135 "Failed to copy certificate, ERR:{ERR}, SRC:{SRC}, DST:{DST}",
Zac Tangcbbec8d2024-09-04 13:37:25 -0700136 "ERR", statusCode, "SRC", certSrcFilePath, "DST", certFilePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800137 elog<InternalFailure>();
138 }
139 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100140}
141
142std::string
143 Certificate::generateUniqueFilePath(const std::string& directoryPath)
144{
Jayanth Othayothcd24c232024-11-24 09:10:15 -0600145 // Create a template for the temporary file name
146 std::string filePath = directoryPath + "/" + "cert-XXXXXX";
147 int fd = mkstemp(filePath.data());
148 if (fd == -1)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100149 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500150 lg2::error(
151 "Error occurred while creating random certificate file path, DIR:{DIR}",
152 "DIR", directoryPath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100153 elog<InternalFailure>();
Jayanth Othayothcd24c232024-11-24 09:10:15 -0600154 throw std::runtime_error("Failed to create unique file path");
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100155 }
Jayanth Othayothcd24c232024-11-24 09:10:15 -0600156
157 // Close the file descriptor and file, just need the unique path
158 close(fd);
159 std::remove(filePath.data());
160 return filePath;
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100161}
162
163std::string Certificate::generateAuthCertFileX509Path(
164 const std::string& certSrcFilePath, const std::string& certDstDirPath)
165{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800166 const internal::X509Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200167 unsigned long hash = X509_subject_name_hash(cert.get());
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000168 static constexpr auto certHashLength = 9;
169 char hashBuf[certHashLength];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200170
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000171 snprintf(hashBuf, certHashLength, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200172
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100173 const std::string certHash(hashBuf);
Nan Zhou718eef32021-12-28 11:03:30 -0800174 for (size_t i = 0; i < maxNumAuthorityCertificates; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100175 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100176 const std::string certDstFileX509Path =
177 certDstDirPath + "/" + certHash + "." + std::to_string(i);
178 if (!fs::exists(certDstFileX509Path))
179 {
180 return certDstFileX509Path;
181 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100182 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200183
Ravi Tejaf2646272023-09-30 13:00:55 -0500184 lg2::error("Authority certificate x509 file path already used, DIR:{DIR}",
185 "DIR", certDstDirPath);
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100186 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100187}
188
189std::string
190 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
191{
192 // If there is a certificate file path (which means certificate replacement
193 // is doing) use it (do not create new one)
194 if (!certFilePath.empty())
195 {
196 return certFilePath;
197 }
198 // If source certificate file is located in the certificates directory use
199 // it (do not create new one)
200 else if (fs::path(certSrcFilePath).parent_path().string() ==
201 certInstallPath)
202 {
203 return certSrcFilePath;
204 }
205 // Otherwise generate new file name/path
206 else
207 {
208 return generateUniqueFilePath(certInstallPath);
209 }
210}
211
212std::string
213 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
214{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000215 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100216 {
217 return generateAuthCertFilePath(certSrcFilePath);
218 }
219 else
220 {
221 return certInstallPath;
222 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200223}
224
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500225Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800226 CertificateType type, const std::string& installPath,
227 const std::string& uploadPath, Watch* watch,
Willy Tu698a5742022-09-23 21:33:01 +0000228 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500229 internal::CertificateInterface(
230 bus, objPath.c_str(),
231 internal::CertificateInterface::action::defer_emit),
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800232 objectPath(objPath), certType(type), certInstallPath(installPath),
233 certWatch(watch), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600234{
235 auto installHelper = [this](const auto& filePath) {
236 if (!compareKeys(filePath))
237 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800238 elog<InvalidCertificateError>(InvalidCertificate::REASON(
239 "Private key does not match the Certificate"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600240 };
241 };
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000242 typeFuncMap[CertificateType::server] = installHelper;
243 typeFuncMap[CertificateType::client] = installHelper;
244 typeFuncMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500245
246 auto appendPrivateKey = [this](const std::string& filePath) {
247 checkAndAppendPrivateKey(filePath);
248 };
249
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000250 appendKeyMap[CertificateType::server] = appendPrivateKey;
251 appendKeyMap[CertificateType::client] = appendPrivateKey;
252 appendKeyMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500253
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100254 // Generate certificate file path
255 certFilePath = generateCertFilePath(uploadPath);
256
Marri Devender Raocd30c492019-06-12 01:40:17 -0500257 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000258 install(uploadPath, restore);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500259
Marri Devender Raoedd11312019-02-27 08:45:10 -0600260 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600261}
262
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500263Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhou6ec13c82021-12-30 11:34:50 -0800264 const CertificateType& type,
265 const std::string& installPath, X509_STORE& x509Store,
266 const std::string& pem, Watch* watchPtr,
Willy Tu698a5742022-09-23 21:33:01 +0000267 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500268 internal::CertificateInterface(
269 bus, objPath.c_str(),
270 internal::CertificateInterface::action::defer_emit),
Nan Zhou6ec13c82021-12-30 11:34:50 -0800271 objectPath(objPath), certType(type), certInstallPath(installPath),
272 certWatch(watchPtr), manager(parent)
273{
274 // Generate certificate file path
275 certFilePath = generateUniqueFilePath(installPath);
276
277 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000278 install(x509Store, pem, restore);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800279
280 this->emit_object_added();
281}
282
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600283Certificate::~Certificate()
284{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100285 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600286 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500287 lg2::info("Certificate file not found! PATH:{PATH}", "PATH",
288 certFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600289 }
290}
291
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500292void Certificate::replace(const std::string filePath)
293{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100294 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500295}
296
Willy Tu698a5742022-09-23 21:33:01 +0000297void Certificate::install(const std::string& certSrcFilePath, bool restore)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600298{
Willy Tu698a5742022-09-23 21:33:01 +0000299 if (restore)
300 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500301 lg2::debug("Certificate install, FILEPATH:{FILEPATH}", "FILEPATH",
302 certSrcFilePath);
Willy Tu698a5742022-09-23 21:33:01 +0000303 }
304 else
305 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500306 lg2::info("Certificate install, FILEPATH:{FILEPATH}", "FILEPATH",
307 certSrcFilePath);
Willy Tu698a5742022-09-23 21:33:01 +0000308 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600309
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500310 // stop watch for user initiated certificate install
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800311 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500312 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800313 certWatch->stopWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500314 }
315
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600316 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100317 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600318 if (!fs::exists(file))
319 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500320 lg2::error("File is Missing, FILE:{FILE}", "FILE", certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600321 elog<InternalFailure>();
322 }
323
324 try
325 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100326 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600327 {
328 // file is empty
Ravi Tejaf2646272023-09-30 13:00:55 -0500329 lg2::error("File is empty, FILE:{FILE}", "FILE", certSrcFilePath);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800330 elog<InvalidCertificateError>(
331 InvalidCertificate::REASON("File is empty"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600332 }
333 }
334 catch (const fs::filesystem_error& e)
335 {
336 // Log Error message
Ravi Tejaf2646272023-09-30 13:00:55 -0500337 lg2::error("File is empty, FILE:{FILE}, ERR:{ERR}", "FILE",
338 certSrcFilePath, "ERR", e);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600339 elog<InternalFailure>();
340 }
341
Nan Zhoue869bb62021-12-30 11:34:42 -0800342 X509StorePtr x509Store = getX509Store(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600343
Nan Zhoubf3cf752021-12-28 11:02:07 -0800344 // Load Certificate file into the X509 structure.
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800345 internal::X509Ptr cert = loadCert(certSrcFilePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800346
347 // Perform validation
348 validateCertificateAgainstStore(*x509Store, *cert);
349 validateCertificateStartDate(*cert);
350 validateCertificateInSSLContext(*cert);
351
352 // Invoke type specific append private key function.
353 if (auto it = appendKeyMap.find(certType); it == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600354 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500355 lg2::error("Unsupported Type, TYPE:{TYPE}", "TYPE",
356 certificateTypeToString(certType));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600357 elog<InternalFailure>();
358 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600359 else
360 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800361 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600362 }
363
Marri Devender Raocd30c492019-06-12 01:40:17 -0500364 // Invoke type specific compare keys function.
Nan Zhoue869bb62021-12-30 11:34:42 -0800365 if (auto it = typeFuncMap.find(certType); it == typeFuncMap.end())
Marri Devender Raocd30c492019-06-12 01:40:17 -0500366 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500367 lg2::error("Unsupported Type, TYPE:{TYPE}", "TYPE",
368 certificateTypeToString(certType));
Marri Devender Raocd30c492019-06-12 01:40:17 -0500369 elog<InternalFailure>();
370 }
Nan Zhoue869bb62021-12-30 11:34:42 -0800371 else
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600372 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800373 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600374 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500375
Nan Zhoue869bb62021-12-30 11:34:42 -0800376 copyCertificate(certSrcFilePath, certFilePath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100377 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600378
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100379 // Keep certificate ID
Nan Zhoue869bb62021-12-30 11:34:42 -0800380 certId = generateCertId(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200381
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600382 // Parse the certificate file and populate properties
Nan Zhoue869bb62021-12-30 11:34:42 -0800383 populateProperties(*cert);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500384
385 // restart watch
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800386 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500387 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800388 certWatch->startWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500389 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600390}
391
Willy Tu698a5742022-09-23 21:33:01 +0000392void Certificate::install(X509_STORE& x509Store, const std::string& pem,
393 bool restore)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800394{
Willy Tu698a5742022-09-23 21:33:01 +0000395 if (restore)
396 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500397 lg2::debug("Certificate install, PEM_STR:{PEM_STR}", "PEM_STR", pem);
Willy Tu698a5742022-09-23 21:33:01 +0000398 }
399 else
400 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500401 lg2::info("Certificate install, PEM_STR:{PEM_STR} ", "PEM_STR", pem);
Willy Tu698a5742022-09-23 21:33:01 +0000402 }
Nan Zhou6ec13c82021-12-30 11:34:50 -0800403
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000404 if (certType != CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800405 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500406 lg2::error("Bulk install error: Unsupported Type; only authority "
407 "supports bulk install, TYPE:{TYPE}",
408 "TYPE", certificateTypeToString(certType));
Nan Zhou6ec13c82021-12-30 11:34:50 -0800409 elog<InternalFailure>();
410 }
411
412 // stop watch for user initiated certificate install
413 if (certWatch)
414 {
415 certWatch->stopWatch();
416 }
417
418 // Load Certificate file into the X509 structure.
419 internal::X509Ptr cert = parseCert(pem);
420 // Perform validation; no type specific compare keys function
421 validateCertificateAgainstStore(x509Store, *cert);
422 validateCertificateStartDate(*cert);
423 validateCertificateInSSLContext(*cert);
424
425 // Copy the PEM to the installation path
426 dumpCertificate(pem, certFilePath);
427 storageUpdate();
428 // Keep certificate ID
429 certId = generateCertId(*cert);
430 // Parse the certificate file and populate properties
431 populateProperties(*cert);
432 // restart watch
433 if (certWatch)
434 {
435 certWatch->startWatch();
436 }
437}
438
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600439void Certificate::populateProperties()
440{
Nan Zhoue869bb62021-12-30 11:34:42 -0800441 internal::X509Ptr cert = loadCert(certInstallPath);
442 populateProperties(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200443}
444
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100445std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200446{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100447 return certId;
448}
449
450bool Certificate::isSame(const std::string& certPath)
451{
Nan Zhoue869bb62021-12-30 11:34:42 -0800452 internal::X509Ptr cert = loadCert(certPath);
453 return getCertId() == generateCertId(*cert);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100454}
455
456void Certificate::storageUpdate()
457{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000458 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100459 {
460 // Create symbolic link in the certificate directory
461 std::string certFileX509Path;
462 try
463 {
464 if (!certFilePath.empty() &&
465 fs::is_regular_file(fs::path(certFilePath)))
466 {
467 certFileX509Path =
468 generateAuthCertFileX509Path(certFilePath, certInstallPath);
469 fs::create_symlink(fs::path(certFilePath),
470 fs::path(certFileX509Path));
471 }
472 }
473 catch (const std::exception& e)
474 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500475 lg2::error("Failed to create symlink for certificate, ERR:{ERR},"
476 "FILE:{FILE}, SYMLINK:{SYMLINK}",
477 "ERR", e, "FILE", certFilePath, "SYMLINK",
478 certFileX509Path);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100479 elog<InternalFailure>();
480 }
481 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200482}
483
Nan Zhoue869bb62021-12-30 11:34:42 -0800484void Certificate::populateProperties(X509& cert)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200485{
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600486 // Update properties if no error thrown
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800487 BIOMemPtr certBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800488 PEM_write_bio_X509(certBio.get(), &cert);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800489 BufMemPtr certBuf(BUF_MEM_new(), BUF_MEM_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600490 BUF_MEM* buf = certBuf.get();
491 BIO_get_mem_ptr(certBio.get(), &buf);
492 std::string certStr(buf->data, buf->length);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800493 certificateString(certStr);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600494
495 static const int maxKeySize = 4096;
496 char subBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800497 BIOMemPtr subBio(BIO_new(BIO_s_mem()), BIO_free);
Manojkiran Eda5d4f7932024-06-17 11:49:21 +0530498 // This pointer cannot be freed independently.
Nan Zhoue869bb62021-12-30 11:34:42 -0800499 X509_NAME* sub = X509_get_subject_name(&cert);
Marri Devender Raodec58772019-06-11 03:10:00 -0500500 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
501 BIO_read(subBio.get(), subBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800502 subject(subBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600503
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600504 char issuerBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800505 BIOMemPtr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
Manojkiran Eda5d4f7932024-06-17 11:49:21 +0530506 // This pointer cannot be freed independently.
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000507 X509_NAME* issuerName = X509_get_issuer_name(&cert);
508 X509_NAME_print_ex(issuerBio.get(), issuerName, 0, XN_FLAG_SEP_COMMA_PLUS);
Marri Devender Raodec58772019-06-11 03:10:00 -0500509 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800510 issuer(issuerBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600511
512 std::vector<std::string> keyUsageList;
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600513
514 // Go through each usage in the bit string and convert to
515 // corresponding string value
Jayanth Othayothcb1ee9d2024-11-24 22:23:33 -0600516 ASN1_BIT_STRING* usage = static_cast<ASN1_BIT_STRING*>(
517 X509_get_ext_d2i(&cert, NID_key_usage, nullptr, nullptr));
518 if (usage != nullptr)
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600519 {
520 for (auto i = 0; i < usage->length; ++i)
521 {
522 for (auto& x : keyUsageToRfStr)
523 {
524 if (x.first & usage->data[i])
525 {
526 keyUsageList.push_back(x.second);
527 break;
528 }
529 }
530 }
531 }
532
Jayanth Othayothcb1ee9d2024-11-24 22:23:33 -0600533 EXTENDED_KEY_USAGE* extUsage = static_cast<EXTENDED_KEY_USAGE*>(
534 X509_get_ext_d2i(&cert, NID_ext_key_usage, nullptr, nullptr));
535 if (extUsage == nullptr)
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600536 {
537 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
538 {
539 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
540 sk_ASN1_OBJECT_value(extUsage, i))]);
541 }
542 }
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800543 keyUsage(keyUsageList);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600544
545 int days = 0;
546 int secs = 0;
547
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800548 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800549 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
550 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600551
Nan Zhoucf811c42021-12-02 14:56:17 -0800552 static const uint64_t dayToSeconds = 24 * 60 * 60;
Nan Zhoue869bb62021-12-30 11:34:42 -0800553 ASN1_TIME* notAfter = X509_get_notAfter(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600554 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800555 validNotAfter((days * dayToSeconds) + secs);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600556
Nan Zhoue869bb62021-12-30 11:34:42 -0800557 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600558 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800559 validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600560}
561
Marri Devender Raocd30c492019-06-12 01:40:17 -0500562void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
563{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800564 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500565 if (!keyBio)
566 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500567 lg2::error("Error occurred during BIO_s_file call, FILE:{FILE}", "FILE",
568 filePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500569 elog<InternalFailure>();
570 }
571 BIO_read_filename(keyBio.get(), filePath.c_str());
572
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800573 EVPPkeyPtr priKey(
Marri Devender Raocd30c492019-06-12 01:40:17 -0500574 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
575 ::EVP_PKEY_free);
576 if (!priKey)
577 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500578 lg2::info("Private key not present in file, FILE:{FILE}", "FILE",
579 filePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500580 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
Nan Zhou718eef32021-12-28 11:03:30 -0800581 privateKeyFile = privateKeyFile / defaultPrivateKeyFileName;
Marri Devender Raocd30c492019-06-12 01:40:17 -0500582 if (!fs::exists(privateKeyFile))
583 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500584 lg2::error("Private key file is not found, FILE:{FILE}", "FILE",
585 privateKeyFile);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500586 elog<InternalFailure>();
587 }
588
589 std::ifstream privKeyFileStream;
590 std::ofstream certFileStream;
Patrick Williamsa2f68d82024-08-16 15:21:36 -0400591 privKeyFileStream.exceptions(
592 std::ifstream::failbit | std::ifstream::badbit |
593 std::ifstream::eofbit);
594 certFileStream.exceptions(
595 std::ofstream::failbit | std::ofstream::badbit |
596 std::ofstream::eofbit);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500597 try
598 {
599 privKeyFileStream.open(privateKeyFile);
600 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500601 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500602 certFileStream << privKeyFileStream.rdbuf() << std::flush;
603 privKeyFileStream.close();
604 certFileStream.close();
605 }
606 catch (const std::exception& e)
607 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500608 lg2::error(
609 "Failed to append private key, ERR:{ERR}, SRC:{SRC}, DST:{DST}",
610 "ERR", e, "SRC", privateKeyFile, "DST", filePath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500611 elog<InternalFailure>();
612 }
613 }
614}
615
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600616bool Certificate::compareKeys(const std::string& filePath)
617{
Ravi Tejaf2646272023-09-30 13:00:55 -0500618 lg2::info("Certificate compareKeys, FILEPATH:{FILEPATH}", "FILEPATH",
619 filePath);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800620 internal::X509Ptr cert(X509_new(), ::X509_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600621 if (!cert)
622 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500623 lg2::error(
624 "Error occurred during X509_new call, FILE:{FILE}, ERRCODE:{ERRCODE}",
625 "FILE", filePath, "ERRCODE", ERR_get_error());
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600626 elog<InternalFailure>();
627 }
628
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800629 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600630 if (!bioCert)
631 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500632 lg2::error("Error occurred during BIO_new_file call, FILE:{FILE}",
633 "FILE", filePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600634 elog<InternalFailure>();
635 }
636
637 X509* x509 = cert.get();
638 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
639
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800640 EVPPkeyPtr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600641 if (!pubKey)
642 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500643 lg2::error(
644 "Error occurred during X509_get_pubkey, FILE:{FILE}, ERRCODE:{ERRCODE}",
645 "FILE", filePath, "ERRCODE", ERR_get_error());
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800646 elog<InvalidCertificateError>(
647 InvalidCertificate::REASON("Failed to get public key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600648 }
649
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800650 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600651 if (!keyBio)
652 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500653 lg2::error("Error occurred during BIO_s_file call, FILE:{FILE}", "FILE",
654 filePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600655 elog<InternalFailure>();
656 }
657 BIO_read_filename(keyBio.get(), filePath.c_str());
658
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800659 EVPPkeyPtr priKey(
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600660 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
661 ::EVP_PKEY_free);
662 if (!priKey)
663 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500664 lg2::error(
665 "Error occurred during PEM_read_bio_PrivateKey, FILE:{FILE}, ERRCODE:{ERRCODE}",
666 "FILE", filePath, "ERRCODE", ERR_get_error());
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800667 elog<InvalidCertificateError>(
668 InvalidCertificate::REASON("Failed to get private key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600669 }
670
Patrick Williams55ceaa22021-12-14 06:52:26 -0600671#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600672 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
Patrick Williams55ceaa22021-12-14 06:52:26 -0600673#else
674 int32_t rc = EVP_PKEY_eq(priKey.get(), pubKey.get());
675#endif
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600676 if (rc != 1)
677 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500678 lg2::error(
679 "Private key is not matching with Certificate, FILE:{FILE}, ERRCODE:{ERRCODE}",
680 "FILE", filePath, "ERRCODE", rc);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600681 return false;
682 }
683 return true;
684}
685
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200686void Certificate::delete_()
687{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100688 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200689}
Nan Zhou6ec13c82021-12-30 11:34:50 -0800690
691std::string Certificate::getObjectPath()
692{
693 return objectPath;
694}
695
696std::string Certificate::getCertFilePath()
697{
698 return certFilePath;
699}
700
701void Certificate::setCertFilePath(const std::string& path)
702{
703 certFilePath = path;
704}
705
706void Certificate::setCertInstallPath(const std::string& path)
707{
708 certInstallPath = path;
709}
710
Nan Zhoue1289ad2021-12-28 11:02:56 -0800711} // namespace phosphor::certs