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