Matter SDK Coverage Report
Current view: top level - protocols/secure_channel - CASEServer.cpp (source / functions) Coverage Total Hit
Test: SHA:b879ecb8e99e175eea0a293a888bda853da2b19c Lines: 87.3 % 71 62
Test Date: 2025-01-17 19:00:11 Functions: 87.5 % 8 7

            Line data    Source code
       1              : /*
       2              :  *
       3              :  *    Copyright (c) 2021 Project CHIP Authors
       4              :  *
       5              :  *    Licensed under the Apache License, Version 2.0 (the "License");
       6              :  *    you may not use this file except in compliance with the License.
       7              :  *    You may obtain a copy of the License at
       8              :  *
       9              :  *        http://www.apache.org/licenses/LICENSE-2.0
      10              :  *
      11              :  *    Unless required by applicable law or agreed to in writing, software
      12              :  *    distributed under the License is distributed on an "AS IS" BASIS,
      13              :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      14              :  *    See the License for the specific language governing permissions and
      15              :  *    limitations under the License.
      16              :  */
      17              : 
      18              : #include <protocols/secure_channel/CASEServer.h>
      19              : 
      20              : #include <lib/core/CHIPError.h>
      21              : #include <lib/support/CHIPFaultInjection.h>
      22              : #include <lib/support/CodeUtils.h>
      23              : #include <lib/support/SafeInt.h>
      24              : #include <lib/support/logging/CHIPLogging.h>
      25              : #include <tracing/macros.h>
      26              : #include <transport/SessionManager.h>
      27              : 
      28              : using namespace ::chip::Inet;
      29              : using namespace ::chip::Transport;
      30              : using namespace ::chip::Credentials;
      31              : 
      32              : namespace chip {
      33              : 
      34            7 : CHIP_ERROR CASEServer::ListenForSessionEstablishment(Messaging::ExchangeManager * exchangeManager, SessionManager * sessionManager,
      35              :                                                      FabricTable * fabrics, SessionResumptionStorage * sessionResumptionStorage,
      36              :                                                      Credentials::CertificateValidityPolicy * certificateValidityPolicy,
      37              :                                                      Credentials::GroupDataProvider * responderGroupDataProvider)
      38              : {
      39            7 :     VerifyOrReturnError(exchangeManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      40            7 :     VerifyOrReturnError(sessionManager != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      41            7 :     VerifyOrReturnError(responderGroupDataProvider != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      42              : 
      43            7 :     mSessionManager            = sessionManager;
      44            7 :     mSessionResumptionStorage  = sessionResumptionStorage;
      45            7 :     mCertificateValidityPolicy = certificateValidityPolicy;
      46            7 :     mFabrics                   = fabrics;
      47            7 :     mExchangeManager           = exchangeManager;
      48            7 :     mGroupDataProvider         = responderGroupDataProvider;
      49              : 
      50              :     // Set up the group state provider that persists across all handshakes.
      51            7 :     GetSession().SetGroupDataProvider(mGroupDataProvider);
      52              : 
      53            7 :     ChipLogProgress(Inet, "CASE Server enabling CASE session setups");
      54            7 :     mExchangeManager->RegisterUnsolicitedMessageHandlerForType(Protocols::SecureChannel::MsgType::CASE_Sigma1, this);
      55              : 
      56            7 :     PrepareForSessionEstablishment();
      57              : 
      58            7 :     return CHIP_NO_ERROR;
      59              : }
      60              : 
      61            7 : CHIP_ERROR CASEServer::InitCASEHandshake(Messaging::ExchangeContext * ec)
      62              : {
      63              :     MATTER_TRACE_SCOPE("InitCASEHandshake", "CASEServer");
      64            7 :     VerifyOrReturnError(ec != nullptr, CHIP_ERROR_INVALID_ARGUMENT);
      65              : 
      66              :     // Hand over the exchange context to the CASE session.
      67            7 :     ec->SetDelegate(&GetSession());
      68              : 
      69            7 :     return CHIP_NO_ERROR;
      70              : }
      71              : 
      72            8 : CHIP_ERROR CASEServer::OnUnsolicitedMessageReceived(const PayloadHeader & payloadHeader, ExchangeDelegate *& newDelegate)
      73              : {
      74              :     // TODO: assign newDelegate to CASESession, let CASESession handle future messages.
      75            8 :     newDelegate = this;
      76            8 :     return CHIP_NO_ERROR;
      77              : }
      78              : 
      79            8 : CHIP_ERROR CASEServer::OnMessageReceived(Messaging::ExchangeContext * ec, const PayloadHeader & payloadHeader,
      80              :                                          System::PacketBufferHandle && payload)
      81              : {
      82              :     MATTER_TRACE_SCOPE("OnMessageReceived", "CASEServer");
      83              : 
      84            8 :     bool busy = GetSession().GetState() != CASESession::State::kInitialized;
      85            8 :     CHIP_FAULT_INJECT(FaultInjection::kFault_CASEServerBusy, busy = true);
      86            8 :     if (busy)
      87              :     {
      88              :         // We are in the middle of CASE handshake
      89              : 
      90              :         // Invoke watchdog to fix any stuck handshakes
      91            1 :         bool watchdogFired = GetSession().InvokeBackgroundWorkWatchdog();
      92            1 :         if (!watchdogFired)
      93              :         {
      94              :             // Handshake wasn't stuck, send the busy status report and let the existing handshake continue.
      95              : 
      96              :             // A successful CASE handshake can take several seconds and some may time out (30 seconds or more).
      97              : 
      98            1 :             System::Clock::Milliseconds16 delay = System::Clock::kZero;
      99            1 :             if (GetSession().GetState() == CASESession::State::kSentSigma2)
     100              :             {
     101              :                 // The delay should be however long we think it will take for
     102              :                 // that to time out.
     103            1 :                 auto sigma2Timeout = CASESession::ComputeSigma2ResponseTimeout(GetSession().GetRemoteMRPConfig());
     104            1 :                 if (sigma2Timeout < System::Clock::Milliseconds16::max())
     105              :                 {
     106            1 :                     delay = std::chrono::duration_cast<System::Clock::Milliseconds16>(sigma2Timeout);
     107              :                 }
     108              :                 else
     109              :                 {
     110              :                     // Avoid overflow issues, just wait for as long as we can to
     111              :                     // get close to our expected Sigma2 timeout.
     112            0 :                     delay = System::Clock::Milliseconds16::max();
     113              :                 }
     114              :             }
     115              :             else
     116              :             {
     117              :                 // For now, setting minimum wait time to 5000 milliseconds if we
     118              :                 // have no other information.
     119            0 :                 delay = System::Clock::Milliseconds16(5000);
     120              :             }
     121            1 :             CHIP_ERROR err = SendBusyStatusReport(ec, delay);
     122            1 :             if (err != CHIP_NO_ERROR)
     123              :             {
     124            0 :                 ChipLogError(Inet, "Failed to send the busy status report, err:%" CHIP_ERROR_FORMAT, err.Format());
     125              :             }
     126            1 :             return err;
     127              :         }
     128              :     }
     129              : 
     130            7 :     if (!ec->GetSessionHandle()->IsUnauthenticatedSession())
     131              :     {
     132            0 :         ChipLogError(Inet, "CASE Server received Sigma1 message %s EC %p", "over encrypted session. Ignoring.", ec);
     133            0 :         return CHIP_ERROR_INCORRECT_STATE;
     134              :     }
     135              : 
     136            7 :     ChipLogProgress(Inet, "CASE Server received Sigma1 message %s EC %p", ". Starting handshake.", ec);
     137              : 
     138            7 :     CHIP_ERROR err = InitCASEHandshake(ec);
     139            7 :     SuccessOrExit(err);
     140              : 
     141              :     // TODO - Enable multiple concurrent CASE session establishment
     142              :     // https://github.com/project-chip/connectedhomeip/issues/8342
     143              : 
     144            7 :     err = GetSession().OnMessageReceived(ec, payloadHeader, std::move(payload));
     145            7 :     SuccessOrExit(err);
     146              : 
     147            7 : exit:
     148              :     // CASESession::OnMessageReceived guarantees that it will call
     149              :     // OnSessionEstablishmentError if it returns error, so nothing else to do here.
     150            7 :     return err;
     151              : }
     152              : 
     153           14 : void CASEServer::PrepareForSessionEstablishment(const ScopedNodeId & previouslyEstablishedPeer)
     154              : {
     155           14 :     GetSession().Clear();
     156              : 
     157              :     //
     158              :     // This releases our reference to a previously pinned session. If that was a successfully established session and is now
     159              :     // active, this will have no effect (the session will remain in the session table).
     160              :     //
     161              :     // If we previously held a session still in the pairing state, it means PairingSession was owning that session. Since it
     162              :     // gave up its reference by the time we got here, releasing the pinned session here will actually result in it being
     163              :     // de-allocated since no one else is holding onto this session. This will mean that when we get to allocating a session below,
     164              :     // we'll at least have one free session available in the session table, and won't need to evict an arbitrary session.
     165              :     //
     166           14 :     mPinnedSecureSession.ClearValue();
     167              : 
     168              :     //
     169              :     // Indicate to the underlying CASE session to prepare for session establishment requests coming its way. This will
     170              :     // involve allocating a SecureSession that will be held until it's needed for the next CASE session handshake.
     171              :     //
     172              :     // Logically speaking, we're attempting to evict a session using details of the just-established session (to ensure
     173              :     // we're evicting sessions from the right fabric if needed) and then transferring the just established session into that
     174              :     // slot (and thereby free'ing up the slot for the next session attempt). However, this transfer isn't necessary - just
     175              :     // evicting a session will ensure it is available for the next attempt.
     176              :     //
     177              :     // This call can fail if we have run out memory to allocate SecureSessions. Continuing without taking any action
     178              :     // however will render this node deaf to future handshake requests, so it's better to die here to raise attention to the problem
     179              :     // / facilitate recovery.
     180              :     //
     181              :     // TODO(#17568): Once session eviction is actually in place, this call should NEVER fail and if so, is a logic bug.
     182              :     // Dying here on failure is even more appropriate then.
     183              :     //
     184           14 :     VerifyOrDie(GetSession().PrepareForSessionEstablishment(*mSessionManager, mFabrics, mSessionResumptionStorage,
     185              :                                                             mCertificateValidityPolicy, this, previouslyEstablishedPeer,
     186              :                                                             GetLocalMRPConfig()) == CHIP_NO_ERROR);
     187              : 
     188              :     //
     189              :     // PairingSession::mSecureSessionHolder is a weak-reference. If MarkForEviction is called on this session, the session is
     190              :     // going to get de-allocated from underneath us. This session that has just been allocated should *never* get evicted, and
     191              :     // remain available till the next hand-shake is received.
     192              :     //
     193              :     // TODO: Converting SessionHolder to a true weak-ref and making PairingSession hold a strong-ref (#18397) would avoid this
     194              :     // headache...
     195              :     //
     196              :     // Let's create a SessionHandle strong-reference to it to keep it resident.
     197              :     //
     198           14 :     mPinnedSecureSession = GetSession().CopySecureSession();
     199              : 
     200              :     //
     201              :     // If we've gotten this far, it means we have successfully allocated a SecureSession to back our next attempt. If we haven't,
     202              :     // there is a bug somewhere and we should raise attention to it by dying.
     203              :     //
     204           14 :     VerifyOrDie(mPinnedSecureSession.HasValue());
     205           14 : }
     206              : 
     207            0 : void CASEServer::OnSessionEstablishmentError(CHIP_ERROR err)
     208              : {
     209              :     MATTER_TRACE_SCOPE("OnSessionEstablishmentError", "CASEServer");
     210            0 :     ChipLogError(Inet, "CASE Session establishment failed: %" CHIP_ERROR_FORMAT, err.Format());
     211              : 
     212              :     MATTER_TRACE_SCOPE("CASEFail", "CASESession");
     213            0 :     PrepareForSessionEstablishment();
     214            0 : }
     215              : 
     216            7 : void CASEServer::OnSessionEstablished(const SessionHandle & session)
     217              : {
     218              :     MATTER_TRACE_SCOPE("OnSessionEstablished", "CASEServer");
     219            7 :     ChipLogProgress(Inet, "CASE Session established to peer: " ChipLogFormatScopedNodeId,
     220              :                     ChipLogValueScopedNodeId(session->GetPeer()));
     221            7 :     PrepareForSessionEstablishment(session->GetPeer());
     222            7 : }
     223              : 
     224            1 : CHIP_ERROR CASEServer::SendBusyStatusReport(Messaging::ExchangeContext * ec, System::Clock::Milliseconds16 minimumWaitTime)
     225              : {
     226              :     MATTER_TRACE_SCOPE("SendBusyStatusReport", "CASEServer");
     227            1 :     ChipLogProgress(Inet, "Already in the middle of CASE handshake, sending busy status report");
     228              : 
     229            1 :     System::PacketBufferHandle handle = Protocols::SecureChannel::StatusReport::MakeBusyStatusReportMessage(minimumWaitTime);
     230            1 :     VerifyOrReturnError(!handle.IsNull(), CHIP_ERROR_NO_MEMORY);
     231              : 
     232            1 :     ChipLogProgress(Inet, "Sending status report, exchange " ChipLogFormatExchange, ChipLogValueExchange(ec));
     233            1 :     return ec->SendMessage(Protocols::SecureChannel::MsgType::StatusReport, std::move(handle));
     234            1 : }
     235              : 
     236              : } // namespace chip
        

Generated by: LCOV version 2.0-1