diff --git a/TLS.cpp b/TLS.cpp new file mode 100644 index 0000000..b79a29b --- /dev/null +++ b/TLS.cpp @@ -0,0 +1,174 @@ +#include "TLS.h" +#include "EPoll.h" +#include "TCPSession.h" +#include "Exception.h" + +namespace core { + + static pthread_mutex_t *lockarray; + + static unsigned long thread_id(void) { + return ((unsigned long) pthread_self()); + } + + static void lock_callback(int mode, int type, const char *file, int line) { + if(mode & CRYPTO_LOCK) + pthread_mutex_lock(&(lockarray[type])); + else + pthread_mutex_unlock(&(lockarray[type])); + } + + TLS::TLS() { + + SSL_library_init(); + SSL_load_error_strings(); + + lockarray = (pthread_mutex_t *)OPENSSL_malloc(CRYPTO_num_locks() * sizeof(pthread_mutex_t)); + for(int i = 0; i < CRYPTO_num_locks(); ++i) + pthread_mutex_init(&(lockarray[i]), NULL); + + CRYPTO_set_id_callback((unsigned long (*)())thread_id); + CRYPTO_set_locking_callback((void (*)(int, int, const char *, int))lock_callback); + + SSLeay_add_ssl_algorithms(); + RAND_load_file("/dev/hwrng", 1024); + + if(!(ctx = SSL_CTX_new(SSLv23_server_method()))) + throw coreutils::Exception("Error while setting server method SSLv23."); + SSL_CTX_set_mode(ctx, SSL_MODE_RELEASE_BUFFERS | SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER); + SSL_CTX_set_options(ctx, SSL_OP_NO_TICKET); + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER); + // SSL_CTX_set_generate_session_id(ctx, generate_session_id); + SSL_CTX_set_cipher_list(ctx, "ECDH-ECDSA-AES256-GCM-SHA384:DHE-DSS-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA256:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA:AES256-GCM-SHA384:AES256-SHA256:AES256-SHA:AES128-GCM-SHA256:AES128-SHA256:AES128-SHA"); + if(SSL_CTX_use_certificate_file(ctx, sip_cert, SSL_FILETYPE_PEM) <= 0) + throw coreutils::Exception("Error looking up certificate."); + if(SSL_CTX_use_PrivateKey_file(ctx, sip_key, SSL_FILETYPE_PEM) < 0) + throw coreutils::Exception("Error with private key."); + if(SSL_CTX_check_private_key(ctx) != 1) + throw coreutils::Exception("Private key does not match certificate."); + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL); + SSL_CTX_set_verify_depth(ctx, 1); + if(!SSL_CTX_load_verify_locations(ctx, sip_cacert, NULL)) + throw coreutils::Exception("Cannot verify locations."); + SSL_CTX_set_client_CA_list(ctx, SSL_load_client_CA_file(sip_cacert)); + coreutils::Log(coreutils::LOG_DEBUG_1) << "Server key authenticated."; + } + + TLS::~TLS() {} + + static int generate_session_id(const SSL *ssl, unsigned char *id, unsigned int *id_len) { + char *session_id_prefix = (char *)"BARANT"; + unsigned int count = 0; + coreutils::Log(coreutils::LOG_DEBUG_3) << "Generating unique session id."; + do { + RAND_bytes(id, *id_len); + memcpy(id, session_id_prefix, (strlen(session_id_prefix) < *id_len)); + } while(SSL_has_matching_session_id(ssl, id, *id_len) && (++count < 10)); + return 1; + } + + void handshake_complete(const SSL *ssl, int where, int ret) { + coreutils::Log(coreutils::LOG_DEBUG_3) << "==>" << SSL_state_string_long(ssl) << "<==" << ret; + if(where & SSL_CB_HANDSHAKE_DONE) { + X509 *ssl_client_cert = SSL_get_peer_certificate(ssl); + if(!ssl_client_cert) + throw std::string("Unable to get peer certificate."); + X509_free(ssl_client_cert); + if(SSL_get_verify_result(ssl) != X509_V_OK) + throw std::string("Certificate verification failed."); + coreutils::Log(coreutils::LOG_DEBUG_3) << "Certificate verified successfully."; + } + else + coreutils::Log(coreutils::LOG_DEBUG_3) << "No client certificate."; + } + + TLSSession::TLSSession(EPoll &ePoll, TCPServer &server) : TCPSession(ePoll, server) {} + + void TLSSession::onRegister() { + initialized = true; + int ret; + + coreutils::Log(coreutils::LOG_DEBUG_3) << "TLS socket initializing on socket " << getDescriptor() << "..."; + + fcntl(getDescriptor(), F_SETFL, fcntl(getDescriptor(), F_GETFL, 0) | O_NONBLOCK); + + ssl = SSL_new(static_cast(server).ctx); +// if(ssl <= 0) +// throw std::string("Error creating new TLS socket."); + + SSL_set_info_callback(ssl, handshake_complete); + + if((ret = SSL_set_fd(ssl, getDescriptor())) == 0) + throw std::string("Error setting TLS socket descriptor."); + +// if(!SSL_set_generate_session_id(ssl, generate_session_id)) +// throw std::string("Error setting session identifier callback."); + + } + + void TLSSession::onRegistered() { + + switch (SSL_get_error(ssl, SSL_accept(ssl))) { + case SSL_ERROR_SSL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SSL on ssl_accept. errno=" << errno; + break; + case SSL_ERROR_WANT_READ: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_READ on ssl_accept."; + break; + case SSL_ERROR_WANT_WRITE: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_WRITE on ssl_accept."; + break; + case SSL_ERROR_SYSCALL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SYSCALL on ssl_accept. errno=" << errno; + shutdown(); + break; + default: + coreutils::Log(coreutils::LOG_DEBUG_3) << "Unknown ERROR on ssl_accept."; + break; + } + + } + + TLSSession::~TLSSession() {} + + void TLSSession::protocol(coreutils::ZString &data) {} + + void TLSSession::receiveData(coreutils::ZString &buffer) { + + int len; + // int error = -1; + // + std::cout << "receiveData TLS" << std::endl; + + if((len = ::SSL_read(ssl, buffer.getData(), buffer.getLength())) >= 0) { + std::cout << "receiveData TLS...len=" << len << ":" << buffer << std::endl; + onDataReceived(buffer); + } + else { + switch (SSL_get_error(ssl, len)) { + case SSL_ERROR_SSL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SSL on ssl_read. error=" << errno; + break; + case SSL_ERROR_WANT_READ: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_READ on ssl_read."; + break; + case SSL_ERROR_WANT_WRITE: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_WANT_WRITE on ssl_read."; + break; + case SSL_ERROR_SYSCALL: + coreutils::Log(coreutils::LOG_DEBUG_3) << "ERROR_SYSCALL on ssl_read. errno=" << errno; + break; + default: + coreutils::Log(coreutils::LOG_DEBUG_3) << "Unknown ERROR on ssl_read."; + break; + } + } + + } + + void TLSSession::output(std::stringstream &out) { + out << "|" << ipAddress.getClientAddressAndPort(); + } + +} + \ No newline at end of file diff --git a/TLS.h b/TLS.h new file mode 100644 index 0000000..4f2dbf9 --- /dev/null +++ b/TLS.h @@ -0,0 +1,65 @@ +#ifndef TLSServerSocket_h__ +#define TLSServerSocket_h__ + +#include "Socket.h" +#include "TCPServer.h" +#include "Command.h" +#include "TCPSession.h" +#include "IPAddress.h" + +namespace core { + + /// + /// TLS + /// + /// This object provides the support methods to handle TLS on the server core and + /// session environment. + /// + + class TLS { + + public: + + TLS(); + + /// + /// The destructor for this object. + /// + + ~TLS(); + + SSL_CTX *ctx; + + private: + + char *sip_cacert = (char *)"../testkeys/certs/pbxca.crt"; + char *sip_cert = (char *)"../testkeys/certs/pbxserver.crt"; + char *sip_key = (char *)"../testkeys/certs/pbxserver.key"; + + + public: + + /// + /// The output method is called by a socket session (Session) and + /// will output the detail information for the client socket. When extending + /// TLSSocket or Session you can override the method to add attributes + /// to the list. + /// + + virtual void output(std::stringstream &out); + virtual void protocol(coreutils::ZString &data) override; + + protected: + void receiveData(coreutils::ZString &buffer) override; + void onRegister(); + void onRegistered(); + + private: + bool initialized = false; + SSL *ssl; + + }; + +} + +#endif