Make the signatures of functions passed to "register_tap_listener()"
[obnox/wireshark/wip.git] / asn1.c
diff --git a/asn1.c b/asn1.c
index 6cc4f0c868413494d84b001ceb82c85610c73801..8edd59bcb81b9f7cdc123e5fbc16eea8c5c812de 100644 (file)
--- a/asn1.c
+++ b/asn1.c
@@ -1,7 +1,7 @@
 /* asn1.c
  * Routines for ASN.1 BER dissection
  *
- * $Id: asn1.c,v 1.8 2002/01/21 07:36:31 guy Exp $
+ * $Id$
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -29,7 +29,7 @@
 
 /*
  * MODULE INFORMATION
- * ------------------ 
+ * ------------------
  *     FILE     NAME:       g_asn1.c
  *     SYSTEM   NAME:       ASN1 Basic Encoding
  *     ORIGINAL AUTHOR(S):  Dirk Wisse
@@ -48,7 +48,7 @@
  *              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.
 # include "config.h"
 #endif
 
-#ifdef HAVE_SYS_TYPES_H
-# include <sys/types.h>
-#endif
+#include <stdio.h>
 
-#ifdef HAVE_WINSOCK_H
-#include <winsock.h>
-#endif
+#include <limits.h>
 
 #include <glib.h>
+
+#ifdef NEED_SNPRINTF_H
+# include "snprintf.h"
+#endif
+
 #include <epan/tvbuff.h>
 #include "asn1.h"
 
@@ -112,7 +113,7 @@ asn1_open(ASN1_SCK *asn1, tvbuff_t *tvb, int offset)
  * RETURNS:     void
  */
 
-void 
+void
 asn1_close(ASN1_SCK *asn1, int *offset)
 {
     *offset = asn1->offset;
@@ -137,22 +138,21 @@ asn1_octet_decode(ASN1_SCK *asn1, guchar *ch)
 }
 
 /*
- * NAME:        asn1_tag_decode
- * SYNOPSIS:    int asn1_tag_decode
+ * NAME:        asn1_tag_get
+ * SYNOPSIS:    int asn1_tag_get
  *                  (
  *                      ASN1_SCK *asn1,
  *                      guint    *tag
  *                  )
- * DESCRIPTION: Decodes a tag.
+ * DESCRIPTION: Decodes a tag number, combining it with existing tag bits.
  * RETURNS:     ASN1_ERR value (ASN1_ERR_NOERROR on success)
  */
-int
-asn1_tag_decode(ASN1_SCK *asn1, guint *tag)
+static int
+asn1_tag_get(ASN1_SCK *asn1, guint *tag)
 {
     int    ret;
     guchar ch;
 
-    *tag = 0;
     do {
        ret = asn1_octet_decode (asn1, &ch);
        if (ret != ASN1_ERR_NOERROR)
@@ -163,6 +163,23 @@ asn1_tag_decode(ASN1_SCK *asn1, guint *tag)
     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
@@ -181,6 +198,7 @@ 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;
@@ -195,6 +213,39 @@ asn1_id_decode(ASN1_SCK *asn1, guint *cls, guint *con, guint *tag)
     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
@@ -331,7 +382,7 @@ 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)
@@ -367,7 +418,17 @@ asn1_eoc_decode (ASN1_SCK *asn1, int eoc)
 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;
 }
 
@@ -377,7 +438,7 @@ asn1_null_decode ( ASN1_SCK *asn1, int enc_len)
  *                  (
  *                      ASN1_SCK *asn1,
  *                      int      enc_len,
- *                      gboolean *bool
+ *                      gboolean *boolean
  *                  )
  * DESCRIPTION: Decodes Boolean.
  *              Parameters:
@@ -387,7 +448,7 @@ asn1_null_decode ( ASN1_SCK *asn1, int enc_len)
  * RETURNS:     ASN1_ERR value (ASN1_ERR_NOERROR on success)
  */
 int
-asn1_bool_decode ( ASN1_SCK *asn1, int enc_len, gboolean *bool)
+asn1_bool_decode ( ASN1_SCK *asn1, int enc_len, gboolean *boolean)
 {
     int    ret;
     guchar ch;
@@ -397,7 +458,7 @@ asn1_bool_decode ( ASN1_SCK *asn1, int enc_len, gboolean *bool)
     ret = asn1_octet_decode (asn1, &ch);
     if (ret != ASN1_ERR_NOERROR)
         return ret;
-    *bool = ch ? TRUE : FALSE;
+    *boolean = ch ? TRUE : FALSE;
     return ASN1_ERR_NOERROR;
 }
 
@@ -505,7 +566,7 @@ done:
  * RETURNS:     ASN1_ERR value (ASN1_ERR_NOERROR on success)
  */
 int
-asn1_uint32_value_decode ( ASN1_SCK *asn1, int enc_len, guint *integer)
+asn1_uint32_value_decode ( ASN1_SCK *asn1, int enc_len, guint32 *integer)
 {
     int          ret;
     int          eoc;
@@ -591,35 +652,59 @@ done:
  *                  )
  * DESCRIPTION: Decodes Bit String.
  *              Parameters:
- *              asn1:   pointer to ASN1 socket.
- *              eoc: offset of end of encoding, or -1 if indefinite.
- *              bits:   pointer to begin of Bit String.
- *              size:   Size of Bit String in characters.
- *              len:    Length of Bit String in characters.
- *              unused: Number of unused bits in last character.
+ *              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 eoc, guchar **bits,
+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;
-    *bits = g_malloc(eoc - asn1->offset);
+
+    /*
+     * 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 *)bits++);
+        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;
 }
 
@@ -636,7 +721,8 @@ asn1_bits_decode ( ASN1_SCK *asn1, int eoc, guchar **bits,
  *              Parameters:
  *              asn1:    pointer to ASN1 socket.
  *              enc_len: length of encoding of value.
- *              octets:  pointer to variable we set to point to string.
+ *              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
@@ -646,8 +732,19 @@ asn1_string_value_decode ( ASN1_SCK *asn1, int enc_len, guchar **octets)
     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;
-    *octets = g_malloc (enc_len);
     ptr = *octets;
     while (asn1->offset < eoc) {
        ret = asn1_octet_decode (asn1, (guchar *)ptr++);
@@ -657,6 +754,7 @@ asn1_string_value_decode ( ASN1_SCK *asn1, int enc_len, guchar **octets)
            return ret;
        }
     }
+    *(guchar *)ptr = '\0';
     return ASN1_ERR_NOERROR;
 }
 
@@ -794,11 +892,23 @@ asn1_oid_value_decode ( ASN1_SCK *asn1, int enc_len, subid_t **oid, guint *len)
     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);
@@ -926,3 +1036,50 @@ 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;
+}