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