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