From e7c9ade670efe916990bc1891b03ce8fb1bf715a Mon Sep 17 00:00:00 2001 From: Anders Broman Date: Fri, 7 Dec 2012 09:33:40 +0000 Subject: [PATCH] From hannes: Enable the plugin to detect and reassemble chunked UA messages for displaying them correctly. From me: - Partly applied by hand. - move hf and ett asignments inseide the register routine as per convention. https://bugs.wireshark.org/bugzilla/show_bug.cgi?id=8068 svn path=/trunk/; revision=46436 --- plugins/opcua/opcua.c | 227 +++++++++++++++++++++++++- plugins/opcua/opcua_transport_layer.c | 17 +- plugins/opcua/opcua_transport_layer.h | 1 + 3 files changed, 230 insertions(+), 15 deletions(-) diff --git a/plugins/opcua/opcua.c b/plugins/opcua/opcua.c index 1116834609..91ca3bb8fc 100644 --- a/plugins/opcua/opcua.c +++ b/plugins/opcua/opcua.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "opcua_transport_layer.h" #include "opcua_security_layer.h" #include "opcua_application_layer.h" @@ -58,6 +59,48 @@ gint ett_opcua_transport = -1; gint ett_opcua_extensionobject = -1; gint ett_opcua_nodeid = -1; +static gint ett_opcua_fragment = -1; +static gint ett_opcua_fragments = -1; + +static int hf_opcua_fragments = -1; +static int hf_opcua_fragment = -1; +static int hf_opcua_fragment_overlap = -1; +static int hf_opcua_fragment_overlap_conflicts = -1; +static int hf_opcua_fragment_multiple_tails = -1; +static int hf_opcua_fragment_too_long_fragment = -1; +static int hf_opcua_fragment_error = -1; +static int hf_opcua_fragment_count = -1; +static int hf_opcua_reassembled_in = -1; +static int hf_opcua_reassembled_length = -1; +static int hf_opcua_reassembled_data = -1; + +static const fragment_items opcua_frag_items = { + /* Fragment subtrees */ + &ett_opcua_fragment, + &ett_opcua_fragments, + /* Fragment fields */ + &hf_opcua_fragments, + &hf_opcua_fragment, + &hf_opcua_fragment_overlap, + &hf_opcua_fragment_overlap_conflicts, + &hf_opcua_fragment_multiple_tails, + &hf_opcua_fragment_too_long_fragment, + &hf_opcua_fragment_error, + &hf_opcua_fragment_count, + /* Reassembled in field */ + &hf_opcua_reassembled_in, + /* Reassembled length field */ + &hf_opcua_reassembled_length, + /* Reassembled data field */ + &hf_opcua_reassembled_data, + /* Tag */ + "Message fragments" +}; + + +static GHashTable *opcua_fragment_table = NULL; +static GHashTable *opcua_reassembled_table = NULL; + /** OpcUa Transport Message Types */ enum MessageType { @@ -83,19 +126,59 @@ static char* g_szMessageTypes[] = }; -/** Setup protocol subtree array */ -static gint *ett[] = -{ - &ett_opcua_transport, - &ett_opcua_extensionobject, - &ett_opcua_nodeid, -}; /** plugin entry functions. * This registers the OpcUa protocol. */ void proto_register_opcua(void) { + + static hf_register_info hf[] = + { + {&hf_opcua_fragments, + {"Message fragments", "opcua.fragments", + FT_NONE, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_fragment, + {"Message fragment", "opcua.fragment", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_fragment_overlap, + {"Message fragment overlap", "opcua.fragment.overlap", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_fragment_overlap_conflicts, + {"Message fragment overlapping with conflicting data", + "opcua.fragment.overlap.conflicts", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_fragment_multiple_tails, + {"Message has multiple tail fragments", + "opcua.fragment.multiple_tails", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_fragment_too_long_fragment, + {"Message fragment too long", "opcua.fragment.too_long_fragment", + FT_BOOLEAN, 0, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_fragment_error, + {"Message defragmentation error", "opcua.fragment.error", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_fragment_count, + {"Message fragment count", "opcua.fragment.count", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_reassembled_in, + {"Reassembled in", "opcua.reassembled.in", + FT_FRAMENUM, BASE_NONE, NULL, 0x00, NULL, HFILL } }, + {&hf_opcua_reassembled_length, + {"Reassembled length", "opcua.reassembled.length", + FT_UINT32, BASE_DEC, NULL, 0x00, NULL, HFILL } } + }; + + /** Setup protocol subtree array */ + static gint *ett[] = + { + &ett_opcua_transport, + &ett_opcua_extensionobject, + &ett_opcua_nodeid, + &ett_opcua_fragment, + &ett_opcua_fragments + }; + module_t *opcua_module; proto_opcua = proto_register_protocol( @@ -117,6 +200,10 @@ void proto_register_opcua(void) range_convert_str(&global_tcp_ports_opcua, ep_strdup_printf("%u", OPCUA_PORT), 65535); + fragment_table_init(&opcua_fragment_table); + reassembled_table_init(&opcua_reassembled_table); + proto_register_field_array(proto_opcua, hf, array_length(hf)); + /* register user preferences */ opcua_module = prefs_register_protocol(proto_opcua, proto_reg_handoff_opcua); prefs_register_range_preference(opcua_module, "tcp_ports", @@ -206,10 +293,13 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree /* Clear out stuff in the info column */ col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]); - if (tree && pfctParse) + if (pfctParse) { gint offset = 0; int iServiceId = -1; + tvbuff_t *next_tvb = tvb; + gboolean bParseService = TRUE; + gboolean bIsLastFragment = FALSE; /* we are being asked for details */ proto_item *ti = NULL; @@ -218,9 +308,121 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA); transport_tree = proto_item_add_subtree(ti, ett_opcua_transport); + /* MSG_MESSAGE might be fragmented, check for that */ + if (msgtype == MSG_MESSAGE) + { + guint8 chunkType = 0; + guint32 opcua_seqid = 0; + guint32 opcua_num = 0; + guint32 opcua_seqnum = 0; + fragment_data *frag_msg = NULL; + + offset = 3; + + chunkType = tvb_get_guint8(tvb, offset); offset += 1; + + offset += 4; /* Message Size */ + offset += 4; /* SecureChannelId */ + offset += 4; /* Security Token Id */ + + opcua_num = tvb_get_letohl(tvb, offset); offset += 4; /* Security Sequence Number */ + opcua_seqid = tvb_get_letohl(tvb, offset); offset += 4; /* Security RequestId */ + + /* check if tvb is part of a chunked message: + the UA protocol does not tell us that, so we look into opcua_fragment_table and + opcua_reassembled_table if the opcua_seqid belongs to a chunked message */ + frag_msg = fragment_get(pinfo, opcua_seqid, opcua_fragment_table); + if (frag_msg == NULL) + { + frag_msg = fragment_get_reassembled_id(pinfo, opcua_seqid, opcua_reassembled_table); + } + + if (frag_msg != NULL || chunkType != 'F') + { + gboolean bSaveFragmented = pinfo->fragmented; + gboolean bMoreFragments = TRUE; + tvbuff_t *new_tvb = NULL; + + pinfo->fragmented = TRUE; + + if (frag_msg == NULL) + { + /* first fragment */ + offset = 0; + opcua_seqnum = 0; + } + else + { + /* the UA protocol does not number the chunks beginning from 0 but from a + arbitrary value, so we have to fake the numbers in the stored fragments. + this way Wireshark reassembles the message, as it expects the fragment + sequence numbers to start at 0 */ + while (frag_msg->next) {frag_msg = frag_msg->next;} + opcua_seqnum = frag_msg->offset + 1; + + if (chunkType == 'F') + { + bMoreFragments = FALSE; + } + } + + frag_msg = fragment_add_seq_check(tvb, + offset, + pinfo, + opcua_seqid, /* ID for fragments belonging together */ + opcua_fragment_table, /* list of message fragments */ + opcua_reassembled_table, /* list of reassembled messages */ + opcua_seqnum, /* fragment sequence number */ + tvb_length_remaining(tvb, offset), /* fragment length - to the end */ + bMoreFragments); /* More fragments? */ + + new_tvb = process_reassembled_data(tvb, + offset, + pinfo, + "Reassembled Message", + frag_msg, + &opcua_frag_items, + NULL, + transport_tree); + + if (new_tvb) + { + /* Reassembled */ + bIsLastFragment = TRUE; + } + else + { + /* Not last packet of reassembled UA message */ + col_append_fstr(pinfo->cinfo, COL_INFO, " (Message fragment %u)", opcua_num); + } + + if (new_tvb) + { + /* take it all */ + next_tvb = new_tvb; + } + else + { + /* only show transport header */ + bParseService = FALSE; + next_tvb = tvb_new_subset(tvb, 0, -1, -1); + } + + pinfo->fragmented = bSaveFragmented; + } + } + + offset = 0; + /* call the transport message dissector */ iServiceId = (*pfctParse)(transport_tree, tvb, &offset); + /* parse the service if not chunked or last chunk */ + if (msgtype == MSG_MESSAGE && bParseService) + { + iServiceId = parseService(transport_tree, next_tvb, &offset); + } + /* display the service type in addition to the message type */ if (iServiceId != -1) { @@ -229,7 +431,14 @@ static void dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree { if (g_requesttypes[indx].value == (guint32)iServiceId) { - col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], g_requesttypes[indx].strptr); + if (bIsLastFragment == FALSE) + { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], g_requesttypes[indx].strptr); + } + else + { + col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s (Message Reassembled)", g_szMessageTypes[msgtype], g_requesttypes[indx].strptr); + } break; } indx++; diff --git a/plugins/opcua/opcua_transport_layer.c b/plugins/opcua/opcua_transport_layer.c index 1009d8b5c5..5d22fa78b4 100644 --- a/plugins/opcua/opcua_transport_layer.c +++ b/plugins/opcua/opcua_transport_layer.c @@ -162,11 +162,6 @@ int parseError(proto_tree *tree, tvbuff_t *tvb, gint *pOffset) int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset) { - proto_item *ti; - 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; @@ -175,12 +170,22 @@ int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset) /* message data contains the security layer */ parseSecurityLayer(tree, tvb, pOffset); + return -1; +} + +int parseService(proto_tree *tree, tvbuff_t *tvb, gint *pOffset) +{ + proto_item *ti; + proto_tree *encobj_tree; + 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 */ - ti = proto_tree_add_text(tree, tvb, 0, -1, "Message : Encodeable Object"); + ti = proto_tree_add_text(tree, tvb, 0, -1, "OpcUa Service : Encodeable Object"); encobj_tree = proto_item_add_subtree(ti, ett_opcua_extensionobject); /* add nodeid subtree */ diff --git a/plugins/opcua/opcua_transport_layer.h b/plugins/opcua/opcua_transport_layer.h index 71779fb525..4ceb14b5ff 100644 --- a/plugins/opcua/opcua_transport_layer.h +++ b/plugins/opcua/opcua_transport_layer.h @@ -26,6 +26,7 @@ int parseHello(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); int parseAcknowledge(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); int parseError(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); int parseMessage(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); +int parseService(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); int parseOpenSecureChannel(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); int parseCloseSecureChannel(proto_tree *tree, tvbuff_t *tvb, gint *pOffset); int registerTransportLayerTypes(int proto);