blob: 4eac8035f5f256ead688d99e789bf388a335964b [file] [log] [blame]
Ed Tanous0fdddb12017-02-28 11:06:34 -08001#pragma once
Ed Tanous55c7b7a2018-05-22 15:27:24 -07002#ifdef BMCWEB_ENABLE_SSL
Ed Tanous0fdddb12017-02-28 11:06:34 -08003
4#include <openssl/bio.h>
5#include <openssl/dh.h>
6#include <openssl/dsa.h>
Ed Tanous0fdddb12017-02-28 11:06:34 -08007#include <openssl/err.h>
8#include <openssl/evp.h>
9#include <openssl/pem.h>
10#include <openssl/rand.h>
11#include <openssl/rsa.h>
12#include <openssl/ssl.h>
Ed Tanous1ff48782017-04-18 12:45:08 -070013#include <random>
Ed Tanousa1e077c2017-04-25 12:42:19 -070014#include <boost/asio.hpp>
Ed Tanous9b65f1f2017-03-07 15:17:13 -080015
Ed Tanous99923322017-03-03 14:21:24 -080016namespace ensuressl {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070017static void initOpenssl();
18static void cleanupOpenssl();
19static EVP_PKEY *createRsaKey();
20static EVP_PKEY *createEcKey();
21static void handleOpensslError();
Ed Tanous0fdddb12017-02-28 11:06:34 -080022
Ed Tanous55c7b7a2018-05-22 15:27:24 -070023inline bool verifyOpensslKeyCert(const std::string &filepath) {
24 bool privateKeyValid = false;
25 bool certValid = false;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080026
Ed Tanous4d92cbf2017-06-22 15:41:02 -070027 std::cout << "Checking certs in file " << filepath << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080028
Ed Tanous99923322017-03-03 14:21:24 -080029 FILE *file = fopen(filepath.c_str(), "r");
30 if (file != NULL) {
31 EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
32 int rc;
Ed Tanous911ac312017-08-15 09:37:42 -070033 if (pkey != nullptr) {
Ed Tanousa1e077c2017-04-25 12:42:19 -070034 RSA *rsa = EVP_PKEY_get1_RSA(pkey);
Ed Tanous911ac312017-08-15 09:37:42 -070035 if (rsa != nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070036 std::cout << "Found an RSA key\n";
Ed Tanousa1e077c2017-04-25 12:42:19 -070037 if (RSA_check_key(rsa) == 1) {
38 // private_key_valid = true;
39 } else {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070040 std::cerr << "Key not valid error number " << ERR_get_error() << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080041 }
Ed Tanousa1e077c2017-04-25 12:42:19 -070042 RSA_free(rsa);
43 } else {
44 EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
Ed Tanous911ac312017-08-15 09:37:42 -070045 if (ec != nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070046 std::cout << "Found an EC key\n";
Ed Tanousa1e077c2017-04-25 12:42:19 -070047 if (EC_KEY_check_key(ec) == 1) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070048 privateKeyValid = true;
Ed Tanousa1e077c2017-04-25 12:42:19 -070049 } else {
Ed Tanous911ac312017-08-15 09:37:42 -070050 std::cerr << "Key not valid error number " << ERR_get_error()
51 << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080052 }
Ed Tanousa1e077c2017-04-25 12:42:19 -070053 EC_KEY_free(ec);
Ed Tanous0fdddb12017-02-28 11:06:34 -080054 }
Ed Tanous99923322017-03-03 14:21:24 -080055 }
56
Ed Tanous55c7b7a2018-05-22 15:27:24 -070057 if (privateKeyValid) {
Ed Tanous99923322017-03-03 14:21:24 -080058 X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL);
Ed Tanous911ac312017-08-15 09:37:42 -070059 if (x509 == nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070060 std::cout << "error getting x509 cert " << ERR_get_error() << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080061 } else {
62 rc = X509_verify(x509, pkey);
63 if (rc == 1) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070064 certValid = true;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080065 } else {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070066 std::cerr << "Error in verifying private key signature "
Ed Tanous911ac312017-08-15 09:37:42 -070067 << ERR_get_error() << "\n";
Ed Tanous9b65f1f2017-03-07 15:17:13 -080068 }
Ed Tanous99923322017-03-03 14:21:24 -080069 }
70 }
71
72 EVP_PKEY_free(pkey);
Ed Tanous0fdddb12017-02-28 11:06:34 -080073 }
Ed Tanous99923322017-03-03 14:21:24 -080074 fclose(file);
75 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -070076 return certValid;
Ed Tanous0fdddb12017-02-28 11:06:34 -080077}
78
Ed Tanous55c7b7a2018-05-22 15:27:24 -070079inline void generateSslCertificate(const std::string &filepath) {
Ed Tanous99923322017-03-03 14:21:24 -080080 FILE *pFile = NULL;
Ed Tanous4d92cbf2017-06-22 15:41:02 -070081 std::cout << "Generating new keys\n";
Ed Tanous55c7b7a2018-05-22 15:27:24 -070082 initOpenssl();
Ed Tanous0fdddb12017-02-28 11:06:34 -080083
Ed Tanous4d92cbf2017-06-22 15:41:02 -070084 // std::cerr << "Generating RSA key";
Ed Tanous1ccd57c2017-03-21 13:15:58 -070085 // EVP_PKEY *pRsaPrivKey = create_rsa_key();
Ed Tanous0fdddb12017-02-28 11:06:34 -080086
Ed Tanous4d92cbf2017-06-22 15:41:02 -070087 std::cerr << "Generating EC key\n";
Ed Tanous55c7b7a2018-05-22 15:27:24 -070088 EVP_PKEY *pRsaPrivKey = createEcKey();
Ed Tanous911ac312017-08-15 09:37:42 -070089 if (pRsaPrivKey != nullptr) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -070090 std::cerr << "Generating x509 Certificate\n";
Ed Tanous1ccd57c2017-03-21 13:15:58 -070091 // Use this code to directly generate a certificate
92 X509 *x509;
93 x509 = X509_new();
Ed Tanous911ac312017-08-15 09:37:42 -070094 if (x509 != nullptr) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -070095 // get a random number from the RNG for the certificate serial number
Ed Tanous1ccd57c2017-03-21 13:15:58 -070096 // If this is not random, regenerating certs throws broswer errors
97 std::random_device rd;
98 int serial = rd();
Ed Tanous9b65f1f2017-03-07 15:17:13 -080099
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700100 ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800101
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700102 // not before this moment
103 X509_gmtime_adj(X509_get_notBefore(x509), 0);
104 // Cert is valid for 10 years
105 X509_gmtime_adj(X509_get_notAfter(x509), 60L * 60L * 24L * 365L * 10L);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800106
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700107 // set the public key to the key we just generated
108 X509_set_pubkey(x509, pRsaPrivKey);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800109
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700110 // get the subject name
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700111 X509_NAME *name;
112 name = X509_get_subject_name(x509);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800113
Ed Tanous911ac312017-08-15 09:37:42 -0700114 X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
115 reinterpret_cast<const unsigned char *>("US"),
Ed Tanous1ff48782017-04-18 12:45:08 -0700116 -1, -1, 0);
Ed Tanous911ac312017-08-15 09:37:42 -0700117 X509_NAME_add_entry_by_txt(
118 name, "O", MBSTRING_ASC,
119 reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1, 0);
120 X509_NAME_add_entry_by_txt(
121 name, "CN", MBSTRING_ASC,
122 reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700123 // set the CSR options
124 X509_set_issuer_name(x509, name);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800125
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700126 // Sign the certificate with our private key
127 X509_sign(x509, pRsaPrivKey, EVP_sha256());
Ed Tanous0fdddb12017-02-28 11:06:34 -0800128
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700129 pFile = fopen(filepath.c_str(), "wt");
Ed Tanous0fdddb12017-02-28 11:06:34 -0800130
Ed Tanous911ac312017-08-15 09:37:42 -0700131 if (pFile != nullptr) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700132 PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800133
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700134 PEM_write_X509(pFile, x509);
135 fclose(pFile);
136 pFile = NULL;
137 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800138
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700139 X509_free(x509);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800140 }
141
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800142 EVP_PKEY_free(pRsaPrivKey);
143 pRsaPrivKey = NULL;
Ed Tanous99923322017-03-03 14:21:24 -0800144 }
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700145
Ed Tanous99923322017-03-03 14:21:24 -0800146 // cleanup_openssl();
147}
148
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700149EVP_PKEY *createRsaKey() {
Ed Tanous99923322017-03-03 14:21:24 -0800150 RSA *pRSA = NULL;
Ed Tanousa1e077c2017-04-25 12:42:19 -0700151#if OPENSSL_VERSION_NUMBER < 0x00908000L
Ed Tanous99923322017-03-03 14:21:24 -0800152 pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700153#else
154 RSA_generate_key_ex(pRSA, 2048, NULL, NULL);
155#endif
156
157 EVP_PKEY *pKey = EVP_PKEY_new();
Ed Tanous911ac312017-08-15 09:37:42 -0700158 if ((pRSA != nullptr) && (pKey != nullptr) &&
159 EVP_PKEY_assign_RSA(pKey, pRSA)) {
Ed Tanous99923322017-03-03 14:21:24 -0800160 /* pKey owns pRSA from now */
161 if (RSA_check_key(pRSA) <= 0) {
162 fprintf(stderr, "RSA_check_key failed.\n");
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700163 handleOpensslError();
Ed Tanous99923322017-03-03 14:21:24 -0800164 EVP_PKEY_free(pKey);
165 pKey = NULL;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800166 }
Ed Tanous99923322017-03-03 14:21:24 -0800167 } else {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700168 handleOpensslError();
Ed Tanous911ac312017-08-15 09:37:42 -0700169 if (pRSA != nullptr) {
Ed Tanous99923322017-03-03 14:21:24 -0800170 RSA_free(pRSA);
171 pRSA = NULL;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800172 }
Ed Tanous911ac312017-08-15 09:37:42 -0700173 if (pKey != nullptr) {
Ed Tanous99923322017-03-03 14:21:24 -0800174 EVP_PKEY_free(pKey);
175 pKey = NULL;
176 }
177 }
178 return pKey;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800179}
180
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700181EVP_PKEY *createEcKey() {
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800182 EVP_PKEY *pKey = NULL;
183 int eccgrp = 0;
184 eccgrp = OBJ_txt2nid("prime256v1");
185
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700186 EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp);
Ed Tanous911ac312017-08-15 09:37:42 -0700187 if (myecc != nullptr) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700188 EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
189 EC_KEY_generate_key(myecc);
Ed Tanous01250f22017-04-18 17:49:51 -0700190 pKey = EVP_PKEY_new();
Ed Tanous911ac312017-08-15 09:37:42 -0700191 if (pKey != nullptr) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700192 if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) {
193 /* pKey owns pRSA from now */
194 if (EC_KEY_check_key(myecc) <= 0) {
195 fprintf(stderr, "EC_check_key failed.\n");
196 }
197 }
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800198 }
199 }
200 return pKey;
201}
202
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700203void initOpenssl() {
Ed Tanous911ac312017-08-15 09:37:42 -0700204#if OPENSSL_VERSION_NUMBER < 0x10100000L
205 SSL_load_error_strings();
206 OpenSSL_add_all_algorithms();
207 RAND_load_file("/dev/urandom", 1024);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700208#endif
Ed Tanous0fdddb12017-02-28 11:06:34 -0800209}
210
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700211void cleanupOpenssl() {
Ed Tanous99923322017-03-03 14:21:24 -0800212 CRYPTO_cleanup_all_ex_data();
213 ERR_free_strings();
Ed Tanousa1e077c2017-04-25 12:42:19 -0700214#if OPENSSL_VERSION_NUMBER < 0x10100000L
Ed Tanous99923322017-03-03 14:21:24 -0800215 ERR_remove_thread_state(0);
Ed Tanousa1e077c2017-04-25 12:42:19 -0700216#endif
Ed Tanous99923322017-03-03 14:21:24 -0800217 EVP_cleanup();
Ed Tanous0fdddb12017-02-28 11:06:34 -0800218}
219
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700220void handleOpensslError() { ERR_print_errors_fp(stderr); }
221inline void ensureOpensslKeyPresentAndValid(const std::string &filepath) {
222 bool pemFileValid = false;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800223
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700224 pemFileValid = verifyOpensslKeyCert(filepath);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700225
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700226 if (!pemFileValid) {
Ed Tanous4d92cbf2017-06-22 15:41:02 -0700227 std::cerr << "Error in verifying signature, regenerating\n";
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700228 generateSslCertificate(filepath);
Ed Tanous99923322017-03-03 14:21:24 -0800229 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800230}
Ed Tanousc4771fb2017-03-13 13:39:49 -0700231
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700232inline boost::asio::ssl::context getSslContext(
Ed Tanous911ac312017-08-15 09:37:42 -0700233 const std::string &ssl_pem_file) {
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700234 boost::asio::ssl::context mSslContext{boost::asio::ssl::context::sslv23};
235 mSslContext.set_options(boost::asio::ssl::context::default_workarounds |
236 boost::asio::ssl::context::no_sslv2 |
237 boost::asio::ssl::context::no_sslv3 |
238 boost::asio::ssl::context::single_dh_use |
239 boost::asio::ssl::context::no_tlsv1 |
240 boost::asio::ssl::context::no_tlsv1_1);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700241
242 // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer);
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700243 mSslContext.use_certificate_file(ssl_pem_file,
244 boost::asio::ssl::context::pem);
245 mSslContext.use_private_key_file(ssl_pem_file,
246 boost::asio::ssl::context::pem);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700247
248 // Set up EC curves to auto (boost asio doesn't have a method for this)
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700249 // There is a pull request to add this. Once this is included in an asio
250 // drop, use the right way
Ed Tanousc4771fb2017-03-13 13:39:49 -0700251 // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700252 if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1) {
253 BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n";
Ed Tanousc4771fb2017-03-13 13:39:49 -0700254 }
255
256 // From mozilla "compatibility"
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700257 std::string mozillaCompatibilityCiphers =
Ed Tanousc4771fb2017-03-13 13:39:49 -0700258 "ECDHE-ECDSA-CHACHA20-POLY1305:"
259 "ECDHE-RSA-CHACHA20-POLY1305:"
260 "ECDHE-ECDSA-AES128-GCM-SHA256:"
261 "ECDHE-RSA-AES128-GCM-SHA256:"
262 "ECDHE-ECDSA-AES256-GCM-SHA384:"
263 "ECDHE-RSA-AES256-GCM-SHA384:"
264 "DHE-RSA-AES128-GCM-SHA256:"
265 "DHE-RSA-AES256-GCM-SHA384:"
266 "ECDHE-ECDSA-AES128-SHA256:"
267 "ECDHE-RSA-AES128-SHA256:"
268 "ECDHE-ECDSA-AES128-SHA:"
269 "ECDHE-RSA-AES256-SHA384:"
270 "ECDHE-RSA-AES128-SHA:"
271 "ECDHE-ECDSA-AES256-SHA384:"
272 "ECDHE-ECDSA-AES256-SHA:"
273 "ECDHE-RSA-AES256-SHA:"
274 "DHE-RSA-AES128-SHA256:"
275 "DHE-RSA-AES128-SHA:"
276 "DHE-RSA-AES256-SHA256:"
277 "DHE-RSA-AES256-SHA:"
278 "ECDHE-ECDSA-DES-CBC3-SHA:"
279 "ECDHE-RSA-DES-CBC3-SHA:"
280 "EDH-RSA-DES-CBC3-SHA:"
281 "AES128-GCM-SHA256:"
282 "AES256-GCM-SHA384:"
283 "AES128-SHA256:"
284 "AES256-SHA256:"
285 "AES128-SHA:"
286 "AES256-SHA:"
287 "DES-CBC3-SHA:"
288 "!DSS";
289
290 // From mozilla "modern"
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700291 std::string mozillaModernCiphers =
Ed Tanousc4771fb2017-03-13 13:39:49 -0700292 "ECDHE-ECDSA-AES256-GCM-SHA384:"
293 "ECDHE-RSA-AES256-GCM-SHA384:"
294 "ECDHE-ECDSA-CHACHA20-POLY1305:"
295 "ECDHE-RSA-CHACHA20-POLY1305:"
296 "ECDHE-ECDSA-AES128-GCM-SHA256:"
297 "ECDHE-RSA-AES128-GCM-SHA256:"
298 "ECDHE-ECDSA-AES256-SHA384:"
299 "ECDHE-RSA-AES256-SHA384:"
300 "ECDHE-ECDSA-AES128-SHA256:"
301 "ECDHE-RSA-AES128-SHA256";
302
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700303 std::string aesOnlyCiphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL";
Ed Tanousc4771fb2017-03-13 13:39:49 -0700304
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700305 if (SSL_CTX_set_cipher_list(mSslContext.native_handle(),
306 mozillaCompatibilityCiphers.c_str()) != 1) {
307 BMCWEB_LOG_ERROR << "Error setting cipher list\n";
Ed Tanousc4771fb2017-03-13 13:39:49 -0700308 }
Ed Tanous55c7b7a2018-05-22 15:27:24 -0700309 return mSslContext;
Ed Tanousc4771fb2017-03-13 13:39:49 -0700310}
Ed Tanous911ac312017-08-15 09:37:42 -0700311} // namespace ensuressl
Ed Tanous01250f22017-04-18 17:49:51 -0700312
313#endif