blob: 39e68a6b35a2365df74a72c2b525e0549ae8f7cd [file] [log] [blame]
Lei YU3c478142022-06-27 14:42:47 +08001#include "config.h"
2
Nan Zhoue869bb62021-12-30 11:34:42 -08003#include "x509_utils.hpp"
4
5#include <openssl/asn1.h>
6#include <openssl/bio.h>
7#include <openssl/err.h>
8#include <openssl/evp.h>
9#include <openssl/pem.h>
10#include <openssl/ssl3.h>
11#include <openssl/x509_vfy.h>
12
Nan Zhoue869bb62021-12-30 11:34:42 -080013#include <phosphor-logging/elog-errors.hpp>
14#include <phosphor-logging/elog.hpp>
Ravi Tejaf2646272023-09-30 13:00:55 -050015#include <phosphor-logging/lg2.hpp>
Nan Zhoue869bb62021-12-30 11:34:42 -080016#include <xyz/openbmc_project/Certs/error.hpp>
17#include <xyz/openbmc_project/Common/error.hpp>
18
Patrick Williams223e4602023-05-10 07:51:11 -050019#include <cstdio>
20#include <ctime>
21#include <exception>
22#include <memory>
23
Nan Zhoue869bb62021-12-30 11:34:42 -080024namespace phosphor::certs
25{
26
27namespace
28{
29
30using ::phosphor::logging::elog;
Nan Zhoue869bb62021-12-30 11:34:42 -080031using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
32using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
33using Reason = ::phosphor::logging::xyz::openbmc_project::Certs::
34 InvalidCertificate::REASON;
35
36// RAII support for openSSL functions.
37using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
38using X509StoreCtxPtr =
39 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
40using X509Ptr = std::unique_ptr<X509, decltype(&::X509_free)>;
41using BIOMemPtr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
42using ASN1TimePtr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
43using SSLCtxPtr = std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)>;
44
45// Trust chain related errors.`
46constexpr bool isTrustChainError(int error)
47{
48 return error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
49 error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN ||
50 error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY ||
51 error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT ||
52 error == X509_V_ERR_CERT_UNTRUSTED ||
53 error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE;
54}
55} // namespace
56
57X509StorePtr getX509Store(const std::string& certSrcPath)
58{
59 // Create an empty X509_STORE structure for certificate validation.
60 X509StorePtr x509Store(X509_STORE_new(), &X509_STORE_free);
61 if (!x509Store)
62 {
Ravi Tejaf2646272023-09-30 13:00:55 -050063 lg2::error("Error occurred during X509_STORE_new call");
Nan Zhoue869bb62021-12-30 11:34:42 -080064 elog<InternalFailure>();
65 }
66
67 OpenSSL_add_all_algorithms();
68
69 // ADD Certificate Lookup method.
70 // lookup will be cleaned up automatically when the holding Store goes away.
71 auto lookup = X509_STORE_add_lookup(x509Store.get(), X509_LOOKUP_file());
72
73 if (!lookup)
74 {
Ravi Tejaf2646272023-09-30 13:00:55 -050075 lg2::error("Error occurred during X509_STORE_add_lookup call");
Nan Zhoue869bb62021-12-30 11:34:42 -080076 elog<InternalFailure>();
77 }
78 // Load the Certificate file into X509 Store.
79 if (int errCode = X509_LOOKUP_load_file(lookup, certSrcPath.c_str(),
80 X509_FILETYPE_PEM);
81 errCode != 1)
82 {
Ravi Tejaf2646272023-09-30 13:00:55 -050083 lg2::error(
84 "Error occurred during X509_LOOKUP_load_file call, FILE:{FILE}",
85 "FILE", certSrcPath);
Nan Zhoue869bb62021-12-30 11:34:42 -080086 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
87 }
88 return x509Store;
89}
90
91X509Ptr loadCert(const std::string& filePath)
92{
93 // Read Certificate file
94 X509Ptr cert(X509_new(), ::X509_free);
95 if (!cert)
96 {
Ravi Tejaf2646272023-09-30 13:00:55 -050097 lg2::error(
98 "Error occurred during X509_new call, FILE:{FILE}, ERRCODE:{ERRCODE}",
99 "FILE", filePath, "ERRCODE", ERR_get_error());
Nan Zhoue869bb62021-12-30 11:34:42 -0800100 elog<InternalFailure>();
101 }
102
103 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
104 if (!bioCert)
105 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500106 lg2::error("Error occurred during BIO_new_file call, FILE:{FILE}",
107 "FILE", filePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800108 elog<InternalFailure>();
109 }
110
111 X509* x509 = cert.get();
112 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
113 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500114 lg2::error("Error occurred during PEM_read_bio_X509 call, FILE:{FILE}",
115 "FILE", filePath);
Nan Zhoue869bb62021-12-30 11:34:42 -0800116 elog<InternalFailure>();
117 }
118 return cert;
119}
120
121// Checks that notBefore is not earlier than the unix epoch given that
122// the corresponding DBus interface is uint64_t.
123void validateCertificateStartDate(X509& cert)
124{
125 int days = 0;
126 int secs = 0;
127
128 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
129 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
130 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
131
132 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
133 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
134
135 if (days < 0 || secs < 0)
136 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500137 lg2::error("Certificate valid date starts before the Unix Epoch");
Nan Zhoue869bb62021-12-30 11:34:42 -0800138 elog<InvalidCertificate>(
139 Reason("NotBefore should after 19700101000000Z"));
140 }
141}
142
143void validateCertificateAgainstStore(X509_STORE& x509Store, X509& cert)
144{
145 int errCode = X509_V_OK;
146 X509StoreCtxPtr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
147 if (!storeCtx)
148 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500149 lg2::error("Error occurred during X509_STORE_CTX_new call");
Nan Zhoue869bb62021-12-30 11:34:42 -0800150 elog<InternalFailure>();
151 }
152
153 errCode = X509_STORE_CTX_init(storeCtx.get(), &x509Store, &cert, nullptr);
154 if (errCode != 1)
155 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500156 lg2::error("Error occurred during X509_STORE_CTX_init call");
Nan Zhoue869bb62021-12-30 11:34:42 -0800157 elog<InternalFailure>();
158 }
159
160 // Set time to current time.
161 auto locTime = time(nullptr);
162
163 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
164 locTime);
165
166 errCode = X509_verify_cert(storeCtx.get());
167 if (errCode == 1)
168 {
169 errCode = X509_V_OK;
170 }
171 else if (errCode == 0)
172 {
173 errCode = X509_STORE_CTX_get_error(storeCtx.get());
Ravi Tejaf2646272023-09-30 13:00:55 -0500174 lg2::info(
Nan Zhoue869bb62021-12-30 11:34:42 -0800175 "Error occurred during X509_verify_cert call, checking for known "
Ravi Tejaf2646272023-09-30 13:00:55 -0500176 "error, ERRCODE:{ERRCODE}, ERROR_STR:{ERROR_STR}",
177 "ERRCODE", errCode, "ERROR_STR",
178 X509_verify_cert_error_string(errCode));
Nan Zhoue869bb62021-12-30 11:34:42 -0800179 }
180 else
181 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500182 lg2::error("Error occurred during X509_verify_cert call");
Nan Zhoue869bb62021-12-30 11:34:42 -0800183 elog<InternalFailure>();
184 }
185
186 // Allow certificate upload, for "certificate is not yet valid" and
187 // trust chain related errors.
Lei YU3c478142022-06-27 14:42:47 +0800188 // If ALLOW_EXPIRED is defined, allow expired certificate so that it
189 // could be replaced
190 bool isOK = (errCode == X509_V_OK) ||
191 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
192 isTrustChainError(errCode) ||
193 (allowExpired && errCode == X509_V_ERR_CERT_HAS_EXPIRED);
194
195 if (!isOK)
Nan Zhoue869bb62021-12-30 11:34:42 -0800196 {
197 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
198 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500199 lg2::error("Expired certificate ");
Nan Zhoue869bb62021-12-30 11:34:42 -0800200 elog<InvalidCertificate>(Reason("Expired Certificate"));
201 }
Manojkiran Eda5d4f7932024-06-17 11:49:21 +0530202 // Logging general error here.
Ravi Tejaf2646272023-09-30 13:00:55 -0500203 lg2::error(
204 "Certificate validation failed, ERRCODE:{ERRCODE}, ERROR_STR:{ERROR_STR}",
205 "ERRCODE", errCode, "ERROR_STR",
206 X509_verify_cert_error_string(errCode));
Nan Zhoue869bb62021-12-30 11:34:42 -0800207 elog<InvalidCertificate>(Reason("Certificate validation failed"));
208 }
209}
210
211void validateCertificateInSSLContext(X509& cert)
212{
213 const SSL_METHOD* method = TLS_method();
214 SSLCtxPtr ctx(SSL_CTX_new(method), SSL_CTX_free);
215 if (SSL_CTX_use_certificate(ctx.get(), &cert) != 1)
216 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500217 lg2::error("Certificate is not usable, ERRCODE:{ERRCODE}", "ERRCODE",
218 ERR_get_error());
Nan Zhoue869bb62021-12-30 11:34:42 -0800219 elog<InvalidCertificate>(Reason("Certificate is not usable"));
220 }
221}
222
223std::string generateCertId(X509& cert)
224{
225 unsigned long subjectNameHash = X509_subject_name_hash(&cert);
226 unsigned long issuerSerialHash = X509_issuer_and_serial_hash(&cert);
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000227 static constexpr auto certIdLength = 17;
228 char idBuff[certIdLength];
Nan Zhoue869bb62021-12-30 11:34:42 -0800229
Nan Zhoue3d47cd2022-09-16 03:41:53 +0000230 snprintf(idBuff, certIdLength, "%08lx%08lx", subjectNameHash,
Nan Zhoue869bb62021-12-30 11:34:42 -0800231 issuerSerialHash);
232
233 return {idBuff};
234}
Nan Zhou6ec13c82021-12-30 11:34:50 -0800235
236std::unique_ptr<X509, decltype(&::X509_free)> parseCert(const std::string& pem)
237{
238 if (pem.size() > INT_MAX)
239 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500240 lg2::error("Error occurred during parseCert: PEM is too long");
Nan Zhou6ec13c82021-12-30 11:34:50 -0800241 elog<InvalidCertificate>(Reason("Invalid PEM: too long"));
242 }
243 X509Ptr cert(X509_new(), ::X509_free);
244 if (!cert)
245 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500246 lg2::error("Error occurred during X509_new call, ERRCODE:{ERRCODE}",
247 "ERRCODE", ERR_get_error());
Nan Zhou6ec13c82021-12-30 11:34:50 -0800248 elog<InternalFailure>();
249 }
250
251 BIOMemPtr bioCert(BIO_new_mem_buf(pem.data(), static_cast<int>(pem.size())),
252 ::BIO_free);
253 X509* x509 = cert.get();
254 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
255 {
Ravi Tejaf2646272023-09-30 13:00:55 -0500256 lg2::error("Error occurred during PEM_read_bio_X509 call, PEM:{PEM}",
257 "PEM", pem);
Nan Zhou6ec13c82021-12-30 11:34:50 -0800258 elog<InternalFailure>();
259 }
260 return cert;
261}
Nan Zhoue869bb62021-12-30 11:34:42 -0800262} // namespace phosphor::certs