Coverage Report

Created: 2025-11-09 06:21

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