libutil++  1.9.3
 All Classes Functions Variables
SecureTCPSocket.cpp
1 /*
2 ** libutil++
3 ** $Id: SecureTCPSocket.cpp 1911 2017-09-02 17:37:35Z sella $
4 ** Copyright (c) 2011-2016 Digital Genesis, LLC. All Rights Reserved.
5 ** Released under the LGPL Version 2.1 License.
6 ** http://www.digitalgenesis.com
7 */
8 
9 static const char rcsid[] __attribute__((used)) = "$Id: SecureTCPSocket.cpp 1911 2017-09-02 17:37:35Z sella $";
10 
11 #include "SecureTCPSocket.h"
12 #include "../util/String.h"
13 
14 #include <sys/types.h>
15 #include <sys/socket.h>
16 #include <fcntl.h>
17 #include <string.h>
18 #include <fstream>
19 
20 using namespace sella::net;
21 using namespace sella::util;
22 
23 extern bool libutilxx__openssl_init;
24 
25 const std::set<SecureTCPSocket::Protocol> SecureTCPSocket::DefaultProtocols = { SSLv3, TLSv1_0, TLSv1_1, TLSv1_2 };
26 const std::string SecureTCPSocket::DefaultCiphers = "ALL:!aNULL:!eNULL";
27 const std::string SecureTCPSocket::PerformanceCiphers = "AES128-SHA:RC4-MD5:RC4-SHA:AES256-SHA:HIGH:!DSS:!aNULL:!eNULL";
28 
29 SecureTCPSocket::SecureTCPSocket(int family) throw (SocketException) :
30  TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
31  ctx(NULL),
32  ssl(NULL),
33  server(true),
34  protocols(DefaultProtocols),
35  preferServerCiphers(false)
36 {
37  try {
38  init();
39  } catch (SocketException &e) {
40  usable = false;
41 
42  RETHROW(SocketException, e);
43  }
44 }
45 
46 SecureTCPSocket::SecureTCPSocket(int family, const IPAddressPort &addressport) throw (SocketException) :
47  TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
48  ctx(NULL),
49  ssl(NULL),
50  server(false),
51  protocols(DefaultProtocols),
52  preferServerCiphers(false)
53 {
54  try {
55  init();
56  } catch (SocketException &e) {
57  usable = false;
58 
59  RETHROW(SocketException, e);
60  }
61 
62  connect(addressport);
63  client_init();
64 }
65 
66 SecureTCPSocket::SecureTCPSocket(int family, const IPAddress &address, const Port &port) throw (SocketException) :
67  TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
68  ctx(NULL),
69  ssl(NULL),
70  server(false),
71  protocols(DefaultProtocols),
72  preferServerCiphers(false)
73 {
74  try {
75  init();
76  } catch (SocketException &e) {
77  usable = false;
78 
79  RETHROW(SocketException, e);
80  }
81 
82  connect(address, port);
83  client_init();
84 }
85 
86 SecureTCPSocket::SecureTCPSocket(int family, const std::string &address, unsigned short port) throw (SocketException) :
87  TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
88  ctx(NULL),
89  ssl(NULL),
90  server(false),
91  protocols(DefaultProtocols),
92  preferServerCiphers(false)
93 {
94  try {
95  init();
96  } catch (SocketException &e) {
97  usable = false;
98 
99  RETHROW(SocketException, e);
100  }
101 
102  connect(address, port);
103  client_init();
104 }
105 
106 SecureTCPSocket::SecureTCPSocket(const IPAddressPort &addressport) throw (SocketException) :
107  TCPSocket(addressport.getFamily(), SOCK_STREAM, IPPROTO_TCP),
108  ctx(NULL),
109  ssl(NULL),
110  server(false),
111  protocols(DefaultProtocols),
112  preferServerCiphers(false)
113 {
114  try {
115  init();
116  } catch (SocketException &e) {
117  usable = false;
118 
119  RETHROW(SocketException, e);
120  }
121 
122  connect(addressport);
123  client_init();
124 }
125 
126 SecureTCPSocket::SecureTCPSocket(const IPAddress &address, const Port &port) throw (SocketException) :
127  TCPSocket(address.getFamily(), SOCK_STREAM, IPPROTO_TCP),
128  ctx(NULL),
129  ssl(NULL),
130  server(false),
131  protocols(DefaultProtocols),
132  preferServerCiphers(false)
133 {
134  try {
135  init();
136  } catch (SocketException &e) {
137  usable = false;
138 
139  RETHROW(SocketException, e);
140  }
141 
142  connect(address, port);
143  client_init();
144 }
145 
146 SecureTCPSocket::SecureTCPSocket(int s, bool unused) throw (SocketException) :
147  TCPSocket(s, unused),
148  ctx(NULL),
149  ssl(NULL),
150  server(true),
151  protocols(DefaultProtocols),
152  preferServerCiphers(false)
153 {
154  try {
155  init();
156  } catch (SocketException &e) {
157  usable = false;
158 
159  RETHROW(SocketException, e);
160  }
161 
162  usable = true;
163 }
164 
165 SecureTCPSocket::~SecureTCPSocket() {
166  shutdown();
167 
168  try {
169  close();
170  } catch (...) { };
171 
172  /* Scrub buffer before release back to stack. */
173  for (size_t i = 0; i < password.size(); i++) {
174  password.at(i) = 0;
175  }
176 
177  if (ctx) {
178  SSL_CTX_free(ctx);
179 
180  ctx = NULL;
181  }
182 }
183 
184 void SecureTCPSocket::setCertificateFile(const std::string &certificateFile) throw () {
185  this->certificateFile = certificateFile;
186 }
187 
188 void SecureTCPSocket::setPrivateKeyFile(const std::string &privateKeyFile) throw () {
189  this->privateKeyFile = privateKeyFile;
190 }
191 
192 void SecureTCPSocket::setPasswordFile(const std::string &passwordFile) throw () {
193  this->passwordFile = passwordFile;
194 }
195 
196 void SecureTCPSocket::setPassword(const std::string &password) throw () {
197  /* Scrub previous buffer before release back to stack. */
198  for (size_t i = 0; i < this->password.size(); i++) {
199  this->password.at(i) = 0;
200  }
201 
202  this->password = password;
203 }
204 
205 void SecureTCPSocket::setCertificateChainFile(const std::string &certificateChainFile) throw () {
206  this->certificateChainFile = certificateChainFile;
207 }
208 
209 void SecureTCPSocket::setCACertificateFile(const std::string &caCertificateFile) throw () {
210  this->caCertificateFile = caCertificateFile;
211 }
212 
213 void SecureTCPSocket::setProtocols(const std::set<Protocol> &protocols) throw () {
214  this->protocols = protocols;
215 }
216 
217 void SecureTCPSocket::setProtocol(Protocol protocol, bool on) throw () {
218  if (on) {
219  this->protocols.insert(protocol);
220  } else {
221  this->protocols.erase(protocol);
222  }
223 }
224 
225 void SecureTCPSocket::setCiphers(const std::string &ciphers) throw () {
226  this->ciphers = ciphers;
227 }
228 
229 void SecureTCPSocket::setPreferServerCiphers(bool on) throw () {
230  this->preferServerCiphers = on;
231 }
232 
233 bool SecureTCPSocket::isSecure(void) const throw () {
234  return true;
235 }
236 
237 TCPSocket::shared SecureTCPSocket::accept(void) throw (SocketException) {
238  int fd;
239 
240  if ((fd = ::accept(s, NULL, NULL)) < 0) {
241  THROW2(SocketException, "accept()");
242  }
243 
244  const SecureTCPSocket::shared socket(new SecureTCPSocket(fd, true));
245 
246  socket->certificateFile = this->certificateFile;
247  socket->privateKeyFile = this->privateKeyFile;
248  socket->passwordFile = this->passwordFile;
249  socket->password = this->password;
250  socket->certificateChainFile = this->certificateChainFile;
251  socket->caCertificateFile = this->caCertificateFile;
252  socket->protocols = this->protocols;
253  socket->ciphers = this->ciphers;
254  socket->preferServerCiphers = this->preferServerCiphers;
255 
256  socket->server_init();
257 
258  return socket;
259 }
260 
261 ssize_t SecureTCPSocket::send(const void *data, size_t len, int flags) throw (SocketException) {
262  if (!ssl) {
263  THROW(SocketException, "Socket is closed");
264  }
265 
266  errno = 0;
267  ssize_t c = SSL_write(ssl, data, len);
268 
269  switch (SSL_get_error(ssl, c)) {
270  case SSL_ERROR_NONE: /* No error */
271  break;
272 
273  case SSL_ERROR_ZERO_RETURN: /* Orderly socket shutdown */
274  shutdown();
275 
276  break;
277 
278  case SSL_ERROR_WANT_READ:
279  case SSL_ERROR_WANT_WRITE:
280  case SSL_ERROR_WANT_CONNECT:
281  case SSL_ERROR_WANT_ACCEPT:
282  case SSL_ERROR_WANT_X509_LOOKUP:
283  return 0; /* Do not toggle read/except and return 0 to have caller retry. */
284 
285  case SSL_ERROR_SSL:
286  read = except = false;
287  shutdown();
288 
289  THROW(SocketException, "SSL library error during receive: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
290 
291  case SSL_ERROR_SYSCALL:
292  default:
293  read = except = false;
294  shutdown();
295 
296  THROW(SocketException, "Error receiving from connection: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
297  }
298 
299  read = except = false;
300 
301  return c;
302 }
303 
304 ssize_t SecureTCPSocket::recv(void *data, size_t len, int flags) throw (SocketException) {
305  if (!ssl) {
306  THROW(SocketException, "Socket is closed");
307  }
308 
309  errno = 0;
310  ssize_t c = SSL_read(ssl, data, len);
311 
312  switch (SSL_get_error(ssl, c)) {
313  case SSL_ERROR_NONE: /* No error */
314  break;
315 
316  case SSL_ERROR_ZERO_RETURN: /* Orderly socket shutdown */
317  shutdown();
318 
319  break;
320 
321  case SSL_ERROR_WANT_READ:
322  case SSL_ERROR_WANT_WRITE:
323  case SSL_ERROR_WANT_CONNECT:
324  case SSL_ERROR_WANT_ACCEPT:
325  case SSL_ERROR_WANT_X509_LOOKUP:
326  return 0; /* Do not toggle read/except and return 0 to have caller retry. */
327 
328  case SSL_ERROR_SSL:
329  write = false;
330  shutdown();
331 
332  THROW(SocketException, "SSL library error during receive: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
333 
334  case SSL_ERROR_SYSCALL:
335  default:
336  write = false;
337  shutdown();
338 
339  THROW(SocketException, "Error receiving from connection: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
340  }
341 
342  write = false;
343 
344  return c;
345 }
346 
347 const char* SecureTCPSocket::getSSLLibraryVersion(void) const throw () {
348  return SSLeay_version(SSLEAY_VERSION);
349 }
350 
351 const char* SecureTCPSocket::getActiveProtocol(void) const throw (SocketException) {
352  const char *protocol;
353 
354  if (!ssl) {
355  THROW(SocketException, "No active SSL session");
356  }
357 
358  if ((protocol = SSL_get_version(ssl)) == NULL) {
359  THROW(SocketException, "No active SSL session");
360  }
361 
362  return protocol;
363 }
364 
365 const char* SecureTCPSocket::getActiveCipher(void) const throw (SocketException) {
366  const char *cipher;
367 
368  if (!ssl) {
369  THROW(SocketException, "No active SSL session");
370  }
371 
372  if ((cipher = SSL_get_cipher(ssl)) == NULL) {
373  THROW(SocketException, "No active SSL session");
374  }
375 
376  return cipher;
377 }
378 
379 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(int family) throw (SocketException) {
380  return std::make_shared<SecureTCPSocket>(family);
381 }
382 
383 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(int family, const IPAddressPort &addressport) throw (SocketException) {
384  return std::make_shared<SecureTCPSocket>(family, addressport);
385 }
386 
387 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(int family, const IPAddress &address, const Port &port) throw (SocketException) {
388  return std::make_shared<SecureTCPSocket>(family, address, port);
389 }
390 
391 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(int family, const std::string &address, unsigned short port) throw (SocketException) {
392  return std::make_shared<SecureTCPSocket>(family, address, port);
393 }
394 
395 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(const IPAddressPort &addressport) throw (SocketException) {
396  return std::make_shared<SecureTCPSocket>(addressport);
397 }
398 
399 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(const IPAddress &address, const Port &port) throw (SocketException) {
400  return std::make_shared<SecureTCPSocket>(address, port);
401 }
402 
403 void SecureTCPSocket::init(void) throw (SocketException) {
404  if (!libutilxx__openssl_init) { /* Nested to avoid lock overhead. */
405  mutex.lock();
406 
407  if (!libutilxx__openssl_init) {
408  SSL_library_init(); /* Initialize OpenSSL library */
409  SSL_load_error_strings(); /* Load the error strings for SSL & CRYPTO APIs */
410 
411  libutilxx__openssl_init = true;
412  }
413 
414  mutex.unlock();
415  }
416 }
417 
418 void SecureTCPSocket::server_init(void) throw (SocketException) {
419  int e;
420  const SSL_METHOD *meth;
421 
422  if (ctx || ssl) {
423  THROW(SocketException, "Already initialized");
424  }
425 
426  if (privateKeyFile.empty() || certificateFile.empty()) {
427  THROW(SocketException, "Must supply certificate and private-key for server side connection");
428  }
429 
430 #if OPENSSL_VERSION_NUMBER < 0x10100000L
431  if ((meth = SSLv23_server_method()) == NULL) {
432  THROW(SocketException, "Failed to create SSL/TLS method structure: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
433  }
434 #else
435  if ((meth = TLS_server_method()) == NULL) {
436  THROW(SocketException, "Failed to create SSL/TLS method structure: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
437  }
438 #endif
439 
440  if ((ctx = SSL_CTX_new(meth)) == NULL) {
441  THROW(SocketException, "Failed to create OpenSSL context: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
442  }
443 
444 #if OPENSSL_VERSION_NUMBER < 0x10100000L
445  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
446 #endif
447 
448  if (!protocols.empty()) {
449  if (!protocols.count(SSLv3)) {
450  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
451  }
452 
453  if (!protocols.count(TLSv1_0)) {
454  SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
455  }
456 
457  if (!protocols.count(TLSv1_1)) {
458  SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
459  }
460 
461  if (!protocols.count(TLSv1_2)) {
462  SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
463  }
464  }
465 
466  if (!ciphers.empty()) {
467  if (SSL_CTX_set_cipher_list(ctx, ciphers.c_str()) < 1) {
468  THROW(SocketException, "Failed to set SSL ciphers: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
469  }
470  }
471 
472  if (preferServerCiphers) {
473  SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
474  }
475 
476  if (!certificateFile.empty()) {
477  if (SSL_CTX_use_certificate_file(ctx, certificateFile.c_str(), SSL_FILETYPE_PEM) <= 0) {
478  THROW(SocketException, "Failed to load PEM certificate file: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
479  }
480  }
481 
482  if (!passwordFile.empty()) {
483  std::ifstream file(passwordFile);
484 
485  if (!file.is_open() || !file.good()) {
486  THROW(SocketException, "Failed to open file %s for reading", passwordFile.c_str());
487  }
488 
489  /* Scrub previous buffer before release back to stack. */
490  for (size_t i = 0; i < this->password.size(); i++) {
491  this->password.at(i) = 0;
492  }
493 
494  std::getline(file, this->password);
495  }
496 
497  SSL_CTX_set_default_passwd_cb(ctx, SSL_password_callback);
498  SSL_CTX_set_default_passwd_cb_userdata(ctx, &password);
499 
500  if (SSL_CTX_use_PrivateKey_file(ctx, privateKeyFile.c_str(), SSL_FILETYPE_PEM) <= 0) {
501  THROW(SocketException, "Failed to load PEM private key file: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
502  }
503 
504  if (!SSL_CTX_check_private_key(ctx)) {
505  THROW(SocketException, "Private key does not match the certificate public key");
506  }
507 
508  if (!certificateChainFile.empty()) {
509  if (SSL_CTX_use_certificate_chain_file(ctx, certificateChainFile.c_str()) <= 0) {
510  THROW(SocketException, "Failed to load PEM certificate chain file: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
511  }
512  }
513 
514  if (!caCertificateFile.empty()) {
515  /* Load the RSA CA certificate into the SSL_CTX structure */
516  if (!SSL_CTX_load_verify_locations(ctx, caCertificateFile.c_str(), NULL)) {
517  THROW(SocketException, "Failed to load CA certificate: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
518  }
519 
520  /* Set to require peer (client) certificate verification */
521  SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
522 
523  /* Set the verification depth to 1 */
524  SSL_CTX_set_verify_depth(ctx, 1);
525  }
526 
527  if ((ssl = SSL_new(ctx)) == NULL) {
528  THROW(SocketException, "Failed to create SSL connection: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
529  }
530 
531  SSL_set_fd(ssl, s);
532 
533  if ((e = SSL_accept(ssl)) != 1) {
534  THROW(SocketException, "Failed SSL/TLS handshake with client: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
535  }
536 }
537 
538 void SecureTCPSocket::client_init(void) throw (SocketException) {
539  int e;
540  std::string server;
541  const SSL_METHOD *meth;
542 
543  if (ctx || ssl) {
544  THROW(SocketException, "Already initialized");
545  }
546 
547 #if OPENSSL_VERSION_NUMBER < 0x10100000L
548  if ((meth = SSLv23_client_method()) == NULL) {
549  THROW(SocketException, "Failed to create SSL/TLS method structure: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
550  }
551 #else
552  if ((meth = TLS_client_method()) == NULL) {
553  THROW(SocketException, "Failed to create SSL/TLS method structure: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
554  }
555 #endif
556 
557  if ((ctx = SSL_CTX_new(meth)) == NULL) {
558  THROW(SocketException, "Failed to create OpenSSL context: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
559  }
560 
561 #if OPENSSL_VERSION_NUMBER < 0x10100000L
562  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
563 #endif
564 
565  if (!protocols.empty()) {
566  if (!protocols.count(SSLv3)) {
567  SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
568  }
569 
570  if (!protocols.count(TLSv1_0)) {
571  SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
572  }
573 
574  if (!protocols.count(TLSv1_1)) {
575  SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
576  }
577 
578  if (!protocols.count(TLSv1_2)) {
579  SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
580  }
581  }
582 
583  if (!ciphers.empty()) {
584  if (SSL_CTX_set_cipher_list(ctx, ciphers.c_str()) < 1) {
585  THROW(SocketException, "Failed to set SSL ciphers: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
586  }
587  }
588 
589  if ((ssl = SSL_new(ctx)) == NULL) {
590  THROW(SocketException, "Failed to create SSL connection: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
591  }
592 
593  SSL_set_fd(ssl, s);
594 
595  if ((e = SSL_connect(ssl)) != 1) {
596  THROW(SocketException, "Failed SSL/TLS handshake with client: %s", String::join(getErrors(), ", ", '\0', "(no additional details)").c_str());
597  }
598 }
599 
600 void SecureTCPSocket::shutdown(void) throw () {
601  usable = false;
602 
603  if (ssl && (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) == 0) {
604  if (SSL_shutdown(ssl) == 0) { /* Handle bidirectional shutdown. */
605  SSL_shutdown(ssl); /* Remote end read from socket, or it will miss the shutdown notify, timeout and this call will return an error. */
606  }
607  }
608 }
609 
610 void SecureTCPSocket::close(void) throw (SocketException) {
611  TCPSocket::close();
612 
613  if (ssl) {
614  SSL_free(ssl);
615 
616  ssl = NULL;
617  }
618 }
619 
620 std::vector<std::string> SecureTCPSocket::getErrors(void) throw (SocketException) {
621  int e;
622  char buf[1024];
623  std::vector<std::string> results;
624 
625  while ((e = ERR_get_error()) != 0) { /* Walks the error stack. */
626  ERR_error_string_n(e, buf, sizeof(buf));
627 
628  results.push_back(buf);
629  }
630 
631  return results;
632 }
633 
634 int SecureTCPSocket::SSL_password_callback(char *buf, int num, int rwflag, void *userdata) {
635  std::string *password = (std::string*) userdata;
636 
637  strncpy(buf, password->c_str(), num);
638 
639  return (int) password->size();
640 }
641 
642 /*
643 ** vim: noet ts=3 sw=3
644 */