已开启
fix(cve): 修复 CVE-2026-35334 在 strongswan 中的漏洞 #107
infra_team创建于 6月13日
fix(cve): 修复 CVE-2026-35334 在 strongswan 中的漏洞 #107
已开启
共 2 个文件变更+256-1
| @@ -0,0 +1,250 @@ | |||
| 1 | +From c23cf29373bb25a24b952ea4c5bf7ea326b78714 Mon Sep 17 00:00:00 2001 | ||
| 2 | +From: Tobias Brunner <tobias@strongswan.org> | ||
| 3 | +Date: Tue, 24 Mar 2026 18:00:23 +0100 | ||
| 4 | +Subject: [PATCH] gmp: Avoid crash and timing leaks in PKCS#1 v1.5 decryption | ||
| 5 | + padding validation | ||
| 6 | +MIME-Version: 1.0 | ||
| 7 | +Content-Type: text/plain; charset=UTF-8 | ||
| 8 | +Content-Transfer-Encoding: 8bit | ||
| 9 | + | ||
| 10 | +This fixes a potential crash due to a null-pointer dereference if rsadp() | ||
| 11 | +returns NULL (e.g. with an all-zero ciphertext). | ||
| 12 | + | ||
| 13 | +And it also implements the PKCS#1 v1.5 decryption padding check in | ||
| 14 | +constant time. | ||
| 15 | + | ||
| 16 | +The timing leak caused by the previous implementation was measured at | ||
| 17 | +~17.5 μs at 3 GHz, which could allow a Bleichenbacher-like attack in | ||
| 18 | +LAN environments. However, because of how RSA encryption is used in | ||
| 19 | +strongSwan, this is not that much of an issue in practice. The mechanism | ||
| 20 | +is only used for two use cases. One is SCEP/EST via PKCS#7 enveloped | ||
| 21 | +data. Fortunately, this can not be triggered in significant numbers by | ||
| 22 | +an attacker. The other use case is TLS as used by EAP methods (EAP-TLS, | ||
| 23 | +EAP-PEAP/TTLS) during the authentication. While the cipher suites that | ||
| 24 | +use RSA encryption are still enabled by default, the TLS messages are | ||
| 25 | +wrapped in EAP and encrypted by IKE, making any kind of attack difficult. | ||
| 26 | + | ||
| 27 | +Note that the gmp plugin isn't enabled anymore by default. And even | ||
| 28 | +before that, most setups had the openssl plugin enabled, which has | ||
| 29 | +priority over the gmp plugin. So it's unlikely the plugin was used in | ||
| 30 | +practice. | ||
| 31 | + | ||
| 32 | +Also note that this patch doesn't modify libstrongswan's Makefile.am | ||
| 33 | +to avoid potentially requiring autotools when patching a tarball. | ||
| 34 | + | ||
| 35 | +Fixes: d615ffdcf3cd ("implement gmp_rsa_private_key.decrypt()") | ||
| 36 | +Fixes: CVE-2026-35334 | ||
| 37 | +--- | ||
| 38 | + .../plugins/gmp/gmp_rsa_private_key.c | 54 ++++++--- | ||
| 39 | + src/libstrongswan/utils/utils.h | 1 + | ||
| 40 | + src/libstrongswan/utils/utils/constant_time.h | 103 ++++++++++++++++++ | ||
| 41 | + 3 files changed, 140 insertions(+), 18 deletions(-) | ||
| 42 | + create mode 100644 src/libstrongswan/utils/utils/constant_time.h | ||
| 43 | + | ||
| 44 | +diff --git a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c | ||
| 45 | +index 63e8f54cddcb..64f730fdf7c3 100644 | ||
| 46 | +--- a/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c | ||
| 47 | ++++ b/src/libstrongswan/plugins/gmp/gmp_rsa_private_key.c | ||
| 48 | +@@ -495,8 +495,8 @@ METHOD(private_key_t, decrypt, bool, | ||
| 49 | + private_gmp_rsa_private_key_t *this, encryption_scheme_t scheme, | ||
| 50 | + void *params, chunk_t crypto, chunk_t *plain) | ||
| 51 | + { | ||
| 52 | +- chunk_t em, stripped; | ||
| 53 | +- bool success = FALSE; | ||
| 54 | ++ chunk_t em; | ||
| 55 | ++ u_int valid, i, j, found_sep = 0, sep_index = 0, m_index; | ||
| 56 | + | ||
| 57 | + if (scheme != ENCRYPT_RSA_PKCS1) | ||
| 58 | + { | ||
| 59 | +@@ -505,33 +505,51 @@ METHOD(private_key_t, decrypt, bool, | ||
| 60 | + return FALSE; | ||
| 61 | + } | ||
| 62 | + /* rsa decryption using PKCS#1 RSADP */ | ||
| 63 | +- stripped = em = rsadp(this, crypto); | ||
| 64 | ++ em = rsadp(this, crypto); | ||
| 65 | ++ if (em.len != this->k) | ||
| 66 | ++ { | ||
| 67 | ++ return FALSE; | ||
| 68 | ++ } | ||
| 69 | + | ||
| 70 | +- /* PKCS#1 v1.5 8.1 encryption-block formatting (EB = 00 || 02 || PS || 00 || D) */ | ||
| 71 | ++ /* PKCS#1 v1.5, RFC 8017, section 7.2.2 message structure: | ||
| 72 | ++ * EM = 00 || 02 || PS || 00 || M */ | ||
| 73 | + | ||
| 74 | + /* check for hex pattern 00 02 in decrypted message */ | ||
| 75 | +- if ((*stripped.ptr++ != 0x00) || (*(stripped.ptr++) != 0x02)) | ||
| 76 | ++ valid = constant_time_eq(em.ptr[0], 0x00); | ||
| 77 | ++ valid &= constant_time_eq(em.ptr[1], 0x02); | ||
| 78 | ++ | ||
| 79 | ++ /* the plaintext data starts after first 0x00 byte */ | ||
| 80 | ++ for (i = 2; i < em.len; i++) | ||
| 81 | + { | ||
| 82 | +- DBG1(DBG_LIB, "incorrect padding - probably wrong rsa key"); | ||
| 83 | +- goto end; | ||
| 84 | ++ u_int zero = constant_time_eq(em.ptr[i], 0x00); | ||
| 85 | ++ | ||
| 86 | ++ sep_index = constant_time_select(i, sep_index, ~found_sep & zero); | ||
| 87 | ++ found_sep |= zero; | ||
| 88 | + } | ||
| 89 | +- stripped.len -= 2; | ||
| 90 | + | ||
| 91 | +- /* the plaintext data starts after first 0x00 byte */ | ||
| 92 | +- while (stripped.len-- > 0 && *stripped.ptr++ != 0x00) | ||
| 93 | ++ /* make sure PS is at least eight bytes long (plus the initial bytes) */ | ||
| 94 | ++ valid &= constant_time_ge(sep_index, 10); | ||
| 95 | ++ | ||
| 96 | ++ /* instead of copying the message directly, we try not to reveal the message | ||
| 97 | ++ * length i.e. where the 0x00 byte was. and since clearing a chunk is | ||
| 98 | ++ * relatively efficient, i.e. doesn't leak much, we always allocate and copy | ||
| 99 | ++ * a value and then clear it if the structure was invalid */ | ||
| 100 | ++ m_index = constant_time_select(sep_index + 1, 11, valid); | ||
| 101 | + | ||
| 102 | +- if (stripped.len == 0) | ||
| 103 | ++ *plain = chunk_alloc(this->k); | ||
| 104 | ++ for (i = 0, j = 0; i < em.len; i++) | ||
| 105 | + { | ||
| 106 | +- DBG1(DBG_LIB, "no plaintext data"); | ||
| 107 | +- goto end; | ||
| 108 | ++ plain->ptr[j] = em.ptr[i]; | ||
| 109 | ++ j += constant_time_ge(i, m_index); | ||
| 110 | + } | ||
| 111 | ++ plain->len = j; | ||
| 112 | + | ||
| 113 | +- *plain = chunk_clone(stripped); | ||
| 114 | +- success = TRUE; | ||
| 115 | +- | ||
| 116 | +-end: | ||
| 117 | ++ if (!valid) | ||
| 118 | ++ { | ||
| 119 | ++ chunk_clear(plain); | ||
| 120 | ++ } | ||
| 121 | + chunk_clear(&em); | ||
| 122 | +- return success; | ||
| 123 | ++ return valid; | ||
| 124 | + } | ||
| 125 | + | ||
| 126 | + METHOD(private_key_t, get_keysize, int, | ||
| 127 | +diff --git a/src/libstrongswan/utils/utils.h b/src/libstrongswan/utils/utils.h | ||
| 128 | +index 42d01144a2f6..ab0be7206ee9 100644 | ||
| 129 | +--- a/src/libstrongswan/utils/utils.h | ||
| 130 | ++++ b/src/libstrongswan/utils/utils.h | ||
| 131 | +@@ -53,6 +53,7 @@ | ||
| 132 | + #include "utils/atomics.h" | ||
| 133 | + #include "utils/align.h" | ||
| 134 | + #include "utils/byteorder.h" | ||
| 135 | ++#include "utils/constant_time.h" | ||
| 136 | + #include "utils/string.h" | ||
| 137 | + #include "utils/memory.h" | ||
| 138 | + #include "utils/strerror.h" | ||
| 139 | +diff --git a/src/libstrongswan/utils/utils/constant_time.h b/src/libstrongswan/utils/utils/constant_time.h | ||
| 140 | +new file mode 100644 | ||
| 141 | +index 000000000000..0c2c6a2e04ae | ||
| 142 | +--- /dev/null | ||
| 143 | ++++ b/src/libstrongswan/utils/utils/constant_time.h | ||
| 144 | +@@ -0,0 +1,103 @@ | ||
| 145 | ++/* | ||
| 146 | ++ * Copyright (C) 2026 Tobias Brunner | ||
| 147 | ++ * | ||
| 148 | ++ * Copyright (C) secunet Security Networks AG | ||
| 149 | ++ * | ||
| 150 | ++ * This program is free software; you can redistribute it and/or modify it | ||
| 151 | ++ * under the terms of the GNU General Public License as published by the | ||
| 152 | ++ * Free Software Foundation; either version 2 of the License, or (at your | ||
| 153 | ++ * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>. | ||
| 154 | ++ * | ||
| 155 | ++ * This program is distributed in the hope that it will be useful, but | ||
| 156 | ++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY | ||
| 157 | ++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | ||
| 158 | ++ * for more details. | ||
| 159 | ++ */ | ||
| 160 | ++ | ||
| 161 | ++/** | ||
| 162 | ++ * @defgroup constant_time_i constant_time | ||
| 163 | ++ * @{ @ingroup constant_time_i | ||
| 164 | ++ */ | ||
| 165 | ++ | ||
| 166 | ++#ifndef CONSTANT_TIME_H_ | ||
| 167 | ++#define CONSTANT_TIME_H_ | ||
| 168 | ++ | ||
| 169 | ++#include <stdint.h> | ||
| 170 | ++ | ||
| 171 | ++/** | ||
| 172 | ++ * Check if the given values are not equal in constant time. | ||
| 173 | ++ * | ||
| 174 | ++ * @param x first value to check | ||
| 175 | ++ * @param y second value to check | ||
| 176 | ++ * @return 1 if values are not equal, 0 otherwise | ||
| 177 | ++ */ | ||
| 178 | ++static inline u_int constant_time_neq(uint32_t x, uint32_t y) | ||
| 179 | ++{ | ||
| 180 | ++ return ((x-y) | (y-x)) >> 31; | ||
| 181 | ++} | ||
| 182 | ++ | ||
| 183 | ++/** | ||
| 184 | ++ * Check if the given values are equal in constant time. | ||
| 185 | ++ * | ||
| 186 | ++ * @param x first value to check | ||
| 187 | ++ * @param y second value to check | ||
| 188 | ++ * @return 1 if values are equal, 0 otherwise | ||
| 189 | ++ */ | ||
| 190 | ++static inline u_int constant_time_eq(uint32_t x, uint32_t y) | ||
| 191 | ++{ | ||
| 192 | ++ return 1 ^ constant_time_neq(x, y); | ||
| 193 | ++} | ||
| 194 | ++ | ||
| 195 | ++/** | ||
| 196 | ++ * Compare the two values and return 1 if the first argument is lower than | ||
| 197 | ++ * the second in constant time. | ||
| 198 | ++ * | ||
| 199 | ++ * @param x first value to check | ||
| 200 | ++ * @param y second value to check | ||
| 201 | ++ * @return 1 if first value is lower than second | ||
| 202 | ++ */ | ||
| 203 | ++static inline u_int constant_time_lt(uint32_t x, uint32_t y) | ||
| 204 | ++{ | ||
| 205 | ++ return (x ^ ((x^y) | ((x-y) ^ y))) >> 31; | ||
| 206 | ++} | ||
| 207 | ++ | ||
| 208 | ++/** | ||
| 209 | ++ * Compare the two values and return 1 if the first argument greater or equal to | ||
| 210 | ++ * the second in constant time. | ||
| 211 | ++ * | ||
| 212 | ++ * @param x first value to check | ||
| 213 | ++ * @param y second value to check | ||
| 214 | ++ * @return 1 if first value is greater or equal to the second | ||
| 215 | ++ */ | ||
| 216 | ++static inline u_int constant_time_ge(uint32_t x, uint32_t y) | ||
| 217 | ++{ | ||
| 218 | ++ return 1 ^ constant_time_lt(x, y); | ||
| 219 | ++} | ||
| 220 | ++ | ||
| 221 | ++/** | ||
| 222 | ++ * Return a 32-bit all bit-set mask if the given value is not 0. | ||
| 223 | ++ * | ||
| 224 | ++ * @param x value to check | ||
| 225 | ++ * @return 0xffffffff if value is != 0, 0 otherwise | ||
| 226 | ++ */ | ||
| 227 | ++static inline uint32_t constant_time_mask(uint32_t x) | ||
| 228 | ++{ | ||
| 229 | ++ return -(uint32_t)constant_time_neq(x, 0); | ||
| 230 | ++} | ||
| 231 | ++ | ||
| 232 | ++/** | ||
| 233 | ++ * Select one of two values depending on whether the condition is != 0 or not. | ||
| 234 | ++ * Basically equivalent to 'c ? x : y'. | ||
| 235 | ++ * | ||
| 236 | ++ * @param x first value to select | ||
| 237 | ++ * @param y second value to select | ||
| 238 | ++ * @param c condition | ||
| 239 | ++ * @return x if c is != 0, y otherwise | ||
| 240 | ++ */ | ||
| 241 | ++static inline uint32_t constant_time_select(uint32_t x, uint32_t y, uint32_t c) | ||
| 242 | ++{ | ||
| 243 | ++ uint32_t m = constant_time_mask(c); | ||
| 244 | ++ return (x & m) | (y & ~m); | ||
| 245 | ++} | ||
| 246 | ++ | ||
| 247 | ++#endif /** CONSTANT_TIME_H_ @} */ | ||
| 248 | +-- | ||
| 249 | +2.43.0 | ||
| 250 | + | ||
| @@ -1,6 +1,6 @@ | |||
| 1 | Name: strongswan | 1 | Name: strongswan |
| 2 | Version: 5.9.14 | 2 | Version: 5.9.14 |
| 3 | -Release: 2 | 3 | +Release: 3 |
| 4 | Summary: An OpenSource IPsec-based VPN and TNC solution | 4 | Summary: An OpenSource IPsec-based VPN and TNC solution |
| 5 | License: GPL-2.0-or-later | 5 | License: GPL-2.0-or-later |
| 6 | URL: https://www.strongswan.org/ | 6 | URL: https://www.strongswan.org/ |
| @@ -11,6 +11,8 @@ Patch1: aes-crypter-support-sw64-arch.patch | |||
| 11 | Patch2: strongswan-5.9.14-harden-strongswan.service.patch | 11 | Patch2: strongswan-5.9.14-harden-strongswan.service.patch |
| 12 | # https://download.strongswan.org/security/CVE-2025-62291/strongswan-4.4.0-6.0.2_eap_mschapv2_failure_request_len.patch | 12 | # https://download.strongswan.org/security/CVE-2025-62291/strongswan-4.4.0-6.0.2_eap_mschapv2_failure_request_len.patch |
| 13 | Patch6001: strongswan-4.4.0-6.0.2_eap_mschapv2_failure_request_len.patch | 13 | Patch6001: strongswan-4.4.0-6.0.2_eap_mschapv2_failure_request_len.patch |
| 14 | +# https://download.strongswan.org/security/CVE-2026-35334/strongswan-5.3.1-6.0.5_gmp_rsa_decrypt_pad.patch | ||
| 15 | +Patch6002: strongswan-5.3.1-6.0.5_gmp_rsa_decrypt_pad.patch | ||
| 14 | 16 | ||
| 15 | BuildRequires: gcc chrpath autoconf automake libtool tpm2-abrmd | 17 | BuildRequires: gcc chrpath autoconf automake libtool tpm2-abrmd |
| 16 | BuildRequires: systemd-devel gmp-devel libcurl-devel NetworkManager-libnm-devel openldap-devel | 18 | BuildRequires: systemd-devel gmp-devel libcurl-devel NetworkManager-libnm-devel openldap-devel |
| @@ -195,6 +197,9 @@ echo "%{_libdir}/strongswan" > %{buildroot}/etc/ld.so.conf.d/%{name}-%{_arch}.co | |||
| 195 | %{_libexecdir}/strongswan/charon-nm | 197 | %{_libexecdir}/strongswan/charon-nm |
| 196 | 198 | ||
| 197 | %changelog | 199 | %changelog |
| 200 | +* Sat Jun 13 2026 infra_team <zhaiwenjie1@huawei.com> - 5.9.14-3 | ||
| 201 | +- fix CVE-2026-35334: gmp plugin RSA decryption NULL pointer dereference | ||
| 202 | + | ||
| 198 | * Sat Nov 08 2025 Funda Wang <fundawang@yeah.net> - 5.9.14-2 | 203 | * Sat Nov 08 2025 Funda Wang <fundawang@yeah.net> - 5.9.14-2 |
| 199 | - fix CVE-2025-62291 | 204 | - fix CVE-2025-62291 |
| 200 | 205 | ||