From e4ba6dbc3f1e76890b22773807ea37fe8fa2b1bc Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:34:10 +0200 Subject: Adding upstream version 4.2.2. Signed-off-by: Daniel Baumann --- epan/dissectors/packet-ber.c | 4614 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 4614 insertions(+) create mode 100644 epan/dissectors/packet-ber.c (limited to 'epan/dissectors/packet-ber.c') diff --git a/epan/dissectors/packet-ber.c b/epan/dissectors/packet-ber.c new file mode 100644 index 00000000..5d01d055 --- /dev/null +++ b/epan/dissectors/packet-ber.c @@ -0,0 +1,4614 @@ +/* packet-ber.c + * Helpers for ASN.1/BER dissection + * Ronnie Sahlberg (C) 2004 + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +/* + * ITU-T Recommendation X.690 (07/2002), + * Information technology ASN.1 encoding rules: + * Specification of Basic Encoding Rules (BER), Canonical Encoding Rules (CER) and Distinguished Encoding Rules (DER) + * + */ +/* TODO: change #.REGISTER signature to dissector_t and + * update call_ber_oid_callback() accordingly. + * + * Since we don't pass the TAG/LENGTH from the CHOICE/SEQUENCE/SEQUENCE OF/ + * SET OF helpers through the callbacks to the next pabket-ber helper + * when the tags are IMPLICIT, this causes a problem when we also have + * indefinite length at the same time as the tags are implicit. + * + * While the proper fix is to change the signatures for packet-ber.c helpers + * as well as the signatures for the callbacks to include the indefinite length + * indication that would be a major job. + * + * Originally we used a kludge - we set a global variable in the + * CHOICE/SEQUENCE [OF]/SET [OF] helpers to indicate to the next helper + * whether the length is indefinite or not. + * That had currently only been implemented for {SEQUENCE|SET} [OF] but not + * CHOICE. + * + * This version attacks the problem(s) in a different way. If we see + * indefinite length the get_ber_length traverses the tags within the + * compound value and then we return the true length of the compound value + * including the EOC. Thus the tvb length is now always correct even for + * indefinite length, then if we get implicit tags they can be handled as + * if they were definite length. + */ + +//#define DEBUG_BER 1 + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEBUG_BER +#include +#endif + +#include "packet-ber.h" + +/* + * Set a limit on recursion so we don't blow away the stack. Another approach + * would be to remove recursion completely but then we'd exhaust CPU+memory + * trying to read a hellabyte of nested indefinite lengths. + + * XXX - Max nesting in the ASN.1 plugin is 32. Should they match? + */ +#define BER_MAX_NESTING 500 + +void proto_register_ber(void); +void proto_reg_handoff_ber(void); + +static gint proto_ber = -1; +static gint hf_ber_id_class = -1; +static gint hf_ber_id_pc = -1; +static gint hf_ber_id_uni_tag = -1; +static gint hf_ber_id_uni_tag_ext = -1; +static gint hf_ber_id_tag = -1; +static gint hf_ber_id_tag_ext = -1; +static gint hf_ber_length = -1; +static gint hf_ber_length_octets = -1; +static gint hf_ber_bitstring_padding = -1; +static gint hf_ber_bitstring_empty = -1; +static gint hf_ber_unknown_OID = -1; +static gint hf_ber_unknown_relative_OID = -1; +static gint hf_ber_unknown_BOOLEAN = -1; +static gint hf_ber_unknown_OCTETSTRING = -1; +static gint hf_ber_unknown_BER_OCTETSTRING = -1; +static gint hf_ber_unknown_BER_primitive = -1; +static gint hf_ber_unknown_GraphicString = -1; +static gint hf_ber_unknown_NumericString = -1; +static gint hf_ber_unknown_PrintableString = -1; +static gint hf_ber_unknown_TeletexString = -1; +static gint hf_ber_unknown_VisibleString = -1; +static gint hf_ber_unknown_GeneralString = -1; +static gint hf_ber_unknown_UniversalString = -1; +static gint hf_ber_unknown_BMPString = -1; +static gint hf_ber_unknown_IA5String = -1; +static gint hf_ber_unknown_UTCTime = -1; +static gint hf_ber_unknown_UTF8String = -1; +static gint hf_ber_unknown_GeneralizedTime = -1; +static gint hf_ber_unknown_INTEGER = -1; +static gint hf_ber_unknown_REAL = -1; +static gint hf_ber_unknown_BITSTRING = -1; +static gint hf_ber_unknown_ENUMERATED = -1; +static gint hf_ber_direct_reference = -1; /* OBJECT_IDENTIFIER */ +static gint hf_ber_indirect_reference = -1; /* INTEGER */ +static gint hf_ber_data_value_descriptor = -1; /* ObjectDescriptor */ +static gint hf_ber_encoding = -1; /* T_encoding */ +static gint hf_ber_single_ASN1_type = -1; /* T_single_ASN1_type */ +static gint hf_ber_octet_aligned = -1; /* OCTET_STRING */ +static gint hf_ber_arbitrary = -1; /* BIT_STRING */ +static gint hf_ber_extra_data = -1; +static gint hf_ber_encoding_boiler_plate = -1; + +/* Generated from convert_proto_tree_add_text.pl */ +static int hf_ber_seq_of_eoc = -1; +static int hf_ber_64bit_uint_as_bytes = -1; +static int hf_ber_choice_eoc = -1; +static int hf_ber_seq_field_eoc = -1; +static int hf_ber_seq_eoc = -1; +static int hf_ber_set_field_eoc = -1; +static int hf_ber_set_eoc = -1; +static int hf_ber_null_tag = -1; +static int hf_ber_unknown_octetstring = -1; +static int hf_ber_unknown_data = -1; + +static int hf_ber_fragments = -1; +static int hf_ber_fragment = -1; +static int hf_ber_fragment_overlap = -1; +static int hf_ber_fragment_overlap_conflicts = -1; +static int hf_ber_fragment_multiple_tails = -1; +static int hf_ber_fragment_too_long_fragment = -1; +static int hf_ber_fragment_error = -1; +static int hf_ber_fragment_count = -1; +static int hf_ber_reassembled_in = -1; +static int hf_ber_reassembled_length = -1; + +static gint ett_ber_octet_string = -1; +static gint ett_ber_reassembled_octet_string = -1; +static gint ett_ber_primitive = -1; +static gint ett_ber_unknown = -1; +static gint ett_ber_SEQUENCE = -1; +static gint ett_ber_EXTERNAL = -1; +static gint ett_ber_T_encoding = -1; +static gint ett_ber_fragment = -1; +static gint ett_ber_fragments = -1; + +static expert_field ei_ber_size_constraint_string = EI_INIT; +static expert_field ei_ber_size_constraint_value = EI_INIT; +static expert_field ei_ber_size_constraint_items = EI_INIT; +static expert_field ei_ber_sequence_field_wrong = EI_INIT; +static expert_field ei_ber_expected_octet_string = EI_INIT; +static expert_field ei_ber_expected_null = EI_INIT; +static expert_field ei_ber_expected_null_zero_length = EI_INIT; +static expert_field ei_ber_expected_sequence = EI_INIT; +static expert_field ei_ber_expected_set = EI_INIT; +static expert_field ei_ber_expected_string = EI_INIT; +static expert_field ei_ber_expected_object_identifier = EI_INIT; +static expert_field ei_ber_expected_generalized_time = EI_INIT; +static expert_field ei_ber_expected_utc_time = EI_INIT; +static expert_field ei_ber_expected_bitstring = EI_INIT; +static expert_field ei_ber_error_length = EI_INIT; +static expert_field ei_ber_wrong_tag_in_tagged_type = EI_INIT; +static expert_field ei_ber_universal_tag_unknown = EI_INIT; +static expert_field ei_ber_no_oid = EI_INIT; +static expert_field ei_ber_syntax_not_implemented = EI_INIT; +static expert_field ei_ber_oid_not_implemented = EI_INIT; +static expert_field ei_ber_value_too_many_bytes = EI_INIT; +static expert_field ei_ber_unknown_field_sequence = EI_INIT; +static expert_field ei_ber_unknown_field_set = EI_INIT; +static expert_field ei_ber_missing_field_set = EI_INIT; +static expert_field ei_ber_empty_choice = EI_INIT; +static expert_field ei_ber_choice_not_found = EI_INIT; +static expert_field ei_ber_bits_unknown = EI_INIT; +static expert_field ei_ber_bits_set_padded = EI_INIT; +static expert_field ei_ber_illegal_padding = EI_INIT; +static expert_field ei_ber_invalid_format_generalized_time = EI_INIT; +static expert_field ei_ber_invalid_format_utctime = EI_INIT; +static expert_field ei_hf_field_not_integer_type = EI_INIT; +static expert_field ei_ber_constr_bitstr = EI_INIT; +static expert_field ei_ber_real_not_primitive = EI_INIT; + +static dissector_handle_t ber_handle; +static dissector_handle_t ber_file_handle; + +static gboolean show_internal_ber_fields = FALSE; +static gboolean decode_octetstring_as_ber = FALSE; +static gboolean decode_primitive_as_ber = FALSE; +static gboolean decode_unexpected = FALSE; +static gboolean decode_warning_leading_zero_bits = FALSE; + +static gchar *decode_as_syntax = NULL; + +static dissector_table_t ber_oid_dissector_table = NULL; +static dissector_table_t ber_syntax_dissector_table = NULL; + +static GHashTable *syntax_table = NULL; + +static gint8 last_class; +static bool last_pc; +static gint32 last_tag; +static guint32 last_length; +static tvbuff_t *last_length_tvb; +static int last_length_offset; +static int last_length_len; +static bool last_ind; + +static const value_string ber_class_codes[] = { + { BER_CLASS_UNI, "UNIVERSAL" }, + { BER_CLASS_APP, "APPLICATION" }, + { BER_CLASS_CON, "CONTEXT" }, + { BER_CLASS_PRI, "PRIVATE" }, + { 0, NULL } +}; + +static const true_false_string ber_pc_codes = { + "Constructed Encoding", + "Primitive Encoding" +}; + + +static const value_string ber_uni_tag_codes[] = { + { BER_UNI_TAG_EOC, "'end-of-content'" }, + { BER_UNI_TAG_BOOLEAN, "BOOLEAN" }, + { BER_UNI_TAG_INTEGER, "INTEGER" }, + { BER_UNI_TAG_BITSTRING, "BIT STRING" }, + { BER_UNI_TAG_OCTETSTRING, "OCTET STRING" }, + { BER_UNI_TAG_NULL, "NULL" }, + { BER_UNI_TAG_OID, "OBJECT IDENTIFIER" }, + { BER_UNI_TAG_ObjectDescriptor, "ObjectDescriptor" }, + { BER_UNI_TAG_EXTERNAL, "EXTERNAL" }, + { BER_UNI_TAG_REAL, "REAL" }, + { BER_UNI_TAG_ENUMERATED, "ENUMERATED" }, + { BER_UNI_TAG_EMBEDDED_PDV, "EMBEDDED PDV" }, + { BER_UNI_TAG_UTF8String, "UTF8String" }, + { BER_UNI_TAG_RELATIVE_OID, "RELATIVE-OID" }, + /* UNIVERSAL 14-15 + * Reserved for future editions of this + * Recommendation | International Standard + */ + { 14, "Reserved for future editions" }, + { 15 , "Reserved for future editions" }, + + { BER_UNI_TAG_SEQUENCE, "SEQUENCE" }, + { BER_UNI_TAG_SET, "SET" }, + { BER_UNI_TAG_NumericString, "NumericString" }, + { BER_UNI_TAG_PrintableString, "PrintableString" }, + { BER_UNI_TAG_TeletexString, "TeletexString, T61String" }, + { BER_UNI_TAG_VideotexString, "VideotexString" }, + { BER_UNI_TAG_IA5String, "IA5String" }, + { BER_UNI_TAG_UTCTime, "UTCTime" }, + { BER_UNI_TAG_GeneralizedTime, "GeneralizedTime" }, + { BER_UNI_TAG_GraphicString, "GraphicString" }, + { BER_UNI_TAG_VisibleString, "VisibleString, ISO64String" }, + { BER_UNI_TAG_GeneralString, "GeneralString" }, + { BER_UNI_TAG_UniversalString, "UniversalString" }, + { BER_UNI_TAG_CHARACTERSTRING, "CHARACTER STRING" }, + { BER_UNI_TAG_BMPString, "BMPString" }, + { 31, "Continued" }, + { 0, NULL } +}; +static value_string_ext ber_uni_tag_codes_ext = VALUE_STRING_EXT_INIT(ber_uni_tag_codes); + +#if 0 +static const true_false_string ber_real_binary_vals = { + "Binary encoding", + "Decimal encoding" +}; + +static const true_false_string ber_real_decimal_vals = { + "SpecialRealValue", + "Decimal encoding" +}; +#endif + +typedef struct _da_data { + GHFunc func; + gpointer user_data; +} da_data; + +typedef struct _oid_user_t { + char *oid; + char *name; + char *syntax; +} oid_user_t; + +UAT_CSTRING_CB_DEF(oid_users, oid, oid_user_t) +UAT_CSTRING_CB_DEF(oid_users, name, oid_user_t) +UAT_VS_CSTRING_DEF(oid_users, syntax, oid_user_t, 0, "") + +static oid_user_t *oid_users; +static guint num_oid_users; + +#define MAX_SYNTAX_NAMES 128 +/* Define non_const_value_string as a hack to prevent chackAPIs.pl from complaining */ +#define non_const_value_string value_string +static non_const_value_string syntax_names[MAX_SYNTAX_NAMES+1] = { + {0, ""}, + {0, NULL} +}; + +static const fragment_items octet_string_frag_items = { + /* Fragment subtrees */ + &ett_ber_fragment, + &ett_ber_fragments, + /* Fragment fields */ + &hf_ber_fragments, + &hf_ber_fragment, + &hf_ber_fragment_overlap, + &hf_ber_fragment_overlap_conflicts, + &hf_ber_fragment_multiple_tails, + &hf_ber_fragment_too_long_fragment, + &hf_ber_fragment_error, + &hf_ber_fragment_count, + /* Reassembled in field */ + &hf_ber_reassembled_in, + /* Reassembled length field */ + &hf_ber_reassembled_length, + /* Reassembled data field */ + NULL, + /* Tag */ + "OCTET STRING fragments" +}; + +void +add_ber_encoded_label(tvbuff_t* tvb, packet_info* pinfo _U_, proto_tree* tree) +{ + proto_item *ti; + + ti = proto_tree_add_item(tree, hf_ber_encoding_boiler_plate, tvb, 0, -1, ENC_NA); + proto_item_set_generated(ti); + +} + +static void * +oid_copy_cb(void *dest, const void *orig, size_t len _U_) +{ + oid_user_t *u = (oid_user_t *)dest; + const oid_user_t *o = (const oid_user_t *)orig; + + u->oid = g_strdup(o->oid); + u->name = g_strdup(o->name); + u->syntax = o->syntax; + + return dest; +} + +static void +oid_free_cb(void *r) +{ + oid_user_t *u = (oid_user_t *)r; + + g_free(u->oid); + g_free(u->name); +} + +static int +cmp_value_string(const void *v1, const void *v2) +{ + const value_string *vs1 = (const value_string *)v1; + const value_string *vs2 = (const value_string *)v2; + + return strcmp(vs1->strptr, vs2->strptr); +} + +static uat_field_t users_flds[] = { + UAT_FLD_OID(oid_users, oid, "OID", "Object Identifier"), + UAT_FLD_CSTRING(oid_users, name, "Name", "Human readable name for the OID"), + UAT_FLD_VS(oid_users, syntax, "Syntax", syntax_names, "Syntax of values associated with the OID"), + UAT_END_FIELDS +}; + + +static void ber_prompt(packet_info *pinfo _U_, gchar* result) +{ + snprintf(result, MAX_DECODE_AS_PROMPT_LEN, "Decode ASN.1 file as"); +} + +static gpointer ber_value(packet_info *pinfo _U_) +{ + /* Not used */ + return NULL; +} + +struct ber_decode_as_populate +{ + decode_as_add_to_list_func add_to_list; + gpointer ui_element; +}; + +static void +decode_ber_add_to_list(gpointer key, gpointer value, gpointer user_data) +{ + struct ber_decode_as_populate* populate = (struct ber_decode_as_populate*)user_data; + populate->add_to_list("ASN.1", (gchar *)key, value, populate->ui_element); +} + +static void ber_populate_list(const gchar *table_name _U_, decode_as_add_to_list_func add_to_list, gpointer ui_element) +{ + struct ber_decode_as_populate populate; + + populate.add_to_list = add_to_list; + populate.ui_element = ui_element; + + ber_decode_as_foreach(decode_ber_add_to_list, &populate); +} + +static gboolean ber_decode_as_reset(const char *name _U_, gconstpointer pattern _U_) +{ + g_free(decode_as_syntax); + decode_as_syntax = NULL; + return FALSE; +} + +static gboolean ber_decode_as_change(const char *name _U_, gconstpointer pattern _U_, gconstpointer handle _U_, const gchar* list_name) +{ + g_free(decode_as_syntax); + decode_as_syntax = g_strdup(list_name); + return FALSE; +} + +int +dissect_ber_oid_NULL_callback(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree _U_, void* data _U_) +{ + return tvb_captured_length(tvb); +} + + +void +register_ber_oid_dissector_handle(const char *oid, dissector_handle_t dissector, int proto _U_, const char *name) +{ + dissector_add_string("ber.oid", oid, dissector); + oid_add_from_string(name, oid); +} + +void +register_ber_oid_dissector(const char *oid, dissector_t dissector, int proto, const char *name) +{ + dissector_handle_t dissector_handle; + + dissector_handle = create_dissector_handle(dissector, proto); + dissector_add_string("ber.oid", oid, dissector_handle); + oid_add_from_string(name, oid); +} + +void +register_ber_syntax_dissector(const char *syntax, int proto, dissector_t dissector) +{ + dissector_handle_t dissector_handle; + + dissector_handle = create_dissector_handle(dissector, proto); + dissector_add_string("ber.syntax", syntax, dissector_handle); + +} + +void +register_ber_oid_syntax(const char *oid, const char *name, const char *syntax) +{ + + if (syntax && *syntax) + g_hash_table_insert(syntax_table, (gpointer)g_strdup(oid), (gpointer)g_strdup(syntax)); + + if (name && *name) + register_ber_oid_name(oid, name); +} + +/* Register the oid name to get translation in proto dissection */ +void +register_ber_oid_name(const char *oid, const char *name) +{ + oid_add_from_string(name, oid); +} + +static void +ber_add_syntax_name(gpointer key, gpointer value _U_, gpointer user_data) +{ + guint *i = (guint*)user_data; + + if (*i < MAX_SYNTAX_NAMES) { + syntax_names[*i].value = *i; + syntax_names[*i].strptr = (const gchar*)key; + + (*i)++; + } + +} + +static void +ber_decode_as_dt(const gchar *table_name _U_, ftenum_t selector_type _U_, gpointer key, gpointer value, gpointer user_data) +{ + da_data *decode_as_data; + + decode_as_data = (da_data *)user_data; + + decode_as_data->func(key, value, decode_as_data->user_data); +} + +void +ber_decode_as_foreach(GHFunc func, gpointer user_data) +{ + da_data decode_as_data; + + decode_as_data.func = func; + decode_as_data.user_data = user_data; + + dissector_table_foreach("ber.syntax", ber_decode_as_dt, &decode_as_data); + +} + +/* Get oid syntax from hash table to get translation in proto dissection(packet-per.c) */ +static const gchar * +get_ber_oid_syntax(const char *oid) +{ + return (const char *)g_hash_table_lookup(syntax_table, oid); +} + +static void +ber_update_oids(void) +{ + guint i; + + for (i = 0; i < num_oid_users; i++) + register_ber_oid_syntax(oid_users[i].oid, oid_users[i].name, oid_users[i].syntax); +} + +static void +ber_check_length (guint32 length, gint32 min_len, gint32 max_len, asn1_ctx_t *actx, proto_item *item, bool bit) +{ + if ((min_len != -1) && (length < (guint32)min_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_string, + "Size constraint: %sstring too short: %d (%d .. %d)", + bit ? "bit " : "", length, min_len, max_len); + } else if ((max_len != -1) && (length > (guint32)max_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_string, + "Size constraint: %sstring too long: %d (%d .. %d)", + bit ? "bit " : "", length, min_len, max_len); + } +} + +static void +ber_check_value64 (gint64 value, gint64 min_len, gint64 max_len, asn1_ctx_t *actx, proto_item *item) +{ + if ((min_len != -1) && (value < min_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_value, + "Size constraint: value too small: %" PRId64 " (%" PRId64" .. %" PRId64 ")", + value, min_len, max_len); + } else if ((max_len != -1) && (value > max_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_value, + "Size constraint: value too big: %" PRId64 " (%" PRId64 " .. %" PRId64 ")", + value, min_len, max_len); + } +} + +static void +ber_check_value (guint32 value, gint32 min_len, gint32 max_len, asn1_ctx_t *actx, proto_item *item) +{ + if ((min_len != -1) && (value < (guint32)min_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_value, + "Size constraint: value too small: %d (%d .. %d)", + value, min_len, max_len); + } else if ((max_len != -1) && (value > (guint32)max_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_value, + "Size constraint: value too big: %d (%d .. %d)", + value, min_len, max_len); + } +} + +static void +ber_check_items (int cnt, gint32 min_len, gint32 max_len, asn1_ctx_t *actx, proto_item *item) +{ + if ((min_len != -1) && (cnt < min_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_items, + "Size constraint: too few items: %d (%d .. %d)", + cnt, min_len, max_len); + } else if ((max_len != -1) && (cnt > max_len)) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_size_constraint_items, + "Size constraint: too many items: %d (%d .. %d)", + cnt, min_len, max_len); + } +} + +/* + * XXX - if the specified length is less than the remaining length + * of data in the tvbuff, either 1) the specified length is bad and + * we should report that with an expert info or 2) the tvbuff is + * unreassembled and we should make the new tvbuff also be an + * unreassembled tvbuff. + */ +static tvbuff_t * +ber_tvb_new_subset_length(tvbuff_t *tvb, const gint backing_offset, const gint backing_length) +{ + gint length_remaining; + + length_remaining = tvb_reported_length_remaining(tvb, backing_offset); + return tvb_new_subset_length(tvb, backing_offset, (length_remaining > backing_length) ? backing_length : length_remaining); +} + +int +dissect_ber_tagged_type(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, gint8 tag_cls, gint32 tag_tag, bool tag_impl, ber_type_fn type) +{ + gint8 tmp_cls; + gint32 tmp_tag; + int identifier_offset; + int identifier_len; + guint32 tmp_len; + tvbuff_t *next_tvb = tvb; + +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "dissect_ber_tagged_type(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n", name, implicit_tag, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "dissect_ber_tagged_type(%s) entered\n", name); +} +} +#endif + + if (implicit_tag) { + offset = type(tag_impl, tvb, offset, actx, tree, hf_id); + return offset; + } + + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &tmp_cls, NULL, &tmp_tag); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &tmp_len, NULL); + + if ((tmp_cls != tag_cls) || (tmp_tag != tag_tag)) { + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_wrong_tag_in_tagged_type, + tvb, identifier_offset, identifier_len, + "BER Error: Wrong tag in tagged type - expected class:%s(%d) tag:%d (%s) but found class:%s(%d) tag:%d", + val_to_str_const(tag_cls, ber_class_codes, "Unknown"), + tag_cls, + tag_tag, + val_to_str_ext_const(tag_tag, &ber_uni_tag_codes_ext, "Unknown"), + val_to_str_const(tmp_cls, ber_class_codes, "Unknown"), + tmp_cls, + tmp_tag); + } + + if (tag_impl) { + next_tvb = ber_tvb_new_subset_length(tvb, offset, tmp_len); + type(tag_impl, next_tvb, 0, actx, tree, hf_id); + offset += tmp_len; + } else { + offset = type(tag_impl, tvb, offset, actx, tree, hf_id); + } + + return offset; +} + +/* + * Add a "length bogus" error. + */ +static proto_item * +ber_add_bad_length_error(packet_info *pinfo, proto_tree *tree, + const char *name, tvbuff_t *tvb, const gint start, + gint length) +{ + proto_item *ti; + + ti = proto_tree_add_expert_format( + tree, pinfo, &ei_ber_error_length, tvb, start, length, + "BER Error: %s: length of item (%d) is not valid", + name, length); + return ti; +} + +/* + * Add an "exceeds tvb length" error. + */ +static void +ber_add_large_length_error(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, + int offset, int length, tvbuff_t *len_tvb, + const gint len_offset, const guint32 len_length) +{ + proto_tree_add_expert_format( + tree, pinfo, &ei_ber_error_length, len_tvb, len_offset, len_length, + "BER Error: length %u longer than tvb_reported_length_remaining: %d", + length, + tvb_reported_length_remaining(tvb, offset)); +} + +/* + * Like proto_tree_add_item(), but checks whether the length of the item + * being added is appropriate for the type of the item being added, so + * if it's not, we report an error rather than a dissector bug. + * + * This is for use when a field that's nominally an OCTET STRING but + * where we want the string further interpreted, e.g. as a number or + * a network address or a UN*X-style time stamp. + * + * XXX - this duplicates the length checking in proto_tree_add_item() + * and the routines it calls; that should really be done in one + * place. We *do* want to report a dissector bug in proto_tree_add_item() + * if the dissector explicitly says, for example, "this IPv4 address is + * 7 bytes long", but we don't want to report a dissector bug if the + * *packet* says "this IPv4 address is 7 bytes long", we want to report + * a malformed packet. + */ +static proto_item * +ber_proto_tree_add_item(packet_info *pinfo, proto_tree *tree, + const int hfindex, tvbuff_t *tvb, const gint start, + gint length, const guint encoding) +{ + header_field_info *hfinfo; + + hfinfo = proto_registrar_get_nth((guint)hfindex); + if (hfinfo != NULL) { + switch (hfinfo->type) { + + case FT_BOOLEAN: + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + if ((length != 1) && (length != 2) && (length != 3) && + (length != 4)) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_IPv4: + if (length != FT_IPv4_LEN) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_IPXNET: + if (length != FT_IPXNET_LEN) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_IPv6: + if ((length < 0) || (length > FT_IPv6_LEN)) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_ETHER: + if (length != FT_ETHER_LEN) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_GUID: + if (length != FT_GUID_LEN) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_FLOAT: + if (length != 4) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_DOUBLE: + if (length != 8) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + case FT_ABSOLUTE_TIME: + case FT_RELATIVE_TIME: + if ((length != 4) && (length != 8)) + return ber_add_bad_length_error(pinfo, tree, + hfinfo->name, tvb, start, length); + break; + + default: + break; + } + } + return proto_tree_add_item(tree, hfindex, tvb, start, length, encoding); +} + +static int +try_dissect_unknown_ber(packet_info *pinfo, tvbuff_t *tvb, volatile int offset, proto_tree *tree, gint nest_level) +{ + int start_offset; + gint8 ber_class; + bool pc, ind; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + int len_offset; + int len_len; + int hdr_len; + proto_item *item = NULL; + proto_tree *next_tree = NULL; + guint8 c; + guint32 i; + bool is_printable; + volatile bool is_decoded_as; + proto_item *pi; + asn1_ctx_t asn1_ctx; + + if (nest_level > BER_MAX_NESTING) { + /* Assume that we have a malformed packet. */ + THROW(ReportedBoundsError); + } + + start_offset = offset; + asn1_ctx_init(&asn1_ctx, ASN1_ENC_BER, TRUE, pinfo); + + offset = get_ber_identifier(tvb, offset, &ber_class, &pc, &tag); + len_offset = offset; + offset = get_ber_length(tvb, offset, &len, &ind); + len_len = offset - len_offset; + + if (len > (guint32)tvb_reported_length_remaining(tvb, offset)) { + /* hmm maybe something bad happened or the frame is short; + since these are not vital outputs just return instead of + throwing an exception. + */ + + if (show_internal_ber_fields) { + offset = dissect_ber_identifier(pinfo, tree, tvb, start_offset, &ber_class, &pc, &tag); + offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); + } + ber_add_large_length_error(pinfo, tree, tvb, offset, len, tvb, len_offset, len_len); + return tvb_reported_length(tvb); + } +/* we don't care about the class only on the constructor flag */ + if (pc != TRUE) { + + /* this is not constructed */ + + switch (ber_class) { /* we do care about the class */ + case BER_CLASS_UNI: /* it a Universal tag - we can decode it */ + switch (tag) { + case BER_UNI_TAG_EOC: + /* XXX: shouldn't really get here */ + break; + case BER_UNI_TAG_INTEGER: + offset = dissect_ber_integer(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_INTEGER, NULL); + break; + case BER_UNI_TAG_REAL: + offset = dissect_ber_real(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_REAL, NULL); + break; + case BER_UNI_TAG_BITSTRING: + offset = dissect_ber_bitstring(FALSE, &asn1_ctx, tree, tvb, start_offset, NULL, 0, hf_ber_unknown_BITSTRING, -1, NULL); + break; + case BER_UNI_TAG_ENUMERATED: + offset = dissect_ber_integer(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_ENUMERATED, NULL); + break; + case BER_UNI_TAG_GraphicString: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_GraphicString, NULL); + break; + case BER_UNI_TAG_OCTETSTRING: + is_decoded_as = FALSE; + if (decode_octetstring_as_ber && (len >= 2)) { + volatile int ber_offset = 0; + guint32 ber_len = 0; + TRY{ + ber_offset = get_ber_identifier(tvb, offset, NULL, &pc, NULL); + ber_offset = get_ber_length(tvb, ber_offset, &ber_len, NULL); + } CATCH_ALL { + } + ENDTRY; + if (pc && (ber_len > 0) && (ber_len + (ber_offset - offset) == len)) { + /* Decoded a constructed ASN.1 tag with a length indicating this + * could be BER encoded data. Try dissecting as unknown BER. + */ + is_decoded_as = TRUE; + if (show_internal_ber_fields) { + offset = dissect_ber_identifier(pinfo, tree, tvb, start_offset, NULL, NULL, NULL); + offset = dissect_ber_length(pinfo, tree, tvb, offset, NULL, NULL); + } + item = ber_proto_tree_add_item(pinfo, tree, hf_ber_unknown_BER_OCTETSTRING, tvb, offset, len, ENC_NA); + next_tree = proto_item_add_subtree(item, ett_ber_octet_string); + offset = try_dissect_unknown_ber(pinfo, tvb, offset, next_tree, nest_level + 1); + } + } + if (!is_decoded_as) { + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_OCTETSTRING, NULL); + } + break; + case BER_UNI_TAG_OID: + offset = dissect_ber_object_identifier_str(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_OID, NULL); + break; + case BER_UNI_TAG_RELATIVE_OID: + offset = dissect_ber_relative_oid_str(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_relative_OID, NULL); + break; + case BER_UNI_TAG_NumericString: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_NumericString, NULL); + break; + case BER_UNI_TAG_PrintableString: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_PrintableString, NULL); + break; + case BER_UNI_TAG_TeletexString: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_TeletexString, NULL); + break; + case BER_UNI_TAG_VisibleString: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_VisibleString, NULL); + break; + case BER_UNI_TAG_GeneralString: + offset = dissect_ber_GeneralString(&asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_GeneralString, NULL, 0); + break; + case BER_UNI_TAG_BMPString: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_BMPString, NULL); + break; + case BER_UNI_TAG_UniversalString: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_UniversalString, NULL); + break; + case BER_UNI_TAG_IA5String: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_IA5String, NULL); + break; + case BER_UNI_TAG_UTCTime: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_UTCTime, NULL); + break; + case BER_UNI_TAG_NULL: + proto_tree_add_item(tree, hf_ber_null_tag, tvb, offset, len, ENC_NA); + break; + case BER_UNI_TAG_UTF8String: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_UTF8String, NULL); + break; + case BER_UNI_TAG_GeneralizedTime: + offset = dissect_ber_octet_string(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_GeneralizedTime, NULL); + break; + case BER_UNI_TAG_BOOLEAN: + offset = dissect_ber_boolean(FALSE, &asn1_ctx, tree, tvb, start_offset, hf_ber_unknown_BOOLEAN, NULL); + break; + default: + identifier_offset = start_offset; + offset = dissect_ber_identifier(pinfo, tree, tvb, start_offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); + proto_tree_add_expert_format( + tree, pinfo, &ei_ber_universal_tag_unknown, + tvb, identifier_offset, identifier_len, + "BER Error: can not handle universal tag:%d", + tag); + offset += len; + } + break; + case BER_CLASS_APP: + case BER_CLASS_CON: + case BER_CLASS_PRI: + default: + /* we dissect again if show_internal_ber_fields is set */ + if (show_internal_ber_fields) { + offset = dissect_ber_identifier(pinfo, tree, tvb, start_offset, &ber_class, &pc, &tag); + offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); + } + + /* we can't dissect this directly as it is specific */ + pi = proto_tree_add_none_format(tree, hf_ber_unknown_BER_primitive, tvb, offset, len, + "[%s %d] ", val_to_str_const(ber_class, ber_class_codes, "Unknown"), tag); + + is_decoded_as = FALSE; + if (decode_primitive_as_ber && (len >= 2)) { + volatile int ber_offset = 0; + guint32 ber_len = 0; + TRY{ + ber_offset = get_ber_identifier(tvb, offset, NULL, &pc, NULL); + ber_offset = get_ber_length(tvb, ber_offset, &ber_len, NULL); + } CATCH_ALL { + } + ENDTRY; + if (pc && (ber_len > 0) && (ber_len + (ber_offset - offset) == len)) { + /* Decoded a constructed ASN.1 tag with a length indicating this + * could be BER encoded data. Try dissecting as unknown BER. + */ + is_decoded_as = TRUE; + proto_item_append_text(pi, "[BER encoded]"); + next_tree = proto_item_add_subtree(pi, ett_ber_primitive); + offset = try_dissect_unknown_ber(pinfo, tvb, offset, next_tree, nest_level + 1); + } + } + + if (!is_decoded_as && len) { + /* we may want to do better and show the bytes */ + is_printable = TRUE; + for (i = 0; i < len; i++) { + c = tvb_get_guint8(tvb, offset + i); + + if (is_printable && !g_ascii_isprint(c)) + is_printable = FALSE; + + proto_item_append_text(pi, "%02x", c); + } + + if (is_printable) { /* give a nicer representation if it looks like a string */ + proto_item_append_text(pi, " ("); + for (i = 0; i < len; i++) { + proto_item_append_text(pi, "%c", tvb_get_guint8(tvb, offset + i)); + } + proto_item_append_text(pi, ")"); + } + offset += len; + } + + break; + } + } else { + /* this is constructed */ + + /* we dissect again if show_internal_ber_fields is set */ + if (show_internal_ber_fields) { + offset = dissect_ber_identifier(pinfo, tree, tvb, start_offset, &ber_class, &pc, &tag); + offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); + } + + hdr_len = offset-start_offset; + + switch (ber_class) { + case BER_CLASS_UNI: + next_tree = proto_tree_add_subtree(tree, tvb, offset, len, ett_ber_SEQUENCE, NULL, + val_to_str_ext_const(tag, &ber_uni_tag_codes_ext, "Unknown")); + while (offset < (int)(start_offset + len + hdr_len)) + offset = try_dissect_unknown_ber(pinfo, tvb, offset, next_tree, nest_level+1); + break; + case BER_CLASS_APP: + case BER_CLASS_CON: + case BER_CLASS_PRI: + default: + next_tree = proto_tree_add_subtree_format(tree, tvb, offset, len, ett_ber_SEQUENCE, NULL, + "[%s %d]", val_to_str_const(ber_class, ber_class_codes, "Unknown"), tag); + while (offset < (int)(start_offset + len + hdr_len)) + offset = try_dissect_unknown_ber(pinfo, tvb, offset, next_tree, nest_level+1); + break; + + } + } + + return offset; +} + +int +dissect_unknown_ber(packet_info *pinfo, tvbuff_t *tvb, int offset, proto_tree *tree) +{ + return try_dissect_unknown_ber(pinfo, tvb, offset, tree, 1); +} + +int +call_ber_oid_callback(const char *oid, tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree, void* data) +{ + tvbuff_t *next_tvb; + const char *syntax = NULL; + int len = 0; + + if (!tvb) { + return offset; + } + + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (oid == NULL || + ((((syntax = get_ber_oid_syntax(oid)) == NULL) || + /* First see if a syntax has been registered for this oid (user defined) */ + (len = dissector_try_string(ber_syntax_dissector_table, syntax, next_tvb, pinfo, tree, data)) == 0) && + /* Then try registered oid's */ + (len = dissector_try_string(ber_oid_dissector_table, oid, next_tvb, pinfo, tree, data)) == 0)) + { + proto_item *item = NULL; + proto_tree *next_tree = NULL; + gint length_remaining; + + /* XXX we should probably use get_ber_length() here */ + length_remaining = tvb_reported_length_remaining(tvb, offset); + + if (oid == NULL) { + item = proto_tree_add_expert(tree, pinfo, &ei_ber_no_oid, next_tvb, 0, length_remaining); + } else if (tvb_get_ntohs (tvb, offset) != 0x0500) { /* Not NULL tag */ + if (syntax) { + item = proto_tree_add_expert_format( + tree, pinfo, &ei_ber_syntax_not_implemented, next_tvb, 0, length_remaining, + "BER: Dissector for syntax:%s not implemented." + " Contact Wireshark developers if you want this supported", + syntax); + } else { + item = proto_tree_add_expert(tree, pinfo, &ei_ber_oid_not_implemented, next_tvb, 0, length_remaining); + } + } else { + next_tree = tree; + } + if (decode_unexpected) { + int ber_offset; + gint32 ber_len; + + if (item) { + next_tree = proto_item_add_subtree(item, ett_ber_unknown); + } + ber_offset = get_ber_identifier(next_tvb, 0, NULL, NULL, NULL); + ber_offset = get_ber_length(next_tvb, ber_offset, &ber_len, NULL); + if ((ber_len + ber_offset) == length_remaining) { + /* Decoded an ASN.1 tag with a length indicating this + * could be BER encoded data. Try dissecting as unknown BER. + */ + dissect_unknown_ber(pinfo, next_tvb, 0, next_tree); + } else { + proto_tree_add_item(next_tree, hf_ber_unknown_data, next_tvb, 0, length_remaining, ENC_NA); + } + } + len = length_remaining; + } + + offset += len; + + return offset; +} + +static int +call_ber_syntax_callback(const char *syntax, tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree) +{ + tvbuff_t *next_tvb; + int len = 0; + + next_tvb = tvb_new_subset_remaining(tvb, offset); + if (syntax == NULL || + (len = dissector_try_string(ber_syntax_dissector_table, syntax, next_tvb, pinfo, tree, NULL)) == 0) + { + proto_item *item = NULL; + + if (syntax == NULL) { + item = proto_tree_add_expert_format( + tree, pinfo, &ei_ber_no_oid, next_tvb, 0, tvb_reported_length_remaining(tvb, offset), + "BER Error: No syntax supplied to call_ber_syntax_callback"); + } else { + item = proto_tree_add_expert_format( + tree, pinfo, &ei_ber_syntax_not_implemented, next_tvb, 0, tvb_reported_length_remaining(tvb, offset), + "BER: Dissector for syntax:%s not implemented." + " Contact Wireshark developers if you want this supported", + syntax); + } + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(item, ett_ber_unknown); + dissect_unknown_ber(pinfo, next_tvb, 0, unknown_tree); + } + len = tvb_reported_length_remaining(tvb, offset); + } + + offset += len; + + return offset; +} + + +/* 8.1 General rules for encoding */ + +/* 8.1.2 Identifier octets */ +int +get_ber_identifier(tvbuff_t *tvb, int offset, gint8 *ber_class, bool *pc, gint32 *tag) { + guint8 id, t; + gint8 tmp_class; + bool tmp_pc; + gint32 tmp_tag; + + id = tvb_get_guint8(tvb, offset); + offset += 1; +#ifdef DEBUG_BER +ws_debug_printf("BER ID=%02x", id); +#endif + /* 8.1.2.2 */ + tmp_class = (id >> 6) & 0x03; + tmp_pc = (id >> 5) & 0x01; + tmp_tag = id & 0x1F; + /* 8.1.2.4 */ + if (tmp_tag == 0x1F) { + tmp_tag = 0; + while (tvb_reported_length_remaining(tvb, offset) > 0) { + t = tvb_get_guint8(tvb, offset); +#ifdef DEBUG_BER +ws_debug_printf(" %02x", t); +#endif + offset += 1; + tmp_tag <<= 7; + tmp_tag |= t & 0x7F; + if (!(t & 0x80)) + break; + } + } + +#ifdef DEBUG_BER +ws_debug_printf("\n"); +#endif + if (ber_class) + *ber_class = tmp_class; + if (pc) + *pc = tmp_pc; + if (tag) + *tag = tmp_tag; + + last_class = tmp_class; + last_pc = tmp_pc; + last_tag = tmp_tag; + + return offset; +} + +static void +get_last_ber_identifier(gint8 *ber_class, bool *pc, gint32 *tag) +{ + if (ber_class) + *ber_class = last_class; + if (pc) + *pc = last_pc; + if (tag) + *tag = last_tag; + +} + +int +dissect_ber_identifier(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, gint8 *ber_class, bool *pc, gint32 *tag) +{ + int old_offset = offset; + gint8 tmp_class; + bool tmp_pc; + gint32 tmp_tag; + + offset = get_ber_identifier(tvb, offset, &tmp_class, &tmp_pc, &tmp_tag); + + if (show_internal_ber_fields) { + proto_tree_add_uint(tree, hf_ber_id_class, tvb, old_offset, 1, tmp_class << 6); + proto_tree_add_boolean(tree, hf_ber_id_pc, tvb, old_offset, 1, (tmp_pc) ? 0x20 : 0x00); + if (tmp_tag > 0x1F) { + if (tmp_class == BER_CLASS_UNI) { + proto_tree_add_uint(tree, hf_ber_id_uni_tag_ext, tvb, old_offset + 1, offset - (old_offset + 1), tmp_tag); + } else { + proto_tree_add_uint(tree, hf_ber_id_tag_ext, tvb, old_offset + 1, offset - (old_offset + 1), tmp_tag); + } + } else { + if (tmp_class == BER_CLASS_UNI) { + proto_tree_add_uint(tree, hf_ber_id_uni_tag, tvb, old_offset, 1, tmp_tag); + } else { + proto_tree_add_uint(tree, hf_ber_id_tag, tvb, old_offset, 1, tmp_tag); + } + } + } + + if (ber_class) + *ber_class = tmp_class; + if (pc) + *pc = tmp_pc; + if (tag) + *tag = tmp_tag; + + return offset; +} + +/** Try to get the length octets of the BER TLV. + * Only (TAGs and) LENGTHs that fit inside 32 bit integers are supported. + * + * @return TRUE if we have the entire length, FALSE if we're in the middle of + * an indefinite length and haven't reached EOC. + */ +/* 8.1.3 Length octets */ + +static int +try_get_ber_length(tvbuff_t *tvb, int offset, guint32 *length, bool *ind, gint nest_level) { + guint8 oct, len; + guint32 indef_len; + guint32 tmp_length; + bool tmp_ind; + int tmp_offset, s_offset; + gint8 tclass; + bool tpc; + gint32 ttag; + + tmp_length = 0; + tmp_ind = FALSE; + + if (nest_level > BER_MAX_NESTING) { + /* Assume that we have a malformed packet. */ + THROW(ReportedBoundsError); + } + + oct = tvb_get_guint8(tvb, offset); + offset += 1; + + if (!(oct & 0x80)) { + /* 8.1.3.4 */ + tmp_length = oct; + } else { + len = oct & 0x7F; + if (len) { + /* 8.1.3.5 */ + while (len--) { + oct = tvb_get_guint8(tvb, offset); + offset++; + tmp_length = (tmp_length<<8) + oct; + } + } else { + /* 8.1.3.6 */ + + tmp_offset = offset; + /* ok in here we can traverse the BER to find the length, this will fix most indefinite length issues */ + /* Assumption here is that indefinite length is always used on constructed types*/ + /* check for EOC */ + while (tvb_get_guint8(tvb, offset) || tvb_get_guint8(tvb, offset+1)) { + /* not an EOC at offset */ + s_offset = offset; + offset= get_ber_identifier(tvb, offset, &tclass, &tpc, &ttag); + offset= try_get_ber_length(tvb, offset, &indef_len, NULL, nest_level+1); + tmp_length += indef_len+(offset-s_offset); /* length + tag and length */ + offset += indef_len; + /* Make sure we've moved forward in the packet */ + if (offset <= s_offset) + THROW(ReportedBoundsError); + } + tmp_length += 2; + tmp_ind = TRUE; + offset = tmp_offset; + } + } + + /* Several users treat the length as signed value, clamp the value to avoid + * an overflow to negative values. */ + if (tmp_length > (guint32)G_MAXINT32) + tmp_length = (guint32)G_MAXINT32; + + if (length) + *length = tmp_length; + if (ind) + *ind = tmp_ind; + +#ifdef DEBUG_BER +ws_debug_printf("get BER length %d, offset %d (remaining %d)\n", tmp_length, offset, tvb_reported_length_remaining(tvb, offset)); +#endif + + return offset; +} + +int +get_ber_length(tvbuff_t *tvb, int offset, guint32 *length, bool *ind) +{ + return try_get_ber_length(tvb, offset, length, ind, 1); +} + +static void +get_last_ber_length(guint32 *length, bool *ind, tvbuff_t **len_tvb, int *len_offset, int *len_len) +{ + if (length) + *length = last_length; + if (ind) + *ind = last_ind; + if (len_tvb) + *len_tvb = last_length_tvb; + if (len_offset) + *len_offset = last_length_offset; + if (len_len) + *len_len = last_length_len; +} + +/* this function dissects the length octets of the BER TLV. + * We only handle (TAGs and) LENGTHs that fit inside 32 bit integers. + */ +int +dissect_ber_length(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, guint32 *length, bool *ind) +{ + int old_offset = offset; + guint32 tmp_length; + bool tmp_ind; + + offset = get_ber_length(tvb, offset, &tmp_length, &tmp_ind); + + if (show_internal_ber_fields) { + if (tmp_ind) { + proto_tree_add_uint_format_value(tree, hf_ber_length, tvb, old_offset, 1, tmp_length, "Indefinite length %d", tmp_length); + } else { + if ((offset - old_offset) > 1) { + proto_tree_add_uint(tree, hf_ber_length_octets, tvb, old_offset, 1, (tvb_get_guint8(tvb, old_offset) & 0x7f)); + proto_tree_add_uint(tree, hf_ber_length, tvb, old_offset+1, offset - (old_offset+1), tmp_length); + } else { + proto_tree_add_uint(tree, hf_ber_length, tvb, old_offset, offset - old_offset, tmp_length); + } + } + } + if (length) + *length = tmp_length; + if (ind) + *ind = tmp_ind; + +#ifdef DEBUG_BER +proto_tree_add_debug_text(tree, "dissect BER length %d, offset %d (remaining %d)\n", tmp_length, offset, tvb_reported_length_remaining(tvb, offset)); +#endif + + last_length = tmp_length; + last_ind = tmp_ind; + last_length_tvb = tvb; + last_length_offset = old_offset; + last_length_len = offset - old_offset; + + return offset; +} + +static reassembly_table octet_segment_reassembly_table; + +static int +dissect_ber_constrained_octet_string_impl(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, gint hf_id, tvbuff_t **out_tvb, guint nest_level, guint encoding); + +static int +reassemble_octet_string(asn1_ctx_t *actx, proto_tree *tree, gint hf_id, tvbuff_t *tvb, int offset, guint32 con_len, bool ind, tvbuff_t **out_tvb, guint nest_level) +{ + fragment_head *fd_head = NULL; + tvbuff_t *next_tvb = NULL; + tvbuff_t *reassembled_tvb = NULL; + guint16 dst_ref = 0; + int start_offset = offset; + bool fragment = TRUE; + bool firstFragment = TRUE; + + if (nest_level > BER_MAX_NESTING) { + /* Assume that we have a malformed packet. */ + THROW(ReportedBoundsError); + } + + /* so we need to consume octet strings for the given length */ + + if (out_tvb) + *out_tvb = NULL; + + if (con_len == 0) /* Zero encodings (8.7.3) */ + return offset; + + /* not sure we need this */ + actx->pinfo->fragmented = TRUE; + + while(!fd_head) { + + offset = dissect_ber_constrained_octet_string_impl(FALSE, actx, NULL, + tvb, offset, NO_BOUND, NO_BOUND, hf_id, &next_tvb, nest_level + 1, 0); + + if (next_tvb == NULL) { + /* Assume that we have a malformed packet. */ + THROW(ReportedBoundsError); + } + + if (ind) { + /* this was indefinite length - so check for EOC */ + + if ((tvb_get_guint8(tvb, offset) == 0) && (tvb_get_guint8(tvb, offset+1) == 0)) { + fragment = FALSE; + /* skip past EOC */ + offset +=2; + } + } else { + + if ((guint32)(offset - start_offset) >= con_len) + fragment = FALSE; + } + + if (!fragment && firstFragment) { + /* there is only one fragment (I'm sure there's a reason it was constructed) */ + /* anyway, we can get out of here */ + bool pc; + get_ber_identifier(tvb, start_offset, NULL, &pc, NULL); + if (!pc && tree) { + /* Only display here if not constructed */ + dissect_ber_octet_string(FALSE, actx, tree, tvb, start_offset, hf_id, NULL); + } + reassembled_tvb = next_tvb; + break; + } + + + if (tvb_reported_length(next_tvb) < 1) { + /* Don't cause an assertion in the reassembly code. */ + THROW(ReportedBoundsError); + } + fd_head = fragment_add_seq_next(&octet_segment_reassembly_table, + next_tvb, 0, actx->pinfo, + (dst_ref | nest_level << 16), NULL, + tvb_reported_length(next_tvb), + fragment); + + firstFragment = FALSE; + } + + if (fd_head) { + if (fd_head->next) { + /* not sure I really want to do this here - should be nearer the application where we can give it a better name*/ + proto_tree *next_tree; + proto_item *frag_tree_item; + + reassembled_tvb = tvb_new_chain(next_tvb, fd_head->tvb_data); + + actx->created_item = proto_tree_add_item(tree, hf_id, reassembled_tvb, 0, -1, ENC_BIG_ENDIAN); + next_tree = proto_item_add_subtree (actx->created_item, ett_ber_reassembled_octet_string); + + add_new_data_source(actx->pinfo, reassembled_tvb, "Reassembled OCTET STRING"); + show_fragment_seq_tree(fd_head, &octet_string_frag_items, next_tree, actx->pinfo, reassembled_tvb, &frag_tree_item); + } + } + + if (out_tvb) + *out_tvb = reassembled_tvb; + + /* again - not sure we need this */ + actx->pinfo->fragmented = FALSE; + + return offset; + +} + +/* 8.7 Encoding of an octetstring value */ +int +dissect_ber_constrained_octet_string(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, gint hf_id, tvbuff_t **out_tvb) { + return dissect_ber_constrained_octet_string_impl(implicit_tag, actx, tree, tvb, offset, min_len, max_len, hf_id, out_tvb, 0, 0); +} + +static int +dissect_ber_constrained_octet_string_impl(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, gint hf_id, tvbuff_t **out_tvb, guint nest_level, guint encoding) { + gint8 ber_class; + bool pc, ind; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + tvbuff_t *len_tvb; + int len_offset; + int len_len; + int hoffset; + int end_offset; + proto_item *it, *cause; + guint32 len_remain; + +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "OCTET STRING dissect_ber_octet string(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n", name, implicit_tag, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "OCTET STRING dissect_ber_octet_string(%s) entered\n", name); +} +} +#endif + + if (out_tvb) + *out_tvb = NULL; + + if (!implicit_tag) { + hoffset = offset; + /* read header and len for the octet string */ + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, &ind); + end_offset = offset+len; + + /* sanity check: we only handle Constructed Universal Sequences */ + if ((ber_class != BER_CLASS_APP) && (ber_class != BER_CLASS_PRI)) { + if ( (ber_class != BER_CLASS_UNI) + || ((tag < BER_UNI_TAG_NumericString) && (tag != BER_UNI_TAG_OCTETSTRING) && (tag != BER_UNI_TAG_UTF8String)) ) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_octet_string, + tvb, identifier_offset, identifier_len, + "BER Error: OctetString expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tfs_get_string(pc, &tfs_constructed_primitive), + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return end_offset; + } + } + } else { + /* implicit tag so get from last tag/length */ + + get_last_ber_identifier(&ber_class, &pc, &tag); + get_last_ber_length(&len, &ind, &len_tvb, &len_offset, &len_len); + + end_offset = offset+len; + + /* caller may have created new buffer for indefinite length data Verify via length */ + len_remain = (guint32)tvb_reported_length_remaining(tvb, offset); + if (ind && (len_remain == (len - 2))) { + /* new buffer received so adjust length and indefinite flag */ + len -= 2; + end_offset -= 2; + ind = FALSE; + } else if (len_remain < len) { + /* + * error - short frame, or this item runs past the + * end of the item containing it + */ + ber_add_large_length_error(actx->pinfo, tree, tvb, offset, len, len_tvb, len_offset, len_len); + return end_offset; + } + + } + + actx->created_item = NULL; + + if (pc) { + /* constructed */ + end_offset = reassemble_octet_string(actx, tree, hf_id, tvb, offset, len, ind, out_tvb, nest_level); + } else { + /* primitive */ + gint length_remaining; + + length_remaining = tvb_reported_length_remaining(tvb, offset); +#if 0 + if (length_remaining < 1) { + return end_offset; + } +#endif + + if (len <= (guint32)length_remaining) { + length_remaining = len; + } + if (hf_id >= 0) { + /* + * Strings are special. See X.680 section 41 "Definition of + * restricted character string types" and X.690 section 8.20 + * "Encoding for values of the restricted character string + * types". + * + * Some restricted character string types are defined in X.680 + * "by reference to a registration number in the ISO International + * Register of Character Sets" - or, in Table 8, by multiple + * registration numbers, or one or more registration numbers + * plus some characters such as SPACE and/or DELETE, or a + * reference to "all G sets" or "all G sets and all C sets". + * Presumably this indicates which characters are allowed + * in strings of those character types, with "all {G,C} sets" + * meaning the only restriction is to character sets registered + * in said registry. + * + * The encodings of those types are specified in X.690 as + * containing "the octets specified in ISO/IEC 2022 for + * encodings in an 8-bit environment, using the escape sequence + * and character codings registered in accordance with ISO/IEC + * 2375". + * + * ISO/IEC 2022 is also ECMA-35: + * + * http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-035.pdf + * + * ISO/IEC 2375 is the procedure for registering character + * codings in the ISO International Register of Character Sets. + * See + * + * http://kikaku.itscj.ipsj.or.jp/ISO-IR/ + * + * and + * + * http://kikaku.itscj.ipsj.or.jp/ISO-IR/overview.htm + * + * for that registry. + * + * If we've been provided with a non-zero encoding, use + * that; otherwise, calculate it based on the tag. (A + * zero encoding is ENC_ASCII|ENC_NA/ENC_BIG_ENDIAN, which + * is the default, so it's OK to use here; this is for + * protcols such as LDAP that use OCTET STRING for UTF-8 + * strings.) + */ + if (encoding == 0) { + switch (tag) { + + case BER_UNI_TAG_UTF8String: + /* + * UTF-8, obviously. + */ + encoding = ENC_UTF_8|ENC_NA; + break; + + case BER_UNI_TAG_NumericString: + case BER_UNI_TAG_PrintableString: + case BER_UNI_TAG_VisibleString: + case BER_UNI_TAG_IA5String: + /* + * (Subsets of) Boring Old ASCII, with no(?) ISO 2022 + * escape sequences. + */ + encoding = ENC_ASCII|ENC_NA; + break; + + case BER_UNI_TAG_TeletexString: + encoding = ENC_T61|ENC_NA; + break; + + case BER_UNI_TAG_VideotexString: + encoding = ENC_T61|ENC_NA; + break; + + case BER_UNI_TAG_GraphicString: + case BER_UNI_TAG_GeneralString: + /* + * One of the types defined in terms of character sets + * in the ISO International Register of Character Sets, + * with the BER encoding being ISO 2022-based. + * + * XXX - treat as ASCII for now. + */ + encoding = ENC_ASCII|ENC_NA; + break; + + case BER_UNI_TAG_UniversalString: + /* + * UCS-4. + */ + encoding = ENC_UCS_4|ENC_BIG_ENDIAN; + break; + + case BER_UNI_TAG_CHARACTERSTRING: + /* + * XXX - what's the transfer syntax? + * Treat as ASCII for now. + */ + encoding = ENC_ASCII|ENC_NA; + break; + + case BER_UNI_TAG_BMPString: + /* + * UCS-2, not UTF-16; as it says, BMP, as in Basic + * Multilingual Plane. + */ + encoding = ENC_UCS_2|ENC_BIG_ENDIAN; + break; + + default: + encoding = ENC_BIG_ENDIAN; + break; + } + } + it = ber_proto_tree_add_item(actx->pinfo, tree, hf_id, tvb, offset, length_remaining, encoding); + actx->created_item = it; + ber_check_length(length_remaining, min_len, max_len, actx, it, FALSE); + } else { + + proto_tree_add_item(tree, hf_ber_unknown_octetstring, tvb, offset, len, ENC_NA); + } + + if (out_tvb) { + *out_tvb = ber_tvb_new_subset_length(tvb, offset, len); + } + } + return end_offset; +} + +int +dissect_ber_octet_string(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **out_tvb) { + return dissect_ber_constrained_octet_string_impl(implicit_tag, actx, tree, tvb, offset, NO_BOUND, NO_BOUND, hf_id, out_tvb, 0, 0); +} + +int +dissect_ber_octet_string_with_encoding(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **out_tvb, guint encoding) { + return dissect_ber_constrained_octet_string_impl(implicit_tag, actx, tree, tvb, offset, NO_BOUND, NO_BOUND, hf_id, out_tvb, 0, encoding); +} + +int +dissect_ber_octet_string_wcb(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, ber_callback func) +{ + tvbuff_t *out_tvb = NULL; + + offset = dissect_ber_octet_string(implicit_tag, actx, tree, tvb, offset, hf_id, (func) ? &out_tvb : NULL); + if (func && out_tvb && (tvb_reported_length(out_tvb) > 0)) { + if (hf_id >= 0) + tree = proto_item_add_subtree(actx->created_item, ett_ber_octet_string); + /* TODO Should hf_id2 be pased as last parameter???*/ + func(FALSE, out_tvb, 0, actx, tree, -1); + } + return offset; +} + +/* 8.8 Encoding of a null value */ +int +dissect_ber_null(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id) { + gint8 ber_class; + bool pc; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + int len_offset; + int len_len; + + if (!implicit_tag) { + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + if (pc || + ((ber_class != BER_CLASS_UNI) || (tag != BER_UNI_TAG_NULL))) { + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_null, + tvb, identifier_offset, identifier_len, + "BER Error: NULL expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tfs_get_string(pc, &tfs_constructed_primitive), + tag); + } + + len_offset = offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + len_len = offset - len_offset; + if (len) { + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_null_zero_length, + tvb, len_offset, len_len, + "BER Error: NULL type expects zero length data but Length=%d", + len); + proto_tree_add_item(tree, hf_ber_extra_data, tvb, offset, len, ENC_NA); + offset += len; + } + } + if (hf_id >= 0) + proto_tree_add_item(tree, hf_id, tvb, offset, 0, ENC_BIG_ENDIAN); + return offset; +} + +int +dissect_ber_integer64(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, gint64 *value) +{ + gint8 ber_class; + bool pc; + gint32 tag; + guint32 len; + gint64 val; + guint32 i; + bool used_too_many_bytes = FALSE; + guint8 first = 0; +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "INTEGERnew dissect_ber_integer(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n", name, implicit_tag, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "INTEGERnew dissect_ber_integer(%s) entered implicit_tag:%d \n", name, implicit_tag); +} +} +#endif + + + if (value) { + *value = 0; + } + + if (!implicit_tag) { + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + } else { + gint32 remaining = tvb_reported_length_remaining(tvb, offset); + len = remaining>0 ? remaining : 0; + } + + val = 0; + if (len > 0) { + + first = tvb_get_guint8(tvb, offset); + /* we can't handle integers > 64 bits */ + /* take into account the use case of a 64bits unsigned integer: you will have a 9th byte set to 0 */ + if ((len > 9) || ((len == 9) && (first != 0))) { + if (hf_id >= 0) { + header_field_info *hfinfo = proto_registrar_get_nth(hf_id); + + /* use the original field only if it is suitable for bytes */ + if (hfinfo->type != FT_BYTES) + hf_id = hf_ber_64bit_uint_as_bytes; + + proto_tree_add_bytes_format(tree, hf_id, tvb, offset, len, NULL, + "%s: 0x%s", hfinfo->name, tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, len)); + } + + offset += len; + return offset; + } + /* extend sign bit for signed fields */ + enum ftenum type = FT_INT32; /* Default to signed, is this correct? */ + if (hf_id >= 0) { + type = proto_registrar_get_ftype(hf_id); + } + if (first & 0x80 && FT_IS_INT(type)) { + val = -1; + } + if ((len > 1) && decode_warning_leading_zero_bits) { + guint8 second = tvb_get_guint8(tvb, offset+1); + if (((first == 0x00) && ((second & 0x80) == 0)) || + ((first == 0xff) && ((second & 0x80) != 0))) { + used_too_many_bytes = TRUE; + } + } + for (i=0; icreated_item = NULL; + + if (hf_id >= 0) { + /* */ + header_field_info* hfi; + + hfi = proto_registrar_get_nth(hf_id); + if ((len < 1) || (len > 9) || ((len == 9) && (first != 0))) { + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_error_length, tvb, offset-len, len, + "BER Error: %s: length of item (%u) is not valid", hfi->name, len); + } else { + switch (hfi->type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + actx->created_item = proto_tree_add_uint(tree, hf_id, tvb, offset-len, len, (guint32)val); + break; + case FT_INT8: + case FT_INT16: + case FT_INT24: + case FT_INT32: + actx->created_item = proto_tree_add_int(tree, hf_id, tvb, offset-len, len, (gint32)val); + break; + case FT_INT64: + actx->created_item = proto_tree_add_int64(tree, hf_id, tvb, offset-len, len, val); + break; + case FT_UINT64: + actx->created_item = proto_tree_add_uint64(tree, hf_id, tvb, offset-len, len, (guint64)val); + break; + case FT_BYTES: + /* + * Some protocols have INTEGER fields that can store values + * larger than 64 bits and therefore have to use FT_BYTES. + * Values larger than 64 bits are handled above while smaller + * values are handled here. + */ + actx->created_item = proto_tree_add_bytes_format(tree, hf_id, tvb, offset-len, len, NULL, + "%s: 0x%s", hfi->name, tvb_bytes_to_str(wmem_packet_scope(), tvb, offset-len, len)); + break; + default: + DISSECTOR_ASSERT_NOT_REACHED(); + } + + if (used_too_many_bytes) { + expert_add_info_format( + actx->pinfo, actx->created_item, &ei_ber_value_too_many_bytes, + "Value is encoded with too many bytes(9 leading zero or one bits), hf_abbr: %s", + hfi->abbrev); + } + } + } + + if (value) { + *value = val; + } + + return offset; +} + +int +dissect_ber_constrained_integer64(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint64 min_len, gint64 max_len, gint hf_id, gint64 *value) +{ + gint64 val; + + offset = dissect_ber_integer64(implicit_tag, actx, tree, tvb, offset, hf_id, &val); + if (value) { + *value = val; + } + + ber_check_value64 (val, min_len, max_len, actx, actx->created_item); + + return offset; +} + +int +dissect_ber_integer(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, guint32 *value) +{ + gint64 val; + + offset = dissect_ber_integer64(implicit_tag, actx, tree, tvb, offset, hf_id, &val); + if (value) { + *value = (guint32)val; + } + + return offset; +} + +int +dissect_ber_constrained_integer(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, gint hf_id, guint32 *value) +{ + gint64 val; + + offset = dissect_ber_integer64(implicit_tag, actx, tree, tvb, offset, hf_id, &val); + if (value) { + *value = (guint32)val; + } + + ber_check_value ((guint32)val, min_len, max_len, actx, actx->created_item); + + return offset; +} + +int +dissect_ber_boolean(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, bool *value) +{ + gint8 ber_class; + bool pc; + gint32 tag; + guint32 len; + guint8 val; + header_field_info *hfi; + + if (!implicit_tag) { + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + /*if (ber_class != BER_CLASS_UNI)*/ + } else { + gint32 remaining = tvb_reported_length_remaining(tvb, offset); + len = remaining > 0 ? remaining : 0; + } + + if (len == 1) + { + val = tvb_get_guint8(tvb, offset); + offset += 1; + + actx->created_item = NULL; + + if (hf_id >= 0) { + hfi = proto_registrar_get_nth(hf_id); + if (hfi->type == FT_BOOLEAN) + actx->created_item = proto_tree_add_boolean(tree, hf_id, tvb, offset-1, 1, val); + else + actx->created_item = proto_tree_add_uint(tree, hf_id, tvb, offset-1, 1, val ? 1 : 0); + } + } else { + val = 0; + actx->created_item = NULL; + + if (hf_id >= 0) { + hfi = proto_registrar_get_nth(hf_id); + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_error_length, tvb, offset, len, + "BER Error: %s: length of item (%u) is not valid", hfi->name, len); + } + } + + if (value) { + *value = (val ? TRUE : FALSE); + } + + return offset; +} + + +/* 8.5 Encoding of a real value */ +/* Somewhat tested */ +int +dissect_ber_real(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, double *value) +{ + gint8 ber_class; + bool pc; + gint32 tag; + guint32 val_length = 0, len_remain, end_offset; + tvbuff_t *len_tvb; + int len_offset; + int len_len; + double val = 0; + + if (!implicit_tag) { + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &val_length, NULL); + + end_offset = offset + val_length; + } else { + /* implicit tag so get from last tag/length */ + get_last_ber_identifier(&ber_class, &pc, &tag); + get_last_ber_length(&val_length, NULL, &len_tvb, &len_offset, &len_len); + + end_offset = offset + val_length; + + /* Check is buffer has (at least) the expected length */ + len_remain = (guint32)tvb_reported_length_remaining(tvb, offset); + if (len_remain < val_length) { + /* error - this item runs past the end of the item containing it */ + ber_add_large_length_error(actx->pinfo, tree, tvb, offset, val_length, len_tvb, len_offset, len_len); + return end_offset; + } + } + /* 8.5.1 The encoding of a real value shall be primitive. */ + if(pc) { + /* Constructed (not primitive) */ + proto_tree_add_expert( + tree, actx->pinfo, &ei_ber_real_not_primitive, tvb, offset - 2, 1); + } + + val = asn1_get_real(tvb_get_ptr(tvb, offset, val_length), val_length); + actx->created_item = proto_tree_add_double(tree, hf_id, tvb, end_offset - val_length, val_length, val); + + if (value) + *value = val; + + return end_offset; + +} +/* this function dissects a BER sequence + */ +int +dissect_ber_sequence(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id) { + gint8 classx; + bool pcx, ind = 0, ind_field, imp_tag = FALSE; + gint32 tagx; + int identifier_offset; + int identifier_len; + guint32 lenx; + proto_tree *tree = parent_tree; + proto_item *item = NULL; + proto_item *cause; + int end_offset = 0; + int hoffset; + tvbuff_t *next_tvb; + +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "SEQUENCE dissect_ber_sequence(%s) entered offset:%d len:%d %02x:%02x:%02x\n", name, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "SEQUENCE dissect_ber_sequence(%s) entered\n", name); +} +} +#endif + hoffset = offset; + if (!implicit_tag) { + offset = get_ber_identifier(tvb, offset, NULL, NULL, NULL); + offset = get_ber_length(tvb, offset, &lenx, NULL); + } else { + /* was implicit tag so just use the length of the tvb */ + lenx = tvb_reported_length_remaining(tvb, offset); + end_offset = offset+lenx; + } + /* create subtree */ + if (hf_id >= 0) { + if (parent_tree) { + item = proto_tree_add_item(parent_tree, hf_id, tvb, hoffset, lenx + offset - hoffset, ENC_BIG_ENDIAN); + tree = proto_item_add_subtree(item, ett_id); + } + } + offset = hoffset; + + if (!implicit_tag) { + /* first we must read the sequence header */ + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &classx, &pcx, &tagx); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &lenx, &ind); + if (ind) { + /* Fixed the length is correctly returned from dissect ber_length + end_offset = tvb_reported_length(tvb);*/ + end_offset = offset + lenx -2; + } else { + end_offset = offset + lenx; + } + + /* sanity check: we only handle Constructed Universal Sequences */ + if ((classx != BER_CLASS_APP) && (classx != BER_CLASS_PRI)) { + if (!pcx + || ((classx != BER_CLASS_UNI) || (tagx != BER_UNI_TAG_SEQUENCE))) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_sequence, + tvb, identifier_offset, identifier_len, + "BER Error: Sequence expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(classx, ber_class_codes, "Unknown"), + classx, + tfs_get_string(pcx, &tfs_constructed_primitive), + tagx); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return end_offset; + } + } + } + if(offset == end_offset){ + proto_item_append_text(item, " [0 length]"); + } + /* loop over all entries until we reach the end of the sequence */ + while (offset < end_offset) { + gint8 ber_class; + bool pc; + gint32 tag; + guint32 len; + int eoffset, count; + + /*if (ind) { this sequence was of indefinite length, if this is implicit indefinite impossible maybe + but ber dissector uses this to eat the tag length then pass into here... EOC still on there...*/ + if ((tvb_get_guint8(tvb, offset) == 0) && (tvb_get_guint8(tvb, offset+1) == 0)) { + /* If the first bytes is 00 00 of a indefenert length field it's a zero length field*/ + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, &ind); + proto_item_append_text(item, " 0 items"); + return end_offset; + /* + if (show_internal_ber_fields) { + proto_tree_add_expert(tree, pinfo, &ei_ber_error_seq_eoc, tvb, s_offset, offset+2, "ERROR WRONG SEQ EOC"); + } + return end_offset; + */ + } + /* } */ + hoffset = offset; + /* read header and len for next field */ + offset = get_ber_identifier(tvb, offset, &ber_class, &pc, &tag); + offset = get_ber_length(tvb, offset, &len, &ind_field); + eoffset = offset + len; + /* Make sure we move forward */ + if (eoffset <= hoffset) + THROW(ReportedBoundsError); + + /*if (ind_field && (len == 2)) { + / disgusting indefinite length zero length field, what are these people doing / + offset = eoffset; + continue; + } + */ + +ber_sequence_try_again: + /* have we run out of known entries in the sequence ?*/ + if (!seq->func) { + /* it was not, move to the next one and try again */ + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_unknown_field_sequence, + tvb, hoffset, ((offset - hoffset) + len), + "BER Error: This field lies beyond the end of the known sequence definition."); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + offset = eoffset; + continue; + } + + /* Verify that this one is the one we want. + * Skip check completely if ber_class == ANY + * of if NOCHKTAG is set + */ +/* XXX Bug in asn2wrs, + * for scope [7] Scope OPTIONAL, + * it generates + * { BER_CLASS_CON, 7, BER_FLAGS_OPTIONAL|BER_FLAGS_NOTCHKTAG, dissect_scope }, + * and there should not be a NOTCHKTAG here + */ + if ( ((seq->ber_class == BER_CLASS_CON) || (seq->ber_class == BER_CLASS_APP) || (seq->ber_class == BER_CLASS_PRI)) + && (!(seq->flags & BER_FLAGS_NOOWNTAG)) ) { + if ( (seq->ber_class != BER_CLASS_ANY) + && (seq->tag != -1) + && ( (seq->ber_class != ber_class) + || (seq->tag != tag) ) ) { + /* it was not, move to the next one and try again */ + if (seq->flags & BER_FLAGS_OPTIONAL) { + /* well this one was optional so just skip to the next one and try again. */ + seq++; + goto ber_sequence_try_again; + } + identifier_offset = hoffset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + identifier_len = offset - identifier_offset; + dissect_ber_length(actx->pinfo, tree, tvb, offset, NULL, NULL); + if (seq->ber_class == BER_CLASS_UNI) { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_sequence_field_wrong, + tvb, identifier_offset, identifier_len, + "BER Error: Wrong field in SEQUENCE: expected class:%s(%d) tag:%d (%s) but found class:%s(%d) tag:%d", + val_to_str_const(seq->ber_class, ber_class_codes, "Unknown"), + seq->ber_class, + seq->tag, + val_to_str_ext_const(seq->tag, &ber_uni_tag_codes_ext, "Unknown"), + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tag); + } else { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_sequence_field_wrong, + tvb, identifier_offset, identifier_len, + "BER Error: Wrong field in SEQUENCE: expected class:%s(%d) tag:%d but found class:%s(%d) tag:%d", + val_to_str_const(seq->ber_class, ber_class_codes, "Unknown"), + seq->ber_class, + seq->tag, + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tag); + } + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + seq++; + offset = eoffset; + continue; + } + } else if (!(seq->flags & BER_FLAGS_NOTCHKTAG)) { + if ( (seq->ber_class != BER_CLASS_ANY) + && (seq->tag != -1) + && ( (seq->ber_class != ber_class) + || (seq->tag != tag) ) ) { + /* it was not, move to the next one and try again */ + if (seq->flags & BER_FLAGS_OPTIONAL) { + /* well this one was optional so just skip to the next one and try again. */ + seq++; + goto ber_sequence_try_again; + } + + identifier_offset = hoffset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + identifier_len = offset - identifier_offset; + dissect_ber_length(actx->pinfo, tree, tvb, offset, NULL, NULL); + if ( seq->ber_class == BER_CLASS_UNI) { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_sequence_field_wrong, + tvb, identifier_offset, identifier_len, + "BER Error: Wrong field in SEQUENCE: expected class:%s(%d) tag:%d(%s) but found class:%s(%d) tag:%d", + val_to_str_const(seq->ber_class, ber_class_codes, "Unknown"), + seq->ber_class, + seq->tag, + val_to_str_ext_const(seq->tag, &ber_uni_tag_codes_ext, "Unknown"), + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, tag); + } else { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_sequence_field_wrong, + tvb, identifier_offset, identifier_len, + "BER Error: Wrong field in SEQUENCE: expected class:%s(%d) tag:%d but found class:%s(%d) tag:%d", + val_to_str_const(seq->ber_class, ber_class_codes, "Unknown"), + seq->ber_class, + seq->tag, + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tag); + } + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + seq++; + offset = eoffset; + continue; + } + } + + if (!(seq->flags & BER_FLAGS_NOOWNTAG) ) { + /* dissect header and len for field */ + if (ind_field && (len == 2)) { + /* This is a Zero length field */ + next_tvb = ber_tvb_new_subset_length(tvb, offset, len); + hoffset = eoffset; + } else { + hoffset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + hoffset = dissect_ber_length(actx->pinfo, tree, tvb, hoffset, NULL, NULL); + next_tvb = ber_tvb_new_subset_length(tvb, hoffset, eoffset - hoffset - (2 * ind_field)); + } + } else { + next_tvb = ber_tvb_new_subset_length(tvb, hoffset, eoffset - hoffset); + } + +#if 0 + /* call the dissector for this field */ + if ((eoffset-hoffset) > length_remaining) { + /* If the field is indefinite (i.e. we don't know the + * length) of if the tvb is short, then just + * give it all of the tvb and hope for the best. + */ + next_tvb = tvb_new_subset_remaining(tvb, hoffset); + } else { + + } +#endif + +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(next_tvb, 0) > 3) { +proto_tree_add_debug_text(tree, "SEQUENCE dissect_ber_sequence(%s) calling subdissector offset:%d len:%d %02x:%02x:%02x\n", name, offset, tvb_reported_length_remaining(next_tvb, 0), tvb_get_guint8(next_tvb, 0), tvb_get_guint8(next_tvb, 1), tvb_get_guint8(next_tvb, 2)); +} else { +proto_tree_add_debug_text(tree, "SEQUENCE dissect_ber_sequence(%s) calling subdissector\n", name); +} +} +#endif + if (next_tvb == NULL) { + /* Assume that we have a malformed packet. */ + THROW(ReportedBoundsError); + } + imp_tag = FALSE; + if (seq->flags & BER_FLAGS_IMPLTAG) { + imp_tag = TRUE; + } + + count = seq->func(imp_tag, next_tvb, 0, actx, tree, *seq->p_id); + +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +proto_tree_add_debug_text(tree, "SEQUENCE dissect_ber_sequence(%s) subdissector ate %d bytes\n", name, count); +} +#endif + /* if it was optional and no bytes were eaten and it was */ + /* supposed to (len <> 0), just try again. */ + if ((len != 0) && (count == 0) && (seq->flags & BER_FLAGS_OPTIONAL)) { + seq++; + goto ber_sequence_try_again; + /* move the offset to the beginning of the next sequenced item */ + } + offset = eoffset; + if (!(seq->flags & BER_FLAGS_NOOWNTAG) ) { + /* if we stripped the tag and length we should also strip the EOC is ind_len + * Unless it's a zero length field (len = 2) + */ + if ((ind_field == 1) && (len > 2)) + { + /* skip over EOC */ + if (show_internal_ber_fields) { + proto_tree_add_item(tree, hf_ber_seq_field_eoc, tvb, offset-2, 2, ENC_NA); + } + } + } + seq++; + } + + /* if we didn't end up at exactly offset, then we ate too many bytes */ + if (offset != end_offset) { + tvb_ensure_bytes_exist(tvb, offset-2, 2); + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_error_length, tvb, offset-2, 2, + "BER Error: SEQUENCE is %d too many bytes long", + offset - end_offset); + } + if (ind) { + /* need to eat this EOC + end_offset = tvb_reported_length(tvb);*/ + end_offset += 2; + if (show_internal_ber_fields) { + proto_tree_add_item(tree, hf_ber_seq_eoc, tvb, end_offset-2, 2, ENC_NA); + } + } + return end_offset; +} + +/* This function dissects a BER set + */ +int +dissect_ber_set(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *set, gint hf_id, gint ett_id) { + gint8 classx; + bool pcx, ind = 0, ind_field, imp_tag; + gint32 tagx; + int identifier_offset; + int identifier_len; + guint32 lenx; + int len_offset; + int len_len; + proto_tree *tree = parent_tree; + proto_item *item = NULL; + proto_item *cause; + int end_offset, s_offset; + int hoffset; + tvbuff_t *next_tvb; + guint32 mandatory_fields = 0; + guint8 set_idx; + bool first_pass; + const ber_sequence_t *cset = NULL; + +#define MAX_SET_ELEMENTS 32 + + s_offset = offset; + +#ifdef DEBUG_BER + { +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "SET dissect_ber_set(%s) entered offset:%d len:%d %02x:%02x:%02x\n", name, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "SET dissect_ber_set(%s) entered\n", name); +} +} +#endif + + if (!implicit_tag) { + hoffset = offset; + /* first we must read the sequence header */ + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &classx, &pcx, &tagx); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &lenx, &ind); + if (ind) { + /* Fixed the length is correctly returned from dissect ber_length + end_offset = tvb_reported_length(tvb);*/ + end_offset = offset + lenx -2; + } else { + end_offset = offset + lenx; + } + + /* sanity check: we only handle Constructed Universal Sets */ + if ((classx != BER_CLASS_APP) && (classx != BER_CLASS_PRI)) { + if (!pcx + || ((classx != BER_CLASS_UNI) || (tagx != BER_UNI_TAG_SET))) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_set, + tvb, identifier_offset, identifier_len, + "BER Error: SET expected but class:%s(%d) %s tag:%d was found", + val_to_str_const(classx, ber_class_codes, "Unknown"), + classx, + tfs_get_string(pcx, &tfs_constructed_primitive), + tagx); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return end_offset; + } + } + } else { + /* was implicit tag so just use the length of the tvb */ + lenx = tvb_reported_length_remaining(tvb, offset); + end_offset = offset+lenx; + identifier_offset = 0; + identifier_len = 0; + } + + /* create subtree */ + if (hf_id >= 0) { + if (parent_tree) { + item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, lenx, ENC_BIG_ENDIAN); + tree = proto_item_add_subtree(item, ett_id); + } + } + + /* record the mandatory elements of the set so we can check we found everything at the end + we can only record 32 elements for now ... */ + for (set_idx = 0; (set_idx < MAX_SET_ELEMENTS) && (cset = &set[set_idx])->func; set_idx++) { + + if (!(cset->flags & BER_FLAGS_OPTIONAL)) + mandatory_fields |= 1 << set_idx; + + } + + /* loop over all entries until we reach the end of the set */ + while (offset < end_offset) { + gint8 ber_class; + bool pc; + gint32 tag; + guint32 len; + int eoffset, count; + + /*if (ind) { this sequence was of indefinite length, if this is implicit indefinite impossible maybe + but ber dissector uses this to eat the tag length then pass into here... EOC still on there...*/ + + if ((tvb_get_guint8(tvb, offset) == 0) && (tvb_get_guint8(tvb, offset+1) == 0)) { + if (show_internal_ber_fields) { + proto_tree_add_item(tree, hf_ber_seq_eoc, tvb, s_offset, offset+2, ENC_NA); + } + return end_offset; + } + /* } */ + hoffset = offset; + /* read header and len for next field */ + identifier_offset = offset; + offset = get_ber_identifier(tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + len_offset = offset; + offset = get_ber_length(tvb, offset, &len, &ind_field); + len_len = offset - len_offset; + eoffset = offset + len; + + if (len > (guint32)(end_offset - offset) || len > (guint32) tvb_reported_length_remaining(tvb, offset)) { + ber_add_large_length_error(actx->pinfo, tree, tvb, offset, len, tvb, len_offset, len_len); + return end_offset; + } + + /* Look through the Set to see if this class/id exists and + * hasn't been seen before + * Skip check completely if ber_class == ANY + * of if NOCHKTAG is set + */ + + for (first_pass = TRUE, cset = set, set_idx = 0; cset->func || first_pass; cset++, set_idx++) { + + /* we reset for a second pass when we will look for choices */ + if (!cset->func) { + first_pass = FALSE; + + cset = set; /* reset to the beginning */ + set_idx = 0; + /* If the set has no values, there is no point in trying again. */ + if (!cset->func) { + break; + } + } + + if ((first_pass && ((cset->ber_class == ber_class) && (cset->tag == tag))) || + (!first_pass && ((cset->ber_class == BER_CLASS_ANY) && (cset->tag == -1))) ) /* choices */ + { + if (!(cset->flags & BER_FLAGS_NOOWNTAG) ) { + /* dissect header and len for field */ + hoffset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + hoffset = dissect_ber_length(actx->pinfo, tree, tvb, hoffset, NULL, NULL); + next_tvb = ber_tvb_new_subset_length(tvb, hoffset, eoffset - hoffset - (2 * ind_field)); + } else { + next_tvb = ber_tvb_new_subset_length(tvb, hoffset, eoffset - hoffset); + } + + +#if 0 + /* call the dissector for this field */ + if ((eoffset-hoffset)>length_remaining) { + /* If the field is indefinite (i.e. we don't know the + * length) of if the tvb is short, then just + * give it all of the tvb and hope for the best. + */ + next_tvb = tvb_new_subset_remaining(tvb, hoffset); + } else { + + } +#endif + +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(next_tvb, 0) > 3) { +proto_tree_add_debug_text(tree, "SET dissect_ber_set(%s) calling subdissector offset:%d len:%d %02x:%02x:%02x\n", name, offset, tvb_reported_length_remaining(next_tvb, 0), tvb_get_guint8(next_tvb, 0), tvb_get_guint8(next_tvb, 1), tvb_get_guint8(next_tvb, 2)); +} else { +proto_tree_add_debug_text(tree, "SET dissect_ber_set(%s) calling subdissector\n", name); +} +} +#endif + if (next_tvb == NULL) { + /* Assume that we have a malformed packet. */ + THROW(ReportedBoundsError); + } + imp_tag = FALSE; + if ((cset->flags & BER_FLAGS_IMPLTAG)) + imp_tag = TRUE; + count = cset->func(imp_tag, next_tvb, 0, actx, tree, *cset->p_id); + + /* if we consumed some bytes, + or we knew the length was zero (during the first pass only) */ + if (count || (first_pass && ((len == 0) || ((ind_field == 1) && (len == 2))))) { + /* we found it! */ + if (set_idx < MAX_SET_ELEMENTS) + mandatory_fields &= ~(1 << set_idx); + + offset = eoffset; + + if (!(cset->flags & BER_FLAGS_NOOWNTAG) ) { + /* if we stripped the tag and length we should also strip the EOC is ind_len */ + if (ind_field == 1) { + /* skip over EOC */ + if (show_internal_ber_fields) { + proto_tree_add_item(tree, hf_ber_set_field_eoc, tvb, offset, count, ENC_NA); + } + } + } + break; + } + } + } + + if (!cset->func) { + /* we didn't find a match */ + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_unknown_field_set, + tvb, identifier_offset, identifier_len, + "BER Error: Unknown field in SET class:%s(%d) tag:%d", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + offset = eoffset; + } + } + + if (mandatory_fields) { + + /* OK - we didn't find some of the elements we expected */ + + for (set_idx = 0; (set_idx < MAX_SET_ELEMENTS) && (cset = &set[set_idx])->func; set_idx++) { + if (mandatory_fields & (1U << set_idx)) { + /* here is something we should have seen - but didn't! */ + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_missing_field_set, + tvb, identifier_offset, identifier_len, + "BER Error: Missing field in SET class:%s(%d) tag:%d expected", + val_to_str_const(cset->ber_class, ber_class_codes, "Unknown"), + cset->ber_class, + cset->tag); + } + + } + } + + /* if we didn't end up at exactly offset, then we ate too many bytes */ + if (offset != end_offset) { + tvb_ensure_bytes_exist(tvb, offset-2, 2); + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_error_length, tvb, offset-2, 2, + "BER Error: SET is %d too many bytes long", + offset - end_offset); + } + + if (ind) { + /* need to eat this EOC + end_offset = tvb_reported_length(tvb);*/ + end_offset += 2; + if (show_internal_ber_fields) { + proto_tree_add_item(tree, hf_ber_set_eoc, tvb, end_offset-2, 2, ENC_NA); + } + } + + return end_offset; + +} + +#ifdef DEBUG_BER +#define DEBUG_BER_CHOICE +#endif + +int +dissect_ber_choice(asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_choice_t *choice, gint hf_id, gint ett_id, gint *branch_taken) +{ + gint8 ber_class; + bool pc, ind, imp_tag = FALSE; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + proto_tree *tree = parent_tree; + proto_item *item = NULL; + int end_offset, start_offset, count; + int hoffset = offset; + gint length; + tvbuff_t *next_tvb; + bool first_pass; + header_field_info *hfinfo; + const ber_choice_t *ch; + +#ifdef DEBUG_BER_CHOICE +{ +const char *name; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "CHOICE dissect_ber_choice(%s) entered offset:%d len:%d %02x:%02x:%02x\n", name, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "CHOICE dissect_ber_choice(%s) entered len:%d\n", name, tvb_reported_length_remaining(tvb, offset)); +} +} +#endif + + start_offset = offset; + + if (branch_taken) { + *branch_taken = -1; + } + + if (tvb_reported_length_remaining(tvb, offset) == 0) { + proto_tree_add_expert( + parent_tree, actx->pinfo, &ei_ber_empty_choice, tvb, offset, 0); + return offset; + } + + /* read header and len for choice field */ + identifier_offset = offset; + offset = get_ber_identifier(tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = get_ber_length(tvb, offset, &len, &ind); + end_offset = offset + len ; + + /* Some sanity checks. + * The hf field passed to us MUST be an integer type + */ + if (hf_id >= 0) { + hfinfo = proto_registrar_get_nth(hf_id); + switch (hfinfo->type) { + case FT_UINT8: + case FT_UINT16: + case FT_UINT24: + case FT_UINT32: + break; + default: + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_hf_field_not_integer_type, + tvb, identifier_offset, identifier_len, + "BER Error: dissect_ber_choice(): frame:%u offset:%d Was passed an HF field that was not integer type : %s", + actx->pinfo->num, offset, hfinfo->abbrev); + return end_offset; + } + } + + /* loop over all entries until we find the right choice or + run out of entries */ + ch = choice; + first_pass = TRUE; + while (ch->func || first_pass) { + if (branch_taken) { + (*branch_taken)++; + } + /* we reset for a second pass when we will look for choices */ + if (!ch->func) { + first_pass = FALSE; + ch = choice; /* reset to the beginning */ + if (branch_taken) { + *branch_taken = -1; + } + continue; + } + +#ifdef DEBUG_BER_CHOICE +proto_tree_add_debug_text(tree, "CHOICE testing potential subdissector class[%p]:%d:(expected)%d tag:%d:(expected)%d flags:%d\n", ch, ber_class, ch->ber_class, tag, ch->tag, ch->flags); +#endif + if ( (first_pass + && (((ch->ber_class == ber_class) && (ch->tag == tag)) + || ((ch->ber_class == ber_class) && (ch->tag == -1) && (ch->flags & BER_FLAGS_NOOWNTAG)))) + || (!first_pass && (((ch->ber_class == BER_CLASS_ANY) && (ch->tag == -1)))) /* we failed on the first pass so now try any choices */ + ) { + if (!(ch->flags & BER_FLAGS_NOOWNTAG)) { + /* dissect header and len for field */ + hoffset = dissect_ber_identifier(actx->pinfo, tree, tvb, start_offset, NULL, NULL, NULL); + hoffset = dissect_ber_length(actx->pinfo, tree, tvb, hoffset, NULL, NULL); + start_offset = hoffset; + if (ind) { + length = len - 2; + } else { + length = len; + } + } else { + length = end_offset- hoffset; + } + /* create subtree */ + if (hf_id >= 0) { + if (parent_tree) { + item = proto_tree_add_uint(parent_tree, hf_id, tvb, hoffset, end_offset - hoffset, ch->value); + tree = proto_item_add_subtree(item, ett_id); + } + } + +#ifdef REMOVED + /* This is bogus and makes the OID_1.0.9506.1.1.cap file + * in Steven J Schaeffer's email of 2005-09-12 fail to dissect + * properly. Maybe we should get rid of 'first_pass' + * completely. + * It was added as a qad workaround for some problem CMIP + * traces anyway. + * God, this file is a mess and it is my fault. /ronnie + */ + if (first_pass) + next_tvb = ber_tvb_new_subset_length(tvb, hoffset, length); + else + next_tvb = tvb; /* we didn't make selection on this class/tag so pass it on */ +#endif + next_tvb = ber_tvb_new_subset_length(tvb, hoffset, length); + + +#ifdef DEBUG_BER_CHOICE +{ +const char *name; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(next_tvb, 0) > 3) { +proto_tree_add_debug_text(tree, "CHOICE dissect_ber_choice(%s) calling subdissector start_offset:%d offset:%d len:%d %02x:%02x:%02x\n", name, start_offset, offset, tvb_reported_length_remaining(next_tvb, 0), tvb_get_guint8(next_tvb, 0), tvb_get_guint8(next_tvb, 1), tvb_get_guint8(next_tvb, 2)); +} else { +proto_tree_add_debug_text(tree, "CHOICE dissect_ber_choice(%s) calling subdissector len:%d\n", name, tvb_reported_length(next_tvb)); +} +} +#endif + if (next_tvb == NULL) { + /* Assume that we have a malformed packet. */ + THROW(ReportedBoundsError); + } + imp_tag = FALSE; + if ((ch->flags & BER_FLAGS_IMPLTAG)) + imp_tag = TRUE; + count = ch->func(imp_tag, next_tvb, 0, actx, tree, *ch->p_id); +#ifdef DEBUG_BER_CHOICE +{ +const char *name; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +proto_tree_add_debug_text(tree, "CHOICE dissect_ber_choice(%s) subdissector ate %d bytes\n", name, count); +} +#endif + if ((count == 0) && (((ch->ber_class == ber_class) && (ch->tag == -1) && (ch->flags & BER_FLAGS_NOOWNTAG)) || !first_pass)) { + /* wrong one, break and try again */ + ch++; +#ifdef DEBUG_BER_CHOICE +{ +const char *name; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +proto_tree_add_debug_text(tree, "CHOICE dissect_ber_choice(%s) trying again\n", name); +} +#endif + continue; + } + if (!(ch->flags & BER_FLAGS_NOOWNTAG)) { + if (ind) { + /* we are traversing a indfinite length choice where we did not pass the tag length */ + /* we need to eat the EOC */ + if (show_internal_ber_fields) { + proto_tree_add_item(tree, hf_ber_choice_eoc, tvb, end_offset-2, 2, ENC_NA); + } + } + } + return end_offset; + } + ch++; + } + if (branch_taken) { + /* none of the branches were taken so set the param + back to -1 */ + *branch_taken = -1; + } + +#ifdef REMOVED + /*XXX here we should have another flag to the CHOICE to distinguish + * between the case when we know it is a mandatory or if the CHOICE is optional == no arm matched */ + + /* oops no more entries and we still haven't found + * our guy :-( + */ + proto_tree_add_expert( + tree, actx->pinfo, &ei_ber_choice_not_found, tvb, offset, len); + return end_offset; +#endif + + return start_offset; +} + +#if 0 +/* this function dissects a BER GeneralString + */ +int +dissect_ber_GeneralString(asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *name_string, int name_len) +{ + gint8 ber_class; + bool pc; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + int end_offset; + int hoffset; + char str_arr[256]; + guint32 max_len; + char *str; + proto_item *cause; + + str = str_arr; + max_len = 255; + if (name_string) { + str = name_string; + max_len = name_len; + } + + hoffset = offset; + /* first we must read the GeneralString header */ + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + end_offset = offset + len; + + /* sanity check: we only handle Universal GeneralString*/ + if ( (ber_class != BER_CLASS_UNI) + || (tag != BER_UNI_TAG_GENSTR) ) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_general_string, + tvb, identifier_offset, identifier_len, + "BER Error: GeneralString expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, tfs_get_string(pc, &tfs_constructed_primitive), + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return end_offset; + } + + if (len >= (max_len - 1)) { + len = max_len - 1; + } + + tvb_memcpy(tvb, str, offset, len); + str[len]=0; + + if (hf_id >= 0) { + proto_tree_add_string(tree, hf_id, tvb, offset, len, str); + } + + return end_offset; +} +#endif + +int +dissect_ber_constrained_restricted_string(bool implicit_tag, gint32 type, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, gint hf_id, tvbuff_t **out_tvb) { + gint8 ber_class; + bool pc; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + int eoffset; + int hoffset = offset; + proto_item *cause; + +#ifdef DEBUG_BER +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "RESTRICTED STRING dissect_ber_octet string(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n", name, implicit_tag, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "RESTRICTED STRING dissect_ber_octet_string(%s) entered\n", name); +} +} +#endif + + if (!implicit_tag) { + identifier_offset = offset; + offset = get_ber_identifier(tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = get_ber_length(tvb, offset, &len, NULL); + eoffset = offset + len; + + /* sanity check */ + if ( (ber_class != BER_CLASS_UNI) + || (tag != type) ) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_string, + tvb, identifier_offset, identifier_len, + "BER Error: String with tag=%d expected but class:%s(%d) %s tag:%d was unexpected", + type, + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, tfs_get_string(pc, &tfs_constructed_primitive), + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return eoffset; + } + } + + /* 8.21.3 */ + return dissect_ber_constrained_octet_string(implicit_tag, actx, tree, tvb, hoffset, min_len, max_len, hf_id, out_tvb); +} + +int dissect_ber_restricted_string(bool implicit_tag, gint32 type, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **out_tvb) +{ + return dissect_ber_constrained_restricted_string(implicit_tag, type, actx, tree, tvb, offset, NO_BOUND, NO_BOUND, hf_id, out_tvb); +} + +int +dissect_ber_GeneralString(asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *name_string, guint name_len) +{ + tvbuff_t *out_tvb = NULL; + gint tvb_len; + + offset = dissect_ber_restricted_string(FALSE, BER_UNI_TAG_GeneralString, actx, tree, tvb, offset, hf_id, (name_string) ? &out_tvb : NULL); + + if (name_string) { + /* + * XXX - do we want to just get what's left in the tvbuff + * if the full length isn't available in the tvbuff, or + * do we want to throw an exception? + */ + if (out_tvb) { + tvb_len = tvb_reported_length(out_tvb); + if ((guint)tvb_len >= name_len) { + tvb_memcpy(out_tvb, (guint8*)name_string, 0, name_len-1); + name_string[name_len-1] = '\0'; + } else { + tvb_memcpy(out_tvb, (guint8*)name_string, 0, tvb_len); + name_string[tvb_len] = '\0'; + } + } + } + + return offset; +} + +/* 8.19 Encoding of a relative or absolute object identifier value. + */ +static int +dissect_ber_any_oid(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **value_tvb, bool is_absolute) +{ + gint8 ber_class; + bool pc; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + int eoffset; + int hoffset; + const char *str; + proto_item *cause; + const gchar *name; + header_field_info *hfi; + +#ifdef DEBUG_BER +{ +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb, offset) > 3) { +proto_tree_add_debug_text(tree, "OBJECT IDENTIFIER dissect_ber_any_oid(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n", name, implicit_tag, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "OBJECT IDENTIFIER dissect_ber_any_oid(%s) entered\n", name); +} +} +#endif + + if (!implicit_tag) { + hoffset = offset; + /* sanity check */ + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + eoffset = offset + len; + if ( (ber_class != BER_CLASS_UNI) + || (is_absolute && tag != BER_UNI_TAG_OID) + || (!is_absolute && tag != BER_UNI_TAG_RELATIVE_OID) ) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_object_identifier, + tvb, identifier_offset, identifier_len, + "BER Error: Object Identifier expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tfs_get_string(pc, &tfs_constructed_primitive), + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return eoffset; + } + } else { + len = tvb_reported_length_remaining(tvb, offset); + eoffset = offset+len; + } + + actx->created_item = NULL; + hfi = proto_registrar_get_nth(hf_id); + if ((is_absolute && hfi->type == FT_OID) || (!is_absolute && hfi->type == FT_REL_OID)) { + actx->created_item = proto_tree_add_item(tree, hf_id, tvb, offset, len, ENC_BIG_ENDIAN); + } else if (FT_IS_STRING(hfi->type)) { + str = oid_encoded2string(wmem_packet_scope(), tvb_get_ptr(tvb, offset, len), len); + actx->created_item = proto_tree_add_string(tree, hf_id, tvb, offset, len, str); + if (actx->created_item) { + /* see if we know the name of this oid */ + name = oid_resolved_from_encoded(wmem_packet_scope(), tvb_get_ptr(tvb, offset, len), len); + if (name) { + proto_item_append_text(actx->created_item, " (%s)", name); + } + } + } else { + DISSECTOR_ASSERT_NOT_REACHED(); + } + + if (value_tvb) + *value_tvb = ber_tvb_new_subset_length(tvb, offset, len); + + return eoffset; +} + +static int +dissect_ber_any_oid_str(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, const char **value_stringx, bool is_absolute) +{ + tvbuff_t *value_tvb = NULL; + guint length; + + offset = dissect_ber_any_oid(implicit_tag, actx, tree, tvb, offset, hf_id, (value_stringx) ? &value_tvb : NULL, is_absolute); + + if (value_stringx) { + if (value_tvb && (length = tvb_reported_length(value_tvb))) { + *value_stringx = oid_encoded2string(wmem_packet_scope(), tvb_get_ptr(value_tvb, 0, length), length); + } else { + *value_stringx = ""; + } + } + + return offset; +} + +/* 8.19 Encoding of a relative object identifier value. + */ +int +dissect_ber_relative_oid(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **value_tvb) +{ + return dissect_ber_any_oid(implicit_tag, actx, tree, tvb, offset, hf_id, value_tvb, FALSE); +} + +int +dissect_ber_relative_oid_str(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, const char **value_stringx) +{ + return dissect_ber_any_oid_str(implicit_tag, actx, tree, tvb, offset, hf_id, value_stringx, FALSE); +} + +/* 8.19 Encoding of an object identifier value. + */ +int +dissect_ber_object_identifier(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **value_tvb) +{ + return dissect_ber_any_oid(implicit_tag, actx, tree, tvb, offset, hf_id, value_tvb, TRUE); +} + +int +dissect_ber_object_identifier_str(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, const char **value_stringx) +{ + return dissect_ber_any_oid_str(implicit_tag, actx, tree, tvb, offset, hf_id, value_stringx, TRUE); +} + +#ifdef DEBUG_BER +#define DEBUG_BER_SQ_OF +#endif + +static int +dissect_ber_sq_of(bool implicit_tag, gint32 type, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, const ber_sequence_t *seq, gint hf_id, gint ett_id) { + gint8 classx; + bool pcx, ind = FALSE, ind_field; + gint32 tagx; + int identifier_offset; + int identifier_len; + guint32 lenx; + + proto_tree *tree = parent_tree; + proto_item *item = NULL; + proto_item *causex; + int cnt, hoffsetx, end_offset; + bool have_cnt; + header_field_info *hfi; + tvbuff_t *next_tvb; + +#ifdef DEBUG_BER_SQ_OF +{ +const char *name; +header_field_info *hfinfo; +if (hf_id >= 0) { +hfinfo = proto_registrar_get_nth(hf_id); +name = hfinfo->name; +} else { +name = "unnamed"; +} +if (tvb_reported_length_remaining(tvb,offset) > 3) { +proto_tree_add_debug_text(tree, "SQ OF dissect_ber_sq_of(%s) entered implicit_tag:%d offset:%d len:%d %02x:%02x:%02x\n", name, implicit_tag, offset, tvb_reported_length_remaining(tvb, offset), tvb_get_guint8(tvb, offset), tvb_get_guint8(tvb, offset+1), tvb_get_guint8(tvb, offset+2)); +} else { +proto_tree_add_debug_text(tree, "SQ OF dissect_ber_sq_of(%s) entered\n", name); +} +} +#endif + + if (!implicit_tag) { + hoffsetx = offset; + /* first we must read the sequence header */ + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &classx, &pcx, &tagx); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &lenx, &ind); + end_offset = offset + lenx; + + /* sanity check: we only handle Constructed Universal Sequences */ + if ((classx != BER_CLASS_APP) && (classx != BER_CLASS_PRI)) { + if (!pcx + || ((classx != BER_CLASS_UNI) || (tagx != type))) { + tvb_ensure_bytes_exist(tvb, hoffsetx, 2); + causex = proto_tree_add_expert_format( + tree, actx->pinfo, + (type == BER_UNI_TAG_SEQUENCE) ? &ei_ber_expected_set : &ei_ber_expected_sequence, + tvb, identifier_offset, identifier_len, + "BER Error: %s OF expected but class:%s(%d) %s tag:%d was unexpected", + (type == BER_UNI_TAG_SEQUENCE) ? "SET" : "SEQUENCE", + val_to_str_const(classx, ber_class_codes, "Unknown"), + classx, tfs_get_string(pcx, &tfs_constructed_primitive), + tagx); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(causex, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffsetx, unknown_tree); + } + return end_offset; + } + } + } else { + /* the tvb length should be correct now nope we could be coming from an implicit choice or sequence, thus we + read the items we match and return the length*/ + lenx = tvb_reported_length_remaining(tvb, offset); + end_offset = offset + lenx; + } + + /* count number of items */ + cnt = 0; + have_cnt = FALSE; + hoffsetx = offset; + /* only count the number of items IFF we have the full blob, + * else this will just generate a [short frame] before we even start + * dissecting a single item. + */ + /* XXX Do we really need to count them at all ? ronnie */ + if (tvb_captured_length_remaining(tvb, offset) == tvb_reported_length_remaining(tvb, offset)) { + have_cnt = TRUE; + while (offset < end_offset) { + guint32 len; + gint s_offset; + + s_offset = offset; + + /*if (ind) { this sequence of was of indefinite length, if this is implicit indefinite impossible maybe + but ber dissector uses this to eat the tag length then pass into here... EOC still on there...*/ + if ((tvb_get_guint8(tvb, offset) == 0) && (tvb_get_guint8(tvb, offset+1) == 0)) { + break; + } + /* } */ + + /* read header and len for next field */ + offset = get_ber_identifier(tvb, offset, NULL, NULL, NULL); + offset = get_ber_length(tvb, offset, &len, &ind); + /* best place to get real length of implicit sequence of or set of is here... */ + /* adjust end_offset if we find somthing that doesn't match */ + offset += len; + cnt++; + if (offset <= s_offset) { + /* Underflow - give up; this can happen with a very large + * length. + */ + have_cnt = FALSE; + cnt = 0; + break; + } + } + } + offset = hoffsetx; + + /* create subtree */ + if (hf_id >= 0) { + hfi = proto_registrar_get_nth(hf_id); + if (parent_tree) { + if (hfi->type == FT_NONE) { + item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, lenx, ENC_BIG_ENDIAN); + proto_item_append_text(item, ":"); + } else { + if (have_cnt) { + item = proto_tree_add_uint(parent_tree, hf_id, tvb, offset, lenx, cnt); + proto_item_append_text(item, (cnt == 1) ? " item" : " items"); + } else + item = proto_tree_add_uint_format_value(parent_tree, hf_id, tvb, offset, lenx, cnt, "unknown number of items"); + } + tree = proto_item_add_subtree(item, ett_id); + ber_check_items (cnt, min_len, max_len, actx, item); + } + } + + /* loop over all entries until we reach the end of the sequence */ + while (offset < end_offset) { + gint8 ber_class; + bool pc; + gint32 tag; + guint32 len; + int eoffset; + int hoffset; + proto_item *cause; + bool imp_tag; + + hoffset = offset; + /*if (ind) { this sequence was of indefinite length, if this is implicit indefinite impossible maybe + but ber dissector uses this to eat the tag length then pass into here... EOC still on there...*/ + if ((tvb_get_guint8(tvb, offset) == 0) && (tvb_get_guint8(tvb, offset+1) == 0)) { + if (show_internal_ber_fields) { + proto_tree_add_item(tree, hf_ber_seq_of_eoc, tvb, hoffset, end_offset-hoffset, ENC_NA); + } + return offset+2; + } + /*}*/ + /* read header and len for next field */ + identifier_offset = offset; + offset = get_ber_identifier(tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = get_ber_length(tvb, offset, &len, &ind_field); + eoffset = offset + len; + /* Make sure we move forward */ + if (eoffset <= hoffset) + THROW(ReportedBoundsError); + + if ((ber_class == BER_CLASS_UNI) && (tag == BER_UNI_TAG_EOC)) { + /* This is a zero length sequence of*/ + hoffset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + dissect_ber_length(actx->pinfo, tree, tvb, hoffset, NULL, NULL); + return eoffset; + } + /* verify that this one is the one we want */ + /* ahup if we are implicit then we return to the upper layer how much we have used */ + if (seq->ber_class != BER_CLASS_ANY) { + if ((seq->ber_class != ber_class) + || (seq->tag != tag) ) { + if (!(seq->flags & BER_FLAGS_NOTCHKTAG)) { + if ( seq->ber_class == BER_CLASS_UNI) { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_sequence_field_wrong, + tvb, identifier_offset, identifier_len, + "BER Error: Wrong field in SEQUENCE OF: expected class:%s(%d) tag:%d(%s) but found class:%s(%d) tag:%d", + val_to_str_const(seq->ber_class, ber_class_codes, "Unknown"), + seq->ber_class, + seq->tag, + val_to_str_ext_const(seq->tag, &ber_uni_tag_codes_ext, "Unknown"), + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, tag); + } else { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_sequence_field_wrong, + tvb, identifier_offset, identifier_len, + "BER Error: Wrong field in SEQUENCE OF: expected class:%s(%d) tag:%d but found class:%s(%d) tag:%d", + val_to_str_const(seq->ber_class, ber_class_codes, "Unknown"), + seq->ber_class, + seq->tag, + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tag); + } + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + offset = eoffset; + continue; + /* wrong.... */ + } + } + } + + if (!(seq->flags & BER_FLAGS_NOOWNTAG) && !(seq->flags & BER_FLAGS_IMPLTAG)) { + /* dissect header and len for field */ + hoffset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + hoffset = dissect_ber_length(actx->pinfo, tree, tvb, hoffset, NULL, NULL); + } + if ((seq->flags == BER_FLAGS_IMPLTAG) && (seq->ber_class == BER_CLASS_CON)) { + /* Constructed sequence of with a tag */ + /* dissect header and len for field */ + hoffset = dissect_ber_identifier(actx->pinfo, tree, tvb, hoffset, NULL, NULL, NULL); + hoffset = dissect_ber_length(actx->pinfo, tree, tvb, hoffset, NULL, NULL); + /* Function has IMPLICIT TAG */ + } + + next_tvb = ber_tvb_new_subset_length(tvb, hoffset, eoffset-hoffset); + + imp_tag = FALSE; + if (seq->flags == BER_FLAGS_IMPLTAG) + imp_tag = TRUE; + /* call the dissector for this field */ + seq->func(imp_tag, next_tvb, 0, actx, tree, *seq->p_id); + /* hold on if we are implicit and the result is zero, i.e. the item in the sequence of + doesn't match the next item, thus this implicit sequence is over, return the number of bytes + we have eaten to allow the possible upper sequence continue... */ + cnt++; /* rubbish*/ + offset = eoffset; + } + + /* if we didn't end up at exactly offset, then we ate too many bytes */ + if (offset != end_offset) { + tvb_ensure_bytes_exist(tvb, offset-2, 2); + proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_error_length, tvb, offset-2, 2, + "BER Error: %s OF contained %d too many bytes", + (type == BER_UNI_TAG_SEQUENCE) ? "SEQUENCE" : "SET", + offset - end_offset); + } + + return end_offset; +} + +int +dissect_ber_constrained_sequence_of(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, const ber_sequence_t *seq, gint hf_id, gint ett_id) { + return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SEQUENCE, actx, parent_tree, tvb, offset, min_len, max_len, seq, hf_id, ett_id); +} + +int +dissect_ber_sequence_of(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id) { + return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SEQUENCE, actx, parent_tree, tvb, offset, NO_BOUND, NO_BOUND, seq, hf_id, ett_id); +} + +int +dissect_ber_constrained_set_of(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, const ber_sequence_t *seq, gint hf_id, gint ett_id) { + return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SET, actx, parent_tree, tvb, offset, min_len, max_len, seq, hf_id, ett_id); +} + +int +dissect_ber_set_of(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_sequence_t *seq, gint hf_id, gint ett_id) { + return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SET, actx, parent_tree, tvb, offset, NO_BOUND, NO_BOUND, seq, hf_id, ett_id); +} + +int +dissect_ber_GeneralizedTime(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id) +{ + nstime_t ts; + const guint8 *tmpstr; + gint8 ber_class; + bool pc; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len; + int len_offset; + int len_len; + int end_offset; + int hoffset; + proto_item *cause; + + if (!implicit_tag) { + hoffset = offset; + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + len_offset = offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + len_len = offset - len_offset; + end_offset = offset+len; + + /* sanity check. we only handle universal/generalized time */ + if ( (ber_class != BER_CLASS_UNI) || (tag != BER_UNI_TAG_GeneralizedTime)) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_generalized_time, + tvb, identifier_offset, identifier_len, + "BER Error: GeneralizedTime expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tfs_get_string(pc, &tfs_constructed_primitive), + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return end_offset; + } + } else { + len = tvb_reported_length_remaining(tvb, offset); + len_offset = 0; + len_len = 0; + end_offset = offset+len; + } + + /* ASN.1 GeneralizedTime is a ISO 8601 Basic profile that omits the T + * between date and time. BER allows accuracy of hours, minutes, seconds, + * fractions of a second "to any degree of accuracy", and even + * fractional minutes or hours (see ITU-T X.680 46.2 and ITU-T X.690 8.25.) + * + * CER/DER (and PER) require that the seconds field be present (cf. + * ITU-T X.690 11.7 and ITU-T X.691 10.6.5), that the decimal point + * element be ".", that fractional seconds trailing zeros MUST be omitted, + * and that the decimal point shall also be omitted if the entire fractional + * second is 0 (in order to have a unique representation.) + * + * RFC 5280 says that X.509 certificate validity dates after 2050, which + * MUST use GeneralizedTime, MUST be expressed in Z and MUST include + * seconds but MUST NOT include fractional seconds. + * + * The minimum that iso8601_to_nstime() handles currently is + * YYYYMMDDhhmm = 12 digits + * and the maximimum is + * YYYYMMDDhhmmss.sssssssss+hhmm = 29 digits + * + * That doesn't handle everything that BER technically supports, but + * everything seen in practice. For the protocols that are more restrictive + * if someone really wants to validate and complain about e.g. fractional + * seconds in a X.509 certificate, that could be added to the conformance + * file. + */ + if ((len < 12) || (len > 29)) { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_error_length, + tvb, len_offset, len_len, + "BER Error: GeneralizedTime invalid length: %u", + len); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, offset, unknown_tree); + } + return end_offset; + } + + tmpstr = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, len, ENC_ASCII); + if (!iso8601_to_nstime(&ts, tmpstr, ISO8601_DATETIME_BASIC)) { + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_invalid_format_generalized_time, + tvb, offset, len, + "BER Error: GeneralizedTime invalid format: %s", + tmpstr); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, offset, unknown_tree); + } + return end_offset; + } + + if (hf_id >= 0) { + proto_tree_add_time(tree, hf_id, tvb, offset, len, &ts); + } + + offset+=len; + return offset; + +} + +/* datestrptr: if not NULL return datetime string instead of adding to tree or NULL when packet is malformed + * tvblen: if not NULL return consumed packet bytes + */ +int +dissect_ber_UTCTime(bool implicit_tag, asn1_ctx_t *actx, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char **datestrptr, guint32 *tvblen) +{ + char *outstr, *outstrptr; + const guint8 *instr; + gint8 ber_class; + bool pc; + gint32 tag; + int identifier_offset; + int identifier_len; + guint32 len, i, n; + int hoffset; + proto_item *cause; + proto_tree *error_tree; + const gchar *error_str = NULL; + + outstrptr = outstr = (char *)wmem_alloc(wmem_packet_scope(), 29); + + if (datestrptr) *datestrptr = NULL; /* mark invalid */ + if (tvblen) *tvblen = 0; + + if (!implicit_tag) { + hoffset = offset; + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, tree, tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, tree, tvb, offset, &len, NULL); + + /* sanity check: we only handle UTCTime */ + if ( (ber_class != BER_CLASS_UNI) || (tag != BER_UNI_TAG_UTCTime) ) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + tree, actx->pinfo, &ei_ber_expected_utc_time, + tvb, identifier_offset, identifier_len, + "BER Error: UTCTime expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, + tfs_get_string(pc, &tfs_constructed_primitive), + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return offset+len; + } + } else { + len = tvb_reported_length_remaining(tvb, offset); + } + + if ((len < 10) || (len > 19)) { + error_str = wmem_strdup_printf(wmem_packet_scope(), "BER Error: UTCTime invalid length: %u", len); + instr = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, len > 19 ? 19 : len, ENC_ASCII); + goto malformed; + } + + instr = tvb_get_string_enc(wmem_packet_scope(), tvb, offset, len, ENC_ASCII); + + /* YYMMDDhhmm */ + for (i=0; i<10; i++) { + if ((instr[i] < '0') || (instr[i] > '9')) { + error_str = "BER Error: malformed UTCTime encoding, " + "first 10 octets have to contain YYMMDDhhmm in digits"; + goto malformed; + } + } + snprintf(outstrptr, 15, "%.2s-%.2s-%.2s %.2s:%.2s", instr, instr+2, instr+4, instr+6, instr+8); + outstrptr+= 14; + + /* (ss)? */ + if (len >= 12) { + if ((instr[i] >= '0') && (instr[i] <= '9')) { + i++; + if ((instr[i] >= '0') && (instr[i] <= '9')) { + i++; + snprintf(outstrptr, 4, ":%.2s", instr+10); + outstrptr+=3; + } else { + error_str = "BER Error: malformed UTCTime encoding, " + "if 11th octet is a digit for seconds, " + "the 12th octet has to be a digit, too"; + goto malformed; + } + } + } + + /* Z|([+-]hhmm) */ + switch (instr[i]) { + case 'Z': + if (len != (i+1)) { + error_str = "BER Error: malformed UTCTime encoding, " + "there must be no further octets after \'Z\'"; + goto malformed; + } + snprintf(outstrptr, 7, " (UTC)"); + i++; + break; + case '-': + case '+': + if (len != (i+5)) { + error_str = "BER Error: malformed UTCTime encoding, " + "4 digits must follow on \'+\' resp. \'-\'"; + goto malformed; + } + for (n=i+1; n '9')) { + error_str = "BER Error: malformed UTCTime encoding, " + "4 digits must follow on \'+\' resp. \'-\'"; + goto malformed; + } + } + snprintf(outstrptr, 12, " (UTC%c%.4s)", instr[i], instr+i+1); + i+=5; + break; + default: + error_str = wmem_strdup_printf(wmem_packet_scope(), + "BER Error: malformed UTCTime encoding, " + "unexpected character in %dth octet, " + "must be \'Z\', \'+\' or \'-\'", i+1); + goto malformed; + break; + } + + if (len != i) { + error_str = wmem_strdup_printf(wmem_packet_scope(), + "BER Error: malformed UTCTime encoding, %d unexpected character%s after %dth octet", + len - i, + (len == (i - 1) ? "s" : ""), + i); + goto malformed; + } + + if (datestrptr) { + *datestrptr = outstr; /* mark as valid */ + } else { + if (hf_id >= 0) { + proto_tree_add_string(tree, hf_id, tvb, offset, len, outstr); + } + } + if (tvblen) *tvblen = len; + + return offset+len; +malformed: + if (hf_id >= 0) { + cause = proto_tree_add_string(tree, hf_id, tvb, offset, len, instr); + error_tree = proto_item_add_subtree(cause, ett_ber_unknown); + } else { + error_tree = tree; + } + + proto_tree_add_expert_format( + error_tree, actx->pinfo, &ei_ber_invalid_format_utctime, + tvb, offset, len, + "%s", + error_str); + + if (tvblen) *tvblen = len; + + return offset+len; +} + +/* 8.6 Encoding of a bitstring value */ + +int +dissect_ber_constrained_bitstring(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, gint32 min_len, gint32 max_len, int * const *named_bits, int num_named_bits, gint hf_id, gint ett_id, tvbuff_t **out_tvb) +{ + gint8 ber_class; + bool pc, ind; + gint32 tag; + int identifier_offset; + int identifier_len; + gint len; + guint8 pad = 0; + int end_offset; + int hoffset; + proto_item *item = NULL; + proto_item *cause; + proto_tree *tree = NULL; + /*const char *sep;*/ + /*const int **nb;*/ + + if (!implicit_tag) { + hoffset = offset; + /* read header and len for the octet string */ + identifier_offset = offset; + offset = dissect_ber_identifier(actx->pinfo, parent_tree, tvb, offset, &ber_class, &pc, &tag); + identifier_len = offset - identifier_offset; + offset = dissect_ber_length(actx->pinfo, parent_tree, tvb, offset, &len, &ind); + end_offset = offset + len; + + /* sanity check: we only handle Universal BitStrings */ + + /* for an IMPLICIT APPLICATION tag asn2wrs seems to call this + function with implicit_tag = FALSE. BER_FLAGS_NOOWNTAG was + set so the APPLICATION tag was still present. + So here we relax it for APPLICATION tags. CONTEXT tags may + still cause a problem. */ + + if (ber_class != BER_CLASS_APP) { + if ((ber_class != BER_CLASS_UNI) + || (tag != BER_UNI_TAG_BITSTRING)) { + tvb_ensure_bytes_exist(tvb, hoffset, 2); + cause = proto_tree_add_expert_format( + parent_tree, actx->pinfo, &ei_ber_expected_bitstring, + tvb, identifier_offset, identifier_len, + "BER Error: BitString expected but class:%s(%d) %s tag:%d was unexpected", + val_to_str_const(ber_class, ber_class_codes, "Unknown"), + ber_class, tfs_get_string(pc, &tfs_constructed_primitive), + tag); + if (decode_unexpected) { + proto_tree *unknown_tree = proto_item_add_subtree(cause, ett_ber_unknown); + dissect_unknown_ber(actx->pinfo, tvb, hoffset, unknown_tree); + } + return end_offset; + } + } + } else { + pc = 0; + len = tvb_reported_length_remaining(tvb, offset); + end_offset = offset + len; + } + if ((int)len <= 0) { + proto_tree_add_expert_format( + parent_tree, actx->pinfo, &ei_ber_constr_bitstr, tvb, offset, len, + "BER Error: dissect_ber_constrained_bitstring(): frame:%u offset:%d Was passed an illegal length of %d", + actx->pinfo->num, offset, len); + return offset; + } + actx->created_item = NULL; + + if (pc) { + /* constructed */ + /* TO DO */ + } else { + /* primitive */ + pad = tvb_get_guint8(tvb, offset); + /* 8.6.2.4 If a bitstring value has no 1 bits, then an encoder (as a sender's option) + * may encode the value with a length of 1 and with an initial octet set to 0 + * or may encode it as a bit string with one or more 0 bits following the initial octet. + */ + if ((pad == 0) && (len == 1)) { + /* empty */ + item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, len, ENC_BIG_ENDIAN); + actx->created_item = item; + proto_tree_add_item(parent_tree, hf_ber_bitstring_empty, tvb, offset, 1, ENC_BIG_ENDIAN); + if (out_tvb) { + *out_tvb = ber_tvb_new_subset_length(tvb, offset, len); + } + ber_check_length(8 * len - pad, min_len, max_len, actx, item, TRUE); + return end_offset; + } else { + /* padding */ + proto_item *pad_item = proto_tree_add_item(parent_tree, hf_ber_bitstring_padding, tvb, offset, 1, ENC_BIG_ENDIAN); + if (pad > 7) { + expert_add_info_format( + actx->pinfo, pad_item, &ei_ber_illegal_padding, + "Illegal padding (0 .. 7): %d", pad); + } + } + offset++; + len--; + if (hf_id >= 0) { + item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, len, ENC_NA); + actx->created_item = item; + if (named_bits) { + guint8 *bitstring = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, offset, len); + const int named_bits_bytelen = (num_named_bits + 7) / 8; + if (show_internal_ber_fields) { + if (len < named_bits_bytelen) { + guint zero_bits_omitted = num_named_bits - ((len * 8) - pad); + proto_item_append_text(item, " [%u zero bits not encoded, but displayed]", zero_bits_omitted); + } + } + if (ett_id != -1) { + tree = proto_item_add_subtree(item, ett_id); + } + for (int i = 0; i < named_bits_bytelen; i++) { + // Process 8 bits at a time instead of 64, each field masks a + // single byte. + const int bit_offset = 8 * i; + int* const* section_named_bits = named_bits + bit_offset; + int* flags[9]; + if (num_named_bits - bit_offset > 8) { + memcpy(&flags[0], named_bits + bit_offset, 8 * sizeof(int*)); + flags[8] = NULL; + section_named_bits = (int* const*)flags; + } + + // If less data is available than the number of named bits, then + // the trailing (right) bits are assumed to be 0. + guint64 value = 0; + if (i < len) { + value = bitstring[i]; + if (num_named_bits - bit_offset > 7) { + bitstring[i] = 0; + } else { + bitstring[i] &= 0xff >> (num_named_bits - bit_offset); + } + } + + // TODO should non-zero pad bits be masked from the value? + // When trailing zeroes are not present in the data, mark the + // last byte for the lack of a better alternative. + proto_tree_add_bitmask_list_value(tree, tvb, offset + MIN(i, len - 1), 1, section_named_bits, value); + } + // If more data is available than the number of named bits, then + // either the spec was updated or the packet is malformed. + for (int i = 0; i < len; i++) { + if (bitstring[i]) { + expert_add_info_format(actx->pinfo, item, &ei_ber_bits_unknown, "Unknown bit(s): 0x%s", + bytes_to_str(wmem_packet_scope(), bitstring, len)); + break; + } + } + } + } + if (out_tvb) { + *out_tvb = ber_tvb_new_subset_length(tvb, offset, len); + } + } + + + if ((pad > 0) && (pad < 8) && (len > 0)) { + guint8 bits_in_pad = tvb_get_guint8(tvb, offset + len - 1) & (0xFF >> (8 - pad)); + if (bits_in_pad) { + expert_add_info_format( + actx->pinfo, item, &ei_ber_bits_set_padded, + "Bits set in padded area: 0x%02x", bits_in_pad); + } + } + + ber_check_length(8 * len - pad, min_len, max_len, actx, item, TRUE); + + return end_offset; +} + + +int +dissect_ber_bitstring(bool implicit_tag, asn1_ctx_t *actx, proto_tree *parent_tree, tvbuff_t *tvb, int offset, int * const *named_bits, gint num_named_bits, gint hf_id, gint ett_id, tvbuff_t **out_tvb) +{ + return dissect_ber_constrained_bitstring(implicit_tag, actx, parent_tree, tvb, offset, -1, -1, named_bits, num_named_bits, hf_id, ett_id, out_tvb); +} + +/* + * 8.18 Encoding of a value of the external type + * 8.18.1 The encoding of a value of the external type shall be the BER encoding of the following + * sequence type, assumed to be defined in an environment of EXPLICIT TAGS, + * with a value as specified in the subclauses below: + * + * [UNIVERSAL 8] IMPLICIT SEQUENCE { + * direct-reference OBJECT IDENTIFIER OPTIONAL, + * indirect-reference INTEGER OPTIONAL, + * data-value-descriptor ObjectDescriptor OPTIONAL, + * encoding CHOICE { + * single-ASN1-type [0] ABSTRACT-SYNTAX.&Type, + * octet-aligned [1] IMPLICIT OCTET STRING, + * arbitrary [2] IMPLICIT BIT STRING } } + * + */ + +static int +dissect_ber_INTEGER(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) { + offset = dissect_ber_integer(implicit_tag, actx, tree, tvb, offset, hf_index, + &actx->external.indirect_reference); + actx->external.indirect_ref_present = TRUE; + + return offset; +} + +static int +dissect_ber_T_octet_aligned(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) +{ + if (actx->external.u.ber.ber_callback) { + offset = actx->external.u.ber.ber_callback(FALSE, tvb, offset, actx, tree, hf_index); + } else if (actx->external.direct_ref_present && + dissector_get_string_handle(ber_oid_dissector_table, actx->external.direct_reference)) { + offset = call_ber_oid_callback(actx->external.direct_reference, tvb, offset, actx->pinfo, tree, NULL); + } else { + offset = dissect_ber_octet_string(implicit_tag, actx, tree, tvb, offset, hf_index, &actx->external.octet_aligned); + } + + return offset; +} +static int +dissect_ber_OBJECT_IDENTIFIER(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) +{ + DISSECTOR_ASSERT(actx); + offset = dissect_ber_object_identifier_str(implicit_tag, actx, tree, tvb, offset, hf_index, &actx->external.direct_reference); + actx->external.direct_ref_present = TRUE; + + return offset; +} + +static int +dissect_ber_ObjectDescriptor(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) +{ + DISSECTOR_ASSERT(actx); + offset = dissect_ber_restricted_string(implicit_tag, BER_UNI_TAG_ObjectDescriptor, + actx, tree, tvb, offset, hf_index, + &actx->external.data_value_descriptor); + + return offset; +} + +static int +dissect_ber_T_single_ASN1_type(bool implicit_tag _U_, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) +{ + if (actx->external.u.ber.ber_callback) { + offset = actx->external.u.ber.ber_callback(FALSE, tvb, offset, actx, tree, hf_index); + } else { + offset = call_ber_oid_callback(actx->external.direct_reference, tvb, offset, actx->pinfo, tree, NULL); + } + + return offset; +} + +static int +dissect_ber_T_arbitrary(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) +{ + if (actx->external.u.ber.ber_callback) { + offset = actx->external.u.ber.ber_callback(FALSE, tvb, offset, actx, tree, hf_index); + } else { + offset = dissect_ber_bitstring(implicit_tag, actx, tree, tvb, offset, + NULL, 0, hf_index, -1, &actx->external.arbitrary); + } + + return offset; +} + +static const value_string ber_T_encoding_vals[] = { + { 0, "single-ASN1-type" }, + { 1, "octet-aligned" }, + { 2, "arbitrary" }, + { 0, NULL } +}; + +static const ber_choice_t T_encoding_choice[] = { + { 0, &hf_ber_single_ASN1_type, BER_CLASS_CON, 0, 0, dissect_ber_T_single_ASN1_type }, + { 1, &hf_ber_octet_aligned , BER_CLASS_CON, 1, BER_FLAGS_IMPLTAG, dissect_ber_T_octet_aligned }, + { 2, &hf_ber_arbitrary , BER_CLASS_CON, 2, BER_FLAGS_IMPLTAG, dissect_ber_T_arbitrary }, + { 0, NULL, 0, 0, 0, NULL } +}; + + +static int +dissect_ber_T_encoding(bool implicit_tag _U_, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, proto_tree *tree, int hf_index) { + // This assertion is used to remove clang's warning. + DISSECTOR_ASSERT(actx); + offset = dissect_ber_choice(actx, tree, tvb, offset, + T_encoding_choice, hf_index, ett_ber_T_encoding, + &actx->external.encoding); + + return offset; +} + + +static const ber_sequence_t external_U_sequence[] = { + { &hf_ber_direct_reference, BER_CLASS_UNI, BER_UNI_TAG_OID, BER_FLAGS_OPTIONAL|BER_FLAGS_NOOWNTAG, dissect_ber_OBJECT_IDENTIFIER }, + { &hf_ber_indirect_reference, BER_CLASS_UNI, BER_UNI_TAG_INTEGER, BER_FLAGS_OPTIONAL|BER_FLAGS_NOOWNTAG, dissect_ber_INTEGER }, + { &hf_ber_data_value_descriptor, BER_CLASS_UNI, BER_UNI_TAG_ObjectDescriptor, BER_FLAGS_OPTIONAL|BER_FLAGS_NOOWNTAG, dissect_ber_ObjectDescriptor }, + { &hf_ber_encoding, BER_CLASS_ANY/*choice*/, -1/*choice*/, BER_FLAGS_NOOWNTAG|BER_FLAGS_NOTCHKTAG, dissect_ber_T_encoding }, + { NULL, 0, 0, 0, NULL } +}; +static int +dissect_ber_external_U(bool implicit_tag, tvbuff_t *tvb, int offset, asn1_ctx_t *actx , proto_tree *tree, int hf_index) +{ + offset = dissect_ber_sequence(implicit_tag, actx, tree, tvb, offset, + external_U_sequence, hf_index, ett_ber_EXTERNAL); + + return offset; +} + +int +dissect_ber_external_type(bool implicit_tag, proto_tree *tree, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, gint hf_id, ber_callback func) { + + actx->external.u.ber.ber_callback = func; + + offset = dissect_ber_tagged_type(implicit_tag, actx, tree, tvb, offset, + hf_id, BER_CLASS_UNI, BER_UNI_TAG_EXTERNAL, TRUE, dissect_ber_external_U); + + asn1_ctx_clean_external(actx); + + return offset; +} +/* Experimental */ +int +dissect_ber_EmbeddedPDV_Type(bool implicit_tag, proto_tree *tree, tvbuff_t *tvb, int offset, asn1_ctx_t *actx, gint hf_id, ber_callback func _U_) { + + + offset = dissect_ber_tagged_type(implicit_tag, actx, tree, tvb, offset, + hf_id, BER_CLASS_UNI, BER_UNI_TAG_EMBEDDED_PDV, TRUE, dissect_ber_external_U); + + return offset; +} + +static int +dissect_ber_syntax(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void* data _U_) +{ + return dissect_unknown_ber(pinfo, tvb, 0, tree); +} + +static int +dissect_ber_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, const char *syntax) +{ + const char *name; + int offset; + + col_set_str(pinfo->cinfo, COL_PROTOCOL, "BER"); + + col_set_str(pinfo->cinfo, COL_DEF_SRC, "BER encoded value"); + + if (!syntax) { + + /* if we got here we couldn't find anything better */ + col_set_str(pinfo->cinfo, COL_INFO, "Unknown BER"); + + offset = dissect_unknown_ber(pinfo, tvb, 0, tree); + + } else { + + offset = call_ber_syntax_callback(syntax, tvb, 0, pinfo, tree); + + /* see if we have a better name */ + name = get_ber_oid_syntax(syntax); + col_add_fstr(pinfo->cinfo, COL_INFO, "Decoded as %s", name ? name : syntax); + } + + return offset; +} + +static int +dissect_ber(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + return dissect_ber_common(tvb, pinfo, tree, decode_as_syntax); +} + +static int +dissect_ber_file(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) +{ + struct ber_phdr *ber = (struct ber_phdr *)data; + const char *ptr; + const char *file_syntax = NULL; + + if ((ptr = strrchr(ber->pathname, '.')) != NULL) + file_syntax = get_ber_oid_syntax(ptr); + return dissect_ber_common(tvb, pinfo, tree, file_syntax); +} + +bool +oid_has_dissector(const char *oid) { + return(dissector_get_string_handle(ber_oid_dissector_table, oid) != NULL); +} + +static void +ber_shutdown(void) +{ + g_hash_table_destroy(syntax_table); +} + +void +proto_register_ber(void) +{ + static hf_register_info hf[] = { + { &hf_ber_id_class, { + "Class", "ber.id.class", FT_UINT8, BASE_DEC, + VALS(ber_class_codes), 0xc0, "Class of BER TLV Identifier", HFILL }}, + { &hf_ber_bitstring_padding, { + "Padding", "ber.bitstring.padding", FT_UINT8, BASE_DEC, + NULL, 0x0, "Number of unused bits in the last octet of the bitstring", HFILL }}, + { &hf_ber_bitstring_empty, { + "Empty", "ber.bitstring.empty", FT_UINT8, BASE_DEC, + NULL, 0x0, "This is an empty bitstring", HFILL }}, + { &hf_ber_id_pc, { + "P/C", "ber.id.pc", FT_BOOLEAN, 8, + TFS(&ber_pc_codes), 0x20, "Primitive or Constructed BER encoding", HFILL }}, + { &hf_ber_id_uni_tag, { + "Tag", "ber.id.uni_tag", FT_UINT8, BASE_DEC|BASE_EXT_STRING, + &ber_uni_tag_codes_ext, 0x1f, "Universal tag type", HFILL }}, + { &hf_ber_id_uni_tag_ext, { + "Tag", "ber.id.uni_tag", FT_UINT32, BASE_DEC, + NULL, 0, "Universal tag type", HFILL }}, + { &hf_ber_id_tag, { + "Tag", "ber.id.tag", FT_UINT8, BASE_DEC, + NULL, 0x1f, "Tag value for non-Universal classes", HFILL }}, + { &hf_ber_id_tag_ext, { + "Tag", "ber.id.tag", FT_UINT32, BASE_DEC, + NULL, 0, "Tag value for non-Universal classes", HFILL }}, + { &hf_ber_length_octets, { + "Length Octets", "ber.length_octets", FT_UINT8, BASE_DEC, + NULL, 0, "Number of length octets", HFILL }}, + { &hf_ber_length, { + "Length", "ber.length", FT_UINT32, BASE_DEC, + NULL, 0, "Length of contents", HFILL }}, + { &hf_ber_unknown_OCTETSTRING, { + "OCTETSTRING", "ber.unknown.OCTETSTRING", FT_BYTES, BASE_NONE, + NULL, 0, "This is an unknown OCTETSTRING", HFILL }}, + { &hf_ber_unknown_BER_OCTETSTRING, { + "OCTETSTRING [BER encoded]", "ber.unknown.OCTETSTRING", FT_NONE, BASE_NONE, + NULL, 0, "This is an BER encoded OCTETSTRING", HFILL }}, + { &hf_ber_unknown_BER_primitive, { + "Primitive [BER encoded]", "ber.unknown.primitive", FT_NONE, BASE_NONE, + NULL, 0, "This is a BER encoded Primitive", HFILL }}, + { &hf_ber_unknown_OID, { + "OID", "ber.unknown.OID", FT_OID, BASE_NONE, + NULL, 0, "This is an unknown Object Identifier", HFILL }}, + { &hf_ber_unknown_relative_OID, { + "OID", "ber.unknown.relative_OID", FT_REL_OID, BASE_NONE, + NULL, 0, "This is an unknown relative Object Identifier", HFILL }}, + { &hf_ber_unknown_GraphicString, { + "GRAPHICSTRING", "ber.unknown.GRAPHICSTRING", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown GRAPHICSTRING", HFILL }}, + { &hf_ber_unknown_NumericString, { + "NumericString", "ber.unknown.NumericString", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown NumericString", HFILL }}, + { &hf_ber_unknown_PrintableString, { + "PrintableString", "ber.unknown.PrintableString", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown PrintableString", HFILL }}, + { &hf_ber_unknown_TeletexString, { + "TeletexString", "ber.unknown.TeletexString", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown TeletexString", HFILL }}, + { &hf_ber_unknown_VisibleString, { + "VisibleString", "ber.unknown.VisibleString", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown VisibleString", HFILL }}, + { &hf_ber_unknown_GeneralString, { + "GeneralString", "ber.unknown.GeneralString", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown GeneralString", HFILL }}, + { &hf_ber_unknown_UniversalString, { + "UniversalString", "ber.unknown.UniversalString", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown UniversalString", HFILL }}, + { &hf_ber_unknown_BMPString, { + "BMPString", "ber.unknown.BMPString", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown BMPString", HFILL }}, + { &hf_ber_unknown_IA5String, { + "IA5String", "ber.unknown.IA5String", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown IA5String", HFILL }}, + { &hf_ber_unknown_UTCTime, { + "UTCTime", "ber.unknown.UTCTime", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown UTCTime", HFILL }}, + { &hf_ber_unknown_UTF8String, { + "UTF8String", "ber.unknown.UTF8String", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown UTF8String", HFILL }}, + { &hf_ber_unknown_GeneralizedTime, { + "GeneralizedTime", "ber.unknown.GeneralizedTime", FT_STRING, BASE_NONE, + NULL, 0, "This is an unknown GeneralizedTime", HFILL }}, + { &hf_ber_unknown_INTEGER, { + "INTEGER", "ber.unknown.INTEGER", FT_INT64, BASE_DEC, + NULL, 0, "This is an unknown INTEGER", HFILL }}, + { &hf_ber_unknown_REAL, { + "REAL", "ber.unknown.REAL", FT_DOUBLE, BASE_NONE, + NULL, 0, "This is an unknown REAL", HFILL }}, + { &hf_ber_unknown_BITSTRING, { + "BITSTRING", "ber.unknown.BITSTRING", FT_BYTES, BASE_NONE, + NULL, 0, "This is an unknown BITSTRING", HFILL }}, + { &hf_ber_unknown_BOOLEAN, { + "BOOLEAN", "ber.unknown.BOOLEAN", FT_UINT8, BASE_HEX, + NULL, 0, "This is an unknown BOOLEAN", HFILL }}, + { &hf_ber_unknown_ENUMERATED, { + "ENUMERATED", "ber.unknown.ENUMERATED", FT_UINT32, BASE_DEC, + NULL, 0, "This is an unknown ENUMERATED", HFILL }}, + { &hf_ber_direct_reference, + { "direct-reference", "ber.direct_reference", + FT_OID, BASE_NONE, NULL, 0, + "ber.OBJECT_IDENTIFIER", HFILL }}, + { &hf_ber_indirect_reference, + { "indirect-reference", "ber.indirect_reference", + FT_INT32, BASE_DEC, NULL, 0, + "ber.INTEGER", HFILL }}, + { &hf_ber_data_value_descriptor, + { "data-value-descriptor", "ber.data_value_descriptor", + FT_STRING, BASE_NONE, NULL, 0, + "ber.ObjectDescriptor", HFILL }}, + { &hf_ber_encoding, + { "encoding", "ber.encoding", + FT_UINT32, BASE_DEC, VALS(ber_T_encoding_vals), 0, + "ber.T_encoding", HFILL }}, + { &hf_ber_octet_aligned, + { "octet-aligned", "ber.octet_aligned", + FT_BYTES, BASE_NONE, NULL, 0, + "ber.T_octet_aligned", HFILL }}, + { &hf_ber_arbitrary, + { "arbitrary", "ber.arbitrary", + FT_BYTES, BASE_NONE, NULL, 0, + "ber.T_arbitrary", HFILL }}, + { &hf_ber_single_ASN1_type, + { "single-ASN1-type", "ber.single_ASN1_type", + FT_NONE, BASE_NONE, NULL, 0, + "ber.T_single_ASN1_type", HFILL }}, + { &hf_ber_extra_data, + { "Extra data", "ber.extra_data", + FT_BYTES, BASE_NONE, NULL, 0, + NULL, HFILL }}, + + /* Fragment entries */ + { &hf_ber_fragments, + { "OCTET STRING fragments", "ber.octet_string.fragments", FT_NONE, BASE_NONE, + NULL, 0x00, NULL, HFILL } }, + { &hf_ber_fragment, + { "OCTET STRING fragment", "ber.octet_string.fragment", FT_FRAMENUM, BASE_NONE, + NULL, 0x00, NULL, HFILL } }, + { &hf_ber_fragment_overlap, + { "OCTET STRING fragment overlap", "ber.octet_string.fragment.overlap", FT_BOOLEAN, + BASE_NONE, NULL, 0x0, NULL, HFILL } }, + { &hf_ber_fragment_overlap_conflicts, + { "OCTET STRING fragment overlapping with conflicting data", + "ber.octet_string.fragment.overlap.conflicts", FT_BOOLEAN, BASE_NONE, NULL, + 0x0, NULL, HFILL } }, + { &hf_ber_fragment_multiple_tails, + { "OCTET STRING has multiple tail fragments", + "ber.octet_string.fragment.multiple_tails", FT_BOOLEAN, BASE_NONE, + NULL, 0x0, NULL, HFILL } }, + { &hf_ber_fragment_too_long_fragment, + { "OCTET STRING fragment too long", "ber.octet_string.fragment.too_long_fragment", + FT_BOOLEAN, BASE_NONE, NULL, 0x0, NULL, + HFILL } }, + { &hf_ber_fragment_error, + { "OCTET STRING defragmentation error", "ber.octet_string.fragment.error", FT_FRAMENUM, + BASE_NONE, NULL, 0x00, NULL, HFILL } }, + { &hf_ber_fragment_count, + { "OCTET STRING fragment count", "ber.octet_string.fragment.count", FT_UINT32, BASE_DEC, + NULL, 0x00, NULL, HFILL } }, + { &hf_ber_reassembled_in, + { "Reassembled in", "ber.octet_string.reassembled.in", FT_FRAMENUM, BASE_NONE, + NULL, 0x00, NULL, HFILL } }, + { &hf_ber_reassembled_length, + { "Reassembled OCTET STRING length", "ber.octet_string.reassembled.length", FT_UINT32, BASE_DEC, + NULL, 0x00, NULL, HFILL } }, + + /* Generated from convert_proto_tree_add_text.pl */ + { &hf_ber_null_tag, { "NULL tag", "ber.null_tag", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_unknown_data, { "Unknown Data", "ber.unknown_data", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_unknown_octetstring, { "Unknown OctetString", "ber.unknown_octetstring", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_seq_field_eoc, { "SEQ FIELD EOC", "ber.seq_field_eoc", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_seq_eoc, { "SEQ EOC", "ber.seq_eoc", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_set_field_eoc, { "SET FIELD EOC", "ber.set_field_eoc", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_set_eoc, { "SET EOC", "ber.set_eoc", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_choice_eoc, { "CHOICE EOC", "ber.choice_eoc", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_seq_of_eoc, { "SEQ OF EOC", "ber.seq_of_eoc", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_64bit_uint_as_bytes, { "64bits unsigned integer", "ber.64bit_uint_as_bytes", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + { &hf_ber_encoding_boiler_plate, { "BER encoded protocol, to see BER internal fields set protocol BER preferences", "ber.encoding_boiler_plate", FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, + }; + + static gint *ett[] = { + &ett_ber_octet_string, + &ett_ber_reassembled_octet_string, + &ett_ber_primitive, + &ett_ber_unknown, + &ett_ber_SEQUENCE, + &ett_ber_EXTERNAL, + &ett_ber_T_encoding, + &ett_ber_fragment, + &ett_ber_fragments + }; + static ei_register_info ei[] = { + { &ei_ber_size_constraint_string, { "ber.size_constraint.string", PI_PROTOCOL, PI_WARN, "Size constraint: string", EXPFILL }}, + { &ei_ber_size_constraint_value, { "ber.size_constraint.value", PI_PROTOCOL, PI_WARN, "Size constraint: values", EXPFILL }}, + { &ei_ber_size_constraint_items, { "ber.size_constraint.items", PI_PROTOCOL, PI_WARN, "Size constraint: items", EXPFILL }}, + { &ei_ber_sequence_field_wrong, { "ber.error.sequence.field_wrong", PI_MALFORMED, PI_WARN, "BER Error: Wrong field in SEQUENCE", EXPFILL }}, + { &ei_ber_expected_octet_string, { "ber.error.expected.octet_string", PI_MALFORMED, PI_WARN, "BER Error: OctetString expected", EXPFILL }}, + { &ei_ber_expected_null, { "ber.error.expected.null", PI_MALFORMED, PI_WARN, "BER Error: NULL expected", EXPFILL }}, + { &ei_ber_expected_null_zero_length, { "ber.error.expected.null_zero_length", PI_MALFORMED, PI_WARN, "BER Error: NULL type expects zero length data", EXPFILL }}, + { &ei_ber_expected_sequence, { "ber.error.expected.sequence", PI_MALFORMED, PI_WARN, "BER Error: Sequence expected", EXPFILL }}, + { &ei_ber_expected_set, { "ber.error.expected.set", PI_MALFORMED, PI_WARN, "BER Error: SET expected", EXPFILL }}, + { &ei_ber_expected_string, { "ber.error.expected.string", PI_MALFORMED, PI_WARN, "BER Error: String expected", EXPFILL }}, + { &ei_ber_expected_object_identifier, { "ber.error.expected.object_identifier", PI_MALFORMED, PI_WARN, "BER Error: Object Identifier expected", EXPFILL }}, + { &ei_ber_expected_generalized_time, { "ber.error.expected.generalized_time", PI_MALFORMED, PI_WARN, "BER Error: GeneralizedTime expected", EXPFILL }}, + { &ei_ber_expected_utc_time, { "ber.error.expected.utc_time", PI_MALFORMED, PI_WARN, "BER Error: UTCTime expected", EXPFILL }}, + { &ei_ber_expected_bitstring, { "ber.error.expected.bitstring", PI_MALFORMED, PI_WARN, "BER Error: BitString expected", EXPFILL }}, + { &ei_ber_error_length, { "ber.error.length", PI_MALFORMED, PI_WARN, "BER Error: length is not valid", EXPFILL }}, + { &ei_ber_wrong_tag_in_tagged_type, { "ber.error.wrong_tag_in_tagged_type", PI_MALFORMED, PI_WARN, "BER Error: Wrong tag in tagged type", EXPFILL }}, + { &ei_ber_universal_tag_unknown, { "ber.error.universal_tag_unknown", PI_MALFORMED, PI_WARN, "BER Error: can not handle universal", EXPFILL }}, + { &ei_ber_no_oid, { "ber.error.no_oid", PI_MALFORMED, PI_WARN, "BER Error: No OID supplied to call_ber_oid_callback", EXPFILL }}, + { &ei_ber_oid_not_implemented, { "ber.error.oid_not_implemented", PI_UNDECODED, PI_WARN, "BER: Dissector for OID not implemented. Contact Wireshark developers if you want this supported", EXPFILL }}, + { &ei_ber_syntax_not_implemented, { "ber.error.syntax_not_implemented", PI_UNDECODED, PI_WARN, "BER: Dissector for syntax not implemented", EXPFILL }}, + { &ei_ber_value_too_many_bytes, { "ber.error.value_too_many_bytes", PI_MALFORMED, PI_WARN, "Value is encoded with too many bytes", EXPFILL }}, + { &ei_ber_unknown_field_sequence, { "ber.error.unknown_field.sequence", PI_MALFORMED, PI_WARN, "BER Error: Unknown field in Sequence", EXPFILL }}, + { &ei_ber_unknown_field_set, { "ber.error.unknown_field.set", PI_MALFORMED, PI_WARN, "BER Error: Unknown field in SET", EXPFILL }}, + { &ei_ber_missing_field_set, { "ber.error.missing_field.set", PI_MALFORMED, PI_WARN, "BER Error: Missing field in SET", EXPFILL }}, + { &ei_ber_empty_choice, { "ber.error.empty_choice", PI_MALFORMED, PI_WARN, "BER Error: Empty choice was found", EXPFILL }}, + { &ei_ber_choice_not_found, { "ber.error.choice_not_found", PI_MALFORMED, PI_WARN, "BER Error: This choice field was not found", EXPFILL }}, + { &ei_ber_bits_unknown, { "ber.error.bits_unknown", PI_UNDECODED, PI_WARN, "BER Error: Bits unknown", EXPFILL }}, + { &ei_ber_bits_set_padded, { "ber.error.bits_set_padded", PI_UNDECODED, PI_WARN, "BER Error: Bits set in padded area", EXPFILL }}, + { &ei_ber_illegal_padding, { "ber.error.illegal_padding", PI_UNDECODED, PI_WARN, "Illegal padding", EXPFILL }}, + { &ei_ber_invalid_format_generalized_time, { "ber.error.invalid_format.generalized_time", PI_MALFORMED, PI_WARN, "BER Error: GeneralizedTime invalid format", EXPFILL }}, + { &ei_ber_invalid_format_utctime, { "ber.error.invalid_format.utctime", PI_MALFORMED, PI_WARN, "BER Error: malformed UTCTime encoding", EXPFILL }}, + { &ei_hf_field_not_integer_type, { "ber.error.hf_field_not_integer_type", PI_PROTOCOL, PI_ERROR, "Was passed a HF field that was not integer type", EXPFILL }}, + { &ei_ber_constr_bitstr,{ "ber.error.constr_bitstr.len", PI_MALFORMED, PI_WARN, "BER Error: malformed Bitstring encoding", EXPFILL } }, + { &ei_ber_real_not_primitive,{ "ber.error.not_primitive.real", PI_MALFORMED, PI_WARN, "BER Error: REAL class not encoded as primitive", EXPFILL } }, + }; + + /* Decode As handling */ + static build_valid_func ber_da_build_value[1] = {ber_value}; + static decode_as_value_t ber_da_values = {ber_prompt, 1, ber_da_build_value}; + static decode_as_t ber_da = {"ber", "ber.syntax", 1, 0, &ber_da_values, NULL, NULL, + ber_populate_list, ber_decode_as_reset, ber_decode_as_change, NULL}; + + module_t *ber_module; + expert_module_t* expert_ber; + uat_t* users_uat = uat_new("OID Tables", + sizeof(oid_user_t), + "oid", + FALSE, + &oid_users, + &num_oid_users, + UAT_AFFECTS_DISSECTION, /* affects dissection of packets, but not set of named fields */ + "ChObjectIdentifiers", + oid_copy_cb, + NULL, + oid_free_cb, + ber_update_oids, + NULL, + users_flds); + + proto_ber = proto_register_protocol("Basic Encoding Rules (ASN.1 X.690)", "BER", "ber"); + + ber_handle = register_dissector("ber", dissect_ber, proto_ber); + ber_file_handle = register_dissector("ber_file", dissect_ber_file, proto_ber); + + proto_register_field_array(proto_ber, hf, array_length(hf)); + proto_register_subtree_array(ett, array_length(ett)); + expert_ber = expert_register_protocol(proto_ber); + expert_register_field_array(expert_ber, ei, array_length(ei)); + + proto_set_cant_toggle(proto_ber); + + /* Register preferences */ + ber_module = prefs_register_protocol(proto_ber, NULL); + + prefs_register_bool_preference(ber_module, "show_internals", + "Show internal BER encapsulation tokens", + "Whether the dissector should also display internal" + " ASN.1 BER details such as Identifier and Length fields", &show_internal_ber_fields); + prefs_register_bool_preference(ber_module, "decode_unexpected", + "Decode unexpected tags as BER encoded data", + "Whether the dissector should decode unexpected tags as" + " ASN.1 BER encoded data", &decode_unexpected); + prefs_register_bool_preference(ber_module, "decode_octetstring", + "Decode OCTET STRING as BER encoded data", + "Whether the dissector should try decoding OCTET STRINGs as" + " constructed ASN.1 BER encoded data", &decode_octetstring_as_ber); + + prefs_register_bool_preference(ber_module, "decode_primitive", + "Decode Primitive as BER encoded data", + "Whether the dissector should try decoding unknown primitive as" + " constructed ASN.1 BER encoded data", &decode_primitive_as_ber); + + prefs_register_bool_preference(ber_module, "warn_too_many_bytes", + "Warn if too many leading zero bits in encoded data", + "Whether the dissector should warn if excessive leading zero (0) bits", + &decode_warning_leading_zero_bits); + + prefs_register_uat_preference(ber_module, "oid_table", "Object Identifiers", + "A table that provides names for object identifiers" + " and the syntax of any associated values", + users_uat); + + ber_oid_dissector_table = register_dissector_table("ber.oid", "BER OID", proto_ber, FT_STRING, STRING_CASE_SENSITIVE); + ber_syntax_dissector_table = register_dissector_table("ber.syntax", "BER syntax", proto_ber, FT_STRING, STRING_CASE_SENSITIVE); + syntax_table = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); /* oid to syntax */ + + register_ber_syntax_dissector("ASN.1", proto_ber, dissect_ber_syntax); + + reassembly_table_register(&octet_segment_reassembly_table, + &addresses_reassembly_table_functions); + + register_shutdown_routine(ber_shutdown); + + register_decode_as(&ber_da); +} + +void +proto_reg_handoff_ber(void) +{ + guint i = 1; + + oid_add_from_string("asn1", "2.1"); + oid_add_from_string("basic-encoding", "2.1.1"); + + ber_decode_as_foreach(ber_add_syntax_name, &i); + + if (i > 1) + qsort(&syntax_names[1], i - 1, sizeof(value_string), cmp_value_string); + syntax_names[i].value = 0; + syntax_names[i].strptr = NULL; + + /* allow the dissection of BER/DER carried over a TCP/UDP transport + by using "Decode As..." */ + dissector_add_for_decode_as_with_preference("tcp.port", ber_handle); + dissector_add_for_decode_as_with_preference("udp.port", ber_handle); + + ber_update_oids(); + + dissector_add_uint("wtap_encap", WTAP_ENCAP_BER, ber_file_handle); +} + +/* + * Editor modelines - https://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ -- cgit v1.2.3