Ethereal-dev: [Ethereal-dev] ASN1.C and Packet-ACSE.c and Packet-pres.c

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Herbert Falk <herb@xxxxxxxxxxxx>
Date: Fri, 10 Sep 2004 08:42:49 -0400
A little history:

The ISO-9506 (MMS) protocol is an ASN1 BER encoded protocol that is emerging for use within the electric utility industry as IEC 61850. I started with Ethereal 0.10.3 and immediately found that ASN1.C did not handle indefinite length encodings.I have migrated to 0.10.6 and have created helper functions that will be submitted to Ethereal that solve this problem and have recently figured out how to merge these changes into ASN1.c so that all ASN1.C based disectors can take advantage of the indefinite length fix with just a relink to the new ASN1.C module.

I would like to request a capture file from somebody whose dissector is ASN1.C based that has indefinite length so I can test if the changes are truly transparent. I have attached the un-tested ASN1.C module in case anybody else wants to give it a go. Once further testing is complete, I intend to submit this module to Ethereal. The changes are in the tag_get and the length_decode functions marked with #if 1 and #if 0. * So please help....*.

IEC 61850 is specifying the use of X,509 certificates for Application Level Authentication within ACSE. I have extended packet-acse.c to decode this and the ACSE Authentication values as specified by ACSE and the ACSE Mechanism Name. In doing so, I found that packet-acse.c and packet-pres.c make limited use of ASN1.c. Thus they are vulnerable and will break if indefinite length encodings are encountered. *The question is, should these modules be re-written to make use of ASN1.C* (I know that there is now a packet-BER.c, but I have not used its functions nor determined how to fix its indefinite length issues if there are any). Since I am not the original author of either of these modules, feedback and/or assistance would be welcomed.

Please feel free to respond  directly to me and/or Ethereal-Dev.c.





--
Herbert Falk
SISCO
6605 19-1/2 Mile Road
Sterling Heights, MI 48314
Ph: 586-254-0020
Fx:  586-254-0053

NOTICE: This communication may contain privileged or other confidential
information. If you are not the intended recipient, or believe that you
have  received this communication in error, please do not print, copy,
retransmit,  disseminate, or otherwise use the information. Also, please indicate to
the sender that you have received this communication in error, and delete the
copy you received. Thank you.

/* asn1.c
 * Routines for ASN.1 BER dissection
 *
 * $Id: asn1.c 11400 2004-07-18 00:24:25Z guy $
 *
 * Ethereal - Network traffic analyzer
 * By Gerald Combs <gerald@xxxxxxxxxxxx>
 *
 * 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.
 */


/* modified by Herbert Falk on 9/3 to allow all asn1 decoders to handle indefinite length*/

#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);

#if 1
    if(*tag==0x00)
	  return(asn1_tag_get(asn1,tag));
	else
#endif
      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)
 */
#if 0
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;
}

#else

/*function to calculate the actual indefinite length		*/
static void
calc_indef_length (ASN1_SCK *asn1,guint *indef_len)
{
ASN1_SCK asn1_indef;
guint tag;
guint indef_level = 1;
guint internal_length;
guint initial_offset = asn1->offset;
gboolean def;
guchar *myoctets;



	memcpy (&asn1_indef, asn1, sizeof(ASN1_SCK));			/*copy and save the information */

	/*now need to go looking for the end	*/

    while(indef_level>0)
	{
      asn1h_tag_get(&asn1_indef, &tag);
      asn1_length_decode (&asn1_indef, &def, &internal_length);
      if(def==FALSE)
	    ++indef_level;
	  else
	   {		/*we have a definite length and need to check if it is the end of indef	*/
	   if ( (tag == 0x0)  && (internal_length == 0x0) )	/*then we have reached the end	*/
	     --indef_level;
	   else
	     {
		 /*need to skip a number of octets					*/
   	     asn1_string_value_decode (&asn1_indef, internal_length, &myoctets);
		 g_free(myoctets);
         }
      }
  	}
	*indef_len = asn1_indef.offset - initial_offset -2;
}

int
asn1_length_decode(ASN1_SCK *asn1, gboolean *def, guint *len)
{
    int    ret;
    guchar ch, cnt;
    guint 	indef_len=0;

    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--;
            }
        }
    }

	/*check if indefinite length, if so, then find the actual length 				*/
	/* note that the length will be 2 octets less than the actual number of octets	*/
	/*this is so that recursive structures can be handled propely					*/
  if (*def == FALSE)
   {
	calc_indef_length (asn1,&indef_len);
	*len = indef_len - 2;
   }

	return ASN1_ERR_NOERROR;
}

#endif
/*
 * 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;
}