/* packet-ber.c * Helpers for ASN.1/BER dissection * Ronnie Sahlberg (C) 2004 * * $Id: packet-ber.c,v 1.3 2004/03/01 09:11:23 sahlberg Exp $ * * 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. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #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_BOOLEAN, "Boolean" }, { BER_UNI_TAG_INTEGER, "Integer" }, { BER_UNI_TAG_BITSTRING, "Bit-String" }, { BER_UNI_TAG_OCTETSTRING, "Octet String" }, { BER_UNI_TAG_SEQUENCE, "Sequence" }, { BER_UNI_TAG_GENTIME, "Generalized Time" }, { BER_UNI_TAG_GENSTR, "General String" }, { 0, NULL } }; /* this function dissects the identifier octer of the BER TLV. * We only handle TAGs (and LENGTHs) that fit inside 32 bit integers. */ int dissect_ber_identifier(packet_info *pinfo _U_, proto_tree *tree, tvbuff_t *tvb, int offset, guint8 *class, gboolean *pc, guint32 *tag) { guint8 id; int old_offset=offset; id=tvb_get_guint8(tvb, offset); offset+=1; *class=(id>>6)&0x03; *pc=(id>>5)&0x01; *tag=id&0x1f; /*XXX handle case when TAG==0x1f */ if(show_internal_ber_fields){ proto_tree_add_uint(tree, hf_ber_id_class, tvb, old_offset, 1, id); proto_tree_add_boolean(tree, hf_ber_id_pc, tvb, old_offset, 1, id); if(*class==BER_CLASS_UNI){ proto_tree_add_uint(tree, hf_ber_id_uni_tag, tvb, old_offset, 1, *tag); } else { proto_tree_add_uint(tree, hf_ber_id_tag, tvb, old_offset, 1, *tag); } } return offset; } /* this function dissects the identifier octer 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) { guint8 id; int old_offset=offset; *length=0; id=tvb_get_guint8(tvb, offset); offset+=1; if(!(id&0x80)){ *length=id; if(show_internal_ber_fields){ proto_tree_add_uint(tree, hf_ber_length, tvb, old_offset, 1, *length); } return offset; } /* length byte has bit 8 set ! */ id&=0x7f; while(id--){ guint tmpl; tmpl=tvb_get_guint8(tvb, offset); offset++; *length=((*length)<<8)+tmpl; } if(show_internal_ber_fields){ /*XXX show the len byte */ proto_tree_add_uint(tree, hf_ber_length, tvb, old_offset+1, offset-old_offset+1, *length); } return offset; } /* func is NULL normally but * if the octet string contains an ber encode struct we provide func as the * dissector for that struct */ int dissect_ber_octet_string(packet_info *pinfo, proto_tree *tree, tvbuff_t *tvb, int offset, gint hf_id, ber_callback func) { guint8 class; gboolean pc; 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); end_offset=offset+len; if(!len){ return end_offset; } /* sanity check: we only handle Constructed Universal Sequences */ 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; } if(hf_id!=-1){ it=proto_tree_add_item(tree, hf_id, tvb, offset, len, FALSE); tree=proto_item_add_subtree(it, ett_ber_octet_string); } if(func){ tvbuff_t *next_tvb; next_tvb=tvb_new_subset(tvb, offset, len, len); func(pinfo, tree, next_tvb, 0); } /*qqq*/ return end_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; guint32 val; guint32 i; offset=dissect_ber_identifier(pinfo, tree, tvb, offset, &class, &pc, &tag); offset=dissect_ber_length(pinfo, tree, tvb, offset, &len); /* if(class!=BER_CLASS_UNI)*/ val=0; for(i=0;ifunc){ /* 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; } proto_tree_add_text(tree, tvb, offset, len, "BER Error: Wrong field"); seq++; offset=eoffset; continue; } /* call the dissector for this field */ seq->func(pinfo, tree, tvb, offset); 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 *ch, gint hf_id, gint ett_id) { guint8 class; gboolean pc; 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); end_offset=offset+len; /* 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 find the right choice or run out of entries */ while(ch->func){ if( (ch->class==class) &&(ch->tag==tag) ){ offset=ch->func(pinfo, tree, tvb, offset); 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; } /* 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); 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; } /* this function dissects a BER sequence of */ int dissect_ber_sequence_of(packet_info *pinfo, proto_tree *parent_tree, tvbuff_t *tvb, int offset, ber_callback func, gint hf_id, gint ett_id) { guint8 class; gboolean pc; 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); end_offset=offset+len; /* sanity check: we only handle Constructed Universal Sequences */ if( (class!=BER_CLASS_UNI) ||(!pc) ||(tag!=BER_UNI_TAG_SEQUENCE) ){ proto_tree_add_text(tree, tvb, offset-2, 2, "BER Error: SequenceOf 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