summaryrefslogtreecommitdiffstats
path: root/plugins/epan/opcua/opcua_transport_layer.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-19 04:14:53 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-09-19 04:14:53 +0000
commita86c5f7cae7ec9a3398300555a0b644689d946a1 (patch)
tree39fe4b107c71174fd1e8a8ceb9a4d2aa14116248 /plugins/epan/opcua/opcua_transport_layer.c
parentReleasing progress-linux version 4.2.6-1~progress7.99u1. (diff)
downloadwireshark-a86c5f7cae7ec9a3398300555a0b644689d946a1.tar.xz
wireshark-a86c5f7cae7ec9a3398300555a0b644689d946a1.zip
Merging upstream version 4.4.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'plugins/epan/opcua/opcua_transport_layer.c')
-rw-r--r--plugins/epan/opcua/opcua_transport_layer.c240
1 files changed, 147 insertions, 93 deletions
diff --git a/plugins/epan/opcua/opcua_transport_layer.c b/plugins/epan/opcua/opcua_transport_layer.c
index 31fdca3b..64867c92 100644
--- a/plugins/epan/opcua/opcua_transport_layer.c
+++ b/plugins/epan/opcua/opcua_transport_layer.c
@@ -17,34 +17,51 @@
#include "config.h"
#include <epan/packet.h>
+#include <epan/conversation.h>
+#include "epan/column-utils.h"
+
#include "opcua_security_layer.h"
#include "opcua_application_layer.h"
#include "opcua_simpletypes.h"
#include "opcua_transport_layer.h"
#include "opcua_servicetable.h"
-static int hf_opcua_transport_type = -1;
-static int hf_opcua_transport_chunk = -1;
-static int hf_opcua_transport_size = -1;
-static int hf_opcua_transport_ver = -1;
-static int hf_opcua_transport_scid = -1;
-static int hf_opcua_transport_rbs = -1;
-static int hf_opcua_transport_sbs = -1;
-static int hf_opcua_transport_mms = -1;
-static int hf_opcua_transport_mcc = -1;
-static int hf_opcua_transport_endpoint = -1;
-static int hf_opcua_transport_suri = -1;
-static int hf_opcua_transport_error = -1;
-static int hf_opcua_transport_reason = -1;
-static int hf_opcua_transport_spu = -1;
-static int hf_opcua_transport_scert = -1;
-static int hf_opcua_transport_rcthumb = -1;
-static int hf_opcua_transport_seq = -1;
-static int hf_opcua_transport_rqid = -1;
+static int hf_opcua_transport_type;
+static int hf_opcua_transport_chunk;
+static int hf_opcua_transport_size;
+static int hf_opcua_transport_ver;
+static int hf_opcua_transport_scid;
+static int hf_opcua_transport_rbs;
+static int hf_opcua_transport_sbs;
+static int hf_opcua_transport_mms;
+static int hf_opcua_transport_mcc;
+static int hf_opcua_transport_endpoint;
+static int hf_opcua_transport_suri;
+static int hf_opcua_transport_error;
+static int hf_opcua_transport_reason;
+static int hf_opcua_transport_spu;
+static int hf_opcua_transport_scert;
+static int hf_opcua_transport_rcthumb;
+static int hf_opcua_transport_seq;
+static int hf_opcua_transport_rqid;
/** subtree types */
-extern gint ett_opcua_nodeid;
-extern gint ett_opcua_extensionobject;
+extern int ett_opcua_nodeid;
+extern int ett_opcua_extensionobject;
+extern int proto_opcua;
+
+/** Defined security policy URL from Part 7 OPC UA Specification. */
+#define UA_SECURITY_POLICY_NONE_STRING "http://opcfoundation.org/UA/SecurityPolicy#None"
+/** Defined security policy URL from Part 7 OPC UA Specification. */
+#define UA_SECURITY_POLICY_BASIC128RSA15_STRING "http://opcfoundation.org/UA/SecurityPolicy#Basic128Rsa15"
+/** Defined security policy URL from Part 7 OPC UA Specification. */
+#define UA_SECURITY_POLICY_BASIC256_STRING "http://opcfoundation.org/UA/SecurityPolicy#Basic256"
+/** Defined security policy URL from Part 7 OPC UA Specification. */
+#define UA_SECURITY_POLICY_BASIC256SHA256_STRING "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"
+/** Defined security policy URL from Part 7 OPC UA Specification. */
+#define UA_SECURITY_POLICY_AES128_SHA256_RSAOAEP_STRING "http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep"
+/** Defined security policy URL from Part 7 OPC UA Specification. */
+#define UA_SECURITY_POLICY_AES256_SHA256_RSAPSS_STRING "http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss"
/** Register transport layer types. */
void registerTransportLayerTypes(int proto)
@@ -75,12 +92,17 @@ void registerTransportLayerTypes(int proto)
proto_register_field_array(proto, hf, array_length(hf));
}
-/* Transport Layer: message parsers */
-int parseHello(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset)
+void parseMessageHeader(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, int *pOffset, struct ua_metadata *data _U_)
{
proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
+}
+
+/* Transport Layer: message parsers */
+int parseHello(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int *pOffset, struct ua_metadata *data _U_)
+{
+ parseMessageHeader(tree, tvb, pinfo, pOffset, data);
proto_tree_add_item(tree, hf_opcua_transport_ver, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
proto_tree_add_item(tree, hf_opcua_transport_rbs, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
proto_tree_add_item(tree, hf_opcua_transport_sbs, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
@@ -90,11 +112,9 @@ int parseHello(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffse
return -1;
}
-int parseAcknowledge(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, gint *pOffset)
+int parseAcknowledge(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, int *pOffset, struct ua_metadata *data _U_)
{
- proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
- proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
- proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
+ parseMessageHeader(tree, tvb, pinfo, pOffset, data);
proto_tree_add_item(tree, hf_opcua_transport_ver, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
proto_tree_add_item(tree, hf_opcua_transport_rbs, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
proto_tree_add_item(tree, hf_opcua_transport_sbs, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
@@ -103,48 +123,40 @@ int parseAcknowledge(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, gi
return -1;
}
-int parseError(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset)
+int parseError(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int *pOffset, struct ua_metadata *data _U_)
{
- proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
- proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
- proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
+ parseMessageHeader(tree, tvb, pinfo, pOffset, data);
parseStatusCode(tree, tvb, pinfo, pOffset, hf_opcua_transport_error);
parseString(tree, tvb, pinfo, pOffset, hf_opcua_transport_reason);
return -1;
}
-int parseReverseHello(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset)
+int parseReverseHello(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int *pOffset, struct ua_metadata *data _U_)
{
- proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
- proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
- proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
+ parseMessageHeader(tree, tvb, pinfo, pOffset, data);
parseString(tree, tvb, pinfo, pOffset, hf_opcua_transport_suri);
parseString(tree, tvb, pinfo, pOffset, hf_opcua_transport_endpoint);
return -1;
}
-int parseMessage(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, gint *pOffset)
+int parseMessage(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, int *pOffset, struct ua_metadata *data _U_)
{
- proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
- proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
- proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
+ parseMessageHeader(tree, tvb, pinfo, pOffset, data);
proto_tree_add_item(tree, hf_opcua_transport_scid, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
- /* message data contains the security layer */
- parseSecurityLayer(tree, tvb, pOffset);
-
return -1;
}
-int parseAbort(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, gint *pOffset)
+int parseAbort(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_, int *pOffset, struct ua_metadata *data _U_)
{
+ parseMessageHeader(tree, tvb, pinfo, pOffset, data);
parseStatusCode(tree, tvb, pinfo, pOffset, hf_opcua_transport_error);
parseString(tree, tvb, pinfo, pOffset, hf_opcua_transport_reason);
return -1;
}
-int parseService(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset)
+int parseService(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int *pOffset, struct ua_metadata *data _U_)
{
proto_item *ti;
proto_item *ti_inner;
@@ -152,83 +164,125 @@ int parseService(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOff
proto_tree *nodeid_tree;
int ServiceId = 0;
- /* AT THE MOMENT NO SECURITY IS IMPLEMENTED IN UA.
- * WE CAN JUST JUMP INTO THE APPLICATION LAYER DATA.
- * THIS WILL CHAHNGE IN THE FUTURE. */
-
/* add encodeable object subtree */
- encobj_tree = proto_tree_add_subtree(tree, tvb, *pOffset, -1, ett_opcua_extensionobject, &ti, "OpcUa Service : Encodeable Object");
+ encobj_tree = proto_tree_add_subtree(tree, tvb, *pOffset, -1, ett_opcua_extensionobject, &ti, "Message: Encodeable Object");
/* add nodeid subtree */
- nodeid_tree = proto_tree_add_subtree(encobj_tree, tvb, *pOffset, -1, ett_opcua_nodeid, &ti_inner, "TypeId : ExpandedNodeId");
+ nodeid_tree = proto_tree_add_subtree(encobj_tree, tvb, *pOffset, -1, ett_opcua_nodeid, &ti_inner, "TypeId: ExpandedNodeId");
ServiceId = parseServiceNodeId(nodeid_tree, tvb, pOffset);
proto_item_set_end(ti_inner, tvb, *pOffset);
- dispatchService(encobj_tree, tvb, pinfo, pOffset, ServiceId);
+ if (ServiceId >= 0) {
+ dispatchService(encobj_tree, tvb, pinfo, pOffset, ServiceId);
+ }
proto_item_set_end(ti, tvb, *pOffset);
return ServiceId;
}
-int parseOpenSecureChannel(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset)
+/**
+ * Stores the messages mode and signature length for this TCP connection.
+ * We need to know this mode in the following message to decide if decryption is required or not.
+ */
+void store_encryption_info(packet_info *pinfo, enum ua_message_mode mode, uint8_t sig_len)
{
- proto_item *ti;
- proto_item *ti_inner;
- proto_tree *encobj_tree;
- proto_tree *nodeid_tree;
- int ServiceId = 0;
-
- proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
- proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
- proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
- proto_tree_add_item(tree, hf_opcua_transport_scid, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
- parseString(tree, tvb, pinfo, pOffset, hf_opcua_transport_spu);
- parseByteString(tree, tvb, pinfo, pOffset, hf_opcua_transport_scert);
- parseByteString(tree, tvb, pinfo, pOffset, hf_opcua_transport_rcthumb);
- proto_tree_add_item(tree, hf_opcua_transport_seq, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
- proto_tree_add_item(tree, hf_opcua_transport_rqid, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
-
- /* add encodeable object subtree */
- encobj_tree = proto_tree_add_subtree(tree, tvb, *pOffset, -1, ett_opcua_extensionobject, &ti, "Message : Encodeable Object");
+ conversation_t *conv = find_conversation_pinfo(pinfo, 0);
+ if (conv) {
+ uintptr_t data = (uintptr_t)mode;
+ data |= ((uintptr_t)sig_len << 8);
+ conversation_add_proto_data(conv, proto_opcua, (void *)data);
+ }
+}
- /* add nodeid subtree */
- nodeid_tree = proto_tree_add_subtree(encobj_tree, tvb, *pOffset, -1, ett_opcua_nodeid, &ti_inner, "TypeId : ExpandedNodeId");
- ServiceId = parseServiceNodeId(nodeid_tree, tvb, pOffset);
- proto_item_set_end(ti_inner, tvb, *pOffset);
+/** Returns the message mode and signature length for current TCP connection. */
+void get_encryption_info(packet_info *pinfo, enum ua_message_mode *mode, uint8_t *sig_len)
+{
+ conversation_t *conv = find_conversation_pinfo(pinfo, 0);
+ if (conv) {
+ uintptr_t data = (uintptr_t)conversation_get_proto_data(conv, proto_opcua);
+ if (data == 0) {
+ *mode = g_opcua_default_sig_len ? UA_MessageMode_MaybeEncrypted : UA_MessageMode_None;
+ *sig_len = g_opcua_default_sig_len;
+ } else {
+ *mode = (enum ua_message_mode)(data & 0xff);
+ *sig_len = (uintptr_t)(data >> 8);
+ }
+ }
+}
- dispatchService(encobj_tree, tvb, pinfo, pOffset, ServiceId);
+/**
+ * Compares an unterminated string of a string constant.
+ *
+ * @param text Unterminated string to compare.
+ * @param text_len String data.
+ * @param ref_text Zero terminated string constant to compare with.
+ *
+ * @return 0 if equal, -1 if not.
+ */
+static int opcua_string_compare(const char *text, int text_len, const char *ref_text)
+{
+ int len = (int)strlen(ref_text);
+ if (text_len == len && memcmp(text, ref_text, len) == 0) return 0;
- proto_item_set_end(ti, tvb, *pOffset);
- return ServiceId;
+ return -1;
}
-int parseCloseSecureChannel(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset)
+int parseOpenSecureChannel(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int *pOffset, struct ua_metadata *data)
{
- proto_item *ti;
- proto_item *ti_inner;
- proto_tree *encobj_tree;
- proto_tree *nodeid_tree;
- int ServiceId = 0;
+ const uint8_t *sec_policy = NULL;
+ int sec_policy_len = 0;
+ int ServiceId = -1;
+ bool encrypted = false;
+ // Message Header
proto_tree_add_item(tree, hf_opcua_transport_type, tvb, *pOffset, 3, ENC_ASCII|ENC_NA); *pOffset+=3;
proto_tree_add_item(tree, hf_opcua_transport_chunk, tvb, *pOffset, 1, ENC_ASCII|ENC_NA); *pOffset+=1;
proto_tree_add_item(tree, hf_opcua_transport_size, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
proto_tree_add_item(tree, hf_opcua_transport_scid, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
+ // Asym Security Header
+ parseString_ret_string_and_length(tree, tvb, pinfo, pOffset, hf_opcua_transport_spu, &sec_policy, &sec_policy_len);
+ parseCertificate(tree, tvb, pinfo, pOffset, hf_opcua_transport_scert);
+ parseByteString(tree, tvb, pinfo, pOffset, hf_opcua_transport_rcthumb);
- parseSecurityLayer(tree, tvb, pOffset);
-
- /* add encodeable object subtree */
- encobj_tree = proto_tree_add_subtree(tree, tvb, *pOffset, -1, ett_opcua_extensionobject, &ti, "Message : Encodeable Object");
+ if (opcua_string_compare(sec_policy, sec_policy_len, UA_SECURITY_POLICY_NONE_STRING ) == 0) {
+ store_encryption_info(pinfo, UA_MessageMode_None, 0);
+ } else {
+ uint8_t sig_len = 0;
+ // OPN is always encrypted for Policies != None, for both message modes Sign and SignAndEncrypted
+ encrypted = true;
+ // determine signature length based on security policy
+ if (opcua_string_compare(sec_policy, sec_policy_len, UA_SECURITY_POLICY_BASIC128RSA15_STRING ) == 0) {
+ sig_len = 20;
+ } else if (opcua_string_compare(sec_policy, sec_policy_len, UA_SECURITY_POLICY_BASIC256_STRING ) == 0) {
+ sig_len = 20;
+ } else if (opcua_string_compare(sec_policy, sec_policy_len, UA_SECURITY_POLICY_BASIC256SHA256_STRING ) == 0) {
+ sig_len = 32;
+ } else if (opcua_string_compare(sec_policy, sec_policy_len, UA_SECURITY_POLICY_AES128_SHA256_RSAOAEP_STRING ) == 0) {
+ sig_len = 32;
+ } else if (opcua_string_compare(sec_policy, sec_policy_len, UA_SECURITY_POLICY_AES256_SHA256_RSAPSS_STRING ) == 0) {
+ sig_len = 32;
+ }
+ // We don't know the messagemode without decrypting the OPN, so we assume it is SignAndEncrypt,
+ // we will try to decode the next service (CreateSession) and if it succeeds we change the mode to Sign
+ // or SignAndEncrypt accordingly
+ store_encryption_info(pinfo, UA_MessageMode_MaybeEncrypted, sig_len);
+ }
+
+ data->encrypted = encrypted;
+ if (!encrypted) {
+ parseSequenceHeader(tree, tvb, pOffset, data);
+ ServiceId = parseService(tree, tvb, pinfo,pOffset, data);
+ }
- /* add nodeid subtree */
- nodeid_tree = proto_tree_add_subtree(encobj_tree, tvb, *pOffset, -1, ett_opcua_nodeid, &ti_inner, "TypeId : ExpandedNodeId");
- ServiceId = parseServiceNodeId(nodeid_tree, tvb, pOffset);
- proto_item_set_end(ti_inner, tvb, *pOffset);
+ return ServiceId;
+}
- dispatchService(encobj_tree, tvb, pinfo, pOffset, ServiceId);
+int parseCloseSecureChannel(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, int *pOffset, struct ua_metadata *data _U_)
+{
+ parseMessageHeader(tree, tvb, pinfo, pOffset, data);
+ proto_tree_add_item(tree, hf_opcua_transport_scid, tvb, *pOffset, 4, ENC_LITTLE_ENDIAN); *pOffset+=4;
- proto_item_set_end(ti, tvb, *pOffset);
- return ServiceId;
+ return -1;
}
/*