Constify d2i, s2i, c2i and r2i functions and other associated
[openssl.git] / crypto / asn1 / tasn_dec.c
index 15ebf267f2291158b11bc1a5cf818df9849c34df..aa26a44eadbdda8d4498e6a6f35b0f02bf3c4a12 100644 (file)
 
 
 #include <stddef.h>
+#include <string.h>
 #include <openssl/asn1.h>
 #include <openssl/asn1t.h>
 #include <openssl/objects.h>
 #include <openssl/buffer.h>
 #include <openssl/err.h>
 
-static int asn1_check_eoc(unsigned char **in, long len);
-static int asn1_collect(BUF_MEM *buf, unsigned char **in, long len, char inf, int tag, int aclass);
-static int collect_data(BUF_MEM *buf, unsigned char **p, long plen);
+static int asn1_check_eoc(const unsigned char **in, long len);
+static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len, char inf, int tag, int aclass);
+static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen);
 static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass, char *inf, char *cst,
-                       unsigned char **in, long len, int exptag, int expclass, char opt, ASN1_TLC *ctx);
-static int asn1_template_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx);
-static int asn1_template_noexp_d2i(ASN1_VALUE **val, unsigned char **in, long len, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx);
-static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, unsigned char **in, long len,
+                       const unsigned char **in, long len, int exptag, int expclass, char opt, ASN1_TLC *ctx);
+static int asn1_template_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx);
+static int asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in, long len, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx);
+static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in, long len,
                                        const ASN1_ITEM *it, int tag, int aclass, char opt, ASN1_TLC *ctx);
 
+/* Table to convert tags to bit values, used for MSTRING type */
+static unsigned long tag2bit[32]={
+0,     0,      0,      B_ASN1_BIT_STRING,      /* tags  0 -  3 */
+B_ASN1_OCTET_STRING,   0,      0,              B_ASN1_UNKNOWN,/* tags  4- 7 */
+B_ASN1_UNKNOWN,        B_ASN1_UNKNOWN, B_ASN1_UNKNOWN, B_ASN1_UNKNOWN,/* tags  8-11 */
+B_ASN1_UTF8STRING,B_ASN1_UNKNOWN,B_ASN1_UNKNOWN,B_ASN1_UNKNOWN,/* tags 12-15 */
+0,     0,      B_ASN1_NUMERICSTRING,B_ASN1_PRINTABLESTRING,   /* tags 16-19 */
+B_ASN1_T61STRING,B_ASN1_VIDEOTEXSTRING,B_ASN1_IA5STRING,       /* tags 20-22 */
+B_ASN1_UTCTIME, B_ASN1_GENERALIZEDTIME,                               /* tags 23-24 */ 
+B_ASN1_GRAPHICSTRING,B_ASN1_ISO64STRING,B_ASN1_GENERALSTRING,  /* tags 25-27 */
+B_ASN1_UNIVERSALSTRING,B_ASN1_UNKNOWN,B_ASN1_BMPSTRING,B_ASN1_UNKNOWN, /* tags 28-31 */
+       };
+
+unsigned long ASN1_tag2bit(int tag)
+{
+       if((tag < 0) || (tag > 30)) return 0;
+       return tag2bit[tag];
+}
+
 /* Macro to initialize and invalidate the cache */
 
 #define asn1_tlc_clear(c)      if(c) (c)->valid = 0
@@ -86,7 +106,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, unsigned char **in, long len
  * case.
  */
 
-ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1_ITEM *it)
+ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it)
 {
        ASN1_TLC c;
        ASN1_VALUE *ptmpval = NULL;
@@ -97,7 +117,7 @@ ASN1_VALUE *ASN1_item_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const
        return NULL;
 }
 
-int ASN1_template_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1_TEMPLATE *tt)
+int ASN1_template_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_TEMPLATE *tt)
 {
        ASN1_TLC c;
        asn1_tlc_clear(&c);
@@ -109,7 +129,7 @@ int ASN1_template_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN
  * If 'opt' set and tag mismatch return -1 to handle OPTIONAL
  */
 
-int ASN1_item_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1_ITEM *it,
+int ASN1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, long len, const ASN1_ITEM *it,
                                int tag, int aclass, char opt, ASN1_TLC *ctx)
 {
        const ASN1_TEMPLATE *tt, *errtt = NULL;
@@ -117,7 +137,9 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1
        const ASN1_EXTERN_FUNCS *ef;
        const ASN1_AUX *aux = it->funcs;
        ASN1_aux_cb *asn1_cb;
-       unsigned char *p, *q, imphack = 0, oclass;
+       const unsigned char *p, *q;
+       unsigned char *wp=NULL; /* BIG FAT WARNING!  BREAKS CONST WHERE USED */
+       unsigned char imphack = 0, oclass;
        char seq_eoc, seq_nolen, cst, isopt;
        long tmplen;
        int i;
@@ -131,8 +153,18 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1
        switch(it->itype) {
 
                case ASN1_ITYPE_PRIMITIVE:
-               if(it->templates)
+               if(it->templates) {
+                       /* tagging or OPTIONAL is currently illegal on an item template
+                        * because the flags can't get passed down. In practice this isn't
+                        * a problem: we include the relevant flags from the item template
+                        * in the template itself.
+                        */
+                       if ((tag != -1) || opt) {
+                               ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ASN1_R_ILLEGAL_OPTIONS_ON_ITEM_TEMPLATE);
+                               goto err;
+                       }
                        return asn1_template_ex_d2i(pval, in, len, it->templates, opt, ctx);
+               }
                return asn1_d2i_ex_primitive(pval, in, len, it, tag, aclass, opt, ctx);
                break;
 
@@ -199,14 +231,14 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1
                 */
 
                if(tag != -1) {
-                       p = *in;
-                       imphack = *p;
-                       *p = (unsigned char)((*p & V_ASN1_CONSTRUCTED) | it->utype);
+                       wp = *(unsigned char **)in;
+                       imphack = *wp;
+                       *wp = (unsigned char)((*p & V_ASN1_CONSTRUCTED) | it->utype);
                }
 
                ptmpval = cf->asn1_d2i(pval, in, len);
 
-               if(tag != -1) *p = imphack;
+               if(tag != -1) *wp = imphack;
 
                if(ptmpval) return 1;
                ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
@@ -216,14 +248,23 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1
                case ASN1_ITYPE_CHOICE:
                if(asn1_cb && !asn1_cb(ASN1_OP_D2I_PRE, pval, it))
                                goto auxerr;
+
+               /* Allocate structure */
+               if(!*pval) {
+                       if(!ASN1_item_ex_new(pval, it)) {
+                               ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
+                               goto err;
+                       }
+               }
                /* CHOICE type, try each possibility in turn */
                pchval = NULL;
                p = *in;
                for(i = 0, tt=it->templates; i < it->tcount; i++, tt++) {
+                       pchptr = asn1_get_field_ptr(pval, tt);
                        /* We mark field as OPTIONAL so its absence
                         * can be recognised.
                         */
-                       ret = asn1_template_ex_d2i(&pchval, &p, len, tt, 1, ctx);
+                       ret = asn1_template_ex_d2i(pchptr, &p, len, tt, 1, ctx);
                        /* If field not present, try the next one */
                        if(ret == -1) continue;
                        /* If positive return, read OK, break loop */
@@ -231,31 +272,26 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1
                        /* Otherwise must be an ASN1 parsing error */
                        errtt = tt;
                        ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
-                       return 0;
+                       goto err;
                }
                /* Did we fall off the end without reading anything? */
                if(i == it->tcount) {
                        /* If OPTIONAL, this is OK */
-                       if(opt) return -1;
-                       ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ASN1_R_NO_MATCHING_CHOICE_TYPE);
-                       return 0;
-               }
-               /* Otherwise we got a match, allocate structure and populate it */
-               if(!*pval) {
-                       if(!ASN1_item_ex_new(pval, it)) {
-                               errtt = tt;
-                               ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ERR_R_NESTED_ASN1_ERROR);
-                               return 0;
+                       if(opt) {
+                               /* Free and zero it */
+                               ASN1_item_ex_free(pval, it);
+                               return -1;
                        }
+                       ASN1err(ASN1_F_ASN1_ITEM_EX_D2I, ASN1_R_NO_MATCHING_CHOICE_TYPE);
+                       goto err;
                }
-               pchptr = asn1_get_field_ptr(pval, tt);
-               *pchptr = pchval;
                asn1_set_choice_selector(pval, i, it);
                *in = p;
                if(asn1_cb && !asn1_cb(ASN1_OP_D2I_POST, pval, it))
                                goto auxerr;
                return 1;
 
+               case ASN1_ITYPE_NDEF_SEQUENCE:
                case ASN1_ITYPE_SEQUENCE:
                p = *in;
                tmplen = len;
@@ -382,12 +418,12 @@ int ASN1_item_ex_d2i(ASN1_VALUE **pval, unsigned char **in, long len, const ASN1
  * rest.
  */
 
-int asn1_template_ex_d2i(ASN1_VALUE **val, unsigned char **in, long inlen, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx)
+static int asn1_template_ex_d2i(ASN1_VALUE **val, const unsigned char **in, long inlen, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx)
 {
        int flags, aclass;
        int ret;
        long len;
-       unsigned char *p, *q;
+       const unsigned char *p, *q;
        char exp_eoc;
        if(!val) return 0;
        flags = tt->flags;
@@ -444,11 +480,11 @@ int asn1_template_ex_d2i(ASN1_VALUE **val, unsigned char **in, long inlen, const
        return 0;
 }
 
-static int asn1_template_noexp_d2i(ASN1_VALUE **val, unsigned char **in, long len, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx)
+static int asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in, long len, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx)
 {
        int flags, aclass;
        int ret;
-       unsigned char *p, *q;
+       const unsigned char *p, *q;
        if(!val) return 0;
        flags = tt->flags;
        aclass = flags & ASN1_TFLG_TAG_CLASS;
@@ -482,7 +518,7 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val, unsigned char **in, long le
                        ASN1_VALUE *vtmp;
                        while(sk_num(sktmp) > 0) {
                                vtmp = (ASN1_VALUE *)sk_pop(sktmp);
-                               ASN1_item_ex_free(&vtmp, tt->item);
+                               ASN1_item_ex_free(&vtmp, ASN1_ITEM_ptr(tt->item));
                        }
                }
                                
@@ -505,7 +541,7 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val, unsigned char **in, long le
                                break;
                        }
                        skfield = NULL;
-                       if(!ASN1_item_ex_d2i(&skfield, &p, len, tt->item, -1, 0, 0, ctx)) {
+                       if(!ASN1_item_ex_d2i(&skfield, &p, len, ASN1_ITEM_ptr(tt->item), -1, 0, 0, ctx)) {
                                ASN1err(ASN1_F_ASN1_TEMPLATE_D2I, ERR_R_NESTED_ASN1_ERROR);
                                goto err;
                        }
@@ -521,14 +557,14 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val, unsigned char **in, long le
                }
        } else if(flags & ASN1_TFLG_IMPTAG) {
                /* IMPLICIT tagging */
-               ret = ASN1_item_ex_d2i(val, &p, len, tt->item, tt->tag, aclass, opt, ctx);
+               ret = ASN1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item), tt->tag, aclass, opt, ctx);
                if(!ret) {
                        ASN1err(ASN1_F_ASN1_TEMPLATE_D2I, ERR_R_NESTED_ASN1_ERROR);
                        goto err;
                } else if(ret == -1) return -1;
        } else {
                /* Nothing special */
-               ret = ASN1_item_ex_d2i(val, &p, len, tt->item, -1, 0, opt, ctx);
+               ret = ASN1_item_ex_d2i(val, &p, len, ASN1_ITEM_ptr(tt->item), -1, 0, opt, ctx);
                if(!ret) {
                        ASN1err(ASN1_F_ASN1_TEMPLATE_D2I, ERR_R_NESTED_ASN1_ERROR);
                        goto err;
@@ -544,16 +580,16 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val, unsigned char **in, long le
        return 0;
 }
 
-static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, unsigned char **in, long inlen, 
+static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in, long inlen, 
                                                const ASN1_ITEM *it,
                                                int tag, int aclass, char opt, ASN1_TLC *ctx)
 {
        int ret = 0, utype;
        long plen;
        char cst, inf, free_cont = 0;
-       unsigned char *p;
+       const unsigned char *p;
        BUF_MEM buf;
-       unsigned char *cont = NULL;
+       const unsigned char *cont = NULL;
        long len; 
        if(!pval) {
                ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ASN1_R_ILLEGAL_NULL);
@@ -597,8 +633,13 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, unsigned char **in, long inl
        } else if(ret == -1) return -1;
        /* SEQUENCE, SET and "OTHER" are left in encoded form */
        if((utype == V_ASN1_SEQUENCE) || (utype == V_ASN1_SET) || (utype == V_ASN1_OTHER)) {
+               /* Clear context cache for type OTHER because the auto clear when
+                * we have a exact match wont work
+                */
+               if(utype == V_ASN1_OTHER) {
+                       asn1_tlc_clear(ctx);
                /* SEQUENCE and SET must be constructed */
-               if((utype != V_ASN1_OTHER) && !cst) {
+               } else if(!cst) {
                        ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ASN1_R_TYPE_NOT_CONSTRUCTED);
                        return 0;
                }
@@ -606,7 +647,7 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, unsigned char **in, long inl
                cont = *in;
                /* If indefinite length constructed find the real end */
                if(inf) {
-                       asn1_collect(NULL, &p, plen, inf, -1, -1);
+                       if(!asn1_collect(NULL, &p, plen, inf, -1, -1)) goto err;
                        len = p - cont;
                } else {
                        len = p - cont + plen;
@@ -623,9 +664,15 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, unsigned char **in, long inl
                 * internally irrespective of the type. So instead just check
                 * for UNIVERSAL class and ignore the tag.
                 */
-               asn1_collect(&buf, &p, plen, inf, -1, V_ASN1_UNIVERSAL);
-               cont = (unsigned char *)buf.data;
+               if(!asn1_collect(&buf, &p, plen, inf, -1, V_ASN1_UNIVERSAL)) goto err;
                len = buf.length;
+               /* Append a final null to string */
+               if(!BUF_MEM_grow_clean(&buf, len + 1)) {
+                       ASN1err(ASN1_F_ASN1_D2I_EX_PRIMITIVE, ERR_R_MALLOC_FAILURE);
+                       return 0;
+               }
+               buf.data[len] = 0;
+               cont = (const unsigned char *)buf.data;
                free_cont = 1;
        } else {
                cont = p;
@@ -645,12 +692,14 @@ static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, unsigned char **in, long inl
 
 /* Translate ASN1 content octets into a structure */
 
-int asn1_ex_c2i(ASN1_VALUE **pval, unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it)
+int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, int len, int utype, char *free_cont, const ASN1_ITEM *it)
 {
+       ASN1_VALUE **opval = NULL;
        ASN1_STRING *stmp;
        ASN1_TYPE *typ = NULL;
        int ret = 0;
        const ASN1_PRIMITIVE_FUNCS *pf;
+       ASN1_INTEGER **tint;
        pf = it->funcs;
        if(pf && pf->prim_c2i) return pf->prim_c2i(pval, cont, len, utype, free_cont, it);
        /* If ANY type clear type and set pointer to internal value */
@@ -660,6 +709,7 @@ int asn1_ex_c2i(ASN1_VALUE **pval, unsigned char *cont, int len, int utype, char
                        *pval = (ASN1_VALUE *)typ;
                } else typ = (ASN1_TYPE *)*pval;
                if(utype != typ->type) ASN1_TYPE_set(typ, utype, NULL);
+               opval = pval;
                pval = (ASN1_VALUE **)&typ->value.ptr;
        }
        switch(utype) {
@@ -694,7 +744,10 @@ int asn1_ex_c2i(ASN1_VALUE **pval, unsigned char *cont, int len, int utype, char
                case V_ASN1_NEG_INTEGER:
                case V_ASN1_ENUMERATED:
                case V_ASN1_NEG_ENUMERATED:
-               if(!c2i_ASN1_INTEGER((ASN1_INTEGER **)pval, &cont, len)) goto err;
+               tint = (ASN1_INTEGER **)pval;
+               if(!c2i_ASN1_INTEGER(tint, &cont, len)) goto err;
+               /* Fixup type to match the expected form */
+               (*tint)->type = utype | ((*tint)->type & V_ASN1_NEG);
                break;
 
                case V_ASN1_OCTET_STRING:
@@ -730,7 +783,7 @@ int asn1_ex_c2i(ASN1_VALUE **pval, unsigned char *cont, int len, int utype, char
                /* If we've already allocated a buffer use it */
                if(*free_cont) {
                        if(stmp->data) OPENSSL_free(stmp->data);
-                       stmp->data = cont;
+                       stmp->data = (unsigned char *)cont; /* UGLY CAST! RL */
                        stmp->length = len;
                        *free_cont = 0;
                } else {
@@ -748,7 +801,12 @@ int asn1_ex_c2i(ASN1_VALUE **pval, unsigned char *cont, int len, int utype, char
 
        ret = 1;
        err:
-       if(!ret) ASN1_TYPE_free(typ);
+       if(!ret)
+               {
+               ASN1_TYPE_free(typ);
+               if (opval)
+                       *opval = NULL;
+               }
        return ret;
 }
 
@@ -760,9 +818,9 @@ int asn1_ex_c2i(ASN1_VALUE **pval, unsigned char *cont, int len, int utype, char
  * length constructed stuff.
  */
 
-static int asn1_collect(BUF_MEM *buf, unsigned char **in, long len, char inf, int tag, int aclass)
+static int asn1_collect(BUF_MEM *buf, const unsigned char **in, long len, char inf, int tag, int aclass)
 {
-       unsigned char *p, *q;
+       const unsigned char *p, *q;
        long plen;
        char cst, ininf;
        p = *in;
@@ -804,12 +862,12 @@ static int asn1_collect(BUF_MEM *buf, unsigned char **in, long len, char inf, in
        return 1;
 }
 
-static int collect_data(BUF_MEM *buf, unsigned char **p, long plen)
+static int collect_data(BUF_MEM *buf, const unsigned char **p, long plen)
 {
                int len;
                if(buf) {
                        len = buf->length;
-                       if(!BUF_MEM_grow(buf, len + plen)) {
+                       if(!BUF_MEM_grow_clean(buf, len + plen)) {
                                ASN1err(ASN1_F_COLLECT_DATA, ERR_R_MALLOC_FAILURE);
                                return 0;
                        }
@@ -821,9 +879,9 @@ static int collect_data(BUF_MEM *buf, unsigned char **p, long plen)
 
 /* Check for ASN1 EOC and swallow it if found */
 
-static int asn1_check_eoc(unsigned char **in, long len)
+static int asn1_check_eoc(const unsigned char **in, long len)
 {
-       unsigned char *p;
+       const unsigned char *p;
        if(len < 2) return 0;
        p = *in;
        if(!p[0] && !p[1]) {
@@ -841,12 +899,12 @@ static int asn1_check_eoc(unsigned char **in, long len)
  */
 
 static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass, char *inf, char *cst,
-               unsigned char **in, long len, int exptag, int expclass, char opt, ASN1_TLC *ctx)
+               const unsigned char **in, long len, int exptag, int expclass, char opt, ASN1_TLC *ctx)
 {
        int i;
        int ptag, pclass;
        long plen;
-       unsigned char *p, *q;
+       const unsigned char *p, *q;
        p = *in;
        q = p;
 
@@ -865,17 +923,17 @@ static int asn1_check_tlen(long *olen, int *otag, unsigned char *oclass, char *i
                        ctx->ptag = ptag;
                        ctx->hdrlen = p - q;
                        ctx->valid = 1;
-                       /* If definite length, length + header can't exceed total
-                        * amount of data available.
+                       /* If definite length, and no error, length +
+                        * header can't exceed total amount of data available. 
                         */
-                       if(!(i & 1) && ((plen + ctx->hdrlen) > len)) {
+                       if(!(i & 0x81) && ((plen + ctx->hdrlen) > len)) {
                                ASN1err(ASN1_F_ASN1_CHECK_TLEN, ASN1_R_TOO_LONG);
                                asn1_tlc_clear(ctx);
                                return 0;
                        }
                }
        }
-               
+
        if(i & 0x80) {
                ASN1err(ASN1_F_ASN1_CHECK_TLEN, ASN1_R_BAD_OBJECT_HEADER);
                asn1_tlc_clear(ctx);