diff options
author | Lars Roland <Lars.Roland@gmx.net> | 2005-02-09 23:38:00 +0000 |
---|---|---|
committer | Lars Roland <Lars.Roland@gmx.net> | 2005-02-09 23:38:00 +0000 |
commit | ec9ca01616d179043c0e84d5e5799cfec8ad63ba (patch) | |
tree | 6b8d6526fe00a336c75b9118009deb4c690984ad /epan/asn1.c | |
parent | 125bb2d27803ba28210643ff404072ab0af31f33 (diff) |
Move the following files from /trunk to /trunk/epan:
asn1.[ch]
follow.[ch]
ptvcursor.[ch]
reassemble.[ch]
xmlstub.[ch]
fix #include statements accordingly.
svn path=/trunk/; revision=13366
Diffstat (limited to 'epan/asn1.c')
-rw-r--r-- | epan/asn1.c | 1085 |
1 files changed, 1085 insertions, 0 deletions
diff --git a/epan/asn1.c b/epan/asn1.c new file mode 100644 index 0000000000..f9ff91f85c --- /dev/null +++ b/epan/asn1.c @@ -0,0 +1,1085 @@ +/* asn1.c + * Routines for ASN.1 BER dissection + * + * $Id$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * + * Based on "g_asn1.c" from: + * + * GXSNMP -- An snmp mangament application + * Copyright (C) 1998 Gregory McLean & Jochen Friedrich + * Beholder RMON ethernet network monitor, Copyright (C) 1993 DNPAP group + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * MODULE INFORMATION + * ------------------ + * FILE NAME: g_asn1.c + * SYSTEM NAME: ASN1 Basic Encoding + * ORIGINAL AUTHOR(S): Dirk Wisse + * VERSION NUMBER: 1 + * CREATION DATE: 1990/11/22 + * + * DESCRIPTION: ASN1 Basic Encoding Rules. + * + * To decode this we must do: + * + * asn1_open (asn1, tvb, offset); + * asn1_header_decode (asn1, &end_of_seq, cls, con, tag, def, len); + * asn1_header_decode (asn1, &end_of_octs, cls, con, tag, def, len); + * asn1_octets_decode (asn1, end_of_octs, str, len); + * asn1_header_decode (asn1, &end_of_int, cls, con, tag); + * asn1_int_decode (asn1, end_of_int, &integer); + * asn1_eoc_decode (asn1, end_of_seq); + * asn1_close (asn1, &offset); + * + * For indefinite encoding end_of_seq and &end_of_seq in the + * example above should be replaced by NULL. + * For indefinite decoding nothing has to be changed. + * This can be very useful if you want to decode both + * definite and indefinite encodings. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <stdio.h> + +#include <limits.h> + +#include <glib.h> + +#ifdef NEED_SNPRINTF_H +# include "snprintf.h" +#endif + +#include <epan/tvbuff.h> +#include <epan/asn1.h> + +/* + * NAME: asn1_open [API] + * SYNOPSIS: void asn1_open + * ( + * ASN1_SCK *asn1, + * tvbuff_t *tvb, + * int offset + * ) + * DESCRIPTION: Opens an ASN1 socket. + * Parameters: + * asn1: pointer to ASN1 socket. + * tvb: Tvbuff for encoding. + * offset: Current offset in tvbuff. + * Encoding starts at the end of the buffer, and + * proceeds to the beginning. + * RETURNS: void + */ + +void +asn1_open(ASN1_SCK *asn1, tvbuff_t *tvb, int offset) +{ + asn1->tvb = tvb; + asn1->offset = offset; +} + +/* + * NAME: asn1_close [API] + * SYNOPSIS: void asn1_close + * ( + * ASN1_SCK *asn1, + * int *offset + * ) + * DESCRIPTION: Closes an ASN1 socket. + * Parameters: + * asn1: pointer to ASN1 socket. + * offset: pointer to variable into which current offset is + * to be put. + * RETURNS: void + */ + +void +asn1_close(ASN1_SCK *asn1, int *offset) +{ + *offset = asn1->offset; +} + +/* + * NAME: asn1_octet_decode + * SYNOPSIS: int asn1_octet_decode + * ( + * ASN1_SCK *asn1, + * guchar *ch + * ) + * DESCRIPTION: Decodes an octet. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_octet_decode(ASN1_SCK *asn1, guchar *ch) +{ + *ch = tvb_get_guint8(asn1->tvb, asn1->offset); + asn1->offset++; + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_tag_get + * SYNOPSIS: int asn1_tag_get + * ( + * ASN1_SCK *asn1, + * guint *tag + * ) + * DESCRIPTION: Decodes a tag number, combining it with existing tag bits. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +static int +asn1_tag_get(ASN1_SCK *asn1, guint *tag) +{ + int ret; + guchar ch; + + do { + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *tag <<= 7; + *tag |= ch & 0x7F; + } while ((ch & 0x80) == 0x80); + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_tag_decode + * SYNOPSIS: int asn1_tag_decode + * ( + * ASN1_SCK *asn1, + * guint *tag + * ) + * DESCRIPTION: Decodes a tag number. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_tag_decode(ASN1_SCK *asn1, guint *tag) +{ + *tag = 0; + return asn1_tag_get(asn1, tag); +} + +/* + * NAME: asn1_id_decode + * SYNOPSIS: int asn1_id_decode + * ( + * ASN1_SCK *asn1, + * guint *cls, + * guint *con, + * guint *tag + * ) + * DESCRIPTION: Decodes an identifier. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_id_decode(ASN1_SCK *asn1, guint *cls, guint *con, guint *tag) +{ + int ret; + guchar ch; + + *tag = 0; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *cls = (ch & 0xC0) >> 6; + *con = (ch & 0x20) >> 5; + *tag = (ch & 0x1F); + if (*tag == 0x1F) { + ret = asn1_tag_decode (asn1, tag); + if (ret != ASN1_ERR_NOERROR) + return ret; + } + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_id_decode1 + * SYNOPSIS: int asn1_id_decode1 + * ( + * ASN1_SCK *asn1, + * guint *tag + * ) + * DESCRIPTION: Decodes an identifier. + * Like asn1_id_decode() except that the Class and Constructor + * bits are returned in the tag. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_id_decode1(ASN1_SCK *asn1, guint *tag) +{ + int ret; + guchar ch; + + *tag = 0; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + + *tag = ch; + if ((*tag & 0x1F) == 0x1F) { /* high-tag-number format */ + *tag = ch >> 5; /* leave just the Class and Constructor bits */ + ret = asn1_tag_get (asn1, tag); + if (ret != ASN1_ERR_NOERROR) + return ret; + } + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_length_decode + * SYNOPSIS: int asn1_length_decode + * ( + * ASN1_SCK *asn1, + * gboolean *def, + * guint *len + * ) + * DESCRIPTION: Decodes an ASN1 length. + * Parameters: + * asn1: pointer to ASN1 socket. + * def: Boolean - TRUE if length definite, FALSE if not + * len: length, if length is definite + * DESCRIPTION: Decodes a definite or indefinite length. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_length_decode(ASN1_SCK *asn1, gboolean *def, guint *len) +{ + int ret; + guchar ch, cnt; + + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + if (ch == 0x80) + *def = FALSE; /* indefinite length */ + else { + *def = TRUE; /* definite length */ + if (ch < 0x80) + *len = ch; + else { + cnt = (guchar) (ch & 0x7F); + *len = 0; + while (cnt > 0) { + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *len <<= 8; + *len |= ch; + cnt--; + } + } + } + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_header_decode [API] + * SYNOPSIS: int asn1_header_decode + * ( + * ASN1_SCK *asn1, + * guint *cls, + * guint *con, + * guint *tag + * gboolean *defp, + * guint *lenp + * ) + * DESCRIPTION: Decodes an ASN1 header. + * Parameters: + * asn1: pointer to ASN1 socket. + * cls: Class (see asn1.h) + * con: Primitive, Constructed (ASN1_PRI, ASN1_CON) + * tag: Tag (see asn1.h) + * defp: Boolean - TRUE if length definite, FALSE if not + * lenp: length, if length is definite + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_header_decode(ASN1_SCK *asn1, guint *cls, guint *con, guint *tag, + gboolean *defp, guint *lenp) +{ + int ret; + guint def, len; + + ret = asn1_id_decode (asn1, cls, con, tag); + if (ret != ASN1_ERR_NOERROR) + return ret; + ret = asn1_length_decode (asn1, &def, &len); + if (ret != ASN1_ERR_NOERROR) + return ret; + *defp = def; + *lenp = len; + return ASN1_ERR_NOERROR; +} + + +/* + * NAME: asn1_eoc [API] + * SYNOPSIS: gboolean asn1_eoc + * ( + * ASN1_SCK *asn1, + * int eoc + * ) + * DESCRIPTION: Checks if decoding is at End Of Contents. + * Parameters: + * asn1: pointer to ASN1 socket. + * eoc: offset of end of encoding, or -1 if indefinite. + * RETURNS: gboolean success + */ +gboolean +asn1_eoc ( ASN1_SCK *asn1, int eoc) +{ + if (eoc == -1) + return (tvb_get_guint8(asn1->tvb, asn1->offset) == 0x00 + && tvb_get_guint8(asn1->tvb, asn1->offset + 1) == 0x00); + else + return (asn1->offset >= eoc); +} + +/* + * NAME: asn1_eoc_decode [API] + * SYNOPSIS: int asn1_eoc_decode + * ( + * ASN1_SCK *asn1, + * int eoc + * ) + * DESCRIPTION: Decodes End Of Contents. + * Parameters: + * asn1: pointer to ASN1 socket. + * eoc: offset of end of encoding, or -1 if indefinite. + * If eoc is -1 it decodes an ASN1 End Of + * Contents (0x00 0x00), so it has to be an + * indefinite length encoding. If eoc is a non-negative + * integer, it probably was filled by asn1_header_decode, + * and should refer to the octet after the last of the encoding. + * It is checked if this offset refers to the octet to be + * decoded. This only takes place in decoding a + * definite length encoding. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_eoc_decode (ASN1_SCK *asn1, int eoc) +{ + int ret; + guchar ch; + + if (eoc == -1) { + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + if (ch != 0x00) + return ASN1_ERR_EOC_MISMATCH; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + if (ch != 0x00) + return ASN1_ERR_EOC_MISMATCH; + return ASN1_ERR_NOERROR; + } else { + if (asn1->offset != eoc) + return ASN1_ERR_LENGTH_MISMATCH; + return ASN1_ERR_NOERROR; + } +} + +/* + * NAME: asn1_null_decode [API] + * SYNOPSIS: int asn1_null_decode + * ( + * ASN1_SCK *asn1, + * int enc_len + * ) + * DESCRIPTION: Decodes Null. + * Parameters: + * asn1: pointer to ASN1 socket. + * enc_len: length of encoding of value. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_null_decode ( ASN1_SCK *asn1, int enc_len) +{ + int start_off = asn1->offset; + + asn1->offset += enc_len; + /* + * Check for integer overflows. + * XXX - ASN1_ERR_LENGTH_MISMATCH seemed like the most appropriate + * error from the ones available. Should we make a new one? + */ + if (asn1->offset < 0 || asn1->offset < start_off) + return ASN1_ERR_LENGTH_MISMATCH; + + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_bool_decode [API] + * SYNOPSIS: int asn1_bool_decode + * ( + * ASN1_SCK *asn1, + * int enc_len, + * gboolean *boolean + * ) + * DESCRIPTION: Decodes Boolean. + * Parameters: + * asn1: pointer to ASN1 socket. + * enc_len: length of encoding of value. + * bool: False, True (0, !0). + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_bool_decode ( ASN1_SCK *asn1, int enc_len, gboolean *boolean) +{ + int ret; + guchar ch; + + if (enc_len != 1) + return ASN1_ERR_LENGTH_MISMATCH; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *boolean = ch ? TRUE : FALSE; + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_int32_value_decode [API] + * SYNOPSIS: int asn1_int32_value_decode + * ( + * ASN1_SCK *asn1, + * int enc_len, + * gint32 *integer + * ) + * DESCRIPTION: Decodes value portion of Integer (which must be no more + * than 32 bits). + * Parameters: + * asn1: pointer to ASN1 socket. + * enc_len: length of encoding of value. + * integer: Integer. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_int32_value_decode ( ASN1_SCK *asn1, int enc_len, gint32 *integer) +{ + int ret; + int eoc; + guchar ch; + guint len; + + eoc = asn1->offset + enc_len; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *integer = (gint) ch; + len = 1; + while (asn1->offset < eoc) { + if (++len > sizeof (gint32)) + return ASN1_ERR_WRONG_LENGTH_FOR_TYPE; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *integer <<= 8; + *integer |= ch; + } + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_int32_decode [API] + * SYNOPSIS: int asn1_int32_decode + * ( + * ASN1_SCK *asn1, + * gint32 *integer, + * guint *nbytes, + * ) + * DESCRIPTION: Decodes Integer (which must be no more than 32 bits). + * Parameters: + * asn1: pointer to ASN1 socket. + * integer: Integer. + * nbytes: number of bytes used to encode it. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_int32_decode ( ASN1_SCK *asn1, gint32 *integer, guint *nbytes) +{ + int ret; + int start; + guint cls; + guint con; + guint tag; + gboolean def; + guint enc_len; + + start = asn1->offset; + ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &enc_len); + if (ret != ASN1_ERR_NOERROR) + goto done; + if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) { + ret = ASN1_ERR_WRONG_TYPE; + goto done; + } + if (!def) { + ret = ASN1_ERR_LENGTH_NOT_DEFINITE; + goto done; + } + ret = asn1_int32_value_decode (asn1, enc_len, integer); + +done: + *nbytes = asn1->offset - start; + return ret; +} + +/* + * NAME: asn1_uint32_value_decode [API] + * SYNOPSIS: int asn1_uint32_value_decode + * ( + * ASN1_SCK *asn1, + * int enc_len, + * guint32 *integer + * ) + * DESCRIPTION: Decodes value part of Unsigned Integer (which must be no + * more than 32 bits). + * Parameters: + * asn1: pointer to ASN1 socket. + * enc_len: length of encoding of value. + * integer: Integer. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_uint32_value_decode ( ASN1_SCK *asn1, int enc_len, guint32 *integer) +{ + int ret; + int eoc; + guchar ch; + guint len; + + eoc = asn1->offset + enc_len; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *integer = ch; + if (ch == 0) + len = 0; + else + len = 1; + while (asn1->offset < eoc) { + if (++len > sizeof (guint32)) + return ASN1_ERR_WRONG_LENGTH_FOR_TYPE; + ret = asn1_octet_decode (asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *integer <<= 8; + *integer |= ch; + } + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_uint32_decode [API] + * SYNOPSIS: int asn1_uint32_decode + * ( + * ASN1_SCK *asn1, + * guint32 *integer, + * guint *nbytes, + * ) + * DESCRIPTION: Decodes Unsigned Integer (which must be no more than 32 bits). + * Parameters: + * asn1: pointer to ASN1 socket. + * integer: Integer. + * nbytes: number of bytes used to encode it. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_uint32_decode ( ASN1_SCK *asn1, guint32 *integer, guint *nbytes) +{ + int ret; + int start; + guint cls; + guint con; + guint tag; + gboolean def; + guint enc_len; + + start = asn1->offset; + ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &enc_len); + if (ret != ASN1_ERR_NOERROR) + goto done; + if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_INT) { + ret = ASN1_ERR_WRONG_TYPE; + goto done; + } + if (!def) { + ret = ASN1_ERR_LENGTH_NOT_DEFINITE; + goto done; + } + ret = asn1_uint32_value_decode (asn1, enc_len, integer); + +done: + *nbytes = asn1->offset - start; + return ret; +} + +/* + * NAME: asn1_bits_decode [API] + * SYNOPSIS: int asn1_bits_decode + * ( + * ASN1_SCK *asn1, + * int eoc, + * guchar *bits, + * guint size, + * guint len, + * guchar unused + * ) + * DESCRIPTION: Decodes Bit String. + * Parameters: + * asn1: pointer to ASN1 socket. + * enc_len: length of value. + * bits: pointer to variable we set to point to strring + * len: Size of Bit String in characters. + * unused: Number of unused bits in last character. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_bits_decode ( ASN1_SCK *asn1, int enc_len, guchar **bits, + guint *len, guchar *unused) +{ + int ret; + int eoc; + guchar *ptr; + + eoc = asn1->offset + enc_len; + *bits = NULL; + ret = asn1_octet_decode (asn1, unused); + if (ret != ASN1_ERR_NOERROR) + return ret; + *len = 0; + + /* + * First, make sure the entire string is in the tvbuff, and throw + * an exception if it isn't. If the length is bogus, this should + * keep us from trying to allocate an immensely large buffer. + * (It won't help if the length is *valid* but immensely large, + * but that's another matter; in any case, that would happen only + * if we had an immensely large tvbuff....) + */ + if (enc_len != 0) { + tvb_ensure_bytes_exist(asn1->tvb, asn1->offset, enc_len); + *bits = g_malloc (enc_len); + } else { + /* + * If the length is 0, we allocate a 1-byte buffer, as + * "g_malloc()" returns NULL if passed 0 as an argument, + * and our caller expects us to return a pointer to a + * buffer. + */ + *bits = g_malloc (1); + } + + ptr = *bits; + while (asn1->offset < eoc) { + ret = asn1_octet_decode (asn1, (guchar *)ptr++); + if (ret != ASN1_ERR_NOERROR) { + g_free(*bits); + *bits = NULL; + return ret; + } + } + *len = ptr - *bits; + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_string_value_decode [API] + * SYNOPSIS: int asn1_string_value_decode + * ( + * ASN1_SCK *asn1, + * int enc_len, + * guchar **octets + * ) + * DESCRIPTION: Decodes value portion of string (Octet String, various + * character string types) + * Parameters: + * asn1: pointer to ASN1 socket. + * enc_len: length of encoding of value. + * octets: pointer to variable we set to point to string, + * which is '\0' terminated for ease of use as C-string + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_string_value_decode ( ASN1_SCK *asn1, int enc_len, guchar **octets) +{ + int ret; + int eoc; + guchar *ptr; + + /* + * First, make sure the entire string is in the tvbuff, and throw + * an exception if it isn't. If the length is bogus, this should + * keep us from trying to allocate an immensely large buffer. + * (It won't help if the length is *valid* but immensely large, + * but that's another matter; in any case, that would happen only + * if we had an immensely large tvbuff....) + */ + if (enc_len != 0) + tvb_ensure_bytes_exist(asn1->tvb, asn1->offset, enc_len); + *octets = g_malloc (enc_len+1); + + eoc = asn1->offset + enc_len; + ptr = *octets; + while (asn1->offset < eoc) { + ret = asn1_octet_decode (asn1, (guchar *)ptr++); + if (ret != ASN1_ERR_NOERROR) { + g_free(*octets); + *octets = NULL; + return ret; + } + } + *(guchar *)ptr = '\0'; + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_string_decode [API] + * SYNOPSIS: int asn1_string_decode + * ( + * ASN1_SCK *asn1, + * guchar **octets, + * guint *str_len, + * guint *nbytes, + * guint expected_tag + * ) + * DESCRIPTION: Decodes string (Octet String, various character string + * types) + * Parameters: + * asn1: pointer to ASN1 socket. + * octets: pointer to variable we set to point to string. + * str_len: length of octet_string. + * nbytes: number of bytes used to encode. + * expected_tag: tag expected for this type of string. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_string_decode ( ASN1_SCK *asn1, guchar **octets, guint *str_len, + guint *nbytes, guint expected_tag) +{ + int ret; + int start; + int enc_len; + guint cls; + guint con; + guint tag; + gboolean def; + + start = asn1->offset; + ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &enc_len); + if (ret != ASN1_ERR_NOERROR) + goto done; + if (cls != ASN1_UNI || con != ASN1_PRI || tag != expected_tag) { + /* XXX - handle the constructed encoding? */ + ret = ASN1_ERR_WRONG_TYPE; + goto done; + } + if (!def) { + ret = ASN1_ERR_LENGTH_NOT_DEFINITE; + goto done; + } + + ret = asn1_string_value_decode (asn1, enc_len, octets); + *str_len = enc_len; + +done: + *nbytes = asn1->offset - start; + return ret; +} + +/* + * NAME: asn1_octet_string_decode [API] + * SYNOPSIS: int asn1_octet_string_decode + * ( + * ASN1_SCK *asn1, + * guchar **octets, + * guint *str_len, + * guint *nbytes, + * ) + * DESCRIPTION: Decodes Octet String. + * Parameters: + * asn1: pointer to ASN1 socket. + * octets: pointer to variable we set to point to string. + * str_len: length of octet_string. + * nbytes: number of bytes used to encode. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_octet_string_decode ( ASN1_SCK *asn1, guchar **octets, guint *str_len, + guint *nbytes) +{ + return asn1_string_decode(asn1, octets, str_len, nbytes, ASN1_OTS); +} + +/* + * NAME: asn1_subid_decode + * SYNOPSIS: int asn1_subid_decode + * ( + * ASN1_SCK *asn1, + * subid_t *subid + * ) + * DESCRIPTION: Decodes Sub Identifier. + * Parameters: + * asn1: pointer to ASN1 socket. + * subid: Sub Identifier. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_subid_decode ( ASN1_SCK *asn1, subid_t *subid) +{ + int ret; + guchar ch; + + *subid = 0; + do { + ret = asn1_octet_decode(asn1, &ch); + if (ret != ASN1_ERR_NOERROR) + return ret; + *subid <<= 7; + *subid |= ch & 0x7F; + } while ((ch & 0x80) == 0x80); + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_oid_value_decode [API] + * SYNOPSIS: int asn1_oid_value_decode + * ( + * ASN1_SCK *asn1, + * int enc_len, + * subid_t **oid, + * guint *len + * ) + * DESCRIPTION: Decodes value portion of Object Identifier. + * Parameters: + * asn1: pointer to ASN1 socket. + * enc_len: length of encoding of value. + * oid: pointer to variable we set to Object Identifier. + * len: Length of Object Identifier in gulongs. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_oid_value_decode ( ASN1_SCK *asn1, int enc_len, subid_t **oid, guint *len) +{ + int ret; + int eoc; + subid_t subid; + guint size; + subid_t *optr; + + /* + * First, make sure the entire string is in the tvbuff, and throw + * an exception if it isn't. If the length is bogus, this should + * keep us from trying to allocate an immensely large buffer. + * (It won't help if the length is *valid* but immensely large, + * but that's another matter; in any case, that would happen only + * if we had an immensely large tvbuff....) + */ + if (enc_len != 0) + tvb_ensure_bytes_exist(asn1->tvb, asn1->offset, enc_len); + + eoc = asn1->offset + enc_len; + + size = enc_len + 1; + *oid = g_malloc(size * sizeof(gulong)); + optr = *oid; + + ret = asn1_subid_decode (asn1, &subid); + if (ret != ASN1_ERR_NOERROR) { + g_free(*oid); + *oid = NULL; + return ret; + } + if (subid < 40) { + optr[0] = 0; + optr[1] = subid; + } else if (subid < 80) { + optr[0] = 1; + optr[1] = subid - 40; + } else { + optr[0] = 2; + optr[1] = subid - 80; + } + *len = 2; + optr += 2; + while (asn1->offset < eoc) { + if (++(*len) > size) { + g_free(*oid); + *oid = NULL; + return ASN1_ERR_WRONG_LENGTH_FOR_TYPE; + } + ret = asn1_subid_decode (asn1, optr++); + if (ret != ASN1_ERR_NOERROR) { + g_free(*oid); + *oid = NULL; + return ret; + } + } + return ASN1_ERR_NOERROR; +} + +/* + * NAME: asn1_oid_decode [API] + * SYNOPSIS: int asn1_oid_decode + * ( + * ASN1_SCK *asn1, + * subid_t **oid, + * guint *len, + * guint *nbytes + * ) + * DESCRIPTION: Decodes Object Identifier. + * Parameters: + * asn1: pointer to ASN1 socket. + * oid: pointer to variable we set to Object Identifier. + * len: Length of Object Identifier in gulongs. + * nbytes: number of bytes used to encode. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_oid_decode ( ASN1_SCK *asn1, subid_t **oid, guint *len, guint *nbytes) +{ + int ret; + int start; + guint cls; + guint con; + guint tag; + gboolean def; + guint enc_len; + + start = asn1->offset; + ret = asn1_header_decode (asn1, &cls, &con, &tag, &def, &enc_len); + if (ret != ASN1_ERR_NOERROR) + goto done; + if (cls != ASN1_UNI || con != ASN1_PRI || tag != ASN1_OJI) { + ret = ASN1_ERR_WRONG_TYPE; + goto done; + } + if (!def) { + ret = ASN1_ERR_LENGTH_NOT_DEFINITE; + goto done; + } + + ret = asn1_oid_value_decode (asn1, enc_len, oid, len); + +done: + *nbytes = asn1->offset - start; + return ret; +} + +/* + * NAME: asn1_sequence_decode [API] + * SYNOPSIS: int asn1_sequence_decode + * ( + * ASN1_SCK *asn1, + * guint *seq_len, + * guint *nbytes + * ) + * DESCRIPTION: Decodes header for SEQUENCE. + * Parameters: + * asn1: pointer to ASN1 socket. + * seq_len: length of sequence. + * nbytes: number of bytes used to encode header. + * RETURNS: ASN1_ERR value (ASN1_ERR_NOERROR on success) + */ +int +asn1_sequence_decode ( ASN1_SCK *asn1, guint *seq_len, guint *nbytes) +{ + int ret; + int start; + guint cls; + guint con; + guint tag; + gboolean def; + + start = asn1->offset; + ret = asn1_header_decode(asn1, &cls, &con, &tag, + &def, seq_len); + if (ret != ASN1_ERR_NOERROR) + goto done; + if (cls != ASN1_UNI || con != ASN1_CON || tag != ASN1_SEQ) { + ret = ASN1_ERR_WRONG_TYPE; + goto done; + } + if (!def) { + /* XXX - might some sequences have an indefinite length? */ + ret = ASN1_ERR_LENGTH_NOT_DEFINITE; + goto done; + } + ret = ASN1_ERR_NOERROR; + +done: + *nbytes = asn1->offset - start; + return ret; +} + +/* + * NAME: asn1_err_to_str [API] + * SYNOPSIS: char *asn1_err_to_str + * ( + * int err + * ) + * DESCRIPTION: Returns the string corresponding to an ASN.1 library error. + * Parameters: + * err: the error code + * RETURNS: string for the error + */ +char * +asn1_err_to_str(int err) +{ + char *errstr; + char errstrbuf[14+1+1+11+1+1]; /* "Unknown error (%d)\0" */ + + switch (err) { + + case ASN1_ERR_EOC_MISMATCH: + errstr = "EOC mismatch"; + break; + + case ASN1_ERR_WRONG_TYPE: + errstr = "Wrong type for that item"; + break; + + case ASN1_ERR_LENGTH_NOT_DEFINITE: + errstr = "Length was indefinite"; + break; + + case ASN1_ERR_LENGTH_MISMATCH: + errstr = "Length mismatch"; + break; + + case ASN1_ERR_WRONG_LENGTH_FOR_TYPE: + errstr = "Wrong length for that item's type"; + break; + + default: + snprintf(errstrbuf, sizeof errstrbuf, "Unknown error (%d)", err); + errstr = errstrbuf; + break; + } + return errstr; +} |