blob: 9628b555f11a2bdba04d530823d4811368c460b8 [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
Nan Zhou014be0b2021-12-28 18:00:14 -080019#include <cstdint>
20#include <cstdio>
21#include <cstdlib>
Nan Zhou014be0b2021-12-28 18:00:14 -080022#include <exception>
23#include <filesystem>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060024#include <fstream>
Nan Zhou014be0b2021-12-28 18:00:14 -080025#include <map>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060026#include <phosphor-logging/elog-errors.hpp>
Nan Zhou014be0b2021-12-28 18:00:14 -080027#include <phosphor-logging/elog.hpp>
28#include <phosphor-logging/log.hpp>
29#include <utility>
30#include <vector>
31#include <watch.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050032#include <xyz/openbmc_project/Certs/error.hpp>
Marri Devender Rao6ceec402019-02-01 03:15:19 -060033#include <xyz/openbmc_project/Common/error.hpp>
Marri Devender Rao13bf74e2019-03-26 01:52:17 -050034
Nan Zhoue1289ad2021-12-28 11:02:56 -080035namespace phosphor::certs
Marri Devender Rao6ceec402019-02-01 03:15:19 -060036{
Nan Zhoucf06ccd2021-12-28 16:25:45 -080037
38namespace
39{
40namespace fs = std::filesystem;
41using ::phosphor::logging::elog;
42using ::phosphor::logging::entry;
43using ::phosphor::logging::level;
44using ::phosphor::logging::log;
45using ::phosphor::logging::report;
46using InvalidCertificateError =
47 ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
48using ::phosphor::logging::xyz::openbmc_project::Certs::InvalidCertificate;
49using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
50
Marri Devender Rao6ceec402019-02-01 03:15:19 -060051// RAII support for openSSL functions.
Nan Zhoucf06ccd2021-12-28 16:25:45 -080052using BIOMemPtr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
53using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
Nan Zhoucf06ccd2021-12-28 16:25:45 -080054using ASN1TimePtr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
55using EVPPkeyPtr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
56using BufMemPtr = std::unique_ptr<BUF_MEM, decltype(&::BUF_MEM_free)>;
Marri Devender Rao6ceec402019-02-01 03:15:19 -060057
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -060058// Refer to schema 2018.3
59// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
60// supported KeyUsage types in redfish
61// Refer to
62// https://github.com/openssl/openssl/blob/master/include/openssl/x509v3.h for
63// key usage bit fields
64std::map<uint8_t, std::string> keyUsageToRfStr = {
65 {KU_DIGITAL_SIGNATURE, "DigitalSignature"},
66 {KU_NON_REPUDIATION, "NonRepudiation"},
67 {KU_KEY_ENCIPHERMENT, "KeyEncipherment"},
68 {KU_DATA_ENCIPHERMENT, "DataEncipherment"},
69 {KU_KEY_AGREEMENT, "KeyAgreement"},
70 {KU_KEY_CERT_SIGN, "KeyCertSign"},
71 {KU_CRL_SIGN, "CRLSigning"},
72 {KU_ENCIPHER_ONLY, "EncipherOnly"},
73 {KU_DECIPHER_ONLY, "DecipherOnly"}};
74
75// Refer to schema 2018.3
76// http://redfish.dmtf.org/schemas/v1/Certificate.json#/definitions/KeyUsage for
77// supported Extended KeyUsage types in redfish
78std::map<uint8_t, std::string> extendedKeyUsageToRfStr = {
79 {NID_server_auth, "ServerAuthentication"},
80 {NID_client_auth, "ClientAuthentication"},
81 {NID_email_protect, "EmailProtection"},
82 {NID_OCSP_sign, "OCSPSigning"},
83 {NID_ad_timeStamping, "Timestamping"},
84 {NID_code_sign, "CodeSigning"}};
85
Nan Zhoue869bb62021-12-30 11:34:42 -080086/**
Nan Zhou6ec13c82021-12-30 11:34:50 -080087 * @brief Dumps the PEM encoded certificate to installFilePath
Nan Zhoue869bb62021-12-30 11:34:42 -080088 *
Nan Zhou6ec13c82021-12-30 11:34:50 -080089 * @param[in] pem - PEM encoded X509 certificate buffer.
90 * @param[in] certFilePath - Path to the destination file.
Nan Zhoue869bb62021-12-30 11:34:42 -080091 *
92 * @return void
93 */
Nan Zhou6ec13c82021-12-30 11:34:50 -080094
95void dumpCertificate(const std::string& pem, const std::string& certFilePath)
96{
97 std::ofstream outputCertFileStream;
98
99 outputCertFileStream.exceptions(
100 std::ofstream::failbit | std::ofstream::badbit | std::ofstream::eofbit);
101
102 try
103 {
104 outputCertFileStream.open(certFilePath, std::ios::out);
105 outputCertFileStream << pem << "\n" << std::flush;
106 outputCertFileStream.close();
107 }
108 catch (const std::exception& e)
109 {
110 log<level::ERR>("Failed to dump certificate", entry("ERR=%s", e.what()),
111 entry("SRC_PEM=%s", pem.c_str()),
112 entry("DST=%s", certFilePath.c_str()));
113 elog<InternalFailure>();
114 }
115}
116} // namespace
117
118void Certificate::copyCertificate(const std::string& certSrcFilePath,
119 const std::string& certFilePath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200120{
Nan Zhoue869bb62021-12-30 11:34:42 -0800121 // Copy the certificate to the installation path
122 // During bootup will be parsing existing file so no need to
123 // copy it.
124 if (certSrcFilePath != certFilePath)
125 {
126 std::ifstream inputCertFileStream;
127 std::ofstream outputCertFileStream;
128 inputCertFileStream.exceptions(std::ifstream::failbit |
129 std::ifstream::badbit |
130 std::ifstream::eofbit);
131 outputCertFileStream.exceptions(std::ofstream::failbit |
132 std::ofstream::badbit |
133 std::ofstream::eofbit);
134 try
135 {
136 inputCertFileStream.open(certSrcFilePath);
137 outputCertFileStream.open(certFilePath, std::ios::out);
138 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
139 inputCertFileStream.close();
140 outputCertFileStream.close();
141 }
142 catch (const std::exception& e)
143 {
144 log<level::ERR>("Failed to copy certificate",
145 entry("ERR=%s", e.what()),
146 entry("SRC=%s", certSrcFilePath.c_str()),
147 entry("DST=%s", certFilePath.c_str()));
148 elog<InternalFailure>();
149 }
150 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100151}
152
153std::string
154 Certificate::generateUniqueFilePath(const std::string& directoryPath)
155{
Nan Zhoucfb58022021-12-28 11:02:26 -0800156 char* filePath = tempnam(directoryPath.c_str(), nullptr);
157 if (filePath == nullptr)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100158 {
159 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800160 "Error occurred while creating random certificate file path",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100161 entry("DIR=%s", directoryPath.c_str()));
162 elog<InternalFailure>();
163 }
164 std::string filePathStr(filePath);
165 free(filePath);
166 return filePathStr;
167}
168
169std::string Certificate::generateAuthCertFileX509Path(
170 const std::string& certSrcFilePath, const std::string& certDstDirPath)
171{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800172 const internal::X509Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200173 unsigned long hash = X509_subject_name_hash(cert.get());
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000174 static constexpr auto certHashLength = 9;
175 char hashBuf[certHashLength];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200176
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000177 snprintf(hashBuf, certHashLength, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200178
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100179 const std::string certHash(hashBuf);
Nan Zhou718eef32021-12-28 11:03:30 -0800180 for (size_t i = 0; i < maxNumAuthorityCertificates; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100181 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100182 const std::string certDstFileX509Path =
183 certDstDirPath + "/" + certHash + "." + std::to_string(i);
184 if (!fs::exists(certDstFileX509Path))
185 {
186 return certDstFileX509Path;
187 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100188 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200189
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100190 log<level::ERR>("Authority certificate x509 file path already used",
191 entry("DIR=%s", certDstDirPath.c_str()));
192 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100193}
194
195std::string
196 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
197{
198 // If there is a certificate file path (which means certificate replacement
199 // is doing) use it (do not create new one)
200 if (!certFilePath.empty())
201 {
202 return certFilePath;
203 }
204 // If source certificate file is located in the certificates directory use
205 // it (do not create new one)
206 else if (fs::path(certSrcFilePath).parent_path().string() ==
207 certInstallPath)
208 {
209 return certSrcFilePath;
210 }
211 // Otherwise generate new file name/path
212 else
213 {
214 return generateUniqueFilePath(certInstallPath);
215 }
216}
217
218std::string
219 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
220{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000221 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100222 {
223 return generateAuthCertFilePath(certSrcFilePath);
224 }
225 else
226 {
227 return certInstallPath;
228 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200229}
230
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500231Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800232 CertificateType type, const std::string& installPath,
233 const std::string& uploadPath, Watch* watch,
Willy Tu698a5742022-09-23 21:33:01 +0000234 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500235 internal::CertificateInterface(
236 bus, objPath.c_str(),
237 internal::CertificateInterface::action::defer_emit),
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800238 objectPath(objPath), certType(type), certInstallPath(installPath),
239 certWatch(watch), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600240{
241 auto installHelper = [this](const auto& filePath) {
242 if (!compareKeys(filePath))
243 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800244 elog<InvalidCertificateError>(InvalidCertificate::REASON(
245 "Private key does not match the Certificate"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600246 };
247 };
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000248 typeFuncMap[CertificateType::server] = installHelper;
249 typeFuncMap[CertificateType::client] = installHelper;
250 typeFuncMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500251
252 auto appendPrivateKey = [this](const std::string& filePath) {
253 checkAndAppendPrivateKey(filePath);
254 };
255
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000256 appendKeyMap[CertificateType::server] = appendPrivateKey;
257 appendKeyMap[CertificateType::client] = appendPrivateKey;
258 appendKeyMap[CertificateType::authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500259
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100260 // Generate certificate file path
261 certFilePath = generateCertFilePath(uploadPath);
262
Marri Devender Raocd30c492019-06-12 01:40:17 -0500263 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000264 install(uploadPath, restore);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500265
Marri Devender Raoedd11312019-02-27 08:45:10 -0600266 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600267}
268
Patrick Williamsb3dbfb32022-07-22 19:26:57 -0500269Certificate::Certificate(sdbusplus::bus_t& bus, const std::string& objPath,
Nan Zhou6ec13c82021-12-30 11:34:50 -0800270 const CertificateType& type,
271 const std::string& installPath, X509_STORE& x509Store,
272 const std::string& pem, Watch* watchPtr,
Willy Tu698a5742022-09-23 21:33:01 +0000273 Manager& parent, bool restore) :
Patrick Williamsebd21ba2022-04-05 14:58:53 -0500274 internal::CertificateInterface(
275 bus, objPath.c_str(),
276 internal::CertificateInterface::action::defer_emit),
Nan Zhou6ec13c82021-12-30 11:34:50 -0800277 objectPath(objPath), certType(type), certInstallPath(installPath),
278 certWatch(watchPtr), manager(parent)
279{
280 // Generate certificate file path
281 certFilePath = generateUniqueFilePath(installPath);
282
283 // install the certificate
Willy Tu698a5742022-09-23 21:33:01 +0000284 install(x509Store, pem, restore);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800285
286 this->emit_object_added();
287}
288
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600289Certificate::~Certificate()
290{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100291 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600292 {
293 log<level::INFO>("Certificate file not found!",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100294 entry("PATH=%s", certFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600295 }
296}
297
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500298void Certificate::replace(const std::string filePath)
299{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100300 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500301}
302
Willy Tu698a5742022-09-23 21:33:01 +0000303void Certificate::install(const std::string& certSrcFilePath, bool restore)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600304{
Willy Tu698a5742022-09-23 21:33:01 +0000305 if (restore)
306 {
307 log<level::DEBUG>("Certificate install ",
308 entry("FILEPATH=%s", certSrcFilePath.c_str()));
309 }
310 else
311 {
312 log<level::INFO>("Certificate install ",
313 entry("FILEPATH=%s", certSrcFilePath.c_str()));
314 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600315
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500316 // stop watch for user initiated certificate install
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800317 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500318 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800319 certWatch->stopWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500320 }
321
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600322 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100323 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600324 if (!fs::exists(file))
325 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100326 log<level::ERR>("File is Missing",
327 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600328 elog<InternalFailure>();
329 }
330
331 try
332 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100333 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600334 {
335 // file is empty
336 log<level::ERR>("File is empty",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100337 entry("FILE=%s", certSrcFilePath.c_str()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800338 elog<InvalidCertificateError>(
339 InvalidCertificate::REASON("File is empty"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600340 }
341 }
342 catch (const fs::filesystem_error& e)
343 {
344 // Log Error message
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100345 log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600346 elog<InternalFailure>();
347 }
348
Nan Zhoue869bb62021-12-30 11:34:42 -0800349 X509StorePtr x509Store = getX509Store(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600350
Nan Zhoubf3cf752021-12-28 11:02:07 -0800351 // Load Certificate file into the X509 structure.
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800352 internal::X509Ptr cert = loadCert(certSrcFilePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800353
354 // Perform validation
355 validateCertificateAgainstStore(*x509Store, *cert);
356 validateCertificateStartDate(*cert);
357 validateCertificateInSSLContext(*cert);
358
359 // Invoke type specific append private key function.
360 if (auto it = appendKeyMap.find(certType); it == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600361 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800362 log<level::ERR>("Unsupported Type",
363 entry("TYPE=%s", certificateTypeToString(certType)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600364 elog<InternalFailure>();
365 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600366 else
367 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800368 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600369 }
370
Marri Devender Raocd30c492019-06-12 01:40:17 -0500371 // Invoke type specific compare keys function.
Nan Zhoue869bb62021-12-30 11:34:42 -0800372 if (auto it = typeFuncMap.find(certType); it == typeFuncMap.end())
Marri Devender Raocd30c492019-06-12 01:40:17 -0500373 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800374 log<level::ERR>("Unsupported Type",
375 entry("TYPE=%s", certificateTypeToString(certType)));
Marri Devender Raocd30c492019-06-12 01:40:17 -0500376 elog<InternalFailure>();
377 }
Nan Zhoue869bb62021-12-30 11:34:42 -0800378 else
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600379 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800380 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600381 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500382
Nan Zhoue869bb62021-12-30 11:34:42 -0800383 copyCertificate(certSrcFilePath, certFilePath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100384 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600385
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100386 // Keep certificate ID
Nan Zhoue869bb62021-12-30 11:34:42 -0800387 certId = generateCertId(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200388
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600389 // Parse the certificate file and populate properties
Nan Zhoue869bb62021-12-30 11:34:42 -0800390 populateProperties(*cert);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500391
392 // restart watch
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800393 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500394 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800395 certWatch->startWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500396 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600397}
398
Willy Tu698a5742022-09-23 21:33:01 +0000399void Certificate::install(X509_STORE& x509Store, const std::string& pem,
400 bool restore)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800401{
Willy Tu698a5742022-09-23 21:33:01 +0000402 if (restore)
403 {
404 log<level::DEBUG>("Certificate install ",
405 entry("PEM_STR=%s", pem.data()));
406 }
407 else
408 {
409 log<level::INFO>("Certificate install ",
410 entry("PEM_STR=%s", pem.data()));
411 }
Nan Zhou6ec13c82021-12-30 11:34:50 -0800412
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000413 if (certType != CertificateType::authority)
Nan Zhou6ec13c82021-12-30 11:34:50 -0800414 {
415 log<level::ERR>("Bulk install error: Unsupported Type; only authority "
416 "supports bulk install",
417 entry("TYPE=%s", certificateTypeToString(certType)));
418 elog<InternalFailure>();
419 }
420
421 // stop watch for user initiated certificate install
422 if (certWatch)
423 {
424 certWatch->stopWatch();
425 }
426
427 // Load Certificate file into the X509 structure.
428 internal::X509Ptr cert = parseCert(pem);
429 // Perform validation; no type specific compare keys function
430 validateCertificateAgainstStore(x509Store, *cert);
431 validateCertificateStartDate(*cert);
432 validateCertificateInSSLContext(*cert);
433
434 // Copy the PEM to the installation path
435 dumpCertificate(pem, certFilePath);
436 storageUpdate();
437 // Keep certificate ID
438 certId = generateCertId(*cert);
439 // Parse the certificate file and populate properties
440 populateProperties(*cert);
441 // restart watch
442 if (certWatch)
443 {
444 certWatch->startWatch();
445 }
446}
447
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600448void Certificate::populateProperties()
449{
Nan Zhoue869bb62021-12-30 11:34:42 -0800450 internal::X509Ptr cert = loadCert(certInstallPath);
451 populateProperties(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200452}
453
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100454std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200455{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100456 return certId;
457}
458
459bool Certificate::isSame(const std::string& certPath)
460{
Nan Zhoue869bb62021-12-30 11:34:42 -0800461 internal::X509Ptr cert = loadCert(certPath);
462 return getCertId() == generateCertId(*cert);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100463}
464
465void Certificate::storageUpdate()
466{
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000467 if (certType == CertificateType::authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100468 {
469 // Create symbolic link in the certificate directory
470 std::string certFileX509Path;
471 try
472 {
473 if (!certFilePath.empty() &&
474 fs::is_regular_file(fs::path(certFilePath)))
475 {
476 certFileX509Path =
477 generateAuthCertFileX509Path(certFilePath, certInstallPath);
478 fs::create_symlink(fs::path(certFilePath),
479 fs::path(certFileX509Path));
480 }
481 }
482 catch (const std::exception& e)
483 {
484 log<level::ERR>("Failed to create symlink for certificate",
485 entry("ERR=%s", e.what()),
486 entry("FILE=%s", certFilePath.c_str()),
487 entry("SYMLINK=%s", certFileX509Path.c_str()));
488 elog<InternalFailure>();
489 }
490 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200491}
492
Nan Zhoue869bb62021-12-30 11:34:42 -0800493void Certificate::populateProperties(X509& cert)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200494{
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600495 // Update properties if no error thrown
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800496 BIOMemPtr certBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800497 PEM_write_bio_X509(certBio.get(), &cert);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800498 BufMemPtr certBuf(BUF_MEM_new(), BUF_MEM_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600499 BUF_MEM* buf = certBuf.get();
500 BIO_get_mem_ptr(certBio.get(), &buf);
501 std::string certStr(buf->data, buf->length);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800502 certificateString(certStr);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600503
504 static const int maxKeySize = 4096;
505 char subBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800506 BIOMemPtr subBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800507 // This pointer cannot be freed independantly.
508 X509_NAME* sub = X509_get_subject_name(&cert);
Marri Devender Raodec58772019-06-11 03:10:00 -0500509 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
510 BIO_read(subBio.get(), subBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800511 subject(subBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600512
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600513 char issuerBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800514 BIOMemPtr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800515 // This pointer cannot be freed independantly.
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000516 X509_NAME* issuerName = X509_get_issuer_name(&cert);
517 X509_NAME_print_ex(issuerBio.get(), issuerName, 0, XN_FLAG_SEP_COMMA_PLUS);
Marri Devender Raodec58772019-06-11 03:10:00 -0500518 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800519 issuer(issuerBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600520
521 std::vector<std::string> keyUsageList;
522 ASN1_BIT_STRING* usage;
523
524 // Go through each usage in the bit string and convert to
525 // corresponding string value
526 if ((usage = static_cast<ASN1_BIT_STRING*>(
Nan Zhoue869bb62021-12-30 11:34:42 -0800527 X509_get_ext_d2i(&cert, NID_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600528 {
529 for (auto i = 0; i < usage->length; ++i)
530 {
531 for (auto& x : keyUsageToRfStr)
532 {
533 if (x.first & usage->data[i])
534 {
535 keyUsageList.push_back(x.second);
536 break;
537 }
538 }
539 }
540 }
541
542 EXTENDED_KEY_USAGE* extUsage;
Nan Zhoue869bb62021-12-30 11:34:42 -0800543 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
544 X509_get_ext_d2i(&cert, NID_ext_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600545 {
546 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
547 {
548 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
549 sk_ASN1_OBJECT_value(extUsage, i))]);
550 }
551 }
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800552 keyUsage(keyUsageList);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600553
554 int days = 0;
555 int secs = 0;
556
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800557 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800558 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
559 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600560
Nan Zhoucf811c42021-12-02 14:56:17 -0800561 static const uint64_t dayToSeconds = 24 * 60 * 60;
Nan Zhoue869bb62021-12-30 11:34:42 -0800562 ASN1_TIME* notAfter = X509_get_notAfter(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600563 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800564 validNotAfter((days * dayToSeconds) + secs);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600565
Nan Zhoue869bb62021-12-30 11:34:42 -0800566 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600567 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800568 validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600569}
570
Marri Devender Raocd30c492019-06-12 01:40:17 -0500571void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
572{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800573 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500574 if (!keyBio)
575 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800576 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Raocd30c492019-06-12 01:40:17 -0500577 entry("FILE=%s", filePath.c_str()));
578 elog<InternalFailure>();
579 }
580 BIO_read_filename(keyBio.get(), filePath.c_str());
581
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800582 EVPPkeyPtr priKey(
Marri Devender Raocd30c492019-06-12 01:40:17 -0500583 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
584 ::EVP_PKEY_free);
585 if (!priKey)
586 {
587 log<level::INFO>("Private key not present in file",
588 entry("FILE=%s", filePath.c_str()));
589 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
Nan Zhou718eef32021-12-28 11:03:30 -0800590 privateKeyFile = privateKeyFile / defaultPrivateKeyFileName;
Marri Devender Raocd30c492019-06-12 01:40:17 -0500591 if (!fs::exists(privateKeyFile))
592 {
593 log<level::ERR>("Private key file is not found",
594 entry("FILE=%s", privateKeyFile.c_str()));
595 elog<InternalFailure>();
596 }
597
598 std::ifstream privKeyFileStream;
599 std::ofstream certFileStream;
600 privKeyFileStream.exceptions(std::ifstream::failbit |
601 std::ifstream::badbit |
602 std::ifstream::eofbit);
603 certFileStream.exceptions(std::ofstream::failbit |
604 std::ofstream::badbit |
605 std::ofstream::eofbit);
606 try
607 {
608 privKeyFileStream.open(privateKeyFile);
609 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500610 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500611 certFileStream << privKeyFileStream.rdbuf() << std::flush;
612 privKeyFileStream.close();
613 certFileStream.close();
614 }
615 catch (const std::exception& e)
616 {
617 log<level::ERR>("Failed to append private key",
618 entry("ERR=%s", e.what()),
619 entry("SRC=%s", privateKeyFile.c_str()),
620 entry("DST=%s", filePath.c_str()));
621 elog<InternalFailure>();
622 }
623 }
624}
625
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600626bool Certificate::compareKeys(const std::string& filePath)
627{
628 log<level::INFO>("Certificate compareKeys",
629 entry("FILEPATH=%s", filePath.c_str()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800630 internal::X509Ptr cert(X509_new(), ::X509_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600631 if (!cert)
632 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800633 log<level::ERR>("Error occurred during X509_new call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600634 entry("FILE=%s", filePath.c_str()),
635 entry("ERRCODE=%lu", ERR_get_error()));
636 elog<InternalFailure>();
637 }
638
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800639 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600640 if (!bioCert)
641 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800642 log<level::ERR>("Error occurred during BIO_new_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600643 entry("FILE=%s", filePath.c_str()));
644 elog<InternalFailure>();
645 }
646
647 X509* x509 = cert.get();
648 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
649
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800650 EVPPkeyPtr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600651 if (!pubKey)
652 {
653 log<level::ERR>("Error occurred during X509_get_pubkey",
654 entry("FILE=%s", filePath.c_str()),
655 entry("ERRCODE=%lu", ERR_get_error()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800656 elog<InvalidCertificateError>(
657 InvalidCertificate::REASON("Failed to get public key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600658 }
659
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800660 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600661 if (!keyBio)
662 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800663 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600664 entry("FILE=%s", filePath.c_str()));
665 elog<InternalFailure>();
666 }
667 BIO_read_filename(keyBio.get(), filePath.c_str());
668
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800669 EVPPkeyPtr priKey(
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600670 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
671 ::EVP_PKEY_free);
672 if (!priKey)
673 {
674 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
675 entry("FILE=%s", filePath.c_str()),
676 entry("ERRCODE=%lu", ERR_get_error()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800677 elog<InvalidCertificateError>(
678 InvalidCertificate::REASON("Failed to get private key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600679 }
680
Patrick Williams55ceaa22021-12-14 06:52:26 -0600681#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600682 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
Patrick Williams55ceaa22021-12-14 06:52:26 -0600683#else
684 int32_t rc = EVP_PKEY_eq(priKey.get(), pubKey.get());
685#endif
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600686 if (rc != 1)
687 {
688 log<level::ERR>("Private key is not matching with Certificate",
689 entry("FILE=%s", filePath.c_str()),
690 entry("ERRCODE=%d", rc));
691 return false;
692 }
693 return true;
694}
695
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200696void Certificate::delete_()
697{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100698 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200699}
Nan Zhou6ec13c82021-12-30 11:34:50 -0800700
701std::string Certificate::getObjectPath()
702{
703 return objectPath;
704}
705
706std::string Certificate::getCertFilePath()
707{
708 return certFilePath;
709}
710
711void Certificate::setCertFilePath(const std::string& path)
712{
713 certFilePath = path;
714}
715
716void Certificate::setCertInstallPath(const std::string& path)
717{
718 certInstallPath = path;
719}
720
Nan Zhoue1289ad2021-12-28 11:02:56 -0800721} // namespace phosphor::certs