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