blob: 6173830286689cc6761900bb55a2f4f17d744239 [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
13#include <cstdio>
14#include <ctime>
15#include <exception>
16#include <memory>
17#include <phosphor-logging/elog-errors.hpp>
18#include <phosphor-logging/elog.hpp>
19#include <phosphor-logging/log.hpp>
20#include <xyz/openbmc_project/Certs/error.hpp>
21#include <xyz/openbmc_project/Common/error.hpp>
22
23namespace phosphor::certs
24{
25
26namespace
27{
28
29using ::phosphor::logging::elog;
30using ::phosphor::logging::entry;
31using ::phosphor::logging::level;
32using ::phosphor::logging::log;
33using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate;
34using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure;
35using Reason = ::phosphor::logging::xyz::openbmc_project::Certs::
36 InvalidCertificate::REASON;
37
38// RAII support for openSSL functions.
39using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
40using X509StoreCtxPtr =
41 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;
42using X509Ptr = std::unique_ptr<X509, decltype(&::X509_free)>;
43using BIOMemPtr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
44using ASN1TimePtr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>;
45using SSLCtxPtr = std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)>;
46
47// Trust chain related errors.`
48constexpr bool isTrustChainError(int error)
49{
50 return error == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT ||
51 error == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN ||
52 error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY ||
53 error == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT ||
54 error == X509_V_ERR_CERT_UNTRUSTED ||
55 error == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE;
56}
57} // namespace
58
59X509StorePtr getX509Store(const std::string& certSrcPath)
60{
61 // Create an empty X509_STORE structure for certificate validation.
62 X509StorePtr x509Store(X509_STORE_new(), &X509_STORE_free);
63 if (!x509Store)
64 {
65 log<level::ERR>("Error occurred during X509_STORE_new call");
66 elog<InternalFailure>();
67 }
68
69 OpenSSL_add_all_algorithms();
70
71 // ADD Certificate Lookup method.
72 // lookup will be cleaned up automatically when the holding Store goes away.
73 auto lookup = X509_STORE_add_lookup(x509Store.get(), X509_LOOKUP_file());
74
75 if (!lookup)
76 {
77 log<level::ERR>("Error occurred during X509_STORE_add_lookup call");
78 elog<InternalFailure>();
79 }
80 // Load the Certificate file into X509 Store.
81 if (int errCode = X509_LOOKUP_load_file(lookup, certSrcPath.c_str(),
82 X509_FILETYPE_PEM);
83 errCode != 1)
84 {
85 log<level::ERR>("Error occurred during X509_LOOKUP_load_file call",
86 entry("FILE=%s", certSrcPath.c_str()));
87 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
88 }
89 return x509Store;
90}
91
92X509Ptr loadCert(const std::string& filePath)
93{
94 // Read Certificate file
95 X509Ptr cert(X509_new(), ::X509_free);
96 if (!cert)
97 {
98 log<level::ERR>("Error occurred during X509_new call",
99 entry("FILE=%s", filePath.c_str()),
100 entry("ERRCODE=%lu", ERR_get_error()));
101 elog<InternalFailure>();
102 }
103
104 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
105 if (!bioCert)
106 {
107 log<level::ERR>("Error occurred during BIO_new_file call",
108 entry("FILE=%s", filePath.c_str()));
109 elog<InternalFailure>();
110 }
111
112 X509* x509 = cert.get();
113 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
114 {
115 log<level::ERR>("Error occurred during PEM_read_bio_X509 call",
116 entry("FILE=%s", filePath.c_str()));
117 elog<InternalFailure>();
118 }
119 return cert;
120}
121
122// Checks that notBefore is not earlier than the unix epoch given that
123// the corresponding DBus interface is uint64_t.
124void validateCertificateStartDate(X509& cert)
125{
126 int days = 0;
127 int secs = 0;
128
129 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
130 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
131 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
132
133 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
134 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
135
136 if (days < 0 || secs < 0)
137 {
138 log<level::ERR>("Certificate valid date starts before the Unix Epoch");
139 elog<InvalidCertificate>(
140 Reason("NotBefore should after 19700101000000Z"));
141 }
142}
143
144void validateCertificateAgainstStore(X509_STORE& x509Store, X509& cert)
145{
146 int errCode = X509_V_OK;
147 X509StoreCtxPtr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
148 if (!storeCtx)
149 {
150 log<level::ERR>("Error occurred during X509_STORE_CTX_new call");
151 elog<InternalFailure>();
152 }
153
154 errCode = X509_STORE_CTX_init(storeCtx.get(), &x509Store, &cert, nullptr);
155 if (errCode != 1)
156 {
157 log<level::ERR>("Error occurred during X509_STORE_CTX_init call");
158 elog<InternalFailure>();
159 }
160
161 // Set time to current time.
162 auto locTime = time(nullptr);
163
164 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
165 locTime);
166
167 errCode = X509_verify_cert(storeCtx.get());
168 if (errCode == 1)
169 {
170 errCode = X509_V_OK;
171 }
172 else if (errCode == 0)
173 {
174 errCode = X509_STORE_CTX_get_error(storeCtx.get());
175 log<level::INFO>(
176 "Error occurred during X509_verify_cert call, checking for known "
177 "error",
178 entry("ERRCODE=%d", errCode),
179 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
180 }
181 else
182 {
183 log<level::ERR>("Error occurred during X509_verify_cert call");
184 elog<InternalFailure>();
185 }
186
187 // Allow certificate upload, for "certificate is not yet valid" and
188 // trust chain related errors.
Lei YU3c478142022-06-27 14:42:47 +0800189 // If ALLOW_EXPIRED is defined, allow expired certificate so that it
190 // could be replaced
191 bool isOK = (errCode == X509_V_OK) ||
192 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
193 isTrustChainError(errCode) ||
194 (allowExpired && errCode == X509_V_ERR_CERT_HAS_EXPIRED);
195
196 if (!isOK)
Nan Zhoue869bb62021-12-30 11:34:42 -0800197 {
198 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
199 {
200 log<level::ERR>("Expired certificate ");
201 elog<InvalidCertificate>(Reason("Expired Certificate"));
202 }
203 // Loging general error here.
204 log<level::ERR>(
205 "Certificate validation failed", entry("ERRCODE=%d", errCode),
206 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
207 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 {
217 log<level::ERR>("Certificate is not usable",
218 entry("ERRCODE=%x", ERR_get_error()));
219 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 {
240 log<level::ERR>("Error occurred during parseCert: PEM is too long");
241 elog<InvalidCertificate>(Reason("Invalid PEM: too long"));
242 }
243 X509Ptr cert(X509_new(), ::X509_free);
244 if (!cert)
245 {
246 log<level::ERR>("Error occurred during X509_new call",
247 entry("ERRCODE=%lu", ERR_get_error()));
248 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 {
256 log<level::ERR>("Error occurred during PEM_read_bio_X509 call",
257 entry("PEM=%s", pem.c_str()));
258 elog<InternalFailure>();
259 }
260 return cert;
261}
Nan Zhoue869bb62021-12-30 11:34:42 -0800262} // namespace phosphor::certs