diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/minitasn1/decoding.c | 141 | ||||
-rw-r--r-- | lib/minitasn1/libtasn1.h | 6 |
2 files changed, 116 insertions, 31 deletions
diff --git a/lib/minitasn1/decoding.c b/lib/minitasn1/decoding.c index 8b88581260..0b5bd104e7 100644 --- a/lib/minitasn1/decoding.c +++ b/lib/minitasn1/decoding.c @@ -39,6 +39,8 @@ # define warn() #endif +#define IS_ERR(len, flags) (len < -1 || ((flags & ASN1_DECODE_FLAG_STRICT_DER) && len < 0)) + #define HAVE_TWO(x) (x>=2?1:0) #define DECR_LEN(l, s) do { \ @@ -274,12 +276,17 @@ asn1_get_octet_der (const unsigned char *der, int der_len, } /* Returns ASN1_SUCCESS on success or an error code on error. + * type should be one of ASN1_ETYPE_GENERALIZED_TIME or ASN1_ETYPE_UTC_TIME. */ static int -_asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len, - char *str, int str_size) +_asn1_get_time_der (unsigned type, const unsigned char *der, int der_len, int *ret_len, + char *str, int str_size, unsigned flags) { int len_len, str_len; + unsigned i; + unsigned sign_count = 0; + unsigned dot_count = 0; + const unsigned char *p; if (der_len <= 0 || str == NULL) return ASN1_DER_ERROR; @@ -288,6 +295,48 @@ _asn1_get_time_der (const unsigned char *der, int der_len, int *ret_len, if (str_len <= 0 || str_size < str_len) return ASN1_DER_ERROR; + /* perform some sanity checks on the data */ + if (str_len < 8) + { + warn(); + return ASN1_DER_ERROR; + } + + p = &der[len_len]; + for (i=0;i<str_len-1;i++) + { + if (isdigit(p[i]) == 0) + { + if (type == ASN1_ETYPE_GENERALIZED_TIME) + { + /* tolerate lax encodings */ + if (p[i] == '.' && dot_count == 0) + { + dot_count++; + continue; + } + + /* This is not really valid DER, but there are + * structures using that */ + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && + (p[i] == '+' || p[i] == '-') && sign_count == 0) + { + sign_count++; + continue; + } + } + + warn(); + return ASN1_DER_ERROR; + } + } + + if (sign_count == 0 && p[str_len-1] != 'Z') + { + warn(); + return ASN1_DER_ERROR; + } + memcpy (str, der + len_len, str_len); str[str_len] = 0; *ret_len = str_len + len_len; @@ -406,7 +455,7 @@ asn1_get_bit_der (const unsigned char *der, int der_len, static int _asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, - int *ret_len) + int *ret_len, unsigned flags) { asn1_node p; int counter, len2, len3, is_tag_implicit; @@ -445,8 +494,13 @@ _asn1_extract_tag_der (asn1_node node, const unsigned char *der, int der_len, DECR_LEN(der_len, len2); counter += len2; - len3 = - asn1_get_length_ber (der + counter, der_len, + if (flags & ASN1_DECODE_FLAG_STRICT_DER) + len3 = + asn1_get_length_der (der + counter, der_len, + &len2); + else + len3 = + asn1_get_length_ber (der + counter, der_len, &len2); if (len3 < 0) return ASN1_DER_ERROR; @@ -581,7 +635,7 @@ cleanup: static int extract_tag_der_recursive(asn1_node node, const unsigned char *der, int der_len, - int *ret_len) + int *ret_len, unsigned flags) { asn1_node p; int ris = ASN1_DER_ERROR; @@ -591,7 +645,7 @@ int ris = ASN1_DER_ERROR; p = node->down; while (p) { - ris = _asn1_extract_tag_der (p, der, der_len, ret_len); + ris = _asn1_extract_tag_der (p, der, der_len, ret_len, flags); if (ris == ASN1_SUCCESS) break; p = p->right; @@ -601,7 +655,7 @@ int ris = ASN1_DER_ERROR; return ris; } else - return _asn1_extract_tag_der (node, der, der_len, ret_len); + return _asn1_extract_tag_der (node, der, der_len, ret_len, flags); } static int @@ -665,7 +719,7 @@ _asn1_delete_not_used (asn1_node node) static int _asn1_extract_der_octet (asn1_node node, const unsigned char *der, - int der_len) + int der_len, unsigned flags) { int len2, len3; int counter, counter_end; @@ -686,8 +740,11 @@ _asn1_extract_der_octet (asn1_node node, const unsigned char *der, { len2 = asn1_get_length_der (der + counter, der_len, &len3); - if (len2 < -1) - return ASN1_DER_ERROR; + if (IS_ERR(len2, flags)) + { + warn(); + return ASN1_DER_ERROR; + } if (len2 >= 0) { @@ -699,7 +756,7 @@ _asn1_extract_der_octet (asn1_node node, const unsigned char *der, DECR_LEN(der_len, len3); result = _asn1_extract_der_octet (node, der + counter + len3, - der_len); + der_len, flags); if (result != ASN1_SUCCESS) return result; len2 = 0; @@ -716,19 +773,25 @@ cleanup: } static int -_asn1_get_octet_string (asn1_node node, const unsigned char *der, int der_len, int *len) +_asn1_get_octet_string (asn1_node node, const unsigned char *der, int der_len, + int *len, unsigned flags) { int len2, len3, counter, tot_len, indefinite; int result; + int orig_der_len = der_len; counter = 0; if (*(der - 1) & ASN1_CLASS_STRUCTURED) { tot_len = 0; + indefinite = asn1_get_length_der (der, der_len, &len3); - if (indefinite < -1) - return ASN1_DER_ERROR; + if (IS_ERR(indefinite, flags)) + { + warn(); + return ASN1_DER_ERROR; + } counter += len3; DECR_LEN(der_len, len3); @@ -752,13 +815,19 @@ _asn1_get_octet_string (asn1_node node, const unsigned char *der, int der_len, i DECR_LEN(der_len, 1); if (der[counter] != ASN1_TAG_OCTET_STRING) - return ASN1_DER_ERROR; + { + warn(); + return ASN1_DER_ERROR; + } counter++; len2 = asn1_get_length_der (der + counter, der_len, &len3); if (len2 <= 0) - return ASN1_DER_ERROR; + { + warn(); + return ASN1_DER_ERROR; + } DECR_LEN(der_len, len3 + len2); counter += len3 + len2; @@ -777,9 +846,12 @@ _asn1_get_octet_string (asn1_node node, const unsigned char *der, int der_len, i asn1_length_der (tot_len, temp, &len2); _asn1_set_value (node, temp, len2); - ret = _asn1_extract_der_octet (node, der, der_len); + ret = _asn1_extract_der_octet (node, der, orig_der_len, flags); if (ret != ASN1_SUCCESS) - return ret; + { + warn(); + return ret; + } } } @@ -787,7 +859,10 @@ _asn1_get_octet_string (asn1_node node, const unsigned char *der, int der_len, i { /* NOT STRUCTURED */ len2 = asn1_get_length_der (der, der_len, &len3); if (len2 < 0) - return ASN1_DER_ERROR; + { + warn(); + return ASN1_DER_ERROR; + } DECR_LEN(der_len, len3+len2); counter = len3 + len2; @@ -889,6 +964,9 @@ static void delete_unneeded_choice_fields(asn1_node p) * padding after the decoded DER data. Upon a successful return the value of * *@max_ider_len will be set to the number of bytes decoded. * + * If %ASN1_DECODE_FLAG_STRICT_DER flag is set then the function will + * not decode any BER-encoded elements. + * * Returns: %ASN1_SUCCESS if DER encoding OK, %ASN1_ELEMENT_NOT_FOUND * if @ELEMENT is %NULL, and %ASN1_TAG_ERROR or * %ASN1_DER_ERROR if the der encoding doesn't match the structure @@ -966,7 +1044,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { ris = extract_tag_der_recursive (p2, der + counter, - ider_len, &len2); + ider_len, &len2, flags); if (ris == ASN1_SUCCESS) { p2->type &= ~CONST_NOT_USED; @@ -1016,7 +1094,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { ris = extract_tag_der_recursive (p->down, der + counter, - ider_len, &len2); + ider_len, &len2, flags); if (ris == ASN1_SUCCESS) { @@ -1062,7 +1140,8 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, if (ris == ASN1_SUCCESS) ris = - extract_tag_der_recursive (p, der + counter, ider_len, &tag_len); + extract_tag_der_recursive (p, der + counter, ider_len, + &tag_len, flags); if (ris != ASN1_SUCCESS) { @@ -1162,8 +1241,8 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, case ASN1_ETYPE_GENERALIZED_TIME: case ASN1_ETYPE_UTC_TIME: result = - _asn1_get_time_der (der + counter, ider_len, &len2, temp, - sizeof (temp) - 1); + _asn1_get_time_der (type_field (p->type), der + counter, ider_len, &len2, temp, + sizeof (temp) - 1, flags); if (result != ASN1_SUCCESS) { warn(); @@ -1180,7 +1259,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, move = RIGHT; break; case ASN1_ETYPE_OCTET_STRING: - result = _asn1_get_octet_string (p, der + counter, ider_len, &len3); + result = _asn1_get_octet_string (p, der + counter, ider_len, &len3, flags); if (result != ASN1_SUCCESS) { warn(); @@ -1248,7 +1327,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { /* move==DOWN || move==RIGHT */ len3 = asn1_get_length_der (der + counter, ider_len, &len2); - if (len3 < -1) + if (IS_ERR(len3, flags)) { result = ASN1_DER_ERROR; warn(); @@ -1331,7 +1410,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, { /* move==DOWN || move==RIGHT */ len3 = asn1_get_length_der (der + counter, ider_len, &len2); - if (len3 < -1) + if (IS_ERR(len3, flags)) { result = ASN1_DER_ERROR; warn(); @@ -1363,7 +1442,9 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, break; case ASN1_ETYPE_ANY: /* Check indefinite lenth method in an EXPLICIT TAG */ - if ((p->type & CONST_TAG) && tag_len == 2 && (der[counter - 1] == 0x80)) + + if (!(flags & ASN1_DECODE_FLAG_STRICT_DER) && (p->type & CONST_TAG) && + tag_len == 2 && (der[counter - 1] == 0x80)) indefinite = 1; else indefinite = 0; @@ -1382,7 +1463,7 @@ asn1_der_decoding2 (asn1_node *element, const void *ider, int *max_ider_len, len4 = asn1_get_length_der (der + counter + len2, ider_len, &len3); - if (len4 < -1) + if (IS_ERR(len4, flags)) { result = ASN1_DER_ERROR; warn(); diff --git a/lib/minitasn1/libtasn1.h b/lib/minitasn1/libtasn1.h index 8f7ff0b262..190443ca96 100644 --- a/lib/minitasn1/libtasn1.h +++ b/lib/minitasn1/libtasn1.h @@ -44,7 +44,7 @@ extern "C" { #endif -#define ASN1_VERSION "4.0" +#define ASN1_VERSION "4.1" #if defined(__GNUC__) && !defined(ASN1_INTERNAL_BUILD) # define _ASN1_GCC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__) @@ -185,7 +185,11 @@ extern "C" #define ASN1_DELETE_FLAG_ZEROIZE 1 /* Flags used by asn1_der_decoding2(). */ + +/* This flag would allow arbitrary data past the DER data */ #define ASN1_DECODE_FLAG_ALLOW_PADDING 1 +/* This flag would ensure that no BER decoding takes place */ +#define ASN1_DECODE_FLAG_STRICT_DER (1<<1) struct asn1_data_node_st |