LCOV - code coverage report
Current view: top level - protocols/secure_channel - CASEServer.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 59 62 95.2 %
Date: 2024-02-15 08:20:41 Functions: 8 8 100.0 %

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

Generated by: LCOV version 1.14