blob: 66ea69800477779622b7e86bc96826c87f058185 [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/**
87 * @brief Copies the certificate from sourceFilePath to installFilePath
88 *
89 * @param[in] sourceFilePath - Path to the source file.
90 * @param[in] sourceFilePath - Path to the destination file.
91 *
92 * @return void
93 */
94void copyCertificate(const std::string& certSrcFilePath,
95 const std::string& certFilePath)
Kowalski, Kamildb029c92019-07-08 17:09:39 +020096{
Nan Zhoue869bb62021-12-30 11:34:42 -080097 // Copy the certificate to the installation path
98 // During bootup will be parsing existing file so no need to
99 // copy it.
100 if (certSrcFilePath != certFilePath)
101 {
102 std::ifstream inputCertFileStream;
103 std::ofstream outputCertFileStream;
104 inputCertFileStream.exceptions(std::ifstream::failbit |
105 std::ifstream::badbit |
106 std::ifstream::eofbit);
107 outputCertFileStream.exceptions(std::ofstream::failbit |
108 std::ofstream::badbit |
109 std::ofstream::eofbit);
110 try
111 {
112 inputCertFileStream.open(certSrcFilePath);
113 outputCertFileStream.open(certFilePath, std::ios::out);
114 outputCertFileStream << inputCertFileStream.rdbuf() << std::flush;
115 inputCertFileStream.close();
116 outputCertFileStream.close();
117 }
118 catch (const std::exception& e)
119 {
120 log<level::ERR>("Failed to copy certificate",
121 entry("ERR=%s", e.what()),
122 entry("SRC=%s", certSrcFilePath.c_str()),
123 entry("DST=%s", certFilePath.c_str()));
124 elog<InternalFailure>();
125 }
126 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100127}
Nan Zhoue869bb62021-12-30 11:34:42 -0800128} // namespace
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100129
130std::string
131 Certificate::generateUniqueFilePath(const std::string& directoryPath)
132{
Nan Zhoucfb58022021-12-28 11:02:26 -0800133 char* filePath = tempnam(directoryPath.c_str(), nullptr);
134 if (filePath == nullptr)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100135 {
136 log<level::ERR>(
Nan Zhoubf3cf752021-12-28 11:02:07 -0800137 "Error occurred while creating random certificate file path",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100138 entry("DIR=%s", directoryPath.c_str()));
139 elog<InternalFailure>();
140 }
141 std::string filePathStr(filePath);
142 free(filePath);
143 return filePathStr;
144}
145
146std::string Certificate::generateAuthCertFileX509Path(
147 const std::string& certSrcFilePath, const std::string& certDstDirPath)
148{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800149 const internal::X509Ptr cert = loadCert(certSrcFilePath);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200150 unsigned long hash = X509_subject_name_hash(cert.get());
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100151 static constexpr auto CERT_HASH_LENGTH = 9;
152 char hashBuf[CERT_HASH_LENGTH];
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200153
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100154 snprintf(hashBuf, CERT_HASH_LENGTH, "%08lx", hash);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200155
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100156 const std::string certHash(hashBuf);
Nan Zhou718eef32021-12-28 11:03:30 -0800157 for (size_t i = 0; i < maxNumAuthorityCertificates; ++i)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100158 {
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100159 const std::string certDstFileX509Path =
160 certDstDirPath + "/" + certHash + "." + std::to_string(i);
161 if (!fs::exists(certDstFileX509Path))
162 {
163 return certDstFileX509Path;
164 }
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100165 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200166
Zbigniew Lukwinski73d1fbf2020-01-15 15:31:12 +0100167 log<level::ERR>("Authority certificate x509 file path already used",
168 entry("DIR=%s", certDstDirPath.c_str()));
169 elog<InternalFailure>();
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100170}
171
172std::string
173 Certificate::generateAuthCertFilePath(const std::string& certSrcFilePath)
174{
175 // If there is a certificate file path (which means certificate replacement
176 // is doing) use it (do not create new one)
177 if (!certFilePath.empty())
178 {
179 return certFilePath;
180 }
181 // If source certificate file is located in the certificates directory use
182 // it (do not create new one)
183 else if (fs::path(certSrcFilePath).parent_path().string() ==
184 certInstallPath)
185 {
186 return certSrcFilePath;
187 }
188 // Otherwise generate new file name/path
189 else
190 {
191 return generateUniqueFilePath(certInstallPath);
192 }
193}
194
195std::string
196 Certificate::generateCertFilePath(const std::string& certSrcFilePath)
197{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800198 if (certType == CertificateType::Authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100199 {
200 return generateAuthCertFilePath(certSrcFilePath);
201 }
202 else
203 {
204 return certInstallPath;
205 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200206}
207
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600208Certificate::Certificate(sdbusplus::bus::bus& bus, const std::string& objPath,
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800209 CertificateType type, const std::string& installPath,
210 const std::string& uploadPath, Watch* watch,
211 Manager& parent) :
212 internal::CertificateInterface(bus, objPath.c_str(), true),
213 objectPath(objPath), certType(type), certInstallPath(installPath),
214 certWatch(watch), manager(parent)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600215{
216 auto installHelper = [this](const auto& filePath) {
217 if (!compareKeys(filePath))
218 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800219 elog<InvalidCertificateError>(InvalidCertificate::REASON(
220 "Private key does not match the Certificate"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600221 };
222 };
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800223 typeFuncMap[CertificateType::Server] = installHelper;
224 typeFuncMap[CertificateType::Client] = installHelper;
225 typeFuncMap[CertificateType::Authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500226
227 auto appendPrivateKey = [this](const std::string& filePath) {
228 checkAndAppendPrivateKey(filePath);
229 };
230
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800231 appendKeyMap[CertificateType::Server] = appendPrivateKey;
232 appendKeyMap[CertificateType::Client] = appendPrivateKey;
233 appendKeyMap[CertificateType::Authority] = [](const std::string&) {};
Marri Devender Raocd30c492019-06-12 01:40:17 -0500234
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100235 // Generate certificate file path
236 certFilePath = generateCertFilePath(uploadPath);
237
Marri Devender Raocd30c492019-06-12 01:40:17 -0500238 // install the certificate
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100239 install(uploadPath);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500240
Marri Devender Raoedd11312019-02-27 08:45:10 -0600241 this->emit_object_added();
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600242}
243
244Certificate::~Certificate()
245{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100246 if (!fs::remove(certFilePath))
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600247 {
248 log<level::INFO>("Certificate file not found!",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100249 entry("PATH=%s", certFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600250 }
251}
252
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500253void Certificate::replace(const std::string filePath)
254{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100255 manager.replaceCertificate(this, filePath);
Marri Devender Rao13bf74e2019-03-26 01:52:17 -0500256}
257
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100258void Certificate::install(const std::string& certSrcFilePath)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600259{
260 log<level::INFO>("Certificate install ",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100261 entry("FILEPATH=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600262
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500263 // stop watch for user initiated certificate install
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800264 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500265 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800266 certWatch->stopWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500267 }
268
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600269 // Verify the certificate file
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100270 fs::path file(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600271 if (!fs::exists(file))
272 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100273 log<level::ERR>("File is Missing",
274 entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600275 elog<InternalFailure>();
276 }
277
278 try
279 {
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100280 if (fs::file_size(certSrcFilePath) == 0)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600281 {
282 // file is empty
283 log<level::ERR>("File is empty",
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100284 entry("FILE=%s", certSrcFilePath.c_str()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800285 elog<InvalidCertificateError>(
286 InvalidCertificate::REASON("File is empty"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600287 }
288 }
289 catch (const fs::filesystem_error& e)
290 {
291 // Log Error message
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100292 log<level::ERR>(e.what(), entry("FILE=%s", certSrcFilePath.c_str()));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600293 elog<InternalFailure>();
294 }
295
Nan Zhoue869bb62021-12-30 11:34:42 -0800296 X509StorePtr x509Store = getX509Store(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600297
Nan Zhoubf3cf752021-12-28 11:02:07 -0800298 // Load Certificate file into the X509 structure.
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800299 internal::X509Ptr cert = loadCert(certSrcFilePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800300
301 // Perform validation
302 validateCertificateAgainstStore(*x509Store, *cert);
303 validateCertificateStartDate(*cert);
304 validateCertificateInSSLContext(*cert);
305
306 // Invoke type specific append private key function.
307 if (auto it = appendKeyMap.find(certType); it == appendKeyMap.end())
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600308 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800309 log<level::ERR>("Unsupported Type",
310 entry("TYPE=%s", certificateTypeToString(certType)));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600311 elog<InternalFailure>();
312 }
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600313 else
314 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800315 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600316 }
317
Marri Devender Raocd30c492019-06-12 01:40:17 -0500318 // Invoke type specific compare keys function.
Nan Zhoue869bb62021-12-30 11:34:42 -0800319 if (auto it = typeFuncMap.find(certType); it == typeFuncMap.end())
Marri Devender Raocd30c492019-06-12 01:40:17 -0500320 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800321 log<level::ERR>("Unsupported Type",
322 entry("TYPE=%s", certificateTypeToString(certType)));
Marri Devender Raocd30c492019-06-12 01:40:17 -0500323 elog<InternalFailure>();
324 }
Nan Zhoue869bb62021-12-30 11:34:42 -0800325 else
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600326 {
Nan Zhoue869bb62021-12-30 11:34:42 -0800327 it->second(certSrcFilePath);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600328 }
Marri Devender Rao8f80c352019-05-13 00:53:01 -0500329
Nan Zhoue869bb62021-12-30 11:34:42 -0800330 copyCertificate(certSrcFilePath, certFilePath);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100331 storageUpdate();
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600332
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100333 // Keep certificate ID
Nan Zhoue869bb62021-12-30 11:34:42 -0800334 certId = generateCertId(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200335
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600336 // Parse the certificate file and populate properties
Nan Zhoue869bb62021-12-30 11:34:42 -0800337 populateProperties(*cert);
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500338
339 // restart watch
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800340 if (certWatch != nullptr)
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500341 {
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800342 certWatch->startWatch();
Marri Devender Raoffad1ef2019-06-03 04:54:12 -0500343 }
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600344}
345
346void Certificate::populateProperties()
347{
Nan Zhoue869bb62021-12-30 11:34:42 -0800348 internal::X509Ptr cert = loadCert(certInstallPath);
349 populateProperties(*cert);
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200350}
351
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100352std::string Certificate::getCertId() const
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200353{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100354 return certId;
355}
356
357bool Certificate::isSame(const std::string& certPath)
358{
Nan Zhoue869bb62021-12-30 11:34:42 -0800359 internal::X509Ptr cert = loadCert(certPath);
360 return getCertId() == generateCertId(*cert);
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100361}
362
363void Certificate::storageUpdate()
364{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800365 if (certType == CertificateType::Authority)
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100366 {
367 // Create symbolic link in the certificate directory
368 std::string certFileX509Path;
369 try
370 {
371 if (!certFilePath.empty() &&
372 fs::is_regular_file(fs::path(certFilePath)))
373 {
374 certFileX509Path =
375 generateAuthCertFileX509Path(certFilePath, certInstallPath);
376 fs::create_symlink(fs::path(certFilePath),
377 fs::path(certFileX509Path));
378 }
379 }
380 catch (const std::exception& e)
381 {
382 log<level::ERR>("Failed to create symlink for certificate",
383 entry("ERR=%s", e.what()),
384 entry("FILE=%s", certFilePath.c_str()),
385 entry("SYMLINK=%s", certFileX509Path.c_str()));
386 elog<InternalFailure>();
387 }
388 }
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200389}
390
Nan Zhoue869bb62021-12-30 11:34:42 -0800391void Certificate::populateProperties(X509& cert)
Kowalski, Kamildb029c92019-07-08 17:09:39 +0200392{
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600393 // Update properties if no error thrown
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800394 BIOMemPtr certBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800395 PEM_write_bio_X509(certBio.get(), &cert);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800396 BufMemPtr certBuf(BUF_MEM_new(), BUF_MEM_free);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600397 BUF_MEM* buf = certBuf.get();
398 BIO_get_mem_ptr(certBio.get(), &buf);
399 std::string certStr(buf->data, buf->length);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800400 certificateString(certStr);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600401
402 static const int maxKeySize = 4096;
403 char subBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800404 BIOMemPtr subBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800405 // This pointer cannot be freed independantly.
406 X509_NAME* sub = X509_get_subject_name(&cert);
Marri Devender Raodec58772019-06-11 03:10:00 -0500407 X509_NAME_print_ex(subBio.get(), sub, 0, XN_FLAG_SEP_COMMA_PLUS);
408 BIO_read(subBio.get(), subBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800409 subject(subBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600410
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600411 char issuerBuffer[maxKeySize] = {0};
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800412 BIOMemPtr issuerBio(BIO_new(BIO_s_mem()), BIO_free);
Nan Zhoue869bb62021-12-30 11:34:42 -0800413 // This pointer cannot be freed independantly.
414 X509_NAME* issuer_name = X509_get_issuer_name(&cert);
Marri Devender Raodec58772019-06-11 03:10:00 -0500415 X509_NAME_print_ex(issuerBio.get(), issuer_name, 0, XN_FLAG_SEP_COMMA_PLUS);
416 BIO_read(issuerBio.get(), issuerBuffer, maxKeySize);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800417 issuer(issuerBuffer);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600418
419 std::vector<std::string> keyUsageList;
420 ASN1_BIT_STRING* usage;
421
422 // Go through each usage in the bit string and convert to
423 // corresponding string value
424 if ((usage = static_cast<ASN1_BIT_STRING*>(
Nan Zhoue869bb62021-12-30 11:34:42 -0800425 X509_get_ext_d2i(&cert, NID_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600426 {
427 for (auto i = 0; i < usage->length; ++i)
428 {
429 for (auto& x : keyUsageToRfStr)
430 {
431 if (x.first & usage->data[i])
432 {
433 keyUsageList.push_back(x.second);
434 break;
435 }
436 }
437 }
438 }
439
440 EXTENDED_KEY_USAGE* extUsage;
Nan Zhoue869bb62021-12-30 11:34:42 -0800441 if ((extUsage = static_cast<EXTENDED_KEY_USAGE*>(
442 X509_get_ext_d2i(&cert, NID_ext_key_usage, nullptr, nullptr))))
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600443 {
444 for (int i = 0; i < sk_ASN1_OBJECT_num(extUsage); i++)
445 {
446 keyUsageList.push_back(extendedKeyUsageToRfStr[OBJ_obj2nid(
447 sk_ASN1_OBJECT_value(extUsage, i))]);
448 }
449 }
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800450 keyUsage(keyUsageList);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600451
452 int days = 0;
453 int secs = 0;
454
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800455 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
Nan Zhoucf811c42021-12-02 14:56:17 -0800456 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
457 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600458
Nan Zhoucf811c42021-12-02 14:56:17 -0800459 static const uint64_t dayToSeconds = 24 * 60 * 60;
Nan Zhoue869bb62021-12-30 11:34:42 -0800460 ASN1_TIME* notAfter = X509_get_notAfter(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600461 ASN1_TIME_diff(&days, &secs, epoch.get(), notAfter);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800462 validNotAfter((days * dayToSeconds) + secs);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600463
Nan Zhoue869bb62021-12-30 11:34:42 -0800464 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
Dhruvaraj Subhashchandran36f25142019-02-14 05:06:26 -0600465 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800466 validNotBefore((days * dayToSeconds) + secs);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600467}
468
Marri Devender Raocd30c492019-06-12 01:40:17 -0500469void Certificate::checkAndAppendPrivateKey(const std::string& filePath)
470{
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800471 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Raocd30c492019-06-12 01:40:17 -0500472 if (!keyBio)
473 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800474 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Raocd30c492019-06-12 01:40:17 -0500475 entry("FILE=%s", filePath.c_str()));
476 elog<InternalFailure>();
477 }
478 BIO_read_filename(keyBio.get(), filePath.c_str());
479
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800480 EVPPkeyPtr priKey(
Marri Devender Raocd30c492019-06-12 01:40:17 -0500481 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
482 ::EVP_PKEY_free);
483 if (!priKey)
484 {
485 log<level::INFO>("Private key not present in file",
486 entry("FILE=%s", filePath.c_str()));
487 fs::path privateKeyFile = fs::path(certInstallPath).parent_path();
Nan Zhou718eef32021-12-28 11:03:30 -0800488 privateKeyFile = privateKeyFile / defaultPrivateKeyFileName;
Marri Devender Raocd30c492019-06-12 01:40:17 -0500489 if (!fs::exists(privateKeyFile))
490 {
491 log<level::ERR>("Private key file is not found",
492 entry("FILE=%s", privateKeyFile.c_str()));
493 elog<InternalFailure>();
494 }
495
496 std::ifstream privKeyFileStream;
497 std::ofstream certFileStream;
498 privKeyFileStream.exceptions(std::ifstream::failbit |
499 std::ifstream::badbit |
500 std::ifstream::eofbit);
501 certFileStream.exceptions(std::ofstream::failbit |
502 std::ofstream::badbit |
503 std::ofstream::eofbit);
504 try
505 {
506 privKeyFileStream.open(privateKeyFile);
507 certFileStream.open(filePath, std::ios::app);
Marri Devender Rao18e51c92019-07-15 04:59:01 -0500508 certFileStream << std::endl; // insert line break
Marri Devender Raocd30c492019-06-12 01:40:17 -0500509 certFileStream << privKeyFileStream.rdbuf() << std::flush;
510 privKeyFileStream.close();
511 certFileStream.close();
512 }
513 catch (const std::exception& e)
514 {
515 log<level::ERR>("Failed to append private key",
516 entry("ERR=%s", e.what()),
517 entry("SRC=%s", privateKeyFile.c_str()),
518 entry("DST=%s", filePath.c_str()));
519 elog<InternalFailure>();
520 }
521 }
522}
523
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600524bool Certificate::compareKeys(const std::string& filePath)
525{
526 log<level::INFO>("Certificate compareKeys",
527 entry("FILEPATH=%s", filePath.c_str()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800528 internal::X509Ptr cert(X509_new(), ::X509_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600529 if (!cert)
530 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800531 log<level::ERR>("Error occurred during X509_new call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600532 entry("FILE=%s", filePath.c_str()),
533 entry("ERRCODE=%lu", ERR_get_error()));
534 elog<InternalFailure>();
535 }
536
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800537 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600538 if (!bioCert)
539 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800540 log<level::ERR>("Error occurred during BIO_new_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600541 entry("FILE=%s", filePath.c_str()));
542 elog<InternalFailure>();
543 }
544
545 X509* x509 = cert.get();
546 PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr);
547
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800548 EVPPkeyPtr pubKey(X509_get_pubkey(cert.get()), ::EVP_PKEY_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600549 if (!pubKey)
550 {
551 log<level::ERR>("Error occurred during X509_get_pubkey",
552 entry("FILE=%s", filePath.c_str()),
553 entry("ERRCODE=%lu", ERR_get_error()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800554 elog<InvalidCertificateError>(
555 InvalidCertificate::REASON("Failed to get public key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600556 }
557
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800558 BIOMemPtr keyBio(BIO_new(BIO_s_file()), ::BIO_free);
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600559 if (!keyBio)
560 {
Nan Zhoubf3cf752021-12-28 11:02:07 -0800561 log<level::ERR>("Error occurred during BIO_s_file call",
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600562 entry("FILE=%s", filePath.c_str()));
563 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 Rao6ceec402019-02-01 03:15:19 -0600568 PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr),
569 ::EVP_PKEY_free);
570 if (!priKey)
571 {
572 log<level::ERR>("Error occurred during PEM_read_bio_PrivateKey",
573 entry("FILE=%s", filePath.c_str()),
574 entry("ERRCODE=%lu", ERR_get_error()));
Nan Zhoucf06ccd2021-12-28 16:25:45 -0800575 elog<InvalidCertificateError>(
576 InvalidCertificate::REASON("Failed to get private key info"));
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600577 }
578
Patrick Williams55ceaa22021-12-14 06:52:26 -0600579#if (OPENSSL_VERSION_NUMBER < 0x30000000L)
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600580 int32_t rc = EVP_PKEY_cmp(priKey.get(), pubKey.get());
Patrick Williams55ceaa22021-12-14 06:52:26 -0600581#else
582 int32_t rc = EVP_PKEY_eq(priKey.get(), pubKey.get());
583#endif
Marri Devender Rao6ceec402019-02-01 03:15:19 -0600584 if (rc != 1)
585 {
586 log<level::ERR>("Private key is not matching with Certificate",
587 entry("FILE=%s", filePath.c_str()),
588 entry("ERRCODE=%d", rc));
589 return false;
590 }
591 return true;
592}
593
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200594void Certificate::delete_()
595{
Zbigniew Lukwinski2f3563c2020-01-08 12:35:23 +0100596 manager.deleteCertificate(this);
Zbigniew Kurzynskia3bb38f2019-09-17 13:34:25 +0200597}
Nan Zhoue1289ad2021-12-28 11:02:56 -0800598} // namespace phosphor::certs