/src/open62541/src/ua_securechannel.c
Line | Count | Source |
1 | | /* This Source Code Form is subject to the terms of the Mozilla Public |
2 | | * License, v. 2.0. If a copy of the MPL was not distributed with this |
3 | | * file, You can obtain one at http://mozilla.org/MPL/2.0/. |
4 | | * |
5 | | * Copyright 2014-2020 (c) Fraunhofer IOSB (Author: Julius Pfrommer) |
6 | | * Copyright 2014, 2016-2017 (c) Florian Palm |
7 | | * Copyright 2015-2016 (c) Sten GrĂ¼ner |
8 | | * Copyright 2015 (c) Oleksiy Vasylyev |
9 | | * Copyright 2016 (c) TorbenD |
10 | | * Copyright 2017 (c) Stefan Profanter, fortiss GmbH |
11 | | * Copyright 2017-2018 (c) Mark Giraud, Fraunhofer IOSB |
12 | | * Copyright 2018-2019 (c) HMS Industrial Networks AB (Author: Jonas Green) |
13 | | * Copyright 2025 (c) o6 Automation GmbH (Author: Julius Pfrommer) |
14 | | * Copyright 2026 (c) o6 Automation GmbH (Author: Andreas Ebner) |
15 | | */ |
16 | | |
17 | | #include <open62541/types.h> |
18 | | #include <open62541/transport_generated.h> |
19 | | |
20 | | #include "ua_securechannel.h" |
21 | | #include "ua_types_encoding_binary.h" |
22 | | |
23 | 0 | #define UA_BITMASK_MESSAGETYPE 0x00ffffffu |
24 | 0 | #define UA_BITMASK_CHUNKTYPE 0xff000000u |
25 | | |
26 | | const UA_String UA_SECURITY_POLICY_NONE_URI = |
27 | | {47, (UA_Byte *)"http://opcfoundation.org/UA/SecurityPolicy#None"}; |
28 | | |
29 | | void |
30 | 2.60k | UA_SecureChannel_init(UA_SecureChannel *channel) { |
31 | | /* Normal linked lists are initialized by zeroing out */ |
32 | 2.60k | memset(channel, 0, sizeof(UA_SecureChannel)); |
33 | 2.60k | TAILQ_INIT(&channel->chunks); |
34 | 2.60k | } |
35 | | |
36 | | UA_StatusCode |
37 | | UA_SecureChannel_setSecurityPolicy(UA_SecureChannel *channel, UA_SecurityPolicy *sp, |
38 | 2.60k | const UA_ByteString *remoteCertificate) { |
39 | | /* Is a policy already configured? */ |
40 | 2.60k | UA_CHECK_ERROR(!channel->securityPolicy, return UA_STATUSCODE_BADINTERNALERROR, |
41 | 2.60k | sp->logger, UA_LOGCATEGORY_SECURITYPOLICY, |
42 | 2.60k | "Security policy already configured"); |
43 | | |
44 | | /* Create the context */ |
45 | 2.60k | UA_StatusCode res = sp->newChannelContext(sp, remoteCertificate, |
46 | 2.60k | &channel->channelContext); |
47 | 2.60k | res |= UA_ByteString_copy(remoteCertificate, &channel->remoteCertificate); |
48 | 2.60k | UA_CHECK_STATUS_ERROR(res, return res, sp->logger, UA_LOGCATEGORY_SECURITYPOLICY, |
49 | 2.60k | "Could not set up the SecureChannel context"); |
50 | | |
51 | | /* Compute the certificate thumbprint */ |
52 | 2.60k | UA_ByteString remoteCertificateThumbprint = |
53 | 2.60k | {20, channel->remoteCertificateThumbprint}; |
54 | 2.60k | res = sp->makeCertThumbprint(sp, &channel->remoteCertificate, |
55 | 2.60k | &remoteCertificateThumbprint); |
56 | 2.60k | UA_CHECK_STATUS_ERROR(res, return res, sp->logger, UA_LOGCATEGORY_SECURITYPOLICY, |
57 | 2.60k | "Could not create the certificate thumbprint"); |
58 | | |
59 | | /* Set the SecurityPolicy and cache the URI-derived properties (the policy |
60 | | * is fixed for the channel's lifetime). */ |
61 | 2.60k | channel->securityPolicy = sp; |
62 | 2.60k | channel->enhancedSecurity = UA_SecurityPolicy_isEnhancedSecurity(sp); |
63 | 2.60k | channel->legacySequenceNumbers = UA_SecurityPolicy_useLegacySequenceNumbers(sp); |
64 | | |
65 | | /* Set a temporary SecurityMode. The client sets the final SecurityMode |
66 | | * right after. The server only after fully decoding the OPN message in the |
67 | | * OpenSecureChannel service. But we need the SecurityMode for decoding. The |
68 | | * OPN message is always signed and encrypted when not #None. */ |
69 | 2.60k | if(sp->policyType == UA_SECURITYPOLICYTYPE_NONE) |
70 | 2.60k | channel->securityMode = UA_MESSAGESECURITYMODE_NONE; |
71 | 0 | else |
72 | 0 | channel->securityMode = UA_MESSAGESECURITYMODE_SIGNANDENCRYPT; |
73 | 2.60k | return UA_STATUSCODE_GOOD; |
74 | 2.60k | } |
75 | | |
76 | | /* The #None SecurityPolicy must use the NONE SecurityMode. All other |
77 | | * SecurityPolicies must not. */ |
78 | | UA_StatusCode |
79 | | UA_SecureChannel_setSecurityMode(UA_SecureChannel *channel, |
80 | 0 | UA_MessageSecurityMode securityMode) { |
81 | 0 | if(securityMode == UA_MESSAGESECURITYMODE_INVALID || |
82 | 0 | securityMode > UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) |
83 | 0 | return UA_STATUSCODE_BADSECURITYMODEREJECTED; |
84 | 0 | UA_SecurityPolicy *sp = channel->securityPolicy; |
85 | 0 | if(!sp) |
86 | 0 | return UA_STATUSCODE_BADSECURITYMODEREJECTED; |
87 | 0 | UA_Boolean isNonePolicy = (sp->policyType == UA_SECURITYPOLICYTYPE_NONE); |
88 | 0 | UA_Boolean isNoneMode = (securityMode == UA_MESSAGESECURITYMODE_NONE); |
89 | 0 | if(isNonePolicy != isNoneMode) |
90 | 0 | return UA_STATUSCODE_BADSECURITYMODEREJECTED; |
91 | 0 | channel->securityMode = securityMode; |
92 | 0 | return UA_STATUSCODE_GOOD; |
93 | 0 | } |
94 | | |
95 | | /* Hides some errors before sending them to a client according to the |
96 | | * standard. */ |
97 | | static void |
98 | 0 | hideErrors(UA_TcpErrorMessage *const error) { |
99 | 0 | switch(error->error) { |
100 | 0 | case UA_STATUSCODE_BADCERTIFICATEINVALID: |
101 | 0 | case UA_STATUSCODE_BADCERTIFICATECHAININCOMPLETE: |
102 | 0 | case UA_STATUSCODE_BADCERTIFICATEPOLICYCHECKFAILED: |
103 | 0 | case UA_STATUSCODE_BADCERTIFICATEUNTRUSTED: |
104 | 0 | case UA_STATUSCODE_BADCERTIFICATEREVOCATIONUNKNOWN: |
105 | 0 | case UA_STATUSCODE_BADCERTIFICATEISSUERREVOCATIONUNKNOWN: |
106 | 0 | case UA_STATUSCODE_BADCERTIFICATEREVOKED: |
107 | 0 | case UA_STATUSCODE_BADCERTIFICATEISSUERREVOKED: |
108 | 0 | case UA_STATUSCODE_BADCERTIFICATEISSUERUSENOTALLOWED: |
109 | 0 | error->error = UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
110 | 0 | error->reason = UA_STRING_NULL; |
111 | 0 | break; |
112 | | // TODO: Check if these are all cases that need to be covered. |
113 | 0 | default: |
114 | 0 | break; |
115 | 0 | } |
116 | 0 | } |
117 | | |
118 | | UA_Boolean |
119 | 0 | UA_SecureChannel_isConnected(UA_SecureChannel *channel) { |
120 | 0 | return (channel->state > UA_SECURECHANNELSTATE_CLOSED && |
121 | 0 | channel->state < UA_SECURECHANNELSTATE_CLOSING); |
122 | 0 | } |
123 | | |
124 | | void |
125 | 0 | UA_SecureChannel_sendERR(UA_SecureChannel *channel, UA_TcpErrorMessage *error) { |
126 | 0 | if(!UA_SecureChannel_isConnected(channel)) |
127 | 0 | return; |
128 | | |
129 | 0 | hideErrors(error); |
130 | |
|
131 | 0 | UA_TcpMessageHeader header; |
132 | 0 | header.messageTypeAndChunkType = UA_MESSAGETYPE_ERR + UA_CHUNKTYPE_FINAL; |
133 | | /* Header + ErrorMessage (error + reasonLength_field + length) */ |
134 | 0 | header.messageSize = 8 + (4 + 4 + (UA_UInt32)error->reason.length); |
135 | | |
136 | | /* Get the send buffer from the network layer */ |
137 | 0 | UA_ConnectionManager *cm = channel->connectionManager; |
138 | 0 | UA_ByteString msg = UA_BYTESTRING_NULL; |
139 | 0 | UA_StatusCode retval = cm->allocNetworkBuffer(cm, channel->connectionId, |
140 | 0 | &msg, header.messageSize); |
141 | 0 | if(retval != UA_STATUSCODE_GOOD) |
142 | 0 | return; |
143 | | |
144 | | /* Encode and send the response */ |
145 | 0 | UA_Byte *bufPos = msg.data; |
146 | 0 | const UA_Byte *bufEnd = &msg.data[msg.length]; |
147 | 0 | retval |= UA_encodeBinaryInternal(&header, |
148 | 0 | &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], |
149 | 0 | &bufPos, &bufEnd, NULL, NULL, NULL); |
150 | 0 | retval |= UA_encodeBinaryInternal(error, |
151 | 0 | &UA_TRANSPORT[UA_TRANSPORT_TCPERRORMESSAGE], |
152 | 0 | &bufPos, &bufEnd, NULL, NULL, NULL); |
153 | 0 | (void)retval; /* Encoding of these cannot fail */ |
154 | 0 | msg.length = header.messageSize; |
155 | 0 | cm->sendWithConnection(cm, channel->connectionId, &UA_KEYVALUEMAP_NULL, &msg); |
156 | 0 | } |
157 | | |
158 | | static void |
159 | 0 | UA_Chunk_delete(UA_Chunk *chunk) { |
160 | 0 | if(chunk->copied) |
161 | 0 | UA_ByteString_clear(&chunk->bytes); |
162 | 0 | UA_free(chunk); |
163 | 0 | } |
164 | | |
165 | | static void |
166 | 2.60k | deleteChunks(UA_SecureChannel *channel) { |
167 | 2.60k | UA_Chunk *chunk, *chunk_tmp; |
168 | 2.60k | TAILQ_FOREACH_SAFE(chunk, &channel->chunks, pointers, chunk_tmp) { |
169 | 0 | TAILQ_REMOVE(&channel->chunks, chunk, pointers); |
170 | 0 | UA_Chunk_delete(chunk); |
171 | 0 | } |
172 | 2.60k | channel->chunksCount = 0; |
173 | 2.60k | channel->chunksLength = 0; |
174 | 2.60k | } |
175 | | |
176 | | void |
177 | 2.60k | UA_SecureChannel_deleteBuffered(UA_SecureChannel *channel) { |
178 | 2.60k | deleteChunks(channel); |
179 | 2.60k | if(channel->unprocessedCopied) |
180 | 0 | UA_ByteString_clear(&channel->unprocessed); |
181 | 2.60k | } |
182 | | |
183 | | void |
184 | | UA_SecureChannel_shutdown(UA_SecureChannel *channel, |
185 | 0 | UA_ShutdownReason shutdownReason) { |
186 | | /* No open socket or already closing -> nothing to do */ |
187 | 0 | if(!UA_SecureChannel_isConnected(channel)) |
188 | 0 | return; |
189 | | |
190 | | /* Set the shutdown event for diagnostics */ |
191 | 0 | channel->shutdownReason = shutdownReason; |
192 | | |
193 | | /* Trigger the async closing of the connection */ |
194 | 0 | UA_ConnectionManager *cm = channel->connectionManager; |
195 | 0 | cm->closeConnection(cm, channel->connectionId); |
196 | 0 | channel->state = UA_SECURECHANNELSTATE_CLOSING; |
197 | 0 | } |
198 | | |
199 | | void |
200 | 2.60k | UA_SecureChannel_clear(UA_SecureChannel *channel) { |
201 | | /* No sessions must be attached to this any longer */ |
202 | 2.60k | UA_assert(channel->sessions == NULL); |
203 | | |
204 | | /* Delete the channel context for the security policy */ |
205 | 2.60k | UA_SecurityPolicy *sp = channel->securityPolicy; |
206 | 2.60k | if(sp) { |
207 | 2.60k | sp->deleteChannelContext(sp, channel->channelContext); |
208 | 2.60k | channel->securityPolicy = NULL; |
209 | 2.60k | channel->channelContext = NULL; |
210 | 2.60k | channel->enhancedSecurity = false; /* No policy => not enhanced */ |
211 | 2.60k | channel->legacySequenceNumbers = true; /* No policy => legacy */ |
212 | 2.60k | } |
213 | | |
214 | | /* Remove remaining delayed callback */ |
215 | 2.60k | if(channel->connectionManager && |
216 | 2.60k | channel->connectionManager->eventSource.eventLoop) { |
217 | 0 | UA_EventLoop *el = channel->connectionManager->eventSource.eventLoop; |
218 | 0 | el->removeDelayedCallback(el, &channel->unprocessedDelayed); |
219 | 0 | } |
220 | | |
221 | | /* The EventLoop connection is no longer valid */ |
222 | 2.60k | channel->connectionId = 0; |
223 | 2.60k | channel->connectionManager = NULL; |
224 | | |
225 | | /* Clean up the SecurityToken */ |
226 | 2.60k | UA_ChannelSecurityToken_clear(&channel->securityToken); |
227 | 2.60k | UA_ChannelSecurityToken_clear(&channel->altSecurityToken); |
228 | | |
229 | | /* Clean up certificate and nonces */ |
230 | 2.60k | UA_ByteString_clear(&channel->remoteCertificate); |
231 | 2.60k | UA_ByteString_clear(&channel->localNonce); |
232 | 2.60k | UA_ByteString_clear(&channel->remoteNonce); |
233 | | |
234 | | /* Clean up the v1.05.07 SecureChannel elements */ |
235 | 2.60k | UA_ByteString_clear(&channel->firstRequestSignature); |
236 | 2.60k | UA_ByteString_clear(&channel->currentIKM); |
237 | 2.60k | UA_ByteString_clear(&channel->channelThumbprint); |
238 | | |
239 | | /* Clean up endpointUrl and remoteAddress */ |
240 | 2.60k | UA_String_clear(&channel->endpointUrl); |
241 | 2.60k | UA_String_clear(&channel->remoteAddress); |
242 | | |
243 | | /* Delete remaining chunks */ |
244 | 2.60k | UA_SecureChannel_deleteBuffered(channel); |
245 | | |
246 | | /* Clean up namespace mapping */ |
247 | 2.60k | UA_NamespaceMapping_delete(channel->namespaceMapping); |
248 | 2.60k | channel->namespaceMapping = NULL; |
249 | | |
250 | | /* Reset the SecureChannel for reuse (in the client) */ |
251 | 2.60k | channel->securityMode = UA_MESSAGESECURITYMODE_INVALID; |
252 | 2.60k | channel->shutdownReason = UA_SHUTDOWNREASON_CLOSE; |
253 | 2.60k | memset(&channel->config, 0, sizeof(UA_ConnectionConfig)); |
254 | 2.60k | channel->receiveSequenceNumber = 0; |
255 | 2.60k | channel->sendSequenceNumber = 0; |
256 | | |
257 | | /* Set the state to closed */ |
258 | 2.60k | channel->state = UA_SECURECHANNELSTATE_CLOSED; |
259 | 2.60k | channel->renewState = UA_SECURECHANNELRENEWSTATE_NORMAL; |
260 | 2.60k | } |
261 | | |
262 | | UA_StatusCode |
263 | | UA_SecureChannel_processHELACK(UA_SecureChannel *channel, |
264 | 0 | const UA_TcpAcknowledgeMessage *remoteConfig) { |
265 | | /* The lowest common version is used by both sides */ |
266 | 0 | if(channel->config.protocolVersion > remoteConfig->protocolVersion) |
267 | 0 | channel->config.protocolVersion = remoteConfig->protocolVersion; |
268 | | |
269 | | /* Can we receive the max send size? */ |
270 | 0 | if(channel->config.sendBufferSize > remoteConfig->receiveBufferSize) |
271 | 0 | channel->config.sendBufferSize = remoteConfig->receiveBufferSize; |
272 | | |
273 | | /* Can we send the max receive size? */ |
274 | 0 | if(channel->config.recvBufferSize > remoteConfig->sendBufferSize) |
275 | 0 | channel->config.recvBufferSize = remoteConfig->sendBufferSize; |
276 | |
|
277 | 0 | channel->config.remoteMaxMessageSize = remoteConfig->maxMessageSize; |
278 | 0 | channel->config.remoteMaxChunkCount = remoteConfig->maxChunkCount; |
279 | | |
280 | | /* Chunks of at least 8192 bytes must be permissible. |
281 | | * See Part 6, Clause 6.7.1 */ |
282 | 0 | if(channel->config.recvBufferSize < 8192 || |
283 | 0 | channel->config.sendBufferSize < 8192 || |
284 | 0 | (channel->config.remoteMaxMessageSize != 0 && |
285 | 0 | channel->config.remoteMaxMessageSize < 8192)) |
286 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
287 | | |
288 | 0 | return UA_STATUSCODE_GOOD; |
289 | 0 | } |
290 | | |
291 | | /* Send an OPN message using asymmetric encryption. |
292 | | * Specification part 6, 6.7.4: The OpenSecureChannel Messages are signed and |
293 | | * encrypted if the SecurityMode is not None (even if the SecurityMode is |
294 | | * SignOnly). */ |
295 | | UA_StatusCode |
296 | | UA_SecureChannel_sendOPN(UA_SecureChannel *channel, |
297 | | UA_UInt32 requestId, const void *content, |
298 | 0 | const UA_DataType *contentType) { |
299 | 0 | if(!content || !contentType) |
300 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
301 | | |
302 | | /* The SecurityPolicy must be configured before sending OPN */ |
303 | 0 | const UA_SecurityPolicy *sp = channel->securityPolicy; |
304 | 0 | UA_CHECK_MEM(sp, return UA_STATUSCODE_BADINTERNALERROR); |
305 | | |
306 | | /* Check for a valid security mode */ |
307 | 0 | UA_assert(channel->securityMode > UA_MESSAGESECURITYMODE_INVALID && |
308 | 0 | channel->securityMode <= UA_MESSAGESECURITYMODE_SIGNANDENCRYPT); |
309 | | |
310 | | /* The #None SecurityPolicy must use the NONE MessageSecurityMode. |
311 | | * All other SecurityPolicies must not. */ |
312 | 0 | UA_assert((sp->policyType == UA_SECURITYPOLICYTYPE_NONE) == |
313 | 0 | (channel->securityMode == UA_MESSAGESECURITYMODE_NONE)); |
314 | | |
315 | | /* Can we use the connection manager? */ |
316 | 0 | UA_ConnectionManager *cm = channel->connectionManager; |
317 | 0 | if(!UA_SecureChannel_isConnected(channel)) |
318 | 0 | return UA_STATUSCODE_BADCONNECTIONCLOSED; |
319 | | |
320 | | /* Allocate the message buffer */ |
321 | 0 | UA_ByteString buf = UA_BYTESTRING_NULL; |
322 | 0 | UA_StatusCode res = cm->allocNetworkBuffer(cm, channel->connectionId, &buf, |
323 | 0 | channel->config.sendBufferSize); |
324 | 0 | UA_CHECK_STATUS(res, return res); |
325 | | |
326 | | /* Restrict buffer to the available space for the payload */ |
327 | 0 | UA_Byte *buf_pos = buf.data; |
328 | 0 | const UA_Byte *buf_end = &buf.data[buf.length]; |
329 | 0 | hideBytesAsym(channel, &buf_pos, &buf_end); |
330 | | |
331 | | /* Define variables here to pacify some compilers wrt goto */ |
332 | 0 | size_t securityHeaderLength, pre_sig_length, total_length, encryptedLength; |
333 | | |
334 | | /* Encode the message type and content */ |
335 | 0 | UA_EncodeBinaryOptions encOpts; |
336 | 0 | memset(&encOpts, 0, sizeof(UA_EncodeBinaryOptions)); |
337 | 0 | encOpts.namespaceMapping = channel->namespaceMapping; |
338 | 0 | res |= UA_NodeId_encodeBinary(&contentType->binaryEncodingId, &buf_pos, buf_end); |
339 | 0 | res |= UA_encodeBinaryInternal(content, contentType, &buf_pos, &buf_end, |
340 | 0 | &encOpts, NULL, NULL); |
341 | 0 | UA_CHECK_STATUS(res, goto error); |
342 | | |
343 | | /* Compute the header length */ |
344 | 0 | securityHeaderLength = calculateAsymAlgSecurityHeaderLength(channel); |
345 | | |
346 | | /* Add padding to the chunk */ |
347 | 0 | if(channel->securityMode != UA_MESSAGESECURITYMODE_NONE) |
348 | 0 | padChunk(channel, &sp->asymSignatureAlgorithm, &sp->asymEncryptionAlgorithm, |
349 | 0 | &buf.data[UA_SECURECHANNEL_CHANNELHEADER_LENGTH + securityHeaderLength], |
350 | 0 | &buf_pos); |
351 | | |
352 | | /* The total message length */ |
353 | 0 | pre_sig_length = (uintptr_t)buf_pos - (uintptr_t)buf.data; |
354 | 0 | total_length = pre_sig_length; |
355 | 0 | if(channel->securityMode != UA_MESSAGESECURITYMODE_NONE) |
356 | 0 | total_length += sp->asymSignatureAlgorithm. |
357 | 0 | getLocalSignatureSize(sp, channel->channelContext); |
358 | | |
359 | | /* The total message length is known here which is why we encode the headers |
360 | | * at this step and not earlier. */ |
361 | 0 | res = prependHeadersAsym(channel, buf.data, buf_end, total_length, |
362 | 0 | securityHeaderLength, requestId, &encryptedLength); |
363 | 0 | UA_CHECK_STATUS(res, goto error); |
364 | | |
365 | | /* Add the signature and encrypt the message */ |
366 | 0 | res = signAndEncryptAsym(channel, pre_sig_length, &buf, |
367 | 0 | securityHeaderLength, total_length); |
368 | 0 | UA_CHECK_STATUS(res, goto error); |
369 | | |
370 | | /* Send the message, the buffer is freed in the network layer */ |
371 | 0 | buf.length = encryptedLength; |
372 | 0 | return cm->sendWithConnection(cm, channel->connectionId, &UA_KEYVALUEMAP_NULL, &buf); |
373 | | |
374 | 0 | error: |
375 | 0 | cm->freeNetworkBuffer(cm, channel->connectionId, &buf); |
376 | 0 | return res; |
377 | 0 | } |
378 | | |
379 | | /* Will this chunk surpass the capacity of the SecureChannel for the message? */ |
380 | | static UA_StatusCode |
381 | 0 | adjustCheckMessageLimitsSym(UA_MessageContext *mc, size_t bodyLength) { |
382 | 0 | mc->messageSizeSoFar += bodyLength; |
383 | 0 | mc->chunksSoFar++; |
384 | |
|
385 | 0 | UA_SecureChannel *channel = mc->channel; |
386 | 0 | if(mc->messageSizeSoFar > channel->config.localMaxMessageSize && |
387 | 0 | channel->config.localMaxMessageSize != 0) |
388 | 0 | return UA_STATUSCODE_BADRESPONSETOOLARGE; |
389 | | |
390 | 0 | if(mc->chunksSoFar > channel->config.localMaxChunkCount && |
391 | 0 | channel->config.localMaxChunkCount != 0) |
392 | 0 | return UA_STATUSCODE_BADRESPONSETOOLARGE; |
393 | | |
394 | 0 | return UA_STATUSCODE_GOOD; |
395 | 0 | } |
396 | | |
397 | | static UA_StatusCode |
398 | 0 | encodeHeadersSym(UA_MessageContext *mc, size_t totalLength) { |
399 | 0 | UA_SecureChannel *channel = mc->channel; |
400 | 0 | UA_Byte *header_pos = mc->messageBuffer.data; |
401 | |
|
402 | 0 | UA_TcpMessageHeader header; |
403 | 0 | header.messageTypeAndChunkType = mc->messageType; |
404 | 0 | header.messageSize = (UA_UInt32)totalLength; |
405 | 0 | if(mc->final) |
406 | 0 | header.messageTypeAndChunkType += UA_CHUNKTYPE_FINAL; |
407 | 0 | else |
408 | 0 | header.messageTypeAndChunkType += UA_CHUNKTYPE_INTERMEDIATE; |
409 | |
|
410 | 0 | UA_SequenceHeader seqHeader; |
411 | 0 | seqHeader.requestId = mc->requestId; |
412 | 0 | seqHeader.sequenceNumber = UA_SecureChannel_nextSequenceNumber(channel); |
413 | |
|
414 | 0 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
415 | 0 | res |= UA_encodeBinaryInternal(&header, &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], |
416 | 0 | &header_pos, &mc->buf_end, NULL, NULL, NULL); |
417 | 0 | res |= UA_UInt32_encodeBinary(&channel->securityToken.channelId, |
418 | 0 | &header_pos, mc->buf_end); |
419 | 0 | res |= UA_UInt32_encodeBinary(&channel->securityToken.tokenId, |
420 | 0 | &header_pos, mc->buf_end); |
421 | 0 | res |= UA_encodeBinaryInternal(&seqHeader, &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], |
422 | 0 | &header_pos, &mc->buf_end, NULL, NULL, NULL); |
423 | 0 | return res; |
424 | 0 | } |
425 | | |
426 | | static UA_StatusCode |
427 | 0 | sendSymmetricChunk(UA_MessageContext *mc) { |
428 | 0 | UA_SecureChannel *channel = mc->channel; |
429 | 0 | const UA_SecurityPolicy *sp = channel->securityPolicy; |
430 | 0 | UA_ConnectionManager *cm = channel->connectionManager; |
431 | 0 | if(!UA_SecureChannel_isConnected(channel)) |
432 | 0 | return UA_STATUSCODE_BADCONNECTIONCLOSED; |
433 | | |
434 | | /* The size of the message payload */ |
435 | 0 | size_t bodyLength = (uintptr_t)mc->buf_pos - |
436 | 0 | (uintptr_t)&mc->messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH]; |
437 | | |
438 | | /* Early-declare variables so we can use a goto in the error case */ |
439 | 0 | size_t total_length = 0; |
440 | 0 | size_t pre_sig_length = 0; |
441 | | |
442 | | /* Check if chunk exceeds the limits for the overall message */ |
443 | 0 | UA_StatusCode res = adjustCheckMessageLimitsSym(mc, bodyLength); |
444 | 0 | UA_CHECK_STATUS(res, goto error); |
445 | | |
446 | 0 | UA_LOG_TRACE_CHANNEL(sp->logger, channel, |
447 | 0 | "Send from a symmetric message buffer of length %lu " |
448 | 0 | "a message of header+payload length of %lu", |
449 | 0 | (long unsigned int)mc->messageBuffer.length, |
450 | 0 | (long unsigned int) |
451 | 0 | ((uintptr_t)mc->buf_pos - (uintptr_t)mc->messageBuffer.data)); |
452 | | |
453 | | /* Add padding if the message is encrypted (not for AEAD policies) */ |
454 | 0 | if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT && |
455 | 0 | !UA_SecurityPolicy_isAead(sp)) |
456 | 0 | padChunk(channel, &sp->symSignatureAlgorithm, &sp->symEncryptionAlgorithm, |
457 | 0 | &mc->messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_UNENCRYPTEDLENGTH], |
458 | 0 | &mc->buf_pos); |
459 | | |
460 | | /* Compute the total message length */ |
461 | 0 | pre_sig_length = (uintptr_t)mc->buf_pos - (uintptr_t)mc->messageBuffer.data; |
462 | 0 | total_length = pre_sig_length; |
463 | 0 | if(channel->securityMode == UA_MESSAGESECURITYMODE_SIGN || |
464 | 0 | channel->securityMode == UA_MESSAGESECURITYMODE_SIGNANDENCRYPT) |
465 | 0 | total_length += sp->symSignatureAlgorithm. |
466 | 0 | getLocalSignatureSize(sp, channel->channelContext); |
467 | |
|
468 | 0 | UA_LOG_TRACE_CHANNEL(sp->logger, channel, |
469 | 0 | "Send from a symmetric message buffer of length %lu " |
470 | 0 | "a message of length %lu", |
471 | 0 | (long unsigned int)mc->messageBuffer.length, |
472 | 0 | (long unsigned int)total_length); |
473 | | |
474 | | /* Space for the padding and the signature have been reserved in setBufPos() */ |
475 | 0 | UA_assert(total_length <= channel->config.sendBufferSize); |
476 | | |
477 | | /* Adjust the buffer size of the network layer */ |
478 | 0 | mc->messageBuffer.length = total_length; |
479 | | |
480 | | /* Generate and encode the header for symmetric messages */ |
481 | 0 | res = encodeHeadersSym(mc, total_length); |
482 | 0 | UA_CHECK_STATUS(res, goto error); |
483 | | |
484 | | /* Sign and encrypt the messge */ |
485 | 0 | res = signAndEncryptSym(mc, pre_sig_length, total_length); |
486 | 0 | UA_CHECK_STATUS(res, goto error); |
487 | | |
488 | | /* Send the chunk. The buffer is freed in the network layer. If sending goes |
489 | | * wrong, the connection is removed in the next iteration of the |
490 | | * SecureChannel. Set the SecureChannel to closing already. */ |
491 | 0 | res = cm->sendWithConnection(cm, channel->connectionId, |
492 | 0 | &UA_KEYVALUEMAP_NULL, &mc->messageBuffer); |
493 | 0 | if(res != UA_STATUSCODE_GOOD && UA_SecureChannel_isConnected(channel)) |
494 | 0 | channel->state = UA_SECURECHANNELSTATE_CLOSING; |
495 | 0 | return res; |
496 | | |
497 | 0 | error: |
498 | | /* Free the unused message buffer */ |
499 | 0 | cm->freeNetworkBuffer(cm, channel->connectionId, &mc->messageBuffer); |
500 | 0 | return res; |
501 | 0 | } |
502 | | |
503 | | /* Callback from the encoding layer. Send the chunk and replace the buffer. */ |
504 | | static UA_StatusCode |
505 | | sendSymmetricEncodingCallback(void *data, UA_Byte **buf_pos, |
506 | 0 | const UA_Byte **buf_end) { |
507 | | /* Set buf values from encoding in the messagecontext */ |
508 | 0 | UA_MessageContext *mc = (UA_MessageContext *)data; |
509 | 0 | mc->buf_pos = *buf_pos; |
510 | 0 | mc->buf_end = *buf_end; |
511 | | |
512 | | /* Send out */ |
513 | 0 | UA_StatusCode res = sendSymmetricChunk(mc); |
514 | 0 | UA_CHECK_STATUS(res, return res); |
515 | | |
516 | | /* Set a new buffer for the next chunk */ |
517 | 0 | UA_ConnectionManager *cm = mc->channel->connectionManager; |
518 | 0 | if(!UA_SecureChannel_isConnected(mc->channel)) |
519 | 0 | return UA_STATUSCODE_BADCONNECTIONCLOSED; |
520 | | |
521 | 0 | res = cm->allocNetworkBuffer(cm, mc->channel->connectionId, |
522 | 0 | &mc->messageBuffer, |
523 | 0 | mc->channel->config.sendBufferSize); |
524 | 0 | UA_CHECK_STATUS(res, return res); |
525 | | |
526 | | /* Hide bytes for header, padding and signature */ |
527 | 0 | setBufPos(mc); |
528 | 0 | *buf_pos = mc->buf_pos; |
529 | 0 | *buf_end = mc->buf_end; |
530 | 0 | return UA_STATUSCODE_GOOD; |
531 | 0 | } |
532 | | |
533 | | UA_StatusCode |
534 | | UA_MessageContext_begin(UA_MessageContext *mc, UA_SecureChannel *channel, |
535 | 0 | UA_UInt32 requestId, UA_MessageType messageType) { |
536 | 0 | UA_CHECK(messageType == UA_MESSAGETYPE_MSG || messageType == UA_MESSAGETYPE_CLO, |
537 | 0 | return UA_STATUSCODE_BADINTERNALERROR); |
538 | | |
539 | 0 | UA_ConnectionManager *cm = channel->connectionManager; |
540 | 0 | if(!UA_SecureChannel_isConnected(channel)) |
541 | 0 | return UA_STATUSCODE_BADCONNECTIONCLOSED; |
542 | | |
543 | | /* Create the chunking info structure */ |
544 | 0 | mc->channel = channel; |
545 | 0 | mc->requestId = requestId; |
546 | 0 | mc->chunksSoFar = 0; |
547 | 0 | mc->messageSizeSoFar = 0; |
548 | 0 | mc->final = false; |
549 | 0 | mc->messageBuffer = UA_BYTESTRING_NULL; |
550 | 0 | mc->messageType = messageType; |
551 | | |
552 | | /* Allocate the message buffer */ |
553 | 0 | UA_StatusCode res = |
554 | 0 | cm->allocNetworkBuffer(cm, channel->connectionId, |
555 | 0 | &mc->messageBuffer, |
556 | 0 | channel->config.sendBufferSize); |
557 | 0 | UA_CHECK_STATUS(res, return res); |
558 | | |
559 | | /* Hide bytes for header, padding and signature */ |
560 | 0 | setBufPos(mc); |
561 | 0 | return UA_STATUSCODE_GOOD; |
562 | 0 | } |
563 | | |
564 | | UA_StatusCode |
565 | | UA_MessageContext_encode(UA_MessageContext *mc, const void *content, |
566 | 0 | const UA_DataType *contentType) { |
567 | 0 | UA_EncodeBinaryOptions encOpts; |
568 | 0 | memset(&encOpts, 0, sizeof(UA_EncodeBinaryOptions)); |
569 | 0 | encOpts.namespaceMapping = mc->channel->namespaceMapping; |
570 | 0 | UA_StatusCode res = |
571 | 0 | UA_encodeBinaryInternal(content, contentType, &mc->buf_pos, &mc->buf_end, |
572 | 0 | &encOpts, sendSymmetricEncodingCallback, mc); |
573 | 0 | if(res != UA_STATUSCODE_GOOD && mc->messageBuffer.length > 0) |
574 | 0 | UA_MessageContext_abort(mc); |
575 | 0 | return res; |
576 | 0 | } |
577 | | |
578 | | UA_StatusCode |
579 | 0 | UA_MessageContext_finish(UA_MessageContext *mc) { |
580 | 0 | mc->final = true; |
581 | 0 | return sendSymmetricChunk(mc); |
582 | 0 | } |
583 | | |
584 | | void |
585 | 0 | UA_MessageContext_abort(UA_MessageContext *mc) { |
586 | 0 | UA_ConnectionManager *cm = mc->channel->connectionManager; |
587 | 0 | if(!UA_SecureChannel_isConnected(mc->channel)) |
588 | 0 | return; |
589 | 0 | cm->freeNetworkBuffer(cm, mc->channel->connectionId, &mc->messageBuffer); |
590 | 0 | } |
591 | | |
592 | | /* Send a MSG or CLO message using symmetric encryption */ |
593 | | static UA_StatusCode |
594 | | sendSymmetric(UA_SecureChannel *channel, UA_UInt32 requestId, |
595 | | UA_MessageType messageType, void *payload, |
596 | 0 | const UA_DataType *payloadType) { |
597 | 0 | if(!channel || !payload || !payloadType) |
598 | 0 | return UA_STATUSCODE_BADINTERNALERROR; |
599 | | |
600 | 0 | if(channel->state != UA_SECURECHANNELSTATE_OPEN) |
601 | 0 | return UA_STATUSCODE_BADCONNECTIONCLOSED; |
602 | | |
603 | 0 | UA_MessageContext mc; |
604 | 0 | UA_StatusCode res = UA_MessageContext_begin(&mc, channel, requestId, messageType); |
605 | 0 | UA_CHECK_STATUS(res, return res); |
606 | | |
607 | | /* Assert's required for clang-analyzer */ |
608 | 0 | UA_assert(mc.buf_pos == |
609 | 0 | &mc.messageBuffer.data[UA_SECURECHANNEL_SYMMETRIC_HEADER_TOTALLENGTH]); |
610 | 0 | UA_assert(mc.buf_end <= &mc.messageBuffer.data[mc.messageBuffer.length]); |
611 | | |
612 | 0 | res = UA_MessageContext_encode(&mc, &payloadType->binaryEncodingId, |
613 | 0 | &UA_TYPES[UA_TYPES_NODEID]); |
614 | 0 | UA_CHECK_STATUS(res, return res); |
615 | | |
616 | 0 | res = UA_MessageContext_encode(&mc, payload, payloadType); |
617 | 0 | UA_CHECK_STATUS(res, return res); |
618 | | |
619 | 0 | return UA_MessageContext_finish(&mc); |
620 | 0 | } |
621 | | |
622 | | UA_StatusCode |
623 | | UA_SecureChannel_sendMSG(UA_SecureChannel *channel, UA_UInt32 requestId, |
624 | 0 | void *payload, const UA_DataType *payloadType) { |
625 | 0 | return sendSymmetric(channel, requestId, UA_MESSAGETYPE_MSG, |
626 | 0 | payload, payloadType); |
627 | 0 | } |
628 | | |
629 | | UA_StatusCode |
630 | | UA_SecureChannel_sendCLO(UA_SecureChannel *channel, UA_UInt32 requestId, |
631 | 0 | UA_CloseSecureChannelRequest *req) { |
632 | 0 | return sendSymmetric(channel, requestId, UA_MESSAGETYPE_CLO, req, |
633 | 0 | &UA_TYPES[UA_TYPES_CLOSESECURECHANNELREQUEST]); |
634 | 0 | } |
635 | | |
636 | | /********************************/ |
637 | | /* Receive and Process Messages */ |
638 | | /********************************/ |
639 | | |
640 | | /* Does the sequence number match? Otherwise try to rollover. See Part 6, |
641 | | * Section 6.7.2.4 of the standard. */ |
642 | 0 | #define UA_SEQUENCENUMBER_ROLLOVER 4294966271 |
643 | | |
644 | | UA_UInt32 |
645 | 0 | UA_SecureChannel_nextSequenceNumber(UA_SecureChannel *channel) { |
646 | | /* channel->sendSequenceNumber mirrors the reference-stack counter: a |
647 | | * pre-increment value initialized to 0. The legacy scheme emits the |
648 | | * counter (first = 1); the non-legacy scheme emits counter-1 (first = 0). */ |
649 | 0 | UA_UInt64 next = (UA_UInt64)channel->sendSequenceNumber + 1; |
650 | 0 | if(channel->legacySequenceNumbers) { |
651 | | /* Legacy rollover: the first number after the max is 1. */ |
652 | 0 | if(next > UA_SEQUENCENUMBER_ROLLOVER) |
653 | 0 | next = 1; |
654 | 0 | channel->sendSequenceNumber = (UA_UInt32)next; |
655 | 0 | return channel->sendSequenceNumber; |
656 | 0 | } |
657 | | /* Non-legacy: the counter wraps to 0 after UA_UINT32_MAX so the emitted |
658 | | * value (counter - 1) covers the full UInt32 range and then restarts at 0. */ |
659 | 0 | if(next > UA_UINT32_MAX) |
660 | 0 | next = 0; |
661 | 0 | channel->sendSequenceNumber = (UA_UInt32)next; |
662 | 0 | return (channel->sendSequenceNumber == 0) ? |
663 | 0 | UA_UINT32_MAX : (channel->sendSequenceNumber - 1); |
664 | 0 | } |
665 | | |
666 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
667 | | static UA_StatusCode |
668 | | processSequenceNumberSym(UA_SecureChannel *channel, UA_UInt32 sequenceNumber) { |
669 | | if(sequenceNumber != channel->receiveSequenceNumber + 1) { |
670 | | /* The non-legacy (ECC) rollover from UA_UINT32_MAX to 0 is already |
671 | | * accepted by the check above (unsigned overflow makes |
672 | | * receiveSequenceNumber + 1 == 0). Only the legacy "< 1024" rollover |
673 | | * needs the special case below; non-legacy policies reject anything |
674 | | * that is not the immediate successor. */ |
675 | | if(!channel->legacySequenceNumbers || |
676 | | channel->receiveSequenceNumber + 1 <= UA_SEQUENCENUMBER_ROLLOVER || |
677 | | sequenceNumber >= 1024) |
678 | | return UA_STATUSCODE_BADSECURITYCHECKSFAILED; |
679 | | channel->receiveSequenceNumber = sequenceNumber - 1; /* Roll over */ |
680 | | } |
681 | | ++channel->receiveSequenceNumber; |
682 | | return UA_STATUSCODE_GOOD; |
683 | | } |
684 | | #endif |
685 | | |
686 | | static UA_StatusCode |
687 | 0 | unpackPayloadOPN(UA_SecureChannel *channel, UA_Chunk *chunk) { |
688 | 0 | UA_assert(chunk->bytes.length >= UA_SECURECHANNEL_MESSAGE_MIN_LENGTH); |
689 | 0 | size_t offset = UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; /* Skip the message header */ |
690 | 0 | UA_UInt32 secureChannelId; |
691 | 0 | UA_StatusCode res = UA_UInt32_decodeBinary(&chunk->bytes, &offset, &secureChannelId); |
692 | 0 | UA_assert(res == UA_STATUSCODE_GOOD); |
693 | | |
694 | 0 | UA_AsymmetricAlgorithmSecurityHeader asymHeader; |
695 | 0 | res = UA_decodeBinaryInternal(&chunk->bytes, &offset, &asymHeader, |
696 | 0 | &UA_TRANSPORT[UA_TRANSPORT_ASYMMETRICALGORITHMSECURITYHEADER], NULL); |
697 | 0 | UA_CHECK_STATUS(res, return res); |
698 | | |
699 | | /* Declare before the first goto to avoid crosses-initialization in C++ */ |
700 | 0 | UA_SecurityPolicy *sp = NULL; |
701 | | |
702 | | /* Client/Server-specific processing. Creates a SecurityPolicy context and |
703 | | * attaches it to the channel. For the client, the remote certificate has |
704 | | * been verified before connecting. For the server the remote certificate is |
705 | | * verified within processOPNHeader. */ |
706 | 0 | UA_assert(channel->processOPNHeader); |
707 | 0 | res = channel->processOPNHeader(channel->processOPNHeaderApplication, |
708 | 0 | channel, &asymHeader); |
709 | 0 | UA_CHECK_STATUS(res, goto error); |
710 | | |
711 | | /* On the client side, take the SecureChannelId from the first response */ |
712 | 0 | if(secureChannelId != 0 && channel->securityToken.channelId == 0) |
713 | 0 | channel->securityToken.channelId = secureChannelId; |
714 | | |
715 | | /* Check the ChannelId */ |
716 | | #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
717 | | if(secureChannelId != channel->securityToken.channelId) { |
718 | | /* Allow the channel id to be different if the sent channel id is zero |
719 | | * and the SecurityToken is not initialized. This only happens on the |
720 | | * server side before we had a chance to tell the client which ChannelId |
721 | | * to use. */ |
722 | | if(secureChannelId != 0 || channel->securityToken.tokenId != 0) { |
723 | | res = UA_STATUSCODE_BADSECURECHANNELIDINVALID; |
724 | | goto error; |
725 | | } |
726 | | } |
727 | | #endif |
728 | | |
729 | | /* Generic header checking (for both client and server). Requires the |
730 | | * channel's SecurityPolicy. */ |
731 | 0 | res = checkAsymHeader(channel, &asymHeader); |
732 | 0 | UA_CHECK_STATUS(res, goto error); |
733 | | |
734 | 0 | UA_AsymmetricAlgorithmSecurityHeader_clear(&asymHeader); |
735 | | |
736 | | /* Decrypt the chunk payload */ |
737 | 0 | sp = channel->securityPolicy; |
738 | 0 | res = decryptAndVerifyChunk(channel, &sp->asymSignatureAlgorithm, |
739 | 0 | &sp->asymEncryptionAlgorithm, |
740 | 0 | chunk->messageType, &chunk->bytes, offset); |
741 | 0 | UA_CHECK_STATUS(res, return res); |
742 | | |
743 | | /* Decode the SequenceHeader */ |
744 | 0 | UA_SequenceHeader sequenceHeader; |
745 | 0 | res = UA_decodeBinaryInternal(&chunk->bytes, &offset, &sequenceHeader, |
746 | 0 | &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], NULL); |
747 | 0 | UA_CHECK_STATUS(res, return res); |
748 | | |
749 | | /* Set the sequence number for the channel from which to count up */ |
750 | 0 | channel->receiveSequenceNumber = sequenceHeader.sequenceNumber; |
751 | 0 | chunk->requestId = sequenceHeader.requestId; /* Set the RequestId of the chunk */ |
752 | | |
753 | | /* Use only the payload */ |
754 | 0 | chunk->bytes.data += offset; |
755 | 0 | chunk->bytes.length -= offset; |
756 | 0 | return UA_STATUSCODE_GOOD; |
757 | | |
758 | 0 | error: |
759 | 0 | UA_AsymmetricAlgorithmSecurityHeader_clear(&asymHeader); |
760 | 0 | return res; |
761 | 0 | } |
762 | | |
763 | | static UA_StatusCode |
764 | | unpackPayloadMSG(UA_SecureChannel *channel, UA_Chunk *chunk, |
765 | 0 | UA_DateTime nowMonotonic) { |
766 | 0 | UA_CHECK_MEM(channel->securityPolicy, return UA_STATUSCODE_BADINTERNALERROR); |
767 | | |
768 | 0 | UA_assert(chunk->bytes.length >= UA_SECURECHANNEL_MESSAGE_MIN_LENGTH); |
769 | 0 | size_t offset = UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; /* Skip the message header */ |
770 | 0 | UA_UInt32 secureChannelId; |
771 | 0 | UA_UInt32 tokenId; /* SymmetricAlgorithmSecurityHeader */ |
772 | 0 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
773 | 0 | res |= UA_UInt32_decodeBinary(&chunk->bytes, &offset, &secureChannelId); |
774 | 0 | res |= UA_UInt32_decodeBinary(&chunk->bytes, &offset, &tokenId); |
775 | 0 | UA_assert(offset == UA_SECURECHANNEL_MESSAGE_MIN_LENGTH); |
776 | 0 | UA_assert(res == UA_STATUSCODE_GOOD); |
777 | | |
778 | | #if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION) |
779 | | /* Check the ChannelId. Non-opened channels have the id zero. */ |
780 | | if(secureChannelId != channel->securityToken.channelId) |
781 | | return UA_STATUSCODE_BADSECURECHANNELIDINVALID; |
782 | | #endif |
783 | | |
784 | | /* Check (and revolve) the SecurityToken */ |
785 | 0 | res = checkSymHeader(channel, tokenId, nowMonotonic); |
786 | 0 | UA_CHECK_STATUS(res, return res); |
787 | | |
788 | | /* Decrypt the chunk payload */ |
789 | 0 | UA_SecurityPolicy *sp = channel->securityPolicy; |
790 | 0 | res = decryptAndVerifyChunk(channel, &sp->symSignatureAlgorithm, |
791 | 0 | &sp->symEncryptionAlgorithm, |
792 | 0 | chunk->messageType, &chunk->bytes, offset); |
793 | 0 | UA_CHECK_STATUS(res, return res); |
794 | | |
795 | | /* Check the sequence number. Skip sequence number checking for fuzzer to |
796 | | * improve coverage */ |
797 | 0 | UA_SequenceHeader sequenceHeader; |
798 | 0 | res = UA_decodeBinaryInternal(&chunk->bytes, &offset, &sequenceHeader, |
799 | 0 | &UA_TRANSPORT[UA_TRANSPORT_SEQUENCEHEADER], NULL); |
800 | | #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
801 | | res |= processSequenceNumberSym(channel, sequenceHeader.sequenceNumber); |
802 | | #endif |
803 | 0 | UA_CHECK_STATUS(res, return res); |
804 | | |
805 | 0 | chunk->requestId = sequenceHeader.requestId; /* Set the RequestId of the chunk */ |
806 | | |
807 | | /* Use only the payload */ |
808 | 0 | chunk->bytes.data += offset; |
809 | 0 | chunk->bytes.length -= offset; |
810 | 0 | return UA_STATUSCODE_GOOD; |
811 | 0 | } |
812 | | |
813 | | static UA_StatusCode |
814 | | extractCompleteChunk(UA_SecureChannel *channel, UA_Chunk *chunk, |
815 | 0 | UA_DateTime nowMonotonic) { |
816 | | /* At least 8 byte needed for the header */ |
817 | 0 | size_t offset = channel->unprocessedOffset; |
818 | 0 | size_t remaining = channel->unprocessed.length - offset; |
819 | 0 | if(remaining < UA_SECURECHANNEL_MESSAGEHEADER_LENGTH) |
820 | 0 | return UA_STATUSCODE_GOOD; |
821 | | |
822 | | /* Decoding the header cannot fail */ |
823 | 0 | UA_TcpMessageHeader hdr; |
824 | 0 | UA_StatusCode res = |
825 | 0 | UA_decodeBinaryInternal(&channel->unprocessed, &offset, &hdr, |
826 | 0 | &UA_TRANSPORT[UA_TRANSPORT_TCPMESSAGEHEADER], NULL); |
827 | 0 | UA_assert(res == UA_STATUSCODE_GOOD); |
828 | 0 | (void)res; /* pacify compilers if assert is ignored */ |
829 | 0 | UA_MessageType msgType = (UA_MessageType) |
830 | 0 | (hdr.messageTypeAndChunkType & UA_BITMASK_MESSAGETYPE); |
831 | 0 | UA_ChunkType chunkType = (UA_ChunkType) |
832 | 0 | (hdr.messageTypeAndChunkType & UA_BITMASK_CHUNKTYPE); |
833 | | |
834 | | /* The message size is not allowed */ |
835 | 0 | if(hdr.messageSize < UA_SECURECHANNEL_MESSAGE_MIN_LENGTH) |
836 | 0 | return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; |
837 | 0 | if(hdr.messageSize > channel->config.recvBufferSize) |
838 | 0 | return UA_STATUSCODE_BADTCPMESSAGETOOLARGE; |
839 | | |
840 | | /* Incomplete chunk. Continue processing later. */ |
841 | 0 | if(hdr.messageSize > remaining) |
842 | 0 | return UA_STATUSCODE_GOOD; |
843 | | |
844 | | /* Set the chunk information */ |
845 | 0 | chunk->bytes.data = channel->unprocessed.data + channel->unprocessedOffset; |
846 | 0 | chunk->bytes.length = hdr.messageSize; |
847 | 0 | chunk->messageType = msgType; |
848 | 0 | chunk->chunkType = chunkType; |
849 | 0 | chunk->requestId = 0; |
850 | 0 | chunk->copied = false; |
851 | | |
852 | | /* Increase the unprocessed offset */ |
853 | 0 | channel->unprocessedOffset += hdr.messageSize; |
854 | | |
855 | | /* Validate, decrypt and unpack the chunk payload */ |
856 | 0 | switch(msgType) { |
857 | 0 | case UA_MESSAGETYPE_OPN: |
858 | 0 | if(chunkType != UA_CHUNKTYPE_FINAL) |
859 | 0 | return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; |
860 | 0 | if(channel->state != UA_SECURECHANNELSTATE_OPEN && |
861 | 0 | channel->state != UA_SECURECHANNELSTATE_OPN_SENT && |
862 | 0 | channel->state != UA_SECURECHANNELSTATE_ACK_SENT) |
863 | 0 | return UA_STATUSCODE_BADINVALIDSTATE; |
864 | 0 | res = unpackPayloadOPN(channel, chunk); |
865 | 0 | break; |
866 | | |
867 | 0 | case UA_MESSAGETYPE_MSG: |
868 | 0 | case UA_MESSAGETYPE_CLO: |
869 | 0 | if(chunkType != UA_CHUNKTYPE_FINAL && |
870 | 0 | chunkType != UA_CHUNKTYPE_INTERMEDIATE && |
871 | 0 | chunkType != UA_CHUNKTYPE_ABORT) |
872 | 0 | return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; |
873 | 0 | if(channel->state != UA_SECURECHANNELSTATE_OPEN) |
874 | 0 | return UA_STATUSCODE_BADINVALIDSTATE; |
875 | 0 | res = unpackPayloadMSG(channel, chunk, nowMonotonic); |
876 | 0 | break; |
877 | | |
878 | 0 | case UA_MESSAGETYPE_RHE: |
879 | 0 | case UA_MESSAGETYPE_HEL: |
880 | 0 | case UA_MESSAGETYPE_ACK: |
881 | 0 | case UA_MESSAGETYPE_ERR: |
882 | 0 | if(chunkType != UA_CHUNKTYPE_FINAL) |
883 | 0 | return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; |
884 | | /* Hide the message header */ |
885 | 0 | chunk->bytes.data += UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; |
886 | 0 | chunk->bytes.length -= UA_SECURECHANNEL_MESSAGEHEADER_LENGTH; |
887 | 0 | break; |
888 | | |
889 | 0 | default: |
890 | 0 | res = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; |
891 | 0 | break; |
892 | 0 | } |
893 | 0 | return res; |
894 | 0 | } |
895 | | |
896 | | UA_StatusCode |
897 | 0 | UA_SecureChannel_loadBuffer(UA_SecureChannel *channel, const UA_ByteString buffer) { |
898 | | /* Append to the previous unprocessed buffer */ |
899 | 0 | if(channel->unprocessed.length > 0) { |
900 | 0 | UA_assert(channel->unprocessedCopied == true); |
901 | | |
902 | 0 | UA_Byte *t = (UA_Byte*) |
903 | 0 | UA_realloc(channel->unprocessed.data, |
904 | 0 | channel->unprocessed.length + buffer.length); |
905 | 0 | if(!t) |
906 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
907 | | |
908 | 0 | if(buffer.length) |
909 | 0 | memcpy(t + channel->unprocessed.length, buffer.data, buffer.length); |
910 | 0 | channel->unprocessed.data = t; |
911 | 0 | channel->unprocessed.length += buffer.length; |
912 | 0 | return UA_STATUSCODE_GOOD; |
913 | 0 | } |
914 | | |
915 | | /* Use the new buffer directly */ |
916 | 0 | channel->unprocessed = buffer; |
917 | 0 | channel->unprocessedCopied = false; |
918 | 0 | return UA_STATUSCODE_GOOD; |
919 | 0 | } |
920 | | |
921 | | UA_StatusCode |
922 | | UA_SecureChannel_getCompleteMessage(UA_SecureChannel *channel, |
923 | | UA_MessageType *messageType, UA_UInt32 *requestId, |
924 | | UA_ByteString *payload, UA_Boolean *copied, |
925 | 0 | UA_DateTime nowMonotonic) { |
926 | 0 | UA_Chunk chunk, *pchunk; |
927 | 0 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
928 | |
|
929 | 0 | extract_chunk: |
930 | | /* Extract+decode the next chunk from the buffer */ |
931 | 0 | memset(&chunk, 0, sizeof(UA_Chunk)); |
932 | 0 | res = extractCompleteChunk(channel, &chunk, nowMonotonic); |
933 | 0 | if(chunk.bytes.length == 0 || res != UA_STATUSCODE_GOOD) |
934 | 0 | return res; /* Error or no complete chunk could be extracted */ |
935 | | |
936 | | /* Process the chunk */ |
937 | 0 | switch(chunk.chunkType) { |
938 | 0 | case UA_CHUNKTYPE_ABORT: |
939 | | /* Remove all chunks received so far. Then continue extracting chunks. */ |
940 | 0 | deleteChunks(channel); |
941 | 0 | if(chunk.copied) |
942 | 0 | UA_ByteString_clear(&chunk.bytes); |
943 | 0 | goto extract_chunk; |
944 | | |
945 | 0 | case UA_CHUNKTYPE_INTERMEDIATE: |
946 | | /* Validate the resource limits */ |
947 | 0 | if((channel->config.localMaxChunkCount != 0 && |
948 | 0 | channel->chunksCount >= channel->config.localMaxChunkCount) || |
949 | 0 | (channel->config.localMaxMessageSize != 0 && |
950 | 0 | channel->chunksLength + chunk.bytes.length > channel->config.localMaxMessageSize)) { |
951 | 0 | if(chunk.copied) |
952 | 0 | UA_ByteString_clear(&chunk.bytes); |
953 | 0 | return UA_STATUSCODE_BADTCPMESSAGETOOLARGE; |
954 | 0 | } |
955 | | |
956 | | /* Add the chunk to the queue. Then continue extracting more chunks. */ |
957 | 0 | pchunk = (UA_Chunk*)UA_malloc(sizeof(UA_Chunk)); |
958 | 0 | if(!pchunk) { |
959 | 0 | if(chunk.copied) |
960 | 0 | UA_ByteString_clear(&chunk.bytes); |
961 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
962 | 0 | } |
963 | 0 | *pchunk = chunk; |
964 | 0 | TAILQ_INSERT_TAIL(&channel->chunks, pchunk, pointers); |
965 | 0 | channel->chunksCount++; |
966 | 0 | channel->chunksLength += pchunk->bytes.length; |
967 | 0 | goto extract_chunk; |
968 | | |
969 | 0 | case UA_CHUNKTYPE_FINAL: |
970 | 0 | default: |
971 | 0 | UA_assert(chunk.chunkType == UA_CHUNKTYPE_FINAL); /* Was checked before */ |
972 | 0 | break; /* A final chunk was received -- assemble the message */ |
973 | 0 | } |
974 | | |
975 | | /* Compute the message size */ |
976 | 0 | size_t messageSize = chunk.bytes.length; |
977 | 0 | UA_Chunk *first = NULL; |
978 | 0 | TAILQ_FOREACH(pchunk, &channel->chunks, pointers) { |
979 | 0 | if(chunk.requestId != pchunk->requestId) |
980 | 0 | continue; |
981 | 0 | if(chunk.messageType != pchunk->messageType) { |
982 | 0 | if(chunk.copied) |
983 | 0 | UA_ByteString_clear(&chunk.bytes); |
984 | 0 | return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID; |
985 | 0 | } |
986 | 0 | if(!first) |
987 | 0 | first = pchunk; |
988 | 0 | messageSize += pchunk->bytes.length; |
989 | 0 | } |
990 | | |
991 | | /* Validate the assembled message size */ |
992 | 0 | if(channel->config.localMaxMessageSize != 0 && |
993 | 0 | messageSize > channel->config.localMaxMessageSize) { |
994 | 0 | if(chunk.copied) |
995 | 0 | UA_ByteString_clear(&chunk.bytes); |
996 | 0 | return UA_STATUSCODE_BADTCPMESSAGETOOLARGE; |
997 | 0 | } |
998 | | |
999 | | /* Assemble the full payload and store it in chunk.bytes */ |
1000 | 0 | if(messageSize > chunk.bytes.length) { |
1001 | 0 | UA_assert(first != NULL); |
1002 | | |
1003 | | /* Allocate the full memory and initialize with the first chunk content. |
1004 | | * Use realloc to speed up. */ |
1005 | 0 | UA_ByteString message; |
1006 | 0 | if(first->copied) { |
1007 | 0 | message.data = (UA_Byte*)UA_realloc(first->bytes.data, messageSize); |
1008 | 0 | } else { |
1009 | 0 | message.data = (UA_Byte*)UA_malloc(messageSize); |
1010 | 0 | if(message.data) |
1011 | 0 | memcpy(message.data, first->bytes.data, first->bytes.length); |
1012 | 0 | } |
1013 | 0 | if(!message.data) { |
1014 | 0 | if(chunk.copied) |
1015 | 0 | UA_ByteString_clear(&chunk.bytes); |
1016 | 0 | return UA_STATUSCODE_BADOUTOFMEMORY; |
1017 | 0 | } |
1018 | 0 | message.length = first->bytes.length; |
1019 | | |
1020 | | /* Remove the the first chunk */ |
1021 | 0 | pchunk = TAILQ_NEXT(first, pointers); |
1022 | 0 | first->copied = false; |
1023 | 0 | channel->chunksCount--; |
1024 | 0 | channel->chunksLength -= first->bytes.length; |
1025 | 0 | TAILQ_REMOVE(&channel->chunks, first, pointers); |
1026 | 0 | UA_Chunk_delete(first); |
1027 | | |
1028 | | /* Copy over the content from the remaining intermediate chunks. |
1029 | | * And remove them right away. */ |
1030 | 0 | UA_Chunk *next; |
1031 | 0 | for(; pchunk; pchunk = next) { |
1032 | 0 | next = TAILQ_NEXT(pchunk, pointers); |
1033 | 0 | if(chunk.requestId != pchunk->requestId) |
1034 | 0 | continue; |
1035 | 0 | memcpy(message.data + message.length, pchunk->bytes.data, pchunk->bytes.length); |
1036 | 0 | message.length += pchunk->bytes.length; |
1037 | 0 | channel->chunksCount--; |
1038 | 0 | channel->chunksLength -= pchunk->bytes.length; |
1039 | 0 | TAILQ_REMOVE(&channel->chunks, pchunk, pointers); |
1040 | 0 | UA_Chunk_delete(pchunk); |
1041 | 0 | } |
1042 | | |
1043 | | /* Copy over the content from the final chunk */ |
1044 | 0 | memcpy(message.data + message.length, chunk.bytes.data, chunk.bytes.length); |
1045 | 0 | message.length += chunk.bytes.length; |
1046 | 0 | UA_assert(message.length == messageSize); |
1047 | | |
1048 | | /* Set assembled message as the content of the final chunk */ |
1049 | 0 | if(chunk.copied) |
1050 | 0 | UA_ByteString_clear(&chunk.bytes); |
1051 | 0 | chunk.bytes = message; |
1052 | 0 | chunk.copied = true; |
1053 | 0 | } |
1054 | | |
1055 | | /* Return the assembled message */ |
1056 | 0 | *requestId = chunk.requestId; |
1057 | 0 | *messageType = chunk.messageType; |
1058 | 0 | *payload = chunk.bytes; |
1059 | 0 | *copied = chunk.copied; |
1060 | 0 | return UA_STATUSCODE_GOOD; |
1061 | 0 | } |
1062 | | |
1063 | | UA_StatusCode |
1064 | 0 | UA_SecureChannel_persistBuffer(UA_SecureChannel *channel) { |
1065 | 0 | UA_StatusCode res = UA_STATUSCODE_GOOD; |
1066 | | |
1067 | | /* Persist the chunks */ |
1068 | 0 | UA_Chunk *chunk; |
1069 | 0 | TAILQ_FOREACH(chunk, &channel->chunks, pointers) { |
1070 | 0 | if(chunk->copied) |
1071 | 0 | continue; |
1072 | 0 | UA_ByteString tmp = UA_BYTESTRING_NULL; |
1073 | 0 | res |= UA_ByteString_copy(&chunk->bytes, &tmp); |
1074 | 0 | chunk->bytes = tmp; |
1075 | 0 | chunk->copied = true; |
1076 | 0 | } |
1077 | | |
1078 | | /* No unprocessed bytes remaining */ |
1079 | 0 | UA_assert(channel->unprocessed.length >= channel->unprocessedOffset); |
1080 | 0 | if(channel->unprocessed.length == channel->unprocessedOffset) { |
1081 | 0 | if(channel->unprocessedCopied) |
1082 | 0 | UA_ByteString_clear(&channel->unprocessed); |
1083 | 0 | else |
1084 | 0 | UA_ByteString_init(&channel->unprocessed); |
1085 | 0 | channel->unprocessedOffset = 0; |
1086 | 0 | return res; |
1087 | 0 | } |
1088 | | |
1089 | | /* Allocate a new unprocessed ByteString. |
1090 | | * tmp is the empty string if malloc fails. */ |
1091 | 0 | UA_ByteString tmp = UA_BYTESTRING_NULL; |
1092 | 0 | UA_ByteString remaining = channel->unprocessed; |
1093 | 0 | remaining.data += channel->unprocessedOffset; |
1094 | 0 | remaining.length -= channel->unprocessedOffset; |
1095 | 0 | res |= UA_ByteString_copy(&remaining, &tmp); |
1096 | 0 | if(channel->unprocessedCopied) |
1097 | 0 | UA_ByteString_clear(&channel->unprocessed); |
1098 | 0 | channel->unprocessed = tmp; |
1099 | 0 | channel->unprocessedOffset = 0; |
1100 | | channel->unprocessedCopied = true; |
1101 | 0 | return res; |
1102 | 0 | } |