|
@@ -79,6 +79,38 @@ UA_SecureChannel_init(UA_SecureChannel *channel,
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
+static void deleteMessage(UA_Message *me) {
|
|
|
+ UA_ChunkPayload *cp;
|
|
|
+ while((cp = SIMPLEQ_FIRST(&me->chunkPayloads))) {
|
|
|
+ if(cp->copied)
|
|
|
+ UA_ByteString_deleteMembers(&cp->bytes);
|
|
|
+ SIMPLEQ_REMOVE_HEAD(&me->chunkPayloads, pointers);
|
|
|
+ UA_free(cp);
|
|
|
+ }
|
|
|
+ UA_free(me);
|
|
|
+}
|
|
|
+
|
|
|
+static void
|
|
|
+deleteLatestMessage(UA_SecureChannel *channel, UA_UInt32 requestId) {
|
|
|
+ UA_Message *me = TAILQ_LAST(&channel->messages, UA_MessageQueue);
|
|
|
+ if(!me)
|
|
|
+ return;
|
|
|
+ if(me->requestId != requestId)
|
|
|
+ return;
|
|
|
+
|
|
|
+ TAILQ_REMOVE(&channel->messages, me, pointers);
|
|
|
+ deleteMessage(me);
|
|
|
+}
|
|
|
+
|
|
|
+void
|
|
|
+UA_SecureChannel_deleteMessages(UA_SecureChannel *channel) {
|
|
|
+ UA_Message *me, *me_tmp;
|
|
|
+ TAILQ_FOREACH_SAFE(me, &channel->messages, pointers, me_tmp) {
|
|
|
+ TAILQ_REMOVE(&channel->messages, me, pointers);
|
|
|
+ deleteMessage(me);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
void
|
|
|
UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
|
|
|
|
|
@@ -107,17 +139,8 @@ UA_SecureChannel_deleteMembersCleanup(UA_SecureChannel *channel) {
|
|
|
LIST_REMOVE(sh, pointers);
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- struct MessageEntry *me, *temp_me;
|
|
|
- LIST_FOREACH_SAFE(me, &channel->chunks, pointers, temp_me) {
|
|
|
- struct ChunkPayload *cp, *temp_cp;
|
|
|
- SIMPLEQ_FOREACH_SAFE(cp, &me->chunkPayload, pointers, temp_cp) {
|
|
|
- UA_ByteString_deleteMembers(&cp->bytes);
|
|
|
- UA_free(cp);
|
|
|
- }
|
|
|
- LIST_REMOVE(me, pointers);
|
|
|
- UA_free(me);
|
|
|
- }
|
|
|
+
|
|
|
+ UA_SecureChannel_deleteMessages(channel);
|
|
|
}
|
|
|
|
|
|
UA_StatusCode
|
|
@@ -225,7 +248,7 @@ UA_SecureChannel_generateNewKeys(UA_SecureChannel *channel) {
|
|
|
UA_SessionHeader *
|
|
|
UA_SecureChannel_getSession(UA_SecureChannel *channel,
|
|
|
const UA_NodeId *authenticationToken) {
|
|
|
- struct UA_SessionHeader *sh;
|
|
|
+ UA_SessionHeader *sh;
|
|
|
LIST_FOREACH(sh, &channel->sessions, pointers) {
|
|
|
if(UA_NodeId_equal(&sh->authenticationToken, authenticationToken))
|
|
|
break;
|
|
@@ -727,101 +750,123 @@ UA_SecureChannel_sendSymmetricMessage(UA_SecureChannel *channel, UA_UInt32 reque
|
|
|
|
|
|
|
|
|
|
|
|
-static void
|
|
|
-UA_SecureChannel_removeChunks(UA_SecureChannel *channel, UA_UInt32 requestId) {
|
|
|
- struct MessageEntry *me;
|
|
|
- LIST_FOREACH(me, &channel->chunks, pointers) {
|
|
|
- if(me->requestId == requestId) {
|
|
|
- struct ChunkPayload *cp, *temp_cp;
|
|
|
- SIMPLEQ_FOREACH_SAFE(cp, &me->chunkPayload, pointers, temp_cp) {
|
|
|
- UA_ByteString_deleteMembers(&cp->bytes);
|
|
|
- UA_free(cp);
|
|
|
- }
|
|
|
- LIST_REMOVE(me, pointers);
|
|
|
- UA_free(me);
|
|
|
- return;
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
static UA_StatusCode
|
|
|
-appendChunk(struct MessageEntry *messageEntry, const UA_ByteString *chunkBody) {
|
|
|
-
|
|
|
- struct ChunkPayload* cp = (struct ChunkPayload*)UA_malloc(sizeof(struct ChunkPayload));
|
|
|
- UA_StatusCode retval = UA_ByteString_copy(chunkBody, &cp->bytes);
|
|
|
- if (retval != UA_STATUSCODE_GOOD)
|
|
|
- return retval;
|
|
|
-
|
|
|
- SIMPLEQ_INSERT_TAIL(&messageEntry->chunkPayload, cp, pointers);
|
|
|
- messageEntry->chunkPayloadSize += chunkBody->length;
|
|
|
- return UA_STATUSCODE_GOOD;
|
|
|
-}
|
|
|
-
|
|
|
-static UA_StatusCode
|
|
|
-UA_SecureChannel_appendChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
|
|
|
- const UA_ByteString *chunkBody) {
|
|
|
- struct MessageEntry *me;
|
|
|
- LIST_FOREACH(me, &channel->chunks, pointers) {
|
|
|
- if(me->requestId == requestId)
|
|
|
- break;
|
|
|
+addChunkPayload(UA_SecureChannel *channel, UA_UInt32 requestId,
|
|
|
+ UA_MessageType messageType, UA_ByteString *chunkPayload,
|
|
|
+ UA_Boolean final) {
|
|
|
+ UA_Message *latest = TAILQ_LAST(&channel->messages, UA_MessageQueue);
|
|
|
+ if(latest) {
|
|
|
+ if(latest->requestId != requestId) {
|
|
|
+
|
|
|
+ if(!latest->final)
|
|
|
+ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
|
|
|
+ latest = NULL;
|
|
|
+ } else {
|
|
|
+ if(latest->messageType != messageType)
|
|
|
+ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
|
|
|
+ if(latest->final)
|
|
|
+ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-
|
|
|
- if(!me) {
|
|
|
- me = (struct MessageEntry *)UA_malloc(sizeof(struct MessageEntry));
|
|
|
- if(!me)
|
|
|
+
|
|
|
+ if(!latest) {
|
|
|
+ latest = (UA_Message*)UA_malloc(sizeof(UA_Message));
|
|
|
+ if(!latest)
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
- memset(me, 0, sizeof(struct MessageEntry));
|
|
|
- me->requestId = requestId;
|
|
|
- SIMPLEQ_INIT(&me->chunkPayload);
|
|
|
- LIST_INSERT_HEAD(&channel->chunks, me, pointers);
|
|
|
+ memset(latest, 0, sizeof(UA_Message));
|
|
|
+ latest->requestId = requestId;
|
|
|
+ latest->messageType = messageType;
|
|
|
+ SIMPLEQ_INIT(&latest->chunkPayloads);
|
|
|
+ TAILQ_INSERT_TAIL(&channel->messages, latest, pointers);
|
|
|
}
|
|
|
|
|
|
- return appendChunk(me, chunkBody);
|
|
|
+
|
|
|
+ const UA_ConnectionConfig *config = &channel->connection->localConf;
|
|
|
+ if(config->maxChunkCount > 0 &&
|
|
|
+ config->maxChunkCount <= latest->chunkPayloadsSize)
|
|
|
+ return UA_STATUSCODE_BADRESPONSETOOLARGE;
|
|
|
+ if(config->maxMessageSize > 0 &&
|
|
|
+ config->maxMessageSize < latest->messageSize + chunkPayload->length)
|
|
|
+ return UA_STATUSCODE_BADRESPONSETOOLARGE;
|
|
|
+
|
|
|
+
|
|
|
+ UA_ChunkPayload* cp = (UA_ChunkPayload*)UA_malloc(sizeof(UA_ChunkPayload));
|
|
|
+ if(!cp)
|
|
|
+ return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ cp->bytes = *chunkPayload;
|
|
|
+ cp->copied = false;
|
|
|
+
|
|
|
+
|
|
|
+ SIMPLEQ_INSERT_TAIL(&latest->chunkPayloads, cp, pointers);
|
|
|
+ latest->chunkPayloadsSize += 1;
|
|
|
+ latest->messageSize += chunkPayload->length;
|
|
|
+ latest->final = final;
|
|
|
+
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
static UA_StatusCode
|
|
|
-UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
|
|
|
- const UA_ByteString *chunkBody, UA_MessageType messageType,
|
|
|
- UA_ProcessMessageCallback callback, void *application) {
|
|
|
- struct MessageEntry *messageEntry;
|
|
|
- LIST_FOREACH(messageEntry, &channel->chunks, pointers) {
|
|
|
- if(messageEntry->requestId == requestId)
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- UA_ByteString bytes;
|
|
|
- if(!messageEntry) {
|
|
|
- bytes = *chunkBody;
|
|
|
+processMessage(UA_SecureChannel *channel, const UA_Message *message,
|
|
|
+ void *application, UA_ProcessMessageCallback callback) {
|
|
|
+ if(message->chunkPayloadsSize == 1) {
|
|
|
+
|
|
|
+ UA_ChunkPayload *cp = SIMPLEQ_FIRST(&message->chunkPayloads);
|
|
|
+ callback(application, channel, message->messageType, message->requestId, &cp->bytes);
|
|
|
} else {
|
|
|
- UA_StatusCode retval = appendChunk(messageEntry, chunkBody);
|
|
|
- if(retval != UA_STATUSCODE_GOOD)
|
|
|
- return retval;
|
|
|
-
|
|
|
- UA_ByteString_init(&bytes);
|
|
|
-
|
|
|
- bytes.data = (UA_Byte*) UA_malloc(messageEntry->chunkPayloadSize);
|
|
|
- if (!bytes.data)
|
|
|
+
|
|
|
+ UA_ByteString bytes;
|
|
|
+ bytes.data = (UA_Byte*) UA_malloc(message->messageSize);
|
|
|
+ if(!bytes.data) {
|
|
|
+ UA_LOG_WARNING(channel->securityPolicy->logger, UA_LOGCATEGORY_SECURECHANNEL,
|
|
|
+ "Could not allocate the memory to assemble the message");
|
|
|
return UA_STATUSCODE_BADOUTOFMEMORY;
|
|
|
+ }
|
|
|
+ bytes.length = message->messageSize;
|
|
|
|
|
|
- struct ChunkPayload *cp, *temp_cp;
|
|
|
+
|
|
|
size_t curPos = 0;
|
|
|
- SIMPLEQ_FOREACH_SAFE(cp, &messageEntry->chunkPayload, pointers, temp_cp) {
|
|
|
+ UA_ChunkPayload *cp;
|
|
|
+ SIMPLEQ_FOREACH(cp, &message->chunkPayloads, pointers) {
|
|
|
memcpy(&bytes.data[curPos], cp->bytes.data, cp->bytes.length);
|
|
|
curPos += cp->bytes.length;
|
|
|
- UA_ByteString_deleteMembers(&cp->bytes);
|
|
|
- UA_free(cp);
|
|
|
}
|
|
|
|
|
|
- bytes.length = messageEntry->chunkPayloadSize;
|
|
|
-
|
|
|
- LIST_REMOVE(messageEntry, pointers);
|
|
|
- UA_free(messageEntry);
|
|
|
+
|
|
|
+ callback(application, channel, message->messageType, message->requestId, &bytes);
|
|
|
+ UA_ByteString_deleteMembers(&bytes);
|
|
|
}
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
|
|
|
- UA_StatusCode retval = callback(application, channel, messageType, requestId, &bytes);
|
|
|
- if(messageEntry)
|
|
|
- UA_ByteString_deleteMembers(&bytes);
|
|
|
+UA_StatusCode
|
|
|
+UA_SecureChannel_processCompleteMessages(UA_SecureChannel *channel, void *application,
|
|
|
+ UA_ProcessMessageCallback callback) {
|
|
|
+ UA_Message *me, *me_tmp;
|
|
|
+ UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
+ TAILQ_FOREACH_SAFE(me, &channel->messages, pointers, me_tmp) {
|
|
|
+
|
|
|
+ if(!me->final)
|
|
|
+ break;
|
|
|
+
|
|
|
+
|
|
|
+ TAILQ_REMOVE(&channel->messages, me, pointers);
|
|
|
+
|
|
|
+
|
|
|
+ retval = processMessage(channel, me, application, callback);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ break;
|
|
|
+
|
|
|
+
|
|
|
+ UA_ChunkPayload *cp;
|
|
|
+ while((cp = SIMPLEQ_FIRST(&me->chunkPayloads))) {
|
|
|
+ if(cp->copied)
|
|
|
+ UA_ByteString_deleteMembers(&cp->bytes);
|
|
|
+ SIMPLEQ_REMOVE_HEAD(&me->chunkPayloads, pointers);
|
|
|
+ UA_free(cp);
|
|
|
+ }
|
|
|
+ UA_free(me);
|
|
|
+ }
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
@@ -829,10 +874,12 @@ UA_SecureChannel_finalizeChunk(UA_SecureChannel *channel, UA_UInt32 requestId,
|
|
|
|
|
|
|
|
|
|
|
|
+
|
|
|
+ * and the sequenceNumber */
|
|
|
static UA_StatusCode
|
|
|
-decryptChunk(UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cryptoModule,
|
|
|
- UA_ByteString *chunk, size_t offset, UA_UInt32 *requestId, UA_UInt32 *sequenceNumber,
|
|
|
- UA_ByteString *payload, UA_MessageType messageType) {
|
|
|
+decryptChunk(const UA_SecureChannel *channel, const UA_SecurityPolicyCryptoModule *cryptoModule,
|
|
|
+ UA_MessageType messageType, const UA_ByteString *chunk, size_t offset,
|
|
|
+ UA_UInt32 *requestId, UA_UInt32 *sequenceNumber, UA_ByteString *payload) {
|
|
|
UA_StatusCode retval = UA_STATUSCODE_GOOD;
|
|
|
const UA_SecurityPolicy *securityPolicy = channel->securityPolicy;
|
|
|
size_t chunkSizeAfterDecryption = chunk->length;
|
|
@@ -973,11 +1020,10 @@ checkSymHeader(UA_SecureChannel *const channel,
|
|
|
return UA_STATUSCODE_GOOD;
|
|
|
}
|
|
|
|
|
|
-UA_StatusCode
|
|
|
-UA_SecureChannel_processChunk(UA_SecureChannel *channel, UA_ByteString *chunk,
|
|
|
- UA_ProcessMessageCallback callback,
|
|
|
- void *application) {
|
|
|
-
|
|
|
+
|
|
|
+static UA_StatusCode
|
|
|
+decryptAddChunk(UA_SecureChannel *channel, const UA_ByteString *chunk) {
|
|
|
+
|
|
|
size_t offset = 0;
|
|
|
UA_SecureConversationMessageHeader messageHeader;
|
|
|
UA_StatusCode retval =
|
|
@@ -997,7 +1043,6 @@ UA_SecureChannel_processChunk(UA_SecureChannel *channel, UA_ByteString *chunk,
|
|
|
UA_ChunkType chunkType = (UA_ChunkType)
|
|
|
(messageHeader.messageHeader.messageTypeAndChunkType & UA_BITMASK_CHUNKTYPE);
|
|
|
|
|
|
-
|
|
|
UA_UInt32 requestId = 0;
|
|
|
UA_UInt32 sequenceNumber = 0;
|
|
|
UA_ByteString chunkPayload;
|
|
@@ -1005,14 +1050,15 @@ UA_SecureChannel_processChunk(UA_SecureChannel *channel, UA_ByteString *chunk,
|
|
|
UA_SequenceNumberCallback sequenceNumberCallback = NULL;
|
|
|
|
|
|
switch(messageType) {
|
|
|
- case UA_MESSAGETYPE_ERR: {
|
|
|
+
|
|
|
+ case UA_MESSAGETYPE_ERR:
|
|
|
if(chunkType != UA_CHUNKTYPE_FINAL)
|
|
|
return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
|
|
|
chunkPayload.length = chunk->length - offset;
|
|
|
chunkPayload.data = chunk->data + offset;
|
|
|
- return callback(application, channel, messageType, requestId, &chunkPayload);
|
|
|
- }
|
|
|
+ goto addPayload;
|
|
|
|
|
|
+
|
|
|
case UA_MESSAGETYPE_MSG:
|
|
|
case UA_MESSAGETYPE_CLO: {
|
|
|
|
|
@@ -1036,6 +1082,8 @@ UA_SecureChannel_processChunk(UA_SecureChannel *channel, UA_ByteString *chunk,
|
|
|
sequenceNumberCallback = processSequenceNumberSym;
|
|
|
break;
|
|
|
}
|
|
|
+
|
|
|
+
|
|
|
case UA_MESSAGETYPE_OPN: {
|
|
|
|
|
|
if(chunkType != UA_CHUNKTYPE_FINAL)
|
|
@@ -1059,44 +1107,86 @@ UA_SecureChannel_processChunk(UA_SecureChannel *channel, UA_ByteString *chunk,
|
|
|
sequenceNumberCallback = processSequenceNumberAsym;
|
|
|
break;
|
|
|
}
|
|
|
- default:return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
|
|
|
+
|
|
|
+
|
|
|
+ default:
|
|
|
+ return UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
|
|
|
}
|
|
|
|
|
|
|
|
|
UA_assert(cryptoModule != NULL);
|
|
|
- retval = decryptChunk(channel, cryptoModule, chunk, offset, &requestId,
|
|
|
- &sequenceNumber, &chunkPayload, messageType);
|
|
|
+ retval = decryptChunk(channel, cryptoModule, messageType, chunk, offset,
|
|
|
+ &requestId, &sequenceNumber, &chunkPayload);
|
|
|
if(retval != UA_STATUSCODE_GOOD)
|
|
|
return retval;
|
|
|
|
|
|
-
|
|
|
+#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
|
|
|
+
|
|
|
+ * improve coverage */
|
|
|
if(sequenceNumberCallback == NULL)
|
|
|
return UA_STATUSCODE_BADINTERNALERROR;
|
|
|
retval = sequenceNumberCallback(channel, sequenceNumber);
|
|
|
-
|
|
|
-
|
|
|
- if(retval != UA_STATUSCODE_GOOD) {
|
|
|
-#if !defined(FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION)
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
return retval;
|
|
|
-#else
|
|
|
- retval = UA_STATUSCODE_GOOD;
|
|
|
#endif
|
|
|
- }
|
|
|
|
|
|
-
|
|
|
- if(chunkType == UA_CHUNKTYPE_FINAL) {
|
|
|
- retval = UA_SecureChannel_finalizeChunk(channel, requestId, &chunkPayload,
|
|
|
- messageType, callback, application);
|
|
|
- } else if(chunkType == UA_CHUNKTYPE_INTERMEDIATE) {
|
|
|
- retval = UA_SecureChannel_appendChunk(channel, requestId, &chunkPayload);
|
|
|
- } else if(chunkType == UA_CHUNKTYPE_ABORT) {
|
|
|
- UA_SecureChannel_removeChunks(channel, requestId);
|
|
|
- } else {
|
|
|
+
|
|
|
+ addPayload:
|
|
|
+ switch(chunkType) {
|
|
|
+ case UA_CHUNKTYPE_INTERMEDIATE:
|
|
|
+ case UA_CHUNKTYPE_FINAL:
|
|
|
+ retval = addChunkPayload(channel, requestId, messageType,
|
|
|
+ &chunkPayload, chunkType == UA_CHUNKTYPE_FINAL);
|
|
|
+ break;
|
|
|
+ case UA_CHUNKTYPE_ABORT:
|
|
|
+ deleteLatestMessage(channel, requestId);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
retval = UA_STATUSCODE_BADTCPMESSAGETYPEINVALID;
|
|
|
}
|
|
|
return retval;
|
|
|
}
|
|
|
|
|
|
+UA_StatusCode
|
|
|
+UA_SecureChannel_decryptAddChunk(UA_SecureChannel *channel, const UA_ByteString *chunk) {
|
|
|
+
|
|
|
+ if(channel->state == UA_SECURECHANNELSTATE_CLOSED)
|
|
|
+ return UA_STATUSCODE_BADSECURECHANNELCLOSED;
|
|
|
+ if(channel->state == UA_SECURECHANNELSTATE_OPEN &&
|
|
|
+ (channel->securityToken.createdAt +
|
|
|
+ (channel->securityToken.revisedLifetime * UA_DATETIME_MSEC))
|
|
|
+ < UA_DateTime_nowMonotonic()) {
|
|
|
+ UA_SecureChannel_deleteMembersCleanup(channel);
|
|
|
+ return UA_STATUSCODE_BADSECURECHANNELCLOSED;
|
|
|
+ }
|
|
|
+
|
|
|
+ UA_StatusCode retval = decryptAddChunk(channel, chunk);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD)
|
|
|
+ UA_SecureChannel_deleteMembersCleanup(channel);
|
|
|
+ return retval;
|
|
|
+}
|
|
|
+
|
|
|
+UA_StatusCode
|
|
|
+UA_SecureChannel_persistIncompleteMessages(UA_SecureChannel *channel) {
|
|
|
+ UA_Message *me;
|
|
|
+ TAILQ_FOREACH(me, &channel->messages, pointers) {
|
|
|
+ UA_ChunkPayload *cp;
|
|
|
+ SIMPLEQ_FOREACH(cp, &me->chunkPayloads, pointers) {
|
|
|
+ if(cp->copied)
|
|
|
+ continue;
|
|
|
+ UA_ByteString copy;
|
|
|
+ UA_StatusCode retval = UA_ByteString_copy(&cp->bytes, ©);
|
|
|
+ if(retval != UA_STATUSCODE_GOOD) {
|
|
|
+ UA_SecureChannel_deleteMembersCleanup(channel);
|
|
|
+ return retval;
|
|
|
+ }
|
|
|
+ cp->bytes = copy;
|
|
|
+ cp->copied = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return UA_STATUSCODE_GOOD;
|
|
|
+}
|
|
|
+
|
|
|
|
|
|
|
|
|
size_t
|