/** * Copyright (C) Justin James - All Rights Reserved. * Unauthorized use or copying of this file via any medium is strictly prohibited. * This document is proprietary and confidential. * This document should be immediately destroyed unless given explicit permission by Justin James. * Written by Justin James */ #include // std::move #include // OpenSSL SSL functions #include // OpenSSL SSL errors #include "SecureSocket.h" #include "CString.h" struct Jupiter::SecureSocket::SSLData { SSL *handle = nullptr; SSL_CTX *context = nullptr; const SSL_METHOD *method = nullptr; Jupiter::SecureSocket::EncryptionMethod eMethod = ANY; Jupiter::CStringS cert; Jupiter::CStringS key; ~SSLData(); }; Jupiter::SecureSocket::SSLData::~SSLData() { if (Jupiter::SecureSocket::SSLData::handle != nullptr) { if (SSL_shutdown(Jupiter::SecureSocket::SSLData::handle) == 0) SSL_shutdown(Jupiter::SecureSocket::SSLData::handle); SSL_free(Jupiter::SecureSocket::SSLData::handle); } if (Jupiter::SecureSocket::SSLData::context != nullptr) SSL_CTX_free(Jupiter::SecureSocket::SSLData::context); } Jupiter::SecureSocket::SecureSocket() : Jupiter::Socket() { Jupiter::SecureSocket::SSLdata_ = new Jupiter::SecureSocket::SSLData(); } Jupiter::SecureSocket::SecureSocket(size_t bufferSize) : Jupiter::Socket(bufferSize) { Jupiter::SecureSocket::SSLdata_ = new Jupiter::SecureSocket::SSLData(); } Jupiter::SecureSocket::SecureSocket(Jupiter::Socket &&source) : Jupiter::Socket(std::move(source)) { Jupiter::SecureSocket::SSLdata_ = new Jupiter::SecureSocket::SSLData(); } Jupiter::SecureSocket::SecureSocket(Jupiter::SecureSocket &&source) : Jupiter::Socket(std::move(source)) { Jupiter::SecureSocket::SSLdata_ = source.SSLdata_; source.SSLdata_ = nullptr; } Jupiter::SecureSocket::~SecureSocket() { if (Jupiter::SecureSocket::SSLdata_ != nullptr) delete Jupiter::SecureSocket::SSLdata_; } Jupiter::SecureSocket *Jupiter::SecureSocket::acceptConnection() { int tSock = SSL_accept(Jupiter::SecureSocket::SSLdata_->handle); if (tSock > 0) { SecureSocket *r = new SecureSocket(Jupiter::SecureSocket::getBufferSize()); r->setDescriptor(tSock); r->setType(this->getType()); r->setProtocol(this->getProtocol()); return r; } return nullptr; } bool Jupiter::SecureSocket::bindToPort(const char *hostname, unsigned short iPort, bool andListen) { return Jupiter::Socket::bindToPort(hostname, iPort, andListen); } void Jupiter::SecureSocket::closeSocket() { if (Jupiter::SecureSocket::SSLdata_ != nullptr) { Jupiter::Socket::closeSocket(); delete Jupiter::SecureSocket::SSLdata_; Jupiter::SecureSocket::SSLdata_ = new Jupiter::SecureSocket::SSLData(); } } const SSL_METHOD *translateEncryptionMethod(Jupiter::SecureSocket::EncryptionMethod method) { switch (method) { case Jupiter::SecureSocket::EncryptionMethod::SSL3: return SSLv3_method(); case Jupiter::SecureSocket::EncryptionMethod::TLS1: return TLSv1_method(); case Jupiter::SecureSocket::EncryptionMethod::TLS1_1: return TLSv1_1_method(); case Jupiter::SecureSocket::EncryptionMethod::TLS1_2: return TLSv1_2_method(); case Jupiter::SecureSocket::EncryptionMethod::DTLS1: return DTLSv1_method(); case Jupiter::SecureSocket::EncryptionMethod::ANY: return SSLv23_method(); default: return nullptr; } } const char *Jupiter::SecureSocket::getCipherName() const { return SSL_CIPHER_get_name(SSL_get_current_cipher(Jupiter::SecureSocket::SSLdata_->handle)); } Jupiter::SecureSocket::EncryptionMethod Jupiter::SecureSocket::getMethod() const { return Jupiter::SecureSocket::SSLdata_->eMethod; } void Jupiter::SecureSocket::setMethod(Jupiter::SecureSocket::EncryptionMethod method) { Jupiter::SecureSocket::SSLdata_->eMethod = method; } bool loadCertificate(SSL_CTX *context, const char *cert, const char *key) { if (SSL_CTX_load_verify_locations(context, cert, key) != 1) ERR_print_errors_fp(stderr); if (SSL_CTX_set_default_verify_paths(context) != 1) ERR_print_errors_fp(stderr); if (SSL_CTX_use_certificate_file(context, cert, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); return false; } if (SSL_CTX_use_PrivateKey_file(context, key, SSL_FILETYPE_PEM) <= 0) { ERR_print_errors_fp(stderr); return false; } if (!SSL_CTX_check_private_key(context)) return false; return true; } void Jupiter::SecureSocket::setCertificate(const Jupiter::ReadableString &cert, const Jupiter::ReadableString &key) { Jupiter::SecureSocket::SSLdata_->cert = cert; Jupiter::SecureSocket::SSLdata_->key = key; } void Jupiter::SecureSocket::setCertificate(const Jupiter::ReadableString &pem) { Jupiter::SecureSocket::setCertificate(pem, pem); } bool Jupiter::SecureSocket::connectToHost(const char *hostname, unsigned short iPort, const char *clientAddress, unsigned short clientPort) { return Jupiter::Socket::connectToHost(hostname, iPort, clientAddress, clientPort) && this->initSSL(); } int Jupiter::SecureSocket::peek() { int r = SSL_peek(Jupiter::SecureSocket::SSLdata_->handle, (char *) this->getBuffer(), this->getBufferSize()-1); if (r >= 0) ((char *) this->getBuffer())[r] = 0; return r; } int Jupiter::SecureSocket::recv() { if (Jupiter::SecureSocket::SSLdata_->handle == nullptr) return -1; int r = SSL_read(Jupiter::SecureSocket::SSLdata_->handle, (char *) this->getBuffer(), this->getBufferSize()-1); if (r >= 0) ((char *) this->getBuffer())[r] = 0; return r; } int Jupiter::SecureSocket::send(const char *data, size_t datalen) { return SSL_write(Jupiter::SecureSocket::SSLdata_->handle, data, datalen); } bool Jupiter::SecureSocket::initSSL() { SSL_load_error_strings(); SSL_library_init(); Jupiter::SecureSocket::SSLdata_->method = translateEncryptionMethod(Jupiter::SecureSocket::SSLdata_->eMethod); if (Jupiter::SecureSocket::SSLdata_->method == nullptr) return false; Jupiter::SecureSocket::SSLdata_->context = SSL_CTX_new(Jupiter::SecureSocket::SSLdata_->method); if (Jupiter::SecureSocket::SSLdata_->context == nullptr) { ERR_print_errors_fp(stderr); return false; } if (Jupiter::SecureSocket::SSLdata_->cert.size() != 0) loadCertificate(Jupiter::SecureSocket::SSLdata_->context, Jupiter::SecureSocket::SSLdata_->cert.c_str(), Jupiter::SecureSocket::SSLdata_->key.c_str()); Jupiter::SecureSocket::SSLdata_->handle = SSL_new(Jupiter::SecureSocket::SSLdata_->context); if (Jupiter::SecureSocket::SSLdata_->handle == nullptr) { ERR_print_errors_fp(stderr); return false; } if (SSL_set_fd(Jupiter::SecureSocket::SSLdata_->handle, this->getDescriptor()) == 0) { ERR_print_errors_fp(stderr); return false; } if (SSL_set_tlsext_host_name(Jupiter::SecureSocket::SSLdata_->handle, this->getHost()) != 1) // This error check is potentially redundant, but no documentation has been found. { ERR_print_errors_fp(stderr); return false; } int t = SSL_connect(Jupiter::SecureSocket::SSLdata_->handle); if (t != 1) { ERR_print_errors_fp(stderr); return false; } return true; }