1 /******************************************************************************
2 ** Copyright (C) 2006-2007 ascolab GmbH. All Rights Reserved.
3 ** Web: http://www.ascolab.com
4 **
5 ** SPDX-License-Identifier: GPL-2.0-or-later
6 **
7 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
8 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
9 **
10 ** Project: OpcUa Wireshark Plugin
11 **
12 ** Description: OpcUa Protocol Decoder.
13 **
14 ** Author: Gerhard Gappmeier <gerhard.gappmeier@ascolab.com>
15 ******************************************************************************/
16 
17 #include "config.h"
18 
19 #include <epan/packet.h>
20 #include <epan/reassemble.h>
21 #include <epan/dissectors/packet-tcp.h>
22 #include "opcua_transport_layer.h"
23 #include "opcua_security_layer.h"
24 #include "opcua_application_layer.h"
25 #include "opcua_complextypeparser.h"
26 #include "opcua_serviceparser.h"
27 #include "opcua_enumparser.h"
28 #include "opcua_simpletypes.h"
29 #include "opcua_hfindeces.h"
30 
31 void proto_register_opcua(void);
32 
33 extern const value_string g_requesttypes[];
34 extern const int g_NumServices;
35 
36 /* forward reference */
37 void proto_reg_handoff_opcua(void);
38 /* declare parse function pointer */
39 typedef int (*FctParse)(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo, gint *pOffset);
40 
41 int proto_opcua = -1;
42 static dissector_handle_t opcua_handle;
43 /** Official IANA registered port for OPC UA Binary Protocol. */
44 #define OPCUA_PORT_RANGE "4840"
45 
46 /** subtree types used in opcua_transport_layer.c */
47 gint ett_opcua_extensionobject = -1;
48 gint ett_opcua_nodeid = -1;
49 
50 /** subtree types used locally */
51 static gint ett_opcua_transport = -1;
52 static gint ett_opcua_fragment = -1;
53 static gint ett_opcua_fragments = -1;
54 
55 static int hf_opcua_fragments = -1;
56 static int hf_opcua_fragment = -1;
57 static int hf_opcua_fragment_overlap = -1;
58 static int hf_opcua_fragment_overlap_conflicts = -1;
59 static int hf_opcua_fragment_multiple_tails = -1;
60 static int hf_opcua_fragment_too_long_fragment = -1;
61 static int hf_opcua_fragment_error = -1;
62 static int hf_opcua_fragment_count = -1;
63 static int hf_opcua_reassembled_in = -1;
64 static int hf_opcua_reassembled_length = -1;
65 
66 static const fragment_items opcua_frag_items = {
67     /* Fragment subtrees */
68     &ett_opcua_fragment,
69     &ett_opcua_fragments,
70     /* Fragment fields */
71     &hf_opcua_fragments,
72     &hf_opcua_fragment,
73     &hf_opcua_fragment_overlap,
74     &hf_opcua_fragment_overlap_conflicts,
75     &hf_opcua_fragment_multiple_tails,
76     &hf_opcua_fragment_too_long_fragment,
77     &hf_opcua_fragment_error,
78     &hf_opcua_fragment_count,
79     /* Reassembled in field */
80     &hf_opcua_reassembled_in,
81     /* Reassembled length field */
82     &hf_opcua_reassembled_length,
83     /* Reassembled data field */
84     NULL,
85     /* Tag */
86     "Message fragments"
87 };
88 
89 
90 static reassembly_table opcua_reassembly_table;
91 
92 /** OpcUa Transport Message Types */
93 enum MessageType
94 {
95     MSG_HELLO = 0,
96     MSG_ACKNOWLEDGE,
97     MSG_ERROR,
98     MSG_REVERSEHELLO,
99     MSG_MESSAGE,
100     MSG_OPENSECURECHANNEL,
101     MSG_CLOSESECURECHANNEL,
102     MSG_INVALID
103 };
104 
105 /** OpcUa Transport Message Type Names */
106 static const char* g_szMessageTypes[] =
107 {
108     "Hello message",
109     "Acknowledge message",
110     "Error message",
111     "Reverse Hello message",
112     "UA Secure Conversation Message",
113     "OpenSecureChannel message",
114     "CloseSecureChannel message",
115     "Invalid message"
116 };
117 
118 
119 
120 
121 /** header length that is needed to compute
122   * the pdu length.
123   * @see get_opcua_message_len
124   */
125 #define FRAME_HEADER_LEN 8
126 
127 /** returns the length of an OpcUa message.
128   * This function reads the length information from
129   * the transport header.
130   */
131 static guint get_opcua_message_len(packet_info *pinfo _U_, tvbuff_t *tvb,
132                                    int offset, void *data _U_)
133 {
134     gint32 plen;
135 
136     /* the message length starts at offset 4 */
137     plen = tvb_get_letohl(tvb, offset + 4);
138 
139     return plen;
140 }
141 
142 /** The OpcUa message dissector.
143   * This method dissects full OpcUa messages.
144   * It gets only called with reassembled data
145   * from tcp_dissect_pdus.
146   */
147 static int dissect_opcua_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_)
148 {
149     FctParse pfctParse = NULL;
150     enum MessageType msgtype = MSG_INVALID;
151 
152     col_set_str(pinfo->cinfo, COL_PROTOCOL, "OpcUa");
153 
154     /* parse message type */
155     if (tvb_memeql(tvb, 0, "HEL", 3) == 0)
156     {
157         msgtype = MSG_HELLO;
158         pfctParse = parseHello;
159     }
160     else if (tvb_memeql(tvb, 0, "ACK", 3) == 0)
161     {
162         msgtype = MSG_ACKNOWLEDGE;
163         pfctParse = parseAcknowledge;
164     }
165     else if (tvb_memeql(tvb, 0, "ERR", 3) == 0)
166     {
167         msgtype = MSG_ERROR;
168         pfctParse = parseError;
169     }
170     else if (tvb_memeql(tvb, 0, "RHE", 3) == 0)
171     {
172         msgtype = MSG_REVERSEHELLO;
173         pfctParse = parseReverseHello;
174     }
175     else if (tvb_memeql(tvb, 0, "MSG", 3) == 0)
176     {
177         msgtype = MSG_MESSAGE;
178         pfctParse = parseMessage;
179     }
180     else if (tvb_memeql(tvb, 0, "OPN", 3) == 0)
181     {
182         msgtype = MSG_OPENSECURECHANNEL;
183         pfctParse = parseOpenSecureChannel;
184     }
185     else if (tvb_memeql(tvb, 0, "CLO", 3) == 0)
186     {
187         msgtype = MSG_CLOSESECURECHANNEL;
188         pfctParse = parseCloseSecureChannel;
189     }
190     else
191     {
192         msgtype = MSG_INVALID;
193 
194         /* Clear out stuff in the info column */
195         col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
196 
197         /* add empty item to make filtering by 'opcua' work */
198         proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA);
199 
200         return tvb_reported_length(tvb);
201     }
202 
203     /* Clear out stuff in the info column */
204     col_set_str(pinfo->cinfo, COL_INFO, g_szMessageTypes[msgtype]);
205 
206     if (pfctParse)
207     {
208         gint offset = 0;
209         int iServiceId = -1;
210         tvbuff_t *next_tvb = tvb;
211         gboolean bParseService = TRUE;
212         gboolean bIsLastFragment = FALSE;
213 
214         /* we are being asked for details */
215         proto_item *ti = NULL;
216         proto_tree *transport_tree = NULL;
217 
218         ti = proto_tree_add_item(tree, proto_opcua, tvb, 0, -1, ENC_NA);
219         transport_tree = proto_item_add_subtree(ti, ett_opcua_transport);
220 
221         /* MSG_MESSAGE might be fragmented, check for that */
222         if (msgtype == MSG_MESSAGE)
223         {
224             guint8 chunkType = 0;
225             guint32 opcua_seqid = 0;
226             guint32 opcua_num = 0;
227             guint32 opcua_seqnum = 0;
228             fragment_head *frag_msg = NULL;
229 
230             offset = 3;
231 
232             chunkType = tvb_get_guint8(tvb, offset); offset += 1;
233 
234             offset += 4; /* Message Size */
235             offset += 4; /* SecureChannelId */
236             offset += 4; /* Security Token Id */
237 
238             opcua_num = tvb_get_letohl(tvb, offset); offset += 4; /* Security Sequence Number */
239             opcua_seqid = tvb_get_letohl(tvb, offset); offset += 4; /* Security RequestId */
240 
241             if (chunkType == 'A')
242             {
243                 fragment_delete(&opcua_reassembly_table, pinfo, opcua_seqid, NULL);
244 
245                 col_clear_fence(pinfo->cinfo, COL_INFO);
246                 col_set_str(pinfo->cinfo, COL_INFO, "Abort message");
247 
248                 offset = 0;
249                 (*pfctParse)(transport_tree, tvb, pinfo, &offset);
250                 parseAbort(transport_tree, tvb, pinfo, &offset);
251 
252                 return tvb_reported_length(tvb);
253             }
254 
255             /* check if tvb is part of a chunked message:
256                the UA protocol does not tell us that, so we look into
257                opcua_reassembly_table if the opcua_seqid belongs to a
258                chunked message */
259             frag_msg = fragment_get(&opcua_reassembly_table, pinfo, opcua_seqid, NULL);
260             if (frag_msg == NULL)
261             {
262                 frag_msg = fragment_get_reassembled_id(&opcua_reassembly_table, pinfo, opcua_seqid);
263             }
264 
265             if (frag_msg != NULL || chunkType != 'F')
266             {
267                 gboolean bSaveFragmented = pinfo->fragmented;
268                 gboolean bMoreFragments = TRUE;
269                 tvbuff_t *new_tvb = NULL;
270 
271                 pinfo->fragmented = TRUE;
272 
273                 if (frag_msg == NULL)
274                 {
275                     /* first fragment */
276                     opcua_seqnum = 0;
277                 }
278                 else
279                 {
280                     /* the UA protocol does not number the chunks beginning from 0 but from a
281                        arbitrary value, so we have to fake the numbers in the stored fragments.
282                        this way Wireshark reassembles the message, as it expects the fragment
283                        sequence numbers to start at 0 */
284                     while (frag_msg->next) {frag_msg = frag_msg->next;}
285                     opcua_seqnum = frag_msg->offset + 1;
286 
287                     if (chunkType == 'F')
288                     {
289                         bMoreFragments = FALSE;
290                     }
291                 }
292 
293                 frag_msg = fragment_add_seq_check(&opcua_reassembly_table,
294                                                   tvb,
295                                                   offset,
296                                                   pinfo,
297                                                   opcua_seqid, /* ID for fragments belonging together */
298                                                   NULL,
299                                                   opcua_seqnum, /* fragment sequence number */
300                                                   tvb_captured_length_remaining(tvb, offset), /* fragment length - to the end */
301                                                   bMoreFragments); /* More fragments? */
302 
303                 new_tvb = process_reassembled_data(tvb,
304                                                    offset,
305                                                    pinfo,
306                                                    "Reassembled Message",
307                                                    frag_msg,
308                                                    &opcua_frag_items,
309                                                    NULL,
310                                                    transport_tree);
311 
312                 if (new_tvb)
313                 {
314                     /* Reassembled */
315                     bIsLastFragment = TRUE;
316                 }
317                 else
318                 {
319                     /* Not last packet of reassembled UA message */
320                     col_append_fstr(pinfo->cinfo, COL_INFO, " (Message fragment %u)", opcua_num);
321                 }
322 
323                 if (new_tvb)
324                 {
325                     /* take it all */
326                     next_tvb = new_tvb;
327                 }
328                 else
329                 {
330                     /* only show transport header */
331                     bParseService = FALSE;
332                     next_tvb = tvb_new_subset_remaining(tvb, 0);
333                 }
334 
335                 pinfo->fragmented = bSaveFragmented;
336             }
337         }
338 
339         offset = 0;
340 
341         /* call the transport message dissector */
342         iServiceId = (*pfctParse)(transport_tree, tvb, pinfo, &offset);
343 
344         /* parse the service if not chunked or last chunk */
345         if (msgtype == MSG_MESSAGE && bParseService)
346         {
347             if (bIsLastFragment != FALSE)
348             {
349                 offset = 0;
350             }
351             iServiceId = parseService(transport_tree, next_tvb, pinfo, &offset);
352         }
353 
354         /* display the service type in addition to the message type */
355         if (iServiceId != -1)
356         {
357             const gchar *szServiceName = val_to_str((guint32)iServiceId, g_requesttypes, "ServiceId %d");
358 
359             if (bIsLastFragment == FALSE)
360             {
361                 col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s", g_szMessageTypes[msgtype], szServiceName);
362             }
363             else
364             {
365                 col_add_fstr(pinfo->cinfo, COL_INFO, "%s: %s (Message Reassembled)", g_szMessageTypes[msgtype], szServiceName);
366             }
367         }
368     }
369 
370     return tvb_reported_length(tvb);
371 }
372 
373 /** The main OpcUa dissector functions.
374   * It uses tcp_dissect_pdus from packet-tcp.h
375   * to reassemble the TCP data.
376   */
377 static int dissect_opcua(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data)
378 {
379     tcp_dissect_pdus(tvb, pinfo, tree, TRUE, FRAME_HEADER_LEN,
380                      get_opcua_message_len, dissect_opcua_message, data);
381     return tvb_reported_length(tvb);
382 }
383 
384 /** plugin entry functions.
385  * This registers the OpcUa protocol.
386  */
387 void proto_register_opcua(void)
388 {
389     static hf_register_info hf[] =
390         {
391             /* id                                    full name                                              abbreviation                        type            display     strings bitmask blurb HFILL */
392             {&hf_opcua_fragments,                   {"Message fragments",                                   "opcua.fragments",                  FT_NONE,        BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
393             {&hf_opcua_fragment,                    {"Message fragment",                                    "opcua.fragment",                   FT_FRAMENUM,    BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
394             {&hf_opcua_fragment_overlap,            {"Message fragment overlap",                            "opcua.fragment.overlap",           FT_BOOLEAN,     BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
395             {&hf_opcua_fragment_overlap_conflicts,  {"Message fragment overlapping with conflicting data",  "opcua.fragment.overlap.conflicts", FT_BOOLEAN,     BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
396             {&hf_opcua_fragment_multiple_tails,     {"Message has multiple tail fragments",                 "opcua.fragment.multiple_tails",    FT_BOOLEAN,     BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
397             {&hf_opcua_fragment_too_long_fragment,  {"Message fragment too long",                           "opcua.fragment.too_long_fragment", FT_BOOLEAN,     BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
398             {&hf_opcua_fragment_error,              {"Message defragmentation error",                       "opcua.fragment.error",             FT_FRAMENUM,    BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
399             {&hf_opcua_fragment_count,              {"Message fragment count",                              "opcua.fragment.count",             FT_UINT32,      BASE_DEC,   NULL,   0x00,   NULL, HFILL}},
400             {&hf_opcua_reassembled_in,              {"Reassembled in",                                      "opcua.reassembled.in",             FT_FRAMENUM,    BASE_NONE,  NULL,   0x00,   NULL, HFILL}},
401             {&hf_opcua_reassembled_length,          {"Reassembled length",                                  "opcua.reassembled.length",         FT_UINT32,      BASE_DEC,   NULL,   0x00,   NULL, HFILL}}
402         };
403 
404     /** Setup protocol subtree array */
405     static gint *ett[] =
406         {
407             &ett_opcua_extensionobject,
408             &ett_opcua_nodeid,
409             &ett_opcua_transport,
410             &ett_opcua_fragment,
411             &ett_opcua_fragments
412         };
413 
414     proto_opcua = proto_register_protocol("OpcUa Binary Protocol", "OpcUa", "opcua");
415 
416     registerTransportLayerTypes(proto_opcua);
417     registerSecurityLayerTypes(proto_opcua);
418     registerApplicationLayerTypes(proto_opcua);
419     registerSimpleTypes(proto_opcua);
420     registerEnumTypes(proto_opcua);
421     registerComplexTypes();
422     registerServiceTypes();
423     registerFieldTypes(proto_opcua);
424 
425     proto_register_subtree_array(ett, array_length(ett));
426     proto_register_field_array(proto_opcua, hf, array_length(hf));
427 
428     reassembly_table_register(&opcua_reassembly_table,
429                           &addresses_reassembly_table_functions);
430 }
431 
432 void proto_reg_handoff_opcua(void)
433 {
434     opcua_handle = create_dissector_handle(dissect_opcua, proto_opcua);
435 
436     dissector_add_uint_range_with_preference("tcp.port", OPCUA_PORT_RANGE, opcua_handle);
437 }
438 
439 /*
440  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
441  *
442  * Local variables:
443  * c-basic-offset: 4
444  * tab-width: 8
445  * indent-tabs-mode: nil
446  * End:
447  *
448  * vi: set shiftwidth=4 tabstop=8 expandtab:
449  * :indentSize=4:tabSize=8:noTabs=true:
450  */
451