summaryrefslogtreecommitdiffstats
path: root/meta-oe
diff options
context:
space:
mode:
authorGyorgy Sarvari <skandigraun@gmail.com>2026-04-06 20:32:54 +0200
committerAnuj Mittal <anuj.mittal@oss.qualcomm.com>2026-04-24 21:13:20 +0530
commit70a903c88847b79ba10fbc5bd40bd537261d77e2 (patch)
treee4bcfc99deb9c829f0322f02fe541cbe3ba1ad1d /meta-oe
parentc4b5bca1e86e481a0376881e5b5eaedfcbedc6bd (diff)
downloadmeta-openembedded-70a903c88847b79ba10fbc5bd40bd537261d77e2.tar.gz
botan: patch CVE-2026-32883
Details: https://nvd.nist.gov/vuln/detail/CVE-2026-32883 Backport the patch that was identified by Debian[1]. The included test passed successfully (along with the other tests). [1]: https://security-tracker.debian.org/tracker/CVE-2026-32883 Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
Diffstat (limited to 'meta-oe')
-rw-r--r--meta-oe/recipes-crypto/botan/botan/CVE-2026-32883.patch171
-rw-r--r--meta-oe/recipes-crypto/botan/botan_3.10.0.bb1
2 files changed, 172 insertions, 0 deletions
diff --git a/meta-oe/recipes-crypto/botan/botan/CVE-2026-32883.patch b/meta-oe/recipes-crypto/botan/botan/CVE-2026-32883.patch
new file mode 100644
index 0000000000..9cace2118e
--- /dev/null
+++ b/meta-oe/recipes-crypto/botan/botan/CVE-2026-32883.patch
@@ -0,0 +1,171 @@
1From 38a49aafc8115ddb6275ca6bbb8748b3d7b3064d Mon Sep 17 00:00:00 2001
2From: Jack Lloyd <jack@randombit.net>
3Date: Sun, 15 Mar 2026 10:28:52 -0400
4Subject: [PATCH] Fix OCSP response verification
5
6During a refactoring of OCSP (#3067) the check that the OCSP response signature
7is valid was omitted. This allows OCSP responses to be forged.
8
9CVE: CVE-2026-32883
10Upstream-Status: Backport [https://github.com/randombit/botan/commit/acbffadcede18b36eea42beae57e6cae4b4da4a0]
11Signed-off-by: Gyorgy Sarvari <skandigraun@gmail.com>
12---
13 src/lib/x509/x509path.cpp | 67 +++++++++++++++++++++++----------------
14 src/tests/test_ocsp.cpp | 47 +++++++++++++++++++++++++++
15 2 files changed, 87 insertions(+), 27 deletions(-)
16
17diff --git a/src/lib/x509/x509path.cpp b/src/lib/x509/x509path.cpp
18index 4059d32..8212bb2 100644
19--- a/src/lib/x509/x509path.cpp
20+++ b/src/lib/x509/x509path.cpp
21@@ -298,6 +298,41 @@ Certificate_Status_Code verify_ocsp_signing_cert(const X509_Certificate& signing
22 return validation_result.result();
23 }
24
25+std::set<Certificate_Status_Code> evaluate_ocsp_response(const OCSP::Response& ocsp_response,
26+ const X509_Certificate& subject,
27+ const X509_Certificate& ca,
28+ const std::vector<X509_Certificate>& cert_path,
29+ const std::vector<Certificate_Store*>& certstores,
30+ std::chrono::system_clock::time_point ref_time,
31+ const Path_Validation_Restrictions& restrictions) {
32+ // Handle softfail conditions (eg. OCSP unavailable)
33+ if(auto dummy_status = ocsp_response.dummy_status()) {
34+ return {dummy_status.value()};
35+ }
36+
37+ // Find the certificate that signed this OCSP response
38+ auto signing_cert = ocsp_response.find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
39+ if(!signing_cert) {
40+ return {Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND};
41+ }
42+
43+ // Verify the signing certificate is trusted
44+ auto cert_status = verify_ocsp_signing_cert(
45+ signing_cert.value(), ca, concat(ocsp_response.certificates(), cert_path), certstores, ref_time, restrictions);
46+ if(cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
47+ return {cert_status, Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED};
48+ }
49+
50+ // Verify the cryptographic signature on the OCSP response
51+ auto sig_status = ocsp_response.verify_signature(signing_cert.value());
52+ if(sig_status != Certificate_Status_Code::OCSP_SIGNATURE_OK) {
53+ return {sig_status};
54+ }
55+
56+ // All checks passed, return the certificate's revocation status
57+ return {ocsp_response.status_for(ca, subject, ref_time, restrictions.max_ocsp_age())};
58+}
59+
60 } // namespace
61
62 CertificatePathStatusCodes PKIX::check_ocsp(const std::vector<X509_Certificate>& cert_path,
63@@ -312,38 +347,16 @@ CertificatePathStatusCodes PKIX::check_ocsp(const std::vector<X509_Certificate>&
64 CertificatePathStatusCodes cert_status(cert_path.size() - 1);
65
66 for(size_t i = 0; i != cert_path.size() - 1; ++i) {
67- std::set<Certificate_Status_Code>& status = cert_status.at(i);
68-
69 const X509_Certificate& subject = cert_path.at(i);
70 const X509_Certificate& ca = cert_path.at(i + 1);
71
72- if(i < ocsp_responses.size() && (ocsp_responses.at(i) != std::nullopt) &&
73- (ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful)) {
74+ if(i < ocsp_responses.size() && ocsp_responses.at(i).has_value() &&
75+ ocsp_responses.at(i)->status() == OCSP::Response_Status_Code::Successful) {
76 try {
77- const auto& ocsp_response = ocsp_responses.at(i);
78-
79- if(auto dummy_status = ocsp_response->dummy_status()) {
80- // handle softfail conditions
81- status.insert(dummy_status.value());
82- } else if(auto signing_cert =
83- ocsp_response->find_signing_certificate(ca, restrictions.trusted_ocsp_responders());
84- !signing_cert) {
85- status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_FOUND);
86- } else if(auto ocsp_signing_cert_status =
87- verify_ocsp_signing_cert(signing_cert.value(),
88- ca,
89- concat(ocsp_response->certificates(), cert_path),
90- certstores,
91- ref_time,
92- restrictions);
93- ocsp_signing_cert_status > Certificate_Status_Code::FIRST_ERROR_STATUS) {
94- status.insert(ocsp_signing_cert_status);
95- status.insert(Certificate_Status_Code::OCSP_ISSUER_NOT_TRUSTED);
96- } else {
97- status.insert(ocsp_response->status_for(ca, subject, ref_time, restrictions.max_ocsp_age()));
98- }
99+ cert_status.at(i) = evaluate_ocsp_response(
100+ ocsp_responses.at(i).value(), subject, ca, cert_path, certstores, ref_time, restrictions);
101 } catch(Exception&) {
102- status.insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
103+ cert_status.at(i).insert(Certificate_Status_Code::OCSP_RESPONSE_INVALID);
104 }
105 }
106 }
107diff --git a/src/tests/test_ocsp.cpp b/src/tests/test_ocsp.cpp
108index 06383cd..54b2c03 100644
109--- a/src/tests/test_ocsp.cpp
110+++ b/src/tests/test_ocsp.cpp
111@@ -408,6 +408,52 @@ class OCSP_Tests final : public Test {
112 return result;
113 }
114
115+ static Test::Result test_forged_ocsp_signature_is_rejected() {
116+ Test::Result result("OCSP response with forged signature is rejected by path validation");
117+
118+ auto ee = load_test_X509_cert("x509/ocsp/randombit.pem");
119+ auto ca = load_test_X509_cert("x509/ocsp/letsencrypt.pem");
120+ auto trust_root = load_test_X509_cert("x509/ocsp/geotrust.pem");
121+
122+ const std::vector<Botan::X509_Certificate> cert_path = {ee, ca, trust_root};
123+
124+ Botan::Certificate_Store_In_Memory certstore;
125+ certstore.add_certificate(trust_root);
126+
127+ const auto valid_time = Botan::calendar_point(2016, 11, 18, 12, 30, 0).to_std_timepoint();
128+
129+ // Verify the unmodified response is accepted
130+ {
131+ auto ocsp = load_test_OCSP_resp("x509/ocsp/randombit_ocsp.der");
132+ const auto ocsp_status = Botan::PKIX::check_ocsp(
133+ cert_path, {ocsp}, {&certstore}, valid_time, Botan::Path_Validation_Restrictions());
134+
135+ if(result.test_eq("Legitimate: expected result count", ocsp_status.size(), 1) &&
136+ result.test_eq("Legitimate: expected status count", ocsp_status[0].size(), 1)) {
137+ result.test_eq("Legitimate response is accepted",
138+ ocsp_status[0].contains(Botan::Certificate_Status_Code::OCSP_RESPONSE_GOOD), true);
139+ }
140+ }
141+
142+ // Tamper with the signature and verify check_ocsp rejects it
143+ {
144+ auto ocsp_bytes = Test::read_binary_data_file("x509/ocsp/randombit_ocsp.der");
145+ ocsp_bytes.back() ^= 0x01;
146+ Botan::OCSP::Response forged_ocsp(ocsp_bytes.data(), ocsp_bytes.size());
147+
148+ const auto ocsp_status = Botan::PKIX::check_ocsp(
149+ cert_path, {forged_ocsp}, {&certstore}, valid_time, Botan::Path_Validation_Restrictions());
150+
151+ if(result.test_eq("Forged: expected result count", ocsp_status.size(), 1) &&
152+ result.test_eq("Forged: expected status count", ocsp_status[0].size(), 1)) {
153+ result.test_eq("Forged signature is rejected",
154+ ocsp_status[0].contains(Botan::Certificate_Status_Code::OCSP_SIGNATURE_ERROR), true);
155+ }
156+ }
157+
158+ return result;
159+ }
160+
161 static Test::Result test_responder_cert_with_nocheck_extension() {
162 Test::Result result("BDr's OCSP response contains certificate featuring NoCheck extension");
163
164@@ -436,6 +482,7 @@ class OCSP_Tests final : public Test {
165 results.push_back(test_response_verification_without_next_update_without_max_age());
166 results.push_back(test_response_verification_softfail());
167 results.push_back(test_response_verification_with_additionally_trusted_responder());
168+ results.push_back(test_forged_ocsp_signature_is_rejected());
169 results.push_back(test_responder_cert_with_nocheck_extension());
170
171 #if defined(BOTAN_HAS_ONLINE_REVOCATION_CHECKS)
diff --git a/meta-oe/recipes-crypto/botan/botan_3.10.0.bb b/meta-oe/recipes-crypto/botan/botan_3.10.0.bb
index 392c1aac94..b613dd2a04 100644
--- a/meta-oe/recipes-crypto/botan/botan_3.10.0.bb
+++ b/meta-oe/recipes-crypto/botan/botan_3.10.0.bb
@@ -6,6 +6,7 @@ SECTION = "libs"
6 6
7SRC_URI = "https://botan.randombit.net/releases/Botan-${PV}.tar.xz \ 7SRC_URI = "https://botan.randombit.net/releases/Botan-${PV}.tar.xz \
8 file://CVE-2026-32877.patch \ 8 file://CVE-2026-32877.patch \
9 file://CVE-2026-32883.patch \
9 " 10 "
10SRC_URI[sha256sum] = "fde194236f6d5434f136ea0a0627f6cc9d26af8b96e9f1e1c7d8c82cd90f4f24" 11SRC_URI[sha256sum] = "fde194236f6d5434f136ea0a0627f6cc9d26af8b96e9f1e1c7d8c82cd90f4f24"
11 12