xref: /openbmc/bmcweb/src/ssl_key_handler.cpp (revision bd79bce8)
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(), &params) <= 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