blob: ba1a2efe7df1537d284b1aeb55da27a60ef9b2ab [file] [log] [blame]
Nan Zhoue869bb62021-12-30 11:34:42 -08001#include "x509_utils.hpp"
2
3#include <openssl/asn1.h>
4#include <openssl/bio.h>
5#include <openssl/err.h>
6#include <openssl/evp.h>
7#include <openssl/pem.h>
8#include <openssl/ssl3.h>
9#include <openssl/x509_vfy.h>
10
11#include <cstdio>
12#include <ctime>
13#include <exception>
14#include <memory>
15#include <phosphor-logging/elog-errors.hpp>
16#include <phosphor-logging/elog.hpp>
17#include <phosphor-logging/log.hpp>
18#include <xyz/openbmc_project/Certs/error.hpp>
19#include <xyz/openbmc_project/Common/error.hpp>
20
21namespace phosphor::certs
22{
23
24namespace
25{
26
27using ::phosphor::logging::elog;
28using ::phosphor::logging::entry;
29using ::phosphor::logging::level;
30using ::phosphor::logging::log;
31using ::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 {
63 log<level::ERR>("Error occurred during X509_STORE_new call");
64 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 {
75 log<level::ERR>("Error occurred during X509_STORE_add_lookup call");
76 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 {
83 log<level::ERR>("Error occurred during X509_LOOKUP_load_file call",
84 entry("FILE=%s", certSrcPath.c_str()));
85 elog<InvalidCertificate>(Reason("Invalid certificate file format"));
86 }
87 return x509Store;
88}
89
90X509Ptr loadCert(const std::string& filePath)
91{
92 // Read Certificate file
93 X509Ptr cert(X509_new(), ::X509_free);
94 if (!cert)
95 {
96 log<level::ERR>("Error occurred during X509_new call",
97 entry("FILE=%s", filePath.c_str()),
98 entry("ERRCODE=%lu", ERR_get_error()));
99 elog<InternalFailure>();
100 }
101
102 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free);
103 if (!bioCert)
104 {
105 log<level::ERR>("Error occurred during BIO_new_file call",
106 entry("FILE=%s", filePath.c_str()));
107 elog<InternalFailure>();
108 }
109
110 X509* x509 = cert.get();
111 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
112 {
113 log<level::ERR>("Error occurred during PEM_read_bio_X509 call",
114 entry("FILE=%s", filePath.c_str()));
115 elog<InternalFailure>();
116 }
117 return cert;
118}
119
120// Checks that notBefore is not earlier than the unix epoch given that
121// the corresponding DBus interface is uint64_t.
122void validateCertificateStartDate(X509& cert)
123{
124 int days = 0;
125 int secs = 0;
126
127 ASN1TimePtr epoch(ASN1_TIME_new(), ASN1_STRING_free);
128 // Set time to 00:00am GMT, Jan 1 1970; format: YYYYMMDDHHMMSSZ
129 ASN1_TIME_set_string(epoch.get(), "19700101000000Z");
130
131 ASN1_TIME* notBefore = X509_get_notBefore(&cert);
132 ASN1_TIME_diff(&days, &secs, epoch.get(), notBefore);
133
134 if (days < 0 || secs < 0)
135 {
136 log<level::ERR>("Certificate valid date starts before the Unix Epoch");
137 elog<InvalidCertificate>(
138 Reason("NotBefore should after 19700101000000Z"));
139 }
140}
141
142void validateCertificateAgainstStore(X509_STORE& x509Store, X509& cert)
143{
144 int errCode = X509_V_OK;
145 X509StoreCtxPtr storeCtx(X509_STORE_CTX_new(), ::X509_STORE_CTX_free);
146 if (!storeCtx)
147 {
148 log<level::ERR>("Error occurred during X509_STORE_CTX_new call");
149 elog<InternalFailure>();
150 }
151
152 errCode = X509_STORE_CTX_init(storeCtx.get(), &x509Store, &cert, nullptr);
153 if (errCode != 1)
154 {
155 log<level::ERR>("Error occurred during X509_STORE_CTX_init call");
156 elog<InternalFailure>();
157 }
158
159 // Set time to current time.
160 auto locTime = time(nullptr);
161
162 X509_STORE_CTX_set_time(storeCtx.get(), X509_V_FLAG_USE_CHECK_TIME,
163 locTime);
164
165 errCode = X509_verify_cert(storeCtx.get());
166 if (errCode == 1)
167 {
168 errCode = X509_V_OK;
169 }
170 else if (errCode == 0)
171 {
172 errCode = X509_STORE_CTX_get_error(storeCtx.get());
173 log<level::INFO>(
174 "Error occurred during X509_verify_cert call, checking for known "
175 "error",
176 entry("ERRCODE=%d", errCode),
177 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
178 }
179 else
180 {
181 log<level::ERR>("Error occurred during X509_verify_cert call");
182 elog<InternalFailure>();
183 }
184
185 // Allow certificate upload, for "certificate is not yet valid" and
186 // trust chain related errors.
187 if (!((errCode == X509_V_OK) ||
188 (errCode == X509_V_ERR_CERT_NOT_YET_VALID) ||
189 isTrustChainError(errCode)))
190 {
191 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED)
192 {
193 log<level::ERR>("Expired certificate ");
194 elog<InvalidCertificate>(Reason("Expired Certificate"));
195 }
196 // Loging general error here.
197 log<level::ERR>(
198 "Certificate validation failed", entry("ERRCODE=%d", errCode),
199 entry("ERROR_STR=%s", X509_verify_cert_error_string(errCode)));
200 elog<InvalidCertificate>(Reason("Certificate validation failed"));
201 }
202}
203
204void validateCertificateInSSLContext(X509& cert)
205{
206 const SSL_METHOD* method = TLS_method();
207 SSLCtxPtr ctx(SSL_CTX_new(method), SSL_CTX_free);
208 if (SSL_CTX_use_certificate(ctx.get(), &cert) != 1)
209 {
210 log<level::ERR>("Certificate is not usable",
211 entry("ERRCODE=%x", ERR_get_error()));
212 elog<InvalidCertificate>(Reason("Certificate is not usable"));
213 }
214}
215
216std::string generateCertId(X509& cert)
217{
218 unsigned long subjectNameHash = X509_subject_name_hash(&cert);
219 unsigned long issuerSerialHash = X509_issuer_and_serial_hash(&cert);
220 static constexpr auto CERT_ID_LENGTH = 17;
221 char idBuff[CERT_ID_LENGTH];
222
223 snprintf(idBuff, CERT_ID_LENGTH, "%08lx%08lx", subjectNameHash,
224 issuerSerialHash);
225
226 return {idBuff};
227}
Nan Zhou6ec13c82021-12-30 11:34:50 -0800228
229std::unique_ptr<X509, decltype(&::X509_free)> parseCert(const std::string& pem)
230{
231 if (pem.size() > INT_MAX)
232 {
233 log<level::ERR>("Error occurred during parseCert: PEM is too long");
234 elog<InvalidCertificate>(Reason("Invalid PEM: too long"));
235 }
236 X509Ptr cert(X509_new(), ::X509_free);
237 if (!cert)
238 {
239 log<level::ERR>("Error occurred during X509_new call",
240 entry("ERRCODE=%lu", ERR_get_error()));
241 elog<InternalFailure>();
242 }
243
244 BIOMemPtr bioCert(BIO_new_mem_buf(pem.data(), static_cast<int>(pem.size())),
245 ::BIO_free);
246 X509* x509 = cert.get();
247 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr))
248 {
249 log<level::ERR>("Error occurred during PEM_read_bio_X509 call",
250 entry("PEM=%s", pem.c_str()));
251 elog<InternalFailure>();
252 }
253 return cert;
254}
Nan Zhoue869bb62021-12-30 11:34:42 -0800255} // namespace phosphor::certs