/src/connectedhomeip/src/transport/SessionManager.cpp
Line | Count | Source |
1 | | /* |
2 | | * |
3 | | * Copyright (c) 2020-2021 Project CHIP Authors |
4 | | * Copyright (c) 2013-2017 Nest Labs, Inc. |
5 | | * All rights reserved. |
6 | | * |
7 | | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | | * you may not use this file except in compliance with the License. |
9 | | * You may obtain a copy of the License at |
10 | | * |
11 | | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | | * |
13 | | * Unless required by applicable law or agreed to in writing, software |
14 | | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | | * See the License for the specific language governing permissions and |
17 | | * limitations under the License. |
18 | | */ |
19 | | |
20 | | /** |
21 | | * @file |
22 | | * This file implements the CHIP Connection object that maintains a UDP connection. |
23 | | * TODO This class should be extended to support TCP as well... |
24 | | * |
25 | | */ |
26 | | |
27 | | #include "SessionManager.h" |
28 | | |
29 | | #include <algorithm> |
30 | | #include <inttypes.h> |
31 | | #include <string.h> |
32 | | |
33 | | #include "transport/TraceMessage.h" |
34 | | #include <app/util/basic-types.h> |
35 | | #include <credentials/GroupDataProvider.h> |
36 | | #include <inttypes.h> |
37 | | #include <lib/core/CHIPKeyIds.h> |
38 | | #include <lib/core/Global.h> |
39 | | #include <lib/support/AutoRelease.h> |
40 | | #include <lib/support/CodeUtils.h> |
41 | | #include <lib/support/SafeInt.h> |
42 | | #include <lib/support/logging/CHIPLogging.h> |
43 | | #include <platform/CHIPDeviceLayer.h> |
44 | | #include <protocols/Protocols.h> |
45 | | #include <protocols/secure_channel/Constants.h> |
46 | | #include <tracing/macros.h> |
47 | | #include <transport/GroupPeerMessageCounter.h> |
48 | | #include <transport/GroupSession.h> |
49 | | #include <transport/SecureMessageCodec.h> |
50 | | #include <transport/TracingStructs.h> |
51 | | #include <transport/TransportMgr.h> |
52 | | #include <transport/raw/GroupcastTesting.h> |
53 | | |
54 | | namespace chip { |
55 | | |
56 | | using System::PacketBufferHandle; |
57 | | using Transport::GroupPeerTable; |
58 | | using Transport::PeerAddress; |
59 | | using Transport::SecureSession; |
60 | | |
61 | | namespace { |
62 | | Global<GroupPeerTable> gGroupPeerTable; |
63 | | |
64 | | // Helper function that strips off the interface ID from a peer address that is |
65 | | // not an IPv6 link-local address. For any other address type we should rely on |
66 | | // the device's routing table to route messages sent. Forcing messages down a |
67 | | // specific interface might fail with "no route to host". |
68 | | void CorrectPeerAddressInterfaceID(Transport::PeerAddress & peerAddress) |
69 | 5.40k | { |
70 | 5.40k | if (peerAddress.GetIPAddress().IsIPv6LinkLocal()) |
71 | 0 | { |
72 | 0 | return; |
73 | 0 | } |
74 | 5.40k | peerAddress.SetInterface(Inet::InterfaceId::Null()); |
75 | 5.40k | } |
76 | | |
77 | | } // namespace |
78 | | |
79 | | uint32_t EncryptedPacketBufferHandle::GetMessageCounter() const |
80 | 7.14k | { |
81 | 7.14k | PacketHeader header; |
82 | 7.14k | uint16_t headerSize = 0; |
83 | 7.14k | CHIP_ERROR err = header.Decode((*this)->Start(), (*this)->DataLength(), &headerSize); |
84 | | |
85 | 7.14k | if (err == CHIP_NO_ERROR) |
86 | 7.14k | { |
87 | 7.14k | return header.GetMessageCounter(); |
88 | 7.14k | } |
89 | | |
90 | 0 | ChipLogError(Inet, "Failed to decode EncryptedPacketBufferHandle header with error: %" CHIP_ERROR_FORMAT, err.Format()); |
91 | |
|
92 | 0 | return 0; |
93 | 7.14k | } |
94 | | |
95 | 4.49k | SessionManager::SessionManager() : mState(State::kNotReady) {} |
96 | | |
97 | | SessionManager::~SessionManager() |
98 | 4.48k | { |
99 | 4.48k | this->Shutdown(); |
100 | 4.48k | } |
101 | | |
102 | | CHIP_ERROR SessionManager::Init(System::Layer * systemLayer, TransportMgrBase * transportMgr, |
103 | | Transport::MessageCounterManagerInterface * messageCounterManager, |
104 | | chip::PersistentStorageDelegate * storageDelegate, FabricTable * fabricTable, |
105 | | Crypto::SessionKeystore & sessionKeystore) |
106 | 4.49k | { |
107 | 4.49k | VerifyOrReturnError(mState == State::kNotReady, CHIP_ERROR_INCORRECT_STATE); |
108 | 4.49k | VerifyOrReturnError(transportMgr != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
109 | 4.49k | VerifyOrReturnError(storageDelegate != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
110 | 4.49k | VerifyOrReturnError(fabricTable != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
111 | 4.49k | ReturnErrorOnFailure(fabricTable->AddFabricDelegate(this)); |
112 | | |
113 | 4.49k | mState = State::kInitialized; |
114 | 4.49k | mSystemLayer = systemLayer; |
115 | 4.49k | mTransportMgr = transportMgr; |
116 | 4.49k | mMessageCounterManager = messageCounterManager; |
117 | 4.49k | mFabricTable = fabricTable; |
118 | 4.49k | mSessionKeystore = &sessionKeystore; |
119 | | |
120 | 4.49k | mSecureSessions.Init(); |
121 | | |
122 | 4.49k | mGlobalUnencryptedMessageCounter.Init(); |
123 | | |
124 | 4.49k | ReturnErrorOnFailure(mGroupClientCounter.Init(storageDelegate)); |
125 | | |
126 | 4.49k | mTransportMgr->SetSessionManager(this); |
127 | | |
128 | 4.49k | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
129 | 4.49k | mConnCompleteCb = nullptr; |
130 | 4.49k | mConnClosedCb = nullptr; |
131 | 4.49k | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
132 | | |
133 | | // Ensure MessageStats struct is at default state on Init |
134 | 4.49k | mMessageStats = MessageStats(); |
135 | | |
136 | 4.49k | return CHIP_NO_ERROR; |
137 | 4.49k | } |
138 | | |
139 | | void SessionManager::Shutdown() |
140 | 8.97k | { |
141 | 8.97k | if (mFabricTable != nullptr) |
142 | 4.49k | { |
143 | 4.49k | mFabricTable->RemoveFabricDelegate(this); |
144 | 4.49k | mFabricTable = nullptr; |
145 | 4.49k | } |
146 | | |
147 | | // Ensure that we don't create new sessions as we iterate our session table. |
148 | 8.97k | mState = State::kNotReady; |
149 | | |
150 | | // Just in case some consumer forgot to do it, expire all our secure |
151 | | // sessions. Note that this stands a good chance of crashing with a |
152 | | // null-deref if there are in fact any secure sessions left, since they will |
153 | | // try to notify their exchanges, which will then try to operate on |
154 | | // partially-shut-down objects. |
155 | 8.97k | ExpireAllSecureSessions(); |
156 | | |
157 | | // We don't have a safe way to check or affect the state of our |
158 | | // mUnauthenticatedSessions. We can only hope they got shut down properly. |
159 | | |
160 | 8.97k | mMessageCounterManager = nullptr; |
161 | | |
162 | 8.97k | mSystemLayer = nullptr; |
163 | 8.97k | mTransportMgr = nullptr; |
164 | 8.97k | mCB = nullptr; |
165 | 8.97k | } |
166 | | |
167 | | /** |
168 | | * @brief Notification that a fabric was removed. |
169 | | * This function doesn't call ExpireAllSessionsForFabric |
170 | | * since the CASE session might still be open to send a response |
171 | | * on the removed fabric. |
172 | | */ |
173 | | void SessionManager::FabricRemoved(FabricIndex fabricIndex) |
174 | 1.06k | { |
175 | 1.06k | TEMPORARY_RETURN_IGNORED gGroupPeerTable->FabricRemoved(fabricIndex); |
176 | 1.06k | } |
177 | | |
178 | | CHIP_ERROR SessionManager::PrepareMessage(const SessionHandle & sessionHandle, PayloadHeader & payloadHeader, |
179 | | System::PacketBufferHandle && message, EncryptedPacketBufferHandle & preparedMessage) |
180 | 5.48k | { |
181 | 5.48k | MATTER_TRACE_SCOPE("PrepareMessage", "SessionManager"); |
182 | | |
183 | 5.48k | VerifyOrReturnError(!message->HasChainedBuffer(), CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
184 | | |
185 | 5.48k | bool headerEncoded = false; |
186 | 5.48k | PacketHeader packetHeader; |
187 | 5.48k | bool isControlMsg = IsControlMessage(payloadHeader); |
188 | 5.48k | if (isControlMsg) |
189 | 16 | { |
190 | 16 | packetHeader.SetSecureSessionControlMsg(true); |
191 | 16 | } |
192 | | |
193 | 5.48k | if (sessionHandle->AllowsLargePayload()) |
194 | 0 | { |
195 | 0 | uint32_t maxPayload = sessionHandle->GetRemoteSessionParameters().GetMaxTCPPayloadSize(); |
196 | 0 | size_t remoteLimit = (maxPayload > 0) ? static_cast<size_t>(maxPayload) : kLegacyDefaultMaxLargeAppMessageLen; |
197 | 0 | size_t limit = std::min(remoteLimit, kMaxLargeAppMessageLen); |
198 | 0 | VerifyOrReturnError(message->TotalLength() <= limit, CHIP_ERROR_MESSAGE_TOO_LONG); |
199 | 0 | } |
200 | 5.48k | else |
201 | 5.48k | { |
202 | 5.48k | VerifyOrReturnError(message->TotalLength() <= kMaxAppMessageLen, CHIP_ERROR_MESSAGE_TOO_LONG); |
203 | 5.48k | } |
204 | | |
205 | 5.48k | #if CHIP_PROGRESS_LOGGING |
206 | 5.48k | NodeId destination; |
207 | 5.48k | FabricIndex fabricIndex; |
208 | 5.48k | #endif // CHIP_PROGRESS_LOGGING |
209 | | |
210 | 5.48k | NodeId sourceNodeId = kUndefinedNodeId; |
211 | 5.48k | PeerAddress destination_address; |
212 | | |
213 | 5.48k | switch (sessionHandle->GetSessionType()) |
214 | 5.48k | { |
215 | 165 | case Transport::Session::SessionType::kGroupOutgoing: { |
216 | 165 | auto groupSession = sessionHandle->AsOutgoingGroupSession(); |
217 | 165 | auto * groups = Credentials::GetGroupDataProvider(); |
218 | 165 | VerifyOrReturnError(nullptr != groups, CHIP_ERROR_INTERNAL); |
219 | | |
220 | 165 | const FabricInfo * fabric = mFabricTable->FindFabricWithIndex(groupSession->GetFabricIndex()); |
221 | 165 | VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
222 | | |
223 | 165 | packetHeader.SetDestinationGroupId(groupSession->GetGroupId()); |
224 | 165 | packetHeader.SetMessageCounter(mGroupClientCounter.GetCounter(isControlMsg)); |
225 | 165 | TEMPORARY_RETURN_IGNORED mGroupClientCounter.IncrementCounter(isControlMsg); |
226 | 165 | packetHeader.SetSessionType(Header::SessionType::kGroupSession); |
227 | 165 | packetHeader.SetFlags(Header::SecFlagValues::kPrivacyFlag); |
228 | 165 | sourceNodeId = fabric->GetNodeId(); |
229 | 165 | packetHeader.SetSourceNodeId(sourceNodeId); |
230 | | |
231 | 165 | if (!packetHeader.IsValidGroupMsg()) |
232 | 16 | { |
233 | 16 | return CHIP_ERROR_INTERNAL; |
234 | 16 | } |
235 | | |
236 | 149 | Credentials::GroupDataProvider::GroupInfo info; |
237 | 149 | ReturnErrorOnFailure(groups->GetGroupInfo(groupSession->GetFabricIndex(), groupSession->GetGroupId(), info)); |
238 | 149 | destination_address = (info.UsePerGroupAddress()) |
239 | 149 | ? Transport::PeerAddress::BuildMatterPerGroupMulticastAddress(fabric->GetFabricId(), groupSession->GetGroupId()) |
240 | 149 | : Transport::PeerAddress::BuildMatterIanaMulticastAddress(); |
241 | | |
242 | 149 | Crypto::SymmetricKeyContext * keyContext = |
243 | 149 | groups->GetKeyContext(groupSession->GetFabricIndex(), groupSession->GetGroupId()); |
244 | 149 | VerifyOrReturnError(nullptr != keyContext, CHIP_ERROR_INTERNAL); |
245 | 149 | AutoRelease<Crypto::SymmetricKeyContext> keyContextOwner(keyContext); |
246 | | |
247 | 149 | packetHeader.SetSessionId(keyContext->GetKeyHash()); |
248 | 149 | CryptoContext cryptoContext(keyContext); |
249 | | |
250 | | // Trace before any encryption |
251 | 149 | MATTER_LOG_MESSAGE_SEND(chip::Tracing::OutgoingMessageType::kGroupMessage, &payloadHeader, &packetHeader, |
252 | 149 | chip::ByteSpan(message->Start(), message->TotalLength()), |
253 | | /* messageTotalSize = */ |
254 | 149 | (packetHeader.EncodeSizeBytes() + payloadHeader.EncodeSizeBytes() + message->TotalLength() + |
255 | 149 | packetHeader.MICTagLength())); |
256 | 149 | CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, destination_address, message->Start(), message->TotalLength()); |
257 | | |
258 | 149 | CryptoContext::NonceStorage nonce; |
259 | 149 | ReturnErrorOnFailure( |
260 | 149 | CryptoContext::BuildNonce(nonce, packetHeader.GetSecurityFlags(), packetHeader.GetMessageCounter(), sourceNodeId)); |
261 | 149 | CHIP_ERROR err = SecureMessageCodec::Encrypt(cryptoContext, nonce, payloadHeader, packetHeader, message); |
262 | 149 | ReturnErrorOnFailure(err); |
263 | | |
264 | | // Encode header now so we can privacy encrypt it |
265 | 149 | ReturnErrorOnFailure(packetHeader.EncodeBeforeData(message)); |
266 | 149 | headerEncoded = true; |
267 | | |
268 | | // Begin privacy encrypt for appropriate header fields |
269 | | |
270 | | // Since we are not using chained buffers, the message data length should be equal to the total length |
271 | 149 | VerifyOrReturnError(message->TotalLength() == message->DataLength(), CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
272 | 149 | uint8_t * data = message->Start(); |
273 | 149 | size_t len = message->TotalLength(); |
274 | 149 | uint16_t footerLen = packetHeader.MICTagLength(); |
275 | 149 | VerifyOrReturnError(footerLen <= len, CHIP_ERROR_INTERNAL); |
276 | | |
277 | 149 | uint16_t taglen = 0; |
278 | 149 | MessageAuthenticationCode mac; |
279 | 149 | ReturnErrorOnFailure(mac.Decode(packetHeader, &data[len - footerLen], footerLen, &taglen)); |
280 | 149 | VerifyOrReturnError(taglen == footerLen, CHIP_ERROR_INTERNAL); |
281 | | |
282 | | // Pointer to the start of the privacy header within the message buffer. |
283 | | // The privacy header contains fields that need to be privacy-encrypted (e.g. Session ID, Message Counter). |
284 | 149 | uint8_t * privacyHeader = packetHeader.PrivacyHeader(message->Start()); |
285 | 149 | size_t privacyLength = packetHeader.PrivacyHeaderLength(); |
286 | | |
287 | | // We must ensure that: |
288 | | // 1. The privacy header starts within the message buffer. |
289 | | // 2. The privacy header lies entirely within the encoded packet header bounds (to prevent encrypting payload). |
290 | | // 3. The packet header lies entirely within the valid message buffer bounds. |
291 | | // |
292 | | // (privacyHeader + privacyLength): Pointer to the end of the privacy header. |
293 | | // (message->Start() + packetHeader.EncodeSizeBytes()): Pointer to the end of the packet header. |
294 | | // (message->Start() + message->TotalLength()): Pointer to the end of the valid message data in the buffer. |
295 | 149 | uint8_t * privacyHeaderEnd = (privacyHeader + privacyLength); |
296 | 149 | uint8_t * headerEnd = (message->Start() + packetHeader.EncodeSizeBytes()); |
297 | 149 | uint8_t * messageEnd = (message->Start() + message->TotalLength()); |
298 | | |
299 | | // Other fields such as message flags and session ID should exist in the header BEFORE the privacy fields, |
300 | | // so the start of the privacy header must be strictly after the message start |
301 | 149 | VerifyOrReturnError(privacyHeader > message->Start(), CHIP_ERROR_INTERNAL); |
302 | | |
303 | | // When the message extensions (MX) security flag is set, this indicates that there will be a message extensions |
304 | | // portion of the header (with a non-zero length). This portion of the header exists after the privacy header end. |
305 | | // If the flag is not set, the end of the privacy header should be the end of the header itself. |
306 | 149 | bool mxEnabled = packetHeader.GetSecurityFlags() & to_underlying(Header::SecFlagValues::kMsgExtensionFlag); |
307 | 149 | if (mxEnabled) |
308 | 0 | { |
309 | 0 | VerifyOrReturnError(privacyHeaderEnd < headerEnd, CHIP_ERROR_INTERNAL); |
310 | 0 | } |
311 | 149 | else |
312 | 149 | { |
313 | 149 | VerifyOrReturnError(privacyHeaderEnd == headerEnd, CHIP_ERROR_INTERNAL); |
314 | 149 | } |
315 | | |
316 | 149 | VerifyOrReturnError(headerEnd < messageEnd, CHIP_ERROR_INTERNAL); |
317 | 149 | ReturnErrorOnFailure(cryptoContext.PrivacyEncrypt(privacyHeader, privacyLength, privacyHeader, packetHeader, mac)); |
318 | | |
319 | 149 | #if CHIP_PROGRESS_LOGGING |
320 | 149 | destination = NodeIdFromGroupId(groupSession->GetGroupId()); |
321 | 149 | fabricIndex = groupSession->GetFabricIndex(); |
322 | 149 | #endif // CHIP_PROGRESS_LOGGING |
323 | 149 | } |
324 | 0 | break; |
325 | 0 | case Transport::Session::SessionType::kSecure: { |
326 | 0 | SecureSession * session = sessionHandle->AsSecureSession(); |
327 | 0 | if (session == nullptr) |
328 | 0 | { |
329 | 0 | return CHIP_ERROR_NOT_CONNECTED; |
330 | 0 | } |
331 | | |
332 | 0 | MessageCounter & counter = session->GetSessionMessageCounter().GetLocalMessageCounter(); |
333 | 0 | uint32_t messageCounter; |
334 | 0 | ReturnErrorOnFailure(counter.AdvanceAndConsume(messageCounter)); |
335 | 0 | packetHeader |
336 | 0 | .SetMessageCounter(messageCounter) // |
337 | 0 | .SetSessionId(session->GetPeerSessionId()) // |
338 | 0 | .SetSessionType(Header::SessionType::kUnicastSession); |
339 | |
|
340 | 0 | destination_address = session->GetPeerAddress(); |
341 | 0 | CryptoContext & cryptoContext = session->GetCryptoContext(); |
342 | | |
343 | | // Trace before any encryption |
344 | 0 | MATTER_LOG_MESSAGE_SEND(chip::Tracing::OutgoingMessageType::kSecureSession, &payloadHeader, &packetHeader, |
345 | 0 | chip::ByteSpan(message->Start(), message->TotalLength()), |
346 | | /* totalMessageSize = */ |
347 | 0 | (packetHeader.EncodeSizeBytes() + payloadHeader.EncodeSizeBytes() + message->TotalLength() + |
348 | 0 | packetHeader.MICTagLength())); |
349 | 0 | CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, destination_address, message->Start(), message->TotalLength()); |
350 | |
|
351 | 0 | CryptoContext::NonceStorage nonce; |
352 | 0 | sourceNodeId = session->GetLocalScopedNodeId().GetNodeId(); |
353 | 0 | ReturnErrorOnFailure(CryptoContext::BuildNonce(nonce, packetHeader.GetSecurityFlags(), messageCounter, sourceNodeId)); |
354 | | |
355 | 0 | ReturnErrorOnFailure(SecureMessageCodec::Encrypt(cryptoContext, nonce, payloadHeader, packetHeader, message)); |
356 | | |
357 | 0 | #if CHIP_PROGRESS_LOGGING |
358 | 0 | destination = session->GetPeerNodeId(); |
359 | 0 | fabricIndex = session->GetFabricIndex(); |
360 | 0 | #endif // CHIP_PROGRESS_LOGGING |
361 | 0 | } |
362 | 0 | break; |
363 | 5.32k | case Transport::Session::SessionType::kUnauthenticated: { |
364 | 5.32k | MessageCounter & counter = mGlobalUnencryptedMessageCounter; |
365 | 5.32k | uint32_t messageCounter; |
366 | 5.32k | ReturnErrorOnFailure(counter.AdvanceAndConsume(messageCounter)); |
367 | 5.32k | packetHeader.SetMessageCounter(messageCounter); |
368 | 5.32k | Transport::UnauthenticatedSession * session = sessionHandle->AsUnauthenticatedSession(); |
369 | 5.32k | switch (session->GetSessionRole()) |
370 | 5.32k | { |
371 | 2.83k | case Transport::UnauthenticatedSession::SessionRole::kInitiator: |
372 | 2.83k | packetHeader.SetSourceNodeId(session->GetEphemeralInitiatorNodeID()); |
373 | 2.83k | break; |
374 | 2.48k | case Transport::UnauthenticatedSession::SessionRole::kResponder: |
375 | 2.48k | packetHeader.SetDestinationNodeId(session->GetEphemeralInitiatorNodeID()); |
376 | 2.48k | break; |
377 | 5.32k | } |
378 | | |
379 | 5.32k | auto unauthenticated = sessionHandle->AsUnauthenticatedSession(); |
380 | 5.32k | destination_address = unauthenticated->GetPeerAddress(); |
381 | | |
382 | | // Trace after all headers are settled. |
383 | 5.32k | MATTER_LOG_MESSAGE_SEND(chip::Tracing::OutgoingMessageType::kUnauthenticated, &payloadHeader, &packetHeader, |
384 | 5.32k | chip::ByteSpan(message->Start(), message->TotalLength()), |
385 | 5.32k | /* messageTotalSize = */ packetHeader.EncodeSizeBytes() + payloadHeader.EncodeSizeBytes() + |
386 | 5.32k | message->TotalLength()); |
387 | 5.32k | CHIP_TRACE_MESSAGE_SENT(payloadHeader, packetHeader, destination_address, message->Start(), message->TotalLength()); |
388 | | |
389 | 5.32k | ReturnErrorOnFailure(payloadHeader.EncodeBeforeData(message)); |
390 | | |
391 | 5.32k | #if CHIP_PROGRESS_LOGGING |
392 | 5.32k | destination = kUndefinedNodeId; |
393 | 5.32k | fabricIndex = kUndefinedFabricIndex; |
394 | 5.32k | if (session->GetSessionRole() == Transport::UnauthenticatedSession::SessionRole::kResponder) |
395 | 2.48k | { |
396 | 2.48k | destination = session->GetEphemeralInitiatorNodeID(); |
397 | 2.48k | } |
398 | 2.83k | else if (session->GetSessionRole() == Transport::UnauthenticatedSession::SessionRole::kInitiator) |
399 | 2.83k | { |
400 | 2.83k | sourceNodeId = session->GetEphemeralInitiatorNodeID(); |
401 | 2.83k | } |
402 | 5.32k | #endif // CHIP_PROGRESS_LOGGING |
403 | 5.32k | } |
404 | 0 | break; |
405 | 0 | default: |
406 | 0 | return CHIP_ERROR_INTERNAL; |
407 | 5.48k | } |
408 | | |
409 | 5.47k | if (!headerEncoded) |
410 | 5.32k | { |
411 | 5.32k | ReturnErrorOnFailure(packetHeader.EncodeBeforeData(message)); |
412 | 5.32k | } |
413 | | |
414 | 5.47k | #if CHIP_PROGRESS_LOGGING |
415 | 5.47k | CompressedFabricId compressedFabricId = kUndefinedCompressedFabricId; |
416 | | |
417 | 5.47k | if (fabricIndex != kUndefinedFabricIndex && mFabricTable != nullptr) |
418 | 149 | { |
419 | 149 | auto fabricInfo = mFabricTable->FindFabricWithIndex(fabricIndex); |
420 | 149 | if (fabricInfo) |
421 | 149 | { |
422 | 149 | compressedFabricId = fabricInfo->GetCompressedFabricId(); |
423 | 149 | } |
424 | 149 | } |
425 | | |
426 | 5.47k | auto * protocolName = Protocols::GetProtocolName(payloadHeader.GetProtocolID()); |
427 | 5.47k | auto * msgTypeName = Protocols::GetMessageTypeName(payloadHeader.GetProtocolID(), payloadHeader.GetMessageType()); |
428 | | |
429 | | // |
430 | | // 32-bit value maximum = 10 chars + text preamble (6) + trailer (1) + null (1) + 2 buffer = 20 |
431 | | // |
432 | 5.47k | char ackBuf[20]; |
433 | 5.47k | ackBuf[0] = '\0'; |
434 | 5.47k | if (payloadHeader.GetAckMessageCounter().HasValue()) |
435 | 3.57k | { |
436 | 3.57k | snprintf(ackBuf, sizeof(ackBuf), " (Ack:" ChipLogFormatMessageCounter ")", payloadHeader.GetAckMessageCounter().Value()); |
437 | 3.57k | } |
438 | | |
439 | 5.47k | char addressStr[Transport::PeerAddress::kMaxToStringSize] = { 0 }; |
440 | 5.47k | destination_address.ToString(addressStr); |
441 | | |
442 | | // Work around pigweed not allowing more than 14 format args in a log |
443 | | // message when using tokenized logs. |
444 | 5.47k | char typeStr[4 + 1 + 2 + 1]; |
445 | 5.47k | snprintf(typeStr, sizeof(typeStr), "%04X:%02X", payloadHeader.GetProtocolID().GetProtocolId(), payloadHeader.GetMessageType()); |
446 | | |
447 | | // More work around pigweed not allowing more than 14 format args in a log |
448 | | // message when using tokenized logs. |
449 | | // ChipLogFormatExchangeId logs the numeric exchange ID (at most 5 chars, |
450 | | // since it's a uint16_t) and one char for initiator/responder. Plus we |
451 | | // need a null-terminator. |
452 | 5.47k | char exchangeStr[5 + 1 + 1]; |
453 | 5.47k | snprintf(exchangeStr, sizeof(exchangeStr), ChipLogFormatExchangeId, ChipLogValueExchangeIdFromSentHeader(payloadHeader)); |
454 | | |
455 | | // More work around pigweed not allowing more than 14 format args in a log |
456 | | // message when using tokenized logs. |
457 | | // text(5) + source(16) + text(4) + fabricIndex(uint16_t, at most 5 chars) + text(1) + destination(16) + text(2) + compressed |
458 | | // fabric id(4) + text(1) + null-terminator |
459 | 5.47k | char sourceDestinationStr[5 + 16 + 4 + 5 + 1 + 16 + 2 + 4 + 1 + 1]; |
460 | 5.47k | snprintf(sourceDestinationStr, sizeof(sourceDestinationStr), "from " ChipLogFormatX64 " to %u:" ChipLogFormatX64 " [%04X]", |
461 | 5.47k | ChipLogValueX64(sourceNodeId), fabricIndex, ChipLogValueX64(destination), static_cast<uint16_t>(compressedFabricId)); |
462 | | |
463 | | // |
464 | | // Legend that can be used to decode this log line can be found in messaging/README.md |
465 | | // |
466 | 5.47k | ChipLogProgress(ExchangeManager, |
467 | 5.47k | "<<< [E:%s S:%u M:" ChipLogFormatMessageCounter "%s] (%s) Msg TX %s [%s] --- Type %s (%s:%s) (B:%u)", |
468 | 5.47k | exchangeStr, sessionHandle->SessionIdForLogging(), packetHeader.GetMessageCounter(), ackBuf, |
469 | 5.47k | Transport::GetSessionTypeString(sessionHandle), sourceDestinationStr, addressStr, typeStr, protocolName, |
470 | 5.47k | msgTypeName, static_cast<unsigned>(message->TotalLength())); |
471 | 5.47k | #endif |
472 | | |
473 | 5.47k | preparedMessage = EncryptedPacketBufferHandle::MarkEncrypted(std::move(message)); |
474 | | |
475 | 5.47k | CountMessagesSent(sessionHandle, payloadHeader); |
476 | 5.47k | return CHIP_NO_ERROR; |
477 | 5.47k | } |
478 | | |
479 | | CHIP_ERROR SessionManager::SendPreparedMessage(const SessionHandle & sessionHandle, |
480 | | const EncryptedPacketBufferHandle & preparedMessage) |
481 | 5.32k | { |
482 | 5.32k | VerifyOrReturnError(mState == State::kInitialized, CHIP_ERROR_INCORRECT_STATE); |
483 | 5.32k | VerifyOrReturnError(!preparedMessage.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); |
484 | | |
485 | 5.32k | Transport::PeerAddress multicastAddress; // Only used for the group case |
486 | 5.32k | const Transport::PeerAddress * destination; |
487 | | |
488 | 5.32k | switch (sessionHandle->GetSessionType()) |
489 | 5.32k | { |
490 | 0 | case Transport::Session::SessionType::kGroupOutgoing: { |
491 | 0 | auto groupSession = sessionHandle->AsOutgoingGroupSession(); |
492 | |
|
493 | 0 | const FabricInfo * fabric = mFabricTable->FindFabricWithIndex(groupSession->GetFabricIndex()); |
494 | 0 | VerifyOrReturnError(fabric != nullptr, CHIP_ERROR_INVALID_ARGUMENT); |
495 | 0 | auto * groups = Credentials::GetGroupDataProvider(); |
496 | 0 | VerifyOrReturnError(nullptr != groups, CHIP_ERROR_INTERNAL); |
497 | | |
498 | 0 | Credentials::GroupDataProvider::GroupInfo info; |
499 | 0 | ReturnErrorOnFailure(groups->GetGroupInfo(groupSession->GetFabricIndex(), groupSession->GetGroupId(), info)); |
500 | 0 | multicastAddress = (info.UsePerGroupAddress()) |
501 | 0 | ? Transport::PeerAddress::BuildMatterPerGroupMulticastAddress(fabric->GetFabricId(), groupSession->GetGroupId()) |
502 | 0 | : Transport::PeerAddress::BuildMatterIanaMulticastAddress(); |
503 | 0 | destination = &multicastAddress; |
504 | 0 | } |
505 | 0 | break; |
506 | 0 | case Transport::Session::SessionType::kSecure: { |
507 | | // Find an active connection to the specified peer node |
508 | 0 | SecureSession * secure = sessionHandle->AsSecureSession(); |
509 | | |
510 | | // This marks any connection where we send data to as 'active' |
511 | 0 | secure->MarkActive(); |
512 | |
|
513 | 0 | destination = &secure->GetPeerAddress(); |
514 | 0 | } |
515 | 0 | break; |
516 | 5.32k | case Transport::Session::SessionType::kUnauthenticated: { |
517 | 5.32k | auto unauthenticated = sessionHandle->AsUnauthenticatedSession(); |
518 | 5.32k | unauthenticated->MarkActive(); |
519 | 5.32k | destination = &unauthenticated->GetPeerAddress(); |
520 | 5.32k | } |
521 | 5.32k | break; |
522 | 0 | default: |
523 | 0 | return CHIP_ERROR_INTERNAL; |
524 | 5.32k | } |
525 | | |
526 | 5.32k | PacketBufferHandle msgBuf = preparedMessage.CastToWritable(); |
527 | 5.32k | VerifyOrReturnError(!msgBuf.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); |
528 | 5.32k | VerifyOrReturnError(!msgBuf->HasChainedBuffer(), CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
529 | | |
530 | 5.32k | #if CHIP_SYSTEM_CONFIG_MULTICAST_HOMING |
531 | 5.32k | if (sessionHandle->GetSessionType() == Transport::Session::SessionType::kGroupOutgoing) |
532 | 0 | { |
533 | 0 | chip::Inet::InterfaceIterator interfaceIt; |
534 | 0 | chip::Inet::InterfaceId interfaceId = chip::Inet::InterfaceId::Null(); |
535 | 0 | chip::Inet::IPAddress addr; |
536 | 0 | bool interfaceFound = false; |
537 | |
|
538 | 0 | while (interfaceIt.Next()) |
539 | 0 | { |
540 | 0 | if (interfaceIt.SupportsMulticast() && interfaceIt.IsUp()) |
541 | 0 | { |
542 | 0 | char name[Inet::InterfaceId::kMaxIfNameLength]; |
543 | 0 | TEMPORARY_RETURN_IGNORED interfaceIt.GetInterfaceName(name, Inet::InterfaceId::kMaxIfNameLength); |
544 | 0 | interfaceId = interfaceIt.GetInterfaceId(); |
545 | 0 | if (CHIP_NO_ERROR == interfaceId.GetLinkLocalAddr(&addr)) |
546 | 0 | { |
547 | 0 | ChipLogDetail(Inet, "Interface %s has a link local address", name); |
548 | |
|
549 | 0 | interfaceFound = true; |
550 | 0 | PacketBufferHandle tempBuf = msgBuf.CloneData(); |
551 | 0 | VerifyOrReturnError(!tempBuf.IsNull(), CHIP_ERROR_INVALID_ARGUMENT); |
552 | 0 | VerifyOrReturnError(!tempBuf->HasChainedBuffer(), CHIP_ERROR_INVALID_MESSAGE_LENGTH); |
553 | | |
554 | 0 | destination = &(multicastAddress.SetInterface(interfaceId)); |
555 | 0 | if (mTransportMgr != nullptr) |
556 | 0 | { |
557 | 0 | if (CHIP_NO_ERROR != mTransportMgr->SendMessage(*destination, std::move(tempBuf))) |
558 | 0 | { |
559 | 0 | ChipLogError(Inet, "Failed to send Multicast message on interface %s", name); |
560 | 0 | } |
561 | 0 | else |
562 | 0 | { |
563 | 0 | ChipLogDetail(Inet, "Successfully send Multicast message on interface %s", name); |
564 | 0 | } |
565 | 0 | } |
566 | 0 | } |
567 | 0 | } |
568 | 0 | } |
569 | | |
570 | 0 | if (!interfaceFound) |
571 | 0 | { |
572 | 0 | ChipLogError(Inet, "No valid Interface found.. Sending to the default one.. "); |
573 | 0 | } |
574 | 0 | else |
575 | 0 | { |
576 | | // Always return No error, because we expect some interface to fails and others to always succeed (e.g. lo interface) |
577 | 0 | return CHIP_NO_ERROR; |
578 | 0 | } |
579 | 0 | } |
580 | | |
581 | 5.32k | #endif // CHIP_SYSTEM_CONFIG_MULTICAST_HOMING |
582 | | |
583 | 5.32k | if (mTransportMgr != nullptr) |
584 | 5.32k | { |
585 | 5.32k | CHIP_ERROR err = mTransportMgr->SendMessage(*destination, std::move(msgBuf)); |
586 | 5.32k | #if CHIP_ERROR_LOGGING |
587 | 5.32k | if (err != CHIP_NO_ERROR) |
588 | 0 | { |
589 | 0 | char addressStr[Transport::PeerAddress::kMaxToStringSize] = { 0 }; |
590 | 0 | destination->ToString(addressStr); |
591 | 0 | ChipLogError(Inet, "SendMessage() to %s failed: %" CHIP_ERROR_FORMAT, addressStr, err.Format()); |
592 | 0 | } |
593 | 5.32k | #endif // CHIP_ERROR_LOGGING |
594 | 5.32k | return err; |
595 | 5.32k | } |
596 | | |
597 | 0 | ChipLogError(Inet, "The transport manager is not initialized. Unable to send the message"); |
598 | 0 | return CHIP_ERROR_INCORRECT_STATE; |
599 | 5.32k | } |
600 | | |
601 | | void SessionManager::ExpireAllSessions(const ScopedNodeId & node) |
602 | 0 | { |
603 | 0 | ChipLogDetail(Inet, "Expiring all sessions for node " ChipLogFormatScopedNodeId "!!", ChipLogValueScopedNodeId(node)); |
604 | |
|
605 | 0 | ForEachMatchingSession(node, [](auto * session) { session->MarkForEviction(); }); |
606 | 0 | } |
607 | | |
608 | | void SessionManager::ExpireAllSessionsForFabric(FabricIndex fabricIndex) |
609 | 0 | { |
610 | 0 | ChipLogDetail(Inet, "Expiring all sessions for fabric 0x%x!!", static_cast<unsigned>(fabricIndex)); |
611 | |
|
612 | 0 | ForEachMatchingSession(fabricIndex, [](auto * session) { session->MarkForEviction(); }); |
613 | 0 | } |
614 | | |
615 | | CHIP_ERROR SessionManager::ExpireAllSessionsOnLogicalFabric(const ScopedNodeId & node) |
616 | 0 | { |
617 | 0 | ChipLogDetail(Inet, "Expiring all sessions to peer " ChipLogFormatScopedNodeId " that are on the same logical fabric!!", |
618 | 0 | ChipLogValueScopedNodeId(node)); |
619 | |
|
620 | 0 | return ForEachMatchingSessionOnLogicalFabric(node, [](auto * session) { session->MarkForEviction(); }); |
621 | 0 | } |
622 | | |
623 | | CHIP_ERROR SessionManager::ExpireAllSessionsOnLogicalFabric(FabricIndex fabricIndex) |
624 | 0 | { |
625 | 0 | ChipLogDetail(Inet, "Expiring all sessions on the same logical fabric as fabric 0x%x!!", static_cast<unsigned>(fabricIndex)); |
626 | |
|
627 | 0 | return ForEachMatchingSessionOnLogicalFabric(fabricIndex, [](auto * session) { session->MarkForEviction(); }); |
628 | 0 | } |
629 | | |
630 | | void SessionManager::ExpireAllPASESessions() |
631 | 0 | { |
632 | 0 | ChipLogDetail(Inet, "Expiring all PASE sessions"); |
633 | 0 | mSecureSessions.ForEachSession([&](auto session) { |
634 | 0 | if (session->GetSecureSessionType() == Transport::SecureSession::Type::kPASE) |
635 | 0 | { |
636 | 0 | session->MarkForEviction(); |
637 | 0 | } |
638 | 0 | return Loop::Continue; |
639 | 0 | }); |
640 | 0 | } |
641 | | |
642 | | void SessionManager::ExpireAllSecureSessions() |
643 | 8.97k | { |
644 | 8.97k | mSecureSessions.ForEachSession([&](auto session) { |
645 | 508 | session->MarkForEviction(); |
646 | 508 | return Loop::Continue; |
647 | 508 | }); |
648 | 8.97k | } |
649 | | |
650 | | void SessionManager::MarkSessionsAsDefunct(const ScopedNodeId & node, const Optional<Transport::SecureSession::Type> & type) |
651 | 0 | { |
652 | 0 | mSecureSessions.ForEachSession([&node, &type](auto session) { |
653 | 0 | if (session->IsActiveSession() && session->GetPeer() == node && |
654 | 0 | (!type.HasValue() || type.Value() == session->GetSecureSessionType())) |
655 | 0 | { |
656 | 0 | session->MarkAsDefunct(); |
657 | 0 | } |
658 | 0 | return Loop::Continue; |
659 | 0 | }); |
660 | 0 | } |
661 | | |
662 | | void SessionManager::UpdateAllSessionsPeerAddress(const ScopedNodeId & node, const Transport::PeerAddress & addr) |
663 | 0 | { |
664 | 0 | mSecureSessions.ForEachSession([&node, &addr](auto session) { |
665 | | // Arguably we should only be updating active and defunct sessions, but there is no harm |
666 | | // in updating evicted sessions. |
667 | 0 | if (session->GetPeer() == node && Transport::SecureSession::Type::kCASE == session->GetSecureSessionType()) |
668 | 0 | { |
669 | 0 | session->SetPeerAddress(addr); |
670 | 0 | } |
671 | 0 | return Loop::Continue; |
672 | 0 | }); |
673 | 0 | } |
674 | | |
675 | | Optional<SessionHandle> SessionManager::AllocateSession(SecureSession::Type secureSessionType, |
676 | | const ScopedNodeId & sessionEvictionHint) |
677 | 2.73k | { |
678 | 2.73k | VerifyOrReturnValue(mState == State::kInitialized, NullOptional); |
679 | 2.73k | return mSecureSessions.CreateNewSecureSession(secureSessionType, sessionEvictionHint); |
680 | 2.73k | } |
681 | | |
682 | | CHIP_ERROR SessionManager::InjectPaseSessionWithTestKey(SessionHolder & sessionHolder, uint16_t localSessionId, NodeId peerNodeId, |
683 | | uint16_t peerSessionId, FabricIndex fabric, |
684 | | const Transport::PeerAddress & peerAddress, CryptoContext::SessionRole role) |
685 | 4 | { |
686 | 4 | NodeId localNodeId = kUndefinedNodeId; |
687 | 4 | Optional<SessionHandle> session = mSecureSessions.CreateNewSecureSessionForTest( |
688 | 4 | chip::Transport::SecureSession::Type::kPASE, localSessionId, localNodeId, peerNodeId, CATValues{}, peerSessionId, fabric, |
689 | 4 | GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); |
690 | 4 | VerifyOrReturnError(session.HasValue(), CHIP_ERROR_NO_MEMORY); |
691 | 4 | SecureSession * secureSession = session.Value()->AsSecureSession(); |
692 | 4 | secureSession->SetPeerAddress(peerAddress); |
693 | | |
694 | 4 | size_t secretLen = CHIP_CONFIG_TEST_SHARED_SECRET_LENGTH; |
695 | 4 | ByteSpan secret(reinterpret_cast<const uint8_t *>(CHIP_CONFIG_TEST_SHARED_SECRET_VALUE), secretLen); |
696 | 4 | ReturnErrorOnFailure(secureSession->GetCryptoContext().InitFromSecret( |
697 | 4 | *mSessionKeystore, secret, ByteSpan(), CryptoContext::SessionInfoType::kSessionEstablishment, role)); |
698 | 4 | secureSession->GetSessionMessageCounter().GetPeerMessageCounter().SetCounter(Transport::PeerMessageCounter::kInitialSyncValue); |
699 | 4 | sessionHolder.Grab(session.Value()); |
700 | 4 | return CHIP_NO_ERROR; |
701 | 4 | } |
702 | | |
703 | | CHIP_ERROR SessionManager::InjectCaseSessionWithTestKey(SessionHolder & sessionHolder, uint16_t localSessionId, |
704 | | uint16_t peerSessionId, NodeId localNodeId, NodeId peerNodeId, |
705 | | FabricIndex fabric, const Transport::PeerAddress & peerAddress, |
706 | | CryptoContext::SessionRole role, const CATValues & cats) |
707 | 0 | { |
708 | 0 | Optional<SessionHandle> session = mSecureSessions.CreateNewSecureSessionForTest( |
709 | 0 | chip::Transport::SecureSession::Type::kCASE, localSessionId, localNodeId, peerNodeId, cats, peerSessionId, fabric, |
710 | 0 | GetLocalMRPConfig().ValueOr(GetDefaultMRPConfig())); |
711 | 0 | VerifyOrReturnError(session.HasValue(), CHIP_ERROR_NO_MEMORY); |
712 | 0 | SecureSession * secureSession = session.Value()->AsSecureSession(); |
713 | 0 | secureSession->SetPeerAddress(peerAddress); |
714 | |
|
715 | 0 | size_t secretLen = CHIP_CONFIG_TEST_SHARED_SECRET_LENGTH; |
716 | 0 | ByteSpan secret(reinterpret_cast<const uint8_t *>(CHIP_CONFIG_TEST_SHARED_SECRET_VALUE), secretLen); |
717 | 0 | ReturnErrorOnFailure(secureSession->GetCryptoContext().InitFromSecret( |
718 | 0 | *mSessionKeystore, secret, ByteSpan(), CryptoContext::SessionInfoType::kSessionEstablishment, role)); |
719 | 0 | secureSession->GetSessionMessageCounter().GetPeerMessageCounter().SetCounter(Transport::PeerMessageCounter::kInitialSyncValue); |
720 | 0 | sessionHolder.Grab(session.Value()); |
721 | 0 | return CHIP_NO_ERROR; |
722 | 0 | } |
723 | | |
724 | | void SessionManager::OnMessageReceived(const PeerAddress & peerAddress, System::PacketBufferHandle && msg, |
725 | | Transport::MessageTransportContext * ctxt) |
726 | 8.67k | { |
727 | 8.67k | PacketHeader partialPacketHeader; |
728 | | |
729 | 8.67k | CHIP_ERROR err = partialPacketHeader.DecodeFixed(msg); |
730 | 8.67k | if (err != CHIP_NO_ERROR) |
731 | 33 | { |
732 | 33 | ChipLogError(Inet, "Failed to decode packet header: %" CHIP_ERROR_FORMAT, err.Format()); |
733 | 33 | return; |
734 | 33 | } |
735 | | |
736 | 8.63k | if (partialPacketHeader.IsEncrypted()) |
737 | 3.19k | { |
738 | 3.19k | if (partialPacketHeader.IsGroupSession()) |
739 | 3.18k | { |
740 | 3.18k | SecureGroupMessageDispatch(partialPacketHeader, peerAddress, std::move(msg)); |
741 | 3.18k | } |
742 | 13 | else |
743 | 13 | { |
744 | 13 | SecureUnicastMessageDispatch(partialPacketHeader, peerAddress, std::move(msg), ctxt); |
745 | 13 | } |
746 | 3.19k | } |
747 | 5.44k | else |
748 | 5.44k | { |
749 | 5.44k | UnauthenticatedMessageDispatch(partialPacketHeader, peerAddress, std::move(msg), ctxt); |
750 | 5.44k | } |
751 | 8.63k | } |
752 | | |
753 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
754 | | void SessionManager::HandleConnectionReceived(Transport::ActiveTCPConnectionState & conn) |
755 | 0 | { |
756 | 0 | char peerAddrBuf[chip::Transport::PeerAddress::kMaxToStringSize]; |
757 | |
|
758 | 0 | conn.mPeerAddr.ToString(peerAddrBuf); |
759 | 0 | ChipLogProgress(Inet, "Received TCP connection request from %s.", peerAddrBuf); |
760 | |
|
761 | 0 | Transport::AppTCPConnectionCallbackCtxt * appTCPConnCbCtxt = conn.mAppState; |
762 | 0 | if (appTCPConnCbCtxt != nullptr && appTCPConnCbCtxt->connReceivedCb != nullptr) |
763 | 0 | { |
764 | 0 | appTCPConnCbCtxt->connReceivedCb(conn); |
765 | 0 | } |
766 | 0 | } |
767 | | |
768 | | void SessionManager::HandleConnectionAttemptComplete(Transport::ActiveTCPConnectionHandle & conn, CHIP_ERROR conErr) |
769 | 0 | { |
770 | 0 | VerifyOrReturn(!conn.IsNull()); |
771 | | |
772 | 0 | Transport::AppTCPConnectionCallbackCtxt * appTCPConnCbCtxt = conn->mAppState; |
773 | 0 | bool callbackHandled = false; |
774 | 0 | if (appTCPConnCbCtxt != nullptr && appTCPConnCbCtxt->connCompleteCb != nullptr) |
775 | 0 | { |
776 | 0 | appTCPConnCbCtxt->connCompleteCb(conn, conErr); |
777 | 0 | callbackHandled = true; |
778 | 0 | } |
779 | |
|
780 | 0 | if ((mConnDelegate == nullptr || !mConnDelegate->OnTCPConnectionAttemptComplete(conn, conErr)) && !callbackHandled) |
781 | 0 | { |
782 | 0 | char peerAddrBuf[chip::Transport::PeerAddress::kMaxToStringSize]; |
783 | 0 | conn->mPeerAddr.ToString(peerAddrBuf); |
784 | |
|
785 | 0 | ChipLogProgress(Inet, "TCP Connection established with peer %s, but no registered handler.", peerAddrBuf); |
786 | 0 | } |
787 | 0 | } |
788 | | |
789 | | void SessionManager::HandleConnectionClosed(Transport::ActiveTCPConnectionState & conn, CHIP_ERROR conErr) |
790 | 0 | { |
791 | 0 | Transport::AppTCPConnectionCallbackCtxt * appTCPConnCbCtxt = conn.mAppState; |
792 | 0 | if (appTCPConnCbCtxt != nullptr && appTCPConnCbCtxt->connClosedCb != nullptr) |
793 | 0 | { |
794 | 0 | appTCPConnCbCtxt->connClosedCb(conn, conErr); |
795 | 0 | } |
796 | 0 | MarkSecureSessionOverTCPForEviction(conn, conErr); |
797 | 0 | mUnauthenticatedSessions.MarkSessionOverTCPForEviction(conn); |
798 | 0 | } |
799 | | |
800 | | CHIP_ERROR SessionManager::TCPConnect(const PeerAddress & peerAddress, Transport::AppTCPConnectionCallbackCtxt * appState, |
801 | | Transport::ActiveTCPConnectionHandle & peerConnState) |
802 | 0 | { |
803 | 0 | char peerAddrBuf[chip::Transport::PeerAddress::kMaxToStringSize]; |
804 | 0 | peerAddress.ToString(peerAddrBuf); |
805 | 0 | if (mTransportMgr != nullptr) |
806 | 0 | { |
807 | 0 | ChipLogProgress(Inet, "Connecting over TCP with peer at %s.", peerAddrBuf); |
808 | 0 | return mTransportMgr->TCPConnect(peerAddress, appState, peerConnState); |
809 | 0 | } |
810 | | |
811 | 0 | ChipLogError(Inet, "The transport manager is not initialized. Unable to connect to peer at %s.", peerAddrBuf); |
812 | |
|
813 | 0 | return CHIP_ERROR_INCORRECT_STATE; |
814 | 0 | } |
815 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
816 | | |
817 | | void SessionManager::UnauthenticatedMessageDispatch(const PacketHeader & partialPacketHeader, |
818 | | const Transport::PeerAddress & peerAddress, System::PacketBufferHandle && msg, |
819 | | Transport::MessageTransportContext * ctxt) |
820 | 5.44k | { |
821 | 5.44k | MATTER_TRACE_SCOPE("Unauthenticated Message Dispatch", "SessionManager"); |
822 | | |
823 | 5.44k | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
824 | 5.44k | if (peerAddress.GetTransportType() == Transport::Type::kTcp && ctxt->conn.IsNull()) |
825 | 0 | { |
826 | 0 | ChipLogError(Inet, "Connection object is missing for received message."); |
827 | 0 | return; |
828 | 0 | } |
829 | 5.44k | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
830 | | |
831 | | // Drop unsecured messages with privacy enabled. |
832 | 5.44k | if (partialPacketHeader.HasPrivacyFlag()) |
833 | 2 | { |
834 | 2 | ChipLogError(Inet, "Dropping unauthenticated message with privacy flag set"); |
835 | 2 | return; |
836 | 2 | } |
837 | | |
838 | | // Capture length before consuming headers. |
839 | 5.44k | [[maybe_unused]] size_t messageTotalSize = msg->TotalLength(); |
840 | | |
841 | 5.44k | PacketHeader packetHeader; |
842 | 5.44k | ReturnOnFailure(packetHeader.DecodeAndConsume(msg)); |
843 | | |
844 | 5.41k | Optional<NodeId> source = packetHeader.GetSourceNodeId(); |
845 | 5.41k | Optional<NodeId> destination = packetHeader.GetDestinationNodeId(); |
846 | | |
847 | 5.41k | if ((source.HasValue() && destination.HasValue()) || (!source.HasValue() && !destination.HasValue())) |
848 | 10 | { |
849 | 10 | ChipLogProgress(Inet, |
850 | 10 | "Received malformed unsecure packet with source 0x" ChipLogFormatX64 " destination 0x" ChipLogFormatX64, |
851 | 10 | ChipLogValueX64(source.ValueOr(kUndefinedNodeId)), ChipLogValueX64(destination.ValueOr(kUndefinedNodeId))); |
852 | 10 | return; // ephemeral node id is only assigned to the initiator, there should be one and only one node id exists. |
853 | 10 | } |
854 | | |
855 | 5.40k | Optional<SessionHandle> optionalSession; |
856 | 5.40k | if (source.HasValue()) |
857 | 2.91k | { |
858 | | // Assume peer is the initiator, we are the responder. |
859 | 2.91k | optionalSession = mUnauthenticatedSessions.FindOrAllocateResponder(source.Value(), GetDefaultMRPConfig(), peerAddress); |
860 | 2.91k | if (!optionalSession.HasValue()) |
861 | 0 | { |
862 | 0 | ChipLogError(Inet, "UnauthenticatedSession exhausted"); |
863 | 0 | return; |
864 | 0 | } |
865 | 2.91k | } |
866 | 2.48k | else |
867 | 2.48k | { |
868 | | // Assume peer is the responder, we are the initiator. |
869 | 2.48k | optionalSession = mUnauthenticatedSessions.FindInitiator(destination.Value(), peerAddress); |
870 | 2.48k | if (!optionalSession.HasValue()) |
871 | 1 | { |
872 | 1 | ChipLogProgress(Inet, "Received unknown unsecure packet for initiator 0x" ChipLogFormatX64, |
873 | 1 | ChipLogValueX64(destination.Value())); |
874 | 1 | return; |
875 | 1 | } |
876 | 2.48k | } |
877 | | |
878 | 5.40k | const SessionHandle & session = optionalSession.Value(); |
879 | 5.40k | Transport::UnauthenticatedSession * unsecuredSession = session->AsUnauthenticatedSession(); |
880 | 5.40k | Transport::PeerAddress mutablePeerAddress = peerAddress; |
881 | 5.40k | CorrectPeerAddressInterfaceID(mutablePeerAddress); |
882 | 5.40k | unsecuredSession->SetPeerAddress(mutablePeerAddress); |
883 | 5.40k | SessionMessageDelegate::DuplicateMessage isDuplicate = SessionMessageDelegate::DuplicateMessage::No; |
884 | 5.40k | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
885 | | // Associate the unauthenticated session with the connection, if not done already. |
886 | 5.40k | if (peerAddress.GetTransportType() == Transport::Type::kTcp) |
887 | 0 | { |
888 | 0 | Transport::ActiveTCPConnectionHandle sessionConn = unsecuredSession->GetTCPConnection(); |
889 | 0 | if (sessionConn.IsNull()) |
890 | 0 | { |
891 | 0 | unsecuredSession->SetTCPConnection(ctxt->conn); |
892 | 0 | } |
893 | 0 | else |
894 | 0 | { |
895 | 0 | if (sessionConn != ctxt->conn) |
896 | 0 | { |
897 | 0 | ChipLogError(Inet, "Unauthenticated data received over TCP connection %p instead of %p. Dropping it!", |
898 | 0 | static_cast<const void *>(ctxt->conn), static_cast<const void *>(sessionConn)); |
899 | 0 | return; |
900 | 0 | } |
901 | 0 | } |
902 | 0 | } |
903 | 5.40k | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
904 | | |
905 | 5.40k | unsecuredSession->MarkActiveRx(); |
906 | | |
907 | 5.40k | PayloadHeader payloadHeader; |
908 | 5.40k | ReturnOnFailure(payloadHeader.DecodeAndConsume(msg)); |
909 | | |
910 | | // Verify message counter |
911 | 5.37k | CHIP_ERROR err = unsecuredSession->GetPeerMessageCounter().VerifyUnencrypted(packetHeader.GetMessageCounter()); |
912 | 5.37k | if (err == CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED) |
913 | 0 | { |
914 | 0 | ChipLogDetail(Inet, |
915 | 0 | "Received a duplicate message with MessageCounter:" ChipLogFormatMessageCounter |
916 | 0 | " on exchange " ChipLogFormatExchangeId, |
917 | 0 | packetHeader.GetMessageCounter(), ChipLogValueExchangeIdFromReceivedHeader(payloadHeader)); |
918 | 0 | isDuplicate = SessionMessageDelegate::DuplicateMessage::Yes; |
919 | 0 | err = CHIP_NO_ERROR; |
920 | 0 | } |
921 | 5.37k | else |
922 | 5.37k | { |
923 | | // VerifyUnencrypted always returns one of CHIP_NO_ERROR or |
924 | | // CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED. |
925 | 5.37k | unsecuredSession->GetPeerMessageCounter().CommitUnencrypted(packetHeader.GetMessageCounter()); |
926 | 5.37k | } |
927 | 5.37k | if (mCB != nullptr) |
928 | 5.37k | { |
929 | 5.37k | MATTER_LOG_MESSAGE_RECEIVED(chip::Tracing::IncomingMessageType::kUnauthenticated, &payloadHeader, &packetHeader, |
930 | 5.37k | unsecuredSession, &peerAddress, chip::ByteSpan(msg->Start(), msg->TotalLength()), |
931 | 5.37k | messageTotalSize); |
932 | | |
933 | 5.37k | CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, unsecuredSession, peerAddress, msg->Start(), msg->TotalLength()); |
934 | 5.37k | CountMessagesReceived(session, payloadHeader); |
935 | 5.37k | mCB->OnMessageReceived(packetHeader, payloadHeader, session, isDuplicate, std::move(msg)); |
936 | 5.37k | } |
937 | 0 | else |
938 | 0 | { |
939 | 0 | ChipLogError(Inet, "Received UNSECURED message was not processed."); |
940 | 0 | } |
941 | 5.37k | } |
942 | | |
943 | | void SessionManager::SecureUnicastMessageDispatch(const PacketHeader & partialPacketHeader, |
944 | | const Transport::PeerAddress & peerAddress, System::PacketBufferHandle && msg, |
945 | | Transport::MessageTransportContext * ctxt) |
946 | 13 | { |
947 | 13 | MATTER_TRACE_SCOPE("Secure Unicast Message Dispatch", "SessionManager"); |
948 | | |
949 | 13 | CHIP_ERROR err = CHIP_NO_ERROR; |
950 | | |
951 | 13 | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
952 | 13 | if (peerAddress.GetTransportType() == Transport::Type::kTcp && ctxt->conn.IsNull()) |
953 | 0 | { |
954 | 0 | ChipLogError(Inet, "Connection object is missing for received message."); |
955 | 0 | return; |
956 | 0 | } |
957 | 13 | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
958 | | |
959 | 13 | Optional<SessionHandle> session = mSecureSessions.FindSecureSessionByLocalKey(partialPacketHeader.GetSessionId()); |
960 | 13 | if (!session.HasValue()) |
961 | 13 | { |
962 | 13 | ChipLogError(Inet, "Data received on an unknown session (LSID=%d). Dropping it!", partialPacketHeader.GetSessionId()); |
963 | 13 | return; |
964 | 13 | } |
965 | | |
966 | 0 | Transport::SecureSession * secureSession = session.Value()->AsSecureSession(); |
967 | |
|
968 | 0 | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
969 | | // Associate the secure session with the connection, if not done already. |
970 | 0 | if (peerAddress.GetTransportType() == Transport::Type::kTcp) |
971 | 0 | { |
972 | 0 | auto sessionConn = secureSession->GetTCPConnection(); |
973 | 0 | if (sessionConn.IsNull()) |
974 | 0 | { |
975 | 0 | secureSession->SetTCPConnection(ctxt->conn); |
976 | 0 | } |
977 | 0 | else |
978 | 0 | { |
979 | 0 | if (sessionConn != ctxt->conn) |
980 | 0 | { |
981 | 0 | ChipLogError(Inet, "Unicast data received over TCP connection %p instead of %p. Dropping it!", |
982 | 0 | static_cast<const void *>(ctxt->conn), static_cast<const void *>(sessionConn)); |
983 | 0 | return; |
984 | 0 | } |
985 | 0 | } |
986 | 0 | } |
987 | 0 | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
988 | | // Capture length before consuming headers. |
989 | 0 | [[maybe_unused]] size_t messageTotalSize = msg->TotalLength(); |
990 | |
|
991 | 0 | PayloadHeader payloadHeader; |
992 | | |
993 | | // Drop secure unicast messages with privacy enabled. |
994 | 0 | if (partialPacketHeader.HasPrivacyFlag()) |
995 | 0 | { |
996 | 0 | ChipLogError(Inet, "Dropping secure unicast message with privacy flag set"); |
997 | 0 | return; |
998 | 0 | } |
999 | | |
1000 | 0 | PacketHeader packetHeader; |
1001 | 0 | ReturnOnFailure(packetHeader.DecodeAndConsume(msg)); |
1002 | | |
1003 | 0 | SessionMessageDelegate::DuplicateMessage isDuplicate = SessionMessageDelegate::DuplicateMessage::No; |
1004 | |
|
1005 | 0 | if (msg.IsNull()) |
1006 | 0 | { |
1007 | 0 | ChipLogError(Inet, "Secure transport received Unicast NULL packet, discarding"); |
1008 | 0 | return; |
1009 | 0 | } |
1010 | | |
1011 | | // We need to allow through messages even on sessions that are pending |
1012 | | // evictions, because for some cases (UpdateNOC, RemoveFabric, etc) there |
1013 | | // can be a single exchange alive on the session waiting for a MRP ack, and |
1014 | | // we need to make sure to send the ack through. The exchange manager is |
1015 | | // responsible for ensuring that such messages do not lead to new exchange |
1016 | | // creation. |
1017 | 0 | if (!secureSession->IsDefunct() && !secureSession->IsActiveSession() && !secureSession->IsPendingEviction()) |
1018 | 0 | { |
1019 | 0 | ChipLogError(Inet, "Secure transport received message on a session in an invalid state (state = '%s')", |
1020 | 0 | secureSession->GetStateStr()); |
1021 | 0 | return; |
1022 | 0 | } |
1023 | | |
1024 | | // Decrypt and verify the message before message counter verification or any further processing. |
1025 | 0 | CryptoContext::NonceStorage nonce; |
1026 | | // PASE Sessions use the undefined node ID of all zeroes, since there is no node ID to use |
1027 | | // and the key is short-lived and always different for each PASE session. |
1028 | 0 | CHIP_ERROR nonceResult = CryptoContext::BuildNonce( |
1029 | 0 | nonce, packetHeader.GetSecurityFlags(), packetHeader.GetMessageCounter(), |
1030 | 0 | secureSession->GetSecureSessionType() == SecureSession::Type::kCASE ? secureSession->GetPeerNodeId() : kUndefinedNodeId); |
1031 | 0 | if ((nonceResult != CHIP_NO_ERROR) || |
1032 | 0 | SecureMessageCodec::Decrypt(secureSession->GetCryptoContext(), nonce, payloadHeader, packetHeader, msg) != CHIP_NO_ERROR) |
1033 | 0 | { |
1034 | 0 | ChipLogError(Inet, "Secure transport received message, but failed to decode/authenticate it, discarding"); |
1035 | 0 | return; |
1036 | 0 | } |
1037 | | |
1038 | 0 | err = |
1039 | 0 | secureSession->GetSessionMessageCounter().GetPeerMessageCounter().VerifyEncryptedUnicast(packetHeader.GetMessageCounter()); |
1040 | 0 | if (err == CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED) |
1041 | 0 | { |
1042 | 0 | ChipLogDetail(Inet, |
1043 | 0 | "Received a duplicate message with MessageCounter:" ChipLogFormatMessageCounter |
1044 | 0 | " on exchange " ChipLogFormatExchangeId, |
1045 | 0 | packetHeader.GetMessageCounter(), ChipLogValueExchangeIdFromReceivedHeader(payloadHeader)); |
1046 | 0 | isDuplicate = SessionMessageDelegate::DuplicateMessage::Yes; |
1047 | 0 | err = CHIP_NO_ERROR; |
1048 | 0 | } |
1049 | 0 | if (err != CHIP_NO_ERROR) |
1050 | 0 | { |
1051 | 0 | ChipLogError(Inet, "Message counter verify failed, err = %" CHIP_ERROR_FORMAT, err.Format()); |
1052 | 0 | return; |
1053 | 0 | } |
1054 | | |
1055 | 0 | secureSession->MarkActiveRx(); |
1056 | |
|
1057 | 0 | if (isDuplicate == SessionMessageDelegate::DuplicateMessage::Yes && !payloadHeader.NeedsAck()) |
1058 | 0 | { |
1059 | | // If it's a duplicate message, but doesn't require an ack, let's drop it right here to save CPU |
1060 | | // cycles on further message processing. |
1061 | 0 | return; |
1062 | 0 | } |
1063 | | |
1064 | 0 | if (isDuplicate == SessionMessageDelegate::DuplicateMessage::No) |
1065 | 0 | { |
1066 | 0 | secureSession->GetSessionMessageCounter().GetPeerMessageCounter().CommitEncryptedUnicast(packetHeader.GetMessageCounter()); |
1067 | | |
1068 | | // Only a message with a new message counter may change the peer address: old messages can be |
1069 | | // captured and resent by anyone, and would otherwise let a third party choose where we send |
1070 | | // this session's traffic. If a peer changes its address, a retransmit from the new address |
1071 | | // therefore does not update it; its next new message does. |
1072 | 0 | Transport::PeerAddress mutablePeerAddress = peerAddress; |
1073 | 0 | CorrectPeerAddressInterfaceID(mutablePeerAddress); |
1074 | 0 | if (secureSession->GetPeerAddress() != mutablePeerAddress) |
1075 | 0 | { |
1076 | 0 | secureSession->SetPeerAddress(mutablePeerAddress); |
1077 | 0 | } |
1078 | 0 | } |
1079 | |
|
1080 | 0 | if (mCB != nullptr) |
1081 | 0 | { |
1082 | 0 | MATTER_LOG_MESSAGE_RECEIVED(chip::Tracing::IncomingMessageType::kSecureUnicast, &payloadHeader, &packetHeader, |
1083 | 0 | secureSession, &peerAddress, chip::ByteSpan(msg->Start(), msg->TotalLength()), |
1084 | 0 | messageTotalSize); |
1085 | 0 | CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeader, secureSession, peerAddress, msg->Start(), msg->TotalLength()); |
1086 | | |
1087 | | // Always recompute whether a message is for a commissioning session based on the latest knowledge of |
1088 | | // the fabric table. |
1089 | 0 | if (secureSession->IsCASESession()) |
1090 | 0 | { |
1091 | 0 | secureSession->SetCaseCommissioningSessionStatus(secureSession->GetFabricIndex() == |
1092 | 0 | mFabricTable->GetPendingNewFabricIndex()); |
1093 | 0 | } |
1094 | |
|
1095 | 0 | CountMessagesReceived(session.Value(), payloadHeader); |
1096 | 0 | mCB->OnMessageReceived(packetHeader, payloadHeader, session.Value(), isDuplicate, std::move(msg)); |
1097 | 0 | } |
1098 | 0 | else |
1099 | 0 | { |
1100 | 0 | ChipLogError(Inet, "Received SECURED message was not processed."); |
1101 | 0 | } |
1102 | 0 | } |
1103 | | |
1104 | | /** |
1105 | | * Helper function to implement a single attempt to decrypt a groupcast message |
1106 | | * using the given group key and privacy setting. |
1107 | | * |
1108 | | * @param[in] partialPacketHeader The partial packet header with non-obfuscated message fields (result of calling DecodeFixed). |
1109 | | * @param[out] packetHeaderCopy A copy of the packet header, to be filled with privacy decrypted fields |
1110 | | * @param[out] payloadHeader The payload header of the decrypted message |
1111 | | * @param[in] applyPrivacy Whether to apply privacy deobfuscation |
1112 | | * @param[out] msgCopy A copy of the message, to be filled with the decrypted message |
1113 | | * @param[in] mac The MAC of the message |
1114 | | * @param[in] groupContext The group context to use for decryption key material |
1115 | | * |
1116 | | * @return true if the message was decrypted successfully |
1117 | | * @return false if the message could not be decrypted |
1118 | | */ |
1119 | | static bool GroupKeyDecryptAttempt(const PacketHeader & partialPacketHeader, PacketHeader & packetHeaderCopy, |
1120 | | PayloadHeader & payloadHeader, bool applyPrivacy, System::PacketBufferHandle & msgCopy, |
1121 | | const MessageAuthenticationCode & mac, |
1122 | | const Credentials::GroupDataProvider::GroupSession & groupContext) |
1123 | 2.64k | { |
1124 | 2.64k | bool decrypted = false; |
1125 | 2.64k | CryptoContext context(groupContext.keyContext); |
1126 | | |
1127 | 2.64k | if (applyPrivacy) |
1128 | 2.45k | { |
1129 | | // Perform privacy deobfuscation, if applicable. |
1130 | 2.45k | uint8_t * privacyHeader = partialPacketHeader.PrivacyHeader(msgCopy->Start()); |
1131 | 2.45k | size_t privacyLength = partialPacketHeader.PrivacyHeaderLength(); |
1132 | | |
1133 | | // Bounds check: we decrypt in place a privacy header located inside the packet. |
1134 | | // Validate that we are still within the packet as the length is based on header flags. |
1135 | 2.45k | VerifyOrReturnValue((privacyHeader + privacyLength) <= (msgCopy->Start() + msgCopy->TotalLength()), false); |
1136 | | |
1137 | 2.44k | if (CHIP_NO_ERROR != context.PrivacyDecrypt(privacyHeader, privacyLength, privacyHeader, partialPacketHeader, mac)) |
1138 | 0 | { |
1139 | 0 | return false; |
1140 | 0 | } |
1141 | 2.44k | } |
1142 | | |
1143 | 2.63k | if (packetHeaderCopy.DecodeAndConsume(msgCopy) != CHIP_NO_ERROR) |
1144 | 11 | { |
1145 | 11 | ChipLogError(Inet, "Failed to decode Groupcast packet header. Discarding."); |
1146 | 11 | return false; |
1147 | 11 | } |
1148 | | |
1149 | | // Optimization to reduce number of decryption attempts |
1150 | 2.62k | GroupId groupId = packetHeaderCopy.GetDestinationGroupId().Value(); |
1151 | 2.62k | if (groupId != groupContext.group_id) |
1152 | 46 | { |
1153 | 46 | return false; |
1154 | 46 | } |
1155 | | |
1156 | 2.57k | CryptoContext::NonceStorage nonce; |
1157 | 2.57k | CHIP_ERROR nonceResult = |
1158 | 2.57k | CryptoContext::BuildNonce(nonce, packetHeaderCopy.GetSecurityFlags(), packetHeaderCopy.GetMessageCounter(), |
1159 | 2.57k | packetHeaderCopy.GetSourceNodeId().Value()); |
1160 | 2.57k | decrypted = (nonceResult == CHIP_NO_ERROR) && |
1161 | 2.57k | (CHIP_NO_ERROR == SecureMessageCodec::Decrypt(context, nonce, payloadHeader, packetHeaderCopy, msgCopy)); |
1162 | | |
1163 | 2.57k | return decrypted; |
1164 | 2.62k | } |
1165 | | |
1166 | | void SessionManager::SecureGroupMessageDispatch(const PacketHeader & partialPacketHeader, |
1167 | | const Transport::PeerAddress & peerAddress, System::PacketBufferHandle && msg) |
1168 | 3.18k | { |
1169 | 3.18k | MATTER_TRACE_SCOPE("Group Message Dispatch", "SessionManager"); |
1170 | | |
1171 | 3.18k | VerifyOrReturn(!msg->HasChainedBuffer()); |
1172 | | |
1173 | | // Capture length before consuming headers. |
1174 | 3.18k | [[maybe_unused]] size_t messageTotalSize = msg->TotalLength(); |
1175 | | |
1176 | 3.18k | PayloadHeader payloadHeader; |
1177 | 3.18k | PacketHeader packetHeaderCopy; /// Packet header decoded per group key, with privacy decrypted fields |
1178 | 3.18k | System::PacketBufferHandle msgCopy; |
1179 | 3.18k | Credentials::GroupDataProvider * groups = Credentials::GetGroupDataProvider(); |
1180 | 3.18k | VerifyOrReturn(nullptr != groups); |
1181 | 3.18k | CHIP_ERROR err = CHIP_NO_ERROR; |
1182 | | |
1183 | 3.18k | if (!partialPacketHeader.HasDestinationGroupId()) |
1184 | 4 | { |
1185 | 4 | return; // malformed packet |
1186 | 4 | } |
1187 | | |
1188 | | // Check if Message Header is valid first |
1189 | 3.17k | if (!(partialPacketHeader.IsValidMCSPMsg() || partialPacketHeader.IsValidGroupMsg())) |
1190 | 484 | { |
1191 | 484 | ChipLogError(Inet, "Invalid condition found in packet header"); |
1192 | 484 | return; |
1193 | 484 | } |
1194 | | |
1195 | | // Trial decryption with GroupDataProvider |
1196 | 2.69k | Credentials::GroupDataProvider::GroupSession groupContext; |
1197 | | |
1198 | 2.69k | AutoRelease<Credentials::GroupDataProvider::GroupSessionIterator> iter( |
1199 | 2.69k | groups->IterateGroupSessions(partialPacketHeader.GetSessionId())); |
1200 | | |
1201 | 2.69k | if (iter.IsNull()) |
1202 | 0 | { |
1203 | 0 | ChipLogError(Inet, "Failed to retrieve Groups iterator. Discarding everything"); |
1204 | 0 | return; |
1205 | 0 | } |
1206 | | |
1207 | | // Extract MIC from the end of the message. |
1208 | 2.69k | uint8_t * data = msg->Start(); |
1209 | 2.69k | size_t len = msg->TotalLength(); |
1210 | 2.69k | uint16_t footerLen = partialPacketHeader.MICTagLength(); |
1211 | 2.69k | VerifyOrReturn(footerLen <= len); |
1212 | | |
1213 | 2.67k | uint16_t taglen = 0; |
1214 | 2.67k | MessageAuthenticationCode mac; |
1215 | 2.67k | ReturnOnFailure(mac.Decode(partialPacketHeader, &data[len - footerLen], footerLen, &taglen)); |
1216 | 2.67k | VerifyOrReturn(taglen == footerLen); |
1217 | | |
1218 | | // Groupcast Testing |
1219 | 2.67k | auto & testing = chip::Groupcast::GetTesting(); |
1220 | | |
1221 | 2.67k | bool decrypted = false; |
1222 | 2.67k | bool hasAnyKeysForFabricUnderTest = false; |
1223 | 5.32k | while (!decrypted && iter->Next(groupContext)) |
1224 | 2.64k | { |
1225 | 2.64k | if (testing.IsEnabled() && testing.IsFabricUnderTest(groupContext.fabric_index)) |
1226 | 173 | { |
1227 | 173 | hasAnyKeysForFabricUnderTest = true; |
1228 | 173 | } |
1229 | 2.64k | CryptoContext context(groupContext.keyContext); |
1230 | 2.64k | msgCopy = msg.CloneData(); |
1231 | 2.64k | if (msgCopy.IsNull()) |
1232 | 0 | { |
1233 | 0 | ChipLogError(Inet, "Failed to clone Groupcast message buffer. Discarding."); |
1234 | 0 | return; |
1235 | 0 | } |
1236 | | |
1237 | 2.64k | bool privacy = partialPacketHeader.HasPrivacyFlag(); |
1238 | 2.64k | decrypted = |
1239 | 2.64k | GroupKeyDecryptAttempt(partialPacketHeader, packetHeaderCopy, payloadHeader, privacy, msgCopy, mac, groupContext); |
1240 | 2.64k | } |
1241 | 2.67k | iter.Release(); |
1242 | | |
1243 | 2.67k | if (testing.IsEnabled()) |
1244 | 186 | { |
1245 | 186 | if (decrypted) |
1246 | 130 | { |
1247 | | // We have a valid groupContext from the loop |
1248 | 130 | if (testing.IsFabricUnderTest(groupContext.fabric_index)) |
1249 | 130 | { |
1250 | 130 | testing.SetGroupID(packetHeaderCopy.GetDestinationGroupId().Value()); |
1251 | 130 | } |
1252 | 130 | } |
1253 | 56 | else |
1254 | 56 | { |
1255 | | // FAILURE CASE: No valid groupContext or decryption failed. This can happen |
1256 | | // for example, when there is an empty group key map. This means GroupSessions |
1257 | | // cannot be iterated over to populate groupContext, and the fabric index cannot be |
1258 | | // explicitly checked here. |
1259 | 56 | testing.SetTestResult(hasAnyKeysForFabricUnderTest ? chip::Groupcast::Testing::Result::kFailedAuth |
1260 | 56 | : chip::Groupcast::Testing::Result::kNoAvailableKey); |
1261 | 56 | testing.NotifyDelegate(); |
1262 | 56 | } |
1263 | 186 | } |
1264 | | |
1265 | 2.67k | if (!decrypted) |
1266 | 114 | { |
1267 | 114 | ChipLogError(Inet, "Failed to decrypt group message. Discarding everything"); |
1268 | 114 | return; |
1269 | 114 | } |
1270 | 2.56k | msg = std::move(msgCopy); |
1271 | | |
1272 | | // MCSP check |
1273 | 2.56k | if (packetHeaderCopy.IsValidMCSPMsg()) |
1274 | 0 | { |
1275 | | // TODO: When MCSP Msg, create Secure Session instead of a Group session |
1276 | | |
1277 | | // TODO |
1278 | | // if (packetHeaderCopy.GetDestinationNodeId().Value() == ThisDeviceNodeID) |
1279 | | // { |
1280 | | // MCSP processing.. |
1281 | | // } |
1282 | |
|
1283 | 0 | return; |
1284 | 0 | } |
1285 | | |
1286 | | // Group Messages should never send an Ack |
1287 | 2.56k | if (payloadHeader.NeedsAck()) |
1288 | 44 | { |
1289 | 44 | ChipLogError(Inet, "Unexpected ACK requested for group message"); |
1290 | 44 | return; |
1291 | 44 | } |
1292 | | |
1293 | | // Handle Group message counter here spec 4.7.3 |
1294 | | // spec 4.5.1.2 for msg counter |
1295 | 2.51k | Transport::PeerMessageCounter * counter = nullptr; |
1296 | | |
1297 | 2.51k | if (CHIP_NO_ERROR == |
1298 | 2.51k | gGroupPeerTable->FindOrAddPeer(groupContext.fabric_index, packetHeaderCopy.GetSourceNodeId().Value(), |
1299 | 2.51k | packetHeaderCopy.IsSecureSessionControlMsg(), counter)) |
1300 | 2.04k | { |
1301 | 2.04k | if (Credentials::GroupDataProvider::SecurityPolicy::kTrustFirst == groupContext.security_policy) |
1302 | 2.04k | { |
1303 | 2.04k | err = counter->VerifyOrTrustFirstGroup(packetHeaderCopy.GetMessageCounter()); |
1304 | 2.04k | } |
1305 | 0 | else |
1306 | 0 | { |
1307 | | |
1308 | | // TODO support cache and sync with MCSP. Issue #11689 |
1309 | 0 | ChipLogError(Inet, "Received Group Msg with key policy Cache and Sync, but MCSP is not implemented"); |
1310 | 0 | return; |
1311 | | |
1312 | | // cache and sync |
1313 | | // err = counter->VerifyGroup(packetHeaderCopy.GetMessageCounter()); |
1314 | 0 | } |
1315 | | |
1316 | 2.04k | if (err != CHIP_NO_ERROR) |
1317 | 243 | { |
1318 | 243 | if (testing.IsEnabled() && testing.IsFabricUnderTest(groupContext.fabric_index)) |
1319 | 46 | { |
1320 | 46 | if (err == CHIP_ERROR_DUPLICATE_MESSAGE_RECEIVED) |
1321 | 46 | { |
1322 | 46 | testing.SetTestResult(chip::Groupcast::Testing::Result::kMessageReplay); |
1323 | 46 | } |
1324 | 0 | else |
1325 | 0 | { |
1326 | 0 | testing.SetTestResult(chip::Groupcast::Testing::Result::kGeneralError); |
1327 | 0 | } |
1328 | 46 | testing.NotifyDelegate(); |
1329 | 46 | } |
1330 | | // Exit now, since Group Messages don't have acks or responses of any kind. |
1331 | 243 | ChipLogError(Inet, "Message counter verify failed, err = %" CHIP_ERROR_FORMAT, err.Format()); |
1332 | 243 | return; |
1333 | 243 | } |
1334 | 2.04k | } |
1335 | 475 | else |
1336 | 475 | { |
1337 | 475 | if (testing.IsEnabled() && testing.IsFabricUnderTest(groupContext.fabric_index)) |
1338 | 3 | { |
1339 | 3 | testing.SetTestResult(chip::Groupcast::Testing::Result::kGeneralError); |
1340 | 3 | testing.NotifyDelegate(); |
1341 | 3 | } |
1342 | 475 | ChipLogError(Inet, |
1343 | 475 | "Group Counter Tables full or invalid NodeId/FabricIndex after decryption of message, dropping everything"); |
1344 | 475 | return; |
1345 | 475 | } |
1346 | | |
1347 | 1.79k | counter->CommitGroup(packetHeaderCopy.GetMessageCounter()); |
1348 | | |
1349 | 1.79k | if (mCB != nullptr) |
1350 | 1.79k | { |
1351 | | // TODO : When MCSP is done, clean up session creation logic |
1352 | 1.79k | Transport::IncomingGroupSession groupSession(groupContext.group_id, groupContext.fabric_index, |
1353 | 1.79k | packetHeaderCopy.GetSourceNodeId().Value()); |
1354 | | |
1355 | 1.79k | MATTER_LOG_MESSAGE_RECEIVED(chip::Tracing::IncomingMessageType::kGroupMessage, &payloadHeader, &packetHeaderCopy, |
1356 | 1.79k | &groupSession, &peerAddress, chip::ByteSpan(msg->Start(), msg->TotalLength()), |
1357 | 1.79k | messageTotalSize); |
1358 | | |
1359 | 1.79k | CHIP_TRACE_MESSAGE_RECEIVED(payloadHeader, packetHeaderCopy, &groupSession, peerAddress, msg->Start(), msg->TotalLength()); |
1360 | 1.79k | SessionHandle session(groupSession); |
1361 | | |
1362 | 1.79k | CountMessagesReceived(session, payloadHeader); |
1363 | 1.79k | mCB->OnMessageReceived(packetHeaderCopy, payloadHeader, session, SessionMessageDelegate::DuplicateMessage::No, |
1364 | 1.79k | std::move(msg)); |
1365 | 1.79k | } |
1366 | 0 | else |
1367 | 0 | { |
1368 | 0 | ChipLogError(Inet, "Received GROUP message was not processed."); |
1369 | 0 | } |
1370 | 1.79k | } |
1371 | | |
1372 | | Optional<SessionHandle> SessionManager::FindSecureSessionForNode(ScopedNodeId peerNodeId, |
1373 | | const Optional<Transport::SecureSession::Type> & type, |
1374 | | TransportPayloadCapability transportPayloadCapability) |
1375 | 0 | { |
1376 | 0 | SecureSession * mrpSession = nullptr; |
1377 | 0 | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
1378 | 0 | SecureSession * tcpSession = nullptr; |
1379 | 0 | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
1380 | |
|
1381 | 0 | mSecureSessions.ForEachSession([&peerNodeId, &type, &mrpSession, |
1382 | 0 | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
1383 | 0 | &tcpSession, |
1384 | 0 | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
1385 | 0 | &transportPayloadCapability](auto session) { |
1386 | 0 | if (session->IsActiveSession() && session->GetPeer() == peerNodeId && |
1387 | 0 | (!type.HasValue() || type.Value() == session->GetSecureSessionType())) |
1388 | 0 | { |
1389 | 0 | if (transportPayloadCapability == TransportPayloadCapability::kMRPOrTCPCompatiblePayload || |
1390 | 0 | transportPayloadCapability == TransportPayloadCapability::kLargePayload) |
1391 | 0 | { |
1392 | 0 | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
1393 | | // Set up a TCP transport based session as standby |
1394 | 0 | if ((tcpSession == nullptr || tcpSession->GetLastPeerActivityTime() < session->GetLastPeerActivityTime()) && |
1395 | 0 | !session->GetTCPConnection().IsNull()) |
1396 | 0 | { |
1397 | 0 | tcpSession = session; |
1398 | 0 | } |
1399 | 0 | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
1400 | 0 | } |
1401 | |
|
1402 | 0 | if ((mrpSession == nullptr) || (mrpSession->GetLastPeerActivityTime() < session->GetLastPeerActivityTime())) |
1403 | 0 | { |
1404 | 0 | mrpSession = session; |
1405 | 0 | } |
1406 | 0 | } |
1407 | |
|
1408 | 0 | return Loop::Continue; |
1409 | 0 | }); |
1410 | |
|
1411 | 0 | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
1412 | 0 | if (transportPayloadCapability == TransportPayloadCapability::kLargePayload) |
1413 | 0 | { |
1414 | 0 | return tcpSession != nullptr ? MakeOptional<SessionHandle>(*tcpSession) : Optional<SessionHandle>::Missing(); |
1415 | 0 | } |
1416 | | |
1417 | 0 | if (transportPayloadCapability == TransportPayloadCapability::kMRPOrTCPCompatiblePayload) |
1418 | 0 | { |
1419 | | // If MRP-based session is available, use it. |
1420 | 0 | if (mrpSession != nullptr) |
1421 | 0 | { |
1422 | 0 | return MakeOptional<SessionHandle>(*mrpSession); |
1423 | 0 | } |
1424 | | |
1425 | | // Otherwise, look for a tcp-based session |
1426 | 0 | if (tcpSession != nullptr) |
1427 | 0 | { |
1428 | 0 | return MakeOptional<SessionHandle>(*tcpSession); |
1429 | 0 | } |
1430 | | |
1431 | 0 | return Optional<SessionHandle>::Missing(); |
1432 | 0 | } |
1433 | 0 | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
1434 | | |
1435 | 0 | return mrpSession != nullptr ? MakeOptional<SessionHandle>(*mrpSession) : Optional<SessionHandle>::Missing(); |
1436 | 0 | } |
1437 | | |
1438 | | #if INET_CONFIG_ENABLE_TCP_ENDPOINT |
1439 | | void SessionManager::MarkSecureSessionOverTCPForEviction(Transport::ActiveTCPConnectionState & conn, CHIP_ERROR conErr) |
1440 | 0 | { |
1441 | | // Mark the corresponding secure sessions for eviction |
1442 | 0 | mSecureSessions.ForEachSession([&](auto session) { |
1443 | 0 | if (session->GetTCPConnection() == conn) |
1444 | 0 | { |
1445 | 0 | bool isActive = session->IsActiveSession(); |
1446 | |
|
1447 | 0 | if (isActive) |
1448 | 0 | { |
1449 | | // Notify the SessionConnection delegate of the connection |
1450 | | // closure before session eviction detaches holders and |
1451 | | // releases exchanges. |
1452 | 0 | if (mConnDelegate != nullptr) |
1453 | 0 | { |
1454 | 0 | SessionHandle handle(*session); |
1455 | 0 | mConnDelegate->OnTCPConnectionClosed(conn, handle, conErr); |
1456 | 0 | } |
1457 | 0 | } |
1458 | | |
1459 | | // Explicitly release the TCP connection handle to ensure the transport resource is reclaimed immediately. |
1460 | 0 | session->ReleaseTCPConnection(); |
1461 | | |
1462 | | // Mark session for eviction regardless of its current state (Active, Defunct, or Establishing). |
1463 | 0 | session->MarkForEviction(); |
1464 | 0 | } |
1465 | |
|
1466 | 0 | return Loop::Continue; |
1467 | 0 | }); |
1468 | 0 | } |
1469 | | #endif // INET_CONFIG_ENABLE_TCP_ENDPOINT |
1470 | | |
1471 | | /** |
1472 | | * Provides a means to get diagnostic information such as number of sessions. |
1473 | | */ |
1474 | | [[maybe_unused]] CHIP_ERROR SessionManager::ForEachSessionHandle(void * context, SessionHandleCallback lambda) |
1475 | 0 | { |
1476 | 0 | mSecureSessions.ForEachSession([&](auto session) { |
1477 | 0 | SessionHandle handle(*session); |
1478 | 0 | lambda(context, handle); |
1479 | 0 | return Loop::Continue; |
1480 | 0 | }); |
1481 | 0 | return CHIP_NO_ERROR; |
1482 | 0 | } |
1483 | | |
1484 | | // Session handle parameter included here for future counting usage. |
1485 | | void SessionManager::CountMessagesReceived(const SessionHandle &, const PayloadHeader & payloadHeader) |
1486 | 7.17k | { |
1487 | 7.17k | if (payloadHeader.GetProtocolID() == Protocols::InteractionModel::Id) |
1488 | 1.80k | { |
1489 | 1.80k | mMessageStats.interactionModelMessagesReceived++; |
1490 | 1.80k | } |
1491 | 7.17k | } |
1492 | | |
1493 | | // Session handle parameter included here for future counting usage. |
1494 | | void SessionManager::CountMessagesSent(const SessionHandle &, const PayloadHeader & payloadHeader) |
1495 | 5.47k | { |
1496 | 5.47k | if (payloadHeader.GetProtocolID() == Protocols::InteractionModel::Id) |
1497 | 149 | { |
1498 | 149 | mMessageStats.interactionModelMessagesSent++; |
1499 | 149 | } |
1500 | 5.47k | } |
1501 | | |
1502 | | } // namespace chip |