1 #include "config.h" 2 3 #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 <phosphor-logging/elog-errors.hpp> 14 #include <phosphor-logging/elog.hpp> 15 #include <phosphor-logging/lg2.hpp> 16 #include <xyz/openbmc_project/Certs/error.hpp> 17 #include <xyz/openbmc_project/Common/error.hpp> 18 19 #include <cstdio> 20 #include <ctime> 21 #include <exception> 22 #include <memory> 23 24 namespace phosphor::certs 25 { 26 27 namespace 28 { 29 30 using ::phosphor::logging::elog; 31 using ::sdbusplus::xyz::openbmc_project::Certs::Error::InvalidCertificate; 32 using ::sdbusplus::xyz::openbmc_project::Common::Error::InternalFailure; 33 using Reason = ::phosphor::logging::xyz::openbmc_project::Certs:: 34 InvalidCertificate::REASON; 35 36 // RAII support for openSSL functions. 37 using X509StorePtr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>; 38 using X509StoreCtxPtr = 39 std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>; 40 using X509Ptr = std::unique_ptr<X509, decltype(&::X509_free)>; 41 using BIOMemPtr = std::unique_ptr<BIO, decltype(&::BIO_free)>; 42 using ASN1TimePtr = std::unique_ptr<ASN1_TIME, decltype(&ASN1_STRING_free)>; 43 using SSLCtxPtr = std::unique_ptr<SSL_CTX, decltype(&::SSL_CTX_free)>; 44 45 // Trust chain related errors.` 46 constexpr 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 57 X509StorePtr 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 lg2::error("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 lg2::error("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 lg2::error( 84 "Error occurred during X509_LOOKUP_load_file call, FILE:{FILE}", 85 "FILE", certSrcPath); 86 elog<InvalidCertificate>(Reason("Invalid certificate file format")); 87 } 88 return x509Store; 89 } 90 91 X509Ptr loadCert(const std::string& filePath) 92 { 93 // Read Certificate file 94 X509Ptr cert(X509_new(), ::X509_free); 95 if (!cert) 96 { 97 lg2::error( 98 "Error occurred during X509_new call, FILE:{FILE}, ERRCODE:{ERRCODE}", 99 "FILE", filePath, "ERRCODE", ERR_get_error()); 100 elog<InternalFailure>(); 101 } 102 103 BIOMemPtr bioCert(BIO_new_file(filePath.c_str(), "rb"), ::BIO_free); 104 if (!bioCert) 105 { 106 lg2::error("Error occurred during BIO_new_file call, FILE:{FILE}", 107 "FILE", filePath); 108 elog<InternalFailure>(); 109 } 110 111 X509* x509 = cert.get(); 112 if (!PEM_read_bio_X509(bioCert.get(), &x509, nullptr, nullptr)) 113 { 114 lg2::error("Error occurred during PEM_read_bio_X509 call, FILE:{FILE}", 115 "FILE", filePath); 116 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. 123 void 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 { 137 lg2::error("Certificate valid date starts before the Unix Epoch"); 138 elog<InvalidCertificate>( 139 Reason("NotBefore should after 19700101000000Z")); 140 } 141 } 142 143 void 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 { 149 lg2::error("Error occurred during X509_STORE_CTX_new call"); 150 elog<InternalFailure>(); 151 } 152 153 errCode = X509_STORE_CTX_init(storeCtx.get(), &x509Store, &cert, nullptr); 154 if (errCode != 1) 155 { 156 lg2::error("Error occurred during X509_STORE_CTX_init call"); 157 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()); 174 lg2::info( 175 "Error occurred during X509_verify_cert call, checking for known " 176 "error, ERRCODE:{ERRCODE}, ERROR_STR:{ERROR_STR}", 177 "ERRCODE", errCode, "ERROR_STR", 178 X509_verify_cert_error_string(errCode)); 179 } 180 else 181 { 182 lg2::error("Error occurred during X509_verify_cert call"); 183 elog<InternalFailure>(); 184 } 185 186 // Allow certificate upload, for "certificate is not yet valid" and 187 // trust chain related errors. 188 // 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) 196 { 197 if (errCode == X509_V_ERR_CERT_HAS_EXPIRED) 198 { 199 lg2::error("Expired certificate "); 200 elog<InvalidCertificate>(Reason("Expired Certificate")); 201 } 202 // Logging general error here. 203 lg2::error( 204 "Certificate validation failed, ERRCODE:{ERRCODE}, ERROR_STR:{ERROR_STR}", 205 "ERRCODE", errCode, "ERROR_STR", 206 X509_verify_cert_error_string(errCode)); 207 elog<InvalidCertificate>(Reason("Certificate validation failed")); 208 } 209 } 210 211 void 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 lg2::error("Certificate is not usable, ERRCODE:{ERRCODE}", "ERRCODE", 218 ERR_get_error()); 219 elog<InvalidCertificate>(Reason("Certificate is not usable")); 220 } 221 } 222 223 std::string generateCertId(X509& cert) 224 { 225 unsigned long subjectNameHash = X509_subject_name_hash(&cert); 226 unsigned long issuerSerialHash = X509_issuer_and_serial_hash(&cert); 227 static constexpr auto certIdLength = 17; 228 char idBuff[certIdLength]; 229 230 snprintf(idBuff, certIdLength, "%08lx%08lx", subjectNameHash, 231 issuerSerialHash); 232 233 return {idBuff}; 234 } 235 236 std::unique_ptr<X509, decltype(&::X509_free)> parseCert(const std::string& pem) 237 { 238 if (pem.size() > INT_MAX) 239 { 240 lg2::error("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 lg2::error("Error occurred during X509_new call, ERRCODE:{ERRCODE}", 247 "ERRCODE", 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 lg2::error("Error occurred during PEM_read_bio_X509 call, PEM:{PEM}", 257 "PEM", pem); 258 elog<InternalFailure>(); 259 } 260 return cert; 261 } 262 } // namespace phosphor::certs 263