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 */
get_opcua_message_len(packet_info * pinfo _U_,tvbuff_t * tvb,int offset,void * data _U_)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 */
dissect_opcua_message(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data _U_)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 */
dissect_opcua(tvbuff_t * tvb,packet_info * pinfo,proto_tree * tree,void * data)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 */
proto_register_opcua(void)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
proto_reg_handoff_opcua(void)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