diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libcrypto/asn1/tasn_dec.c | 157 |
1 files changed, 80 insertions, 77 deletions
diff --git a/src/lib/libcrypto/asn1/tasn_dec.c b/src/lib/libcrypto/asn1/tasn_dec.c index 7e416719e0..0131e3c27c 100644 --- a/src/lib/libcrypto/asn1/tasn_dec.c +++ b/src/lib/libcrypto/asn1/tasn_dec.c | |||
@@ -1,4 +1,4 @@ | |||
1 | /* $OpenBSD: tasn_dec.c,v 1.56 2022/05/04 10:53:26 jsing Exp $ */ | 1 | /* $OpenBSD: tasn_dec.c,v 1.57 2022/05/04 10:57:48 jsing Exp $ */ |
2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL | 2 | /* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL |
3 | * project 2000. | 3 | * project 2000. |
4 | */ | 4 | */ |
@@ -76,10 +76,11 @@ | |||
76 | #define ASN1_MAX_CONSTRUCTED_NEST 30 | 76 | #define ASN1_MAX_CONSTRUCTED_NEST 30 |
77 | 77 | ||
78 | static int asn1_check_eoc(const unsigned char **in, long len); | 78 | static int asn1_check_eoc(const unsigned char **in, long len); |
79 | static int asn1_find_end(const unsigned char **in, long len, char inf); | 79 | static int asn1_check_eoc_cbs(CBS *cbs); |
80 | static int asn1_find_end(CBS *cbs, size_t length, char indefinite); | ||
80 | 81 | ||
81 | static int asn1_collect(CBB *cbb, const unsigned char **in, long len, | 82 | static int asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag, |
82 | char inf, int tag, int aclass, int depth); | 83 | int expected_class, int depth); |
83 | 84 | ||
84 | static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, | 85 | static int asn1_item_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, |
85 | long len, const ASN1_ITEM *it, int tag, int aclass, char opt, int depth); | 86 | long len, const ASN1_ITEM *it, int tag, int aclass, char opt, int depth); |
@@ -716,8 +717,12 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in, long inlen, | |||
716 | } | 717 | } |
717 | 718 | ||
718 | content = *in; | 719 | content = *in; |
719 | if (!asn1_find_end(&p, plen, inf)) | 720 | if (plen < 0) |
720 | goto err; | 721 | goto err; |
722 | CBS_init(&cbs, p, plen); | ||
723 | if (!asn1_find_end(&cbs, plen, inf)) | ||
724 | goto err; | ||
725 | p = CBS_data(&cbs); | ||
721 | len = p - content; | 726 | len = p - content; |
722 | } else if (cst) { | 727 | } else if (cst) { |
723 | /* | 728 | /* |
@@ -729,8 +734,12 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in, long inlen, | |||
729 | */ | 734 | */ |
730 | if (!CBB_init(&cbb, 0)) | 735 | if (!CBB_init(&cbb, 0)) |
731 | goto err; | 736 | goto err; |
732 | if (!asn1_collect(&cbb, &p, plen, inf, -1, V_ASN1_UNIVERSAL, 0)) | 737 | if (plen < 0) |
738 | goto err; | ||
739 | CBS_init(&cbs, p, plen); | ||
740 | if (!asn1_collect(&cbb, &cbs, inf, -1, V_ASN1_UNIVERSAL, 0)) | ||
733 | goto err; | 741 | goto err; |
742 | p = CBS_data(&cbs); | ||
734 | if (!CBB_finish(&cbb, &data, &data_len)) | 743 | if (!CBB_finish(&cbb, &data, &data_len)) |
735 | goto err; | 744 | goto err; |
736 | 745 | ||
@@ -901,63 +910,48 @@ asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) | |||
901 | return ret; | 910 | return ret; |
902 | } | 911 | } |
903 | 912 | ||
904 | /* This function finds the end of an ASN1 structure when passed its maximum | 913 | /* Find the end of an ASN.1 object. */ |
905 | * length, whether it is indefinite length and a pointer to the content. | ||
906 | * This is more efficient than calling asn1_collect because it does not | ||
907 | * recurse on each indefinite length header. | ||
908 | */ | ||
909 | |||
910 | static int | 914 | static int |
911 | asn1_find_end(const unsigned char **in, long len, char inf) | 915 | asn1_find_end(CBS *cbs, size_t length, char indefinite) |
912 | { | 916 | { |
913 | int expected_eoc; | 917 | size_t eoc_count; |
914 | long plen; | ||
915 | const unsigned char *p = *in, *q; | ||
916 | 918 | ||
917 | /* If not indefinite length constructed just add length */ | 919 | if (!indefinite) { |
918 | if (inf == 0) { | 920 | if (!CBS_skip(cbs, length)) { |
919 | *in += len; | 921 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
922 | return 0; | ||
923 | } | ||
920 | return 1; | 924 | return 1; |
921 | } | 925 | } |
922 | expected_eoc = 1; | 926 | |
923 | /* Indefinite length constructed form. Find the end when enough EOCs | 927 | eoc_count = 1; |
924 | * are found. If more indefinite length constructed headers | 928 | |
925 | * are encountered increment the expected eoc count otherwise just | 929 | while (CBS_len(cbs) > 0) { |
926 | * skip to the end of the data. | 930 | if (asn1_check_eoc_cbs(cbs)) { |
927 | */ | 931 | if (--eoc_count == 0) |
928 | while (len > 0) { | ||
929 | if (asn1_check_eoc(&p, len)) { | ||
930 | expected_eoc--; | ||
931 | if (expected_eoc == 0) | ||
932 | break; | 932 | break; |
933 | len -= 2; | ||
934 | continue; | 933 | continue; |
935 | } | 934 | } |
936 | q = p; | 935 | if (!asn1_check_tag_cbs(cbs, &length, NULL, NULL, |
937 | /* Just read in a header: only care about the length */ | 936 | &indefinite, NULL, -1, 0, 0)) { |
938 | if (!asn1_check_tag(&plen, NULL, NULL, &inf, NULL, &p, len, | ||
939 | -1, 0, 0)) { | ||
940 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 937 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
941 | return 0; | 938 | return 0; |
942 | } | 939 | } |
943 | if (inf) | 940 | if (indefinite) { |
944 | expected_eoc++; | 941 | eoc_count++; |
945 | else | 942 | continue; |
946 | p += plen; | 943 | } |
947 | len -= p - q; | 944 | if (!CBS_skip(cbs, length)) |
945 | return 0; | ||
948 | } | 946 | } |
949 | if (expected_eoc) { | 947 | |
948 | if (eoc_count > 0) { | ||
950 | ASN1error(ASN1_R_MISSING_EOC); | 949 | ASN1error(ASN1_R_MISSING_EOC); |
951 | return 0; | 950 | return 0; |
952 | } | 951 | } |
953 | *in = p; | 952 | |
954 | return 1; | 953 | return 1; |
955 | } | 954 | } |
956 | /* This function collects the asn1 data from a constructred string | ||
957 | * type into a buffer. The values of 'in' and 'len' should refer | ||
958 | * to the contents of the constructed type and 'inf' should be set | ||
959 | * if it is indefinite length. | ||
960 | */ | ||
961 | 955 | ||
962 | #ifndef ASN1_MAX_STRING_NEST | 956 | #ifndef ASN1_MAX_STRING_NEST |
963 | /* This determines how many levels of recursion are permitted in ASN1 | 957 | /* This determines how many levels of recursion are permitted in ASN1 |
@@ -968,63 +962,72 @@ asn1_find_end(const unsigned char **in, long len, char inf) | |||
968 | #define ASN1_MAX_STRING_NEST 5 | 962 | #define ASN1_MAX_STRING_NEST 5 |
969 | #endif | 963 | #endif |
970 | 964 | ||
965 | /* Collect the contents from a constructed ASN.1 object. */ | ||
971 | static int | 966 | static int |
972 | asn1_collect(CBB *cbb, const unsigned char **in, long len, char inf, | 967 | asn1_collect(CBB *cbb, CBS *cbs, char indefinite, int expected_tag, |
973 | int tag, int aclass, int depth) | 968 | int expected_class, int depth) |
974 | { | 969 | { |
975 | const unsigned char *p, *q; | 970 | char constructed; |
976 | long plen; | 971 | size_t length; |
977 | char cst, ininf; | 972 | CBS content; |
973 | int need_eoc; | ||
978 | 974 | ||
979 | if (depth > ASN1_MAX_STRING_NEST) { | 975 | if (depth > ASN1_MAX_STRING_NEST) { |
980 | ASN1error(ASN1_R_NESTED_ASN1_STRING); | 976 | ASN1error(ASN1_R_NESTED_ASN1_STRING); |
981 | return 0; | 977 | return 0; |
982 | } | 978 | } |
983 | 979 | ||
984 | p = *in; | 980 | need_eoc = indefinite; |
985 | inf &= 1; | ||
986 | 981 | ||
987 | while (len > 0) { | 982 | while (CBS_len(cbs) > 0) { |
988 | q = p; | 983 | if (asn1_check_eoc_cbs(cbs)) { |
989 | /* Check for EOC */ | 984 | if (!need_eoc) { |
990 | if (asn1_check_eoc(&p, len)) { | ||
991 | /* EOC is illegal outside indefinite length | ||
992 | * constructed form */ | ||
993 | if (!inf) { | ||
994 | ASN1error(ASN1_R_UNEXPECTED_EOC); | 985 | ASN1error(ASN1_R_UNEXPECTED_EOC); |
995 | return 0; | 986 | return 0; |
996 | } | 987 | } |
997 | inf = 0; | 988 | return 1; |
998 | break; | ||
999 | } | 989 | } |
1000 | 990 | if (!asn1_check_tag_cbs(cbs, &length, NULL, NULL, &indefinite, | |
1001 | if (!asn1_check_tag(&plen, NULL, NULL, &ininf, &cst, &p, len, | 991 | &constructed, expected_tag, expected_class, 0)) { |
1002 | tag, aclass, 0)) { | ||
1003 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | 992 | ASN1error(ERR_R_NESTED_ASN1_ERROR); |
1004 | return 0; | 993 | return 0; |
1005 | } | 994 | } |
1006 | 995 | ||
1007 | /* If indefinite length constructed update max length */ | 996 | if (constructed) { |
1008 | if (cst) { | 997 | if (!asn1_collect(cbb, cbs, indefinite, expected_tag, |
1009 | if (!asn1_collect(cbb, &p, plen, ininf, tag, aclass, | 998 | expected_class, depth + 1)) |
1010 | depth + 1)) | ||
1011 | return 0; | ||
1012 | } else if (plen > 0) { | ||
1013 | if (!CBB_add_bytes(cbb, p, plen)) | ||
1014 | return 0; | 999 | return 0; |
1015 | p += plen; | 1000 | continue; |
1016 | } | 1001 | } |
1017 | len -= p - q; | 1002 | |
1003 | if (!CBS_get_bytes(cbs, &content, length)) { | ||
1004 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | ||
1005 | return 0; | ||
1006 | } | ||
1007 | if (!CBB_add_bytes(cbb, CBS_data(&content), CBS_len(&content))) | ||
1008 | return 0; | ||
1018 | } | 1009 | } |
1019 | if (inf) { | 1010 | |
1011 | if (need_eoc) { | ||
1020 | ASN1error(ASN1_R_MISSING_EOC); | 1012 | ASN1error(ASN1_R_MISSING_EOC); |
1021 | return 0; | 1013 | return 0; |
1022 | } | 1014 | } |
1023 | *in = p; | 1015 | |
1024 | return 1; | 1016 | return 1; |
1025 | } | 1017 | } |
1026 | 1018 | ||
1027 | /* Check for ASN1 EOC and swallow it if found */ | 1019 | static int |
1020 | asn1_check_eoc_cbs(CBS *cbs) | ||
1021 | { | ||
1022 | uint16_t eoc; | ||
1023 | |||
1024 | if (!CBS_peek_u16(cbs, &eoc)) | ||
1025 | return 0; | ||
1026 | if (eoc != 0) | ||
1027 | return 0; | ||
1028 | |||
1029 | return CBS_skip(cbs, 2); | ||
1030 | } | ||
1028 | 1031 | ||
1029 | static int | 1032 | static int |
1030 | asn1_check_eoc(const unsigned char **in, long len) | 1033 | asn1_check_eoc(const unsigned char **in, long len) |