1 #pragma once
2
3 #include "bmcweb_config.h"
4
5 #include "logging.hpp"
6 #include "ossl_random.hpp"
7
8 #include <boost/beast/core/file_posix.hpp>
9
10 extern "C"
11 {
12 #include <nghttp2/nghttp2.h>
13 #include <openssl/bio.h>
14 #include <openssl/dh.h>
15 #include <openssl/dsa.h>
16 #include <openssl/err.h>
17 #include <openssl/evp.h>
18 #include <openssl/pem.h>
19 #include <openssl/rand.h>
20 #include <openssl/rsa.h>
21 #include <openssl/ssl.h>
22 }
23
24 #include <boost/asio/ssl/context.hpp>
25 #include <boost/system/error_code.hpp>
26
27 #include <filesystem>
28 #include <memory>
29 #include <optional>
30 #include <random>
31 #include <string>
32
33 namespace ensuressl
34 {
35 constexpr const char* trustStorePath = "/etc/ssl/certs/authority";
36 constexpr const char* x509Comment = "Generated from OpenBMC service";
37 static void initOpenssl();
38 static EVP_PKEY* createEcKey();
39
40 // Trust chain related errors.`
isTrustChainError(int errnum)41 inline bool isTrustChainError(int errnum)
42 {
43 return (errnum == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) ||
44 (errnum == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) ||
45 (errnum == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY) ||
46 (errnum == X509_V_ERR_CERT_UNTRUSTED) ||
47 (errnum == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE);
48 }
49
validateCertificate(X509 * const cert)50 inline bool validateCertificate(X509* const cert)
51 {
52 // Create an empty X509_STORE structure for certificate validation.
53 X509_STORE* x509Store = X509_STORE_new();
54 if (x509Store == nullptr)
55 {
56 BMCWEB_LOG_ERROR("Error occurred during X509_STORE_new call");
57 return false;
58 }
59
60 // Load Certificate file into the X509 structure.
61 X509_STORE_CTX* storeCtx = X509_STORE_CTX_new();
62 if (storeCtx == nullptr)
63 {
64 BMCWEB_LOG_ERROR("Error occurred during X509_STORE_CTX_new call");
65 X509_STORE_free(x509Store);
66 return false;
67 }
68
69 int errCode = X509_STORE_CTX_init(storeCtx, x509Store, cert, nullptr);
70 if (errCode != 1)
71 {
72 BMCWEB_LOG_ERROR("Error occurred during X509_STORE_CTX_init call");
73 X509_STORE_CTX_free(storeCtx);
74 X509_STORE_free(x509Store);
75 return false;
76 }
77
78 errCode = X509_verify_cert(storeCtx);
79 if (errCode == 1)
80 {
81 BMCWEB_LOG_INFO("Certificate verification is success");
82 X509_STORE_CTX_free(storeCtx);
83 X509_STORE_free(x509Store);
84 return true;
85 }
86 if (errCode == 0)
87 {
88 errCode = X509_STORE_CTX_get_error(storeCtx);
89 X509_STORE_CTX_free(storeCtx);
90 X509_STORE_free(x509Store);
91 if (isTrustChainError(errCode))
92 {
93 BMCWEB_LOG_DEBUG("Ignoring Trust Chain error. Reason: {}",
94 X509_verify_cert_error_string(errCode));
95 return true;
96 }
97 BMCWEB_LOG_ERROR("Certificate verification failed. Reason: {}",
98 X509_verify_cert_error_string(errCode));
99 return false;
100 }
101
102 BMCWEB_LOG_ERROR(
103 "Error occurred during X509_verify_cert call. ErrorCode: {}", errCode);
104 X509_STORE_CTX_free(storeCtx);
105 X509_STORE_free(x509Store);
106 return false;
107 }
108
verifyOpensslKeyCert(const std::string & filepath)109 inline std::string verifyOpensslKeyCert(const std::string& filepath)
110 {
111 bool privateKeyValid = false;
112
113 BMCWEB_LOG_INFO("Checking certs in file {}", filepath);
114 boost::beast::file_posix file;
115 boost::system::error_code ec;
116 file.open(filepath.c_str(), boost::beast::file_mode::read, ec);
117 if (ec)
118 {
119 return "";
120 }
121 bool certValid = false;
122 std::string fileContents;
123 fileContents.resize(static_cast<size_t>(file.size(ec)), '\0');
124 file.read(fileContents.data(), fileContents.size(), ec);
125 if (ec)
126 {
127 BMCWEB_LOG_ERROR("Failed to read file");
128 return "";
129 }
130
131 BIO* bufio = BIO_new_mem_buf(static_cast<void*>(fileContents.data()),
132 static_cast<int>(fileContents.size()));
133 EVP_PKEY* pkey = PEM_read_bio_PrivateKey(bufio, nullptr, nullptr, nullptr);
134 BIO_free(bufio);
135 if (pkey != nullptr)
136 {
137 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
138 RSA* rsa = EVP_PKEY_get1_RSA(pkey);
139 if (rsa != nullptr)
140 {
141 BMCWEB_LOG_INFO("Found an RSA key");
142 if (RSA_check_key(rsa) == 1)
143 {
144 privateKeyValid = true;
145 }
146 else
147 {
148 BMCWEB_LOG_ERROR("Key not valid error number {}",
149 ERR_get_error());
150 }
151 RSA_free(rsa);
152 }
153 else
154 {
155 EC_KEY* ec = EVP_PKEY_get1_EC_KEY(pkey);
156 if (ec != nullptr)
157 {
158 BMCWEB_LOG_INFO("Found an EC key");
159 if (EC_KEY_check_key(ec) == 1)
160 {
161 privateKeyValid = true;
162 }
163 else
164 {
165 BMCWEB_LOG_ERROR("Key not valid error number {}",
166 ERR_get_error());
167 }
168 EC_KEY_free(ec);
169 }
170 }
171 #else
172 EVP_PKEY_CTX* pkeyCtx = EVP_PKEY_CTX_new_from_pkey(nullptr, pkey,
173 nullptr);
174
175 if (pkeyCtx == nullptr)
176 {
177 BMCWEB_LOG_ERROR("Unable to allocate pkeyCtx {}", ERR_get_error());
178 }
179 else if (EVP_PKEY_check(pkeyCtx) == 1)
180 {
181 privateKeyValid = true;
182 }
183 else
184 {
185 BMCWEB_LOG_ERROR("Key not valid error number {}", ERR_get_error());
186 }
187 #endif
188
189 if (privateKeyValid)
190 {
191 BIO* bufio2 =
192 BIO_new_mem_buf(static_cast<void*>(fileContents.data()),
193 static_cast<int>(fileContents.size()));
194 X509* x509 = PEM_read_bio_X509(bufio2, nullptr, nullptr, nullptr);
195 BIO_free(bufio2);
196 if (x509 == nullptr)
197 {
198 BMCWEB_LOG_ERROR("error getting x509 cert {}", ERR_get_error());
199 }
200 else
201 {
202 certValid = validateCertificate(x509);
203 X509_free(x509);
204 }
205 }
206
207 #if (OPENSSL_VERSION_NUMBER > 0x30000000L)
208 EVP_PKEY_CTX_free(pkeyCtx);
209 #endif
210 EVP_PKEY_free(pkey);
211 }
212 if (!certValid)
213 {
214 return "";
215 }
216 return fileContents;
217 }
218
loadCert(const std::string & filePath)219 inline X509* loadCert(const std::string& filePath)
220 {
221 BIO* certFileBio = BIO_new_file(filePath.c_str(), "rb");
222 if (certFileBio == nullptr)
223 {
224 BMCWEB_LOG_ERROR("Error occurred during BIO_new_file call, FILE= {}",
225 filePath);
226 return nullptr;
227 }
228
229 X509* cert = X509_new();
230 if (cert == nullptr)
231 {
232 BMCWEB_LOG_ERROR("Error occurred during X509_new call, {}",
233 ERR_get_error());
234 BIO_free(certFileBio);
235 return nullptr;
236 }
237
238 if (PEM_read_bio_X509(certFileBio, &cert, nullptr, nullptr) == nullptr)
239 {
240 BMCWEB_LOG_ERROR(
241 "Error occurred during PEM_read_bio_X509 call, FILE= {}", filePath);
242
243 BIO_free(certFileBio);
244 X509_free(cert);
245 return nullptr;
246 }
247 BIO_free(certFileBio);
248 return cert;
249 }
250
addExt(X509 * cert,int nid,const char * value)251 inline int addExt(X509* cert, int nid, const char* value)
252 {
253 X509_EXTENSION* ex = nullptr;
254 X509V3_CTX ctx{};
255 X509V3_set_ctx(&ctx, cert, cert, nullptr, nullptr, 0);
256
257 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
258 ex = X509V3_EXT_conf_nid(nullptr, &ctx, nid, const_cast<char*>(value));
259 if (ex == nullptr)
260 {
261 BMCWEB_LOG_ERROR("Error: In X509V3_EXT_conf_nidn: {}", value);
262 return -1;
263 }
264 X509_add_ext(cert, ex, -1);
265 X509_EXTENSION_free(ex);
266 return 0;
267 }
268
269 // Writes a certificate to a path, ignoring errors
writeCertificateToFile(const std::string & filepath,const std::string & certificate)270 inline void writeCertificateToFile(const std::string& filepath,
271 const std::string& certificate)
272 {
273 boost::system::error_code ec;
274 boost::beast::file_posix file;
275 file.open(filepath.c_str(), boost::beast::file_mode::write, ec);
276 if (!ec)
277 {
278 file.write(certificate.data(), certificate.size(), ec);
279 // ignore result
280 }
281 }
282
generateSslCertificate(const std::string & cn)283 inline std::string generateSslCertificate(const std::string& cn)
284 {
285 BMCWEB_LOG_INFO("Generating new keys");
286 initOpenssl();
287
288 std::string buffer;
289 BMCWEB_LOG_INFO("Generating EC key");
290 EVP_PKEY* pPrivKey = createEcKey();
291 if (pPrivKey != nullptr)
292 {
293 BMCWEB_LOG_INFO("Generating x509 Certificates");
294 // Use this code to directly generate a certificate
295 X509* x509 = X509_new();
296 if (x509 != nullptr)
297 {
298 // get a random number from the RNG for the certificate serial
299 // number If this is not random, regenerating certs throws browser
300 // errors
301 bmcweb::OpenSSLGenerator gen;
302 std::uniform_int_distribution<int> dis(
303 1, std::numeric_limits<int>::max());
304 int serial = dis(gen);
305
306 ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
307
308 // not before this moment
309 X509_gmtime_adj(X509_get_notBefore(x509), 0);
310 // Cert is valid for 10 years
311 X509_gmtime_adj(X509_get_notAfter(x509),
312 60L * 60L * 24L * 365L * 10L);
313
314 // set the public key to the key we just generated
315 X509_set_pubkey(x509, pPrivKey);
316
317 // get the subject name
318 X509_NAME* name = X509_get_subject_name(x509);
319
320 using x509String = const unsigned char;
321 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
322 x509String* country = reinterpret_cast<x509String*>("US");
323 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
324 x509String* company = reinterpret_cast<x509String*>("OpenBMC");
325 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
326 x509String* cnStr = reinterpret_cast<x509String*>(cn.c_str());
327
328 X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, country, -1, -1,
329 0);
330 X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC, company, -1, -1,
331 0);
332 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, cnStr, -1, -1,
333 0);
334 // set the CSR options
335 X509_set_issuer_name(x509, name);
336
337 X509_set_version(x509, 2);
338 addExt(x509, NID_basic_constraints, ("critical,CA:TRUE"));
339 addExt(x509, NID_subject_alt_name, ("DNS:" + cn).c_str());
340 addExt(x509, NID_subject_key_identifier, ("hash"));
341 addExt(x509, NID_authority_key_identifier, ("keyid"));
342 addExt(x509, NID_key_usage, ("digitalSignature, keyEncipherment"));
343 addExt(x509, NID_ext_key_usage, ("serverAuth"));
344 addExt(x509, NID_netscape_comment, (x509Comment));
345
346 // Sign the certificate with our private key
347 X509_sign(x509, pPrivKey, EVP_sha256());
348
349 BIO* bufio = BIO_new(BIO_s_mem());
350
351 int pkeyRet = PEM_write_bio_PrivateKey(
352 bufio, pPrivKey, nullptr, nullptr, 0, nullptr, nullptr);
353 if (pkeyRet <= 0)
354 {
355 BMCWEB_LOG_ERROR(
356 "Failed to write pkey with code {}. Ignoring.", pkeyRet);
357 }
358
359 char* data = nullptr;
360 long int dataLen = BIO_get_mem_data(bufio, &data);
361 buffer += std::string_view(data, static_cast<size_t>(dataLen));
362 BIO_free(bufio);
363
364 bufio = BIO_new(BIO_s_mem());
365 pkeyRet = PEM_write_bio_X509(bufio, x509);
366 if (pkeyRet <= 0)
367 {
368 BMCWEB_LOG_ERROR(
369 "Failed to write X509 with code {}. Ignoring.", pkeyRet);
370 }
371 dataLen = BIO_get_mem_data(bufio, &data);
372 buffer += std::string_view(data, static_cast<size_t>(dataLen));
373
374 BIO_free(bufio);
375 BMCWEB_LOG_INFO("Cert size is {}", buffer.size());
376 X509_free(x509);
377 }
378
379 EVP_PKEY_free(pPrivKey);
380 pPrivKey = nullptr;
381 }
382
383 // cleanup_openssl();
384 return buffer;
385 }
386
createEcKey()387 EVP_PKEY* createEcKey()
388 {
389 EVP_PKEY* pKey = nullptr;
390
391 #if (OPENSSL_VERSION_NUMBER < 0x30000000L)
392 int eccgrp = 0;
393 eccgrp = OBJ_txt2nid("secp384r1");
394
395 EC_KEY* myecc = EC_KEY_new_by_curve_name(eccgrp);
396 if (myecc != nullptr)
397 {
398 EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
399 EC_KEY_generate_key(myecc);
400 pKey = EVP_PKEY_new();
401 if (pKey != nullptr)
402 {
403 if (EVP_PKEY_assign(pKey, EVP_PKEY_EC, myecc) != 0)
404 {
405 /* pKey owns myecc from now */
406 if (EC_KEY_check_key(myecc) <= 0)
407 {
408 BMCWEB_LOG_ERROR("EC_check_key failed.");
409 }
410 }
411 }
412 }
413 #else
414 // Create context for curve parameter generation.
415 std::unique_ptr<EVP_PKEY_CTX, decltype(&::EVP_PKEY_CTX_free)> ctx{
416 EVP_PKEY_CTX_new_id(EVP_PKEY_EC, nullptr), &::EVP_PKEY_CTX_free};
417 if (!ctx)
418 {
419 return nullptr;
420 }
421
422 // Set up curve parameters.
423 EVP_PKEY* params = nullptr;
424 if ((EVP_PKEY_paramgen_init(ctx.get()) <= 0) ||
425 (EVP_PKEY_CTX_set_ec_param_enc(ctx.get(), OPENSSL_EC_NAMED_CURVE) <=
426 0) ||
427 (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(ctx.get(), NID_secp384r1) <=
428 0) ||
429 (EVP_PKEY_paramgen(ctx.get(), ¶ms) <= 0))
430 {
431 return nullptr;
432 }
433
434 // Set up RAII holder for params.
435 std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)> pparams{
436 params, &::EVP_PKEY_free};
437
438 // Set new context for key generation, using curve parameters.
439 ctx.reset(EVP_PKEY_CTX_new_from_pkey(nullptr, params, nullptr));
440 if (!ctx || (EVP_PKEY_keygen_init(ctx.get()) <= 0))
441 {
442 return nullptr;
443 }
444
445 // Generate key.
446 if (EVP_PKEY_keygen(ctx.get(), &pKey) <= 0)
447 {
448 return nullptr;
449 }
450 #endif
451
452 return pKey;
453 }
454
initOpenssl()455 void initOpenssl()
456 {
457 #if OPENSSL_VERSION_NUMBER < 0x10100000L
458 SSL_load_error_strings();
459 OpenSSL_add_all_algorithms();
460 RAND_load_file("/dev/urandom", 1024);
461 #endif
462 }
463
ensureOpensslKeyPresentAndValid(const std::string & filepath)464 inline std::string ensureOpensslKeyPresentAndValid(const std::string& filepath)
465 {
466 std::string cert = verifyOpensslKeyCert(filepath);
467
468 if (cert.empty())
469 {
470 BMCWEB_LOG_WARNING("Error in verifying signature, regenerating");
471 cert = generateSslCertificate("testhost");
472 if (cert.empty())
473 {
474 BMCWEB_LOG_ERROR("Failed to generate cert");
475 }
476 else
477 {
478 writeCertificateToFile(filepath, cert);
479 }
480 }
481 return cert;
482 }
483
ensureCertificate()484 inline std::string ensureCertificate()
485 {
486 namespace fs = std::filesystem;
487 // Cleanup older certificate file existing in the system
488 fs::path oldcertPath = fs::path("/home/root/server.pem");
489 std::error_code ec;
490 fs::remove(oldcertPath, ec);
491 // Ignore failure to remove; File might not exist.
492
493 fs::path certPath = "/etc/ssl/certs/https/";
494 // if path does not exist create the path so that
495 // self signed certificate can be created in the
496 // path
497 fs::path certFile = certPath / "server.pem";
498
499 if (!fs::exists(certPath, ec))
500 {
501 fs::create_directories(certPath, ec);
502 }
503 BMCWEB_LOG_INFO("Building SSL Context file= {}", certFile.string());
504 std::string sslPemFile(certFile);
505 return ensuressl::ensureOpensslKeyPresentAndValid(sslPemFile);
506 }
507
nextProtoCallback(SSL *,const unsigned char ** data,unsigned int * len,void *)508 inline int nextProtoCallback(SSL* /*unused*/, const unsigned char** data,
509 unsigned int* len, void* /*unused*/)
510 {
511 // First byte is the length.
512 constexpr std::string_view h2 = "\x02h2";
513 *data = std::bit_cast<const unsigned char*>(h2.data());
514 *len = static_cast<unsigned int>(h2.size());
515 return SSL_TLSEXT_ERR_OK;
516 }
517
alpnSelectProtoCallback(SSL *,const unsigned char ** out,unsigned char * outlen,const unsigned char * in,unsigned int inlen,void *)518 inline int alpnSelectProtoCallback(SSL* /*unused*/, const unsigned char** out,
519 unsigned char* outlen,
520 const unsigned char* in, unsigned int inlen,
521 void* /*unused*/)
522 {
523 // There's a mismatch in constness for nghttp2_select_next_protocol. The
524 // examples in nghttp2 don't show this problem. Unclear what the right fix
525 // is here.
526
527 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
528 unsigned char** outNew = const_cast<unsigned char**>(out);
529 int rv = nghttp2_select_next_protocol(outNew, outlen, in, inlen);
530 if (rv != 1)
531 {
532 return SSL_TLSEXT_ERR_NOACK;
533 }
534
535 return SSL_TLSEXT_ERR_OK;
536 }
537
getSslContext(boost::asio::ssl::context & mSslContext,const std::string & sslPemFile)538 inline bool getSslContext(boost::asio::ssl::context& mSslContext,
539 const std::string& sslPemFile)
540 {
541 mSslContext.set_options(boost::asio::ssl::context::default_workarounds |
542 boost::asio::ssl::context::no_sslv2 |
543 boost::asio::ssl::context::no_sslv3 |
544 boost::asio::ssl::context::single_dh_use |
545 boost::asio::ssl::context::no_tlsv1 |
546 boost::asio::ssl::context::no_tlsv1_1);
547
548 BMCWEB_LOG_DEBUG("Using default TrustStore location: {}", trustStorePath);
549 mSslContext.add_verify_path(trustStorePath);
550
551 if (!sslPemFile.empty())
552 {
553 boost::system::error_code ec;
554
555 boost::asio::const_buffer buf(sslPemFile.data(), sslPemFile.size());
556 mSslContext.use_certificate(buf, boost::asio::ssl::context::pem, ec);
557 if (ec)
558 {
559 return false;
560 }
561 mSslContext.use_private_key(buf, boost::asio::ssl::context::pem, ec);
562 if (ec)
563 {
564 BMCWEB_LOG_CRITICAL("Failed to open ssl pkey");
565 return false;
566 }
567 }
568
569 // Set up EC curves to auto (boost asio doesn't have a method for this)
570 // There is a pull request to add this. Once this is included in an asio
571 // drop, use the right way
572 // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
573 if (SSL_CTX_set_ecdh_auto(mSslContext->native_handle(), 1) != 1)
574 {}
575
576 // Mozilla intermediate cipher suites v5.7
577 // Sourced from: https://ssl-config.mozilla.org/guidelines/5.7.json
578 const char* mozillaIntermediate = "ECDHE-ECDSA-AES128-GCM-SHA256:"
579 "ECDHE-RSA-AES128-GCM-SHA256:"
580 "ECDHE-ECDSA-AES256-GCM-SHA384:"
581 "ECDHE-RSA-AES256-GCM-SHA384:"
582 "ECDHE-ECDSA-CHACHA20-POLY1305:"
583 "ECDHE-RSA-CHACHA20-POLY1305:"
584 "DHE-RSA-AES128-GCM-SHA256:"
585 "DHE-RSA-AES256-GCM-SHA384:"
586 "DHE-RSA-CHACHA20-POLY1305";
587
588 if (SSL_CTX_set_cipher_list(mSslContext.native_handle(),
589 mozillaIntermediate) != 1)
590 {
591 BMCWEB_LOG_ERROR("Error setting cipher list");
592 return false;
593 }
594 return true;
595 }
596
getSslServerContext()597 inline std::shared_ptr<boost::asio::ssl::context> getSslServerContext()
598 {
599 boost::asio::ssl::context sslCtx(boost::asio::ssl::context::tls_server);
600
601 auto certFile = ensureCertificate();
602 if (!getSslContext(sslCtx, certFile))
603 {
604 BMCWEB_LOG_CRITICAL("Couldn't get server context");
605 return nullptr;
606 }
607
608 // BIG WARNING: This needs to stay disabled, as there will always be
609 // unauthenticated endpoints
610 // mSslContext->set_verify_mode(boost::asio::ssl::verify_peer);
611
612 SSL_CTX_set_options(sslCtx.native_handle(), SSL_OP_NO_RENEGOTIATION);
613
614 if constexpr (BMCWEB_EXPERIMENTAL_HTTP2)
615 {
616 SSL_CTX_set_next_protos_advertised_cb(sslCtx.native_handle(),
617 nextProtoCallback, nullptr);
618
619 SSL_CTX_set_alpn_select_cb(sslCtx.native_handle(),
620 alpnSelectProtoCallback, nullptr);
621 }
622
623 return std::make_shared<boost::asio::ssl::context>(std::move(sslCtx));
624 }
625
getSSLClientContext()626 inline std::optional<boost::asio::ssl::context> getSSLClientContext()
627 {
628 namespace fs = std::filesystem;
629
630 boost::asio::ssl::context sslCtx(boost::asio::ssl::context::tls_client);
631
632 // NOTE, this path is temporary; In the future it will need to change to
633 // be set per subscription. Do not rely on this.
634 fs::path certPath = "/etc/ssl/certs/https/client.pem";
635 std::string cert = verifyOpensslKeyCert(certPath);
636
637 if (!getSslContext(sslCtx, cert))
638 {
639 return std::nullopt;
640 }
641
642 // Add a directory containing certificate authority files to be used
643 // for performing verification.
644 boost::system::error_code ec;
645 sslCtx.set_default_verify_paths(ec);
646 if (ec)
647 {
648 BMCWEB_LOG_ERROR("SSL context set_default_verify failed");
649 return std::nullopt;
650 }
651
652 // Verify the remote server's certificate
653 sslCtx.set_verify_mode(boost::asio::ssl::verify_peer, ec);
654 if (ec)
655 {
656 BMCWEB_LOG_ERROR("SSL context set_verify_mode failed");
657 return std::nullopt;
658 }
659
660 // All cipher suites are set as per OWASP datasheet.
661 // https://cheatsheetseries.owasp.org/cheatsheets/TLS_Cipher_String_Cheat_Sheet.html
662 constexpr const char* sslCiphers = "ECDHE-ECDSA-AES128-GCM-SHA256:"
663 "ECDHE-RSA-AES128-GCM-SHA256:"
664 "ECDHE-ECDSA-AES256-GCM-SHA384:"
665 "ECDHE-RSA-AES256-GCM-SHA384:"
666 "ECDHE-ECDSA-CHACHA20-POLY1305:"
667 "ECDHE-RSA-CHACHA20-POLY1305:"
668 "DHE-RSA-AES128-GCM-SHA256:"
669 "DHE-RSA-AES256-GCM-SHA384"
670 "TLS_AES_128_GCM_SHA256:"
671 "TLS_AES_256_GCM_SHA384:"
672 "TLS_CHACHA20_POLY1305_SHA256";
673
674 if (SSL_CTX_set_cipher_list(sslCtx.native_handle(), sslCiphers) != 1)
675 {
676 BMCWEB_LOG_ERROR("SSL_CTX_set_cipher_list failed");
677 return std::nullopt;
678 }
679
680 return {std::move(sslCtx)};
681 }
682
683 } // namespace ensuressl
684