1 #pragma once
2 #ifdef BMCWEB_ENABLE_SSL
3 
4 #include <openssl/bio.h>
5 #include <openssl/dh.h>
6 #include <openssl/dsa.h>
7 #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>
13 
14 #include <boost/asio.hpp>
15 #include <random>
16 
17 namespace ensuressl
18 {
19 static void initOpenssl();
20 static void cleanupOpenssl();
21 static EVP_PKEY *createRsaKey();
22 static EVP_PKEY *createEcKey();
23 static void handleOpensslError();
24 
25 inline bool verifyOpensslKeyCert(const std::string &filepath)
26 {
27     bool privateKeyValid = false;
28     bool certValid = false;
29 
30     std::cout << "Checking certs in file " << filepath << "\n";
31 
32     FILE *file = fopen(filepath.c_str(), "r");
33     if (file != NULL)
34     {
35         EVP_PKEY *pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL);
36         int rc;
37         if (pkey != nullptr)
38         {
39             RSA *rsa = EVP_PKEY_get1_RSA(pkey);
40             if (rsa != nullptr)
41             {
42                 std::cout << "Found an RSA key\n";
43                 if (RSA_check_key(rsa) == 1)
44                 {
45                     // private_key_valid = true;
46                 }
47                 else
48                 {
49                     std::cerr << "Key not valid error number "
50                               << ERR_get_error() << "\n";
51                 }
52                 RSA_free(rsa);
53             }
54             else
55             {
56                 EC_KEY *ec = EVP_PKEY_get1_EC_KEY(pkey);
57                 if (ec != nullptr)
58                 {
59                     std::cout << "Found an EC key\n";
60                     if (EC_KEY_check_key(ec) == 1)
61                     {
62                         privateKeyValid = true;
63                     }
64                     else
65                     {
66                         std::cerr << "Key not valid error number "
67                                   << ERR_get_error() << "\n";
68                     }
69                     EC_KEY_free(ec);
70                 }
71             }
72 
73             if (privateKeyValid)
74             {
75                 X509 *x509 = PEM_read_X509(file, NULL, NULL, NULL);
76                 if (x509 == nullptr)
77                 {
78                     std::cout << "error getting x509 cert " << ERR_get_error()
79                               << "\n";
80                 }
81                 else
82                 {
83                     rc = X509_verify(x509, pkey);
84                     if (rc == 1)
85                     {
86                         certValid = true;
87                     }
88                     else
89                     {
90                         std::cerr << "Error in verifying private key signature "
91                                   << ERR_get_error() << "\n";
92                     }
93                 }
94             }
95 
96             EVP_PKEY_free(pkey);
97         }
98         fclose(file);
99     }
100     return certValid;
101 }
102 
103 inline void generateSslCertificate(const std::string &filepath)
104 {
105     FILE *pFile = NULL;
106     std::cout << "Generating new keys\n";
107     initOpenssl();
108 
109     // std::cerr << "Generating RSA key";
110     // EVP_PKEY *pRsaPrivKey = create_rsa_key();
111 
112     std::cerr << "Generating EC key\n";
113     EVP_PKEY *pRsaPrivKey = createEcKey();
114     if (pRsaPrivKey != nullptr)
115     {
116         std::cerr << "Generating x509 Certificate\n";
117         // Use this code to directly generate a certificate
118         X509 *x509;
119         x509 = X509_new();
120         if (x509 != nullptr)
121         {
122             // get a random number from the RNG for the certificate serial
123             // number If this is not random, regenerating certs throws broswer
124             // errors
125             std::random_device rd;
126             int serial = rd();
127 
128             ASN1_INTEGER_set(X509_get_serialNumber(x509), serial);
129 
130             // not before this moment
131             X509_gmtime_adj(X509_get_notBefore(x509), 0);
132             // Cert is valid for 10 years
133             X509_gmtime_adj(X509_get_notAfter(x509),
134                             60L * 60L * 24L * 365L * 10L);
135 
136             // set the public key to the key we just generated
137             X509_set_pubkey(x509, pRsaPrivKey);
138 
139             // get the subject name
140             X509_NAME *name;
141             name = X509_get_subject_name(x509);
142 
143             X509_NAME_add_entry_by_txt(
144                 name, "C", MBSTRING_ASC,
145                 reinterpret_cast<const unsigned char *>("US"), -1, -1, 0);
146             X509_NAME_add_entry_by_txt(
147                 name, "O", MBSTRING_ASC,
148                 reinterpret_cast<const unsigned char *>("Intel BMC"), -1, -1,
149                 0);
150             X509_NAME_add_entry_by_txt(
151                 name, "CN", MBSTRING_ASC,
152                 reinterpret_cast<const unsigned char *>("testhost"), -1, -1, 0);
153             // set the CSR options
154             X509_set_issuer_name(x509, name);
155 
156             // Sign the certificate with our private key
157             X509_sign(x509, pRsaPrivKey, EVP_sha256());
158 
159             pFile = fopen(filepath.c_str(), "wt");
160 
161             if (pFile != nullptr)
162             {
163                 PEM_write_PrivateKey(pFile, pRsaPrivKey, NULL, NULL, 0, 0,
164                                      NULL);
165 
166                 PEM_write_X509(pFile, x509);
167                 fclose(pFile);
168                 pFile = NULL;
169             }
170 
171             X509_free(x509);
172         }
173 
174         EVP_PKEY_free(pRsaPrivKey);
175         pRsaPrivKey = NULL;
176     }
177 
178     // cleanup_openssl();
179 }
180 
181 EVP_PKEY *createRsaKey()
182 {
183     RSA *pRSA = NULL;
184 #if OPENSSL_VERSION_NUMBER < 0x00908000L
185     pRSA = RSA_generate_key(2048, RSA_3, NULL, NULL);
186 #else
187     RSA_generate_key_ex(pRSA, 2048, NULL, NULL);
188 #endif
189 
190     EVP_PKEY *pKey = EVP_PKEY_new();
191     if ((pRSA != nullptr) && (pKey != nullptr) &&
192         EVP_PKEY_assign_RSA(pKey, pRSA))
193     {
194         /* pKey owns pRSA from now */
195         if (RSA_check_key(pRSA) <= 0)
196         {
197             fprintf(stderr, "RSA_check_key failed.\n");
198             handleOpensslError();
199             EVP_PKEY_free(pKey);
200             pKey = NULL;
201         }
202     }
203     else
204     {
205         handleOpensslError();
206         if (pRSA != nullptr)
207         {
208             RSA_free(pRSA);
209             pRSA = NULL;
210         }
211         if (pKey != nullptr)
212         {
213             EVP_PKEY_free(pKey);
214             pKey = NULL;
215         }
216     }
217     return pKey;
218 }
219 
220 EVP_PKEY *createEcKey()
221 {
222     EVP_PKEY *pKey = NULL;
223     int eccgrp = 0;
224     eccgrp = OBJ_txt2nid("prime256v1");
225 
226     EC_KEY *myecc = EC_KEY_new_by_curve_name(eccgrp);
227     if (myecc != nullptr)
228     {
229         EC_KEY_set_asn1_flag(myecc, OPENSSL_EC_NAMED_CURVE);
230         EC_KEY_generate_key(myecc);
231         pKey = EVP_PKEY_new();
232         if (pKey != nullptr)
233         {
234             if (EVP_PKEY_assign_EC_KEY(pKey, myecc))
235             {
236                 /* pKey owns pRSA from now */
237                 if (EC_KEY_check_key(myecc) <= 0)
238                 {
239                     fprintf(stderr, "EC_check_key failed.\n");
240                 }
241             }
242         }
243     }
244     return pKey;
245 }
246 
247 void initOpenssl()
248 {
249 #if OPENSSL_VERSION_NUMBER < 0x10100000L
250     SSL_load_error_strings();
251     OpenSSL_add_all_algorithms();
252     RAND_load_file("/dev/urandom", 1024);
253 #endif
254 }
255 
256 void cleanupOpenssl()
257 {
258     CRYPTO_cleanup_all_ex_data();
259     ERR_free_strings();
260 #if OPENSSL_VERSION_NUMBER < 0x10100000L
261     ERR_remove_thread_state(0);
262 #endif
263     EVP_cleanup();
264 }
265 
266 void handleOpensslError()
267 {
268     ERR_print_errors_fp(stderr);
269 }
270 inline void ensureOpensslKeyPresentAndValid(const std::string &filepath)
271 {
272     bool pemFileValid = false;
273 
274     pemFileValid = verifyOpensslKeyCert(filepath);
275 
276     if (!pemFileValid)
277     {
278         std::cerr << "Error in verifying signature, regenerating\n";
279         generateSslCertificate(filepath);
280     }
281 }
282 
283 inline boost::asio::ssl::context getSslContext(const std::string &ssl_pem_file)
284 {
285     boost::asio::ssl::context mSslContext{boost::asio::ssl::context::sslv23};
286     mSslContext.set_options(boost::asio::ssl::context::default_workarounds |
287                             boost::asio::ssl::context::no_sslv2 |
288                             boost::asio::ssl::context::no_sslv3 |
289                             boost::asio::ssl::context::single_dh_use |
290                             boost::asio::ssl::context::no_tlsv1 |
291                             boost::asio::ssl::context::no_tlsv1_1);
292 
293     // m_ssl_context.set_verify_mode(boost::asio::ssl::verify_peer);
294     mSslContext.use_certificate_file(ssl_pem_file,
295                                      boost::asio::ssl::context::pem);
296     mSslContext.use_private_key_file(ssl_pem_file,
297                                      boost::asio::ssl::context::pem);
298 
299     // Set up EC curves to auto (boost asio doesn't have a method for this)
300     // There is a pull request to add this.  Once this is included in an asio
301     // drop, use the right way
302     // http://stackoverflow.com/questions/18929049/boost-asio-with-ecdsa-certificate-issue
303     if (SSL_CTX_set_ecdh_auto(mSslContext.native_handle(), 1) != 1)
304     {
305         BMCWEB_LOG_ERROR << "Error setting tmp ecdh list\n";
306     }
307 
308     // From mozilla "compatibility"
309     std::string mozillaCompatibilityCiphers = "ECDHE-ECDSA-CHACHA20-POLY1305:"
310                                               "ECDHE-RSA-CHACHA20-POLY1305:"
311                                               "ECDHE-ECDSA-AES128-GCM-SHA256:"
312                                               "ECDHE-RSA-AES128-GCM-SHA256:"
313                                               "ECDHE-ECDSA-AES256-GCM-SHA384:"
314                                               "ECDHE-RSA-AES256-GCM-SHA384:"
315                                               "DHE-RSA-AES128-GCM-SHA256:"
316                                               "DHE-RSA-AES256-GCM-SHA384:"
317                                               "ECDHE-ECDSA-AES128-SHA256:"
318                                               "ECDHE-RSA-AES128-SHA256:"
319                                               "ECDHE-ECDSA-AES128-SHA:"
320                                               "ECDHE-RSA-AES256-SHA384:"
321                                               "ECDHE-RSA-AES128-SHA:"
322                                               "ECDHE-ECDSA-AES256-SHA384:"
323                                               "ECDHE-ECDSA-AES256-SHA:"
324                                               "ECDHE-RSA-AES256-SHA:"
325                                               "DHE-RSA-AES128-SHA256:"
326                                               "DHE-RSA-AES128-SHA:"
327                                               "DHE-RSA-AES256-SHA256:"
328                                               "DHE-RSA-AES256-SHA:"
329                                               "ECDHE-ECDSA-DES-CBC3-SHA:"
330                                               "ECDHE-RSA-DES-CBC3-SHA:"
331                                               "EDH-RSA-DES-CBC3-SHA:"
332                                               "AES128-GCM-SHA256:"
333                                               "AES256-GCM-SHA384:"
334                                               "AES128-SHA256:"
335                                               "AES256-SHA256:"
336                                               "AES128-SHA:"
337                                               "AES256-SHA:"
338                                               "DES-CBC3-SHA:"
339                                               "!DSS";
340 
341     // From mozilla "modern"
342     std::string mozillaModernCiphers = "ECDHE-ECDSA-AES256-GCM-SHA384:"
343                                        "ECDHE-RSA-AES256-GCM-SHA384:"
344                                        "ECDHE-ECDSA-CHACHA20-POLY1305:"
345                                        "ECDHE-RSA-CHACHA20-POLY1305:"
346                                        "ECDHE-ECDSA-AES128-GCM-SHA256:"
347                                        "ECDHE-RSA-AES128-GCM-SHA256:"
348                                        "ECDHE-ECDSA-AES256-SHA384:"
349                                        "ECDHE-RSA-AES256-SHA384:"
350                                        "ECDHE-ECDSA-AES128-SHA256:"
351                                        "ECDHE-RSA-AES128-SHA256";
352 
353     std::string aesOnlyCiphers = "AES128+EECDH:AES128+EDH:!aNULL:!eNULL";
354 
355     if (SSL_CTX_set_cipher_list(mSslContext.native_handle(),
356                                 mozillaCompatibilityCiphers.c_str()) != 1)
357     {
358         BMCWEB_LOG_ERROR << "Error setting cipher list\n";
359     }
360     return mSslContext;
361 }
362 } // namespace ensuressl
363 
364 #endif