diff options
author | jsing <> | 2022-05-17 19:09:16 +0000 |
---|---|---|
committer | jsing <> | 2022-05-17 19:09:16 +0000 |
commit | 422d86c6404e0488f549a5e1def8849243e656c6 (patch) | |
tree | 676a4511890618bc9cee65df2e48c88fd13a907b /src | |
parent | 440516cf733a3d776a7133a827812490af8d75e1 (diff) | |
download | openbsd-422d86c6404e0488f549a5e1def8849243e656c6.tar.gz openbsd-422d86c6404e0488f549a5e1def8849243e656c6.tar.bz2 openbsd-422d86c6404e0488f549a5e1def8849243e656c6.zip |
Refactor asn1_d2i_ex_primitive()
Split the object content handling off into asn1_d2i_ex_primitive_content(),
move the handling ov V_ASN1_ANY into asn1_d2i_ex_any() and move the MSTRING
handling into asn1_d2i_ex_mstring(). This way we parse the header once
(rather than twice for ANY and MSTRING), then process the content, while
also avoiding complex special cases in a single code path.
ok tb@
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/libcrypto/asn1/tasn_dec.c | 244 |
1 files changed, 146 insertions, 98 deletions
diff --git a/src/lib/libcrypto/asn1/tasn_dec.c b/src/lib/libcrypto/asn1/tasn_dec.c index 88b61a997d..234484d6b2 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.69 2022/05/17 12:23:52 jsing Exp $ */ | 1 | /* $OpenBSD: tasn_dec.c,v 1.70 2022/05/17 19:09:16 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 | */ |
@@ -91,6 +91,8 @@ static int asn1_template_ex_d2i(ASN1_VALUE **pval, const unsigned char **in, | |||
91 | long len, const ASN1_TEMPLATE *tt, char opt, int depth); | 91 | long len, const ASN1_TEMPLATE *tt, char opt, int depth); |
92 | static int asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in, | 92 | static int asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in, |
93 | long len, const ASN1_TEMPLATE *tt, char opt, int depth); | 93 | long len, const ASN1_TEMPLATE *tt, char opt, int depth); |
94 | static int asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *CBS, | ||
95 | const ASN1_ITEM *it, int tag_number, int tag_class, char optional); | ||
94 | static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, | 96 | static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, |
95 | const ASN1_ITEM *it, int tag_number, int tag_class, char optional); | 97 | const ASN1_ITEM *it, int tag_number, int tag_class, char optional); |
96 | static int asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, | 98 | static int asn1_ex_c2i(ASN1_VALUE **pval, CBS *content, int utype, |
@@ -399,9 +401,6 @@ asn1_item_ex_d2i_cbs(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | |||
399 | const ASN1_EXTERN_FUNCS *ef = it->funcs; | 401 | const ASN1_EXTERN_FUNCS *ef = it->funcs; |
400 | const unsigned char *p = NULL; | 402 | const unsigned char *p = NULL; |
401 | ASN1_TLC ctx = { 0 }; | 403 | ASN1_TLC ctx = { 0 }; |
402 | CBS cbs_mstring; | ||
403 | unsigned char oclass; | ||
404 | int otag; | ||
405 | int ret = 0; | 404 | int ret = 0; |
406 | 405 | ||
407 | if (pval == NULL) | 406 | if (pval == NULL) |
@@ -433,39 +432,8 @@ asn1_item_ex_d2i_cbs(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | |||
433 | tag_class, optional); | 432 | tag_class, optional); |
434 | 433 | ||
435 | case ASN1_ITYPE_MSTRING: | 434 | case ASN1_ITYPE_MSTRING: |
436 | /* | 435 | return asn1_d2i_ex_mstring(pval, cbs, it, tag_number, tag_class, |
437 | * It never makes sense for multi-strings to have implicit | 436 | optional); |
438 | * tagging, so if tag != -1, then this looks like an error in | ||
439 | * the template. | ||
440 | */ | ||
441 | if (tag_number != -1) { | ||
442 | ASN1error(ASN1_R_BAD_TEMPLATE); | ||
443 | goto err; | ||
444 | } | ||
445 | |||
446 | /* XXX - avoid reading the header twice for MSTRING. */ | ||
447 | CBS_dup(cbs, &cbs_mstring); | ||
448 | if (asn1_check_tag_cbs(&cbs_mstring, NULL, &otag, &oclass, | ||
449 | NULL, NULL, -1, 0, 1) != 1) { | ||
450 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | ||
451 | goto err; | ||
452 | } | ||
453 | |||
454 | /* Must be UNIVERSAL class */ | ||
455 | if (oclass != V_ASN1_UNIVERSAL) { | ||
456 | if (optional) | ||
457 | return -1; | ||
458 | ASN1error(ASN1_R_MSTRING_NOT_UNIVERSAL); | ||
459 | goto err; | ||
460 | } | ||
461 | /* Check tag matches bit map */ | ||
462 | if (!(ASN1_tag2bit(otag) & it->utype)) { | ||
463 | if (optional) | ||
464 | return -1; | ||
465 | ASN1error(ASN1_R_MSTRING_WRONG_TAG); | ||
466 | goto err; | ||
467 | } | ||
468 | return asn1_d2i_ex_primitive(pval, cbs, it, otag, 0, 0); | ||
469 | 437 | ||
470 | case ASN1_ITYPE_EXTERN: | 438 | case ASN1_ITYPE_EXTERN: |
471 | if (CBS_len(cbs) > LONG_MAX) | 439 | if (CBS_len(cbs) > LONG_MAX) |
@@ -750,74 +718,35 @@ asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in, long len, | |||
750 | return 0; | 718 | return 0; |
751 | } | 719 | } |
752 | 720 | ||
721 | /* | ||
722 | * Decode ASN.1 content into a primitive type. There are three possible forms - | ||
723 | * a SEQUENCE/SET/OTHER that is stored verbatim (including the ASN.1 tag and | ||
724 | * length octets), constructed objects and non-constructed objects. In the | ||
725 | * first two cases indefinite length is permitted, which we may need to handle. | ||
726 | * When this function is called the *cbs should reference the start of the | ||
727 | * ASN.1 object (i.e. the tag/length header), while *cbs_object should | ||
728 | * reference the start of the object contents (i.e. after the tag/length | ||
729 | * header. Additionally, the *cbs_object offset should be relative to the | ||
730 | * ASN.1 object being parsed. On success the *cbs will point at the octet | ||
731 | * after the object. | ||
732 | */ | ||
753 | static int | 733 | static int |
754 | asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | 734 | asn1_d2i_ex_primitive_content(ASN1_VALUE **pval, CBS *cbs, CBS *cbs_object, |
755 | int tag_number, int tag_class, char optional) | 735 | int utype, char constructed, char indefinite, size_t length, |
736 | const ASN1_ITEM *it) | ||
756 | { | 737 | { |
757 | CBS cbs_any, cbs_content, cbs_object, cbs_initial; | 738 | CBS cbs_content, cbs_initial; |
758 | char constructed, indefinite; | ||
759 | uint8_t *data = NULL; | 739 | uint8_t *data = NULL; |
760 | size_t data_len = 0; | 740 | size_t data_len = 0; |
761 | int utype = it->utype; | ||
762 | unsigned char oclass; | ||
763 | size_t length; | ||
764 | CBB cbb; | 741 | CBB cbb; |
765 | int tag_ret; | ||
766 | int ret = 0; | 742 | int ret = 0; |
767 | 743 | ||
768 | memset(&cbb, 0, sizeof(cbb)); | 744 | memset(&cbb, 0, sizeof(cbb)); |
769 | 745 | ||
770 | CBS_dup(cbs, &cbs_initial); | 746 | CBS_dup(cbs, &cbs_initial); |
771 | CBS_init(&cbs_content, NULL, 0); | 747 | CBS_init(&cbs_content, NULL, 0); |
772 | CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); | ||
773 | |||
774 | if (pval == NULL) { | ||
775 | ASN1error(ASN1_R_ILLEGAL_NULL); | ||
776 | goto err; | ||
777 | } | ||
778 | |||
779 | if (it->itype == ASN1_ITYPE_MSTRING) { | ||
780 | utype = tag_number; | ||
781 | tag_number = -1; | ||
782 | } | ||
783 | |||
784 | if (utype == V_ASN1_ANY) { | ||
785 | /* Determine type from ASN.1 tag. */ | ||
786 | if (tag_number >= 0) { | ||
787 | ASN1error(ASN1_R_ILLEGAL_TAGGED_ANY); | ||
788 | goto err; | ||
789 | } | ||
790 | if (optional) { | ||
791 | ASN1error(ASN1_R_ILLEGAL_OPTIONAL_ANY); | ||
792 | goto err; | ||
793 | } | ||
794 | /* XXX - avoid reading the header twice for ANY. */ | ||
795 | CBS_dup(&cbs_object, &cbs_any); | ||
796 | tag_ret = asn1_check_tag_cbs(&cbs_any, NULL, &utype, &oclass, | ||
797 | NULL, NULL, -1, 0, 0); | ||
798 | if (tag_ret != 1) { | ||
799 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | ||
800 | goto err; | ||
801 | } | ||
802 | if (oclass != V_ASN1_UNIVERSAL) | ||
803 | utype = V_ASN1_OTHER; | ||
804 | } | ||
805 | 748 | ||
806 | if (tag_number == -1) { | 749 | /* XXX - check primitive vs constructed based on utype. */ |
807 | tag_number = utype; | ||
808 | tag_class = V_ASN1_UNIVERSAL; | ||
809 | } | ||
810 | |||
811 | tag_ret = asn1_check_tag_cbs(&cbs_object, &length, NULL, NULL, &indefinite, | ||
812 | &constructed, tag_number, tag_class, optional); | ||
813 | if (tag_ret == -1) { | ||
814 | ret = -1; | ||
815 | goto err; | ||
816 | } | ||
817 | if (tag_ret != 1) { | ||
818 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | ||
819 | goto err; | ||
820 | } | ||
821 | 750 | ||
822 | /* SEQUENCE and SET must be constructed. */ | 751 | /* SEQUENCE and SET must be constructed. */ |
823 | if ((utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET) && !constructed) { | 752 | if ((utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET) && !constructed) { |
@@ -828,10 +757,10 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | |||
828 | /* SEQUENCE, SET and "OTHER" are left in encoded form. */ | 757 | /* SEQUENCE, SET and "OTHER" are left in encoded form. */ |
829 | if (utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET || | 758 | if (utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET || |
830 | utype == V_ASN1_OTHER) { | 759 | utype == V_ASN1_OTHER) { |
831 | if (!asn1_find_end(&cbs_object, length, indefinite)) | 760 | if (!asn1_find_end(cbs_object, length, indefinite)) |
832 | goto err; | 761 | goto err; |
833 | if (!CBS_get_bytes(&cbs_initial, &cbs_content, | 762 | if (!CBS_get_bytes(&cbs_initial, &cbs_content, |
834 | CBS_offset(&cbs_object))) | 763 | CBS_offset(cbs_object))) |
835 | goto err; | 764 | goto err; |
836 | } else if (constructed) { | 765 | } else if (constructed) { |
837 | /* | 766 | /* |
@@ -843,7 +772,7 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | |||
843 | */ | 772 | */ |
844 | if (!CBB_init(&cbb, 0)) | 773 | if (!CBB_init(&cbb, 0)) |
845 | goto err; | 774 | goto err; |
846 | if (!asn1_collect(&cbb, &cbs_object, indefinite, -1, | 775 | if (!asn1_collect(&cbb, cbs_object, indefinite, -1, |
847 | V_ASN1_UNIVERSAL, 0)) | 776 | V_ASN1_UNIVERSAL, 0)) |
848 | goto err; | 777 | goto err; |
849 | if (!CBB_finish(&cbb, &data, &data_len)) | 778 | if (!CBB_finish(&cbb, &data, &data_len)) |
@@ -851,14 +780,14 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | |||
851 | 780 | ||
852 | CBS_init(&cbs_content, data, data_len); | 781 | CBS_init(&cbs_content, data, data_len); |
853 | } else { | 782 | } else { |
854 | if (!CBS_get_bytes(&cbs_object, &cbs_content, length)) | 783 | if (!CBS_get_bytes(cbs_object, &cbs_content, length)) |
855 | goto err; | 784 | goto err; |
856 | } | 785 | } |
857 | 786 | ||
858 | if (!asn1_ex_c2i(pval, &cbs_content, utype, it)) | 787 | if (!asn1_ex_c2i(pval, &cbs_content, utype, it)) |
859 | goto err; | 788 | goto err; |
860 | 789 | ||
861 | if (!CBS_skip(cbs, CBS_offset(&cbs_object))) | 790 | if (!CBS_skip(cbs, CBS_offset(cbs_object))) |
862 | goto err; | 791 | goto err; |
863 | 792 | ||
864 | ret = 1; | 793 | ret = 1; |
@@ -871,6 +800,125 @@ asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | |||
871 | } | 800 | } |
872 | 801 | ||
873 | static int | 802 | static int |
803 | asn1_d2i_ex_any(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | ||
804 | int tag_number, int tag_class, char optional) | ||
805 | { | ||
806 | char constructed, indefinite; | ||
807 | unsigned char object_class; | ||
808 | int object_type; | ||
809 | CBS cbs_object; | ||
810 | size_t length; | ||
811 | |||
812 | CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); | ||
813 | |||
814 | if (it->utype != V_ASN1_ANY) | ||
815 | return 0; | ||
816 | |||
817 | if (tag_number >= 0) { | ||
818 | ASN1error(ASN1_R_ILLEGAL_TAGGED_ANY); | ||
819 | return 0; | ||
820 | } | ||
821 | if (optional) { | ||
822 | ASN1error(ASN1_R_ILLEGAL_OPTIONAL_ANY); | ||
823 | return 0; | ||
824 | } | ||
825 | |||
826 | /* Determine type from ASN.1 tag. */ | ||
827 | if (asn1_check_tag_cbs(&cbs_object, &length, &object_type, &object_class, | ||
828 | &indefinite, &constructed, -1, 0, 0) != 1) { | ||
829 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | ||
830 | return 0; | ||
831 | } | ||
832 | if (object_class != V_ASN1_UNIVERSAL) | ||
833 | object_type = V_ASN1_OTHER; | ||
834 | |||
835 | return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, object_type, | ||
836 | constructed, indefinite, length, it); | ||
837 | } | ||
838 | |||
839 | static int | ||
840 | asn1_d2i_ex_mstring(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | ||
841 | int tag_number, int tag_class, char optional) | ||
842 | { | ||
843 | char constructed, indefinite; | ||
844 | unsigned char object_class; | ||
845 | int object_tag; | ||
846 | CBS cbs_object; | ||
847 | size_t length; | ||
848 | |||
849 | CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); | ||
850 | |||
851 | /* | ||
852 | * It never makes sense for multi-strings to have implicit tagging, so | ||
853 | * if tag_number != -1, then this looks like an error in the template. | ||
854 | */ | ||
855 | if (tag_number != -1) { | ||
856 | ASN1error(ASN1_R_BAD_TEMPLATE); | ||
857 | return 0; | ||
858 | } | ||
859 | |||
860 | if (asn1_check_tag_cbs(&cbs_object, &length, &object_tag, &object_class, | ||
861 | &indefinite, &constructed, -1, 0, 1) != 1) { | ||
862 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | ||
863 | return 0; | ||
864 | } | ||
865 | |||
866 | /* Class must be UNIVERSAL. */ | ||
867 | if (object_class != V_ASN1_UNIVERSAL) { | ||
868 | if (optional) | ||
869 | return -1; | ||
870 | ASN1error(ASN1_R_MSTRING_NOT_UNIVERSAL); | ||
871 | return 0; | ||
872 | } | ||
873 | /* Check tag matches bit map. */ | ||
874 | if ((ASN1_tag2bit(object_tag) & it->utype) == 0) { | ||
875 | if (optional) | ||
876 | return -1; | ||
877 | ASN1error(ASN1_R_MSTRING_WRONG_TAG); | ||
878 | return 0; | ||
879 | } | ||
880 | |||
881 | return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, | ||
882 | object_tag, constructed, indefinite, length, it); | ||
883 | } | ||
884 | |||
885 | static int | ||
886 | asn1_d2i_ex_primitive(ASN1_VALUE **pval, CBS *cbs, const ASN1_ITEM *it, | ||
887 | int tag_number, int tag_class, char optional) | ||
888 | { | ||
889 | CBS cbs_object; | ||
890 | char constructed, indefinite; | ||
891 | int utype = it->utype; | ||
892 | size_t length; | ||
893 | int ret; | ||
894 | |||
895 | CBS_init(&cbs_object, CBS_data(cbs), CBS_len(cbs)); | ||
896 | |||
897 | if (it->itype == ASN1_ITYPE_MSTRING) | ||
898 | return 0; | ||
899 | |||
900 | if (it->utype == V_ASN1_ANY) | ||
901 | return asn1_d2i_ex_any(pval, cbs, it, tag_number, tag_class, optional); | ||
902 | |||
903 | if (tag_number == -1) { | ||
904 | tag_number = it->utype; | ||
905 | tag_class = V_ASN1_UNIVERSAL; | ||
906 | } | ||
907 | |||
908 | ret = asn1_check_tag_cbs(&cbs_object, &length, NULL, NULL, &indefinite, | ||
909 | &constructed, tag_number, tag_class, optional); | ||
910 | if (ret == -1) | ||
911 | return -1; | ||
912 | if (ret != 1) { | ||
913 | ASN1error(ERR_R_NESTED_ASN1_ERROR); | ||
914 | return 0; | ||
915 | } | ||
916 | |||
917 | return asn1_d2i_ex_primitive_content(pval, cbs, &cbs_object, utype, | ||
918 | constructed, indefinite, length, it); | ||
919 | } | ||
920 | |||
921 | static int | ||
874 | asn1_ex_c2i_primitive(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) | 922 | asn1_ex_c2i_primitive(ASN1_VALUE **pval, CBS *content, int utype, const ASN1_ITEM *it) |
875 | { | 923 | { |
876 | ASN1_STRING *stmp; | 924 | ASN1_STRING *stmp; |