diff options
author | Andriy Byzhynar <AByzhynar@luxoft.com> | 2018-02-23 17:54:43 +0200 |
---|---|---|
committer | Andriy Byzhynar <AByzhynar@luxoft.com> | 2018-04-06 11:18:31 +0300 |
commit | 546cd986780759dd5eee64998f99bc66099a3b32 (patch) | |
tree | cb769a5a625b29ed1e8982c8543594e826e8d31c /src/components/security_manager/src | |
parent | 3ba8a85c7fa6b9f7d2c9dca3e102168b3df6a00a (diff) | |
download | sdl_core-546cd986780759dd5eee64998f99bc66099a3b32.tar.gz |
Implement fully functional GetSystemTime feature
Implemented fully working GetSystemTime feature
Fixed UT in the security manager due to code changes
Disable randomly failed test
Diffstat (limited to 'src/components/security_manager/src')
3 files changed, 272 insertions, 66 deletions
diff --git a/src/components/security_manager/src/crypto_manager_impl.cc b/src/components/security_manager/src/crypto_manager_impl.cc index 6bee28a976..8db1d633a7 100644 --- a/src/components/security_manager/src/crypto_manager_impl.cc +++ b/src/components/security_manager/src/crypto_manager_impl.cc @@ -63,7 +63,12 @@ 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); + if (error == X509_V_ERR_CERT_NOT_YET_VALID || + error == X509_V_ERR_CERT_HAS_EXPIRED) { + // return success result code instead of error because start + // and expiration cert dates will be checked by SDL + return 1; + } LOG4CXX_WARN(logger_, "Certificate verification failed with error " << error << " \"" << X509_verify_cert_error_string(error) @@ -264,24 +269,17 @@ std::string CryptoManagerImpl::LastError() const { return std::string(reason ? reason : ""); } -bool CryptoManagerImpl::IsCertificateUpdateRequired() const { +bool CryptoManagerImpl::IsCertificateUpdateRequired( + const time_t system_time, const time_t certificates_time) const { LOG4CXX_AUTO_TRACE(logger_); - const time_t cert_date = mktime(&expiration_time_); + const double seconds = difftime(certificates_time, system_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 UTC time: " << asctime(gmtime(&certificates_time))); - LOG4CXX_DEBUG(logger_, - "Certificate expiration time: " << asctime(&expiration_time_)); - LOG4CXX_DEBUG(logger_, - "Host time: " << asctime(localtime(&now)) - << ". Seconds before expiration: " << seconds); + LOG4CXX_DEBUG(logger_, "Host UTC time: " << asctime(gmtime(&system_time))); + LOG4CXX_DEBUG(logger_, "Seconds before expiration: " << seconds); if (seconds < 0) { LOG4CXX_WARN(logger_, "Certificate is already expired."); return true; @@ -331,8 +329,6 @@ bool CryptoManagerImpl::set_certificate(const std::string& cert_data) { return false; } - asn1_time_to_tm(X509_get_notAfter(cert)); - if (!SSL_CTX_use_PrivateKey(context_, pkey)) { LOG4CXX_ERROR(logger_, "Could not use key: " << LastError()); return false; @@ -359,7 +355,8 @@ bool CryptoManagerImpl::set_certificate(const std::string& cert_data) { return true; } -int CryptoManagerImpl::pull_number_from_buf(char* buf, int* idx) { +int CryptoManagerImpl::SSLContextImpl::get_number_from_char_buf( + char* buf, int* idx) const { if (!idx) { return 0; } @@ -368,40 +365,6 @@ int CryptoManagerImpl::pull_number_from_buf(char* buf, int* idx) { 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; - } -} - void CryptoManagerImpl::InitCertExpTime() { strptime("1 Jan 1970 00:00:00", "%d %b %Y %H:%M:%S", &expiration_time_); } diff --git a/src/components/security_manager/src/security_manager_impl.cc b/src/components/security_manager/src/security_manager_impl.cc index 1853b218b4..d68e6b456e 100644 --- a/src/components/security_manager/src/security_manager_impl.cc +++ b/src/components/security_manager/src/security_manager_impl.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, Ford Motor Company + * Copyright (c) 2018, Ford Motor Company * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -31,11 +31,13 @@ */ #include "security_manager/security_manager_impl.h" +#include <functional> #include "security_manager/crypto_manager_impl.h" #include "protocol_handler/protocol_packet.h" #include "utils/logger.h" #include "utils/byte_order.h" #include "json/json.h" +#include "utils/helpers.h" namespace security_manager { @@ -44,11 +46,22 @@ CREATE_LOGGERPTR_GLOBAL(logger_, "SecurityManager") static const char* kErrId = "id"; static const char* kErrText = "text"; -SecurityManagerImpl::SecurityManagerImpl() +SecurityManagerImpl::SecurityManagerImpl( + std::unique_ptr<utils::SystemTimeHandler>&& system_time_handler) : security_messages_("SecurityManager", this) , session_observer_(NULL) , crypto_manager_(NULL) - , protocol_handler_(NULL) {} + , protocol_handler_(NULL) + , system_time_handler_(std::move(system_time_handler)) + , waiting_for_certificate_(false) + , waiting_for_time_(false) { + DCHECK(system_time_handler_); + system_time_handler_->SubscribeOnSystemTime(this); +} + +SecurityManagerImpl::~SecurityManagerImpl() { + system_time_handler_->UnSubscribeFromSystemTime(this); +} void SecurityManagerImpl::OnMessageReceived( const ::protocol_handler::RawMessagePtr message) { @@ -136,16 +149,20 @@ void SecurityManagerImpl::Handle(const SecurityMessage message) { } security_manager::SSLContext* SecurityManagerImpl::CreateSSLContext( - const uint32_t& connection_key) { + const uint32_t& connection_key, ContextCreationStrategy cc_strategy) { LOG4CXX_INFO(logger_, "ProtectService processing"); DCHECK(session_observer_); DCHECK(crypto_manager_); - 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; + security_manager::SSLContext* ssl_context = NULL; + if (kUseExisting == cc_strategy) { + security_manager::SSLContext* ssl_context = + session_observer_->GetSSLContext(connection_key, + protocol_handler::kControl); + // If SSLContext for current connection/session exists - return it + if (ssl_context) { + return ssl_context; + } } ssl_context = crypto_manager_->CreateSSLContext(); @@ -172,6 +189,40 @@ security_manager::SSLContext* SecurityManagerImpl::CreateSSLContext( return ssl_context; } +void SecurityManagerImpl::PostponeHandshake(const uint32_t connection_key) { + LOG4CXX_TRACE(logger_, "Handshake postponed"); + sync_primitives::AutoLock lock(connections_lock_); + if (waiting_for_certificate_) { + awaiting_certificate_connections_.insert(connection_key); + } + if (waiting_for_time_) { + awaiting_time_connections_.insert(connection_key); + } +} + +void SecurityManagerImpl::ResumeHandshake(uint32_t connection_key) { + LOG4CXX_TRACE(logger_, "Handshake resumed"); + + security_manager::SSLContext* ssl_context = + CreateSSLContext(connection_key, kForceRecreation); + + if (!ssl_context) { + LOG4CXX_WARN(logger_, + "Unable to resume handshake. No SSL context for key " + << connection_key); + return; + } + + ssl_context->ResetConnection(); + if (!ssl_context->HasCertificate()) { + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_Fail); + return; + } + + ProceedHandshake(ssl_context, connection_key); +} + void SecurityManagerImpl::StartHandshake(uint32_t connection_key) { DCHECK(session_observer_); LOG4CXX_INFO(logger_, "StartHandshake: connection_key " << connection_key); @@ -187,6 +238,35 @@ void SecurityManagerImpl::StartHandshake(uint32_t connection_key) { SSLContext::Handshake_Result_Fail); return; } + if (!ssl_context->HasCertificate()) { + LOG4CXX_ERROR(logger_, "Security certificate is absent"); + sync_primitives::AutoLock lock(waiters_lock_); + waiting_for_certificate_ = true; + NotifyOnCertififcateUpdateRequired(); + } + + { + sync_primitives::AutoLock lock(waiters_lock_); + waiting_for_time_ = true; + } + + PostponeHandshake(connection_key); + system_time_handler_->QuerySystemTime(); +} + +bool SecurityManagerImpl::IsSystemTimeProviderReady() const { + return system_time_handler_->system_time_can_be_received(); +} + +void SecurityManagerImpl::ProceedHandshake( + security_manager::SSLContext* ssl_context, uint32_t connection_key) { + LOG4CXX_AUTO_TRACE(logger_); + if (!ssl_context) { + LOG4CXX_WARN(logger_, + "Unable to process handshake. No SSL context for key " + << connection_key); + return; + } if (ssl_context->IsInitCompleted()) { NotifyListenersOnHandshakeDone(connection_key, @@ -194,8 +274,33 @@ void SecurityManagerImpl::StartHandshake(uint32_t connection_key) { return; } - ssl_context->SetHandshakeContext( - session_observer_->GetHandshakeContext(connection_key)); + time_t cert_due_date; + if (!ssl_context->GetCertificateDueDate(cert_due_date)) { + LOG4CXX_ERROR(logger_, "Failed to get certificate due date!"); + return; + } + + if (crypto_manager_->IsCertificateUpdateRequired( + system_time_handler_->GetUTCTime(), cert_due_date)) { + LOG4CXX_DEBUG(logger_, "Host certificate update required"); + if (helpers::in_range(awaiting_certificate_connections_, connection_key)) { + NotifyListenersOnHandshakeDone(connection_key, + SSLContext::Handshake_Result_CertExpired); + return; + } + { + sync_primitives::AutoLock lock(waiters_lock_); + waiting_for_certificate_ = true; + } + PostponeHandshake(connection_key); + NotifyOnCertififcateUpdateRequired(); + return; + } + + SSLContext::HandshakeContext handshake_context = + session_observer_->GetHandshakeContext(connection_key); + handshake_context.system_time = system_time_handler_->GetUTCTime(); + ssl_context->SetHandshakeContext(handshake_context); size_t data_size = 0; const uint8_t* data = NULL; @@ -216,9 +321,21 @@ void SecurityManagerImpl::StartHandshake(uint32_t connection_key) { } } -bool SecurityManagerImpl::IsCertificateUpdateRequired() { +bool SecurityManagerImpl::IsCertificateUpdateRequired( + const uint32_t connection_key) { LOG4CXX_AUTO_TRACE(logger_); - return crypto_manager_->IsCertificateUpdateRequired(); + security_manager::SSLContext* ssl_context = + CreateSSLContext(connection_key, kUseExisting); + DCHECK_OR_RETURN(ssl_context, true); + LOG4CXX_DEBUG(logger_, + "Set SSL context to connection_key " << connection_key); + time_t cert_due_date; + if (!ssl_context->GetCertificateDueDate(cert_due_date)) { + LOG4CXX_ERROR(logger_, "Failed to get certificate due date!"); + return true; + } + return crypto_manager_->IsCertificateUpdateRequired( + system_time_handler_->GetUTCTime(), cert_due_date); } void SecurityManagerImpl::AddListener(SecurityManagerListener* const listener) { @@ -227,7 +344,6 @@ void SecurityManagerImpl::AddListener(SecurityManagerListener* const listener) { "Invalid (NULL) pointer to SecurityManagerListener."); return; } - LOG4CXX_DEBUG(logger_, "Adding listener " << listener); listeners_.push_back(listener); } @@ -241,6 +357,37 @@ void SecurityManagerImpl::RemoveListener( listeners_.remove(listener); } +bool SecurityManagerImpl::OnCertificateUpdated(const std::string& data) { + LOG4CXX_AUTO_TRACE(logger_); + { + sync_primitives::AutoLock lock(waiters_lock_); + waiting_for_certificate_ = false; + } + crypto_manager_->OnCertificateUpdated(data); + std::for_each( + awaiting_certificate_connections_.begin(), + awaiting_certificate_connections_.end(), + std::bind1st(std::mem_fun(&SecurityManagerImpl::ResumeHandshake), this)); + + std::set<uint32_t>().swap(awaiting_certificate_connections_); + return true; +} + +void SecurityManagerImpl::OnSystemTimeArrived(const time_t utc_time) { + LOG4CXX_AUTO_TRACE(logger_); + { + sync_primitives::AutoLock lock(waiters_lock_); + waiting_for_time_ = false; + } + + std::for_each( + awaiting_time_connections_.begin(), + awaiting_time_connections_.end(), + std::bind1st(std::mem_fun(&SecurityManagerImpl::ResumeHandshake), this)); + + std::set<uint32_t>().swap(awaiting_time_connections_); +} + void SecurityManagerImpl::NotifyListenersOnHandshakeDone( const uint32_t& connection_key, SSLContext::HandshakeResult error) { LOG4CXX_AUTO_TRACE(logger_); diff --git a/src/components/security_manager/src/ssl_context_impl.cc b/src/components/security_manager/src/ssl_context_impl.cc index 5be5ff8363..69e22dc44e 100644 --- a/src/components/security_manager/src/ssl_context_impl.cc +++ b/src/components/security_manager/src/ssl_context_impl.cc @@ -77,6 +77,7 @@ bool CryptoManagerImpl::SSLContextImpl::IsInitCompleted() const { SSLContext::HandshakeResult CryptoManagerImpl::SSLContextImpl::StartHandshake( const uint8_t** const out_data, size_t* out_data_size) { + LOG4CXX_AUTO_TRACE(logger_); is_handshake_pending_ = true; return DoHandshakeStep(NULL, 0, out_data, out_data_size); } @@ -174,6 +175,7 @@ const std::string CryptoManagerImpl::SSLContextImpl::RemoveDisallowedInfo( void CryptoManagerImpl::SSLContextImpl::PrintCertData( X509* cert, const std::string& cert_owner) { + LOG4CXX_AUTO_TRACE(logger_); if (!cert) { LOG4CXX_DEBUG(logger_, "Empty certificate data"); return; @@ -206,6 +208,7 @@ void CryptoManagerImpl::SSLContextImpl::PrintCertData( } void CryptoManagerImpl::SSLContextImpl::PrintCertInfo() { + LOG4CXX_AUTO_TRACE(logger_); PrintCertData(SSL_get_certificate(connection_), "HU's"); STACK_OF(X509)* peer_certs = SSL_get_peer_cert_chain(connection_); @@ -217,12 +220,42 @@ void CryptoManagerImpl::SSLContextImpl::PrintCertInfo() { SSLContext::HandshakeResult CryptoManagerImpl::SSLContextImpl::CheckCertContext() { + LOG4CXX_AUTO_TRACE(logger_); 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; } + ASN1_TIME* notBefore = X509_get_notBefore(cert); + ASN1_TIME* notAfter = X509_get_notAfter(cert); + + time_t start = convert_asn1_time_to_time_t(notBefore); + time_t end = convert_asn1_time_to_time_t(notAfter); + + const double start_seconds = difftime(hsh_context_.system_time, start); + const double end_seconds = difftime(end, hsh_context_.system_time); + + if (start_seconds < 0) { + LOG4CXX_ERROR(logger_, + "Certificate is not yet valid. Time before validity " + << start_seconds << " seconds"); + return Handshake_Result_NotYetValid; + } else { + LOG4CXX_DEBUG(logger_, + "Time since certificate validity " << start_seconds + << "seconds"); + } + + if (end_seconds < 0) { + LOG4CXX_ERROR(logger_, + "Certificate already expired. Time after expiration " + << end_seconds << " seconds"); + return Handshake_Result_CertExpired; + } else { + LOG4CXX_DEBUG(logger_, + "Time until expiration " << end_seconds << "seconds"); + } X509_NAME* subj_name = X509_get_subject_name(cert); @@ -247,6 +280,47 @@ CryptoManagerImpl::SSLContextImpl::CheckCertContext() { return Handshake_Result_Success; } +time_t CryptoManagerImpl::SSLContextImpl::convert_asn1_time_to_time_t( + ASN1_TIME* time_to_convert) const { + struct tm cert_time; + memset(&cert_time, 0, sizeof(struct tm)); + // the minimum value for day of month is 1, otherwise exception will be thrown + cert_time.tm_mday = 1; + char* buf = reinterpret_cast<char*>(time_to_convert->data); + int index = 0; + const int year = get_number_from_char_buf(buf, &index); + if (V_ASN1_GENERALIZEDTIME == time_to_convert->type) { + cert_time.tm_year = + (year * 100 - 1900) + get_number_from_char_buf(buf, &index); + } else { + cert_time.tm_year = year < 50 ? year + 100 : year; + } + + const int mon = get_number_from_char_buf(buf, &index); + const int day = get_number_from_char_buf(buf, &index); + const int hour = get_number_from_char_buf(buf, &index); + const int mn = get_number_from_char_buf(buf, &index); + + cert_time.tm_mon = mon - 1; + cert_time.tm_mday = day; + cert_time.tm_hour = hour; + cert_time.tm_min = mn; + + if (buf[index] == 'Z') { + cert_time.tm_sec = 0; + } + if ((buf[index] == '+') || (buf[index] == '-')) { + const int mn = get_number_from_char_buf(buf, &index); + const int mn1 = get_number_from_char_buf(buf, &index); + cert_time.tm_sec = (mn * 3600) + (mn1 * 60); + } else { + const int sec = get_number_from_char_buf(buf, &index); + cert_time.tm_sec = sec; + } + + return mktime(&cert_time); +} + bool CryptoManagerImpl::SSLContextImpl::ReadHandshakeData( const uint8_t** const out_data, size_t* out_data_size) { LOG4CXX_AUTO_TRACE(logger_); @@ -288,7 +362,9 @@ bool CryptoManagerImpl::SSLContextImpl::WriteHandshakeData( SSLContext::HandshakeResult CryptoManagerImpl::SSLContextImpl::PerformHandshake() { + LOG4CXX_AUTO_TRACE(logger_); const int handshake_result = SSL_do_handshake(connection_); + LOG4CXX_TRACE(logger_, "Handshake result: " << handshake_result); if (handshake_result == 1) { const HandshakeResult result = CheckCertContext(); if (result != Handshake_Result_Success) { @@ -307,6 +383,7 @@ CryptoManagerImpl::SSLContextImpl::PerformHandshake() { is_handshake_pending_ = false; } else if (handshake_result == 0) { + LOG4CXX_DEBUG(logger_, "SSL handshake failed"); SSL_clear(connection_); is_handshake_pending_ = false; return Handshake_Result_Fail; @@ -447,6 +524,25 @@ bool CryptoManagerImpl::SSLContextImpl::IsHandshakePending() const { return is_handshake_pending_; } +bool CryptoManagerImpl::SSLContextImpl::GetCertificateDueDate( + time_t& due_date) const { + LOG4CXX_AUTO_TRACE(logger_); + + X509* cert = SSL_get_certificate(connection_); + if (!cert) { + LOG4CXX_DEBUG(logger_, "Get certificate failed."); + return false; + } + + due_date = convert_asn1_time_to_time_t(X509_get_notAfter(cert)); + + return true; +} + +bool CryptoManagerImpl::SSLContextImpl::HasCertificate() const { + return SSL_get_certificate(connection_) != NULL; +} + CryptoManagerImpl::SSLContextImpl::~SSLContextImpl() { SSL_shutdown(connection_); SSL_free(connection_); |