blob: eef4d550117f59531c7a39b388f78661e91a4f67 [file] [log] [blame]
Ed Tanous0fdddb12017-02-28 11:06:34 -08001#pragma once
Ed Tanous01250f22017-04-18 17:49:51 -07002#ifdef CROW_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>
7#include <openssl/dsa.h>
8#include <openssl/err.h>
9#include <openssl/evp.h>
10#include <openssl/pem.h>
11#include <openssl/rand.h>
12#include <openssl/rsa.h>
13#include <openssl/ssl.h>
Ed Tanousc4771fb2017-03-13 13:39:49 -070014#include <boost/asio.hpp>
Ed Tanous9b65f1f2017-03-07 15:17:13 -080015#include <g3log/g3log.hpp>
Ed Tanous1ff48782017-04-18 12:45:08 -070016#include <random>
Ed Tanous9b65f1f2017-03-07 15:17:13 -080017
Ed Tanous99923322017-03-03 14:21:24 -080018namespace ensuressl {
Ed Tanous0fdddb12017-02-28 11:06:34 -080019static void init_openssl(void);
20static void cleanup_openssl(void);
21static EVP_PKEY *create_rsa_key(void);
Ed Tanous9b65f1f2017-03-07 15:17:13 -080022static EVP_PKEY *create_ec_key(void);
Ed Tanous0fdddb12017-02-28 11:06:34 -080023static void handle_openssl_error(void);
24
Ed Tanous99923322017-03-03 14:21:24 -080025inline bool verify_openssl_key_cert(const std::string &filepath) {
26 bool private_key_valid = false;
27 bool cert_valid = false;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080028
29 LOG(DEBUG) << "Checking certs in file " << filepath;
30
Ed Tanous99923322017-03-03 14:21:24 -080031 FILE *file = fopen(filepath.c_str(), "r");
32 if (file != NULL) {
33 EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
34 int rc;
35 if (pkey) {
36 int type = EVP_PKEY_type(pkey->type);
37 switch (type) {
38 case EVP_PKEY_RSA:
39 case EVP_PKEY_RSA2: {
Ed Tanous9b65f1f2017-03-07 15:17:13 -080040 LOG(DEBUG) << "Found an RSA key";
Ed Tanous99923322017-03-03 14:21:24 -080041 RSA *rsa = EVP_PKEY_get1_RSA(pkey);
Ed Tanous1ccd57c2017-03-21 13:15:58 -070042 if (rsa) {
Ed Tanous9b65f1f2017-03-07 15:17:13 -080043 if (RSA_check_key(rsa) == 1) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -070044 // private_key_valid = true;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080045 } else {
46 LOG(WARNING) << "Key not valid error number " << ERR_get_error();
47 }
48 RSA_free(rsa);
Ed Tanous99923322017-03-03 14:21:24 -080049 }
Ed Tanous9b65f1f2017-03-07 15:17:13 -080050 break;
51 }
Ed Tanous1ccd57c2017-03-21 13:15:58 -070052 case EVP_PKEY_EC: {
Ed Tanous9b65f1f2017-03-07 15:17:13 -080053 LOG(DEBUG) << "Found an EC key";
Ed Tanous1ccd57c2017-03-21 13:15:58 -070054 EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
55 if (ec) {
Ed Tanous9b65f1f2017-03-07 15:17:13 -080056 if (EC_KEY_check_key(ec) == 1) {
Ed Tanousc4771fb2017-03-13 13:39:49 -070057 private_key_valid = true;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080058 } else {
59 LOG(WARNING) << "Key not valid error number " << ERR_get_error();
60 }
61 EC_KEY_free(ec);
62 }
Ed Tanous99923322017-03-03 14:21:24 -080063 break;
Ed Tanous0fdddb12017-02-28 11:06:34 -080064 }
Ed Tanous99923322017-03-03 14:21:24 -080065 default:
Ed Tanous9b65f1f2017-03-07 15:17:13 -080066 LOG(WARNING) << "Found an unrecognized key type " << type;
Ed Tanous99923322017-03-03 14:21:24 -080067 break;
68 }
69
70 if (private_key_valid) {
71 X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL);
Ed Tanous1ccd57c2017-03-21 13:15:58 -070072 if (!x509) {
Ed Tanous9b65f1f2017-03-07 15:17:13 -080073 LOG(DEBUG) << "error getting x509 cert " << ERR_get_error();
74 } else {
75 rc = X509_verify(x509, pkey);
76 if (rc == 1) {
77 cert_valid = true;
78 } else {
Ed Tanous1ccd57c2017-03-21 13:15:58 -070079 LOG(WARNING) << "Error in verifying private key signature "
80 << ERR_get_error();
Ed Tanous9b65f1f2017-03-07 15:17:13 -080081 }
Ed Tanous99923322017-03-03 14:21:24 -080082 }
83 }
84
85 EVP_PKEY_free(pkey);
Ed Tanous0fdddb12017-02-28 11:06:34 -080086 }
Ed Tanous99923322017-03-03 14:21:24 -080087 fclose(file);
88 }
89 return cert_valid;
Ed Tanous0fdddb12017-02-28 11:06:34 -080090}
91
Ed Tanous99923322017-03-03 14:21:24 -080092inline void generate_ssl_certificate(const std::string &filepath) {
Ed Tanous99923322017-03-03 14:21:24 -080093 FILE *pFile = NULL;
Ed Tanous9b65f1f2017-03-07 15:17:13 -080094 LOG(WARNING) << "Generating new keys";
Ed Tanous99923322017-03-03 14:21:24 -080095 init_openssl();
Ed Tanous0fdddb12017-02-28 11:06:34 -080096
Ed Tanous1ccd57c2017-03-21 13:15:58 -070097 // LOG(WARNING) << "Generating RSA key";
98 // EVP_PKEY *pRsaPrivKey = create_rsa_key();
Ed Tanous0fdddb12017-02-28 11:06:34 -080099
Ed Tanousc4771fb2017-03-13 13:39:49 -0700100 LOG(WARNING) << "Generating EC key";
101 EVP_PKEY *pRsaPrivKey = create_ec_key();
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700102 if (pRsaPrivKey) {
103 LOG(WARNING) << "Generating x509 Certificate";
104 // Use this code to directly generate a certificate
105 X509 *x509;
106 x509 = X509_new();
107 if (x509) {
108 // Get a random number from the RNG for the certificate serial number
109 // If this is not random, regenerating certs throws broswer errors
110 std::random_device rd;
111 int serial = rd();
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800112
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700113 ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800114
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700115 // not before this moment
116 X509_gmtime_adj(X509_get_notBefore(x509), 0);
117 // Cert is valid for 10 years
118 X509_gmtime_adj(X509_get_notAfter(x509), 60L * 60L * 24L * 365L * 10L);
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800119
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700120 // set the public key to the key we just generated
121 X509_set_pubkey(x509, pRsaPrivKey);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800122
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700123 // Get the subject name
124 X509_NAME *name;
125 name = X509_get_subject_name(x509);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800126
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700127 X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC, (unsigned char *)"US",
Ed Tanous1ff48782017-04-18 12:45:08 -0700128 -1, -1, 0);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700129 X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
Ed Tanous1ff48782017-04-18 12:45:08 -0700130 (unsigned char *)"Intel BMC", -1, -1, 0);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700131 X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
Ed Tanous1ff48782017-04-18 12:45:08 -0700132 (unsigned char *)"testhost", -1, -1, 0);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700133 // set the CSR options
134 X509_set_issuer_name(x509, name);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800135
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700136 // Sign the certificate with our private key
137 X509_sign(x509, pRsaPrivKey, EVP_sha256());
Ed Tanous0fdddb12017-02-28 11:06:34 -0800138
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700139 pFile = fopen(filepath.c_str(), "wt");
Ed Tanous0fdddb12017-02-28 11:06:34 -0800140
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700141 if (pFile) {
142 PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0, NULL);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800143
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700144 PEM_write_X509(pFile, x509);
145 fclose(pFile);
146 pFile = NULL;
147 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800148
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700149 X509_free(x509);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800150 }
151
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800152 EVP_PKEY_free(pRsaPrivKey);
153 pRsaPrivKey = NULL;
Ed Tanous99923322017-03-03 14:21:24 -0800154 }
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700155
Ed Tanous99923322017-03-03 14:21:24 -0800156 // cleanup_openssl();
157}
158
159EVP_PKEY *create_rsa_key(void) {
160 RSA *pRSA = NULL;
161 EVP_PKEY *pKey = NULL;
162 pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL);
163 pKey = EVP_PKEY_new();
164 if (pRSA && pKey && EVP_PKEY_assign_RSA(pKey, pRSA)) {
165 /* pKey owns pRSA from now */
166 if (RSA_check_key(pRSA) <= 0) {
167 fprintf(stderr, "RSA_check_key failed.\n");
168 handle_openssl_error();
169 EVP_PKEY_free(pKey);
170 pKey = NULL;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800171 }
Ed Tanous99923322017-03-03 14:21:24 -0800172 } else {
173 handle_openssl_error();
174 if (pRSA) {
175 RSA_free(pRSA);
176 pRSA = NULL;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800177 }
Ed Tanous99923322017-03-03 14:21:24 -0800178 if (pKey) {
179 EVP_PKEY_free(pKey);
180 pKey = NULL;
181 }
182 }
183 return pKey;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800184}
185
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800186EVP_PKEY *create_ec_key(void) {
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800187 EVP_PKEY *pKey = NULL;
188 int eccgrp = 0;
189 eccgrp = OBJ_txt2nid("prime256v1");
190
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700191 EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp);
192 if (myecc) {
193 EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
194 EC_KEY_generate_key(myecc);
Ed Tanous01250f22017-04-18 17:49:51 -0700195 pKey = EVP_PKEY_new();
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800196 if (pKey) {
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700197 if (EVP_PKEY_assign_EC_KEY(pKey, myecc)) {
198 /* pKey owns pRSA from now */
199 if (EC_KEY_check_key(myecc) <= 0) {
200 fprintf(stderr, "EC_check_key failed.\n");
201 }
202 }
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800203 }
204 }
205 return pKey;
206}
207
Ed Tanous99923322017-03-03 14:21:24 -0800208void init_openssl(void) {
209 if (SSL_library_init()) {
210 SSL_load_error_strings();
211 OpenSSL_add_all_algorithms();
212 RAND_load_file("/dev/urandom", 1024);
213 } else
214 exit(EXIT_FAILURE);
Ed Tanous0fdddb12017-02-28 11:06:34 -0800215}
216
Ed Tanous99923322017-03-03 14:21:24 -0800217void cleanup_openssl(void) {
218 CRYPTO_cleanup_all_ex_data();
219 ERR_free_strings();
220 ERR_remove_thread_state(0);
221 EVP_cleanup();
Ed Tanous0fdddb12017-02-28 11:06:34 -0800222}
223
224void handle_openssl_error(void) { ERR_print_errors_fp(stderr); }
Ed Tanous99923322017-03-03 14:21:24 -0800225inline void ensure_openssl_key_present_and_valid(const std::string &filepath) {
226 bool pem_file_valid = false;
Ed Tanous0fdddb12017-02-28 11:06:34 -0800227
Ed Tanous99923322017-03-03 14:21:24 -0800228 pem_file_valid = verify_openssl_key_cert(filepath);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700229
Ed Tanous99923322017-03-03 14:21:24 -0800230 if (!pem_file_valid) {
Ed Tanous9b65f1f2017-03-07 15:17:13 -0800231 LOG(WARNING) << "Error in verifying signature, regenerating";
Ed Tanous99923322017-03-03 14:21:24 -0800232 generate_ssl_certificate(filepath);
233 }
Ed Tanous0fdddb12017-02-28 11:06:34 -0800234}
Ed Tanousc4771fb2017-03-13 13:39:49 -0700235
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700236boost::asio::ssl::context get_ssl_context(std::string ssl_pem_file) {
Ed Tanousc4771fb2017-03-13 13:39:49 -0700237 boost::asio::ssl::context m_ssl_context{boost::asio::ssl::context::sslv23};
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700238 m_ssl_context.set_options(boost::asio::ssl::context::default_workarounds |
239 boost::asio::ssl::context::no_sslv2 |
240 boost::asio::ssl::context::no_sslv3 |
241 boost::asio::ssl::context::single_dh_use |
242 boost::asio::ssl::context::no_tlsv1 |
243 boost::asio::ssl::context::no_tlsv1_1);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700244
245 // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer);
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700246 m_ssl_context.use_certificate_file(ssl_pem_file,
247 boost::asio::ssl::context::pem);
248 m_ssl_context.use_private_key_file(ssl_pem_file,
249 boost::asio::ssl::context::pem);
Ed Tanousc4771fb2017-03-13 13:39:49 -0700250
251 // Set up EC curves to auto (boost asio doesn't have a method for this)
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700252 // There is a pull request to add this. Once this is included in an asio
253 // drop, use the right way
Ed Tanousc4771fb2017-03-13 13:39:49 -0700254 // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
255 if (SSL_CTX_set_ecdh_auto(m_ssl_context.native_handle(), 1) != 1) {
256 CROW_LOG_ERROR << "Error setting tmp ecdh list\n";
257 }
258
259 // From mozilla "compatibility"
260 std::string ciphers =
261 "ECDHE-ECDSA-CHACHA20-POLY1305:"
262 "ECDHE-RSA-CHACHA20-POLY1305:"
263 "ECDHE-ECDSA-AES128-GCM-SHA256:"
264 "ECDHE-RSA-AES128-GCM-SHA256:"
265 "ECDHE-ECDSA-AES256-GCM-SHA384:"
266 "ECDHE-RSA-AES256-GCM-SHA384:"
267 "DHE-RSA-AES128-GCM-SHA256:"
268 "DHE-RSA-AES256-GCM-SHA384:"
269 "ECDHE-ECDSA-AES128-SHA256:"
270 "ECDHE-RSA-AES128-SHA256:"
271 "ECDHE-ECDSA-AES128-SHA:"
272 "ECDHE-RSA-AES256-SHA384:"
273 "ECDHE-RSA-AES128-SHA:"
274 "ECDHE-ECDSA-AES256-SHA384:"
275 "ECDHE-ECDSA-AES256-SHA:"
276 "ECDHE-RSA-AES256-SHA:"
277 "DHE-RSA-AES128-SHA256:"
278 "DHE-RSA-AES128-SHA:"
279 "DHE-RSA-AES256-SHA256:"
280 "DHE-RSA-AES256-SHA:"
281 "ECDHE-ECDSA-DES-CBC3-SHA:"
282 "ECDHE-RSA-DES-CBC3-SHA:"
283 "EDH-RSA-DES-CBC3-SHA:"
284 "AES128-GCM-SHA256:"
285 "AES256-GCM-SHA384:"
286 "AES128-SHA256:"
287 "AES256-SHA256:"
288 "AES128-SHA:"
289 "AES256-SHA:"
290 "DES-CBC3-SHA:"
291 "!DSS";
292
293 // From mozilla "modern"
294 std::string modern_ciphers =
295 "ECDHE-ECDSA-AES256-GCM-SHA384:"
296 "ECDHE-RSA-AES256-GCM-SHA384:"
297 "ECDHE-ECDSA-CHACHA20-POLY1305:"
298 "ECDHE-RSA-CHACHA20-POLY1305:"
299 "ECDHE-ECDSA-AES128-GCM-SHA256:"
300 "ECDHE-RSA-AES128-GCM-SHA256:"
301 "ECDHE-ECDSA-AES256-SHA384:"
302 "ECDHE-RSA-AES256-SHA384:"
303 "ECDHE-ECDSA-AES128-SHA256:"
304 "ECDHE-RSA-AES128-SHA256";
305
306 std::string lighttp_ciphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL";
307
Ed Tanous1ccd57c2017-03-21 13:15:58 -0700308 if (SSL_CTX_set_cipher_list(m_ssl_context.native_handle(), ciphers.c_str()) !=
309 1) {
Ed Tanousc4771fb2017-03-13 13:39:49 -0700310 CROW_LOG_ERROR << "Error setting cipher list\n";
311 }
312 return m_ssl_context;
313}
Ed Tanous01250f22017-04-18 17:49:51 -0700314}
315
316#endif