wireshark/asn1.c

1076 lines
31 KiB
C

/* asn1.c
* Routines for ASN.1 BER dissection
*
* $Id: asn1.c,v 1.23 2003/11/09 22:57:52 guy Exp $
*
* 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 "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)
{
asn1->offset += enc_len;
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;
}