1724985ffSEd Tanous #include "ssl_key_handler.hpp" 2724985ffSEd Tanous 3724985ffSEd Tanous #include "bmcweb_config.h" 4724985ffSEd Tanous 5724985ffSEd Tanous #include "logging.hpp" 6724985ffSEd Tanous #include "ossl_random.hpp" 7724985ffSEd Tanous #include "persistent_data.hpp" 8724985ffSEd Tanous 9724985ffSEd Tanous #include <boost/beast/core/file_posix.hpp> 10724985ffSEd Tanous 11724985ffSEd Tanous extern "C" 12724985ffSEd Tanous { 13724985ffSEd Tanous #include <nghttp2/nghttp2.h> 14724985ffSEd Tanous #include <openssl/bio.h> 15724985ffSEd Tanous #include <openssl/dh.h> 16724985ffSEd Tanous #include <openssl/dsa.h> 17724985ffSEd Tanous #include <openssl/err.h> 18724985ffSEd Tanous #include <openssl/evp.h> 19724985ffSEd Tanous #include <openssl/pem.h> 20724985ffSEd Tanous #include <openssl/rand.h> 21724985ffSEd Tanous #include <openssl/rsa.h> 22724985ffSEd Tanous #include <openssl/ssl.h> 23724985ffSEd Tanous } 24724985ffSEd Tanous 25724985ffSEd Tanous #include <boost/asio/ssl/context.hpp> 26724985ffSEd Tanous #include <boost/system/error_code.hpp> 27724985ffSEd Tanous 28724985ffSEd Tanous #include <filesystem> 29724985ffSEd Tanous #include <memory> 30724985ffSEd Tanous #include <optional> 31724985ffSEd Tanous #include <random> 32724985ffSEd Tanous #include <string> 33724985ffSEd Tanous 34724985ffSEd Tanous namespace ensuressl 35724985ffSEd Tanous { 36724985ffSEd Tanous 37724985ffSEd Tanous static EVP_PKEY* createEcKey(); 38724985ffSEd Tanous 39724985ffSEd Tanous // Mozilla intermediate cipher suites v5.7 40724985ffSEd Tanous // Sourced from: https://ssl-config.mozilla.org/guidelines/5.7.json 41*bd79bce8SPatrick Williams constexpr const char* mozillaIntermediate = 42*bd79bce8SPatrick Williams "ECDHE-ECDSA-AES128-GCM-SHA256:" 43724985ffSEd Tanous "ECDHE-RSA-AES128-GCM-SHA256:" 44724985ffSEd Tanous "ECDHE-ECDSA-AES256-GCM-SHA384:" 45724985ffSEd Tanous "ECDHE-RSA-AES256-GCM-SHA384:" 46724985ffSEd Tanous "ECDHE-ECDSA-CHACHA20-POLY1305:" 47724985ffSEd Tanous "ECDHE-RSA-CHACHA20-POLY1305:" 48724985ffSEd Tanous "DHE-RSA-AES128-GCM-SHA256:" 49724985ffSEd Tanous "DHE-RSA-AES256-GCM-SHA384:" 50724985ffSEd Tanous "DHE-RSA-CHACHA20-POLY1305"; 51724985ffSEd Tanous 52724985ffSEd Tanous // Trust chain related errors.` 53724985ffSEd Tanous bool isTrustChainError(int errnum) 54724985ffSEd Tanous { 55724985ffSEd Tanous return (errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || 56724985ffSEd Tanous (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) || 57724985ffSEd Tanous (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) || 58724985ffSEd Tanous (errnum == X509_V_ERR_CERT_UNTRUSTED) || 59724985ffSEd Tanous (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE); 60724985ffSEd Tanous } 61724985ffSEd Tanous 62724985ffSEd Tanous bool validateCertificate(X509* const cert) 63724985ffSEd Tanous { 64724985ffSEd Tanous // Create an empty X509_STORE structure for certificate validation. 65724985ffSEd Tanous X509_STORE* x509Store = X509_STORE_new(); 66724985ffSEd Tanous if (x509Store == nullptr) 67724985ffSEd Tanous { 68724985ffSEd Tanous BMCWEB_LOG_ERROR("Error occurred during X509_STORE_new call"); 69724985ffSEd Tanous return false; 70724985ffSEd Tanous } 71724985ffSEd Tanous 72724985ffSEd Tanous // Load Certificate file into the X509 structure. 73724985ffSEd Tanous X509_STORE_CTX* storeCtx = X509_STORE_CTX_new(); 74724985ffSEd Tanous if (storeCtx == nullptr) 75724985ffSEd Tanous { 76724985ffSEd Tanous BMCWEB_LOG_ERROR("Error occurred during X509_STORE_CTX_new call"); 77724985ffSEd Tanous X509_STORE_free(x509Store); 78724985ffSEd Tanous return false; 79724985ffSEd Tanous } 80724985ffSEd Tanous 81724985ffSEd Tanous int errCode = X509_STORE_CTX_init(storeCtx, x509Store, cert, nullptr); 82724985ffSEd Tanous if (errCode != 1) 83724985ffSEd Tanous { 84724985ffSEd Tanous BMCWEB_LOG_ERROR("Error occurred during X509_STORE_CTX_init call"); 85724985ffSEd Tanous X509_STORE_CTX_free(storeCtx); 86724985ffSEd Tanous X509_STORE_free(x509Store); 87724985ffSEd Tanous return false; 88724985ffSEd Tanous } 89724985ffSEd Tanous 90724985ffSEd Tanous errCode = X509_verify_cert(storeCtx); 91724985ffSEd Tanous if (errCode == 1) 92724985ffSEd Tanous { 93724985ffSEd Tanous BMCWEB_LOG_INFO("Certificate verification is success"); 94724985ffSEd Tanous X509_STORE_CTX_free(storeCtx); 95724985ffSEd Tanous X509_STORE_free(x509Store); 96724985ffSEd Tanous return true; 97724985ffSEd Tanous } 98724985ffSEd Tanous if (errCode == 0) 99724985ffSEd Tanous { 100724985ffSEd Tanous errCode = X509_STORE_CTX_get_error(storeCtx); 101724985ffSEd Tanous X509_STORE_CTX_free(storeCtx); 102724985ffSEd Tanous X509_STORE_free(x509Store); 103724985ffSEd Tanous if (isTrustChainError(errCode)) 104724985ffSEd Tanous { 105724985ffSEd Tanous BMCWEB_LOG_DEBUG("Ignoring Trust Chain error. Reason: {}", 106724985ffSEd Tanous X509_verify_cert_error_string(errCode)); 107724985ffSEd Tanous return true; 108724985ffSEd Tanous } 109724985ffSEd Tanous BMCWEB_LOG_ERROR("Certificate verification failed. Reason: {}", 110724985ffSEd Tanous X509_verify_cert_error_string(errCode)); 111724985ffSEd Tanous return false; 112724985ffSEd Tanous } 113724985ffSEd Tanous 114724985ffSEd Tanous BMCWEB_LOG_ERROR( 115724985ffSEd Tanous "Error occurred during X509_verify_cert call. ErrorCode: {}", errCode); 116724985ffSEd Tanous X509_STORE_CTX_free(storeCtx); 117724985ffSEd Tanous X509_STORE_free(x509Store); 118724985ffSEd Tanous return false; 119724985ffSEd Tanous } 120724985ffSEd Tanous 121724985ffSEd Tanous std::string verifyOpensslKeyCert(const std::string& filepath) 122724985ffSEd Tanous { 123724985ffSEd Tanous bool privateKeyValid = false; 124724985ffSEd Tanous 125724985ffSEd Tanous BMCWEB_LOG_INFO("Checking certs in file {}", filepath); 126724985ffSEd Tanous boost::beast::file_posix file; 127724985ffSEd Tanous boost::system::error_code ec; 128724985ffSEd Tanous file.open(filepath.c_str(), boost::beast::file_mode::read, ec); 129724985ffSEd Tanous if (ec) 130724985ffSEd Tanous { 131724985ffSEd Tanous return ""; 132724985ffSEd Tanous } 133724985ffSEd Tanous bool certValid = false; 134724985ffSEd Tanous std::string fileContents; 135724985ffSEd Tanous fileContents.resize(static_cast<size_t>(file.size(ec)), '\0'); 136724985ffSEd Tanous file.read(fileContents.data(), fileContents.size(), ec); 137724985ffSEd Tanous if (ec) 138724985ffSEd Tanous { 139724985ffSEd Tanous BMCWEB_LOG_ERROR("Failed to read file"); 140724985ffSEd Tanous return ""; 141724985ffSEd Tanous } 142724985ffSEd Tanous 143724985ffSEd Tanous BIO* bufio = BIO_new_mem_buf(static_cast<void*>(fileContents.data()), 144724985ffSEd Tanous static_cast<int>(fileContents.size())); 145724985ffSEd Tanous EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bufio, nullptr, nullptr, nullptr); 146724985ffSEd Tanous BIO_free(bufio); 147724985ffSEd Tanous if (pkey != nullptr) 148724985ffSEd Tanous { 149*bd79bce8SPatrick Williams EVP_PKEY_CTX* pkeyCtx = 150*bd79bce8SPatrick Williams EVP_PKEY_CTX_new_from_pkey(nullptr, pkey, nullptr); 151724985ffSEd Tanous 152724985ffSEd Tanous if (pkeyCtx == nullptr) 153724985ffSEd Tanous { 154724985ffSEd Tanous BMCWEB_LOG_ERROR("Unable to allocate pkeyCtx {}", ERR_get_error()); 155724985ffSEd Tanous } 156724985ffSEd Tanous else if (EVP_PKEY_check(pkeyCtx) == 1) 157724985ffSEd Tanous { 158724985ffSEd Tanous privateKeyValid = true; 159724985ffSEd Tanous } 160724985ffSEd Tanous else 161724985ffSEd Tanous { 162724985ffSEd Tanous BMCWEB_LOG_ERROR("Key not valid error number {}", ERR_get_error()); 163724985ffSEd Tanous } 164724985ffSEd Tanous 165724985ffSEd Tanous if (privateKeyValid) 166724985ffSEd Tanous { 167724985ffSEd Tanous BIO* bufio2 = 168724985ffSEd Tanous BIO_new_mem_buf(static_cast<void*>(fileContents.data()), 169724985ffSEd Tanous static_cast<int>(fileContents.size())); 170724985ffSEd Tanous X509* x509 = PEM_read_bio_X509(bufio2, nullptr, nullptr, nullptr); 171724985ffSEd Tanous BIO_free(bufio2); 172724985ffSEd Tanous if (x509 == nullptr) 173724985ffSEd Tanous { 174724985ffSEd Tanous BMCWEB_LOG_ERROR("error getting x509 cert {}", ERR_get_error()); 175724985ffSEd Tanous } 176724985ffSEd Tanous else 177724985ffSEd Tanous { 178724985ffSEd Tanous certValid = validateCertificate(x509); 179724985ffSEd Tanous X509_free(x509); 180724985ffSEd Tanous } 181724985ffSEd Tanous } 182724985ffSEd Tanous 183724985ffSEd Tanous EVP_PKEY_CTX_free(pkeyCtx); 184724985ffSEd Tanous EVP_PKEY_free(pkey); 185724985ffSEd Tanous } 186724985ffSEd Tanous if (!certValid) 187724985ffSEd Tanous { 188724985ffSEd Tanous return ""; 189724985ffSEd Tanous } 190724985ffSEd Tanous return fileContents; 191724985ffSEd Tanous } 192724985ffSEd Tanous 193724985ffSEd Tanous X509* loadCert(const std::string& filePath) 194724985ffSEd Tanous { 195724985ffSEd Tanous BIO* certFileBio = BIO_new_file(filePath.c_str(), "rb"); 196724985ffSEd Tanous if (certFileBio == nullptr) 197724985ffSEd Tanous { 198724985ffSEd Tanous BMCWEB_LOG_ERROR("Error occurred during BIO_new_file call, FILE= {}", 199724985ffSEd Tanous filePath); 200724985ffSEd Tanous return nullptr; 201724985ffSEd Tanous } 202724985ffSEd Tanous 203724985ffSEd Tanous X509* cert = X509_new(); 204724985ffSEd Tanous if (cert == nullptr) 205724985ffSEd Tanous { 206724985ffSEd Tanous BMCWEB_LOG_ERROR("Error occurred during X509_new call, {}", 207724985ffSEd Tanous ERR_get_error()); 208724985ffSEd Tanous BIO_free(certFileBio); 209724985ffSEd Tanous return nullptr; 210724985ffSEd Tanous } 211724985ffSEd Tanous 212724985ffSEd Tanous if (PEM_read_bio_X509(certFileBio, &cert, nullptr, nullptr) == nullptr) 213724985ffSEd Tanous { 214724985ffSEd Tanous BMCWEB_LOG_ERROR( 215724985ffSEd Tanous "Error occurred during PEM_read_bio_X509 call, FILE= {}", filePath); 216724985ffSEd Tanous 217724985ffSEd Tanous BIO_free(certFileBio); 218724985ffSEd Tanous X509_free(cert); 219724985ffSEd Tanous return nullptr; 220724985ffSEd Tanous } 221724985ffSEd Tanous BIO_free(certFileBio); 222724985ffSEd Tanous return cert; 223724985ffSEd Tanous } 224724985ffSEd Tanous 225724985ffSEd Tanous int addExt(X509* cert, int nid, const char* value) 226724985ffSEd Tanous { 227724985ffSEd Tanous X509_EXTENSION* ex = nullptr; 228724985ffSEd Tanous X509V3_CTX ctx{}; 229724985ffSEd Tanous X509V3_set_ctx(&ctx, cert, cert, nullptr, nullptr, 0); 230724985ffSEd Tanous 231724985ffSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) 232724985ffSEd Tanous ex = X509V3_EXT_conf_nid(nullptr, &ctx, nid, const_cast<char*>(value)); 233724985ffSEd Tanous if (ex == nullptr) 234724985ffSEd Tanous { 235724985ffSEd Tanous BMCWEB_LOG_ERROR("Error: In X509V3_EXT_conf_nidn: {}", value); 236724985ffSEd Tanous return -1; 237724985ffSEd Tanous } 238724985ffSEd Tanous X509_add_ext(cert, ex, -1); 239724985ffSEd Tanous X509_EXTENSION_free(ex); 240724985ffSEd Tanous return 0; 241724985ffSEd Tanous } 242724985ffSEd Tanous 243724985ffSEd Tanous // Writes a certificate to a path, ignoring errors 244724985ffSEd Tanous void writeCertificateToFile(const std::string& filepath, 245724985ffSEd Tanous const std::string& certificate) 246724985ffSEd Tanous { 247724985ffSEd Tanous boost::system::error_code ec; 248724985ffSEd Tanous boost::beast::file_posix file; 249724985ffSEd Tanous file.open(filepath.c_str(), boost::beast::file_mode::write, ec); 250724985ffSEd Tanous if (!ec) 251724985ffSEd Tanous { 252724985ffSEd Tanous file.write(certificate.data(), certificate.size(), ec); 253724985ffSEd Tanous // ignore result 254724985ffSEd Tanous } 255724985ffSEd Tanous } 256724985ffSEd Tanous 257724985ffSEd Tanous std::string generateSslCertificate(const std::string& cn) 258724985ffSEd Tanous { 259724985ffSEd Tanous BMCWEB_LOG_INFO("Generating new keys"); 260724985ffSEd Tanous 261724985ffSEd Tanous std::string buffer; 262724985ffSEd Tanous BMCWEB_LOG_INFO("Generating EC key"); 263724985ffSEd Tanous EVP_PKEY* pPrivKey = createEcKey(); 264724985ffSEd Tanous if (pPrivKey != nullptr) 265724985ffSEd Tanous { 266724985ffSEd Tanous BMCWEB_LOG_INFO("Generating x509 Certificates"); 267724985ffSEd Tanous // Use this code to directly generate a certificate 268724985ffSEd Tanous X509* x509 = X509_new(); 269724985ffSEd Tanous if (x509 != nullptr) 270724985ffSEd Tanous { 271724985ffSEd Tanous // get a random number from the RNG for the certificate serial 272724985ffSEd Tanous // number If this is not random, regenerating certs throws browser 273724985ffSEd Tanous // errors 274724985ffSEd Tanous bmcweb::OpenSSLGenerator gen; 275724985ffSEd Tanous std::uniform_int_distribution<int> dis( 276724985ffSEd Tanous 1, std::numeric_limits<int>::max()); 277724985ffSEd Tanous int serial = dis(gen); 278724985ffSEd Tanous 279724985ffSEd Tanous ASN1_INTEGER_set(X509_get_serialNumber(x509), serial); 280724985ffSEd Tanous 281724985ffSEd Tanous // not before this moment 282724985ffSEd Tanous X509_gmtime_adj(X509_get_notBefore(x509), 0); 283724985ffSEd Tanous // Cert is valid for 10 years 284724985ffSEd Tanous X509_gmtime_adj(X509_get_notAfter(x509), 285724985ffSEd Tanous 60L * 60L * 24L * 365L * 10L); 286724985ffSEd Tanous 287724985ffSEd Tanous // set the public key to the key we just generated 288724985ffSEd Tanous X509_set_pubkey(x509, pPrivKey); 289724985ffSEd Tanous 290724985ffSEd Tanous // get the subject name 291724985ffSEd Tanous X509_NAME* name = X509_get_subject_name(x509); 292724985ffSEd Tanous 293724985ffSEd Tanous using x509String = const unsigned char; 294724985ffSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 295724985ffSEd Tanous x509String* country = reinterpret_cast<x509String*>("US"); 296724985ffSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 297724985ffSEd Tanous x509String* company = reinterpret_cast<x509String*>("OpenBMC"); 298724985ffSEd Tanous // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) 299724985ffSEd Tanous x509String* cnStr = reinterpret_cast<x509String*>(cn.c_str()); 300724985ffSEd Tanous 301724985ffSEd Tanous X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, country, -1, -1, 302724985ffSEd Tanous 0); 303724985ffSEd Tanous X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, company, -1, -1, 304724985ffSEd Tanous 0); 305724985ffSEd Tanous X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cnStr, -1, -1, 306724985ffSEd Tanous 0); 307724985ffSEd Tanous // set the CSR options 308724985ffSEd Tanous X509_set_issuer_name(x509, name); 309724985ffSEd Tanous 310724985ffSEd Tanous X509_set_version(x509, 2); 311724985ffSEd Tanous addExt(x509, NID_basic_constraints, ("critical,CA:TRUE")); 312724985ffSEd Tanous addExt(x509, NID_subject_alt_name, ("DNS:" + cn).c_str()); 313724985ffSEd Tanous addExt(x509, NID_subject_key_identifier, ("hash")); 314724985ffSEd Tanous addExt(x509, NID_authority_key_identifier, ("keyid")); 315724985ffSEd Tanous addExt(x509, NID_key_usage, ("digitalSignature, keyEncipherment")); 316724985ffSEd Tanous addExt(x509, NID_ext_key_usage, ("serverAuth")); 317724985ffSEd Tanous addExt(x509, NID_netscape_comment, (x509Comment)); 318724985ffSEd Tanous 319724985ffSEd Tanous // Sign the certificate with our private key 320724985ffSEd Tanous X509_sign(x509, pPrivKey, EVP_sha256()); 321724985ffSEd Tanous 322724985ffSEd Tanous BIO* bufio = BIO_new(BIO_s_mem()); 323724985ffSEd Tanous 324724985ffSEd Tanous int pkeyRet = PEM_write_bio_PrivateKey( 325724985ffSEd Tanous bufio, pPrivKey, nullptr, nullptr, 0, nullptr, nullptr); 326724985ffSEd Tanous if (pkeyRet <= 0) 327724985ffSEd Tanous { 328724985ffSEd Tanous BMCWEB_LOG_ERROR( 329724985ffSEd Tanous "Failed to write pkey with code {}. Ignoring.", pkeyRet); 330724985ffSEd Tanous } 331724985ffSEd Tanous 332724985ffSEd Tanous char* data = nullptr; 333724985ffSEd Tanous long int dataLen = BIO_get_mem_data(bufio, &data); 334724985ffSEd Tanous buffer += std::string_view(data, static_cast<size_t>(dataLen)); 335724985ffSEd Tanous BIO_free(bufio); 336724985ffSEd Tanous 337724985ffSEd Tanous bufio = BIO_new(BIO_s_mem()); 338724985ffSEd Tanous pkeyRet = PEM_write_bio_X509(bufio, x509); 339724985ffSEd Tanous if (pkeyRet <= 0) 340724985ffSEd Tanous { 341724985ffSEd Tanous BMCWEB_LOG_ERROR( 342724985ffSEd Tanous "Failed to write X509 with code {}. Ignoring.", pkeyRet); 343724985ffSEd Tanous } 344724985ffSEd Tanous dataLen = BIO_get_mem_data(bufio, &data); 345724985ffSEd Tanous buffer += std::string_view(data, static_cast<size_t>(dataLen)); 346724985ffSEd Tanous 347724985ffSEd Tanous BIO_free(bufio); 348724985ffSEd Tanous BMCWEB_LOG_INFO("Cert size is {}", buffer.size()); 349724985ffSEd Tanous X509_free(x509); 350724985ffSEd Tanous } 351724985ffSEd Tanous 352724985ffSEd Tanous EVP_PKEY_free(pPrivKey); 353724985ffSEd Tanous pPrivKey = nullptr; 354724985ffSEd Tanous } 355724985ffSEd Tanous 356724985ffSEd Tanous // cleanup_openssl(); 357724985ffSEd Tanous return buffer; 358724985ffSEd Tanous } 359724985ffSEd Tanous 360724985ffSEd Tanous EVP_PKEY* createEcKey() 361724985ffSEd Tanous { 362724985ffSEd Tanous EVP_PKEY* pKey = nullptr; 363724985ffSEd Tanous 364724985ffSEd Tanous // Create context for curve parameter generation. 365724985ffSEd Tanous std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)> ctx{ 366724985ffSEd Tanous EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free}; 367724985ffSEd Tanous if (!ctx) 368724985ffSEd Tanous { 369724985ffSEd Tanous return nullptr; 370724985ffSEd Tanous } 371724985ffSEd Tanous 372724985ffSEd Tanous // Set up curve parameters. 373724985ffSEd Tanous EVP_PKEY* params = nullptr; 374724985ffSEd Tanous if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) || 375724985ffSEd Tanous (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <= 376724985ffSEd Tanous 0) || 377724985ffSEd Tanous (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_secp384r1) <= 378724985ffSEd Tanous 0) || 379724985ffSEd Tanous (EVP_PKEY_paramgen(ctx.get(), ¶ms) <= 0)) 380724985ffSEd Tanous { 381724985ffSEd Tanous return nullptr; 382724985ffSEd Tanous } 383724985ffSEd Tanous 384724985ffSEd Tanous // Set up RAII holder for params. 385724985ffSEd Tanous std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)> pparams{ 386724985ffSEd Tanous params, &::EVP_PKEY_free}; 387724985ffSEd Tanous 388724985ffSEd Tanous // Set new context for key generation, using curve parameters. 389724985ffSEd Tanous ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr)); 390724985ffSEd Tanous if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0)) 391724985ffSEd Tanous { 392724985ffSEd Tanous return nullptr; 393724985ffSEd Tanous } 394724985ffSEd Tanous 395724985ffSEd Tanous // Generate key. 396724985ffSEd Tanous if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0) 397724985ffSEd Tanous { 398724985ffSEd Tanous return nullptr; 399724985ffSEd Tanous } 400724985ffSEd Tanous 401724985ffSEd Tanous return pKey; 402724985ffSEd Tanous } 403724985ffSEd Tanous 404724985ffSEd Tanous std::string ensureOpensslKeyPresentAndValid(const std::string& filepath) 405724985ffSEd Tanous { 406724985ffSEd Tanous std::string cert = verifyOpensslKeyCert(filepath); 407724985ffSEd Tanous 408724985ffSEd Tanous if (cert.empty()) 409724985ffSEd Tanous { 410724985ffSEd Tanous BMCWEB_LOG_WARNING("Error in verifying signature, regenerating"); 411724985ffSEd Tanous cert = generateSslCertificate("testhost"); 412724985ffSEd Tanous if (cert.empty()) 413724985ffSEd Tanous { 414724985ffSEd Tanous BMCWEB_LOG_ERROR("Failed to generate cert"); 415724985ffSEd Tanous } 416724985ffSEd Tanous else 417724985ffSEd Tanous { 418724985ffSEd Tanous writeCertificateToFile(filepath, cert); 419724985ffSEd Tanous } 420724985ffSEd Tanous } 421724985ffSEd Tanous return cert; 422724985ffSEd Tanous } 423724985ffSEd Tanous 424724985ffSEd Tanous static std::string ensureCertificate() 425724985ffSEd Tanous { 426724985ffSEd Tanous namespace fs = std::filesystem; 427724985ffSEd Tanous // Cleanup older certificate file existing in the system 428724985ffSEd Tanous fs::path oldcertPath = fs::path("/home/root/server.pem"); 429724985ffSEd Tanous std::error_code ec; 430724985ffSEd Tanous fs::remove(oldcertPath, ec); 431724985ffSEd Tanous // Ignore failure to remove; File might not exist. 432724985ffSEd Tanous 433724985ffSEd Tanous fs::path certPath = "/etc/ssl/certs/https/"; 434724985ffSEd Tanous // if path does not exist create the path so that 435724985ffSEd Tanous // self signed certificate can be created in the 436724985ffSEd Tanous // path 437724985ffSEd Tanous fs::path certFile = certPath / "server.pem"; 438724985ffSEd Tanous 439724985ffSEd Tanous if (!fs::exists(certPath, ec)) 440724985ffSEd Tanous { 441724985ffSEd Tanous fs::create_directories(certPath, ec); 442724985ffSEd Tanous } 443724985ffSEd Tanous BMCWEB_LOG_INFO("Building SSL Context file= {}", certFile.string()); 444724985ffSEd Tanous std::string sslPemFile(certFile); 445724985ffSEd Tanous return ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile); 446724985ffSEd Tanous } 447724985ffSEd Tanous 448724985ffSEd Tanous static int nextProtoCallback(SSL* /*unused*/, const unsigned char** data, 449724985ffSEd Tanous unsigned int* len, void* /*unused*/) 450724985ffSEd Tanous { 451724985ffSEd Tanous // First byte is the length. 452724985ffSEd Tanous constexpr std::string_view h2 = "\x02h2"; 453724985ffSEd Tanous *data = std::bit_cast<const unsigned char*>(h2.data()); 454724985ffSEd Tanous *len = static_cast<unsigned int>(h2.size()); 455724985ffSEd Tanous return SSL_TLSEXT_ERR_OK; 456724985ffSEd Tanous } 457724985ffSEd Tanous 458*bd79bce8SPatrick Williams static int alpnSelectProtoCallback( 459*bd79bce8SPatrick Williams SSL* /*unused*/, const unsigned char** out, unsigned char* outlen, 460*bd79bce8SPatrick Williams const unsigned char* in, unsigned int inlen, void* /*unused*/) 461724985ffSEd Tanous { 462fa2d6ae7SEd Tanous int rv = nghttp2_select_alpn(out, outlen, in, inlen); 463fa2d6ae7SEd Tanous if (rv == -1) 464724985ffSEd Tanous { 465724985ffSEd Tanous return SSL_TLSEXT_ERR_NOACK; 466724985ffSEd Tanous } 467fa2d6ae7SEd Tanous if (rv == 1) 468fa2d6ae7SEd Tanous { 469fa2d6ae7SEd Tanous BMCWEB_LOG_DEBUG("Selected HTTP2"); 470fa2d6ae7SEd Tanous } 471724985ffSEd Tanous return SSL_TLSEXT_ERR_OK; 472724985ffSEd Tanous } 473724985ffSEd Tanous 474724985ffSEd Tanous static bool getSslContext(boost::asio::ssl::context& mSslContext, 475724985ffSEd Tanous const std::string& sslPemFile) 476724985ffSEd Tanous { 477*bd79bce8SPatrick Williams mSslContext.set_options( 478*bd79bce8SPatrick Williams boost::asio::ssl::context::default_workarounds | 479724985ffSEd Tanous boost::asio::ssl::context::no_sslv2 | 480724985ffSEd Tanous boost::asio::ssl::context::no_sslv3 | 481724985ffSEd Tanous boost::asio::ssl::context::single_dh_use | 482724985ffSEd Tanous boost::asio::ssl::context::no_tlsv1 | 483724985ffSEd Tanous boost::asio::ssl::context::no_tlsv1_1); 484724985ffSEd Tanous 485724985ffSEd Tanous BMCWEB_LOG_DEBUG("Using default TrustStore location: {}", trustStorePath); 486724985ffSEd Tanous mSslContext.add_verify_path(trustStorePath); 487724985ffSEd Tanous 488724985ffSEd Tanous if (!sslPemFile.empty()) 489724985ffSEd Tanous { 490724985ffSEd Tanous boost::system::error_code ec; 491724985ffSEd Tanous 492724985ffSEd Tanous boost::asio::const_buffer buf(sslPemFile.data(), sslPemFile.size()); 493724985ffSEd Tanous mSslContext.use_certificate(buf, boost::asio::ssl::context::pem, ec); 494724985ffSEd Tanous if (ec) 495724985ffSEd Tanous { 496724985ffSEd Tanous return false; 497724985ffSEd Tanous } 498724985ffSEd Tanous mSslContext.use_private_key(buf, boost::asio::ssl::context::pem, ec); 499724985ffSEd Tanous if (ec) 500724985ffSEd Tanous { 501724985ffSEd Tanous BMCWEB_LOG_CRITICAL("Failed to open ssl pkey"); 502724985ffSEd Tanous return false; 503724985ffSEd Tanous } 504724985ffSEd Tanous } 505724985ffSEd Tanous 506724985ffSEd Tanous // Set up EC curves to auto (boost asio doesn't have a method for this) 507724985ffSEd Tanous // There is a pull request to add this. Once this is included in an asio 508724985ffSEd Tanous // drop, use the right way 509724985ffSEd Tanous // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue 510724985ffSEd Tanous if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1) 511724985ffSEd Tanous {} 512724985ffSEd Tanous 513724985ffSEd Tanous if (SSL_CTX_set_cipher_list(mSslContext.native_handle(), 514724985ffSEd Tanous mozillaIntermediate) != 1) 515724985ffSEd Tanous { 516724985ffSEd Tanous BMCWEB_LOG_ERROR("Error setting cipher list"); 517724985ffSEd Tanous return false; 518724985ffSEd Tanous } 519724985ffSEd Tanous return true; 520724985ffSEd Tanous } 521724985ffSEd Tanous 522724985ffSEd Tanous std::shared_ptr<boost::asio::ssl::context> getSslServerContext() 523724985ffSEd Tanous { 524724985ffSEd Tanous boost::asio::ssl::context sslCtx(boost::asio::ssl::context::tls_server); 525724985ffSEd Tanous 526724985ffSEd Tanous auto certFile = ensureCertificate(); 527724985ffSEd Tanous if (!getSslContext(sslCtx, certFile)) 528724985ffSEd Tanous { 529724985ffSEd Tanous BMCWEB_LOG_CRITICAL("Couldn't get server context"); 530724985ffSEd Tanous return nullptr; 531724985ffSEd Tanous } 532724985ffSEd Tanous const persistent_data::AuthConfigMethods& c = 533724985ffSEd Tanous persistent_data::SessionStore::getInstance().getAuthMethodsConfig(); 534724985ffSEd Tanous 535724985ffSEd Tanous boost::asio::ssl::verify_mode mode = boost::asio::ssl::verify_peer; 536724985ffSEd Tanous if (c.tlsStrict) 537724985ffSEd Tanous { 538724985ffSEd Tanous BMCWEB_LOG_DEBUG("Setting verify peer"); 539724985ffSEd Tanous mode |= boost::asio::ssl::verify_fail_if_no_peer_cert; 540724985ffSEd Tanous } 541724985ffSEd Tanous 542724985ffSEd Tanous boost::system::error_code ec; 543724985ffSEd Tanous sslCtx.set_verify_mode(mode, ec); 544724985ffSEd Tanous if (ec) 545724985ffSEd Tanous { 546724985ffSEd Tanous BMCWEB_LOG_DEBUG("Failed to set verify mode {}", ec.message()); 547724985ffSEd Tanous return nullptr; 548724985ffSEd Tanous } 549724985ffSEd Tanous SSL_CTX_set_options(sslCtx.native_handle(), SSL_OP_NO_RENEGOTIATION); 550724985ffSEd Tanous 551724985ffSEd Tanous if constexpr (BMCWEB_EXPERIMENTAL_HTTP2) 552724985ffSEd Tanous { 553724985ffSEd Tanous SSL_CTX_set_next_protos_advertised_cb(sslCtx.native_handle(), 554724985ffSEd Tanous nextProtoCallback, nullptr); 555724985ffSEd Tanous 556724985ffSEd Tanous SSL_CTX_set_alpn_select_cb(sslCtx.native_handle(), 557724985ffSEd Tanous alpnSelectProtoCallback, nullptr); 558724985ffSEd Tanous } 559724985ffSEd Tanous 560724985ffSEd Tanous return std::make_shared<boost::asio::ssl::context>(std::move(sslCtx)); 561724985ffSEd Tanous } 562724985ffSEd Tanous 563724985ffSEd Tanous std::optional<boost::asio::ssl::context> 564724985ffSEd Tanous getSSLClientContext(VerifyCertificate verifyCertificate) 565724985ffSEd Tanous { 566724985ffSEd Tanous namespace fs = std::filesystem; 567724985ffSEd Tanous 568724985ffSEd Tanous boost::asio::ssl::context sslCtx(boost::asio::ssl::context::tls_client); 569724985ffSEd Tanous 570724985ffSEd Tanous // NOTE, this path is temporary; In the future it will need to change to 571724985ffSEd Tanous // be set per subscription. Do not rely on this. 572724985ffSEd Tanous fs::path certPath = "/etc/ssl/certs/https/client.pem"; 573724985ffSEd Tanous std::string cert = verifyOpensslKeyCert(certPath); 574724985ffSEd Tanous 575724985ffSEd Tanous if (!getSslContext(sslCtx, cert)) 576724985ffSEd Tanous { 577724985ffSEd Tanous return std::nullopt; 578724985ffSEd Tanous } 579724985ffSEd Tanous 580724985ffSEd Tanous // Add a directory containing certificate authority files to be used 581724985ffSEd Tanous // for performing verification. 582724985ffSEd Tanous boost::system::error_code ec; 583724985ffSEd Tanous sslCtx.set_default_verify_paths(ec); 584724985ffSEd Tanous if (ec) 585724985ffSEd Tanous { 586724985ffSEd Tanous BMCWEB_LOG_ERROR("SSL context set_default_verify failed"); 587724985ffSEd Tanous return std::nullopt; 588724985ffSEd Tanous } 589724985ffSEd Tanous 590724985ffSEd Tanous int mode = boost::asio::ssl::verify_peer; 591724985ffSEd Tanous if (verifyCertificate == VerifyCertificate::NoVerify) 592724985ffSEd Tanous { 593724985ffSEd Tanous mode = boost::asio::ssl::verify_none; 594724985ffSEd Tanous } 595724985ffSEd Tanous 596724985ffSEd Tanous // Verify the remote server's certificate 597724985ffSEd Tanous sslCtx.set_verify_mode(mode, ec); 598724985ffSEd Tanous if (ec) 599724985ffSEd Tanous { 600724985ffSEd Tanous BMCWEB_LOG_ERROR("SSL context set_verify_mode failed"); 601724985ffSEd Tanous return std::nullopt; 602724985ffSEd Tanous } 603724985ffSEd Tanous 604724985ffSEd Tanous if (SSL_CTX_set_cipher_list(sslCtx.native_handle(), mozillaIntermediate) != 605724985ffSEd Tanous 1) 606724985ffSEd Tanous { 607724985ffSEd Tanous BMCWEB_LOG_ERROR("SSL_CTX_set_cipher_list failed"); 608724985ffSEd Tanous return std::nullopt; 609724985ffSEd Tanous } 610724985ffSEd Tanous 611724985ffSEd Tanous return {std::move(sslCtx)}; 612724985ffSEd Tanous } 613724985ffSEd Tanous 614724985ffSEd Tanous } // namespace ensuressl 615