9 static const char rcsid[] __attribute__((used)) =
"$Id: SecureTCPSocket.cpp 1911 2017-09-02 17:37:35Z sella $";
11 #include "SecureTCPSocket.h"
12 #include "../util/String.h"
14 #include <sys/types.h>
15 #include <sys/socket.h>
20 using namespace sella::net;
21 using namespace sella::util;
23 extern bool libutilxx__openssl_init;
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";
29 SecureTCPSocket::SecureTCPSocket(
int family)
throw (SocketException) :
30 TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
34 protocols(DefaultProtocols),
35 preferServerCiphers(
false)
39 }
catch (SocketException &e) {
42 RETHROW(SocketException, e);
46 SecureTCPSocket::SecureTCPSocket(
int family,
const IPAddressPort &addressport)
throw (SocketException) :
47 TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
51 protocols(DefaultProtocols),
52 preferServerCiphers(
false)
56 }
catch (SocketException &e) {
59 RETHROW(SocketException, e);
66 SecureTCPSocket::SecureTCPSocket(
int family,
const IPAddress &address,
const Port &port)
throw (SocketException) :
67 TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
71 protocols(DefaultProtocols),
72 preferServerCiphers(
false)
76 }
catch (SocketException &e) {
79 RETHROW(SocketException, e);
82 connect(address, port);
86 SecureTCPSocket::SecureTCPSocket(
int family,
const std::string &address,
unsigned short port)
throw (SocketException) :
87 TCPSocket(family, SOCK_STREAM, IPPROTO_TCP),
91 protocols(DefaultProtocols),
92 preferServerCiphers(
false)
96 }
catch (SocketException &e) {
99 RETHROW(SocketException, e);
102 connect(address, port);
106 SecureTCPSocket::SecureTCPSocket(
const IPAddressPort &addressport)
throw (SocketException) :
107 TCPSocket(addressport.getFamily(), SOCK_STREAM, IPPROTO_TCP),
111 protocols(DefaultProtocols),
112 preferServerCiphers(
false)
116 }
catch (SocketException &e) {
119 RETHROW(SocketException, e);
122 connect(addressport);
126 SecureTCPSocket::SecureTCPSocket(
const IPAddress &address,
const Port &port)
throw (SocketException) :
127 TCPSocket(address.getFamily(), SOCK_STREAM, IPPROTO_TCP),
131 protocols(DefaultProtocols),
132 preferServerCiphers(
false)
136 }
catch (SocketException &e) {
139 RETHROW(SocketException, e);
142 connect(address, port);
146 SecureTCPSocket::SecureTCPSocket(
int s,
bool unused)
throw (SocketException) :
151 protocols(DefaultProtocols),
152 preferServerCiphers(
false)
156 }
catch (SocketException &e) {
159 RETHROW(SocketException, e);
165 SecureTCPSocket::~SecureTCPSocket() {
173 for (
size_t i = 0; i < password.size(); i++) {
184 void SecureTCPSocket::setCertificateFile(
const std::string &certificateFile)
throw () {
185 this->certificateFile = certificateFile;
188 void SecureTCPSocket::setPrivateKeyFile(
const std::string &privateKeyFile)
throw () {
189 this->privateKeyFile = privateKeyFile;
192 void SecureTCPSocket::setPasswordFile(
const std::string &passwordFile)
throw () {
193 this->passwordFile = passwordFile;
196 void SecureTCPSocket::setPassword(
const std::string &password)
throw () {
198 for (
size_t i = 0; i < this->password.size(); i++) {
199 this->password.at(i) = 0;
202 this->password = password;
205 void SecureTCPSocket::setCertificateChainFile(
const std::string &certificateChainFile)
throw () {
206 this->certificateChainFile = certificateChainFile;
209 void SecureTCPSocket::setCACertificateFile(
const std::string &caCertificateFile)
throw () {
210 this->caCertificateFile = caCertificateFile;
213 void SecureTCPSocket::setProtocols(
const std::set<Protocol> &protocols)
throw () {
214 this->protocols = protocols;
217 void SecureTCPSocket::setProtocol(Protocol protocol,
bool on)
throw () {
219 this->protocols.insert(protocol);
221 this->protocols.erase(protocol);
225 void SecureTCPSocket::setCiphers(
const std::string &ciphers)
throw () {
226 this->ciphers = ciphers;
229 void SecureTCPSocket::setPreferServerCiphers(
bool on)
throw () {
230 this->preferServerCiphers = on;
233 bool SecureTCPSocket::isSecure(
void)
const throw () {
237 TCPSocket::shared SecureTCPSocket::accept(
void) throw (SocketException) {
240 if ((fd = ::accept(s, NULL, NULL)) < 0) {
241 THROW2(SocketException,
"accept()");
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;
256 socket->server_init();
261 ssize_t SecureTCPSocket::send(
const void *data,
size_t len,
int flags)
throw (SocketException) {
263 THROW(SocketException,
"Socket is closed");
267 ssize_t c = SSL_write(ssl, data, len);
269 switch (SSL_get_error(ssl, c)) {
273 case SSL_ERROR_ZERO_RETURN:
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:
286 read = except =
false;
289 THROW(SocketException,
"SSL library error during receive: %s", String::join(getErrors(),
", ",
'\0',
"(no additional details)").c_str());
291 case SSL_ERROR_SYSCALL:
293 read = except =
false;
296 THROW(SocketException,
"Error receiving from connection: %s", String::join(getErrors(),
", ",
'\0',
"(no additional details)").c_str());
299 read = except =
false;
304 ssize_t SecureTCPSocket::recv(
void *data,
size_t len,
int flags)
throw (SocketException) {
306 THROW(SocketException,
"Socket is closed");
310 ssize_t c = SSL_read(ssl, data, len);
312 switch (SSL_get_error(ssl, c)) {
316 case SSL_ERROR_ZERO_RETURN:
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:
332 THROW(SocketException,
"SSL library error during receive: %s", String::join(getErrors(),
", ",
'\0',
"(no additional details)").c_str());
334 case SSL_ERROR_SYSCALL:
339 THROW(SocketException,
"Error receiving from connection: %s", String::join(getErrors(),
", ",
'\0',
"(no additional details)").c_str());
347 const char* SecureTCPSocket::getSSLLibraryVersion(
void)
const throw () {
348 return SSLeay_version(SSLEAY_VERSION);
351 const char* SecureTCPSocket::getActiveProtocol(
void)
const throw (SocketException) {
352 const char *protocol;
355 THROW(SocketException,
"No active SSL session");
358 if ((protocol = SSL_get_version(ssl)) == NULL) {
359 THROW(SocketException,
"No active SSL session");
365 const char* SecureTCPSocket::getActiveCipher(
void)
const throw (SocketException) {
369 THROW(SocketException,
"No active SSL session");
372 if ((cipher = SSL_get_cipher(ssl)) == NULL) {
373 THROW(SocketException,
"No active SSL session");
379 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(
int family)
throw (SocketException) {
380 return std::make_shared<SecureTCPSocket>(family);
383 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(
int family,
const IPAddressPort &addressport)
throw (SocketException) {
384 return std::make_shared<SecureTCPSocket>(family, addressport);
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);
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);
395 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(
const IPAddressPort &addressport)
throw (SocketException) {
396 return std::make_shared<SecureTCPSocket>(addressport);
399 const SecureTCPSocket::shared SecureTCPSocket::shared_ptr(
const IPAddress &address,
const Port &port)
throw (SocketException) {
400 return std::make_shared<SecureTCPSocket>(address, port);
403 void SecureTCPSocket::init(
void) throw (SocketException) {
404 if (!libutilxx__openssl_init) {
407 if (!libutilxx__openssl_init) {
409 SSL_load_error_strings();
411 libutilxx__openssl_init =
true;
418 void SecureTCPSocket::server_init(
void) throw (SocketException) {
420 const SSL_METHOD *meth;
423 THROW(SocketException,
"Already initialized");
426 if (privateKeyFile.empty() || certificateFile.empty()) {
427 THROW(SocketException,
"Must supply certificate and private-key for server side connection");
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());
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());
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());
444 #if OPENSSL_VERSION_NUMBER < 0x10100000L
445 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
448 if (!protocols.empty()) {
449 if (!protocols.count(SSLv3)) {
450 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
453 if (!protocols.count(TLSv1_0)) {
454 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
457 if (!protocols.count(TLSv1_1)) {
458 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
461 if (!protocols.count(TLSv1_2)) {
462 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
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());
472 if (preferServerCiphers) {
473 SSL_CTX_set_options(ctx, SSL_OP_CIPHER_SERVER_PREFERENCE);
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());
482 if (!passwordFile.empty()) {
483 std::ifstream file(passwordFile);
485 if (!file.is_open() || !file.good()) {
486 THROW(SocketException,
"Failed to open file %s for reading", passwordFile.c_str());
490 for (
size_t i = 0; i < this->password.size(); i++) {
491 this->password.at(i) = 0;
494 std::getline(file, this->password);
497 SSL_CTX_set_default_passwd_cb(ctx, SSL_password_callback);
498 SSL_CTX_set_default_passwd_cb_userdata(ctx, &password);
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());
504 if (!SSL_CTX_check_private_key(ctx)) {
505 THROW(SocketException,
"Private key does not match the certificate public key");
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());
514 if (!caCertificateFile.empty()) {
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());
521 SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
524 SSL_CTX_set_verify_depth(ctx, 1);
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());
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());
538 void SecureTCPSocket::client_init(
void) throw (SocketException) {
541 const SSL_METHOD *meth;
544 THROW(SocketException,
"Already initialized");
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());
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());
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());
561 #if OPENSSL_VERSION_NUMBER < 0x10100000L
562 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2);
565 if (!protocols.empty()) {
566 if (!protocols.count(SSLv3)) {
567 SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv3);
570 if (!protocols.count(TLSv1_0)) {
571 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1);
574 if (!protocols.count(TLSv1_1)) {
575 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_1);
578 if (!protocols.count(TLSv1_2)) {
579 SSL_CTX_set_options(ctx, SSL_OP_NO_TLSv1_2);
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());
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());
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());
600 void SecureTCPSocket::shutdown(
void) throw () {
603 if (ssl && (SSL_get_shutdown(ssl) & SSL_SENT_SHUTDOWN) == 0) {
604 if (SSL_shutdown(ssl) == 0) {
610 void SecureTCPSocket::close(
void) throw (SocketException) {
620 std::vector<std::string> SecureTCPSocket::getErrors(
void) throw (SocketException) {
623 std::vector<std::string> results;
625 while ((e = ERR_get_error()) != 0) {
626 ERR_error_string_n(e, buf,
sizeof(buf));
628 results.push_back(buf);
634 int SecureTCPSocket::SSL_password_callback(
char *buf,
int num,
int rwflag,
void *userdata) {
635 std::string *password = (std::string*) userdata;
637 strncpy(buf, password->c_str(), num);
639 return (
int) password->size();