From 70abaaee79202531035a40c3e1061f503d28074f Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 25 Dec 2009 14:26:39 +0000 Subject: [PATCH] [egg] Implement asn validation, and some read functions. --- egg/egg-asn1x.c | 888 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- egg/egg-asn1x.h | 2 + 2 files changed, 843 insertions(+), 47 deletions(-) diff --git a/egg/egg-asn1x.c b/egg/egg-asn1x.c index 2fa30d8..0ef073f 100644 --- a/egg/egg-asn1x.c +++ b/egg/egg-asn1x.c @@ -95,7 +95,6 @@ typedef struct _Atlv { gint len; const guchar *buf; const guchar *end; - struct _Atlv *next; } Atlv; typedef struct _Anode { @@ -109,6 +108,7 @@ typedef struct _Anode { /* Forward Declarations */ static gboolean anode_decode_anything (GNode*, Atlv*); static gboolean anode_decode_anything_for_flags (GNode *, Atlv*, gint); +static gboolean anode_validate_anything (GNode*); static GNode* anode_new (const ASN1_ARRAY_TYPE *def) @@ -124,7 +124,7 @@ anode_clear (GNode *node) { Anode *an = node->data; if (an->data); - g_slice_free_chain (Atlv, an->data, next); + g_slice_free (Atlv, an->data); an->data = NULL; } @@ -176,20 +176,21 @@ anode_def_type_is_real (GNode *node) switch (anode_def_type (node)) { case TYPE_INTEGER: case TYPE_BOOLEAN: - case TYPE_SEQUENCE: case TYPE_BIT_STRING: case TYPE_OCTET_STRING: - case TYPE_SEQUENCE_OF: case TYPE_OBJECT_ID: - case TYPE_ANY: - case TYPE_SET: - case TYPE_SET_OF: case TYPE_TIME: - case TYPE_CHOICE: case TYPE_NULL: case TYPE_ENUMERATED: case TYPE_GENERALSTRING: return TRUE; + case TYPE_SEQUENCE: + case TYPE_SEQUENCE_OF: + case TYPE_ANY: + case TYPE_SET: + case TYPE_SET_OF: + case TYPE_CHOICE: + return TRUE; case TYPE_CONSTANT: case TYPE_IDENTIFIER: case TYPE_TAG: @@ -227,18 +228,31 @@ anode_def_value (GNode *node) return an->def->value; } -static gulong -anode_def_value_as_ulong (GNode *node) +static glong +anode_def_value_as_long (GNode *node) { const gchar* value; gchar *end = NULL; - gulong ulval; + gulong lval; value = anode_def_value (node); g_return_val_if_fail (value, G_MAXULONG); - ulval = strtoul (value, &end, 10); + lval = strtol (value, &end, 10); g_return_val_if_fail (end && !end[0], G_MAXULONG); - return ulval; + return lval; +} + +static GNode* +anode_child_with_name (GNode *node, const gchar *name) +{ + GNode *child; + + for (child = node->children; child; child = child->next) { + if (g_str_equal (name, anode_def_name (child))) + return child; + } + + return NULL; } static GNode* @@ -255,6 +269,16 @@ anode_child_with_type (GNode *node, gint type) } static GNode* +anode_next_with_type (GNode *node, gint type) +{ + for (node = node->next; node; node = node->next) { + if (anode_def_type (node) == type) + return node; + } + return NULL; +} + +static GNode* anode_child_with_real_type (GNode *node) { GNode *child; @@ -278,22 +302,50 @@ anode_next_with_real_type (GNode *node) return NULL; } +static gboolean +anode_def_size_value (GNode *node, const gchar *text, gulong *value) +{ + gchar *end = NULL; + + if (text == NULL) { + *value = 0; + return FALSE; + } else if (g_str_equal (text, "MAX")) { + *value = G_MAXULONG; + return TRUE; + } else if (g_ascii_isalpha (text[0])) { + node = anode_child_with_name (node, text); + g_return_val_if_fail (node, FALSE); + return anode_def_size_value (node, anode_def_value (node), value); + } + + *value = strtoul (text, &end, 10); + g_return_val_if_fail (end && !end[0], FALSE); + return TRUE; +} + static void -anode_add_tlv_data (GNode *node, Atlv *tlv) +anode_set_tlv_data (GNode *node, Atlv *tlv) { Anode *an = node->data; - Atlv **last = &an->data; + g_assert (!an->data); g_assert (tlv->len >= 0); - while (*last) - last = &(*last)->next; - *last = g_slice_new0 (Atlv); - memcpy (*last, tlv, sizeof (tlv)); + an->data = g_slice_new0 (Atlv); + memcpy (an->data, tlv, sizeof (Atlv)); +} + +static Atlv* +anode_get_tlv_data (GNode *node) +{ + Anode *an = node->data; + return an->data; } static gulong anode_encode_tag_for_flags (GNode *node, gint flags) { GNode *child; + gulong tag; g_return_val_if_fail (anode_def_type_is_real (node), G_MAXULONG); @@ -301,7 +353,9 @@ anode_encode_tag_for_flags (GNode *node, gint flags) if (flags & FLAG_TAG) { child = anode_child_with_type (node, TYPE_TAG); g_return_val_if_fail (child, G_MAXULONG); - return anode_def_value_as_ulong (child); + tag = anode_def_value_as_long (child); + g_return_val_if_fail (tag >= 0, G_MAXULONG); + return tag; } /* A tag from the universal set */ @@ -488,28 +542,16 @@ anode_decode_tlv_for_contents (Atlv *outer, gboolean first, Atlv *tlv) } static gboolean -anode_decode_tlv_ensure_length (Atlv *tlv) -{ - if (tlv->len >= 0) - return TRUE; - - if (!anode_decode_indefinite_len (tlv->buf + tlv->off, tlv->end, &tlv->len)) - return FALSE; - - g_assert (tlv->len >= 0); - tlv->end = tlv->buf + tlv->off + tlv->len; - return TRUE; -} - -static gboolean anode_decode_choice (GNode *node, Atlv *tlv) { GNode *child; for (child = anode_child_with_real_type (node); child; child = anode_next_with_real_type (child)) { - if (anode_decode_anything (child, tlv)) + if (anode_decode_anything (child, tlv)) { + anode_set_tlv_data (node, tlv); return TRUE; + } } return FALSE; @@ -527,11 +569,26 @@ anode_decode_struct_string (GNode *node, Atlv *outer) for (i = 0; TRUE; ++i) { if (!anode_decode_tlv_for_contents (outer, i == 0, &tlv)) return FALSE; - anode_add_tlv_data (node, &tlv); + if (tlv.tag != outer->tag) + return FALSE; outer->len = (tlv.end - outer->buf) - outer->off; } g_assert (outer->len >= 0); + anode_set_tlv_data (node, outer); + return TRUE; +} + +static gboolean +anode_decode_struct_any (GNode *node, Atlv *tlv) +{ + if (tlv->len < 0) { + if (!anode_decode_indefinite_len (tlv->buf + tlv->off, tlv->end, &tlv->len)) + return FALSE; + tlv->end = tlv->buf + tlv->off + tlv->len; + } + + anode_set_tlv_data (node, tlv); return TRUE; } @@ -564,6 +621,7 @@ anode_decode_sequence_or_set (GNode *node, Atlv *outer) } g_assert (outer->len >= 0); + anode_set_tlv_data (node, outer); return TRUE; } @@ -601,6 +659,7 @@ anode_decode_sequence_or_set_of (GNode *node, Atlv *outer) } g_assert (outer->len >= 0); + anode_set_tlv_data (node, outer); return TRUE; } @@ -626,13 +685,14 @@ anode_decode_primitive (GNode *node, Atlv *tlv, gint flags) case TYPE_NULL: case TYPE_GENERALSTRING: case TYPE_TIME: - anode_add_tlv_data (node, tlv); + anode_set_tlv_data (node, tlv); return TRUE; /* Transparent types */ case TYPE_ANY: - anode_add_tlv_data (node, tlv); + anode_set_tlv_data (node, tlv); return TRUE; + case TYPE_CHOICE: return anode_decode_choice (node, tlv); @@ -664,22 +724,20 @@ anode_decode_structured (GNode *node, Atlv *tlv, gint flags) flags &= ~FLAG_TAG; if (!anode_decode_anything_for_flags (node, &ctlv, flags)) return FALSE; - tlv->len = ctlv.off + ctlv.len; + g_assert (ctlv.end >= ctlv.buf); + tlv->len = ctlv.end - ctlv.buf; /* Other structured types */ } else { switch (anode_def_type (node)) { case TYPE_ANY: - if (!anode_decode_tlv_ensure_length (tlv)) + if (!anode_decode_struct_any (node, tlv)) return FALSE; - anode_add_tlv_data (node, tlv); break; - case TYPE_CHOICE: if (!anode_decode_choice (node, tlv)) return FALSE; break; - case TYPE_GENERALSTRING: case TYPE_OCTET_STRING: if (!anode_decode_struct_string (node, tlv)) @@ -709,12 +767,11 @@ anode_decode_structured (GNode *node, Atlv *tlv, gint flags) return FALSE; if (!anode_check_indefinite_end (cls, tag, len)) return FALSE; - tlv->len += off; - end = tlv->buf + tlv->off + tlv->len; + end = tlv->buf + tlv->off + tlv->len + off; } /* A structure must be filled up, no stuff ignored */ - if (tlv->buf + tlv->off + tlv->len != end) + if (tlv->buf + tlv->off + tlv->len + off != end) return FALSE; tlv->end = end; @@ -779,12 +836,724 @@ egg_asn1x_decode (GNode *asn, gconstpointer data, gsize n_data) if (!anode_decode_anything (asn, &tlv)) return FALSE; - if (tlv.buf + tlv.off + tlv.len != tlv.end) + return egg_asn1x_validate (asn); +} + +/* ----------------------------------------------------------------------------------- + * READing + */ + +static int +atoin (const char *p, int digits) +{ + int ret = 0, base = 1; + while(--digits >= 0) { + if (p[digits] < '0' || p[digits] > '9') + return -1; + ret += (p[digits] - '0') * base; + base *= 10; + } + return ret; +} + +static int +two_to_four_digit_year (int year) +{ + time_t now; + struct tm tm; + int century, current; + + g_return_val_if_fail (year >= 0 && year <= 99, -1); + + /* Get the current year */ + now = time (NULL); + g_return_val_if_fail (now >= 0, -1); + if (!gmtime_r (&now, &tm)) + g_return_val_if_reached (-1); + + current = (tm.tm_year % 100); + century = (tm.tm_year + 1900) - current; + + /* + * Check if it's within 40 years before the + * current date. + */ + if (current < 40) { + if (year < current) + return century + year; + if (year > 100 - (40 - current)) + return (century - 100) + year; + } else { + if (year < current && year > (current - 40)) + return century + year; + } + + /* + * If it's after then adjust for overflows to + * the next century. + */ + if (year < current) + return century + 100 + year; + else + return century + year; +} + +#ifndef HAVE_TIMEGM +time_t timegm(struct tm *t) +{ + time_t tl, tb; + struct tm *tg; + + tl = mktime (t); + if (tl == -1) + { + t->tm_hour--; + tl = mktime (t); + if (tl == -1) + return -1; /* can't deal with output from strptime */ + tl += 3600; + } + tg = gmtime (&tl); + tg->tm_isdst = 0; + tb = mktime (tg); + if (tb == -1) + { + tg->tm_hour--; + tb = mktime (tg); + if (tb == -1) + return -1; /* can't deal with output from gmtime */ + tb += 3600; + } + return (tl - (tb - tl)); +} +#endif // NOT_HAVE_TIMEGM + +static gboolean +parse_utc_time (const gchar *time, gsize n_time, + struct tm* when, gint *offset) +{ + const char *p, *e; + int year; + + g_assert (when); + g_assert (time); + g_assert (offset); + + /* YYMMDDhhmmss.ffff Z | +0000 */ + if (n_time < 6 || n_time >= 28) + return FALSE; + + /* Reset everything to default legal values */ + memset (when, 0, sizeof (*when)); + *offset = 0; + when->tm_mday = 1; + + /* Select the digits part of it */ + p = time; + for (e = p; *e >= '0' && *e <= '9'; ++e); + + if (p + 2 <= e) { + year = atoin (p, 2); + p += 2; + + /* + * 40 years in the past is our century. 60 years + * in the future is the next century. + */ + when->tm_year = two_to_four_digit_year (year) - 1900; + } + if (p + 2 <= e) { + when->tm_mon = atoin (p, 2) - 1; + p += 2; + } + if (p + 2 <= e) { + when->tm_mday = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_hour = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_min = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_sec = atoin (p, 2); + p += 2; + } + + if (when->tm_year < 0 || when->tm_year > 9999 || + when->tm_mon < 0 || when->tm_mon > 11 || + when->tm_mday < 1 || when->tm_mday > 31 || + when->tm_hour < 0 || when->tm_hour > 23 || + when->tm_min < 0 || when->tm_min > 59 || + when->tm_sec < 0 || when->tm_sec > 59) + return FALSE; + + /* Make sure all that got parsed */ + if (p != e) + return FALSE; + + /* Now the remaining optional stuff */ + e = time + n_time; + + /* See if there's a fraction, and discard it if so */ + if (p < e && *p == '.' && p + 5 <= e) + p += 5; + + /* See if it's UTC */ + if (p < e && *p == 'Z') { + p += 1; + + /* See if it has a timezone */ + } else if ((*p == '-' || *p == '+') && p + 3 <= e) { + int off, neg; + + neg = *p == '-'; + ++p; + + off = atoin (p, 2) * 3600; + if (off < 0 || off > 86400) + return -1; + p += 2; + + if (p + 2 <= e) { + off += atoin (p, 2) * 60; + p += 2; + } + + /* Use TZ offset */ + if (neg) + *offset = 0 - off; + else + *offset = off; + } + + /* Make sure everything got parsed */ + if (p != e) + return FALSE; + + return TRUE; +} + +static gboolean +parse_general_time (const gchar *time, gsize n_time, + struct tm* when, gint *offset) +{ + const char *p, *e; + + g_assert (time); + g_assert (when); + g_assert (offset); + + /* YYYYMMDDhhmmss.ffff Z | +0000 */ + if (n_time < 8 || n_time >= 30) + return FALSE; + + /* Reset everything to default legal values */ + memset (when, 0, sizeof (*when)); + *offset = 0; + when->tm_mday = 1; + + /* Select the digits part of it */ + p = time; + for (e = p; *e >= '0' && *e <= '9'; ++e); + + if (p + 4 <= e) { + when->tm_year = atoin (p, 4) - 1900; + p += 4; + } + if (p + 2 <= e) { + when->tm_mon = atoin (p, 2) - 1; + p += 2; + } + if (p + 2 <= e) { + when->tm_mday = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_hour = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_min = atoin (p, 2); + p += 2; + } + if (p + 2 <= e) { + when->tm_sec = atoin (p, 2); + p += 2; + } + + if (when->tm_year < 0 || when->tm_year > 9999 || + when->tm_mon < 0 || when->tm_mon > 11 || + when->tm_mday < 1 || when->tm_mday > 31 || + when->tm_hour < 0 || when->tm_hour > 23 || + when->tm_min < 0 || when->tm_min > 59 || + when->tm_sec < 0 || when->tm_sec > 59) + return FALSE; + + /* Make sure all that got parsed */ + if (p != e) + return FALSE; + + /* Now the remaining optional stuff */ + e = time + n_time; + + /* See if there's a fraction, and discard it if so */ + if (p < e && *p == '.' && p + 5 <= e) + p += 5; + + /* See if it's UTC */ + if (p < e && *p == 'Z') { + p += 1; + + /* See if it has a timezone */ + } else if ((*p == '-' || *p == '+') && p + 3 <= e) { + int off, neg; + + neg = *p == '-'; + ++p; + + off = atoin (p, 2) * 3600; + if (off < 0 || off > 86400) + return -1; + p += 2; + + if (p + 2 <= e) { + off += atoin (p, 2) * 60; + p += 2; + } + + /* Use TZ offset */ + if (neg) + *offset = 0 - off; + else + *offset = off; + } + + /* Make sure everything got parsed */ + if (p != e) + return FALSE; + + return TRUE; +} + +static gboolean +anode_read_time (GNode *node, Atlv *tlv, time_t *value) +{ + const gchar *data; + gboolean ret; + struct tm when; + gint offset; + gint flags; + + flags = anode_def_flags (node); + data = (gchar*)(tlv->buf + tlv->off); + + if (flags & FLAG_GENERALIZED) + ret = parse_general_time (data, tlv->len, &when, &offset); + else if (flags & FLAG_UTC) + ret = parse_utc_time (data, tlv->len, &when, &offset); + else + g_return_val_if_reached (FALSE); + + if (!ret) + return FALSE; + + /* In order to work with 32 bit time_t. */ + if (sizeof (time_t) <= 4 && when.tm_year >= 2038) { + *value = (time_t)2145914603; /* 2037-12-31 23:23:23 */ + + /* Convert to seconds since epoch */ + } else { + *value = timegm (&when); + if (*time < 0) + return FALSE; + *value += offset; + } + + return TRUE; +} + +static gboolean +anode_read_integer_as_long (GNode *node, Atlv *tlv, glong *value) +{ + const guchar *p; + gsize k; + + if (tlv->len < 1 || tlv->len > 4) + return FALSE; + + p = tlv->buf + tlv->off; + *value = 0; + for (k = 0; k < tlv->len; ++k) + *value |= p[k] << (8 * ((tlv->len - 1) - k)); + + return TRUE; +} + +static gboolean +anode_read_string (GNode *node, Atlv *tlv, gpointer value, gsize *n_value) +{ + Atlv ctlv; + guchar *buf; + gint n_buf; + gint i; + + g_assert (tlv); + g_assert (n_value); + + buf = value; + n_buf = *n_value; + + /* Is it constructed ? */ + if (tlv->cls & ASN1_CLASS_STRUCTURED) { + *n_value = 0; + for (i = 0; TRUE; ++i) { + if (!anode_decode_tlv_for_contents (tlv, i == 0, &ctlv)) + return FALSE; + if (ctlv.off == 0) + break; + if (ctlv.cls & ASN1_CLASS_STRUCTURED) + return FALSE; + *n_value += ctlv.len; + if (buf) { + if (n_buf >= ctlv.len) + memcpy (buf, ctlv.buf + ctlv.off, ctlv.len); + buf += ctlv.len; + n_buf -= ctlv.len; + } + } + if (n_buf < 0) + return FALSE; + + /* Primitive, just return the contents */ + } else { + *n_value = tlv->len; + if (buf) { + if (n_buf < tlv->len) + return FALSE; + memcpy (buf, tlv->buf + tlv->off, tlv->len); + } + } + + return TRUE; +} + +/* ----------------------------------------------------------------------------------- + * VALIDATION + */ + +static gboolean +anode_validate_size (GNode *node, gulong length) +{ + GNode *size; + gulong value1 = 0; + gulong value2 = G_MAXULONG; + + if (anode_def_flags (node) & FLAG_SIZE) { + size = anode_child_with_type (node, TYPE_SIZE); + g_return_val_if_fail (size, FALSE); + if (!anode_def_size_value (size, anode_def_value (size), &value1)) + g_return_val_if_reached (FALSE); + if (anode_def_flags (size) & FLAG_MIN_MAX) { + if (!anode_def_size_value (size, anode_def_name (size), &value2)) + g_return_val_if_reached (FALSE); + if (length < value1 || length >= value2) + return FALSE; + } else { + if (length != value1) + return FALSE; + } + } + + return TRUE; +} + +static gboolean +anode_validate_integer (GNode *node, Atlv *tlv) +{ + glong value, check; + gboolean found; + GNode *child; + gint flags; + + g_assert (tlv); + + /* Integers must be at least one byte long */ + if (tlv->len <= 0) + return FALSE; + + flags = anode_def_flags (node); + if (flags & FLAG_LIST) { + /* Parse out the value, we only support small integers*/ + if (!anode_read_integer_as_long (node, tlv, &value)) + return FALSE; + + /* Look through the list of constants */ + found = FALSE; + for (child = anode_child_with_type (node, TYPE_CONSTANT); + child; child = anode_next_with_type (child, TYPE_CONSTANT)) { + check = anode_def_value_as_long (child); + g_return_val_if_fail (check != G_MAXULONG, FALSE); + if (check == value) { + found = TRUE; + break; + } + } + + if (!found) + return FALSE; + } + + return TRUE; +} + +static gboolean +anode_validate_enumerated (GNode *node, Atlv *tlv) +{ + g_assert (tlv); + + if (!anode_validate_integer (node, tlv)) + return FALSE; + g_assert (tlv->len); /* Checked above */ + /* Enumerated must be positive */ + if (tlv->buf[tlv->off] & 0x80) + return FALSE; + return TRUE; +} + +static gboolean +anode_validate_boolean (GNode *node, Atlv *tlv) +{ + g_assert (tlv); + + /* Must one byte, and zero or all ones */ + if (tlv->len != 1) + return FALSE; + if (tlv->buf[tlv->off] != 0x00 && tlv->buf[tlv->off] != 0xFF) + return FALSE; + return TRUE; +} + +static gboolean +anode_validate_bit_string (GNode *node, Atlv *tlv) +{ + guchar empty, mask; + g_assert (tlv); + + /* At least two bytes in length */ + if (tlv->len < 2) return FALSE; + /* First byte is the number of free bits at end */ + empty = tlv->buf[tlv->off]; + if (empty > 7) + return FALSE; + /* Free octets at end must be zero */ + mask = 0xFF >> (8 - empty); + if (tlv->buf[tlv->off + tlv->len - 1] & mask) + return FALSE; + return TRUE; +} + +static gboolean +anode_validate_string (GNode *node, Atlv *tlv) +{ + gsize length; + + if (!anode_read_string (node, tlv, NULL, &length)) + return FALSE; + + return anode_validate_size (node, (gulong)length); +} + +static gboolean +anode_validate_object_id (GNode *node, Atlv *tlv) +{ + const guchar *p; + gboolean lead; + guint val, pval; + gint k; + + g_assert (tlv); + if (tlv->len <= 0) + return FALSE; + p = tlv->buf + tlv->off; + + /* TODO: Validate first byte? */ + for (k = 1, lead = 1, val = 0, pval = 0; k < tlv->len; ++k) { + /* X.690: the leading byte must never be 0x80 */ + if (lead && p[k] == 0x80) + return FALSE; + val = val << 7; + val |= p[k] & 0x7F; + /* Check for wrap around */ + if (val < pval) + return FALSE; + pval = val; + if (!(p[k] & 0x80)) { + pval = val = 0; + lead = 1; + } + } + + return TRUE; +} + +static gboolean +anode_validate_null (GNode *node, Atlv *tlv) +{ + g_assert (tlv); + return (tlv->len == 0); +} + +static gboolean +anode_validate_time (GNode *node, Atlv *tlv) +{ + glong time; + return anode_read_time (node, tlv, &time); +} + +static gboolean +anode_validate_choice (GNode *node) +{ + gboolean have = FALSE; + GNode *child; + + /* One and only one of the children must be set */ + for (child = anode_child_with_real_type (node); + child; child = anode_next_with_real_type (child)) { + if (anode_get_tlv_data (child)) { + if (have) + return FALSE; + have = TRUE; + if (!anode_validate_anything (child)) + return FALSE; + } + } + + return have; +} + +static gboolean +anode_validate_sequence_or_set (GNode *node) +{ + GNode *child; + + /* All of the children must validate properly */ + for (child = anode_child_with_real_type (node); + child; child = anode_next_with_real_type (child)) { + if (!anode_validate_anything (child)) + return FALSE; + } return TRUE; } +static gboolean +anode_validate_sequence_or_set_of (GNode *node) +{ + GNode *child; + Atlv *tlv; + gulong tag; + gulong count; + + /* The first one must be empty */ + child = anode_child_with_real_type (node); + g_return_val_if_fail (child, FALSE); + g_return_val_if_fail (!anode_get_tlv_data (child), FALSE); + + tag = anode_encode_tag (child); + + /* All of the other children must validate properly */ + for (child = anode_next_with_real_type (child); + child; child = anode_next_with_real_type (child)) { + if (!anode_validate_anything (child)) + return FALSE; + + /* Must have same tag as the top */ + if (tag != G_MAXULONG) { + tlv = anode_get_tlv_data (child); + g_return_val_if_fail (tlv, FALSE); + if (tlv->tag != tag) + return FALSE; + } + + ++count; + } + + return anode_validate_size (node, count); +} + +static gboolean +anode_validate_anything (GNode *node) +{ + Atlv *tlv; + gint type; + + type = anode_def_type (node); + tlv = anode_get_tlv_data (node); + + if (!tlv) { + if (anode_def_flags (node) & FLAG_OPTION) + return TRUE; + return FALSE; + } + + switch (type) { + + /* The primitive value types */ + case TYPE_INTEGER: + return anode_validate_integer (node, tlv); + case TYPE_ENUMERATED: + return anode_validate_enumerated (node, tlv); + case TYPE_BOOLEAN: + return anode_validate_boolean (node, tlv); + case TYPE_BIT_STRING: + return anode_validate_bit_string (node, tlv); + case TYPE_OCTET_STRING: + return anode_validate_string (node, tlv); + case TYPE_OBJECT_ID: + return anode_validate_object_id (node, tlv); + case TYPE_NULL: + return anode_validate_null (node, tlv); + case TYPE_GENERALSTRING: + return anode_validate_string (node, tlv); + case TYPE_TIME: + return anode_validate_time (node, tlv); + + /* Transparent types */ + case TYPE_ANY: + return TRUE; + case TYPE_CHOICE: + return anode_validate_choice (node); + + /* Structured types */ + case TYPE_SEQUENCE: + case TYPE_SET: + return anode_validate_sequence_or_set (node); + + case TYPE_SEQUENCE_OF: + case TYPE_SET_OF: + return anode_validate_sequence_or_set_of (node); + + default: + g_return_val_if_reached (FALSE); + } +} + +gboolean +egg_asn1x_validate (GNode *asn) +{ + g_return_val_if_fail (asn, FALSE); + return anode_validate_anything (asn); +} + +/* ----------------------------------------------------------------------------------- + * TREE CREATION + */ + static gint compare_nodes_by_tag (gconstpointer a, gconstpointer b) { @@ -814,6 +1583,20 @@ join_each_child (GNode *child, gpointer data) g_node_append (node, child); } +static GNode* +lookup_type_node (const ASN1_ARRAY_TYPE *defs, const gchar *identifier, gint type) +{ + /* Find the one we're interested in */ + while (defs && (defs->value || defs->type || defs->name)) { + if ((defs->type & 0xFF) == type && + defs->name && g_str_equal (identifier, defs->name)) + return anode_new (defs); + ++defs; + } + + return NULL; +} + static gboolean traverse_and_prepare (GNode *node, gpointer data) { @@ -841,6 +1624,17 @@ traverse_and_prepare (GNode *node, gpointer data) egg_asn1x_destroy (join); } + /* Lookup the max set size */ + if (anode_def_type (node) == TYPE_SIZE) { + identifier = anode_def_name (node); + if (identifier && !g_str_equal (identifier, "MAX") && + g_ascii_isalpha (identifier[0])) { + join = lookup_type_node (defs, identifier, TYPE_INTEGER); + g_return_val_if_fail (join, TRUE); + g_node_append (node, join); + } + } + /* Sort the children of any sets */ if (anode_def_type (node) == TYPE_SET) { child = node->children; diff --git a/egg/egg-asn1x.h b/egg/egg-asn1x.h index 43c72b7..ec1bbcc 100644 --- a/egg/egg-asn1x.h +++ b/egg/egg-asn1x.h @@ -45,6 +45,8 @@ gboolean egg_asn1x_decode (GNode *asn, gconstpointer data, gsize n_data); +gboolean egg_asn1x_validate (GNode *asn); + gpointer egg_asn1x_encode (GNode *asn, EggAllocator allocator, gsize *n_data); -- 2.7.4