Add CIP Security objects

These objects are defined in Volume 8, but it doesn't make sense to create a new dissector file for them, so just distribute them where it makes the most sense in the existing CIP dissectors.

Also do some conversions to proto_tree_add_bitmask while in the neighborhood.

Add support for EtherNet/IP over DTLS/TLS.

Change-Id: I4e658e8871eebb222816229de7594ff766264308
Reviewed-on: https://code.wireshark.org/review/13710
Reviewed-by: D. Ulis <daulis0@gmail.com>
Reviewed-by: Michael Mann <mmann78@netscape.net>
This commit is contained in:
Michael Mann 2016-01-28 16:34:14 -05:00
parent b1aa1be33c
commit fb033d0f39
3 changed files with 653 additions and 79 deletions

View File

@ -479,6 +479,7 @@ static int hf_time_sync_port_proto_addr_info_port_proto_addr = -1;
static int hf_time_sync_steps_removed = -1;
static int hf_time_sync_sys_time_and_offset_time = -1;
static int hf_time_sync_sys_time_and_offset_offset = -1;
static int hf_cip_security_state = -1;
static int hf_conn_path_class = -1;
/* Initialize the subtree pointers */
@ -870,6 +871,15 @@ static const value_string cip_time_sync_network_protocol_vals[] = {
{ 0, NULL }
};
static const value_string cip_security_state_vals[] = {
{ 0, "Factory Default Configuration" },
{ 1, "Initial Commissioning In Progress" },
{ 2, "Configured" },
{ 3, "Incomplete Configuration" },
{ 0, NULL }
};
static const value_string cip_path_seg_vals[] = {
{ ((CI_PORT_SEGMENT>>5)&7), "Port Segment" },
@ -2583,6 +2593,9 @@ static const value_string cip_class_names_vals[] = {
{ 0x53, "Power Management Object" },
{ 0x54, "RSTP Bridge Object" },
{ 0x55, "RSTP Port Object" },
{ 0x5D, "CIP Security Object" },
{ 0x5E, "EtherNet/IP Security Object" },
{ 0x5F, "Certificate Management Object" },
{ 0xF0, "ControlNet Object" },
{ 0xF1, "ControlNet Keeper Object" },
{ 0xF2, "ControlNet Scheduling Object" },
@ -3258,6 +3271,9 @@ static attribute_info_t cip_attribute_vals[] = {
{0x43, FALSE, 26, -1, "Port Protocol Address Info", cip_dissector_func, NULL, dissect_time_sync_port_proto_addr_info},
{0x43, FALSE, 27, -1, "Steps Removed", cip_uint, &hf_time_sync_steps_removed, NULL},
{0x43, FALSE, 28, -1, "System Time and Offset", cip_dissector_func, NULL, dissect_time_sync_sys_time_and_offset},
/* CIP Security Object (instance attributes) */
{0x5D, FALSE, 1, 0, "State", cip_usint, &hf_cip_security_state, NULL},
};
typedef struct attribute_val_array {
@ -7512,6 +7528,7 @@ proto_register_cip(void)
{ &hf_time_sync_steps_removed, { "Steps Removed", "cip.time_sync.steps_removed", FT_UINT16, BASE_DEC, NULL, 0, NULL, HFILL }},
{ &hf_time_sync_sys_time_and_offset_time, { "System Time (Microseconds)", "cip.time_sync.sys_time_and_offset.time", FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL }},
{ &hf_time_sync_sys_time_and_offset_offset, { "System Offset (Microseconds)", "cip.time_sync.sys_time_and_offset.offset", FT_UINT64, BASE_DEC, NULL, 0, NULL, HFILL }},
{ &hf_cip_security_state, { "State", "cip.security.state", FT_UINT8, BASE_DEC, VALS(cip_security_state_vals), 0, NULL, HFILL }},
{ &hf_conn_path_class, { "CIP Connection Path Class", "cip.conn_path_class", FT_UINT16, BASE_HEX | BASE_EXT_STRING, &cip_class_names_vals_ext, 0, NULL, HFILL }},
};

View File

@ -46,12 +46,16 @@
#include "packet-cip.h"
#include "packet-enip.h"
#include "packet-cipsafety.h"
#include "packet-dtls.h"
#include "packet-ssl.h"
#include "packet-ssl-utils.h"
void proto_register_enip(void);
void proto_reg_handoff_enip(void);
/* Communication Ports */
#define ENIP_ENCAP_PORT 44818 /* EtherNet/IP located on port 44818 */
#define ENIP_SECURE_PORT 2221 /* EtherNet/IP TLS/DTLS port */
#define ENIP_IO_PORT 2222 /* EtherNet/IP IO located on port 2222 */
/* EtherNet/IP function codes */
@ -284,6 +288,40 @@ static int hf_dlr_aga_ip_addr = -1;
static int hf_dlr_aga_physical_address = -1;
static int hf_dlr_active_gateway_precedence = -1;
static int hf_eip_security_state = -1;
static int hf_eip_security_verify_client_cert = -1;
static int hf_eip_security_send_cert_chain = -1;
static int hf_eip_security_check_expiration = -1;
static int hf_eip_security_capability_flags = -1;
static int hf_eip_security_capflags_secure_renegotiation = -1;
static int hf_eip_security_capflags_reserved = -1;
static int hf_eip_security_num_avail_cipher_suites = -1;
static int hf_eip_security_avail_cipher_suite = -1;
static int hf_eip_security_num_allow_cipher_suites = -1;
static int hf_eip_security_allow_cipher_suite = -1;
static int hf_eip_security_num_psk = -1;
static int hf_eip_security_psk_identity_size = -1;
static int hf_eip_security_psk_identity = -1;
static int hf_eip_security_psk_size = -1;
static int hf_eip_security_psk = -1;
static int hf_eip_security_num_active_certs = -1;
static int hf_eip_security_active_cert_path_size = -1;
static int hf_eip_security_num_trusted_auths = -1;
static int hf_eip_security_trusted_auth_path_size = -1;
static int hf_eip_security_cert_revocation_list_path_size = -1;
static int hf_eip_cert_name = -1;
static int hf_eip_cert_state = -1;
static int hf_eip_cert_device_cert_status = -1;
static int hf_eip_cert_device_cert_path_size = -1;
static int hf_eip_cert_ca_cert_status = -1;
static int hf_eip_cert_ca_cert_path_size = -1;
static int hf_eip_cert_capflags_push = -1;
static int hf_eip_cert_capflags_reserved = -1;
static int hf_eip_cert_capability_flags = -1;
static int hf_eip_cert_num_certs = -1;
static int hf_eip_cert_cert_name = -1;
static int hf_eip_cert_path_size = -1;
/* Initialize the subtree pointers */
static gint ett_enip = -1;
static gint ett_path = -1;
@ -300,6 +338,12 @@ static gint ett_elink_interface_flags = -1;
static gint ett_elink_icontrol_bits = -1;
static gint ett_dlr_capability_flags = -1;
static gint ett_dlr_lnknbrstatus_flags = -1;
static gint ett_eip_security_capability_flags = -1;
static gint ett_eip_security_psk = -1;
static gint ett_eip_security_active_certs = -1;
static gint ett_eip_security_trusted_auths = -1;
static gint ett_eip_cert_capability_flags = -1;
static gint ett_eip_cert_num_certs = -1;
static expert_field ei_mal_tcpip_status = EI_INIT;
static expert_field ei_mal_tcpip_config_cap = EI_INIT;
@ -322,6 +366,13 @@ static expert_field ei_mal_dlr_active_supervisor_address = EI_INIT;
static expert_field ei_mal_dlr_capability_flags = EI_INIT;
static expert_field ei_mal_dlr_redundant_gateway_config = EI_INIT;
static expert_field ei_mal_dlr_active_gateway_address = EI_INIT;
static expert_field ei_mal_eip_security_capability_flags = EI_INIT;
static expert_field ei_mal_eip_security_avail_cipher_suites = EI_INIT;
static expert_field ei_mal_eip_security_allow_cipher_suites = EI_INIT;
static expert_field ei_mal_eip_security_preshared_keys = EI_INIT;
static expert_field ei_mal_eip_security_active_certs = EI_INIT;
static expert_field ei_mal_eip_security_trusted_auths = EI_INIT;
static expert_field ei_mal_eip_cert_capability_flags = EI_INIT;
static dissector_table_t subdissector_srrd_table;
static dissector_table_t subdissector_sud_table;
@ -573,6 +624,28 @@ static const value_string enip_dlr_redundant_gateway_status_vals[] = {
{ 0, NULL }
};
static const value_string eip_security_state_vals[] = {
{ 0, "Factory Default Configuration" },
{ 1, "Configuration In Progress" },
{ 2, "Configured" },
{ 0, NULL }
};
static const value_string eip_cert_state_vals[] = {
{ 0, "Created" },
{ 0, NULL }
};
static const value_string eip_cert_status_vals[] = {
{ 0, "Not Verified" },
{ 1, "Verified" },
{ 2, "Invalid" },
{ 0, NULL }
};
/* Translate interface handle to string */
static const value_string enip_interface_handle_vals[] = {
{ 0, "CIP" },
@ -1276,8 +1349,14 @@ dissect_tcpip_status(packet_info *pinfo, proto_tree *tree, proto_item *item, tvb
int offset, int total_len)
{
proto_item *status_item;
proto_tree *status_tree;
static const int * status[] = {
&hf_tcpip_status_interface_config,
&hf_tcpip_status_mcast_pending,
&hf_tcpip_status_interface_config_pending,
&hf_tcpip_status_acd,
&hf_tcpip_status_reserved,
NULL
};
if (total_len < 4)
{
@ -1285,14 +1364,7 @@ dissect_tcpip_status(packet_info *pinfo, proto_tree *tree, proto_item *item, tvb
return total_len;
}
status_item = proto_tree_add_item(tree, hf_tcpip_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
status_tree = proto_item_add_subtree(status_item, ett_tcpip_status);
proto_tree_add_item(status_tree, hf_tcpip_status_interface_config, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(status_tree, hf_tcpip_status_mcast_pending, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(status_tree, hf_tcpip_status_interface_config_pending, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(status_tree, hf_tcpip_status_acd, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(status_tree, hf_tcpip_status_reserved, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_bitmask(tree, tvb, offset, hf_tcpip_status, ett_tcpip_status, status, ENC_LITTLE_ENDIAN);
return 4;
}
@ -1301,8 +1373,18 @@ dissect_tcpip_config_cap(packet_info *pinfo, proto_tree *tree, proto_item *item,
int offset, int total_len)
{
proto_item *cap_item;
proto_tree *cap_tree;
static const int * capabilities[] = {
&hf_tcpip_config_cap_bootp,
&hf_tcpip_config_cap_dns,
&hf_tcpip_config_cap_dhcp,
&hf_tcpip_config_cap_dhcp_dns_update,
&hf_tcpip_config_cap_config_settable,
&hf_tcpip_config_cap_hardware_config,
&hf_tcpip_config_cap_interface_reset,
&hf_tcpip_config_cap_acd,
&hf_tcpip_config_cap_reserved,
NULL
};
if (total_len < 4)
{
@ -1310,18 +1392,7 @@ dissect_tcpip_config_cap(packet_info *pinfo, proto_tree *tree, proto_item *item,
return total_len;
}
cap_item = proto_tree_add_item(tree, hf_tcpip_config_cap, tvb, offset, 4, ENC_LITTLE_ENDIAN);
cap_tree = proto_item_add_subtree(cap_item, ett_tcpip_config_cap);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_bootp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_dns, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_dhcp, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_dhcp_dns_update, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_config_settable, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_hardware_config, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_interface_reset, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_acd, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(cap_tree, hf_tcpip_config_cap_reserved, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_bitmask(tree, tvb, offset, hf_tcpip_config_cap, ett_tcpip_config_cap, capabilities, ENC_LITTLE_ENDIAN);
return 4;
}
@ -1330,8 +1401,12 @@ dissect_tcpip_config_control(packet_info *pinfo, proto_tree *tree, proto_item *i
int offset, int total_len)
{
proto_item *control_item;
proto_tree *control_tree;
static const int * control_bits[] = {
&hf_tcpip_config_control_config,
&hf_tcpip_config_control_dns,
&hf_tcpip_config_control_reserved,
NULL
};
if (total_len < 4)
{
@ -1339,12 +1414,7 @@ dissect_tcpip_config_control(packet_info *pinfo, proto_tree *tree, proto_item *i
return total_len;
}
control_item = proto_tree_add_item(tree, hf_tcpip_config_control, tvb, offset, 4, ENC_LITTLE_ENDIAN);
control_tree = proto_item_add_subtree(control_item, ett_tcpip_config_control);
proto_tree_add_item(control_tree, hf_tcpip_config_control_config, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(control_tree, hf_tcpip_config_control_dns, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(control_tree, hf_tcpip_config_control_reserved, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_bitmask(tree, tvb, offset, hf_tcpip_config_control, ett_tcpip_config_control, control_bits, ENC_LITTLE_ENDIAN);
return 4;
}
@ -1467,8 +1537,15 @@ dissect_elink_interface_flags(packet_info *pinfo, proto_tree *tree, proto_item *
int offset, int total_len)
{
proto_item *flag_item;
proto_tree *flag_tree;
static const int * flags[] = {
&hf_elink_iflags_link_status,
&hf_elink_iflags_duplex,
&hf_elink_iflags_neg_status,
&hf_elink_iflags_manual_reset,
&hf_elink_iflags_local_hw_fault,
&hf_elink_iflags_reserved,
NULL
};
if (total_len < 4)
{
@ -1476,15 +1553,7 @@ dissect_elink_interface_flags(packet_info *pinfo, proto_tree *tree, proto_item *
return total_len;
}
flag_item = proto_tree_add_item(tree, hf_elink_interface_flags, tvb, offset, 4, ENC_LITTLE_ENDIAN);
flag_tree = proto_item_add_subtree(flag_item, ett_elink_interface_flags);
proto_tree_add_item(flag_tree, hf_elink_iflags_link_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_elink_iflags_duplex, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_elink_iflags_neg_status, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_elink_iflags_manual_reset, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_elink_iflags_local_hw_fault, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_elink_iflags_reserved, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_bitmask(tree, tvb, offset, hf_elink_interface_flags, ett_elink_interface_flags, flags, ENC_LITTLE_ENDIAN);
return 4;
}
@ -1560,8 +1629,12 @@ dissect_elink_interface_control(packet_info *pinfo, proto_tree *tree, proto_item
int offset, int total_len)
{
proto_item *control_item;
proto_tree *control_tree;
static const int * control_bits[] = {
&hf_elink_icontrol_control_bits_auto_neg,
&hf_elink_icontrol_control_bits_forced_duplex,
&hf_elink_icontrol_control_bits_reserved,
NULL
};
if (total_len < 4)
{
@ -1569,13 +1642,8 @@ dissect_elink_interface_control(packet_info *pinfo, proto_tree *tree, proto_item
return total_len;
}
control_item = proto_tree_add_item(tree, hf_elink_icontrol_control_bits, tvb, offset, 2, ENC_LITTLE_ENDIAN);
control_tree = proto_item_add_subtree(control_item, ett_elink_icontrol_bits);
proto_tree_add_item(control_tree, hf_elink_icontrol_control_bits_auto_neg, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(control_tree, hf_elink_icontrol_control_bits_forced_duplex, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(control_tree, hf_elink_icontrol_control_bits_reserved, tvb, offset, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_elink_icontrol_forced_speed, tvb, offset+2, 2, ENC_LITTLE_ENDIAN);
proto_tree_add_bitmask(tree, tvb, offset, hf_elink_icontrol_control_bits, ett_elink_icontrol_bits, control_bits, ENC_LITTLE_ENDIAN);
proto_tree_add_item(tree, hf_elink_icontrol_forced_speed, tvb, offset+2, 2, ENC_LITTLE_ENDIAN);
return 4;
}
@ -1674,8 +1742,16 @@ dissect_dlr_capability_flags(packet_info *pinfo, proto_tree *tree, proto_item *i
int offset, int total_len)
{
proto_item* flag_item;
proto_tree* flag_tree;
static const int * capabilities[] = {
&hf_dlr_capflags_announce_base_node,
&hf_dlr_capflags_beacon_base_node,
&hf_dlr_capflags_reserved1,
&hf_dlr_capflags_supervisor_capable,
&hf_dlr_capflags_redundant_gateway_capable,
&hf_dlr_capflags_flush_frame_capable,
&hf_dlr_capflags_reserved2,
NULL
};
if (total_len < 4)
{
@ -1683,16 +1759,7 @@ dissect_dlr_capability_flags(packet_info *pinfo, proto_tree *tree, proto_item *i
return total_len;
}
flag_item = proto_tree_add_item(tree, hf_dlr_capability_flags, tvb, offset, 4, ENC_LITTLE_ENDIAN);
flag_tree = proto_item_add_subtree(flag_item, ett_dlr_capability_flags);
proto_tree_add_item(flag_tree, hf_dlr_capflags_announce_base_node, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_dlr_capflags_beacon_base_node, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_dlr_capflags_reserved1, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_dlr_capflags_supervisor_capable, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_dlr_capflags_redundant_gateway_capable, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_dlr_capflags_flush_frame_capable, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_item(flag_tree, hf_dlr_capflags_reserved2, tvb, offset, 4, ENC_LITTLE_ENDIAN);
proto_tree_add_bitmask(tree, tvb, offset, hf_dlr_capability_flags, ett_dlr_capability_flags, capabilities, ENC_LITTLE_ENDIAN);
return 4;
}
@ -1731,7 +1798,281 @@ dissect_dlr_active_gateway_address(packet_info *pinfo, proto_tree *tree, proto_i
return 10;
}
attribute_info_t enip_attribute_vals[75] = {
static int
dissect_eip_security_cap(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
static const int * capabilities[] = {
&hf_eip_security_capflags_secure_renegotiation,
&hf_eip_security_capflags_reserved,
NULL
};
if (total_len < 4)
{
expert_add_info(pinfo, item, &ei_mal_eip_security_capability_flags);
return total_len;
}
proto_tree_add_bitmask(tree, tvb, offset, hf_eip_security_capability_flags, ett_eip_security_capability_flags, capabilities, ENC_LITTLE_ENDIAN);
return 4;
}
static int
dissect_eip_security_avail_cipher_suites(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
guint32 i, num_suites;
if (total_len < 1)
{
expert_add_info(pinfo, item, &ei_mal_eip_security_avail_cipher_suites);
return total_len;
}
proto_tree_add_item_ret_uint(tree, hf_eip_security_num_avail_cipher_suites, tvb, offset, 1, ENC_NA, &num_suites);
for (i = 0; i < num_suites; i++)
{
proto_tree_add_item(tree, hf_eip_security_avail_cipher_suite, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
return ((num_suites*2)+1);
}
static int
dissect_eip_security_allow_cipher_suites(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
guint32 i, num_suites;
if (total_len < 1)
{
expert_add_info(pinfo, item, &ei_mal_eip_security_allow_cipher_suites);
return total_len;
}
proto_tree_add_item_ret_uint(tree, hf_eip_security_num_allow_cipher_suites, tvb, offset, 1, ENC_NA, &num_suites);
for (i = 0; i < num_suites; i++)
{
proto_tree_add_item(tree, hf_eip_security_allow_cipher_suite, tvb, offset, 2, ENC_BIG_ENDIAN);
offset += 2;
}
return ((num_suites*2)+1);
}
static int
dissect_eip_security_preshared_keys(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
guint32 i, num, id_size, psk_size;
proto_item* ti;
proto_tree* psk_tree;
int start_offset = offset;
if (total_len < 3)
{
expert_add_info(pinfo, item, &ei_mal_eip_security_preshared_keys);
return total_len;
}
ti = proto_tree_add_item_ret_uint(tree, hf_eip_security_num_psk, tvb, offset, 1, ENC_NA, &num);
psk_tree = proto_item_add_subtree(ti, ett_eip_security_psk);
offset++;
for (i = 0; i < num; i++)
{
proto_tree_add_item_ret_uint(psk_tree, hf_eip_security_psk_identity_size, tvb, offset, 1, ENC_NA, &id_size);
if (total_len < (int)(id_size+2))
{
expert_add_info(pinfo, item, &ei_mal_eip_security_preshared_keys);
return total_len;
}
offset++;
proto_tree_add_item(psk_tree, hf_eip_security_psk_identity, tvb, offset, id_size, ENC_NA);
offset += id_size;
proto_tree_add_item_ret_uint(psk_tree, hf_eip_security_psk_size, tvb, offset, 1, ENC_NA, &psk_size);
offset++;
if (total_len < (int)(id_size+psk_size+2))
{
expert_add_info(pinfo, item, &ei_mal_eip_security_preshared_keys);
return total_len;
}
proto_tree_add_item(psk_tree, hf_eip_security_psk, tvb, offset, id_size, ENC_NA);
offset += psk_size;
}
proto_item_set_len(ti, offset-start_offset);
return offset-start_offset;
}
static int
dissect_eip_security_active_certs(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
guint32 i, num, path_size;
proto_item *ti, *path_item;
proto_tree* cert_tree;
int start_offset = offset;
if (total_len < 2)
{
expert_add_info(pinfo, item, &ei_mal_eip_security_active_certs);
return total_len;
}
ti = proto_tree_add_item_ret_uint(tree, hf_eip_security_num_active_certs, tvb, offset, 1, ENC_NA, &num);
cert_tree = proto_item_add_subtree(ti, ett_eip_security_active_certs);
offset++;
for (i = 0; i < num; i++)
{
path_item = proto_tree_add_item_ret_uint(cert_tree, hf_eip_security_active_cert_path_size, tvb, offset, 1, ENC_NA, &path_size);
offset++;
dissect_epath( tvb, pinfo, cert_tree, path_item, offset, path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY);
offset += path_size;
}
proto_item_set_len(ti, offset-start_offset);
return offset-start_offset;
}
static int
dissect_eip_security_trusted_auths(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
guint32 i, num, path_size;
proto_item *ti, *path_item;
proto_tree* cert_tree;
int start_offset = offset;
if (total_len < 2)
{
expert_add_info(pinfo, item, &ei_mal_eip_security_trusted_auths);
return total_len;
}
ti = proto_tree_add_item_ret_uint(tree, hf_eip_security_num_trusted_auths, tvb, offset, 1, ENC_NA, &num);
cert_tree = proto_item_add_subtree(ti, ett_eip_security_trusted_auths);
offset++;
for (i = 0; i < num; i++)
{
path_item = proto_tree_add_item_ret_uint(cert_tree, hf_eip_security_trusted_auth_path_size, tvb, offset, 1, ENC_NA, &path_size);
offset++;
dissect_epath( tvb, pinfo, cert_tree, path_item, offset, path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY);
offset += path_size;
}
proto_item_set_len(ti, offset-start_offset);
return offset-start_offset;
}
static int
dissect_eip_security_cert_revocation_list(packet_info *pinfo, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
int offset, int total_len _U_)
{
guint32 path_size;
proto_item *path_item;
int start_offset = offset;
path_item = proto_tree_add_item_ret_uint(tree, hf_eip_security_cert_revocation_list_path_size, tvb, offset, 1, ENC_NA, &path_size);
offset++;
dissect_epath( tvb, pinfo, tree, path_item, offset, path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY);
offset += path_size;
return offset-start_offset;
}
static int
dissect_eip_cert_cap_flags(packet_info *pinfo, proto_tree *tree, proto_item *item, tvbuff_t *tvb,
int offset, int total_len)
{
static const int * capabilities[] = {
&hf_eip_cert_capflags_push,
&hf_eip_cert_capflags_reserved,
NULL
};
if (total_len < 4)
{
expert_add_info(pinfo, item, &ei_mal_eip_cert_capability_flags);
return total_len;
}
proto_tree_add_bitmask(tree, tvb, offset, hf_eip_cert_capability_flags, ett_eip_cert_capability_flags, capabilities, ENC_LITTLE_ENDIAN);
return 4;
}
static int
dissect_eip_cert_cert_list(packet_info *pinfo, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
int offset, int total_len _U_)
{
guint32 i, num, path_size;
proto_item *ti, *path_item;
proto_tree* cert_tree;
int start_offset = offset;
ti = proto_tree_add_item_ret_uint(tree, hf_eip_cert_num_certs, tvb, offset, 1, ENC_NA, &num);
cert_tree = proto_item_add_subtree(ti, ett_eip_cert_num_certs);
offset++;
for (i = 0; i < num; i++)
{
path_size = tvb_get_guint8( tvb, offset );
proto_tree_add_item(tree, hf_eip_cert_cert_name, tvb, offset+1, path_size, ENC_ASCII|ENC_NA);
offset += (1+path_size);
path_item = proto_tree_add_item_ret_uint(cert_tree, hf_eip_cert_path_size, tvb, offset, 1, ENC_NA, &path_size);
offset++;
dissect_epath( tvb, pinfo, cert_tree, path_item, offset, path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY);
offset += path_size;
}
proto_item_set_len(ti, offset-start_offset);
return offset-start_offset;
}
static int
dissect_eip_cert_device_cert(packet_info *pinfo, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
int offset, int total_len _U_)
{
guint32 path_size;
proto_item *path_item;
int start_offset = offset;
proto_tree_add_item(tree, hf_eip_cert_device_cert_status, tvb, offset, 1, ENC_NA);
offset++;
path_item = proto_tree_add_item_ret_uint(tree, hf_eip_cert_device_cert_path_size, tvb, offset, 1, ENC_NA, &path_size);
offset++;
dissect_epath( tvb, pinfo, tree, path_item, offset, path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY);
offset += path_size;
return offset-start_offset;
}
static int
dissect_eip_cert_ca_cert(packet_info *pinfo, proto_tree *tree, proto_item *item _U_, tvbuff_t *tvb,
int offset, int total_len _U_)
{
guint32 path_size;
proto_item *path_item;
int start_offset = offset;
proto_tree_add_item(tree, hf_eip_cert_ca_cert_status, tvb, offset, 1, ENC_NA);
offset++;
path_item = proto_tree_add_item_ret_uint(tree, hf_eip_cert_ca_cert_path_size, tvb, offset, 1, ENC_NA, &path_size);
offset++;
dissect_epath( tvb, pinfo, tree, path_item, offset, path_size, FALSE, FALSE, NULL, NULL, NO_DISPLAY);
offset += path_size;
return offset-start_offset;
}
attribute_info_t enip_attribute_vals[99] = {
/* TCP/IP Object (class attributes) */
{0xF5, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
@ -1823,6 +2164,35 @@ attribute_info_t enip_attribute_vals[75] = {
{0x47, FALSE, 14, -1, "Redundant Gateway Status", cip_usint, &hf_dlr_redundant_gateway_status, NULL},
{0x47, FALSE, 15, -1, "Active Gateway Address", cip_dissector_func, NULL, dissect_dlr_active_gateway_address},
{0x47, FALSE, 16, -1, "Active Gateway Precedence", cip_usint, &hf_dlr_active_gateway_precedence, NULL},
/* EtherNet/IP Security object (instance attributes) */
{0x5E, FALSE, 1, 0, "State", cip_usint, &hf_eip_security_state, NULL},
{0x5E, FALSE, 2, 1, "Capability Flags", cip_dissector_func, NULL, dissect_eip_security_cap},
{0x5E, FALSE, 3, 2, "Available Cipher Suites", cip_dissector_func, NULL, dissect_eip_security_avail_cipher_suites},
{0x5E, FALSE, 4, 3, "Allowed Cipher Suites", cip_dissector_func, NULL, dissect_eip_security_allow_cipher_suites},
{0x5E, FALSE, 5, 4, "Pre-Shared Keys", cip_dissector_func, NULL, dissect_eip_security_preshared_keys},
{0x5E, FALSE, 6, 5, "Active Device Certificates", cip_dissector_func, NULL, dissect_eip_security_active_certs},
{0x5E, FALSE, 7, 6, "Trusted Authorities", cip_dissector_func, NULL, dissect_eip_security_trusted_auths},
{0x5E, FALSE, 8, 7, "Certificate Revocation List", cip_dissector_func, NULL, dissect_eip_security_cert_revocation_list},
{0x5E, FALSE, 9, 8, "Verify Client Certificate", cip_bool, &hf_eip_security_verify_client_cert, NULL},
{0x5E, FALSE, 10, 9, "Send Certificate Chain", cip_bool, &hf_eip_security_send_cert_chain, NULL},
{0x5E, FALSE, 11, 10, "Check Expiration", cip_bool, &hf_eip_security_check_expiration, NULL},
/* Certificate Management Object (class attributes) */
{0x5F, TRUE, 1, 0, CLASS_ATTRIBUTE_1_NAME, cip_uint, &hf_attr_class_revision, NULL },
{0x5F, TRUE, 2, 1, CLASS_ATTRIBUTE_2_NAME, cip_uint, &hf_attr_class_max_instance, NULL },
{0x5F, TRUE, 3, -1, CLASS_ATTRIBUTE_3_NAME, cip_uint, &hf_attr_class_num_instance, NULL },
{0x5F, TRUE, 4, -1, CLASS_ATTRIBUTE_4_NAME, cip_dissector_func, NULL, dissect_optional_attr_list },
{0x5F, TRUE, 5, -1, CLASS_ATTRIBUTE_5_NAME, cip_dissector_func, NULL, dissect_optional_service_list },
{0x5F, TRUE, 6, 2, CLASS_ATTRIBUTE_6_NAME, cip_uint, &hf_attr_class_num_class_attr, NULL },
{0x5F, TRUE, 7, 3, CLASS_ATTRIBUTE_7_NAME, cip_uint, &hf_attr_class_num_inst_attr, NULL },
{0x5F, TRUE, 8, 4, "Capability Flags", cip_dissector_func, NULL, dissect_eip_cert_cap_flags },
{0x5F, TRUE, 9, 5, "Certificate List", cip_dissector_func, NULL, dissect_eip_cert_cert_list },
{0x5F, FALSE, 1, 0, "Name", cip_short_string, &hf_eip_cert_name, NULL},
{0x5F, FALSE, 2, 1, "State", cip_usint, &hf_eip_cert_state, NULL},
{0x5F, FALSE, 3, 2, "Device Certificate", cip_dissector_func, NULL, dissect_eip_cert_device_cert},
{0x5F, FALSE, 4, 3, "CA Certificate", cip_dissector_func, NULL, dissect_eip_cert_ca_cert},
};
@ -2245,15 +2615,20 @@ classify_packet(packet_info *pinfo)
{
/* see if nature of packets can be derived from src/dst ports */
/* if so, return as found */
if ( ( ENIP_ENCAP_PORT == pinfo->srcport && ENIP_ENCAP_PORT != pinfo->destport ) ||
( ENIP_ENCAP_PORT != pinfo->srcport && ENIP_ENCAP_PORT == pinfo->destport ) ) {
if ( ENIP_ENCAP_PORT == pinfo->srcport )
return ENIP_RESPONSE_PACKET;
else if ( ENIP_ENCAP_PORT == pinfo->destport )
return ENIP_REQUEST_PACKET;
if (((ENIP_ENCAP_PORT == pinfo->srcport && ENIP_ENCAP_PORT != pinfo->destport)) ||
((ENIP_SECURE_PORT == pinfo->srcport && ENIP_SECURE_PORT != pinfo->destport)))
{
return ENIP_RESPONSE_PACKET;
}
else if (((ENIP_ENCAP_PORT != pinfo->srcport && ENIP_ENCAP_PORT == pinfo->destport)) ||
((ENIP_SECURE_PORT != pinfo->srcport && ENIP_SECURE_PORT == pinfo->destport)))
{
return ENIP_REQUEST_PACKET;
}
else
{
return ENIP_CANNOT_CLASSIFY;
}
/* else, cannot classify */
return ENIP_CANNOT_CLASSIFY;
}
static guint
@ -3527,9 +3902,173 @@ proto_register_enip(void)
{ &hf_dlr_active_gateway_precedence,
{ "Active Gateway Precedence", "cip.dlr.active_gateway_precedence",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }}
};
NULL, HFILL }},
{ &hf_eip_security_state,
{ "State", "cip.eip_security.state",
FT_UINT8, BASE_DEC, VALS(eip_security_state_vals), 0,
NULL, HFILL }},
{ &hf_eip_security_verify_client_cert,
{ "Verify Client Certificate", "cip.eip_security.verify_client_cert",
FT_BOOLEAN, 8, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_send_cert_chain,
{ "Send Certificate Chain", "cip.eip_security.send_cert_chain",
FT_BOOLEAN, 8, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_check_expiration,
{ "Check Expiration", "cip.eip_security.check_expiration",
FT_BOOLEAN, 8, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_capability_flags,
{ "Capability Flags", "cip.eip_security.capability_flags",
FT_UINT32, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_capflags_secure_renegotiation,
{ "Secure Renegotiation", "cip.eip_security.capability_flags.secure_renegotiation",
FT_BOOLEAN, 32, TFS(&tfs_supported_not_supported), 0x00000001,
NULL, HFILL }},
{ &hf_eip_security_capflags_reserved,
{ "Reserved", "cip.eip_security.capability_flags.reserved",
FT_UINT32, BASE_HEX, NULL, 0xFFFFFFFE,
NULL, HFILL }},
{ &hf_eip_security_num_avail_cipher_suites,
{ "Number of Available Cipher Suites", "cip.eip_security.num_avail_cipher_suites",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_avail_cipher_suite,
{ "Available Cipher Suite", "cip.eip_security.avail_cipher_suite",
FT_UINT16, BASE_HEX|BASE_EXT_STRING, &ssl_31_ciphersuite_ext, 0,
NULL, HFILL }},
{ &hf_eip_security_num_allow_cipher_suites,
{ "Number of Allowed Cipher Suites", "cip.eip_security.num_allow_cipher_suites",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_allow_cipher_suite,
{ "Allowed Cipher Suite", "cip.eip_security.allow_cipher_suite",
FT_UINT16, BASE_HEX|BASE_EXT_STRING, &ssl_31_ciphersuite_ext, 0,
NULL, HFILL }},
{ &hf_eip_security_num_psk,
{ "Number of PSKs", "cip.eip_security.num_psk",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_psk_identity_size,
{ "PSK Identity Size", "cip.eip_security.psk_identity_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_psk_identity,
{ "PSK Identity", "cip.eip_security.psk_identity",
FT_BYTES, BASE_NONE|BASE_ALLOW_ZERO, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_psk_size,
{ "PSK Size", "cip.eip_security.psk_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_psk,
{ "PSK", "cip.eip_security.psk",
FT_BYTES, BASE_NONE|BASE_ALLOW_ZERO, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_num_active_certs,
{ "Number of Active Certificates", "cip.eip_security.num_active_certs",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_active_cert_path_size,
{ "Active Certificates Path Size", "cip.eip_security.active_cert_path_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_num_trusted_auths,
{ "Number of Trusted Authorities", "cip.eip_security.num_trusted_auths",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_trusted_auth_path_size,
{ "Trusted Authorities Path Size", "cip.eip_security.trusted_auth_path_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_security_cert_revocation_list_path_size,
{ "Certificate Revocation List Path Size", "cip.eip_security.cert_revocation_list_path_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_cert_name,
{ "Name", "cip.eip_cert.name",
FT_STRING, BASE_NONE, NULL, 0,
NULL, HFILL }},
{ &hf_eip_cert_state,
{ "State", "cip.eip_cert.state",
FT_UINT8, BASE_DEC, VALS(eip_cert_state_vals), 0,
NULL, HFILL }},
{ &hf_eip_cert_device_cert_status,
{ "Certificate Status", "cip.eip_cert.device_cert.status",
FT_UINT8, BASE_DEC, VALS(eip_cert_status_vals), 0,
NULL, HFILL }},
{ &hf_eip_cert_device_cert_path_size,
{ "Path size", "cip.eip_cert.device_cert.path_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_cert_ca_cert_status,
{ "Certificate Status", "cip.eip_cert.ca_cert.status",
FT_UINT8, BASE_DEC, VALS(eip_cert_status_vals), 0,
NULL, HFILL }},
{ &hf_eip_cert_ca_cert_path_size,
{ "Path size", "cip.eip_cert.ca_cert.path_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_cert_capflags_push,
{ "Path size", "cip.eip_cert.capflags.push",
FT_BOOLEAN, 32, NULL, 0x00000001,
NULL, HFILL }},
{ &hf_eip_cert_capflags_reserved,
{ "Path size", "cip.eip_cert.capflags.reserved",
FT_BOOLEAN, 32, NULL, 0xFFFFFFFE,
NULL, HFILL }},
{ &hf_eip_cert_capability_flags,
{ "Capability flags", "cip.eip_cert.capflags",
FT_UINT32, BASE_HEX, NULL, 0,
NULL, HFILL }},
{ &hf_eip_cert_num_certs,
{ "Number of Certificates", "cip.eip_cert.num_certs",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_cert_path_size,
{ "Certificate path size", "cip.eip_cert.cert_path_size",
FT_UINT8, BASE_DEC, NULL, 0,
NULL, HFILL }},
{ &hf_eip_cert_cert_name,
{ "Certificate name", "cip.eip_cert.cert_name",
FT_STRING, BASE_NONE, NULL, 0,
NULL, HFILL }},
};
/* Setup protocol subtree array */
static gint *ett[] = {
@ -3547,7 +4086,13 @@ proto_register_enip(void)
&ett_elink_interface_flags,
&ett_elink_icontrol_bits,
&ett_dlr_capability_flags,
&ett_dlr_lnknbrstatus_flags
&ett_dlr_lnknbrstatus_flags,
&ett_eip_security_capability_flags,
&ett_eip_security_psk,
&ett_eip_security_active_certs,
&ett_eip_security_trusted_auths,
&ett_eip_cert_capability_flags,
&ett_eip_cert_num_certs,
};
static ei_register_info ei[] = {
@ -3572,6 +4117,13 @@ proto_register_enip(void)
{ &ei_mal_dlr_capability_flags, { "cip.malformed.dlr.capability_flags", PI_MALFORMED, PI_ERROR, "Malformed DLR Capability Flag", EXPFILL }},
{ &ei_mal_dlr_redundant_gateway_config, { "cip.malformed.dlr.redundant_gateway_config", PI_MALFORMED, PI_ERROR, "Malformed DLR Redundant Gateway Config", EXPFILL }},
{ &ei_mal_dlr_active_gateway_address, { "cip.malformed.dlr.active_gateway_address", PI_MALFORMED, PI_ERROR, "Malformed DLR Active Gateway Address", EXPFILL }},
{ &ei_mal_eip_security_capability_flags, { "cip.malformed.eip_security.capability_flags", PI_MALFORMED, PI_ERROR, "Malformed EIP Security Capability Flags", EXPFILL }},
{ &ei_mal_eip_security_avail_cipher_suites, { "cip.malformed.eip_security.avail_cipher_suites", PI_MALFORMED, PI_ERROR, "Malformed EIP Security Available Cipher Suites", EXPFILL }},
{ &ei_mal_eip_security_allow_cipher_suites, { "cip.malformed.eip_security.allow_cipher_suites", PI_MALFORMED, PI_ERROR, "Malformed EIP Security Allowed Cipher Suites", EXPFILL }},
{ &ei_mal_eip_security_preshared_keys, { "cip.malformed.eip_security.preshared_keys", PI_MALFORMED, PI_ERROR, "Malformed EIP Security Pre-Shared Keys", EXPFILL }},
{ &ei_mal_eip_security_active_certs, { "cip.malformed.eip_security.active_certs", PI_MALFORMED, PI_ERROR, "Malformed EIP Security Active Device Certificates", EXPFILL }},
{ &ei_mal_eip_security_trusted_auths, { "cip.malformed.eip_security.trusted_auths", PI_MALFORMED, PI_ERROR, "Malformed EIP Security Trusted Authorities", EXPFILL }},
{ &ei_mal_eip_cert_capability_flags, { "cip.malformed.eip_cert.capability_flags", PI_MALFORMED, PI_ERROR, "Malformed EIP Certificate Management Capability Flags", EXPFILL }},
};
/* Setup list of header fields for DLR See Section 1.6.1 for details*/
@ -3803,6 +4355,7 @@ proto_register_enip(void)
proto_enipio = proto_register_protocol("EtherNet/IP I/O", "ENIP I/O", "enip_io");
register_dissector("enip", dissect_enip_tcp, proto_enip);
register_dissector("enip_io", dissect_enipio, proto_enipio);
/* Required function calls to register the header fields and subtrees used */
proto_register_field_array(proto_enip, hf, array_length(hf));
@ -3863,7 +4416,7 @@ proto_reg_handoff_enip(void)
dissector_handle_t dlr_handle;
/* Register for EtherNet/IP, using TCP */
enip_tcp_handle = create_dissector_handle(dissect_enip_tcp, proto_enip);
enip_tcp_handle = find_dissector("enip");
dissector_add_uint("tcp.port", ENIP_ENCAP_PORT, enip_tcp_handle);
/* Register for EtherNet/IP, using UDP */
@ -3871,9 +4424,13 @@ proto_reg_handoff_enip(void)
dissector_add_uint("udp.port", ENIP_ENCAP_PORT, enip_udp_handle);
/* Register for EtherNet/IP IO data (UDP) */
enipio_handle = create_dissector_handle(dissect_enipio, proto_enipio);
enipio_handle = find_dissector("enip_io");
dissector_add_uint("udp.port", ENIP_IO_PORT, enipio_handle);
/* Register for EtherNet/IP TLS */
ssl_dissector_add(ENIP_SECURE_PORT, enip_tcp_handle);
dtls_dissector_add(ENIP_SECURE_PORT, enipio_handle);
/* Find dissector for data packet */
data_handle = find_dissector("data");

View File

@ -112,6 +112,6 @@ enum enip_connid_type {ECIDT_UNKNOWN, ECIDT_O2T, ECIDT_T2O};
void enip_close_cip_connection( packet_info *pinfo, guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber );
void enip_mark_connection_triad( packet_info *pinfo, guint16 ConnSerialNumber, guint16 VendorID, guint32 DeviceSerialNumber );
extern attribute_info_t enip_attribute_vals[75];
extern attribute_info_t enip_attribute_vals[99];
#endif /* PACKET_ENIP_H */