diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 20:34:10 +0000 |
commit | e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc (patch) | |
tree | 68cb5ef9081156392f1dd62a00c6ccc1451b93df /epan/dissectors/asn1/c1222 | |
parent | Initial commit. (diff) | |
download | wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.tar.xz wireshark-e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc.zip |
Adding upstream version 4.2.2.upstream/4.2.2
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'epan/dissectors/asn1/c1222')
-rw-r--r-- | epan/dissectors/asn1/c1222/CMakeLists.txt | 38 | ||||
-rw-r--r-- | epan/dissectors/asn1/c1222/c1222.asn | 81 | ||||
-rw-r--r-- | epan/dissectors/asn1/c1222/c1222.cnf | 115 | ||||
-rw-r--r-- | epan/dissectors/asn1/c1222/packet-c1222-template.c | 1496 | ||||
-rw-r--r-- | epan/dissectors/asn1/c1222/packet-c1222-template.h | 29 |
5 files changed, 1759 insertions, 0 deletions
diff --git a/epan/dissectors/asn1/c1222/CMakeLists.txt b/epan/dissectors/asn1/c1222/CMakeLists.txt new file mode 100644 index 00000000..96c3e46a --- /dev/null +++ b/epan/dissectors/asn1/c1222/CMakeLists.txt @@ -0,0 +1,38 @@ +# CMakeLists.txt +# +# Wireshark - Network traffic analyzer +# By Gerald Combs <gerald@wireshark.org> +# Copyright 1998 Gerald Combs +# +# SPDX-License-Identifier: GPL-2.0-or-later +# + +set( PROTOCOL_NAME c1222 ) + +set( PROTO_OPT ) + +set( EXT_ASN_FILE_LIST +) + +set( ASN_FILE_LIST + ${PROTOCOL_NAME}.asn +) + +set( EXTRA_DIST + ${ASN_FILE_LIST} + packet-${PROTOCOL_NAME}-template.c + packet-${PROTOCOL_NAME}-template.h + ${PROTOCOL_NAME}.cnf +) + +set( SRC_FILES + ${EXTRA_DIST} + ${EXT_ASN_FILE_LIST} +) + +set( A2W_FLAGS -b ) + +set( EXTRA_CNF +) + +ASN2WRS() diff --git a/epan/dissectors/asn1/c1222/c1222.asn b/epan/dissectors/asn1/c1222/c1222.asn new file mode 100644 index 00000000..a92781ce --- /dev/null +++ b/epan/dissectors/asn1/c1222/c1222.asn @@ -0,0 +1,81 @@ +-- C1222 PROTOCOL +-- + +C1222-PROTOCOL DEFINITIONS ::= +BEGIN + +MESSAGE ::= [APPLICATION 0] IMPLICIT SEQUENCE { + aSO-context [1] ASO-qualifier OPTIONAL, + called-AP-title [2] Called-AP-title OPTIONAL, + called-AP-invocation-id [4] Called-AP-invocation-id OPTIONAL, + calling-AP-title [6] Calling-AP-title OPTIONAL, + calling-AE-qualifier [7] Calling-AE-qualifier OPTIONAL, + calling-AP-invocation-id [8] Calling-AP-invocation-id, + mechanism-name [11] IMPLICIT Mechanism-name OPTIONAL, + calling-authentication-value [12] Calling-authentication-value OPTIONAL, + user-information [30] User-information +} + +ASO-qualifier ::= OBJECT IDENTIFIER + +Called-AP-title ::= CHOICE { + called-ap-title-abs OBJECT IDENTIFIER, + called-ap-title-rel [0] IMPLICIT RELATIVE-OID +} +Calling-AP-title ::= CHOICE { + calling-ap-title-abs OBJECT IDENTIFIER, + calling-ap-title-rel [0] IMPLICIT RELATIVE-OID +} +AP-invocation-id ::= INTEGER +Called-AP-invocation-id ::= AP-invocation-id +Calling-AP-invocation-id ::= AP-invocation-id +AE-qualifier ::= INTEGER +Calling-AE-qualifier ::= AE-qualifier +Mechanism-name ::= OBJECT IDENTIFIER + +-- this is all of the authentication mechanism related stuff + +Calling-authentication-value ::= [2] IMPLICIT SEQUENCE { + calling-authentication-value-indirect INTEGER DEFAULT 0, + calling-authentication-value-encoding Authentication-value-encoding +} + +Authentication-value-encoding ::= CHOICE { + calling-authentication-value-single-asn1 [0] Calling-authentication-value-single-asn1, + calling-authentication-value-octet-aligned [1] IMPLICIT OCTET STRING +} + +Calling-authentication-value-single-asn1 ::= CHOICE { + calling-authentication-value-c1222 Calling-authentication-value-c1222, + calling-authentication-value-c1221 Calling-authentication-value-c1221 +} + +Calling-authentication-value-c1222 ::= [1] IMPLICIT SEQUENCE { + key-id-element [0] IMPLICIT Key-id-element OPTIONAL, + iv-element [1] IMPLICIT Iv-element OPTIONAL +} + +Key-id-element ::= OCTET STRING (SIZE(1)) +Iv-element ::= OCTET STRING (SIZE(4)) + +Calling-authentication-value-c1221 ::= [0] IMPLICIT CHOICE { + c1221-auth-identification [0] IMPLICIT OCTET STRING (SIZE(3|5..259)), + c1221-auth-request [1] IMPLICIT OCTET STRING (SIZE(1..255)), + c1221-auth-response [2] IMPLICIT OCTET STRING (SIZE(0|1..255)), + ... +} + +-- now the user information (EPSEM) +User-information ::= EXTERNAL + +--User-information EPSEM-TYPE ::= { ... } + +--EPSEM-TYPE ::= CLASS { +-- &direct-reference OBJECT IDENTIFIER OPTIONAL, +-- &octet-aligned [1] IMPLICIT OCTET STRING +--} + + +END + + diff --git a/epan/dissectors/asn1/c1222/c1222.cnf b/epan/dissectors/asn1/c1222/c1222.cnf new file mode 100644 index 00000000..d3bf6dd5 --- /dev/null +++ b/epan/dissectors/asn1/c1222/c1222.cnf @@ -0,0 +1,115 @@ +# c1222.cnf +# C1222 conformation file +# Copyright 2010, Edward J. Beroset, edward.beroset@elster.com + +#.MODULE_IMPORT +IMPORT ../acse/acse.inf + +#.EXPORTS + +#.PDU +MESSAGE + +#.NO_EMIT + +#.TYPE_RENAME + +#.FIELD_RENAME + +#.TYPE_ATTR +Calling-authentication-value-c1222/_untag/iv-element BLURB="c1222.iv-element" +Calling-authentication-value-c1222/_untag/key-id-element BLURB = "c1222.key-id-element" +AP-invocation-id TYPE=FT_UINT32 +AE-qualifier TYPE=FT_UINT32 + +#.FN_BODY User-information + gint8 end_device_class; + bool pc, ind; + gint32 tag; + guint32 len; + proto_item *tf = NULL; + proto_tree *epsem_tree = NULL; + FILL_START; + + /* get Tag and Length */ + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &end_device_class, &pc, &tag); + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, &ind); + FILL_TABLE_TRUNCATE(user_information, len+offset-start_offset); + if (tag == 0x8) { /* BER_TAG_EXTERNAL */ + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &end_device_class, &pc, &tag); + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, &ind); + if (tag == 0x1) { /* implicit octet string */ + tf = proto_tree_add_item(tree, hf_c1222_user_information, tvb, offset, len, ENC_NA); + epsem_tree = proto_item_add_subtree(tf, ett_c1222_epsem); + dissect_epsem(tvb, offset, len, actx->pinfo, epsem_tree); + offset += len; + } + } + +#.FN_BODY ASO-qualifier + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(aSO_context); + +#.FN_BODY Called-AP-title + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE_APTITLE(called_AP_title); + +#.FN_BODY Called-AP-invocation-id + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(called_AP_invocation_id); + +#.FN_BODY Calling-AE-qualifier + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(calling_AE_qualifier); + +#.FN_BODY Calling-AP-invocation-id + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(calling_AP_invocation_id); + +#.FN_BODY Mechanism-name + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(mechanism_name); + +#.FN_BODY Calling-authentication-value + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(calling_authentication_value); + +#.FN_BODY Calling-AP-title + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE_APTITLE(calling_AP_title); + +#.FN_BODY Key-id-element + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(key_id_element); + +#.FN_BODY Iv-element + FILL_START; + %(DEFAULT_BODY)s + FILL_TABLE(iv_element); + +#.FN_BODY MESSAGE + clear_canon(); + %(DEFAULT_BODY)s +#.END + +# +# Editor modelines - https://www.wireshark.org/tools/modelines.html +# +# Local variables: +# c-basic-offset: 2 +# tab-width: 8 +# indent-tabs-mode: nil +# End: +# +# vi: set shiftwidth=2 tabstop=8 expandtab: +# :indentSize=2:tabSize=8:noTabs=true: +# diff --git a/epan/dissectors/asn1/c1222/packet-c1222-template.c b/epan/dissectors/asn1/c1222/packet-c1222-template.c new file mode 100644 index 00000000..276a9278 --- /dev/null +++ b/epan/dissectors/asn1/c1222/packet-c1222-template.c @@ -0,0 +1,1496 @@ +/* packet-c1222.c + * Routines for ANSI C12.22 packet dissection + * Copyright 2010, Edward J. Beroset, edward.beroset@elster.com + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "config.h" + +#include <epan/packet.h> +#include <epan/conversation.h> +#include <epan/expert.h> +#include <epan/prefs.h> +#include <epan/strutil.h> +#include <epan/uat.h> +#include <epan/oids.h> +#include <wsutil/eax.h> +#include "packet-ber.h" +#include "packet-tcp.h" +#include "packet-c1222.h" + +#define PNAME "ANSI C12.22" +#define PSNAME "C12.22" +#define PFNAME "c1222" +#define C1222_PORT 1153 /* TCP port */ + +/* C12.22 flag definitions */ +#define C1222_EPSEM_FLAG_RESERVED 0x80 +#define C1222_EPSEM_FLAG_RECOVERY_SESSION 0x40 +#define C1222_EPSEM_FLAG_PROXY_SERVICE_USED 0x20 +#define C1222_EPSEM_FLAG_ED_CLASS_INCLUDED 0x10 +#define C1222_EPSEM_FLAG_SECURITY_MODE 0x0c +#define C1222_EPSEM_FLAG_RESPONSE_CONTROL 0x03 + +#define C1222_PROCEDURE_RESPONSE 0xf000 +#define C1222_PROCEDURE_MFG 0x800 +#define C1222_PROCEDURE_NUMBER 0x7ff + +/* if the packet is encrypted, it can be + * good, bad, or simply not checked + */ +#define C1222_EPSEM_CRYPTO_GOOD 0x01 +#define C1222_EPSEM_CRYPTO_BAD 0x02 + +/* these defines are for each of the C12.22 services */ +#define C1222_CMD_IDENTIFY 0x20 +#define C1222_CMD_TERMINATE 0x21 +#define C1222_CMD_DISCONNECT 0x22 +#define C1222_CMD_FULL_READ 0x30 +#define C1222_CMD_DEFAULT_READ 0x3E +#define C1222_CMD_PARTIAL_READ_OFFSET 0x3F +#define C1222_CMD_FULL_WRITE 0x40 +#define C1222_CMD_DEFAULT_WRITE 0x4E +#define C1222_CMD_PARTIAL_WRITE_OFFSET 0x4F +#define C1222_CMD_LOGON 0x50 +#define C1222_CMD_SECURITY 0x51 +#define C1222_CMD_LOGOFF 0x52 +#define C1222_CMD_AUTHENTICATE 0x53 +#define C1222_CMD_NEGOTIATE 0x60 +#define C1222_CMD_WAIT 0x70 +#define C1222_CMD_TIMING_SETUP 0x71 + +void proto_register_c1222(void); + +static dissector_handle_t c1222_handle=NULL; +static dissector_handle_t c1222_udp_handle=NULL; + +/* Initialize the protocol and registered fields */ +static int proto_c1222 = -1; + +#include "packet-c1222-hf.c" +/* These are the EPSEM pieces */ +/* first, the flag components */ +static int hf_c1222_epsem_flags = -1; +static int hf_c1222_epsem_flags_reserved = -1; +static int hf_c1222_epsem_flags_recovery = -1; +static int hf_c1222_epsem_flags_proxy = -1; +static int hf_c1222_epsem_flags_ed_class = -1; +static int hf_c1222_epsem_flags_security_modes = -1; +static int hf_c1222_epsem_flags_response_control = -1; +/* and the structure of the flag components */ +static int * const c1222_flags[] = { + &hf_c1222_epsem_flags_reserved, + &hf_c1222_epsem_flags_recovery, + &hf_c1222_epsem_flags_proxy, + &hf_c1222_epsem_flags_ed_class, + &hf_c1222_epsem_flags_security_modes, + &hf_c1222_epsem_flags_response_control, + NULL +}; +/* next the optional ed_class */ +static int hf_c1222_epsem_ed_class = -1; +/* now the aggregate epsem */ +static int hf_c1222_epsem_total = -1; +/* generic command */ +static int hf_c1222_cmd = -1; +static int hf_c1222_err = -1; +static int hf_c1222_data = -1; +/* individual epsem fields */ +static int hf_c1222_logon_id = -1; +static int hf_c1222_logon_user = -1; +static int hf_c1222_security_password = -1; +static int hf_c1222_auth_len = -1; +static int hf_c1222_auth_data = -1; +static int hf_c1222_read_table = -1; +static int hf_c1222_read_offset = -1; +static int hf_c1222_read_count = -1; +static int hf_c1222_write_table = -1; +static int hf_c1222_write_offset = -1; +static int hf_c1222_write_size = -1; +static int hf_c1222_write_data = -1; +static int hf_c1222_procedure_response = -1; +static int hf_c1222_procedure_mfg = -1; +static int hf_c1222_procedure_num = -1; +static int hf_c1222_procedure_sequence = -1; +static int hf_c1222_write_chksum = -1; +static int hf_c1222_write_chksum_status = -1; +static int hf_c1222_wait_secs = -1; +static int hf_c1222_neg_pkt_size = -1; +static int hf_c1222_neg_nbr_pkts = -1; +static int hf_c1222_timing_setup_traffic = -1; +static int hf_c1222_timing_setup_inter_char = -1; +static int hf_c1222_timing_setup_resp_to = -1; +static int hf_c1222_timing_setup_nbr_retries = -1; + +/* the MAC */ +static int hf_c1222_epsem_mac = -1; + +/* crypto result flags */ +static int hf_c1222_epsem_crypto_good = -1; +static int hf_c1222_epsem_crypto_bad = -1; + +/* Initialize the subtree pointers */ +static int ett_c1222 = -1; +static int ett_c1222_epsem = -1; +static int ett_c1222_flags = -1; +static int ett_c1222_crypto = -1; +static int ett_c1222_cmd = -1; + +/* these pointers are for the header elements that may be needed to verify the crypto */ +static guint8 *aSO_context = NULL; +static guint8 *called_AP_title = NULL; +static guint8 *called_AP_invocation_id = NULL; +static guint8 *calling_AE_qualifier = NULL; +static guint8 *calling_AP_invocation_id = NULL; +static guint8 *mechanism_name = NULL; +static guint8 *calling_authentication_value = NULL; +static guint8 *user_information = NULL; +static guint8 *calling_AP_title = NULL; +static guint8 *key_id_element = NULL; +static guint8 *iv_element = NULL; + +/* these are the related lengths */ +static guint32 aSO_context_len = 0; +static guint32 called_AP_title_len = 0; +static guint32 called_AP_invocation_id_len = 0; +static guint32 calling_AE_qualifier_len = 0; +static guint32 calling_AP_invocation_id_len = 0; +static guint32 mechanism_name_len = 0; +static guint32 calling_authentication_value_len = 0; +static guint32 user_information_len = 0; +static guint32 calling_AP_title_len = 0; +static guint32 key_id_element_len = 0; +static guint32 iv_element_len = 0; + +/* these are the related allocation sizes (which might be different from the lengths) */ +static guint32 aSO_context_allocated = 0; +static guint32 called_AP_title_allocated = 0; +static guint32 called_AP_invocation_id_allocated = 0; +static guint32 calling_AE_qualifier_allocated = 0; +static guint32 calling_AP_invocation_id_allocated = 0; +static guint32 mechanism_name_allocated = 0; +static guint32 calling_authentication_value_allocated = 0; +static guint32 user_information_allocated = 0; +static guint32 calling_AP_title_allocated = 0; +static guint32 key_id_element_allocated = 0; +static guint32 iv_element_allocated = 0; + +#include "packet-c1222-ett.c" + +static expert_field ei_c1222_command_truncated = EI_INIT; +static expert_field ei_c1222_bad_checksum = EI_INIT; +static expert_field ei_c1222_epsem_missing = EI_INIT; +static expert_field ei_c1222_epsem_failed_authentication = EI_INIT; +static expert_field ei_c1222_epsem_not_decrypted = EI_INIT; +static expert_field ei_c1222_ed_class_missing = EI_INIT; +static expert_field ei_c1222_epsem_ber_length_error = EI_INIT; +static expert_field ei_c1222_epsem_field_length_error = EI_INIT; +static expert_field ei_c1222_mac_missing = EI_INIT; + +/* Preferences */ +static gboolean c1222_desegment = TRUE; +static gboolean c1222_decrypt = TRUE; +static gboolean c1222_big_endian = FALSE; +static const gchar *c1222_baseoid_str = NULL; +static guint8 *c1222_baseoid = NULL; +static guint c1222_baseoid_len = 0; + +/*------------------------------ + * Data Structures + *------------------------------ + */ +static const value_string c1222_security_modes[] = { + { 0x00, "Cleartext"}, + { 0x01, "Cleartext with authentication"}, + { 0x02, "Ciphertext with authentication"}, + { 0, NULL } +}; + +static const value_string c1222_response_control[] = { + { 0x00, "Always respond"}, + { 0x01, "Respond on exception"}, + { 0x02, "Never respond"}, + { 0, NULL } +}; + +static const value_string tableflags[] = { + { 0x00, "ST" }, + { 0x08, "MT" }, + { 0x10, "Pending ST" }, + { 0x18, "Pending MT" }, + { 0x20, "UDT" }, + { 0x30, "Pending UDT" }, + { 0, NULL } +}; + +static const value_string procflags[] = { + { 0x00, "SF" }, + { 0x08, "MF" }, + { 0, NULL } +}; + +static const value_string c1222_proc_response_control[] = { + { 0x00, "Post response in ST-8 on completion" }, + { 0x01, "Post response in ST-8 on exception" }, + { 0x02, "Do not post response in ST-8" }, + { 0x03, "Post response in ST-8 now, and on completion" }, + { 0, NULL } +}; + +static const value_string commandnames[] = { +/* error codes are in the range 0x00 - 0x1f inclusive */ + { 0x00, "OK" }, + { 0x01, "Error" }, + { 0x02, "Service Not Supported" }, + { 0x03, "Insufficient Security Clearance" }, + { 0x04, "Operation Not Possible" }, + { 0x05, "Inappropriate Action Requested" }, + { 0x06, "Device Busy" }, + { 0x07, "Data Not Ready" }, + { 0x08, "Data Locked" }, + { 0x09, "Renegotiate Request" }, + { 0x0A, "Invalid Service Sequence State" }, + { 0x0B, "Security Mechanism Error" }, + { 0x0C, "Unknown Application Title" }, + { 0x0D, "Network Time-out" }, + { 0x0E, "Network Not Reachable" }, + { 0x0F, "Request Too Large" }, + { 0x10, "Response Too Large" }, + { 0x11, "Segmentation Not Possible" }, + { 0x12, "Segmentation Error" }, +/* commands are in the range 0x20 - 0x7f inclusive */ + {C1222_CMD_IDENTIFY, "Identify" }, + {C1222_CMD_TERMINATE, "Terminate" }, + {C1222_CMD_DISCONNECT, "Disconnect" }, + {C1222_CMD_FULL_READ, "Full Read" }, + {C1222_CMD_DEFAULT_READ, "Default Read" }, + {C1222_CMD_PARTIAL_READ_OFFSET, "Partial Read Offset" }, + {C1222_CMD_FULL_WRITE, "Full Write" }, + {C1222_CMD_DEFAULT_WRITE, "Default Write" }, + {C1222_CMD_PARTIAL_WRITE_OFFSET, "Partial Write Offset" }, + {C1222_CMD_LOGON, "Logon" }, + {C1222_CMD_SECURITY, "Security" }, + {C1222_CMD_LOGOFF, "Logoff" }, + {C1222_CMD_AUTHENTICATE, "Authenticate" }, + {C1222_CMD_NEGOTIATE, "Negotiate" }, + {C1222_CMD_NEGOTIATE | 0x1, "Negotiate w/ 1 Baud Rate" }, + {C1222_CMD_NEGOTIATE | 0x2, "Negotiate w/ 2 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0x3, "Negotiate w/ 3 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0x4, "Negotiate w/ 4 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0x5, "Negotiate w/ 5 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0x6, "Negotiate w/ 6 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0x7, "Negotiate w/ 7 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0x8, "Negotiate w/ 8 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0x9, "Negotiate w/ 9 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0xA, "Negotiate w/ 10 Baud Rates" }, + {C1222_CMD_NEGOTIATE | 0xB, "Negotiate w/ 11 Baud Rates" }, + {C1222_CMD_WAIT, "Wait" }, + {C1222_CMD_TIMING_SETUP, "Timing Setup" }, + { 0, NULL } +}; + +/* these are for the key tables */ +typedef struct _c1222_uat_data { + guint keynum; + guchar *key; + guint keylen; +} c1222_uat_data_t; + +UAT_HEX_CB_DEF(c1222_users, keynum, c1222_uat_data_t) +UAT_BUFFER_CB_DEF(c1222_users, key, c1222_uat_data_t, key, keylen) + +static c1222_uat_data_t *c1222_uat_data = NULL; +static guint num_c1222_uat_data = 0; +static uat_t *c1222_uat; + +/* these macros ares used to populate fields needed to verify crypto */ +#define FILL_START int length, start_offset = offset; +#define FILL_TABLE(fieldname) \ + length = offset - start_offset; \ + fieldname = (guint8 *)tvb_memdup(actx->pinfo->pool, tvb, start_offset, length); \ + fieldname##_len = length; \ + fieldname##_allocated = length; +#define FILL_TABLE_TRUNCATE(fieldname, len) \ + length = 1 + 2*(offset - start_offset); \ + fieldname = (guint8 *)tvb_memdup(actx->pinfo->pool, tvb, start_offset, length); \ + fieldname##_len = len; \ + fieldname##_allocated = length; +#define FILL_TABLE_APTITLE(fieldname) \ + length = offset - start_offset; \ + switch (tvb_get_guint8(tvb, start_offset)) { \ + case 0x80: /* relative OID */ \ + tvb_ensure_bytes_exist(tvb, start_offset, length); \ + fieldname##_len = length + c1222_baseoid_len; \ + fieldname = (guint8 *)wmem_alloc(actx->pinfo->pool, fieldname##_len); \ + fieldname##_allocated = fieldname##_len; \ + fieldname[0] = 0x06; /* create absolute OID tag */ \ + fieldname[1] = (fieldname##_len - 2) & 0xff; \ + memcpy(&(fieldname[2]), c1222_baseoid, c1222_baseoid_len); \ + tvb_memcpy(tvb, &(fieldname[c1222_baseoid_len+2]), start_offset+2, length-2); \ + break; \ + case 0x06: /* absolute OID */ \ + default: \ + fieldname = (guint8 *)tvb_memdup(actx->pinfo->pool, tvb, start_offset, length); \ + fieldname##_len = length; \ + fieldname##_allocated = length; \ + break; \ + } + +/*------------------------------ + * Function Prototypes + *------------------------------ + */ +void proto_reg_handoff_c1222(void); + + +/*------------------------------ + * Code + *------------------------------ + */ + +/** + * Calculates simple one's complement checksum. + * + * \param tvb pointer to tvbuff containing data to be checksummed + * \param offset offset within tvbuff to beginning of data + * \param len length of data to be checksummed + * \returns calculated checksum + */ +static guint8 +c1222_cksum(tvbuff_t *tvb, gint offset, int len) +{ + guint8 sum; + for (sum = 0; len; offset++, len--) + sum += tvb_get_guint8(tvb, offset); + return ~sum + 1; +} +/** + * Dissects C12.22 packet in detail (with a tree). + * + * \param tvb input buffer containing packet to be dissected + * \param pinfo the packet info of the current data + * \param tree the tree to append this item to + * \param length length of data + * \param offset the offset in the tvb + */ +static void +parse_c1222_detailed(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int cmd, guint32 *length, int *offset) +{ + guint16 user_id = 0; + const guint8 *user_name = NULL; + const guint8 *password = NULL; + guint8 auth_len = 0; + gchar *auth_req = NULL; + guint16 table = 0; + guint16 tblsize = 0; + guint16 calcsum = 0; + guint8 wait_seconds = 0; + guint8 proc_seq = 0; + int numrates = 0; + guint16 packet_size; + guint16 procedure_num = 0; + guint8 nbr_packet; + /* timing setup parameters */ + guint8 traffic; + guint8 inter_char; + guint8 resp_to; + guint8 nbr_retries; + + /* special case to simplify handling of Negotiate service */ + if ((cmd & 0xF0) == C1222_CMD_NEGOTIATE) { + numrates = cmd & 0x0F; + cmd = C1222_CMD_NEGOTIATE; + } + proto_tree_add_uint(tree, cmd >= 0x20 ? hf_c1222_cmd : hf_c1222_err, tvb, *offset, 1, cmd); + (*offset)++; + (*length)--; + switch (cmd) { + case C1222_CMD_LOGON: + if (*length >= 12) { + user_id = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_logon_id, tvb, *offset, 2, user_id); + *offset += 2; + proto_tree_add_item_ret_string(tree, hf_c1222_logon_user, tvb, *offset, 10, ENC_ASCII|ENC_NA, pinfo->pool, &user_name); + *offset += 10; + *length -= 12; + proto_item_set_text(tree, "C12.22 EPSEM: %s (id %d, user \"%s\")", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), user_id, user_name); + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 LOGON command truncated"); + } + break; + case C1222_CMD_SECURITY: + if (*length >= 20) { + proto_tree_add_item_ret_string(tree, hf_c1222_security_password, tvb, *offset, 20, ENC_ASCII|ENC_NA, pinfo->pool, &password); + *offset += 20; + *length -= 20; + if (*length >= 2) { + user_id = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_logon_id, tvb, *offset, 2, user_id); + *offset += 2; + *length -= 2; + proto_item_set_text(tree, "C12.22 EPSEM: %s (password \"%s\", id %d)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), password, user_id); + } else { + proto_item_set_text(tree, "C12.22 EPSEM: %s (password \"%s\")", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), password); + } + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 SECURITY command truncated"); + } + break; + case C1222_CMD_AUTHENTICATE: + if (*length >= 1) { + auth_len = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_auth_len, tvb, *offset, 1, auth_len); + *offset += 1; + if (*length >= auth_len) { + auth_req = tvb_bytes_to_str(pinfo->pool, tvb, *offset, auth_len); + proto_tree_add_item(tree, hf_c1222_auth_data, tvb, *offset, auth_len, ENC_NA); + *offset += auth_len; + *length -= auth_len + 1; + proto_item_set_text(tree, "C12.22 EPSEM: %s (%d bytes: %s)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), auth_len, auth_req); + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 AUTHENTICATE command truncated"); + } + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 AUTHENTICATE command truncated"); + } + break; + case C1222_CMD_FULL_READ: + if (*length >= 2) { + table = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_read_table, tvb, *offset, 2, table); + proto_item_set_text(tree, "C12.22 EPSEM: %s (%s-%d)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), + val_to_str((table >> 8) & 0xF8, tableflags,"Unknown (0x%04x)"), table & 0x7FF); + *offset += 2; + *length -= 2; + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 READ command truncated"); + } + break; + case C1222_CMD_PARTIAL_READ_OFFSET: + if (*length >= 7) { + table = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_read_table, tvb, *offset, 2, table); + *offset += 2; + *length -= 2; + proto_tree_add_item(tree, hf_c1222_read_offset, tvb, *offset, 3, ENC_BIG_ENDIAN); + *offset += 3; + *length -= 3; + proto_tree_add_item(tree, hf_c1222_read_count, tvb, *offset, 2, ENC_BIG_ENDIAN); + *offset += 2; + *length -= 2; + proto_item_set_text(tree, "C12.22 EPSEM: %s (%s-%d)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), + val_to_str((table >> 8) & 0xF8, tableflags,"Unknown (0x%04x)"), table & 0x7FF); + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 READ command truncated"); + } + break; + case C1222_CMD_FULL_WRITE: + if (*length >= 5) { + table = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_write_table, tvb, *offset, 2, table); + *offset += 2; + *length -= 2; + tblsize = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_write_size, tvb, *offset, 2, tblsize); + *offset += 2; + *length -= 2; + if (*length >= tblsize+1U) { + if (table == 7) {/* is it a procedure call? */ + procedure_num = tvb_get_guint16(tvb, *offset, c1222_big_endian ? ENC_BIG_ENDIAN : ENC_LITTLE_ENDIAN); + proto_tree_add_uint(tree, hf_c1222_procedure_response, tvb, *offset, 2, procedure_num); + proto_tree_add_uint(tree, hf_c1222_procedure_mfg, tvb, *offset, 2, procedure_num); + proto_tree_add_uint(tree, hf_c1222_procedure_num, tvb, *offset, 2, procedure_num); + *offset += 2; + *length -= 2; + proc_seq = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_procedure_sequence, tvb, *offset, 1, proc_seq); + *offset += 1; + *length -= 1; + tblsize -= 3; + } + proto_tree_add_item(tree, hf_c1222_write_data, tvb, *offset, tblsize, ENC_NA); + *offset += tblsize; + *length -= tblsize; + if (table == 7) {/* is it a procedure call? */ + calcsum = c1222_cksum(tvb, (*offset)-tblsize-3, tblsize+3); + } else { + calcsum = c1222_cksum(tvb, (*offset)-tblsize, tblsize); + } + proto_tree_add_checksum(tree, tvb, *offset, hf_c1222_write_chksum, hf_c1222_write_chksum_status, + &ei_c1222_bad_checksum, pinfo, calcsum, ENC_NA, PROTO_CHECKSUM_VERIFY); + + if (table == 7) {/* is it a procedure call? */ + proto_item_set_text(tree, "C12.22 EPSEM: %s (%s-%d, %s-%d)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), + val_to_str((table >> 8) & 0xF8, tableflags,"Unknown (0x%04x)"), table & 0x7FF, + val_to_str((procedure_num >> 8) & 0x08, procflags,"Unknown (0x%04x)"), procedure_num & 0x7FF); + } else { + proto_item_set_text(tree, "C12.22 EPSEM: %s (%s-%d)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), + val_to_str((table >> 8) & 0xF8, tableflags,"Unknown (0x%04x)"), table & 0x7FF); + } + *offset += 1; + *length -= 1; + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 WRITE command truncated"); + } + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 WRITE command truncated"); + } + break; + case C1222_CMD_PARTIAL_WRITE_OFFSET: + if (*length >= 8) { + table = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_write_table, tvb, *offset, 2, table); + *offset += 2; + *length -= 2; + proto_tree_add_item(tree, hf_c1222_write_offset, tvb, *offset, 3, ENC_BIG_ENDIAN); + *offset += 3; + *length -= 3; + tblsize = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_write_size, tvb, *offset, 2, tblsize); + *offset += 2; + *length -= 2; + if (*length >= tblsize+1U) { + proto_tree_add_item(tree, hf_c1222_write_data, tvb, *offset, tblsize, ENC_NA); + *offset += tblsize; + *length -= tblsize; + calcsum = c1222_cksum(tvb, (*offset)-tblsize, tblsize); + proto_tree_add_checksum(tree, tvb, *offset, hf_c1222_write_chksum, hf_c1222_write_chksum_status, + &ei_c1222_bad_checksum, pinfo, calcsum, ENC_NA, PROTO_CHECKSUM_VERIFY); + proto_item_set_text(tree, "C12.22 EPSEM: %s (%s-%d)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), + val_to_str((table >> 8) & 0xF8, tableflags,"Unknown (0x%04x)"), table & 0x7FF); + *offset += 1; + *length -= 1; + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 WRITE command truncated"); + } + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 WRITE command truncated"); + } + break; + case C1222_CMD_WAIT: + if (*length >= 1) { + wait_seconds = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_wait_secs, tvb, *offset, 1, wait_seconds); + *offset += 1; + *length -= 1; + proto_item_set_text(tree, "C12.22 EPSEM: %s (%d seconds)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), wait_seconds); + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 WAIT command truncated"); + } + break; + case C1222_CMD_NEGOTIATE: + if (*length >= 3) { + packet_size = tvb_get_ntohs(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_neg_pkt_size, tvb, *offset, 2, packet_size); + *offset += 2; + *length -= 2; + nbr_packet = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_neg_nbr_pkts, tvb, *offset, 1, nbr_packet); + *offset += 1; + *length -= 1; + proto_item_set_text(tree, "C12.22 EPSEM: %s (pkt size %d, num pkts %d, with %d baud rates)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), packet_size, nbr_packet, numrates); + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 NEGOTIATE command truncated"); + } + break; + case C1222_CMD_TIMING_SETUP: + if (*length >= 4) { + traffic = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_timing_setup_traffic, tvb, *offset, 1, traffic); + *offset += 1; + *length -= 1; + inter_char = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_timing_setup_inter_char, tvb, *offset, 1, inter_char); + *offset += 1; + *length -= 1; + resp_to = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_timing_setup_resp_to, tvb, *offset, 1, resp_to); + *offset += 1; + *length -= 1; + nbr_retries = tvb_get_guint8(tvb, *offset); + proto_tree_add_uint(tree, hf_c1222_timing_setup_nbr_retries, tvb, *offset, 1, nbr_retries); + *offset += 1; + *length -= 1; + proto_item_set_text(tree, "C12.22 EPSEM: %s (traffic to %d s, inter-char to %d s, response to %d s, %d retries)", + val_to_str(cmd,commandnames,"Unknown (0x%02x)"), traffic, inter_char, resp_to, nbr_retries); + } else { + expert_add_info_format(pinfo, tree, &ei_c1222_command_truncated, "C12.22 NEGOTIATE command truncated"); + } + break; + + default: + /* don't do anything */ + proto_item_set_text(tree, "C12.22 EPSEM: %s", val_to_str(cmd, commandnames, "Unknown (0x%02x)")); + if (*length) { + proto_tree_add_item(tree, hf_c1222_data, tvb, *offset, *length, ENC_NA); + } + break; + } +} + +typedef struct tagTOP_ELEMENT_CONTROL +{ + /* TRUE if this tag is required */ + gboolean required; + /* TRUE if we must truncate this tag */ + gboolean truncate; + /* actual hex value of the tag we're seeking */ + guint8 tag; + /* if TRUE, add tag and length before copying */ + gboolean addtag; + /* pointer to pointer to memory copy of element */ + guint8 **element; + /* pointer to element length */ + guint32 *length; + /* pointer to element allocated size */ + guint32 *allocated; +} TOP_ELEMENT_CONTROL; + +static const TOP_ELEMENT_CONTROL canonifyTable[] = { + { FALSE, FALSE, 0xA1, TRUE, &aSO_context, &aSO_context_len, &aSO_context_allocated }, + { TRUE , FALSE, 0xA2, TRUE, &called_AP_title, &called_AP_title_len, &called_AP_title_allocated }, + { FALSE, FALSE, 0xA4, TRUE, &called_AP_invocation_id, &called_AP_invocation_id_len, &called_AP_invocation_id_allocated }, + { FALSE, FALSE, 0xA7, TRUE, &calling_AE_qualifier, &calling_AE_qualifier_len, &calling_AE_qualifier_allocated }, + { TRUE, FALSE, 0xA8, TRUE, &calling_AP_invocation_id, &calling_AP_invocation_id_len, &calling_AP_invocation_id_allocated }, + { FALSE, FALSE, 0x8B, TRUE, &mechanism_name, &mechanism_name_len, &mechanism_name_allocated }, + { FALSE, FALSE, 0xAC, TRUE, &calling_authentication_value, &calling_authentication_value_len, &calling_authentication_value_allocated }, + { TRUE , TRUE , 0xBE, TRUE, &user_information, &user_information_len, &user_information_allocated }, + { FALSE, FALSE, 0xA6, TRUE, &calling_AP_title, &calling_AP_title_len, &calling_AP_title_allocated }, + { FALSE, FALSE, 0xAC, FALSE, &key_id_element, &key_id_element_len, &key_id_element_allocated }, + { FALSE, FALSE, 0xAC, FALSE, &iv_element, &iv_element_len, &iv_element_allocated }, + { FALSE, FALSE, 0x0, TRUE, NULL, NULL, NULL } +}; + +static void +clear_canon(void) +{ + const TOP_ELEMENT_CONTROL *t = canonifyTable; + + for (t = canonifyTable; t->element != NULL; t++) { + *(t->length) = 0; + *(t->element) = NULL; + } +} + +/** + * Calculates the size of the passed number n as encoded as a BER length field. + * + * \param n is the length value to be BER encoded + * \returns the sized of the encoding + */ +static guint32 +get_ber_len_size(guint32 n) +{ + guint32 len = 1; + if (n > 0x7f) len++; + if (n > 0xff) len++; + if (n > 0xffff) len++; + if (n > 0xffffff) len++; + return len; +} +/** + * Encodes the passed value n as a BER-encoded length at puts it in memory. + * + * \param ptr points to the buffer to be written + * \param n is the length to be BER encoded + * \param maxsize is the maximum number of bytes we're allowed to write + * \returns length of encoded value in bytes + */ +static int +encode_ber_len(guint8 *ptr, guint32 n, int maxsize) +{ + int len = get_ber_len_size(n); + if (len > maxsize) return 0; + if (len == 1) { + *ptr = 0x7f & n; + } else { + *ptr = (len -1) | 0x80; + for (ptr += len-1; n; n >>= 8) + *ptr-- = n & 0xff; + } + return len; + +} + +static void* +c1222_uat_data_copy_cb(void *dest, const void *source, size_t len _U_) +{ + const c1222_uat_data_t* o = (const c1222_uat_data_t*)source; + c1222_uat_data_t* d = (c1222_uat_data_t*)dest; + + d->keynum = o->keynum; + d->keylen = o->keylen; + d->key = (guchar *)g_memdup2(o->key, o->keylen); + + return dest; +} + +/** + * Checks a new encryption table item for validity. + * + * \param n points to the new record + * \param err is updated to point to an error string if needed + * \return FALSE if error; TRUE otherwise + */ +static bool +c1222_uat_data_update_cb(void* n, char** err) +{ + c1222_uat_data_t* new_rec = (c1222_uat_data_t *)n; + + if (new_rec->keynum > 0xff) { + *err = g_strdup("Invalid key number; must be less than 256"); + return FALSE; + } + if (new_rec->keylen != EAX_SIZEOF_KEY) { + *err = g_strdup("Invalid key size; must be 16 bytes"); + return FALSE; + } + return TRUE; +} + +static void +c1222_uat_data_free_cb(void *r) +{ + c1222_uat_data_t *rec = (c1222_uat_data_t *)r; + g_free(rec->key); +} + +/** + * Canonifies header fields in preparation for authenticating and/or decrypting the packet. + * + * \param buff points to the allocated canonization buffer + * \param offset points to start of unallocated space in buffer and + is updated as we put bytes into buffer + * \param buffsize total size of allocated buffer + * \return FALSE if element is required and not present; otherwise TRUE + */ +static gboolean +canonify_unencrypted_header(guchar *buff, guint32 *offset, guint32 buffsize) +{ + const TOP_ELEMENT_CONTROL *t = canonifyTable; + guint32 len, allocated; + + for (t = canonifyTable; t->element != NULL; t++) + { + len = *(t->length); + allocated = *(t->allocated); + if (t->required && *(t->element) == NULL) + return FALSE; + if (*(t->element) != NULL) { + if (t->addtag) { + /* recreate original tag and length */ + buff[(*offset)++] = t->tag; + (*offset) += encode_ber_len(&buff[*offset], len, 4); + } + if (t->truncate) { + len = 3+2*get_ber_len_size(len); + } + /* bail out if the cannonization buffer is too small */ + /* this should never happen! */ + if (buffsize < *offset + len) { + return FALSE; + } + /* bail out if our we're trying to read past the end of our element */ + /* the network is always hostile */ + if (allocated < len) { + return FALSE; + } + memcpy(&buff[*offset], *(t->element), len); + (*offset) += len; + if (t->addtag) { + *(t->element) = NULL; + } + } + } + return TRUE; +} + +/** + * Looks up the required key in the key table. + * + * \param keybuff is updated with a copy of the key data if successful lookup. + * \param keyid is the ID number of the desired key + * \returns TRUE if key was found; otherwise FALSE + */ +static gboolean +keylookup(guint8 *keybuff, guint8 keyid) +{ + guint i; + + if (c1222_uat_data == NULL) + return FALSE; + for (i = 0; i < num_c1222_uat_data; i++) { + if (c1222_uat_data[i].keynum == keyid) { + memcpy(keybuff, c1222_uat_data[i].key, EAX_SIZEOF_KEY); + return TRUE; + } + } + return FALSE; +} + +/** + * Authenticates and decrypts the passed packet. + * + * \param buffer points to a memory copy of the packet to be authenticated/decrypted + * and contains the decrypted value on successful return. + * \param length lenth of input packet + * \param decrypt TRUE if packet is to be authenticated and decrypted; FALSE if authentication only is requested + * \returns TRUE if the requested operation was successful; otherwise FALSE + */ +static gboolean +decrypt_packet(guchar *buffer, guint32 length, gboolean decrypt) +{ +#define CANONBUFFSIZE 300U + guchar canonbuff[CANONBUFFSIZE]; + guint8 c1222_key[EAX_SIZEOF_KEY]; + guchar key_id = 0; + guint32 offset = 0; + gboolean status = FALSE; + + /* must be at least 4 bytes long to include the MAC */ + if (length < 4) + return status; + if (key_id_element != NULL) + key_id = key_id_element[0]; + /* extract unencrypted header information */ + if (!canonify_unencrypted_header(canonbuff, &offset, CANONBUFFSIZE)) + return status; + /* decrypt and authenticate in place */ +/* PARAMETERS: pN : Pointer to ClearText (Input, Canonified form). */ +/* pK : Pointer to secret key (Input). */ +/* pC : Pointer to CipherText (Input/Output). */ +/* SizeN : Byte length of ClearText buffer. */ +/* SizeK : Byte length of secret key. */ +/* SizeC : Byte length of CipherText buffer. */ +/* pMac : Four byte Message Authentication Code. */ +/* Mode : Operating mode (See EAX_MODE_xxx). */ +/* RETURNS: TRUE if message has been authenticated. */ +/* FALSE if not authenticated, invalid Mode, or error. */ + if (offset) { + if (!keylookup((guint8 *)&c1222_key, key_id)) + return FALSE; + status = Eax_Decrypt(canonbuff, c1222_key, buffer, + offset, EAX_SIZEOF_KEY, length-4, + (MAC_T *)&buffer[length-4], + decrypt ? EAX_MODE_CIPHERTEXT_AUTH : EAX_MODE_CLEARTEXT_AUTH); + } + return status; +} + +/** + * Checks to make sure that a complete, valid BER-encoded length is in the buffer. + * + * \param tvb contains the buffer to be examined + * \param offset is the offset within the buffer at which the BER-encoded length begins + * \returns TRUE if a complete, valid BER-encoded length is in the buffer; otherwise FALSE + */ +static gboolean +ber_len_ok(tvbuff_t *tvb, int offset) +{ + guint8 ch; + + if (tvb_offset_exists(tvb, offset)) { + ch = tvb_get_guint8(tvb, offset); + offset++; + if (!(ch & 0x80)) { + return TRUE; + } else if (tvb_offset_exists(tvb, offset)) { + ch = tvb_get_guint8(tvb, offset); + offset++; + if (!(ch & 0x80)) { + return TRUE; + } else if (tvb_offset_exists(tvb, offset)) { + ch = tvb_get_guint8(tvb, offset); + offset++; + if (!(ch & 0x80)) { + return TRUE; + } else if (tvb_offset_exists(tvb, offset)) { + ch = tvb_get_guint8(tvb, offset); + /*offset++;*/ + if (!(ch & 0x80)) { + return TRUE; + } + } + } + } + } + return FALSE; +} + +/** + * Dissects the EPSEM portion of the User-information part of a C12.22 message. + * + * \param tvb the tv buffer of the current data + * \param offset the offset in the tvb + * \param len length of data + * \param pinfo the packet info of the current data + * \param tree the tree to append this item to + */ +static int +dissect_epsem(tvbuff_t *tvb, int offset, guint32 len, packet_info *pinfo, proto_tree *tree) +{ + proto_tree *cmd_tree = NULL; + proto_tree *ct = NULL; + proto_tree *crypto_tree = NULL; + proto_tree *yt = NULL; + proto_item *item = NULL; + guint8 flags; + int local_offset; + gint len2; + int cmd_err; + bool ind; + guchar *buffer; + tvbuff_t *epsem_buffer = NULL; + gboolean crypto_good = FALSE; + gboolean crypto_bad = FALSE; + gboolean hasmac = FALSE; + gboolean encrypted = FALSE; + + if ((tvb == NULL) && (len == 0)) { + expert_add_info(pinfo, tree, &ei_c1222_epsem_missing); + return offset; + } + /* parse the flags byte which is always unencrypted */ + flags = tvb_get_guint8(tvb, offset); + proto_tree_add_bitmask(tree, tvb, offset, hf_c1222_epsem_flags, ett_c1222_flags, c1222_flags, ENC_BIG_ENDIAN); + offset++; + switch ((flags & C1222_EPSEM_FLAG_SECURITY_MODE) >> 2) { + case EAX_MODE_CIPHERTEXT_AUTH: + /* mode is ciphertext with authentication */ + hasmac = TRUE; + len2 = tvb_reported_length_remaining(tvb, offset); + if (len2 <= 0) + return offset; + encrypted = TRUE; + if (c1222_decrypt) { + buffer = (guchar *)tvb_memdup(pinfo->pool, tvb, offset, len2); + if (!decrypt_packet(buffer, len2, TRUE)) { + crypto_bad = TRUE; + } else { + epsem_buffer = tvb_new_real_data(buffer, len2, len2); + tvb_set_child_real_data_tvbuff(tvb, epsem_buffer); + add_new_data_source(pinfo, epsem_buffer, "Decrypted EPSEM Data"); + crypto_good = TRUE; + encrypted = FALSE; + } + } + break; + case EAX_MODE_CLEARTEXT_AUTH: + /* mode is cleartext with authentication */ + hasmac = TRUE; + len2 = tvb_reported_length_remaining(tvb, offset); + if (len2 <= 0) + return offset; + epsem_buffer = tvb_new_subset_remaining(tvb, offset); + buffer = (guchar *)tvb_memdup(pinfo->pool, tvb, offset, len2); + if (c1222_decrypt) { + if (!decrypt_packet(buffer, len2, FALSE)) { + crypto_bad = TRUE; + expert_add_info(pinfo, tree, &ei_c1222_epsem_failed_authentication); + } else { + crypto_good = TRUE; + } + } + break; + default: + /* it's not encrypted */ + epsem_buffer = tvb_new_subset_remaining(tvb, offset); + } + /* it's only encrypted if we have an undecrypted payload */ + if (encrypted) { + proto_tree_add_item(tree, hf_c1222_epsem_total, tvb, offset, -1, ENC_NA); + expert_add_info(pinfo, tree, &ei_c1222_epsem_not_decrypted); + local_offset = offset+len2-4; + epsem_buffer = tvb; + } else { /* it's not (now) encrypted */ + local_offset = 0; + /* retrieve the ed_class if it's there */ + if (flags & C1222_EPSEM_FLAG_ED_CLASS_INCLUDED) { + if (tvb_offset_exists(epsem_buffer, local_offset+4-1)) { + proto_tree_add_item(tree, hf_c1222_epsem_ed_class, epsem_buffer, local_offset, 4, ENC_NA); + local_offset += 4; + } else { + expert_add_info(pinfo, tree, &ei_c1222_ed_class_missing); + } + } + /* what follows are one or more <epsem-data> elements possibly followed by + * a <mac>. Each <epsem-data> element is defined as <service-length><res-req>, + * so we fetch such pairs until there isn't anything left (except possibly + * the <mac>). + */ + while (tvb_offset_exists(epsem_buffer, local_offset+(hasmac?5:1))) { + if (ber_len_ok(epsem_buffer, local_offset)) { + local_offset = dissect_ber_length(pinfo, tree, epsem_buffer, local_offset, (guint32 *)&len2, &ind); + } else { + expert_add_info(pinfo, tree, &ei_c1222_epsem_ber_length_error); + return offset+len; + } + if (tvb_offset_exists(epsem_buffer, local_offset+len2-1)) { + cmd_err = tvb_get_guint8(epsem_buffer, local_offset); + ct = proto_tree_add_item(tree, hf_c1222_epsem_total, epsem_buffer, local_offset, len2, ENC_NA); + cmd_tree = proto_item_add_subtree(ct, ett_c1222_cmd); + parse_c1222_detailed(epsem_buffer, pinfo, cmd_tree, cmd_err, (guint32 *)&len2, &local_offset); + local_offset += len2; + } else { + expert_add_info(pinfo, tree, &ei_c1222_epsem_field_length_error); + return offset+len; + } + } + } + if (hasmac) { + if (tvb_offset_exists(epsem_buffer, local_offset+4-1)) { + yt = proto_tree_add_item(tree, hf_c1222_epsem_mac, epsem_buffer, local_offset, 4, ENC_NA); + /* now we have enough information to fill in the crypto subtree */ + crypto_tree = proto_item_add_subtree(yt, ett_c1222_crypto); + item = proto_tree_add_boolean(crypto_tree, hf_c1222_epsem_crypto_good, tvb, local_offset, 4, crypto_good); + proto_item_set_generated(item); + item = proto_tree_add_boolean(crypto_tree, hf_c1222_epsem_crypto_bad, tvb, local_offset, 4, crypto_bad); + proto_item_set_generated(item); + } else { + expert_add_info(pinfo, tree, &ei_c1222_mac_missing); + return offset+len; + } + } + return offset; +} + +#include "packet-c1222-fn.c" + +/** + * Dissects a a full (reassembled) C12.22 message. + * + * \param tvb the tv buffer of the current data + * \param pinfo the packet info of the current data + * \param tree the tree to append this item to + */ +static int +dissect_c1222_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + proto_item *c1222_item = NULL; + proto_tree *c1222_tree = NULL; + + /* make entry in the Protocol column on summary display */ + col_set_str(pinfo->cinfo, COL_PROTOCOL, PNAME); + + /* create the c1222 protocol tree */ + c1222_item = proto_tree_add_item(tree, proto_c1222, tvb, 0, -1, ENC_NA); + c1222_tree = proto_item_add_subtree(c1222_item, ett_c1222); + return dissect_MESSAGE_PDU(tvb, pinfo, c1222_tree, NULL); +} + +/** + * Fetches the length of an entire C12.22 message to assist in reassembly. + * + * \param pinfo the packet info of the current data + * \param tvb the tv buffer of the current data + * \param offset the offset in the tvb + * \returns length of entire C12.22 message + */ +static guint +get_c1222_message_len(packet_info *pinfo, tvbuff_t *tvb, int offset, void *data _U_) +{ + int orig_offset; + guint length; + bool ind; + + orig_offset = offset; + /* note that this assumes a Tag length of 1 which is always valid for C12.22 */ + offset = dissect_ber_length(pinfo, NULL, tvb, offset+1, &length, &ind); + return length+(offset - orig_offset); +} + +/** + * Reassembles and dissects C12.22 messages. + * + * \param tvb the tv buffer of the current data + * \param pinfo the packet info of the current data + * \param tree the tree to append this item to + */ +static int +dissect_c1222(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data) +{ + tcp_dissect_pdus(tvb, pinfo, tree, c1222_desegment, 5, + get_c1222_message_len, dissect_c1222_common, data); + return tvb_captured_length(tvb); +} + +/*--- proto_register_c1222 -------------------------------------------*/ +void proto_register_c1222(void) { + + /* List of fields */ + static hf_register_info hf[] = { + { &hf_c1222_epsem_flags, + { "C12.22 EPSEM Flags", "c1222.epsem.flags", + FT_UINT8, BASE_HEX, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_epsem_flags_reserved, + { "C12.22 Reserved Flag", "c1222.epsem.flags.reserved", + FT_BOOLEAN, 8, + NULL, C1222_EPSEM_FLAG_RESERVED, + NULL, HFILL } + }, + { &hf_c1222_epsem_flags_recovery, + { "C12.22 Recovery Flag", "c1222.epsem.flags.recovery", + FT_BOOLEAN, 8, + NULL, C1222_EPSEM_FLAG_RECOVERY_SESSION, + NULL, HFILL } + }, + { &hf_c1222_epsem_flags_proxy, + { "C12.22 Proxy Service Used Flag", "c1222.epsem.flags.proxy", + FT_BOOLEAN, 8, + NULL, C1222_EPSEM_FLAG_PROXY_SERVICE_USED, + NULL, HFILL } + }, + { &hf_c1222_epsem_flags_ed_class, + { "C12.22 ED Class Flag", "c1222.epsem.flags.ed_class", + FT_BOOLEAN, 8, + NULL, C1222_EPSEM_FLAG_ED_CLASS_INCLUDED, + NULL, HFILL } + }, + { &hf_c1222_epsem_flags_security_modes, + { "C12.22 Security Mode Flags", "c1222.epsem.flags.security", + FT_UINT8, BASE_HEX, + VALS(c1222_security_modes), C1222_EPSEM_FLAG_SECURITY_MODE, + NULL, HFILL } + }, + { &hf_c1222_epsem_flags_response_control, + { "C12.22 Response Control Flags", "c1222.epsem.flags.response_control", + FT_UINT8, BASE_HEX, + VALS(c1222_response_control), C1222_EPSEM_FLAG_RESPONSE_CONTROL, + NULL, HFILL } + }, + { &hf_c1222_epsem_ed_class, + { "C12.22 EPSEM ED Class", "c1222.epsem.edclass", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_epsem_total, + { "C12.22 EPSEM", "c1222.epsem.data", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_epsem_mac, + { "C12.22 EPSEM MAC", "c1222.epsem.mac", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_cmd, + { "C12.22 Command", "c1222.cmd", + FT_UINT8, BASE_HEX, + VALS(commandnames), 0x0, + NULL, HFILL } + }, + { &hf_c1222_err, + { "C12.22 Response", "c1222.err", + FT_UINT8, BASE_HEX, + VALS(commandnames), 0x0, + NULL, HFILL } + }, + { &hf_c1222_logon_id, + { "C12.22 Logon User-Id", "c1222.logon.id", + FT_UINT16, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_logon_user, + { "C12.22 Logon User", "c1222.logon.user", + FT_STRING, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_security_password, + { "C12.22 Security Password", "c1222.security.password", + FT_STRING, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_auth_len, + { "C12.22 Authenticate Request Length", "c1222.authenticate.len", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_auth_data, + { "C12.22 Authenticate Data", "c1222.authenticate.data", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_read_table, + { "C12.22 Table", "c1222.read.table", + FT_UINT16, BASE_HEX, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_read_offset, + { "C12.22 Offset", "c1222.read.offset", + FT_UINT24, BASE_HEX, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_read_count, + { "C12.22 Count", "c1222.read.count", + FT_UINT16, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_write_table, + { "C12.22 Table", "c1222.write.table", + FT_UINT16, BASE_HEX, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_write_offset, + { "C12.22 Offset", "c1222.write.offset", + FT_UINT24, BASE_HEX, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_write_size, + { "C12.22 Table Size", "c1222.write.size", + FT_UINT16, BASE_HEX, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_write_data, + { "C12.22 Table Data", "c1222.write.data", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_write_chksum, + { "C12.22 Table Data Checksum", "c1222.write.chksum", + FT_UINT8, BASE_HEX, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_write_chksum_status, + { "C12.22 Table Data Checksum Status", "c1222.write.chksum.status", + FT_UINT8, BASE_NONE, + VALS(proto_checksum_vals), 0x0, + NULL, HFILL } + }, + { &hf_c1222_procedure_response, + { "C12.22 Procedure Response", "c1222.procedure.response", + FT_UINT16, BASE_DEC, + VALS(c1222_proc_response_control), C1222_PROCEDURE_RESPONSE, + NULL, HFILL } + }, + { &hf_c1222_procedure_mfg, + { "C12.22 Procedure Mfg", "c1222.procedure.mfg", + FT_UINT16, BASE_DEC, + NULL, C1222_PROCEDURE_MFG, + NULL, HFILL } + }, + { &hf_c1222_procedure_num, + { "C12.22 Procedure Number", "c1222.procedure.num", + FT_UINT16, BASE_DEC, + NULL, C1222_PROCEDURE_NUMBER, + NULL, HFILL } + }, + { &hf_c1222_procedure_sequence, + { "C12.22 Procedure Sequence Number", "c1222.procedure.sequence", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_neg_pkt_size, + { "C12.22 Negotiate Packet Size", "c1222.negotiate.pktsize", + FT_UINT16, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_neg_nbr_pkts, + { "C12.22 Negotiate Number of Packets", "c1222.negotiate.numpkts", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_wait_secs, + { "C12.22 Wait Seconds", "c1222.wait.seconds", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_timing_setup_traffic, + { "C12.22 Timing Setup Channel Traffic Timeout", "c1222.timingsetup.traffic", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_timing_setup_inter_char, + { "C12.22 Timing Setup Intercharacter Timeout", "c1222.timingsetup.interchar", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_timing_setup_resp_to, + { "C12.22 Timing Setup Response Timeout", "c1222.timingsetup.respto", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_timing_setup_nbr_retries, + { "C12.22 Timing Setup Number of Retries", "c1222.timingsetup.nbrretries", + FT_UINT8, BASE_DEC, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_data, + { "C12.22 data", "c1222.data", + FT_BYTES, BASE_NONE, + NULL, 0x0, + NULL, HFILL } + }, + { &hf_c1222_epsem_crypto_good, + { "Crypto good", "c1222.crypto_good", + FT_BOOLEAN, BASE_NONE, + NULL, 0x0, + "True: crypto ok; False: doesn't match or not checked", HFILL } + }, + { &hf_c1222_epsem_crypto_bad, + { "Crypto bad", "c1222.crypto_bad", + FT_BOOLEAN, BASE_NONE, + NULL, 0x0, + "True: crypto bad; False: crypto ok or not checked", HFILL } + }, +#include "packet-c1222-hfarr.c" + }; + + /* List of subtrees */ + static gint *ett[] = { + &ett_c1222, + &ett_c1222_epsem, + &ett_c1222_flags, + &ett_c1222_crypto, + &ett_c1222_cmd, +#include "packet-c1222-ettarr.c" + }; + + static ei_register_info ei[] = { + { &ei_c1222_command_truncated, { "c1222.command_truncated", PI_MALFORMED, PI_ERROR, "C12.22 command truncated", EXPFILL }}, + { &ei_c1222_bad_checksum, { "c1222.bad_checksum", PI_CHECKSUM, PI_ERROR, "Bad checksum", EXPFILL }}, + { &ei_c1222_epsem_missing, { "c1222.epsem.missing", PI_MALFORMED, PI_ERROR, "C12.22 EPSEM missing", EXPFILL }}, + { &ei_c1222_epsem_failed_authentication, { "c1222.epsem.failed_authentication", PI_SECURITY, PI_ERROR, "C12.22 EPSEM failed authentication", EXPFILL }}, + { &ei_c1222_epsem_not_decrypted, { "c1222.epsem.not_decrypted", PI_UNDECODED, PI_WARN, "C12.22 EPSEM could not be decrypted", EXPFILL }}, + { &ei_c1222_ed_class_missing, { "c1222.ed_class_missing", PI_SECURITY, PI_ERROR, "C12.22 ED Class missing", EXPFILL }}, + { &ei_c1222_epsem_ber_length_error, { "c1222.epsem.ber_length_error", PI_MALFORMED, PI_ERROR, "C12.22 EPSEM BER length error", EXPFILL }}, + { &ei_c1222_epsem_field_length_error, { "c1222.epsem.field_length_error", PI_MALFORMED, PI_ERROR, "C12.22 EPSEM field length error", EXPFILL }}, + { &ei_c1222_mac_missing, { "c1222.mac_missing", PI_MALFORMED, PI_ERROR, "C12.22 MAC missing", EXPFILL }}, + }; + + expert_module_t* expert_c1222; + module_t *c1222_module; + + static uat_field_t c1222_uat_flds[] = { + UAT_FLD_HEX(c1222_users,keynum,"Key ID","Key identifier in hexadecimal"), + UAT_FLD_BUFFER(c1222_users, key, "Key", "Encryption key as 16-byte hex string"), + UAT_END_FIELDS + }; + + /* Register protocol */ + proto_c1222 = proto_register_protocol(PNAME, PSNAME, PFNAME); + /* Register fields and subtrees */ + proto_register_field_array(proto_c1222, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_c1222 = expert_register_protocol(proto_c1222); + expert_register_field_array(expert_c1222, ei, array_length(ei)); + /* Register dissectors */ + c1222_handle = register_dissector("c1222.tcp", dissect_c1222, proto_c1222); + c1222_udp_handle = register_dissector("c1222.udp", dissect_c1222_common, proto_c1222); + /* Register dissection preferences */ + c1222_module = prefs_register_protocol(proto_c1222, proto_reg_handoff_c1222); + prefs_register_bool_preference(c1222_module, "desegment", + "Reassemble all C12.22 messages spanning multiple TCP segments", + "Whether the C12.22 dissector should reassemble all messages spanning multiple TCP segments", + &c1222_desegment); + prefs_register_string_preference(c1222_module, "baseoid", "Base OID to use for relative OIDs", + "Base object identifier for use in resolving relative object identifiers", + &c1222_baseoid_str); + prefs_register_bool_preference(c1222_module, "decrypt", + "Verify crypto for all applicable C12.22 messages", + "Whether the C12.22 dissector should verify the crypto for all relevant messages", + &c1222_decrypt); + prefs_register_bool_preference(c1222_module, "big_endian", + "Interpret multibyte numbers as big endian", + "Whether the C12.22 dissector should interpret procedure numbers as big-endian", + &c1222_big_endian); + + c1222_uat = uat_new("Decryption Table", + sizeof(c1222_uat_data_t), /* record size */ + "c1222_decryption_table", /* filename */ + TRUE, /* from_profile */ + &c1222_uat_data, /* data_ptr */ + &num_c1222_uat_data, /* numitems_ptr */ + UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */ + NULL, /* help */ + c1222_uat_data_copy_cb, /* copy callback */ + c1222_uat_data_update_cb, /* update callback */ + c1222_uat_data_free_cb, /* free callback */ + NULL, /* post update callback */ + NULL, /* reset callback */ + c1222_uat_flds); /* UAT field definitions */ + + prefs_register_uat_preference(c1222_module, + "decryption_table", + "Decryption Table", + "Table of security parameters for decryption of C12.22 packets", + c1222_uat); +} + +/*--- proto_reg_handoff_c1222 ---------------------------------------*/ +void +proto_reg_handoff_c1222(void) +{ + static gboolean initialized = FALSE; + guint8 *temp = NULL; + + if( !initialized ) { + dissector_add_uint_with_preference("tcp.port", C1222_PORT, c1222_handle); + dissector_add_uint_with_preference("udp.port", C1222_PORT, c1222_udp_handle); + initialized = TRUE; + } + if (c1222_baseoid_str && (c1222_baseoid_str[0] != '\0') && + ((c1222_baseoid_len = oid_string2encoded(NULL, c1222_baseoid_str, &temp)) != 0)) { + c1222_baseoid = (guint8 *)wmem_realloc(wmem_epan_scope(), c1222_baseoid, c1222_baseoid_len); + memcpy(c1222_baseoid, temp, c1222_baseoid_len); + wmem_free(NULL, temp); + } else if (c1222_baseoid) { + wmem_free(wmem_epan_scope(), c1222_baseoid); + c1222_baseoid = NULL; + c1222_baseoid_len = 0; + } +} +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ diff --git a/epan/dissectors/asn1/c1222/packet-c1222-template.h b/epan/dissectors/asn1/c1222/packet-c1222-template.h new file mode 100644 index 00000000..5adebd32 --- /dev/null +++ b/epan/dissectors/asn1/c1222/packet-c1222-template.h @@ -0,0 +1,29 @@ +/* packet-c1222.h + * Routines for ANSI C12.22 packet dissection + * Copyright 2010, Edward J. Beroset, edward.beroset@elster.com + * + * Wireshark - Network traffic analyzer + * By Gerald Combs <gerald@wireshark.org> + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef PACKET_C1222_H +#define PACKET_C1222_H + +#endif /* PACKET_C1222_H */ + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 2 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=2 tabstop=8 expandtab: + * :indentSize=2:tabSize=8:noTabs=true: + */ + |