已关闭
fix(cve): 修复 CVE-2026-44378 在 botan2 中的漏洞 #39
infra_team创建于 6月15日关闭于 14 天前
fix(cve): 修复 CVE-2026-44378 在 botan2 中的漏洞 #39
已关闭
infra_team创建于 6月15日关闭于 14 天前
共 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 
3Name: botan23Name: botan2
4Version: 2.19.54Version: 2.19.5
5-Release: 35+Release: 4
6Summary: Crypto and TLS for C++116Summary: Crypto and TLS for C++11
7 7 
8License: BSD8License: BSD
@@ -11,6 +11,8 @@ Source0: %{url}/releases/Botan-%{version}.tar.xz
11 11 
12Patch01: backport-CVE-2024-50382.patch12Patch01: backport-CVE-2024-50382.patch
13Patch02: 0001-botan2-uos-add-sw64-support.patch13Patch02: 0001-botan2-uos-add-sw64-support.patch
14+Patch03: backport-CVE-2026-44378.patch
15+Patch04: fix-x509_dn-test-data.patch
14 16 
15BuildRequires: gcc-c++ python3 python3-devel python3-sphinx python-docutils17BuildRequires: gcc-c++ python3 python3-devel python3-sphinx python-docutils
16BuildRequires: bzip2-devel zlib-devel make18BuildRequires: bzip2-devel zlib-devel make
@@ -127,6 +129,9 @@ LD_LIBRARY_PATH=%{buildroot}%{_libdir} ./botan-test
127 129 
128 130 
129%changelog131%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-3135* Mon Feb 17 2025 liweigang <liweiganga@uniontech.com> - 2.19.5-3
131- add sw_64 support136- 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