/* packet-ber.c * Helpers for ASN.1/BER dissection * Ronnie Sahlberg (C) 2004 * * $Id$ * * Ethereal - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * 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) * */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include "prefs.h" #include "packet-ber.h" 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_tag = -1; static gint hf_ber_length = -1; static gint hf_ber_bitstring_padding = -1; static gint ett_ber_octet_string = -1; static gboolean show_internal_ber_fields = FALSE; proto_item *ber_last_created_item=NULL; static const value_string ber_class_codes[] = { { BER_CLASS_UNI, "Universal" }, { BER_CLASS_APP, "Application" }, { BER_CLASS_CON, "Context Specific" }, { 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_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" }, { BER_UNI_TAG_SEQUENCE , "SEQUENCE, SEQUENCE OF" }, { BER_UNI_TAG_SET , "SET, SET OF" }, { BER_UNI_TAG_NumericString , "NumericString" }, { BER_UNI_TAG_PrintableString , "PrintableString" }, { BER_UNI_TAG_TeletextString , "TeletextString, 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" }, { 0, NULL } }; proto_item *get_ber_last_created_item(void) { return ber_last_created_item; } static int dissect_ber_sq_of(gboolean implicit_tag, guint32 type, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, ber_sequence *seq, gint hf_id, gint ett_id); /* 8.1 General rules for encoding */ /* 8.1.2 Identifier octets */ int get_ber_identifier(tvbuff_t *tvb, int offset, guint8 *class, gboolean *pc, guint32 *tag) { guint8 id, t; guint8 tmp_class; gboolean tmp_pc; guint32 tmp_tag; id = tvb_get_guint8(tvb, offset); offset += 1; /* 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_length_remaining(tvb, offset) > 0) { t = tvb_get_guint8(tvb, offset); offset += 1; tmp_tag <<= 7; tmp_tag |= t & 0x7F; if (t & 0x80) break; } } if (class) *class = tmp_class; if (pc) *pc = tmp_pc; if (tag) *tag = tmp_tag; return offset; } int dissect_ber_identifier(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, guint8 *class, gboolean *pc, guint32 *tag) { int old_offset = offset; guint8 tmp_class; gboolean tmp_pc; guint32 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_class==BER_CLASS_UNI){ proto_tree_add_uint(tree, hf_ber_id_uni_tag, tvb, old_offset, offset - old_offset, tmp_tag); } else { proto_tree_add_uint(tree, hf_ber_id_tag, tvb, old_offset, offset - old_offset, tmp_tag); } } if (class) *class = tmp_class; if (pc) *pc = tmp_pc; if (tag) *tag = tmp_tag; return offset; } /* this function gets the length octets of the BER TLV. * We only handle (TAGs and) LENGTHs that fit inside 32 bit integers. */ /* 8.1.3 Length octets */ int get_ber_length(tvbuff_t *tvb, int offset, guint32 *length, gboolean *ind) { guint8 oct, len; guint32 tmp_length; gboolean tmp_ind; tmp_length = 0; tmp_ind = FALSE; 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_ind = TRUE; /* TO DO */ } } if (length) *length = tmp_length; if (ind) *ind = tmp_ind; return offset; } /* 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, gboolean *ind) { int old_offset = offset; guint32 tmp_length; gboolean tmp_ind; offset = get_ber_length(tvb, offset, &tmp_length, &tmp_ind); if(show_internal_ber_fields){ 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; return offset; } /* 8.7 Encoding of an octetstring value */ int dissect_ber_octet_string(gboolean implicit_tag, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **out_tvb) { guint8 class; gboolean pc, ind; guint32 tag; guint32 len; int end_offset; proto_item *it; /* read header and len for the octet string */ offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, &ind); end_offset=offset+len; /* sanity check: we only handle Constructed Universal Sequences */ if (!implicit_tag) { if( (class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_OCTETSTRING) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: OctetString expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; } } ber_last_created_item = NULL; if (pc) { /* constructed */ /* TO DO */ } else { /* primitive */ if (hf_id != -1) { it = proto_tree_add_item(tree, hf_id, tvb, offset, len, FALSE); ber_last_created_item = it; } if (out_tvb) { *out_tvb = tvb_new_subset(tvb, offset, len, len); } } return end_offset; } int dissect_ber_octet_string_wcb(gboolean implicit_tag, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, ber_callback func) { tvbuff_t *out_tvb; offset = dissect_ber_octet_string(implicit_tag, pinfo, tree, tvb, offset, hf_id, (func)?&out_tvb:NULL); if (func && (tvb_length(out_tvb)>0)) { if (hf_id != -1) tree = proto_item_add_subtree(ber_last_created_item, ett_ber_octet_string); func(pinfo, tree, out_tvb, 0); } return offset; } int dissect_ber_integer(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, guint32 *value) { guint8 class; gboolean pc; guint32 tag; guint32 len; gint32 val; guint32 i; offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); /* if(class!=BER_CLASS_UNI)*/ /* ok, we cant handle >4 byte integers so lets fake them */ if(len>8){ proto_tree_add_text(tree, tvb, offset, len, "BER: Can not parse %d byte integers", len); return 0xdeadbeef; } if(len>4){ header_field_info *hfinfo; char buf[8]={0,0,0,0,0,0,0,0}; tvb_memcpy(tvb, buf+8-len, offset, len); hfinfo = proto_registrar_get_nth(hf_id); proto_tree_add_text(tree, tvb, offset, len, "%s: %s", hfinfo->name, u64toa(buf)); return 0xdeadbeef; } val=0; if (len > 0) { /* extend sign bit */ val = (gint8)tvb_get_guint8(tvb, offset); offset++; } for(i=1;itype == FT_BOOLEAN) ber_last_created_item=proto_tree_add_boolean(tree, hf_id, tvb, offset-1, 1, val); else ber_last_created_item=proto_tree_add_uint(tree, hf_id, tvb, offset-1, 1, val?1:0); } return offset; } /* this function dissects a BER sequence */ int dissect_ber_sequence(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, ber_sequence *seq, gint hf_id, gint ett_id) { guint8 class; gboolean pc, ind; guint32 tag; guint32 len; proto_tree *tree = parent_tree; proto_item *item = NULL; int end_offset; /* first we must read the sequence header */ offset = dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, &ind); end_offset = offset + len; /* sanity check: we only handle Constructed Universal Sequences */ if ((!pc) ||(!implicit_tag&&((class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_SEQUENCE)))) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: Sequence expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; } /* create subtree */ if (hf_id != -1) { if(parent_tree){ item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, len, FALSE); tree = proto_item_add_subtree(item, ett_id); } } /* loop over all entries until we reach the end of the sequence */ while (offset < end_offset){ guint8 class; gboolean pc; guint32 tag; guint32 len; int hoffset, eoffset; hoffset = offset; /* read header and len for next field */ offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = get_ber_length(tvb, offset, &len, NULL); eoffset = offset + len; ber_sequence_try_again: /* have we run out of known entries in the sequence ?*/ if (!seq->func) { /* it was not, move to the enxt one and try again */ proto_tree_add_text(tree, tvb, offset, len, "BER Error: This field lies beyond the end of the known sequence definition."); offset = eoffset; continue; } /* verify that this one is the one we want */ if( (seq->class!=class) ||(seq->tag!=tag) ){ /* it was not, move to the enxt 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; } if (!(seq->flags & BER_FLAGS_NOTCHKTAG)) { proto_tree_add_text(tree, tvb, offset, len, "BER Error: Wrong field"); seq++; offset=eoffset; continue; } } if (!(seq->flags & BER_FLAGS_NOOWNTAG) && !(seq->flags & BER_FLAGS_IMPLTAG)) { /* dissect header and len for field */ hoffset = dissect_ber_identifier(pinfo, tree, tvb, hoffset, NULL, NULL, NULL); hoffset = dissect_ber_length(pinfo, tree, tvb, hoffset, NULL, NULL); } /* call the dissector for this field */ seq->func(pinfo, tree, tvb, hoffset); seq++; offset = eoffset; } /* if we didnt end up at exactly offset, then we ate too many bytes */ if (offset != end_offset) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: Sequence ate %d too many bytes", offset-end_offset); } return end_offset; } /* this function dissects a BER choice */ int dissect_ber_choice(packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, const ber_choice *choice, gint hf_id, gint ett_id) { guint8 class; gboolean pc; guint32 tag; guint32 len; const ber_choice *ch; proto_tree *tree=parent_tree; proto_item *item=NULL; int end_offset; int hoffset = offset; header_field_info *hfinfo; /* read header and len for choice field */ offset=get_ber_identifier(tvb, offset, &class, &pc, &tag); offset=get_ber_length(tvb, offset, &len, NULL); end_offset=offset+len; /* Some sanity checks. * The hf field passed to us MUST be an integer type */ if(hf_id!=-1){ 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_text(tree, tvb, offset, len,"dissect_ber_choice(): Was passed a HF field that was not integer type : %s",hfinfo->abbrev); fprintf(stderr,"dissect_ber_choice(): frame:%d offset:%d Was passed a HF field that was not integer type : %s\n",pinfo->fd->num,offset,hfinfo->abbrev); return end_offset; } } /* loop over all entries until we find the right choice or run out of entries */ ch = choice; while(ch->func){ if( (ch->class==class) &&(ch->tag==tag) ){ if (!(ch->flags & BER_FLAGS_NOOWNTAG) && !(ch->flags & BER_FLAGS_IMPLTAG)) { /* dissect header and len for field */ hoffset = dissect_ber_identifier(pinfo, tree, tvb, hoffset, NULL, NULL, NULL); hoffset = dissect_ber_length(pinfo, tree, tvb, hoffset, NULL, NULL); } /* create subtree */ if(hf_id!=-1){ 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); } } offset=ch->func(pinfo, tree, tvb, hoffset); return end_offset; break; } ch++; } /* oops no more entries and we still havent found * our guy :-( */ proto_tree_add_text(tree, tvb, offset, len, "BER Error: This choice field was not found."); return end_offset; } #if 0 /* this function dissects a BER GeneralString */ int dissect_ber_GeneralString(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *name_string, int name_len) { guint8 class; gboolean pc; guint32 tag; guint32 len; int end_offset; char str_arr[256]; guint32 max_len; char *str; str=str_arr; max_len=255; if(name_string){ str=name_string; max_len=name_len; } /* first we must read the GeneralString header */ offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); end_offset=offset+len; /* sanity check: we only handle Universal GeneralString*/ if( (class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_GENSTR) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: GeneralString expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; } if(len>=(max_len-1)){ len=max_len-1; } tvb_memcpy(tvb, str, offset, len); str[len]=0; if(hf_id!=-1){ proto_tree_add_string(tree, hf_id, tvb, offset, len, str); } return end_offset; } #endif int dissect_ber_restricted_string(gboolean implicit_tag, guint32 type, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, tvbuff_t **out_tvb) { guint8 class; gboolean pc; guint32 tag; guint32 len; int eoffset; int hoffset = offset; offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = get_ber_length(tvb, offset, &len, NULL); eoffset = offset + len; /* sanity check */ if (!implicit_tag) { if( (class!=BER_CLASS_UNI) ||(tag != type) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: String with tag=%d expected but Class:%d PC:%d Tag:%d was unexpected", type, class, pc, tag); return eoffset; } } /* 8.21.3 */ return dissect_ber_octet_string(TRUE, pinfo, tree, tvb, hoffset, hf_id, out_tvb); } int dissect_ber_GeneralString(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *name_string, guint name_len) { tvbuff_t *out_tvb; offset = dissect_ber_restricted_string(FALSE, BER_UNI_TAG_GeneralString, pinfo, tree, tvb, offset, hf_id, (name_string)?&out_tvb:NULL); if (name_string) { if (tvb_length(out_tvb) >= name_len) { tvb_memcpy(out_tvb, name_string, 0, name_len-1); name_string[name_len-1] = '\0'; } else { tvb_memcpy(out_tvb, name_string, 0, -1); name_string[tvb_length(out_tvb)] = '\0'; } } return offset; } /* 8.19 Encoding of an object identifier value */ int dissect_ber_object_identifier(gboolean implicit_tag, packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, char *value_string) { guint8 class; gboolean pc; guint32 tag; guint32 i, len; int eoffset; guint8 byte; guint32 value; char str[256],*strp; offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); eoffset = offset + len; if (value_string) { value_string[0] = '\0'; } /* sanity check */ if (!implicit_tag) { if( (class!=BER_CLASS_UNI) ||(tag != BER_UNI_TAG_OID) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: Object Identifier expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return eoffset; } } value=0; for (i=0,strp=str; i200){ proto_tree_add_text(tree, tvb, offset, eoffset - offset, "BER Error: too long Object Identifier"); return offset; } /* 8.19.4 */ if (i == 0) { strp += sprintf(strp, "%d.%d", byte/40, byte%40); continue; } value = (value << 7) | (byte & 0x7F); if (byte & 0x80) { continue; } strp += sprintf(strp, ".%d", value); value = 0; } *strp = '\0'; if (hf_id != -1) { proto_tree_add_string(tree, hf_id, tvb, offset - len, len, str); } if (value_string) { strcpy(value_string, str); } return eoffset; } static int dissect_ber_sq_of(gboolean implicit_tag, guint32 type, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, ber_sequence *seq, gint hf_id, gint ett_id) { guint8 class; gboolean pc, ind; guint32 tag; guint32 len; proto_tree *tree = parent_tree; proto_item *item = NULL; int cnt, hoffset, end_offset; header_field_info *hfi; /* first we must read the sequence header */ offset = dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, tree, tvb, offset, &len, &ind); end_offset = offset + len; /* sanity check: we only handle Constructed Universal Sequences */ if (!pc ||(!implicit_tag&&((class!=BER_CLASS_UNI) ||(tag!=type)))) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: %s Of expected but Class:%d PC:%d Tag:%d was unexpected", (type==BER_UNI_TAG_SEQUENCE)?"Set":"Sequence", class, pc, tag); return end_offset; } /* count number of items */ cnt = 0; hoffset = offset; while (offset < end_offset){ guint32 len; /* read header and len for next field */ offset = get_ber_identifier(tvb, offset, NULL, NULL, NULL); offset = get_ber_length(tvb, offset, &len, NULL); offset += len; cnt++; } offset = hoffset; /* create subtree */ if (hf_id != -1) { 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, len, FALSE); proto_item_append_text(item, ":"); } else { item = proto_tree_add_uint(parent_tree, hf_id, tvb, offset, len, cnt); proto_item_append_text(item, (cnt==1)?" item":" items"); } tree = proto_item_add_subtree(item, ett_id); } } /* loop over all entries until we reach the end of the sequence */ while (offset < end_offset){ guint8 class; gboolean pc; guint32 tag; guint32 len; int eoffset; int hoffset; hoffset = offset; /* read header and len for next field */ offset = get_ber_identifier(tvb, offset, &class, &pc, &tag); offset = get_ber_length(tvb, offset, &len, NULL); eoffset = offset + len; /* verify that this one is the one we want */ if ((seq->class!=class) ||(seq->tag!=tag) ){ if (!(seq->flags & BER_FLAGS_NOTCHKTAG)) { proto_tree_add_text(tree, tvb, offset, len, "BER Error: Wrong field"); offset = eoffset; continue; } } if (!(seq->flags & BER_FLAGS_NOOWNTAG) && !(seq->flags & BER_FLAGS_IMPLTAG)) { /* dissect header and len for field */ hoffset = dissect_ber_identifier(pinfo, tree, tvb, hoffset, NULL, NULL, NULL); hoffset = dissect_ber_length(pinfo, tree, tvb, hoffset, NULL, NULL); } /* call the dissector for this field */ seq->func(pinfo, tree, tvb, hoffset); cnt++; offset = eoffset; } /* if we didnt end up at exactly offset, then we ate too many bytes */ if (offset != end_offset) { proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: %s Of ate %d too many bytes", (type==BER_UNI_TAG_SEQUENCE)?"Set":"Sequence", offset-end_offset); } return end_offset; } int dissect_ber_sequence_of(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, ber_sequence *seq, gint hf_id, gint ett_id) { return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SEQUENCE, pinfo, parent_tree, tvb, offset, seq, hf_id, ett_id); } int dissect_ber_set_of(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, ber_sequence *seq, gint hf_id, gint ett_id) { return dissect_ber_sq_of(implicit_tag, BER_UNI_TAG_SET, pinfo, parent_tree, tvb, offset, seq, hf_id, ett_id); } int dissect_ber_generalized_time(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id) { char str[32]; const guint8 *tmpstr; guint8 class; gboolean pc; guint32 tag; guint32 len; int end_offset; offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len, NULL); end_offset=offset+len; /* sanity check. we only handle universal/generalized time */ if( (class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_GeneralizedTime)){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: GeneralizedTime expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; end_offset=offset+len; } tmpstr=tvb_get_ptr(tvb, offset, len); snprintf(str, 31, "%.4s-%.2s-%.2s %.2s:%.2s:%.2s (%.1s)", tmpstr, tmpstr+4, tmpstr+6, tmpstr+8, tmpstr+10, tmpstr+12, tmpstr+14); str[31]=0; /* just in case ... */ if(hf_id!=-1){ proto_tree_add_string(tree, hf_id, tvb, offset, len, str); } offset+=len; return offset; } /* 8.6 Encoding of a bitstring value */ int dissect_ber_bitstring(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, asn_namedbit *named_bits, gint hf_id, gint ett_id, tvbuff_t **out_tvb) { guint8 class; gboolean pc, ind; guint32 tag; guint32 len; guint8 pad=0, b0, b1, val; int end_offset; proto_item *item = NULL; proto_tree *tree = NULL; asn_namedbit *nb; char *sep; gboolean term; /* read header and len for the octet string */ offset = dissect_ber_identifier(pinfo, parent_tree, tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, parent_tree, tvb, offset, &len, &ind); end_offset = offset + len; /* sanity check: we only handle Universal BitSrings */ if (!implicit_tag) { if( (class!=BER_CLASS_UNI) ||(tag!=BER_UNI_TAG_BITSTRING) ){ proto_tree_add_text(parent_tree, tvb, offset-2, 2, "BER Error: BitString expected but Class:%d PC:%d Tag:%d was unexpected", class, pc, tag); return end_offset; } } ber_last_created_item = NULL; if (pc) { /* constructed */ /* TO DO */ } else { /* primitive */ /* padding */ pad = tvb_get_guint8(tvb, offset); proto_tree_add_item(parent_tree, hf_ber_bitstring_padding, tvb, offset, 1, FALSE); offset++; len--; if ( hf_id != -1) { item = proto_tree_add_item(parent_tree, hf_id, tvb, offset, len, FALSE); ber_last_created_item = item; if (ett_id != -1) { tree = proto_item_add_subtree(item, ett_id); } } if (out_tvb) { *out_tvb = tvb_new_subset(tvb, offset, len, 8*len-pad); } } if (named_bits) { sep = " ("; term = FALSE; nb = named_bits; while (nb->p_id) { if (nb->bit < (8*len-pad)) { val = tvb_get_guint8(tvb, offset + nb->bit/8); val &= 0x80 >> (nb->bit%8); b0 = (nb->gb0 == -1) ? nb->bit/8 : ((guint32)nb->gb0)/8; b1 = (nb->gb1 == -1) ? nb->bit/8 : ((guint32)nb->gb1)/8; proto_tree_add_item(tree, *(nb->p_id), tvb, offset + b0, b1 - b0 + 1, FALSE); } else { /* 8.6.2.4 */ val = 0; proto_tree_add_boolean(tree, *(nb->p_id), tvb, offset + len, 0, 0x00); } if (val) { if (item && nb->tstr) proto_item_append_text(item, "%s%s", sep, nb->tstr); } else { if (item && nb->fstr) proto_item_append_text(item, "%s%s", sep, nb->fstr); } nb++; sep = ", "; term = TRUE; } if (term) proto_item_append_text(item, ")"); } return end_offset; } int dissect_ber_bitstring32(gboolean implicit_tag, packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, int **bit_fields, gint hf_id, gint ett_id, tvbuff_t **out_tvb) { tvbuff_t *tmp_tvb; proto_tree *tree; guint32 val; int **bf; header_field_info *hfi; char *sep; gboolean term; unsigned int i, tvb_len; offset = dissect_ber_bitstring(implicit_tag, pinfo, parent_tree, tvb, offset, NULL, hf_id, ett_id, &tmp_tvb); tree = proto_item_get_subtree(ber_last_created_item); if (bit_fields && tree) { /* tmp_tvb points to the actual bitstring (including any pad bits at the end. * note that this bitstring is not neccessarily always encoded as 4 bytes * so we have to read it byte by byte. */ val=0; tvb_len=tvb_length(tmp_tvb); for(i=0;i<4;i++){ val<<=8; if(ibitmask) { proto_item_append_text(ber_last_created_item, "%s%s", sep, hfi->name); sep = ", "; term = TRUE; } bf++; } if (term) proto_item_append_text(ber_last_created_item, ")"); } if (out_tvb) *out_tvb = tmp_tvb; return offset; } 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 unsused bits in the last octet of the 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, VALS(ber_uni_tag_codes), 0x1f, "Universal tag type", HFILL }}, { &hf_ber_id_tag, { "Tag", "ber.id.tag", FT_UINT32, BASE_DEC, NULL, 0, "Tag value for non-Universal classes", HFILL }}, { &hf_ber_length, { "Length", "ber.length", FT_UINT32, BASE_DEC, NULL, 0, "Length of contents", HFILL }}, }; static gint *ett[] = { &ett_ber_octet_string, }; module_t *ber_module; proto_ber = proto_register_protocol("Basic Encoding Rules (ASN.1 X.690)", "BER", "ber"); proto_register_field_array(proto_ber, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); 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); } void proto_reg_handoff_ber(void) { }