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