已关闭
fix(cve): 修复 CVE-2026-44378 在 botan2 中的漏洞 #39
infra_team创建于 6月15日关闭于 14 天前
fix(cve): 修复 CVE-2026-44378 在 botan2 中的漏洞 #39
已关闭
共 3 个文件变更+274-1
| @@ -0,0 +1,225 @@ | |||
| 1 | +From: Jack Lloyd <jack@randombit.net> | ||
| 2 | +Date: Sun, 19 Apr 2026 14:21:46 -0400 | ||
| 3 | +Subject: [PATCH] fix: CVE-2026-44378 - Fix quadratic complexity in BER indefinite length decoding | ||
| 4 | + | ||
| 5 | +Fix bugs in handling of indefinite length BER data that could cause | ||
| 6 | +quadratic parsing behavior resulting in denial of service. The length | ||
| 7 | +was not being accounted for correctly, and if an EOC was the last | ||
| 8 | +expected value but missing from the stream, it would be silently accepted. | ||
| 9 | + | ||
| 10 | +Key changes: | ||
| 11 | +1. Add X.690 8.1.2.4.2(c) check for long-form tag encoding (reject 0x00/0x80) | ||
| 12 | +2. Replace buffer-based EOC scanning with peek-based scanning to avoid quadratic behavior | ||
| 13 | +3. Correct indefinite length handling to properly account for EOC marker bytes | ||
| 14 | +4. Add EOC marker validation per X.690 8.1.5 (must be exactly 00 00) | ||
| 15 | +5. Throw error on missing EOC marker instead of silent acceptance | ||
| 16 | + | ||
| 17 | +Upstream-commit: https://github.com/randombit/botan/commit/ded48791efb92e84e563388dd08547081aabfc59 | ||
| 18 | +Signed-off-by: infra_team <zhaiwenjie1@huawei.com> | ||
| 19 | + | ||
| 20 | +--- Botan-2.19.5/src/lib/asn1/ber_dec.cpp 2024-07-08 18:54:05.000000000 +0800 | ||
| 21 | ++++ Botan-2.19.5-fixed/src/lib/asn1/ber_dec.cpp 2026-06-15 03:47:44.029470533 +0800 | ||
| 22 | +@@ -23,6 +23,7 @@ | ||
| 23 | + | ||
| 24 | + /* | ||
| 25 | + * BER decode an ASN.1 type tag | ||
| 26 | ++* CVE-2026-44378: Added X.690 8.1.2.4.2(c) check for long-form tag encoding | ||
| 27 | + */ | ||
| 28 | + size_t decode_tag(DataSource* ber, ASN1_Tag& type_tag, ASN1_Tag& class_tag) | ||
| 29 | + { | ||
| 30 | +@@ -48,6 +49,10 @@ | ||
| 31 | + { | ||
| 32 | + if(!ber->read_byte(b)) | ||
| 33 | + throw BER_Decoding_Error("Long-form tag truncated"); | ||
| 34 | ++ // CVE-2026-44378: X.690 8.1.2.4.2(c) bits 7-1 of first subsequent octet must not be all zero | ||
| 35 | ++ // This rules out 0x80 (continuation with no data) and 0x00 (long-form tag 0, which collides with EOC marker) | ||
| 36 | ++ if(tag_bytes == 1 && (b & 0x7F) == 0) | ||
| 37 | ++ throw BER_Decoding_Error("Long form tag with leading zero"); | ||
| 38 | + if(tag_buf & 0xFF000000) | ||
| 39 | + throw BER_Decoding_Error("Long-form tag overflowed 32 bits"); | ||
| 40 | + ++tag_bytes; | ||
| 41 | +@@ -60,11 +65,13 @@ | ||
| 42 | + | ||
| 43 | + /* | ||
| 44 | + * Find the EOC marker | ||
| 45 | ++* CVE-2026-44378: Updated signature with base_offset parameter for peek-based scanning | ||
| 46 | + */ | ||
| 47 | +-size_t find_eoc(DataSource* src, size_t allow_indef); | ||
| 48 | ++size_t find_eoc(DataSource* src, size_t base_offset, size_t allow_indef); | ||
| 49 | + | ||
| 50 | + /* | ||
| 51 | + * BER decode an ASN.1 length field | ||
| 52 | ++* CVE-2026-44378: Fixed indefinite length handling to properly account for EOC marker bytes | ||
| 53 | + */ | ||
| 54 | + size_t decode_length(DataSource* ber, size_t& field_size, size_t allow_indef) | ||
| 55 | + { | ||
| 56 | +@@ -87,7 +94,12 @@ | ||
| 57 | + } | ||
| 58 | + else | ||
| 59 | + { | ||
| 60 | +- return find_eoc(ber, allow_indef - 1); | ||
| 61 | ++ // CVE-2026-44378: find_eoc now returns bytes including EOC marker | ||
| 62 | ++ // Return content length (excluding 2-byte EOC marker) | ||
| 63 | ++ const size_t eoc_len = find_eoc(ber, 0, allow_indef - 1); | ||
| 64 | ++ if(eoc_len < 2) | ||
| 65 | ++ throw BER_Decoding_Error("Invalid EOC encoding"); | ||
| 66 | ++ return eoc_len - 2; | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | +@@ -106,43 +118,107 @@ | ||
| 71 | + | ||
| 72 | + /* | ||
| 73 | + * Find the EOC marker | ||
| 74 | ++* CVE-2026-44378: Changed from buffer-based to peek-based scanning to avoid quadratic parsing behavior. | ||
| 75 | ++* Also added EOC marker validation per X.690 8.1.5 (must be exactly 00 00). | ||
| 76 | + */ | ||
| 77 | +-size_t find_eoc(DataSource* ber, size_t allow_indef) | ||
| 78 | ++size_t find_eoc(DataSource* src, size_t base_offset, size_t allow_indef) | ||
| 79 | + { | ||
| 80 | +- secure_vector<uint8_t> buffer(BOTAN_DEFAULT_BUFFER_SIZE), data; | ||
| 81 | ++ size_t offset = base_offset; | ||
| 82 | + | ||
| 83 | + while(true) | ||
| 84 | + { | ||
| 85 | +- const size_t got = ber->peek(buffer.data(), buffer.size(), data.size()); | ||
| 86 | +- if(got == 0) | ||
| 87 | +- break; | ||
| 88 | ++ ASN1_Tag type_tag, class_tag; | ||
| 89 | ++ size_t tag_size = 0; | ||
| 90 | ++ uint8_t first_byte; | ||
| 91 | ++ if(src->peek(&first_byte, 1, offset) == 0) | ||
| 92 | ++ { | ||
| 93 | ++ type_tag = class_tag = NO_OBJECT; | ||
| 94 | ++ } | ||
| 95 | ++ else | ||
| 96 | ++ { | ||
| 97 | ++ // Peek tag without consuming | ||
| 98 | ++ if((first_byte & 0x1F) != 0x1F) | ||
| 99 | ++ { | ||
| 100 | ++ type_tag = ASN1_Tag(first_byte & 0x1F); | ||
| 101 | ++ class_tag = ASN1_Tag(first_byte & 0xE0); | ||
| 102 | ++ tag_size = 1; | ||
| 103 | ++ } | ||
| 104 | ++ else | ||
| 105 | ++ { | ||
| 106 | ++ // Long-form tag - parse it manually | ||
| 107 | ++ class_tag = ASN1_Tag(first_byte & 0xE0); | ||
| 108 | ++ size_t tb = 1; | ||
| 109 | ++ uint8_t b; | ||
| 110 | ++ while(true) | ||
| 111 | ++ { | ||
| 112 | ++ if(src->peek(&b, 1, offset + tb) == 0) | ||
| 113 | ++ throw BER_Decoding_Error("Long-form tag truncated in EOC scan"); | ||
| 114 | ++ if(tb == 1 && (b & 0x7F) == 0) | ||
| 115 | ++ throw BER_Decoding_Error("Long form tag with leading zero in EOC scan"); | ||
| 116 | ++ ++tb; | ||
| 117 | ++ if((b & 0x80) == 0) break; | ||
| 118 | ++ } | ||
| 119 | ++ tag_size = tb; | ||
| 120 | ++ type_tag = NO_OBJECT; // We don't need exact value for EOC scan | ||
| 121 | ++ } | ||
| 122 | ++ } | ||
| 123 | + | ||
| 124 | +- data += std::make_pair(buffer.data(), got); | ||
| 125 | +- } | ||
| 126 | ++ if(type_tag == NO_OBJECT) | ||
| 127 | ++ throw BER_Decoding_Error("Missing EOC marker in indefinite-length encoding"); | ||
| 128 | + | ||
| 129 | +- DataSource_Memory source(data); | ||
| 130 | +- data.clear(); | ||
| 131 | ++ // Peek length | ||
| 132 | ++ uint8_t len_byte; | ||
| 133 | ++ if(src->peek(&len_byte, 1, offset + tag_size) == 0) | ||
| 134 | ++ throw BER_Decoding_Error("Length field not found in EOC scan"); | ||
| 135 | + | ||
| 136 | +- size_t length = 0; | ||
| 137 | +- while(true) | ||
| 138 | +- { | ||
| 139 | +- ASN1_Tag type_tag, class_tag; | ||
| 140 | +- size_t tag_size = decode_tag(&source, type_tag, class_tag); | ||
| 141 | +- if(type_tag == NO_OBJECT) | ||
| 142 | +- break; | ||
| 143 | ++ size_t length_size = 1; | ||
| 144 | ++ size_t item_size = 0; | ||
| 145 | + | ||
| 146 | +- size_t length_size = 0; | ||
| 147 | +- size_t item_size = decode_length(&source, length_size, allow_indef); | ||
| 148 | +- source.discard_next(item_size); | ||
| 149 | +- | ||
| 150 | +- length = BOTAN_CHECKED_ADD(length, item_size); | ||
| 151 | +- length = BOTAN_CHECKED_ADD(length, tag_size); | ||
| 152 | +- length = BOTAN_CHECKED_ADD(length, length_size); | ||
| 153 | ++ if((len_byte & 0x80) == 0) | ||
| 154 | ++ { | ||
| 155 | ++ item_size = len_byte; | ||
| 156 | ++ } | ||
| 157 | ++ else | ||
| 158 | ++ { | ||
| 159 | ++ size_t len_octets = (len_byte & 0x7F); | ||
| 160 | ++ if(len_octets > 4) | ||
| 161 | ++ throw BER_Decoding_Error("Length field too large in EOC scan"); | ||
| 162 | ++ if(len_octets == 0) | ||
| 163 | ++ { | ||
| 164 | ++ // Indefinite length - recurse | ||
| 165 | ++ if(allow_indef == 0) | ||
| 166 | ++ throw BER_Decoding_Error("Nested EOC markers too deep"); | ||
| 167 | ++ item_size = find_eoc(src, offset + tag_size + 1, allow_indef - 1); | ||
| 168 | ++ length_size = 1; | ||
| 169 | ++ } | ||
| 170 | ++ else | ||
| 171 | ++ { | ||
| 172 | ++ length_size = 1 + len_octets; | ||
| 173 | ++ for(size_t i = 0; i < len_octets; ++i) | ||
| 174 | ++ { | ||
| 175 | ++ uint8_t b; | ||
| 176 | ++ if(src->peek(&b, 1, offset + tag_size + 1 + i) == 0) | ||
| 177 | ++ throw BER_Decoding_Error("Corrupted length field in EOC scan"); | ||
| 178 | ++ item_size = (item_size << 8) | b; | ||
| 179 | ++ } | ||
| 180 | ++ } | ||
| 181 | ++ } | ||
| 182 | ++ | ||
| 183 | ++ offset = BOTAN_CHECKED_ADD(offset, tag_size); | ||
| 184 | ++ offset = BOTAN_CHECKED_ADD(offset, length_size); | ||
| 185 | ++ offset = BOTAN_CHECKED_ADD(offset, item_size); | ||
| 186 | + | ||
| 187 | + if(type_tag == EOC && class_tag == UNIVERSAL) | ||
| 188 | ++ { | ||
| 189 | ++ // CVE-2026-44378: X.690 8.1.5 EOC marker must be exactly two zero octets (00 00) | ||
| 190 | ++ // Reject EOC with non-zero length | ||
| 191 | ++ if(length_size != 1 || item_size != 0) | ||
| 192 | ++ throw BER_Decoding_Error("EOC marker with non-zero length"); | ||
| 193 | + break; | ||
| 194 | ++ } | ||
| 195 | + } | ||
| 196 | +- return length; | ||
| 197 | ++ | ||
| 198 | ++ return offset - base_offset; | ||
| 199 | + } | ||
| 200 | + | ||
| 201 | + class DataSource_BERObject final : public DataSource | ||
| 202 | +@@ -233,6 +309,7 @@ | ||
| 203 | + | ||
| 204 | + /* | ||
| 205 | + * Return the BER encoding of the next object | ||
| 206 | ++* CVE-2026-44378: Added EOC marker validation per X.690 8.1.5 | ||
| 207 | + */ | ||
| 208 | + BER_Object BER_Decoder::get_next_object() | ||
| 209 | + { | ||
| 210 | +@@ -254,6 +331,15 @@ | ||
| 211 | + | ||
| 212 | + size_t field_size; | ||
| 213 | + const size_t length = decode_length(m_source, field_size, ALLOWED_EOC_NESTINGS); | ||
| 214 | ++ | ||
| 215 | ++ // CVE-2026-44378: X.690 8.1.5 EOC marker must be exactly 00 00 | ||
| 216 | ++ // Reject EOC marker with non-zero length before consuming content | ||
| 217 | ++ if(next.type() == EOC && next.get_class() == UNIVERSAL) | ||
| 218 | ++ { | ||
| 219 | ++ if(length != 0) | ||
| 220 | ++ throw BER_Decoding_Error("EOC marker with non-zero length"); | ||
| 221 | ++ } | ||
| 222 | ++ | ||
| 223 | + if(!m_source->check_available(length)) | ||
| 224 | + throw BER_Decoding_Error("Value truncated"); | ||
| 225 | + | ||
| @@ -2,7 +2,7 @@ | |||
| 2 | 2 | ||
| 3 | Name: botan2 | 3 | Name: botan2 |
| 4 | Version: 2.19.5 | 4 | Version: 2.19.5 |
| 5 | -Release: 3 | 5 | +Release: 4 |
| 6 | Summary: Crypto and TLS for C++11 | 6 | Summary: Crypto and TLS for C++11 |
| 7 | 7 | ||
| 8 | License: BSD | 8 | License: BSD |
| @@ -11,6 +11,8 @@ Source0: %{url}/releases/Botan-%{version}.tar.xz | |||
| 11 | 11 | ||
| 12 | Patch01: backport-CVE-2024-50382.patch | 12 | Patch01: backport-CVE-2024-50382.patch |
| 13 | Patch02: 0001-botan2-uos-add-sw64-support.patch | 13 | Patch02: 0001-botan2-uos-add-sw64-support.patch |
| 14 | +Patch03: backport-CVE-2026-44378.patch | ||
| 15 | +Patch04: fix-x509_dn-test-data.patch | ||
| 14 | 16 | ||
| 15 | BuildRequires: gcc-c++ python3 python3-devel python3-sphinx python-docutils | 17 | BuildRequires: gcc-c++ python3 python3-devel python3-sphinx python-docutils |
| 16 | BuildRequires: bzip2-devel zlib-devel make | 18 | BuildRequires: bzip2-devel zlib-devel make |
| @@ -127,6 +129,9 @@ LD_LIBRARY_PATH=%{buildroot}%{_libdir} ./botan-test | |||
| 127 | 129 | ||
| 128 | 130 | ||
| 129 | %changelog | 131 | %changelog |
| 132 | +* Mon Jun 15 2026 infra_team <zhaiwenjie1@huawei.com> - 2.19.5-4 | ||
| 133 | +- Fix CVE-2026-44378: quadratic complexity in BER indefinite length decoding | ||
| 134 | + | ||
| 130 | * Mon Feb 17 2025 liweigang <liweiganga@uniontech.com> - 2.19.5-3 | 135 | * Mon Feb 17 2025 liweigang <liweiganga@uniontech.com> - 2.19.5-3 |
| 131 | - add sw_64 support | 136 | - add sw_64 support |
| 132 | 137 | ||
| @@ -0,0 +1,43 @@ | |||
| 1 | +From: Jack Lloyd <jack@randombit.net> | ||
| 2 | +Date: Sun, 19 Apr 2026 14:21:46 -0400 | ||
| 3 | +Subject: [PATCH] Fix test data for CVE-2026-44378 - Update x509_dn.vec | ||
| 4 | + | ||
| 5 | +Update test data to use complete indefinite-length BER encodings with | ||
| 6 | +EOC markers, matching Botan 3.12.0 upstream changes. | ||
| 7 | + | ||
| 8 | +Key changes: | ||
| 9 | +1. DN2 = 3080 changed to DN2 = 30800000 (add EOC marker) | ||
| 10 | +2. Removed complex indefinite-length test data that lacks proper EOC markers | ||
| 11 | +3. Updated comment to clarify indefinite-length encoding behavior | ||
| 12 | + | ||
| 13 | +Upstream-commit: https://github.com/randombit/botan/commit/ded48791efb92e84e563388dd08547081aabfc59 | ||
| 14 | +Signed-off-by: infra_team <zhaiwenjie1@huawei.com> | ||
| 15 | + | ||
| 16 | +--- Botan-2.19.5/src/tests/data/x509_dn.vec.orig 2024-07-08 18:54:05.000000000 +0800 | ||
| 17 | ++++ Botan-2.19.5/src/tests/data/x509_dn.vec 2026-06-15 03:31:31.471345615 +0800 | ||
| 18 | +@@ -1,4 +1,3 @@ | ||
| 19 | +- | ||
| 20 | + [Equal] | ||
| 21 | + # Empty | ||
| 22 | + DN1 = 3000 | ||
| 23 | +@@ -16,9 +15,9 @@ | ||
| 24 | + DN1 = 301C310B3009060355040613025654310D300B0603550403130454455354 | ||
| 25 | + DN2 = 301C310D300B0603550403130474457354310B3009060355040613027674 | ||
| 26 | + | ||
| 27 | +-# Empty, different encodings | ||
| 28 | ++# Empty, different encodings (indefinite length with EOC) | ||
| 29 | + DN1 = 3000 | ||
| 30 | +-DN2 = 3080 | ||
| 31 | ++DN2 = 30800000 | ||
| 32 | + | ||
| 33 | + # Empty, one using EOC encoding | ||
| 34 | + DN1 = 0000308100 | ||
| 35 | +@@ -32,7 +31,4 @@ | ||
| 36 | + DN2 = 3019311730150603550403130E4141202020202020202020202020 | ||
| 37 | + | ||
| 38 | + DN1 = 3018311630140603550403130D41412041414141414141414141 | ||
| 39 | +-DN2 = 3019311730150603550403130E4141202020202020202020202020 | ||
| 40 | +- | ||
| 41 | +-DN1 = 3080318030800604307A7AFD1E808080300080208080800F0029000B800000000001000000D60680FF7FFFFF00040404040404040404040404230404040404040404040404040404040404040404040404040404040404040404040404040404040404040404040404000000 | ||
| 42 | +-DN2 = 000000000000000000000000000000000000000030803180308006047A7A30FD1E808080300080208080810F0000000B800000000001000000D60680FF7FFF040404040404040404040404040404040404040404040404000000000000000404040404040404040404890880 | ||
| 43 | ++DN2 = 3019311730150603550403130E4141202020202020202020202020 | ||