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