diff options
Diffstat (limited to 'src/components/security_manager/src')
4 files changed, 798 insertions, 332 deletions
diff --git a/src/components/security_manager/src/crypto_manager_impl.cc b/src/components/security_manager/src/crypto_manager_impl.cc index 69121a7b19..f44198953b 100644 --- a/src/components/security_manager/src/crypto_manager_impl.cc +++ b/src/components/security_manager/src/crypto_manager_impl.cc @@ -31,143 +31,205 @@ */ #include "security_manager/crypto_manager_impl.h" + #include <openssl/bio.h> #include <openssl/ssl.h> #include <openssl/err.h> +#include <openssl/pkcs12.h> + +#include <fstream> +#include <iostream> +#include <stdio.h> +#include <ctime> #include "security_manager/security_manager.h" + #include "utils/logger.h" #include "utils/atomic.h" +#include "utils/macro.h" +#include "utils/scope_guard.h" +#include "utils/date_time.h" -#define TLS1_1_MINIMAL_VERSION 0x1000103fL -#define CONST_SSL_METHOD_MINIMAL_VERSION 0x00909000L +#define TLS1_1_MINIMAL_VERSION 0x1000103fL +#define CONST_SSL_METHOD_MINIMAL_VERSION 0x00909000L namespace security_manager { -CREATE_LOGGERPTR_GLOBAL(logger_, "CryptoManagerImpl") +CREATE_LOGGERPTR_GLOBAL(logger_, "SecurityManager") uint32_t CryptoManagerImpl::instance_count_ = 0; +sync_primitives::Lock CryptoManagerImpl::instance_lock_; -CryptoManagerImpl::CryptoManagerImpl() - : context_(NULL), mode_(CLIENT) { +namespace { +int debug_callback(int preverify_ok, X509_STORE_CTX* ctx) { + if (!preverify_ok) { + const int error = X509_STORE_CTX_get_error(ctx); + UNUSED(error); + LOG4CXX_WARN(logger_, + "Certificate verification failed with error " + << error << " \"" << X509_verify_cert_error_string(error) + << '"'); + } + return preverify_ok; } -bool CryptoManagerImpl::Init(Mode mode, - Protocol protocol, - const std::string &cert_filename, - const std::string &key_filename, - const std::string &ciphers_list, - bool verify_peer) { - if (atomic_post_inc(&instance_count_) == 0) { +void free_ctx(SSL_CTX** ctx) { + if (ctx) { + SSL_CTX_free(*ctx); + *ctx = NULL; + } +} +} + +CryptoManagerImpl::CryptoManagerImpl( + const utils::SharedPtr<const CryptoManagerSettings> set) + : settings_(set), context_(NULL) { + LOG4CXX_AUTO_TRACE(logger_); + sync_primitives::AutoLock lock(instance_lock_); + instance_count_++; + if (instance_count_ == 1) { + LOG4CXX_DEBUG(logger_, "Openssl engine initialization"); SSL_load_error_strings(); ERR_load_BIO_strings(); OpenSSL_add_all_algorithms(); SSL_library_init(); } +} - mode_ = mode; +CryptoManagerImpl::~CryptoManagerImpl() { + LOG4CXX_AUTO_TRACE(logger_); + sync_primitives::AutoLock lock(instance_lock_); + LOG4CXX_DEBUG(logger_, "Deinitilization"); + if (!context_) { + LOG4CXX_WARN(logger_, "Manager is not initialized"); + } else { + SSL_CTX_free(context_); + } + instance_count_--; + if (instance_count_ == 0) { + LOG4CXX_DEBUG(logger_, "Openssl engine deinitialization"); + EVP_cleanup(); + ERR_free_strings(); + } +} + +bool CryptoManagerImpl::Init() { + LOG4CXX_AUTO_TRACE(logger_); + + const Mode mode = get_settings().security_manager_mode(); const bool is_server = (mode == SERVER); + if (is_server) { + LOG4CXX_DEBUG(logger_, "Server mode"); + } else { + LOG4CXX_DEBUG(logger_, "Client mode"); + } + LOG4CXX_DEBUG(logger_, + "Peer verification " + << (get_settings().verify_peer() ? "enabled" : "disabled")); + LOG4CXX_DEBUG(logger_, + "CA certificate file is \"" << get_settings().ca_cert_path() + << '"'); + #if OPENSSL_VERSION_NUMBER < CONST_SSL_METHOD_MINIMAL_VERSION - SSL_METHOD *method; + SSL_METHOD* method; #else - const SSL_METHOD *method; + const SSL_METHOD* method; #endif - switch (protocol) { + switch (get_settings().security_manager_protocol_name()) { case SSLv3: - method = is_server ? - SSLv3_server_method() : - SSLv3_client_method(); + method = is_server ? SSLv3_server_method() : SSLv3_client_method(); break; case TLSv1: - method = is_server ? - TLSv1_server_method() : - TLSv1_client_method(); + method = is_server ? TLSv1_server_method() : TLSv1_client_method(); break; case TLSv1_1: #if OPENSSL_VERSION_NUMBER < TLS1_1_MINIMAL_VERSION - LOG4CXX_WARN(logger_, - "OpenSSL has no TLSv1.1 with version lower 1.0.1, set TLSv1.0"); - method = is_server ? - TLSv1_server_method() : - TLSv1_client_method(); + LOG4CXX_WARN( + logger_, + "OpenSSL has no TLSv1.1 with version lower 1.0.1, set TLSv1.0"); + method = is_server ? TLSv1_server_method() : TLSv1_client_method(); #else - method = is_server ? - TLSv1_1_server_method() : - TLSv1_1_client_method(); + method = is_server ? TLSv1_1_server_method() : TLSv1_1_client_method(); #endif break; case TLSv1_2: #if OPENSSL_VERSION_NUMBER < TLS1_1_MINIMAL_VERSION - LOG4CXX_WARN(logger_, - "OpenSSL has no TLSv1.2 with version lower 1.0.1, set TLSv1.0"); - method = is_server ? - TLSv1_server_method() : - TLSv1_client_method(); + LOG4CXX_WARN( + logger_, + "OpenSSL has no TLSv1.2 with version lower 1.0.1, set TLSv1.0"); + method = is_server ? TLSv1_server_method() : TLSv1_client_method(); #else - method = is_server ? - TLSv1_2_server_method() : - TLSv1_2_client_method(); + method = is_server ? TLSv1_2_server_method() : TLSv1_2_client_method(); #endif break; default: - LOG4CXX_ERROR(logger_, "Unknown protocol: " << protocol); + LOG4CXX_ERROR(logger_, + "Unknown protocol: " + << get_settings().security_manager_protocol_name()); return false; } + if (context_) { + free_ctx(&context_); + } context_ = SSL_CTX_new(method); + utils::ScopeGuard guard = utils::MakeGuard(free_ctx, &context_); + // Disable SSL2 as deprecated SSL_CTX_set_options(context_, SSL_OP_NO_SSLv2); - if (cert_filename.empty()) { - LOG4CXX_WARN(logger_, "Empty certificate path"); + set_certificate(get_settings().certificate_data()); + + if (get_settings().ciphers_list().empty()) { + LOG4CXX_WARN(logger_, "Empty ciphers list"); } else { - LOG4CXX_INFO(logger_, "Certificate path: " << cert_filename); - if (!SSL_CTX_use_certificate_file(context_, cert_filename.c_str(), - SSL_FILETYPE_PEM)) { - LOG4CXX_ERROR(logger_, "Could not use certificate " << cert_filename); + LOG4CXX_DEBUG(logger_, "Cipher list: " << get_settings().ciphers_list()); + if (!SSL_CTX_set_cipher_list(context_, + get_settings().ciphers_list().c_str())) { + LOG4CXX_ERROR( + logger_, + "Could not set cipher list: " << get_settings().ciphers_list()); return false; } } - if (key_filename.empty()) { - LOG4CXX_WARN(logger_, "Empty key path"); - } else { - LOG4CXX_INFO(logger_, "Key path: " << key_filename); - if (!SSL_CTX_use_PrivateKey_file(context_, key_filename.c_str(), - SSL_FILETYPE_PEM)) { - LOG4CXX_ERROR(logger_, "Could not use key " << key_filename); - return false; - } - if (!SSL_CTX_check_private_key(context_)) { - LOG4CXX_ERROR(logger_, "Could not use certificate " << cert_filename); - return false; - } + if (get_settings().ca_cert_path().empty()) { + LOG4CXX_WARN(logger_, "Setting up empty CA certificate location"); } - if (ciphers_list.empty()) { - LOG4CXX_WARN(logger_, "Empty ciphers list"); - } else { - LOG4CXX_INFO(logger_, "Cipher list: " << ciphers_list); - if (!SSL_CTX_set_cipher_list(context_, ciphers_list.c_str())) { - LOG4CXX_ERROR(logger_, "Could not set cipher list: " << ciphers_list); - return false; - } + LOG4CXX_DEBUG(logger_, "Setting up CA certificate location"); + const int result = SSL_CTX_load_verify_locations( + context_, NULL, get_settings().ca_cert_path().c_str()); + + if (!result) { + const unsigned long error = ERR_get_error(); + UNUSED(error); + LOG4CXX_WARN(logger_, + "Wrong certificate file '" + << get_settings().ca_cert_path() << "', err 0x" << std::hex + << error << " \"" << ERR_reason_error_string(error) + << '"'); } - // TODO(EZamakhov): add loading SSL_VERIFY_FAIL_IF_NO_PEER_CERT from INI - const int verify_mode = verify_peer - ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT - : SSL_VERIFY_NONE; - SSL_CTX_set_verify(context_, verify_mode, NULL); + guard.Dismiss(); + const int verify_mode = + get_settings().verify_peer() + ? SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT + : SSL_VERIFY_NONE; + LOG4CXX_DEBUG(logger_, + "Setting up peer verification in mode: " << verify_mode); + SSL_CTX_set_verify(context_, verify_mode, &debug_callback); return true; } -void CryptoManagerImpl::Finish() { - SSL_CTX_free(context_); - if (atomic_post_dec(&instance_count_) == 1) { - EVP_cleanup(); - ERR_free_strings(); +bool CryptoManagerImpl::OnCertificateUpdated(const std::string& data) { + LOG4CXX_AUTO_TRACE(logger_); + if (!context_) { + LOG4CXX_WARN(logger_, "Not initialized"); + return false; } + + return set_certificate(data); } SSLContext* CryptoManagerImpl::CreateSSLContext() { @@ -175,19 +237,21 @@ SSLContext* CryptoManagerImpl::CreateSSLContext() { return NULL; } - SSL *conn = SSL_new(context_); + SSL* conn = SSL_new(context_); if (conn == NULL) return NULL; - if (mode_ == SERVER) { + if (get_settings().security_manager_mode() == SERVER) { SSL_set_accept_state(conn); } else { SSL_set_connect_state(conn); } - return new SSLContextImpl(conn, mode_); + return new SSLContextImpl(conn, + get_settings().security_manager_mode(), + get_settings().maximum_payload_size()); } -void CryptoManagerImpl::ReleaseSSLContext(SSLContext *context) { +void CryptoManagerImpl::ReleaseSSLContext(SSLContext* context) { delete context; } @@ -195,8 +259,142 @@ std::string CryptoManagerImpl::LastError() const { if (!context_) { return std::string("Initialization is not completed"); } - const char *reason = ERR_reason_error_string(ERR_get_error()); + const char* reason = ERR_reason_error_string(ERR_get_error()); return std::string(reason ? reason : ""); } +bool CryptoManagerImpl::IsCertificateUpdateRequired() const { + LOG4CXX_AUTO_TRACE(logger_); + + const time_t cert_date = mktime(&expiration_time_); + + if (cert_date == -1) { + LOG4CXX_WARN(logger_, + "The certifiacte expiration time cannot be represented."); + return false; + } + const time_t now = time(NULL); + const double seconds = difftime(cert_date, now); + + LOG4CXX_DEBUG(logger_, + "Certificate expiration time: " << asctime(&expiration_time_)); + LOG4CXX_DEBUG(logger_, + "Host time: " << asctime(localtime(&now)) + << ". Seconds before expiration: " << seconds); + if (seconds < 0) { + LOG4CXX_WARN(logger_, "Certificate is already expired."); + return true; + } + + return seconds <= (get_settings().update_before_hours() * + date_time::DateTime::SECONDS_IN_HOUR); +} + +const CryptoManagerSettings& CryptoManagerImpl::get_settings() const { + return *settings_; +} + +bool CryptoManagerImpl::set_certificate(const std::string& cert_data) { + if (cert_data.empty()) { + LOG4CXX_WARN(logger_, "Empty certificate"); + return false; + } + + BIO* bio = BIO_new(BIO_f_base64()); + BIO* bmem = BIO_new_mem_buf((char*)cert_data.c_str(), cert_data.length()); + bmem = BIO_push(bio, bmem); + + char* buf = new char[cert_data.length()]; + int len = BIO_read(bmem, buf, cert_data.length()); + + BIO* bio_cert = BIO_new(BIO_s_mem()); + if (NULL == bio_cert) { + LOG4CXX_WARN(logger_, "Unable to update certificate. BIO not created"); + return false; + } + + utils::ScopeGuard bio_guard = utils::MakeGuard(BIO_free, bio_cert); + UNUSED(bio_guard) + int k = 0; + if ((k = BIO_write(bio_cert, buf, len)) <= 0) { + LOG4CXX_WARN(logger_, "Unable to write into BIO"); + return false; + } + + PKCS12* p12 = d2i_PKCS12_bio(bio_cert, NULL); + if (NULL == p12) { + LOG4CXX_ERROR(logger_, "Unable to parse certificate"); + return false; + } + + EVP_PKEY* pkey = NULL; + X509* cert = NULL; + PKCS12_parse(p12, NULL, &pkey, &cert, NULL); + + if (NULL == cert || NULL == pkey) { + LOG4CXX_WARN(logger_, "Either certificate or key not valid."); + return false; + } + + if (!SSL_CTX_use_certificate(context_, cert)) { + LOG4CXX_WARN(logger_, "Could not use certificate"); + return false; + } + + asn1_time_to_tm(X509_get_notAfter(cert)); + + if (!SSL_CTX_use_PrivateKey(context_, pkey)) { + LOG4CXX_ERROR(logger_, "Could not use key"); + return false; + } + if (!SSL_CTX_check_private_key(context_)) { + LOG4CXX_ERROR(logger_, "Could not use certificate "); + return false; + } + return true; +} + +int CryptoManagerImpl::pull_number_from_buf(char* buf, int* idx) { + if (!idx) { + return 0; + } + const int val = ((buf[*idx] - '0') * 10) + buf[(*idx) + 1] - '0'; + *idx = *idx + 2; + return val; +} + +void CryptoManagerImpl::asn1_time_to_tm(ASN1_TIME* time) { + char* buf = (char*)time->data; + int index = 0; + const int year = pull_number_from_buf(buf, &index); + if (V_ASN1_GENERALIZEDTIME == time->type) { + expiration_time_.tm_year = + (year * 100 - 1900) + pull_number_from_buf(buf, &index); + } else { + expiration_time_.tm_year = year < 50 ? year + 100 : year; + } + + const int mon = pull_number_from_buf(buf, &index); + const int day = pull_number_from_buf(buf, &index); + const int hour = pull_number_from_buf(buf, &index); + const int mn = pull_number_from_buf(buf, &index); + + expiration_time_.tm_mon = mon - 1; + expiration_time_.tm_mday = day; + expiration_time_.tm_hour = hour; + expiration_time_.tm_min = mn; + + if (buf[index] == 'Z') { + expiration_time_.tm_sec = 0; + } + if ((buf[index] == '+') || (buf[index] == '-')) { + const int mn = pull_number_from_buf(buf, &index); + const int mn1 = pull_number_from_buf(buf, &index); + expiration_time_.tm_sec = (mn * 3600) + (mn1 * 60); + } else { + const int sec = pull_number_from_buf(buf, &index); + expiration_time_.tm_sec = sec; + } +} + } // namespace security_manager diff --git a/src/components/security_manager/src/security_manager_impl.cc b/src/components/security_manager/src/security_manager_impl.cc index dee1770e70..556cc291d1 100644 --- a/src/components/security_manager/src/security_manager_impl.cc +++ b/src/components/security_manager/src/security_manager_impl.cc @@ -45,9 +45,10 @@ static const char* kErrId = "id"; static const char* kErrText = "text"; SecurityManagerImpl::SecurityManagerImpl() - : security_messages_("SecurityManager", this), - session_observer_(NULL), crypto_manager_(NULL), protocol_handler_(NULL) { -} + : security_messages_("SecurityManager", this) + , session_observer_(NULL) + , crypto_manager_(NULL) + , protocol_handler_(NULL) {} void SecurityManagerImpl::OnMessageReceived( const ::protocol_handler::RawMessagePtr message) { @@ -56,14 +57,14 @@ void SecurityManagerImpl::OnMessageReceived( } SecurityMessage securityMessagePtr(new SecurityQuery()); - const bool result = securityMessagePtr->SerializeQuery( - message->data(), message->data_size()); + const bool result = + securityMessagePtr->SerializeQuery(message->data(), message->data_size()); if (!result) { // result will be false only if data less then query header const std::string error_text("Incorrect message received"); LOG4CXX_ERROR(logger_, error_text); - SendInternalError(message->connection_key(), - ERROR_INVALID_QUERY_SIZE, error_text); + SendInternalError( + message->connection_key(), ERROR_INVALID_QUERY_SIZE, error_text); return; } securityMessagePtr->set_connection_key(message->connection_key()); @@ -73,11 +74,10 @@ void SecurityManagerImpl::OnMessageReceived( } void SecurityManagerImpl::OnMobileMessageSent( - const ::protocol_handler::RawMessagePtr ) { -} + const ::protocol_handler::RawMessagePtr) {} void SecurityManagerImpl::set_session_observer( - protocol_handler::SessionObserver *observer) { + protocol_handler::SessionObserver* observer) { if (!observer) { LOG4CXX_ERROR(logger_, "Invalid (NULL) pointer to SessionObserver."); return; @@ -86,7 +86,7 @@ void SecurityManagerImpl::set_session_observer( } void SecurityManagerImpl::set_protocol_handler( - protocol_handler::ProtocolHandler *handler) { + protocol_handler::ProtocolHandler* handler) { if (!handler) { LOG4CXX_ERROR(logger_, "Invalid (NULL) pointer to ProtocolHandler."); return; @@ -94,7 +94,7 @@ void SecurityManagerImpl::set_protocol_handler( protocol_handler_ = handler; } -void SecurityManagerImpl::set_crypto_manager(CryptoManager *crypto_manager) { +void SecurityManagerImpl::set_crypto_manager(CryptoManager* crypto_manager) { if (!crypto_manager) { LOG4CXX_ERROR(logger_, "Invalid (NULL) pointer to CryptoManager."); return; @@ -105,11 +105,11 @@ void SecurityManagerImpl::set_crypto_manager(CryptoManager *crypto_manager) { void SecurityManagerImpl::Handle(const SecurityMessage message) { DCHECK(message); LOG4CXX_INFO(logger_, "Received Security message from Mobile side"); - if (!crypto_manager_) { + if (!crypto_manager_) { const std::string error_text("Invalid (NULL) CryptoManager."); LOG4CXX_ERROR(logger_, error_text); - SendInternalError(message->get_connection_key(), - ERROR_NOT_SUPPORTED, error_text); + SendInternalError( + message->get_connection_key(), ERROR_NOT_SUPPORTED, error_text); return; } switch (message->get_header().query_id) { @@ -128,21 +128,21 @@ void SecurityManagerImpl::Handle(const SecurityMessage message) { const std::string error_text("Unknown query identifier."); LOG4CXX_ERROR(logger_, error_text); SendInternalError(message->get_connection_key(), - ERROR_INVALID_QUERY_ID, error_text, + ERROR_INVALID_QUERY_ID, + error_text, message->get_header().seq_number); - } - break; - } + } break; + } } -security_manager::SSLContext *SecurityManagerImpl::CreateSSLContext( - const uint32_t &connection_key) { +security_manager::SSLContext* SecurityManagerImpl::CreateSSLContext( + const uint32_t& connection_key) { LOG4CXX_INFO(logger_, "ProtectService processing"); DCHECK(session_observer_); DCHECK(crypto_manager_); - security_manager::SSLContext *ssl_context = - session_observer_->GetSSLContext(connection_key, protocol_handler::kControl); + security_manager::SSLContext* ssl_context = session_observer_->GetSSLContext( + connection_key, protocol_handler::kControl); // return exists SSLCOntext for current connection/session if (ssl_context) { return ssl_context; @@ -153,12 +153,12 @@ security_manager::SSLContext *SecurityManagerImpl::CreateSSLContext( const std::string error_text("CryptoManager could not create SSL context."); LOG4CXX_ERROR(logger_, error_text); // Generate response query and post to security_messages_ - SendInternalError(connection_key, ERROR_INTERNAL, - error_text); + SendInternalError(connection_key, ERROR_INTERNAL, error_text); return NULL; } - const int result = session_observer_->SetSSLContext(connection_key, ssl_context); + const int result = + session_observer_->SetSSLContext(connection_key, ssl_context); if (ERROR_SUCCESS != result) { // delete SSLContext on any error crypto_manager_->ReleaseSSLContext(ssl_context); @@ -167,38 +167,51 @@ security_manager::SSLContext *SecurityManagerImpl::CreateSSLContext( } DCHECK(session_observer_->GetSSLContext(connection_key, protocol_handler::kControl)); - LOG4CXX_DEBUG(logger_, "Set SSL context to connection_key " << connection_key); + LOG4CXX_DEBUG(logger_, + "Set SSL context to connection_key " << connection_key); return ssl_context; } void SecurityManagerImpl::StartHandshake(uint32_t connection_key) { DCHECK(session_observer_); LOG4CXX_INFO(logger_, "StartHandshake: connection_key " << connection_key); - security_manager::SSLContext *ssl_context = - session_observer_->GetSSLContext(connection_key, - protocol_handler::kControl); + security_manager::SSLContext* ssl_context = session_observer_->GetSSLContext( + connection_key, protocol_handler::kControl); if (!ssl_context) { - const std::string error_text("StartHandshake failed, " - "connection is not protected"); + const std::string error_text( + "StartHandshake failed, " + "connection is not protected"); LOG4CXX_ERROR(logger_, error_text); SendInternalError(connection_key, ERROR_INTERNAL, error_text); - NotifyListenersOnHandshakeDone(connection_key, false); + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_Fail); return; } + if (crypto_manager_->IsCertificateUpdateRequired()) { + NotifyOnCertififcateUpdateRequired(); + } + if (ssl_context->IsInitCompleted()) { - NotifyListenersOnHandshakeDone(connection_key, true); + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_Success); return; } + + ssl_context->SetHandshakeContext( + session_observer_->GetHandshakeContext(connection_key)); + size_t data_size = 0; - const uint8_t *data = NULL; + const uint8_t* data = NULL; + const security_manager::SSLContext::HandshakeResult result = ssl_context->StartHandshake(&data, &data_size); if (security_manager::SSLContext::Handshake_Result_Success != result) { const std::string error_text("StartHandshake failed, handshake step fail"); LOG4CXX_ERROR(logger_, error_text); SendInternalError(connection_key, ERROR_INTERNAL, error_text); - NotifyListenersOnHandshakeDone(connection_key, false); + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_Fail); return; } // for client mode will be generated output data @@ -206,26 +219,29 @@ void SecurityManagerImpl::StartHandshake(uint32_t connection_key) { SendHandshakeBinData(connection_key, data, data_size); } } -void SecurityManagerImpl::AddListener(SecurityManagerListener *const listener) { +void SecurityManagerImpl::AddListener(SecurityManagerListener* const listener) { if (!listener) { - LOG4CXX_ERROR(logger_, "Invalid (NULL) pointer to SecurityManagerListener."); + LOG4CXX_ERROR(logger_, + "Invalid (NULL) pointer to SecurityManagerListener."); return; } listeners_.push_back(listener); } -void SecurityManagerImpl::RemoveListener(SecurityManagerListener *const listener) { +void SecurityManagerImpl::RemoveListener( + SecurityManagerListener* const listener) { if (!listener) { - LOG4CXX_ERROR(logger_, "Invalid (NULL) pointer to SecurityManagerListener."); + LOG4CXX_ERROR(logger_, + "Invalid (NULL) pointer to SecurityManagerListener."); return; } listeners_.remove(listener); } -void SecurityManagerImpl::NotifyListenersOnHandshakeDone(const uint32_t &connection_key, - const bool success) { - LOG4CXX_TRACE(logger_, "NotifyListenersOnHandshakeDone"); +void SecurityManagerImpl::NotifyListenersOnHandshakeDone( + const uint32_t& connection_key, SSLContext::HandshakeResult error) { + LOG4CXX_AUTO_TRACE(logger_); std::list<SecurityManagerListener*>::iterator it = listeners_.begin(); while (it != listeners_.end()) { - if ((*it)->OnHandshakeDone(connection_key, success)) { + if ((*it)->OnHandshakeDone(connection_key, error)) { // On get notification remove listener it = listeners_.erase(it); } else { @@ -234,140 +250,166 @@ void SecurityManagerImpl::NotifyListenersOnHandshakeDone(const uint32_t &connect } } -bool SecurityManagerImpl::ProccessHandshakeData(const SecurityMessage &inMessage) { +void SecurityManagerImpl::NotifyOnCertififcateUpdateRequired() { + LOG4CXX_AUTO_TRACE(logger_); + std::list<SecurityManagerListener*>::iterator it = listeners_.begin(); + while (it != listeners_.end()) { + (*it)->OnCertificateUpdateRequired(); + ++it; + } +} + +bool SecurityManagerImpl::ProccessHandshakeData( + const SecurityMessage& inMessage) { LOG4CXX_INFO(logger_, "SendHandshakeData processing"); DCHECK(inMessage); - DCHECK(inMessage->get_header().query_id == SecurityQuery::SEND_HANDSHAKE_DATA); + DCHECK(inMessage->get_header().query_id == + SecurityQuery::SEND_HANDSHAKE_DATA); const uint32_t seqNumber = inMessage->get_header().seq_number; const uint32_t connection_key = inMessage->get_connection_key(); - LOG4CXX_DEBUG(logger_, "Received " << inMessage->get_data_size() - << " bytes handshake data "); + LOG4CXX_DEBUG(logger_, + "Received " << inMessage->get_data_size() + << " bytes handshake data "); if (!inMessage->get_data_size()) { const std::string error_text("SendHandshakeData: null arguments size."); LOG4CXX_ERROR(logger_, error_text); - SendInternalError(connection_key, ERROR_INVALID_QUERY_SIZE, - error_text, seqNumber); + SendInternalError( + connection_key, ERROR_INVALID_QUERY_SIZE, error_text, seqNumber); return false; } DCHECK(session_observer_); - SSLContext *sslContext = - session_observer_->GetSSLContext(connection_key, - protocol_handler::kControl); + SSLContext* sslContext = session_observer_->GetSSLContext( + connection_key, protocol_handler::kControl); if (!sslContext) { const std::string error_text("SendHandshakeData: No ssl context."); LOG4CXX_ERROR(logger_, error_text); - SendInternalError(connection_key, ERROR_SERVICE_NOT_PROTECTED, - error_text, seqNumber); - NotifyListenersOnHandshakeDone(connection_key, false); + SendInternalError( + connection_key, ERROR_SERVICE_NOT_PROTECTED, error_text, seqNumber); + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_Fail); return false; } size_t out_data_size; - const uint8_t *out_data; + const uint8_t* out_data; const SSLContext::HandshakeResult handshake_result = - sslContext->DoHandshakeStep(inMessage->get_data(), inMessage->get_data_size(), - &out_data, &out_data_size); + sslContext->DoHandshakeStep(inMessage->get_data(), + inMessage->get_data_size(), + &out_data, + &out_data_size); if (handshake_result == SSLContext::Handshake_Result_AbnormalFail) { // Do not return handshake data on AbnormalFail or null returned values const std::string erorr_text(sslContext->LastError()); - LOG4CXX_ERROR(logger_, "SendHandshakeData: Handshake failed: " << erorr_text); - SendInternalError(connection_key, - ERROR_SSL_INVALID_DATA, erorr_text, seqNumber); - NotifyListenersOnHandshakeDone(connection_key, false); + LOG4CXX_ERROR(logger_, + "SendHandshakeData: Handshake failed: " << erorr_text); + SendInternalError( + connection_key, ERROR_SSL_INVALID_DATA, erorr_text, seqNumber); + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_Fail); // no handshake data to send return false; } if (sslContext->IsInitCompleted()) { // On handshake success LOG4CXX_DEBUG(logger_, "SSL initialization finished success."); - NotifyListenersOnHandshakeDone(connection_key, true); - } else if (handshake_result == SSLContext::Handshake_Result_Fail) { + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_Success); + } else if (handshake_result != SSLContext::Handshake_Result_Success) { // On handshake fail LOG4CXX_WARN(logger_, "SSL initialization finished with fail."); - NotifyListenersOnHandshakeDone(connection_key, false); + NotifyListenersOnHandshakeDone(connection_key, handshake_result); } if (out_data && out_data_size) { // answer with the same seqNumber as income message - SendHandshakeBinData(connection_key, out_data, out_data_size, - seqNumber); + SendHandshakeBinData(connection_key, out_data, out_data_size, seqNumber); } return true; } -bool SecurityManagerImpl::ProccessInternalError(const SecurityMessage &inMessage) { - LOG4CXX_INFO(logger_, "Received InternalError with Json message" - << inMessage->get_json_message()); +bool SecurityManagerImpl::ProccessInternalError( + const SecurityMessage& inMessage) { + LOG4CXX_INFO(logger_, + "Received InternalError with Json message" + << inMessage->get_json_message()); Json::Value root; Json::Reader reader; const bool parsingSuccessful = reader.parse(inMessage->get_json_message(), root); if (!parsingSuccessful) return false; - LOG4CXX_DEBUG(logger_, "Received InternalError id " << root[kErrId].asString() - << ", text: " << root[kErrText].asString()); + LOG4CXX_DEBUG(logger_, + "Received InternalError id " + << root[kErrId].asString() + << ", text: " << root[kErrText].asString()); return true; } -void SecurityManagerImpl::SendHandshakeBinData( - const uint32_t connection_key, const uint8_t *const data, - const size_t data_size, const uint32_t seq_number) { - const SecurityQuery::QueryHeader header( - SecurityQuery::NOTIFICATION, - SecurityQuery::SEND_HANDSHAKE_DATA, seq_number); - DCHECK(data_size < 1024 * 1024 *1024 ); - const SecurityQuery query = SecurityQuery(header, connection_key, data, data_size); +void SecurityManagerImpl::SendHandshakeBinData(const uint32_t connection_key, + const uint8_t* const data, + const size_t data_size, + const uint32_t seq_number) { + const SecurityQuery::QueryHeader header(SecurityQuery::NOTIFICATION, + SecurityQuery::SEND_HANDSHAKE_DATA, + seq_number); + DCHECK(data_size < 1024 * 1024 * 1024); + const SecurityQuery query = + SecurityQuery(header, connection_key, data, data_size); SendQuery(query, connection_key); LOG4CXX_DEBUG(logger_, "Sent " << data_size << " bytes handshake data "); } void SecurityManagerImpl::SendInternalError(const uint32_t connection_key, - const uint8_t &error_id, - const std::string &erorr_text, - const uint32_t seq_number) { + const uint8_t& error_id, + const std::string& erorr_text, + const uint32_t seq_number) { Json::Value value; - value[kErrId] = error_id; + value[kErrId] = error_id; value[kErrText] = erorr_text; const std::string error_str = value.toStyledString(); - SecurityQuery::QueryHeader header(SecurityQuery::NOTIFICATION, - SecurityQuery::SEND_INTERNAL_ERROR, - // header save json size only (exclude last byte) - seq_number, error_str.size()); + SecurityQuery::QueryHeader header( + SecurityQuery::NOTIFICATION, + SecurityQuery::SEND_INTERNAL_ERROR, + // header save json size only (exclude last byte) + seq_number, + error_str.size()); // Raw data is json string and error id at last byte std::vector<uint8_t> data_sending(error_str.size() + 1); memcpy(&data_sending[0], error_str.c_str(), error_str.size()); - data_sending[data_sending.size()-1] = error_id; + data_sending[data_sending.size() - 1] = error_id; - const SecurityQuery query(header, connection_key, - &data_sending[0], data_sending.size()); + const SecurityQuery query( + header, connection_key, &data_sending[0], data_sending.size()); SendQuery(query, connection_key); - LOG4CXX_DEBUG(logger_, "Sent Internal error id " << static_cast<int>(error_id) - << " : \"" << erorr_text << "\"."); + LOG4CXX_DEBUG(logger_, + "Sent Internal error id " << static_cast<int>(error_id) + << " : \"" << erorr_text << "\"."); } void SecurityManagerImpl::SendQuery(const SecurityQuery& query, - const uint32_t connection_key) { + const uint32_t connection_key) { const std::vector<uint8_t> data_sending = query.DeserializeQuery(); uint32_t connection_handle = 0; uint8_t sessionID = 0; uint8_t protocol_version; - session_observer_->PairFromKey(connection_key, &connection_handle, - &sessionID); - if (session_observer_->ProtocolVersionUsed(connection_handle, sessionID, - protocol_version)) { + session_observer_->PairFromKey( + connection_key, &connection_handle, &sessionID); + if (session_observer_->ProtocolVersionUsed( + connection_handle, sessionID, protocol_version)) { const ::protocol_handler::RawMessagePtr rawMessagePtr( - new protocol_handler::RawMessage(connection_key, - protocol_version, - &data_sending[0], data_sending.size(), - protocol_handler::kControl)); + new protocol_handler::RawMessage(connection_key, + protocol_version, + &data_sending[0], + data_sending.size(), + protocol_handler::kControl)); DCHECK(protocol_handler_); // Add RawMessage to ProtocolHandler message query protocol_handler_->SendMessageToMobileApp(rawMessagePtr, false); } } -const char *SecurityManagerImpl::ConfigSection() { +const char* SecurityManagerImpl::ConfigSection() { return "Security Manager"; } diff --git a/src/components/security_manager/src/security_query.cc b/src/components/security_manager/src/security_query.cc index cd4da03c15..60233dde5e 100644 --- a/src/components/security_manager/src/security_query.cc +++ b/src/components/security_manager/src/security_query.cc @@ -38,35 +38,37 @@ namespace security_manager { SecurityQuery::QueryHeader::QueryHeader() - : query_type(INVALID_QUERY_TYPE), query_id(INVALID_QUERY_ID), - seq_number(0), json_size(0) { -} - -SecurityQuery::QueryHeader::QueryHeader(uint8_t queryType, uint32_t queryId, - uint32_t seqNumber, uint32_t jsonSize) - : query_type(queryType), query_id(queryId), - seq_number(seqNumber), json_size(jsonSize) { -} + : query_type(INVALID_QUERY_TYPE) + , query_id(INVALID_QUERY_ID) + , seq_number(0) + , json_size(0) {} + +SecurityQuery::QueryHeader::QueryHeader(uint8_t queryType, + uint32_t queryId, + uint32_t seqNumber, + uint32_t jsonSize) + : query_type(queryType) + , query_id(queryId) + , seq_number(seqNumber) + , json_size(jsonSize) {} SecurityQuery::SecurityQuery() - : header_(INVALID_QUERY_TYPE, INVALID_QUERY_ID, 0), connection_key_(0) { -} + : header_(INVALID_QUERY_TYPE, INVALID_QUERY_ID, 0), connection_key_(0) {} -SecurityQuery::SecurityQuery(const SecurityQuery::QueryHeader &header, +SecurityQuery::SecurityQuery(const SecurityQuery::QueryHeader& header, const uint32_t connection_key, - const uint8_t *const raw_data, + const uint8_t* const raw_data, const size_t raw_data_size) - : header_(header), connection_key_(connection_key), - data_(raw_data, raw_data + raw_data_size) { -} + : header_(header) + , connection_key_(connection_key) + , data_(raw_data, raw_data + raw_data_size) {} -SecurityQuery::SecurityQuery(const SecurityQuery::QueryHeader &header, +SecurityQuery::SecurityQuery(const SecurityQuery::QueryHeader& header, const uint32_t connection_key) - : header_(header), connection_key_(connection_key) { -} + : header_(header), connection_key_(connection_key) {} -bool SecurityQuery::SerializeQuery(const uint8_t *const raw_data, - const size_t raw_data_size) { +bool SecurityQuery::SerializeQuery(const uint8_t* const raw_data, + const size_t raw_data_size) { const size_t header_size = sizeof(QueryHeader); if (raw_data_size < header_size || !raw_data) { return false; @@ -74,21 +76,21 @@ bool SecurityQuery::SerializeQuery(const uint8_t *const raw_data, const uint8_t query_type = raw_data[0]; switch (query_type) { case REQUEST: - header_.query_type = REQUEST; + header_.query_type = REQUEST; break; case RESPONSE: - header_.query_type = RESPONSE; + header_.query_type = RESPONSE; break; case NOTIFICATION: - header_.query_type = NOTIFICATION; + header_.query_type = NOTIFICATION; break; default: header_.query_type = INVALID_QUERY_TYPE; break; } // Convert to Little-Endian and clean high byte - const uint32_t query_id = 0x00FFFFFF & - BE_TO_LE32(*reinterpret_cast<const uint32_t*>(raw_data)); + const uint32_t query_id = + 0x00FFFFFF & BE_TO_LE32(*reinterpret_cast<const uint32_t*>(raw_data)); switch (query_id) { case SEND_HANDSHAKE_DATA: header_.query_id = SEND_HANDSHAKE_DATA; @@ -100,22 +102,25 @@ bool SecurityQuery::SerializeQuery(const uint8_t *const raw_data, header_.query_id = INVALID_QUERY_ID; break; } - header_.seq_number = BE_TO_LE32(*reinterpret_cast<const uint32_t*>(raw_data + 4)); - header_.json_size = BE_TO_LE32(*reinterpret_cast<const uint32_t*>(raw_data + 8)); + header_.seq_number = + BE_TO_LE32(*reinterpret_cast<const uint32_t*>(raw_data + 4)); + header_.json_size = + BE_TO_LE32(*reinterpret_cast<const uint32_t*>(raw_data + 8)); if (header_.json_size > raw_data_size - header_size) return false; if (header_.json_size > 0) { - const char *const json_data = + const char* const json_data = reinterpret_cast<const char*>(raw_data + header_size); json_message_.assign(json_data, json_data + header_.json_size); } - const uint32_t bin_data_size = raw_data_size - (header_size + header_.json_size); + const uint32_t bin_data_size = + raw_data_size - (header_size + header_.json_size); if (bin_data_size > 0) { - const char *const bin_data = - reinterpret_cast<const char*>(raw_data + header_size + header_.json_size); + const char* const bin_data = reinterpret_cast<const char*>( + raw_data + header_size + header_.json_size); data_.assign(bin_data, bin_data + bin_data_size); } return true; @@ -124,31 +129,33 @@ bool SecurityQuery::SerializeQuery(const uint8_t *const raw_data, const std::vector<uint8_t> SecurityQuery::DeserializeQuery() const { SecurityQuery::QueryHeader deserialize_header(header_); const uint32_t tmp = deserialize_header.query_id << 8; - deserialize_header.query_id = LE_TO_BE32(tmp); + deserialize_header.query_id = LE_TO_BE32(tmp); deserialize_header.seq_number = LE_TO_BE32(deserialize_header.seq_number); - deserialize_header.json_size = LE_TO_BE32(deserialize_header.json_size); + deserialize_header.json_size = LE_TO_BE32(deserialize_header.json_size); const size_t header_size = sizeof(deserialize_header); // vector of header and raw_data - std::vector<uint8_t> data_sending(header_size + data_.size() + json_message_.size()); + std::vector<uint8_t> data_sending(header_size + data_.size() + + json_message_.size()); // copy header memcpy(&data_sending[0], &deserialize_header, header_size); // copy binary data std::copy(data_.begin(), data_.end(), data_sending.begin() + header_size); // copy text (json) data - std::copy(json_message_.begin(), json_message_.end(), + std::copy(json_message_.begin(), + json_message_.end(), data_sending.begin() + header_size + data_.size()); return data_sending; } -void SecurityQuery::set_data(const uint8_t *const binary_data, - const size_t bin_data_size) { - DCHECK(binary_data); - DCHECK(bin_data_size); - data_.assign(binary_data, binary_data + bin_data_size); +void SecurityQuery::set_data(const uint8_t* const binary_data, + const size_t bin_data_size) { + DCHECK(binary_data); + DCHECK(bin_data_size); + data_.assign(binary_data, binary_data + bin_data_size); } -void SecurityQuery::set_json_message(const std::string &json_message) { +void SecurityQuery::set_json_message(const std::string& json_message) { json_message_ = json_message; } @@ -156,11 +163,11 @@ void SecurityQuery::set_connection_key(const uint32_t connection_key) { connection_key_ = connection_key; } -void SecurityQuery::set_header(const SecurityQuery::QueryHeader &header) { +void SecurityQuery::set_header(const SecurityQuery::QueryHeader& header) { header_ = header; } -const SecurityQuery::QueryHeader &SecurityQuery::get_header() const { +const SecurityQuery::QueryHeader& SecurityQuery::get_header() const { return header_; } @@ -172,7 +179,7 @@ size_t SecurityQuery::get_data_size() const { return data_.size(); } -const std::string &SecurityQuery::get_json_message() const { +const std::string& SecurityQuery::get_json_message() const { return json_message_; } diff --git a/src/components/security_manager/src/ssl_context_impl.cc b/src/components/security_manager/src/ssl_context_impl.cc index 09c2efd196..6f53234867 100644 --- a/src/components/security_manager/src/ssl_context_impl.cc +++ b/src/components/security_manager/src/ssl_context_impl.cc @@ -37,22 +37,25 @@ #include <openssl/err.h> #include <memory.h> #include <map> +#include <algorithm> #include "utils/macro.h" namespace security_manager { -CryptoManagerImpl::SSLContextImpl::SSLContextImpl(SSL *conn, Mode mode) - : connection_(conn), - bioIn_(BIO_new(BIO_s_mem())), - bioOut_(BIO_new(BIO_s_mem())), - bioFilter_(NULL), - // TODO(EZamakhov): get MTU by parameter (from transport) - // default buffer size is TCP MTU - buffer_size_(1500), - buffer_(new uint8_t[buffer_size_]), - is_handshake_pending_(false), - mode_(mode) { +CREATE_LOGGERPTR_GLOBAL(logger_, "SecurityManager") + +CryptoManagerImpl::SSLContextImpl::SSLContextImpl(SSL* conn, + Mode mode, + size_t maximum_payload_size) + : connection_(conn) + , bioIn_(BIO_new(BIO_s_mem())) + , bioOut_(BIO_new(BIO_s_mem())) + , bioFilter_(NULL) + , buffer_size_(maximum_payload_size) + , buffer_(new uint8_t[buffer_size_]) + , is_handshake_pending_(false) + , mode_(mode) { SSL_set_bio(connection_, bioIn_, bioOut_); } @@ -60,7 +63,7 @@ std::string CryptoManagerImpl::SSLContextImpl::LastError() const { if (!IsInitCompleted()) { return std::string("Initialization is not completed"); } - const char *reason = ERR_reason_error_string(ERR_get_error()); + const char* reason = ERR_reason_error_string(ERR_get_error()); return std::string(reason ? reason : ""); } @@ -69,137 +72,273 @@ bool CryptoManagerImpl::SSLContextImpl::IsInitCompleted() const { return SSL_is_init_finished(connection_); } -SSLContext::HandshakeResult CryptoManagerImpl::SSLContextImpl:: -StartHandshake(const uint8_t** const out_data, size_t *out_data_size) { +SSLContext::HandshakeResult CryptoManagerImpl::SSLContextImpl::StartHandshake( + const uint8_t** const out_data, size_t* out_data_size) { is_handshake_pending_ = true; return DoHandshakeStep(NULL, 0, out_data, out_data_size); } namespace { - size_t aes128_gcm_sha256_max_block_size(size_t mtu) { - if (mtu < 29) - return 0; - return mtu - 29; - } - size_t rc4_md5_max_block_size(size_t mtu) { - if (mtu < 21) - return 0; - return mtu - 21; - } - size_t rc4_sha_max_block_size(size_t mtu) { - if (mtu < 25) - return 0; - return mtu - 25; - } - size_t seed_sha_max_block_size(size_t mtu) { - if (mtu < 53) - return 0; - return ((mtu - 37) & 0xfffffff0) - 5; - } - size_t aes128_sha256_max_block_size(size_t mtu) { - if (mtu < 69) - return 0; - return ((mtu - 53) & 0xfffffff0) - 1; - } - size_t des_cbc3_sha_max_block_size(size_t mtu) { - if (mtu < 37) - return 0; - return ((mtu - 29) & 0xfffffff8) - 5; - } +size_t aes128_gcm_sha256_max_block_size(size_t mtu) { + if (mtu < 29) + return 0; + return mtu - 29; +} +size_t rc4_md5_max_block_size(size_t mtu) { + if (mtu < 21) + return 0; + return mtu - 21; +} +size_t rc4_sha_max_block_size(size_t mtu) { + if (mtu < 25) + return 0; + return mtu - 25; +} +size_t seed_sha_max_block_size(size_t mtu) { + if (mtu < 53) + return 0; + return ((mtu - 37) & 0xfffffff0) - 5; +} +size_t aes128_sha256_max_block_size(size_t mtu) { + if (mtu < 69) + return 0; + return ((mtu - 53) & 0xfffffff0) - 1; +} +size_t des_cbc3_sha_max_block_size(size_t mtu) { + if (mtu < 37) + return 0; + return ((mtu - 29) & 0xfffffff8) - 5; +} } // namespace std::map<std::string, CryptoManagerImpl::SSLContextImpl::BlockSizeGetter> CryptoManagerImpl::SSLContextImpl::create_max_block_sizes() { std::map<std::string, CryptoManagerImpl::SSLContextImpl::BlockSizeGetter> rc; - rc.insert(std::make_pair("AES128-GCM-SHA256", aes128_gcm_sha256_max_block_size)); - rc.insert(std::make_pair("AES128-SHA256", aes128_sha256_max_block_size)); - rc.insert(std::make_pair("AES128-SHA", seed_sha_max_block_size)); - rc.insert(std::make_pair("AES256-GCM-SHA384", aes128_gcm_sha256_max_block_size)); - rc.insert(std::make_pair("AES256-SHA256", aes128_sha256_max_block_size)); - rc.insert(std::make_pair("AES256-SHA", seed_sha_max_block_size)); - rc.insert(std::make_pair("CAMELLIA128-SHA", seed_sha_max_block_size)); - rc.insert(std::make_pair("CAMELLIA256-SHA", seed_sha_max_block_size)); - rc.insert(std::make_pair("DES-CBC3-SHA", des_cbc3_sha_max_block_size)); - rc.insert(std::make_pair("DES-CBC-SHA", des_cbc3_sha_max_block_size)); - rc.insert(std::make_pair("RC4-MD5", rc4_md5_max_block_size)); - rc.insert(std::make_pair("RC4-SHA", rc4_sha_max_block_size)); - rc.insert(std::make_pair("SEED-SHA", seed_sha_max_block_size)); + rc.insert( + std::make_pair("AES128-GCM-SHA256", aes128_gcm_sha256_max_block_size)); + rc.insert(std::make_pair("AES128-SHA256", aes128_sha256_max_block_size)); + rc.insert(std::make_pair("AES128-SHA", seed_sha_max_block_size)); + rc.insert( + std::make_pair("AES256-GCM-SHA384", aes128_gcm_sha256_max_block_size)); + rc.insert(std::make_pair("AES256-SHA256", aes128_sha256_max_block_size)); + rc.insert(std::make_pair("AES256-SHA", seed_sha_max_block_size)); + rc.insert(std::make_pair("CAMELLIA128-SHA", seed_sha_max_block_size)); + rc.insert(std::make_pair("CAMELLIA256-SHA", seed_sha_max_block_size)); + rc.insert(std::make_pair("DES-CBC3-SHA", des_cbc3_sha_max_block_size)); + rc.insert(std::make_pair("DES-CBC-SHA", des_cbc3_sha_max_block_size)); + rc.insert(std::make_pair("RC4-MD5", rc4_md5_max_block_size)); + rc.insert(std::make_pair("RC4-SHA", rc4_sha_max_block_size)); + rc.insert(std::make_pair("SEED-SHA", seed_sha_max_block_size)); return rc; } std::map<std::string, CryptoManagerImpl::SSLContextImpl::BlockSizeGetter> -CryptoManagerImpl::SSLContextImpl::max_block_sizes = - CryptoManagerImpl::SSLContextImpl::create_max_block_sizes(); + CryptoManagerImpl::SSLContextImpl::max_block_sizes = + CryptoManagerImpl::SSLContextImpl::create_max_block_sizes(); + +void CryptoManagerImpl::SSLContextImpl::PrintCertData( + X509* cert, const std::string& cert_owner) { + if (cert) { + X509_NAME* subj_name = X509_get_subject_name(cert); + char* subj = X509_NAME_oneline(subj_name, NULL, 0); + if (subj) { + std::replace(subj, subj + strlen(subj), '/', ' '); + LOG4CXX_DEBUG(logger_, cert_owner << " subject:" << subj); + OPENSSL_free(subj); + } + char* issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); + if (issuer) { + std::replace(issuer, issuer + strlen(issuer), '/', ' '); + LOG4CXX_DEBUG(logger_, cert_owner << " issuer:" << issuer); + OPENSSL_free(issuer); + } -SSLContext::HandshakeResult CryptoManagerImpl::SSLContextImpl:: -DoHandshakeStep(const uint8_t* const in_data, size_t in_data_size, - const uint8_t** const out_data, size_t* out_data_size) { - DCHECK(out_data); - DCHECK(out_data_size); - *out_data = NULL; - *out_data_size = 0; - // TODO(Ezamakhov): add test - hanshake fail -> restart StartHandshake - sync_primitives::AutoLock locker(bio_locker); - if (SSL_is_init_finished(connection_)) { - is_handshake_pending_ = false; - return SSLContext::Handshake_Result_Success; + ASN1_TIME* notBefore = X509_get_notBefore(cert); + ASN1_TIME* notAfter = X509_get_notAfter(cert); + + if (notBefore) { + LOG4CXX_DEBUG(logger_, " Start date: " << (char*)notBefore->data); + } + if (notAfter) { + LOG4CXX_DEBUG(logger_, " End date: " << (char*)notAfter->data); + } } +} + +void CryptoManagerImpl::SSLContextImpl::PrintCertInfo() { + PrintCertData(SSL_get_certificate(connection_), "HU's"); + + STACK_OF(X509)* peer_certs = SSL_get_peer_cert_chain(connection_); + while (sk_X509_num(peer_certs) > 0) { + X509* cert = sk_X509_pop(peer_certs); + PrintCertData(cert, "SERVERS"); + } +} + +SSLContext::HandshakeResult +CryptoManagerImpl::SSLContextImpl::CheckCertContext() { + X509* cert = SSL_get_peer_certificate(connection_); + if (!cert) { + // According to the openssl documentation the peer certificate + // might be ommitted for the SERVER but required for the cient. + return CLIENT == mode_ ? Handshake_Result_Fail : Handshake_Result_Success; + } + + X509_NAME* subj_name = X509_get_subject_name(cert); + + const std::string& cn = GetTextBy(subj_name, NID_commonName); + const std::string& sn = GetTextBy(subj_name, NID_serialNumber); + + if (!(hsh_context_.expected_cn.CompareIgnoreCase(cn.c_str()))) { + LOG4CXX_ERROR(logger_, + "Trying to run handshake with wrong app name: " + << cn << ". Expected app name: " + << hsh_context_.expected_cn.AsMBString()); + return Handshake_Result_AppNameMismatch; + } + + if (!(hsh_context_.expected_sn.CompareIgnoreCase(sn.c_str()))) { + LOG4CXX_ERROR(logger_, + "Trying to run handshake with wrong app id: " + << sn << ". Expected app id: " + << hsh_context_.expected_sn.AsMBString()); + return Handshake_Result_AppIDMismatch; + } + return Handshake_Result_Success; +} + +bool CryptoManagerImpl::SSLContextImpl::ReadHandshakeData( + const uint8_t** const out_data, size_t* out_data_size) { + LOG4CXX_AUTO_TRACE(logger_); + const size_t pend = BIO_ctrl_pending(bioOut_); + LOG4CXX_DEBUG(logger_, "Available " << pend << " bytes for handshake"); + + if (pend > 0) { + LOG4CXX_DEBUG(logger_, "Reading handshake data"); + EnsureBufferSizeEnough(pend); + + const int read_count = BIO_read(bioOut_, buffer_, pend); + if (read_count == static_cast<int>(pend)) { + *out_data_size = read_count; + *out_data = buffer_; + } else { + LOG4CXX_WARN(logger_, "BIO read fail"); + is_handshake_pending_ = false; + ResetConnection(); + return false; + } + } + + return true; +} +bool CryptoManagerImpl::SSLContextImpl::WriteHandshakeData( + const uint8_t* const in_data, size_t in_data_size) { + LOG4CXX_AUTO_TRACE(logger_); if (in_data && in_data_size) { const int ret = BIO_write(bioIn_, in_data, in_data_size); if (ret <= 0) { is_handshake_pending_ = false; - SSL_clear(connection_); - return SSLContext::Handshake_Result_AbnormalFail; + ResetConnection(); + return Handshake_Result_AbnormalFail; } } + return true; +} +SSLContext::HandshakeResult +CryptoManagerImpl::SSLContextImpl::PerformHandshake() { const int handshake_result = SSL_do_handshake(connection_); if (handshake_result == 1) { + const HandshakeResult result = CheckCertContext(); + if (result != Handshake_Result_Success) { + ResetConnection(); + is_handshake_pending_ = false; + return result; + } + + LOG4CXX_DEBUG(logger_, "SSL handshake successfully finished"); // Handshake is successful bioFilter_ = BIO_new(BIO_f_ssl()); BIO_set_ssl(bioFilter_, connection_, BIO_NOCLOSE); - const SSL_CIPHER *cipher = SSL_get_current_cipher(connection_); + const SSL_CIPHER* cipher = SSL_get_current_cipher(connection_); max_block_size_ = max_block_sizes[SSL_CIPHER_get_name(cipher)]; is_handshake_pending_ = false; + } else if (handshake_result == 0) { SSL_clear(connection_); is_handshake_pending_ = false; - return SSLContext::Handshake_Result_Fail; - } else if (SSL_get_error(connection_, handshake_result) != SSL_ERROR_WANT_READ) { - SSL_clear(connection_); - is_handshake_pending_ = false; - return SSLContext::Handshake_Result_AbnormalFail; + return Handshake_Result_Fail; + } else { + const int error = SSL_get_error(connection_, handshake_result); + if (error != SSL_ERROR_WANT_READ) { + const long error = SSL_get_verify_result(connection_); + SetHandshakeError(error); + LOG4CXX_WARN(logger_, + "Handshake failed with error " + << " -> " << SSL_get_error(connection_, error) << " \"" + << LastError() << '"'); + ResetConnection(); + is_handshake_pending_ = false; + + // In case error happened but ssl verification shows OK + // method will return AbnormalFail. + if (X509_V_OK == error) { + return Handshake_Result_AbnormalFail; + } + return openssl_error_convert_to_internal(error); + } } + return Handshake_Result_Success; +} - const size_t pend = BIO_ctrl_pending(bioOut_); +SSLContext::HandshakeResult CryptoManagerImpl::SSLContextImpl::DoHandshakeStep( + const uint8_t* const in_data, + size_t in_data_size, + const uint8_t** const out_data, + size_t* out_data_size) { + LOG4CXX_AUTO_TRACE(logger_); + DCHECK(out_data); + DCHECK(out_data_size); + *out_data = NULL; + *out_data_size = 0; - if (pend) { - EnsureBufferSizeEnough(pend); + // TODO(Ezamakhov): add test - hanshake fail -> restart StartHandshake + { + sync_primitives::AutoLock locker(bio_locker); - const int read_count = BIO_read(bioOut_, buffer_, pend); - if (read_count == static_cast<int>(pend)) { - *out_data_size = read_count; - *out_data = buffer_; - } else { + if (SSL_is_init_finished(connection_)) { + LOG4CXX_DEBUG(logger_, "SSL initilization is finished"); is_handshake_pending_ = false; - SSL_clear(connection_); - return SSLContext::Handshake_Result_AbnormalFail; + return Handshake_Result_Success; } } - return SSLContext::Handshake_Result_Success; -} + if (!WriteHandshakeData(in_data, in_data_size)) { + return Handshake_Result_AbnormalFail; + } -bool CryptoManagerImpl::SSLContextImpl::Encrypt( - const uint8_t * const in_data, size_t in_data_size, - const uint8_t ** const out_data, size_t *out_data_size) { + PrintCertInfo(); + const HandshakeResult res = PerformHandshake(); + if (res != Handshake_Result_Success) { + return res; + } + + if (!ReadHandshakeData(out_data, out_data_size)) { + return Handshake_Result_AbnormalFail; + } + + return res; +} + +bool CryptoManagerImpl::SSLContextImpl::Encrypt(const uint8_t* const in_data, + size_t in_data_size, + const uint8_t** const out_data, + size_t* out_data_size) { sync_primitives::AutoLock locker(bio_locker); - if (!SSL_is_init_finished(connection_) || - !in_data || - !in_data_size) { + if (!SSL_is_init_finished(connection_) || !in_data || !in_data_size) { return false; } @@ -220,10 +359,10 @@ bool CryptoManagerImpl::SSLContextImpl::Encrypt( return true; } -bool CryptoManagerImpl::SSLContextImpl::Decrypt( - const uint8_t * const in_data, size_t in_data_size, - const uint8_t ** const out_data, size_t *out_data_size) { - +bool CryptoManagerImpl::SSLContextImpl::Decrypt(const uint8_t* const in_data, + size_t in_data_size, + const uint8_t** const out_data, + size_t* out_data_size) { sync_primitives::AutoLock locker(bio_locker); if (!SSL_is_init_finished(connection_)) { return false; @@ -274,14 +413,94 @@ CryptoManagerImpl::SSLContextImpl::~SSLContextImpl() { delete[] buffer_; } +void CryptoManagerImpl::SSLContextImpl::SetHandshakeError(const int error) { + const char* error_str = X509_verify_cert_error_string(error); + if (error_str) { + last_error_ = error_str; + } else { + // Error will be updated with the next LastError call + last_error_.clear(); + } +} + +void CryptoManagerImpl::SSLContextImpl::ResetConnection() { + LOG4CXX_AUTO_TRACE(logger_); + const int shutdown_result = SSL_shutdown(connection_); + if (shutdown_result != 1) { + const size_t pend = BIO_ctrl_pending(bioOut_); + LOG4CXX_DEBUG(logger_, "Available " << pend << " bytes for shutdown"); + if (pend > 0) { + LOG4CXX_DEBUG(logger_, "Reading shutdown data"); + EnsureBufferSizeEnough(pend); + BIO_read(bioOut_, buffer_, pend); + } + SSL_shutdown(connection_); + } + LOG4CXX_DEBUG(logger_, "SSL connection recreation"); + SSL_CTX* ssl_context = connection_->ctx; + SSL_free(connection_); + connection_ = SSL_new(ssl_context); + if (mode_ == SERVER) { + SSL_set_accept_state(connection_); + } else { + SSL_set_connect_state(connection_); + } + bioIn_ = BIO_new(BIO_s_mem()); + bioOut_ = BIO_new(BIO_s_mem()); + SSL_set_bio(connection_, bioIn_, bioOut_); +} + +void CryptoManagerImpl::SSLContextImpl::SetHandshakeContext( + const SSLContext::HandshakeContext& hsh_ctx) { + hsh_context_ = hsh_ctx; +} + void CryptoManagerImpl::SSLContextImpl::EnsureBufferSizeEnough(size_t size) { if (buffer_size_ < size) { delete[] buffer_; - buffer_ = new(std::nothrow) uint8_t[size]; + buffer_ = new (std::nothrow) uint8_t[size]; if (buffer_) { buffer_size_ = size; } } } +SSLContext::HandshakeResult +CryptoManagerImpl::SSLContextImpl::openssl_error_convert_to_internal( + const long error) { + switch (error) { + case X509_V_ERR_CERT_HAS_EXPIRED: + return Handshake_Result_CertExpired; + case X509_V_ERR_CERT_NOT_YET_VALID: + return Handshake_Result_NotYetValid; + case X509_V_ERR_SUBJECT_ISSUER_MISMATCH: + case X509_V_ERR_CERT_SIGNATURE_FAILURE: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + return Handshake_Result_CertNotSigned; + default: + return Handshake_Result_Fail; + } +} + +std::string CryptoManagerImpl::SSLContextImpl::GetTextBy(X509_NAME* name, + int object) const { + const int req_len = X509_NAME_get_text_by_NID(name, object, NULL, 0); + + if (-1 == req_len) { + LOG4CXX_WARN(logger_, + "Unable to obtain object: " << object << " from certificate"); + return std::string(); + } + + std::vector<char> data; + data.resize(req_len + 1); + X509_NAME_get_text_by_NID(name, object, &data.front(), data.size()); + + std::string str(data.begin(), data.end() - 1); + + std::transform(str.begin(), str.end(), str.begin(), ::tolower); + return str; +} + } // namespace security_manager |