diff options
Diffstat (limited to 'sbin/isakmpd/asn.c')
-rw-r--r-- | sbin/isakmpd/asn.c | 1155 |
1 files changed, 1155 insertions, 0 deletions
diff --git a/sbin/isakmpd/asn.c b/sbin/isakmpd/asn.c new file mode 100644 index 00000000000..fb6af6cec83 --- /dev/null +++ b/sbin/isakmpd/asn.c @@ -0,0 +1,1155 @@ +/* $OpenBSD: asn.c,v 1.4 1998/11/16 21:07:14 niklas Exp $ */ + +/* + * Copyright (c) 1998 Niels Provos. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Ericsson Radio Systems. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + * This code was written under funding by Ericsson Radio Systems. + */ + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <gmp.h> +#include <unistd.h> + +#include "log.h" +#include "asn.h" +#include "gmp_util.h" + +struct asn_handler table[] = { + {TAG_INTEGER, asn_free_integer, + asn_get_encoded_len_integer, asn_decode_integer, asn_encode_integer}, + {TAG_OBJECTID, asn_free_objectid, + asn_get_encoded_len_objectid, asn_decode_objectid, asn_encode_objectid}, + {TAG_SEQUENCE, asn_free_sequence, + asn_get_encoded_len_sequence, asn_decode_sequence, asn_encode_sequence}, + {TAG_SET, asn_free_sequence, + asn_get_encoded_len_sequence, asn_decode_sequence, asn_encode_sequence}, + {TAG_UTCTIME, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_BITSTRING, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_OCTETSTRING, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_BOOL, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_PRINTSTRING, asn_free_string, + asn_get_encoded_len_string, asn_decode_string, asn_encode_string}, + {TAG_RAW, asn_free_raw, + asn_get_encoded_len_raw, asn_decode_raw, asn_encode_raw}, + {TAG_NULL, asn_free_null, + asn_get_encoded_len_null, asn_decode_null, asn_encode_null}, + {TAG_ANY, asn_free_null, + NULL, asn_decode_any, NULL}, + {TAG_STOP, NULL, NULL, NULL, NULL} +}; + +int +asn_get_from_file (char *name, u_int8_t **asn, u_int32_t *asnlen) +{ + int fd, res = 0; + struct stat st; + + if (stat (name, &st) == -1) + { + log_error ("asn_get_from_file: failed to state %s", name); + return 0; + } + + *asnlen = st.st_size; + + if ((fd = open (name, O_RDONLY)) == -1) + { + log_error ("asn_get_from_file: failed to open %s", name); + return 0; + } + + if ((*asn = malloc (st.st_size)) == NULL) + { + log_print ("asn_get_from_file: out of memory"); + res = 0; + goto done; + } + + if (read (fd, *asn, st.st_size) != st.st_size || + asn_get_len (*asn) != *asnlen) + { + log_print ("x509_asn_obtain: asn file ended early"); + free (*asn); + res = 0; + goto done; + } + + res = 1; + + done: + close (fd); + + return res; +} + +struct norm_type * +asn_template_clone (struct norm_type *obj, int constructed) +{ + struct norm_type *p; + u_int32_t i; + + if (!constructed) + { + p = malloc (sizeof (struct norm_type)); + if (p == NULL) + return NULL; + + memcpy (p, obj, sizeof (struct norm_type)); + + obj = p; + } + + if (obj->type != TAG_SEQUENCE && obj->type != TAG_SET) + { + obj->len = 0; + obj->data = NULL; + } + else if (obj->type == TAG_SEQUENCE || obj->type == TAG_SET) + { + p = obj; + obj = obj->data; + i = 0; + while (obj[i++].type != TAG_STOP); + + p->data = malloc (i * sizeof (struct norm_type)); + if (p->data == NULL) + return NULL; + + memcpy (p->data, obj, i * sizeof (struct norm_type)); + obj = p->data; + + i = 0; + while (obj[i].type != TAG_STOP) + { + obj[i].len = 0; + if (asn_template_clone (&obj[i], 1) == NULL) + return NULL; + + i++; + } + } + + return obj; +} + +/* Associates a human readable name to an OBJECT IDENTIFIER */ + +char * +asn_parse_objectid (struct asn_objectid *table, char *id) +{ + u_int32_t len = 0; + char *p = NULL; + static char buf[LINE_MAX]; + + if (id == NULL) + return NULL; + + while (table->name != NULL) + { + if (!strcmp (table->objectid, id)) + return table->name; + if (!strncmp (table->objectid, id, strlen (table->objectid)) && + strlen (table->objectid) > len) + { + len = strlen (table->objectid); + p = table->name; + } + + table++; + } + + if (len == 0) + return NULL; + + strncpy (buf, p, sizeof (buf) - 1); + buf[sizeof (buf) - 1] = 0; + strncat (buf + strlen (buf), id + len, sizeof (buf) -1 - strlen (buf)); + buf[sizeof (buf) - 1] = 0; + + return buf; +} + +/* Retrieves the pointer to a data type referenced by the path name */ + +struct norm_type * +asn_decompose (char *path, struct norm_type *obj) +{ + char *p, *p2, *tmp; + int counter; + + if (!strcasecmp (path, obj->name)) + return obj->data; + + p = path = strdup (path); + p2 = strsep (&p, "."); + + if (strcasecmp (p2, obj->name) || p == NULL) + goto fail; + + while (p != NULL) + { + obj = obj->data; + if (obj == NULL) + break; + + p2 = strsep (&p, "."); + + /* + * For SEQUENCE OF or SET OF, we want to be able to say + * AttributeValueAssertion[1] for the 2nd value. + */ + tmp = strchr (p2, '['); + if (tmp != NULL) + { + counter = atoi (tmp+1); + *tmp = 0; + } + else + counter = 0; + + /* Find the Tag */ + while (obj->type != TAG_STOP) + { + if (!strcasecmp (p2, obj->name) && counter-- == 0) + break; + obj++; + } + + if (obj->type == TAG_STOP) + goto fail; + + if (p == NULL) + goto done; + + if (obj->type != TAG_SEQUENCE && obj->type != TAG_SET) + goto fail; + } + + done: + free (path); + return obj; + + fail: + free (path); + return NULL; +} + +/* Gets an entry from the ASN.1 tag switch table */ + +struct asn_handler * +asn_get (enum asn_tags type) +{ + struct asn_handler *h = table; + + while (h->type != TAG_STOP) + if (h->type == type) + return h; + else + h++; + + return NULL; +} + +/* + * For the long form of BER encoding we need to know in how many + * octets the length can be encoded. + */ + +u_int32_t +asn_sizeinoctets (u_int32_t len) +{ + u_int32_t log = 0; + + while (len) + { + log++; + len >>= 8; + } + + return log; +} + +u_int8_t * +asn_format_header (struct norm_type *obj, u_int8_t *asn, u_int8_t **data) +{ + u_int8_t *buf = NULL, type; + u_int16_t len_off, len; + struct asn_handler *h; + + h = asn_get (obj->type); + if (h == NULL) + return NULL; + + if (asn != NULL) + buf = asn; + + /* We only do low tag at the moment */ + len_off = 1; + + len = h->get_encoded_len (obj, &type); + + if (buf == NULL && (buf = malloc (len)) == NULL) + return NULL; + + if (type != ASN_LONG_FORM) + { + len -= len_off + 1; + buf[len_off] = len; + + *data = buf + len_off + 1; + } + else + { + u_int16_t tmp; + int octets = asn_sizeinoctets (len); + + len -= len_off + 1 + octets; + *data = buf + len_off + 1 + octets; + + buf[len_off] = octets | ASN_LONG_FORM; + + tmp = len; + while (--octets >= 0) + { + buf[len_off + 1 + octets] = tmp; + tmp >>= 8; + } + } + + if (ISEXPLICIT(obj)) + { + u_int8_t *erg; + /* Explicit tagging add an outer layer */ + struct norm_type tmp = {obj->type, obj->class&0x3, NULL, 0, obj->data}; + + /* XXX - force the class to be CONTEXT */ + buf[0] = GET_EXP(obj) | (((enum asn_classes)CONTEXT & 0x3) << 6) | + ASN_CONSTRUCTED; + erg = asn_format_header (&tmp, *data, data); + + if (erg && (obj->type == TAG_SEQUENCE || obj->type == TAG_SET)) + erg[0] |= ASN_CONSTRUCTED; + } + else + /* XXX low tag only */ + buf[0] = obj->type | (obj->class << 6); + + return buf; +} + +u_int32_t +asn_get_encoded_len (struct norm_type *obj, u_int32_t len, u_int8_t *type) +{ + u_int32_t len_off = 1; + + if (len <= 127) + { + /* Short form */ + len = len + 1 + len_off; + if (type != NULL) + *type = 0; + } + else + { + /* Long Form */ + len = len + asn_sizeinoctets (len) + 1 + len_off; + if (type != NULL) + *type = ASN_LONG_FORM; + } + + if (obj != NULL && ISEXPLICIT(obj)) + len = asn_get_encoded_len (NULL, len, NULL); + + return len; +} + +/* Tries to decode an ANY tag, if we cant handle it we just raw encode it */ + +u_int8_t * +asn_decode_any (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + struct asn_handler *h; + enum asn_tags type; + + type = TAG_TYPE (asn); + if (type == TAG_SEQUENCE || type == TAG_SET) + type = TAG_RAW; + + h = asn_get (type); + if (h == NULL) + { + type = TAG_RAW; + h = asn_get (type); + } + + obj->type = type; + return h->decode (asn, asnlen, obj); +} + +u_int32_t +asn_get_encoded_len_integer (struct norm_type *obj, u_int8_t *type) +{ + u_int16_t len_off; + u_int32_t len = obj->len; + u_int32_t tmp; + mpz_t a; + + /* XXX - We only do low tag at the moment */ + len_off = 1; + + obj->len = len = mpz_sizeinoctets ((mpz_ptr) obj->data); + mpz_init_set (a, (mpz_ptr) obj->data); + + if (len > 1) + mpz_fdiv_q_2exp (a, a, (len - 1) << 3); + + tmp = mpz_fdiv_r_ui (a, a, 256); + mpz_clear (a); + + /* + * We only need to encode positive integers, ASN.1 defines + * negative integers to have the msb set, so if data[0] has + * msb set we need to introduce a zero octet. + */ + if (tmp & 0x80) + len++; + + return asn_get_encoded_len (obj, len, type); +} + +/* + * Encode an integer value. + * Input = obj, output = asn or return value. + */ + +u_int8_t * +asn_encode_integer (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf, *data; + u_int32_t len; + + buf = asn_format_header (obj, asn, &data); + + if (buf == NULL) + return NULL; + + len = mpz_sizeinoctets ((mpz_ptr) obj->data); + mpz_getraw (data, (mpz_ptr) obj->data, len); + + /* XXX - We only deal with unsigned integers at the moment */ + if (data[0] & 0x80) + { + memmove (data + 1, data, len); + data[0] = 0; + } + + return buf; +} + +u_int8_t * +asn_decode_integer (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *data; + u_int32_t len; + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_integer: ASN.1 content is bigger than buffer"); + return NULL; + } + + len = asn_get_data_len (obj, &asn, &data); + + if (TAG_TYPE(asn) != TAG_INTEGER) + { + log_print ("asn_decode_integer: expected tag type INTEGER, got %d", + TAG_TYPE(asn)); + return NULL; + } + + obj->data = malloc (sizeof (mpz_ptr)); + if (obj->data == NULL) + { + log_print ("asn_decode_integer: out of memory."); + return NULL; + } + + mpz_init ((mpz_ptr) obj->data); + mpz_setraw ((mpz_ptr) obj->data, data, len); + + obj->len = len; + + return data + len; +} + +void +asn_free_integer (struct norm_type *obj) +{ + if (obj->data != NULL) + { + mpz_clear ((mpz_ptr) obj->data); + free (obj->data); + } +} + + +u_int32_t +asn_get_encoded_len_string (struct norm_type *obj, u_int8_t *type) +{ + return asn_get_encoded_len (obj, obj->len, type); +} + +/* + * Encode a String + * Input = obj, output = asn or return value. + */ + +u_int8_t * +asn_encode_string (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf, *data; + + buf = asn_format_header (obj, asn, &data); + + if (buf == NULL) + return NULL; + + memcpy (data, obj->data, obj->len); + + return buf; +} + +u_int8_t * +asn_decode_string (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *data; + u_int32_t len; + + obj->len = len = asn_get_data_len (obj, &asn, &data); + + if (TAG_TYPE(asn) != obj->type) + { + log_print ("asn_decode_string: expected tag type STRING(%d), got %d", + obj->type, TAG_TYPE(asn)); + return NULL; + } + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_string: ASN.1 content is bigger than buffer"); + return NULL; + } + + obj->data = malloc (obj->len + 1); + if (obj->data == NULL) + return NULL; + memcpy ((char *)obj->data, data, obj->len); + /* + * Encode a terminating '0', this is irrelevant for OCTET strings + * but nice for printable strings which do not include the terminating + * zero. + */ + ((char *)obj->data)[obj->len] = 0; + + return data + len; +} + +void +asn_free_string (struct norm_type *obj) +{ + if (obj->data != NULL) + free (obj->data); +} + + +u_int32_t +asn_get_encoded_len_objectid (struct norm_type *obj, u_int8_t *type) +{ + u_int16_t len_off; + u_int32_t len; + u_int32_t tmp; + char *buf, *buf2; + + /* XXX - We only do low tag at the moment */ + len_off = 1; + + /* The first two numbers are encoded together */ + buf = obj->data; + tmp = strtol (buf, &buf2, 10); + buf = buf2; + tmp = strtol (buf, &buf2, 10); + buf = buf2; + + len = 1; + while (*buf) + { + tmp = strtol (buf, &buf2, 10); + if (buf == buf2) + break; + + buf = buf2; + do { + tmp >>= 7; + len++; + } while (tmp); + } + + /* The first two ids are encoded as one octet */ + obj->len = len - 1; + + return asn_get_encoded_len (obj, len, type); +} + +/* + * Encode an Object Identifier + * Input = obj, output = asn or return value. + */ + +u_int8_t * +asn_encode_objectid (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf, *data; + char *enc, *enc2; + u_int32_t tmp, tmp2; + int flag = 0; + + buf = asn_format_header (obj, asn, &data); + + if (buf == NULL) + return NULL; + + enc = obj->data; + while (*enc) + { + /* First two ids are encoded as one octet */ + if (flag == 0) + { + tmp = strtol (enc, &enc2, 10); + if (enc == enc2) + return NULL; + enc = enc2; + tmp2 = strtol (enc, &enc2, 10) + 40 * tmp; + flag = 1; + } + else + tmp2 = strtol (enc, &enc2, 10); + + if (enc == enc2) + break; + + /* Reverse the digits to base-128 */ + tmp = 0; + do { + tmp <<= 7; + tmp += tmp2 & 0x7f; + tmp2 >>= 7; + } while (tmp2); + + enc = enc2; + do { + /* If the next octet still belongs to the data set msb */ + *data++ = (tmp & 0x7f) | ( tmp > 127 ? 0x80 : 0); + tmp >>= 7; + } while (tmp); + } + + return buf; +} + +u_int8_t * +asn_decode_objectid (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *data; + u_int32_t len, c, tmp; + int flag = 0; + void *new_buf; + + len = asn_get_data_len (obj, &asn, &data); + + if (TAG_TYPE(asn) != TAG_OBJECTID) + { + log_print ("asn_decode_objectid: expected tag type OBJECTID, got %d", + TAG_TYPE(asn)); + return NULL; + } + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_objectid: ASN.1 content is bigger than buffer"); + return NULL; + } + + obj->data = NULL; + obj->len = 0; + while (len > 0) + { + tmp = 0; + do { + tmp <<= 7; + tmp += *data & 0x7f; + } while (len-- > 0 && (*data++ & 0x80)); + + if (flag == 0) + c = snprintf (NULL, 0, "%d %d ", tmp/40, tmp % 40) + 1; + else + c = snprintf (NULL, 0, "%d ", tmp) + 1; + + new_buf = realloc (obj->data, obj->len + c); + if (new_buf == NULL) + { + free (obj->data); + obj->data = NULL; + log_print ("asn_decode_objectid: out of memory."); + return NULL; + } + obj->data = new_buf; + + if (flag == 0) + { + sprintf (obj->data + obj->len, "%d %d ", tmp/40, tmp % 40); + flag = 1; + } + else + sprintf (obj->data + obj->len, "%d ", tmp); + + obj->len = strlen (obj->data); + } + + if (obj->data != NULL) + ((char *)obj->data)[obj->len - 1] = 0; + + return data; +} + +void +asn_free_objectid (struct norm_type *obj) +{ + if (obj->data != NULL) + free (obj->data); +} + + +u_int32_t +asn_get_encoded_len_raw (struct norm_type *obj, u_int8_t *type) +{ + if (type != NULL) + { + if (obj->len > 127) + *type = ASN_LONG_FORM; + else + *type = 0; + } + + return obj->len; +} + +u_int8_t * +asn_encode_raw (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf = NULL; + + if (obj->len == 0) + return asn; + + if (asn != NULL) + buf = asn; + + if (buf == NULL && (buf = malloc (obj->len)) == NULL) + return NULL; + + memcpy (buf, obj->data, obj->len); + + return buf; +} + +u_int8_t * +asn_decode_raw (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + obj->len = asn_get_len (asn); + if (asnlen < obj->len) + { + log_print ("asn_decode_raw: ASN.1 content is bigger than buffer"); + return NULL; + } + + obj->data = malloc (obj->len); + if (obj->data == NULL) + { + log_print ("asn_decode_raw: out of memory"); + return NULL; + } + + memcpy (obj->data, asn, obj->len); + + return asn + obj->len; +} + +void +asn_free_raw (struct norm_type *obj) +{ + if (obj->data != NULL) + free (obj->data); +} + +u_int32_t +asn_get_encoded_len_null (struct norm_type *obj, u_int8_t *type) +{ + return asn_get_encoded_len (obj, 0, type); +} + +u_int8_t * +asn_encode_null (struct norm_type *obj, u_int8_t *asn) +{ + u_int8_t *buf = NULL; + + if (asn != NULL) + buf = asn; + + if (buf == NULL && (buf = malloc (2)) == NULL) + return NULL; + + buf[0] = obj->type; + buf[1] = 0; + + return buf; +} + +u_int8_t * +asn_decode_null (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + obj->data = NULL; + obj->len = 0; + + return asn + asn_get_len (asn); +} + +void +asn_free_null (struct norm_type *obj) +{ + obj->data = NULL; +} + +void +asn_free (struct norm_type *obj) +{ + struct asn_handler *h = asn_get (obj->type); + + if (h == NULL) + log_print ("asn_free: unkown ASN.1 type %d", obj->type); + else + h->free (obj); +} + +/* + * Returns the whole length of the BER encoded ASN.1 object. + */ + +u_int32_t +asn_get_len (u_int8_t *asn) +{ + u_int32_t len; + u_int8_t *data; + struct norm_type tmp = {TAG_RAW, UNIVERSAL, NULL, 0, NULL}; + + len = asn_get_data_len (&tmp, &asn, &data); + + if (asn == NULL) + return 0; + + return (data - asn) + len; +} + +/* + * Returns the length of the ASN content, and a pointer to the content + * data itself. + * For TAG_NULL the data length is zero, so we have to return an error + * in asn, asn will be NULL in case of error. + */ + +u_int32_t +asn_get_data_len (struct norm_type *obj, u_int8_t **asn, u_int8_t **data) +{ + u_int32_t len; + u_int16_t len_off = 1; + + if (obj != NULL && ISEXPLICIT(obj)) + { + struct norm_type tmp = {TAG_RAW, UNIVERSAL, NULL, 0, NULL}; + + if (TAG_TYPE(*asn) != GET_EXP(obj)) + { + log_print ("asn_get_data_len: explict tagging was needed"); + *asn = NULL; + return 0; + } + + asn_get_data_len (&tmp, asn, data); + *asn = *data; + } + + if ((*asn)[len_off] & ASN_LONG_FORM) + { + int i, octets = (*asn)[len_off] & 0x7f; + + /* XXX - we only decode really small length */ + if (octets > sizeof (len)) + { + log_print ("asn_get_data_len: long form length %d exceeds " + "allowed maximum", octets); + *asn = NULL; + return 0; + } + + for (len = 0, i = 0; i < octets; i++) + { + len = (len << 8) | (*asn)[len_off + 1 + i]; + } + + if (data != NULL) + *data = *asn + len_off + 1 + octets; + } + else + { + /* Short form */ + len = (*asn)[len_off]; + + if (data != NULL) + *data = *asn + len_off + 1; + } + + return len; +} + +void +asn_free_sequence (struct norm_type *obj) +{ + struct norm_type *in = obj->data; + struct asn_handler *h; + + if (in == NULL) + return; + + while (in->type != TAG_STOP) + { + h = asn_get (in->type); + if (h == NULL) + break; + + h->free (in++); + } + + free (obj->data); +} + +u_int32_t +asn_get_encoded_len_sequence (struct norm_type *seq, u_int8_t *type) +{ + u_int32_t len, i; + struct asn_handler *h; + struct norm_type *obj = (struct norm_type *) seq->data; + + /* Get whole data length */ + for (len = 0, i = 0; obj[i].type != TAG_STOP; i++) + { + h = asn_get (obj[i].type); + if (h == NULL) + { + log_print ("asn_encode_sequence: unkown type %d", obj[i].type); + break; + } + len += h->get_encoded_len (&obj[i], NULL); + } + + return asn_get_encoded_len (seq, len, type); +} + +u_int8_t * +asn_encode_sequence (struct norm_type *seq, u_int8_t *asn) +{ + u_int32_t len; + u_int8_t *erg, *data; + struct norm_type *obj; + struct asn_handler *h; + int i; + + if ((h = asn_get (seq->type)) == NULL) + return NULL; + + obj = (struct norm_type *) seq->data; + + erg = asn_format_header (seq, asn, &data); + if (erg == NULL) + return NULL; + + for (i = 0, len = 0; obj[i].type != TAG_STOP; i++) + { + h = asn_get (obj[i].type); + if (h == NULL) + { + log_print ("asn_encode_sequence: unknown ASN.1 tag %d", obj[i].type); + return NULL; + } + + /* A structure can be optional, indicated by data == NULL */ + if (h->encode (&obj[i], data + len) == NULL && obj->data != NULL) + { + log_print ("asn_encode_sequence: encoding of %s failed", + obj[i].name); + return NULL; + } + len += h->get_encoded_len (&obj[i], NULL); + } + + erg[0] |= ASN_CONSTRUCTED; + + return erg; +} + +u_int8_t * +asn_decode_sequence (u_int8_t *asn, u_int32_t asnlen, struct norm_type *obj) +{ + u_int8_t *p, *data; + u_int32_t len, flags, objects; + struct asn_handler *h; + void *new_buf; + + if (asnlen < asn_get_len (asn)) + { + log_print ("asn_decode_sequence: ASN.1 content is bigger than buffer"); + return NULL; + } + + len = asn_get_data_len (obj, &asn, &data); + + /* XXX - an empty sequence is that okay */ + if (len == 0) + return data; + + if (TAG_TYPE(asn) != obj->type) + { + log_print ("asn_decode_sequence: expected tag type SEQUENCE/SET, got %d", + TAG_TYPE(asn)); + return NULL; + } + + /* Handle dynamic sized sets and sequences */ + flags = obj->flags; + + if (flags & ASN_FLAG_ZEROORMORE) + { + struct norm_type stop_tag = {TAG_STOP}; + struct norm_type *tmp; + + /* Zero occurences */ + if (len == 0) + { + asn_free (obj); + obj->data = NULL; + return data; + } + + /* Count number of objects */ + p = data; + objects = 0; + while (p < data + len) + { + objects++; + p += asn_get_len (p); + } + if (p != data + len) + { + log_print ("asn_decode_sequence: SEQ/SET OF too many elements"); + return NULL; + } + + /* + * Create new templates for dynamically added objects, + * the ASN.1 tags SEQUENCE OF and SET OF, specify an unknown + * number of elements. + */ + + new_buf = realloc (obj->data, + (objects+1) * sizeof (struct norm_type)); + if (new_buf == NULL) + { + asn_free (obj); + obj->data = NULL; + log_print ("asn_decode_sequence: out of memory"); + return NULL; + } + obj->data = new_buf; + + tmp = obj->data; + + /* Copy TAG_STOP */ + memcpy (tmp + objects, &stop_tag, sizeof (struct norm_type)); + while (objects-- > 1) + { + memcpy (tmp + objects, tmp, sizeof (struct norm_type)); + if (asn_template_clone (tmp + objects, 1) == NULL) + return NULL; + } + } + + obj = (struct norm_type *) obj->data; + + p = data; + while (p < data + len) + { + if (obj->type == TAG_STOP) + break; + h = asn_get (obj->type); + if (h == NULL) + { + log_print ("asn_decode_sequence: unknown ASN.1 tag %d", obj->type); + return NULL; + } + + if ((p = h->decode (p, (data - p) + len, obj++)) == NULL) + break; + } + + if (p < data + len) + log_print ("asn_decode_sequence: ASN tag was not decoded completely"); + + if (p == NULL) + return NULL; + + return data + len; +} |