Preliminary streaming ASN1 encode support.
[openssl.git] / crypto / asn1 / tasn_dec.c
index 6b0c2481732ac2f96323ecfabeef33d0ca385adc..a9b1d9ef811aa569f5f6b47915be060e6a86a52f 100644 (file)
@@ -75,6 +75,25 @@ static int asn1_template_noexp_d2i(ASN1_VALUE **val, unsigned char **in, long le
 static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, 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
@@ -132,8 +151,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;
 
@@ -217,14 +246,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 */
@@ -232,31 +270,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;
@@ -383,7 +416,7 @@ 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, unsigned char **in, long inlen, const ASN1_TEMPLATE *tt, char opt, ASN1_TLC *ctx)
 {
        int flags, aclass;
        int ret;
@@ -483,7 +516,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));
                        }
                }
                                
@@ -506,7 +539,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;
                        }
@@ -522,14 +555,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;
@@ -598,8 +631,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;
                }
@@ -876,17 +914,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);