Skip to content

Commit 395aa06

Browse files
thnielsenawazn
authored andcommitted
Bug#35489217 default cipher list contains additional deprecated TLS ciphers
Update unacceptable cipher list: 3DES and LOW permanently removed. Add tls_server_context unit test to harness to verify mandatory, acceptable, deprecated and unacceptable cipher handling. Change-Id: I706990e3e6b7420f21f2a5c83137a21492394049
1 parent 7ce1877 commit 395aa06

File tree

3 files changed

+362
-3
lines changed

3 files changed

+362
-3
lines changed

router/src/harness/include/mysql/harness/tls_server_context.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,9 @@ class HARNESS_TLS_EXPORT TlsServerContext : public TlsContext {
4949
*
5050
* they are filtered out if set through cipher_list()
5151
*/
52-
static constexpr std::array<const char *, 9> unacceptable_cipher_spec{
53-
"!aNULL", "!eNULL", "!EXPORT", "!MD5", "!DES",
54-
"!RC2", "!RC4", "!PSK", "!SSLv3"};
52+
static constexpr std::array<const char *, 12> unacceptable_cipher_spec{
53+
"!aNULL", "!eNULL", "!EXPORT", "!LOW", "!MD5", "!DES",
54+
"!3DES", "!RC2", "!RC4", "!PSK", "!kDH", "!SSLv3"};
5555

5656
/**
5757
* construct a TLS Context for server-side.

router/src/harness/tests/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,18 @@ FOREACH(TEST ${TESTS})
100100
LIB_DEPENDS test-helpers;${SSL_LIBRARIES})
101101
ENDFOREACH()
102102

103+
SET(TESTS
104+
test_tls_server_context.cc
105+
)
106+
107+
FOREACH(TEST ${TESTS})
108+
ADD_HARNESS_TEST_FILE(${TEST} MODULE ${TEST_MODULE}
109+
INCLUDE_DIRS ${MySQLRouter_SOURCE_DIR}/src/harness/shared/include/
110+
INCLUDE_DIRS ${MySQLRouter_SOURCE_DIR}/src/harness/include/
111+
INCLUDE_DIRS ${MySQLRouter_BINARY_DIR}/src/harness/include/
112+
LIB_DEPENDS harness_tls;test-helpers;${SSL_LIBRARIES})
113+
ENDFOREACH()
114+
103115
SET(TESTS
104116
test_net_ts_buffer.cc
105117
test_net_ts_executor.cc
Lines changed: 347 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,347 @@
1+
/*
2+
Copyright (c) 2023, Oracle and/or its affiliates.
3+
4+
This program is free software; you can redistribute it and/or modify
5+
it under the terms of the GNU General Public License, version 2.0,
6+
as published by the Free Software Foundation.
7+
8+
This program is also distributed with certain software (including
9+
but not limited to OpenSSL) that is licensed under separate terms,
10+
as designated in a particular file or component or in included license
11+
documentation. The authors of MySQL hereby grant you an additional
12+
permission to link the program and your derivative works with the
13+
separately licensed software that they have included with MySQL.
14+
15+
This program is distributed in the hope that it will be useful,
16+
but WITHOUT ANY WARRANTY; without even the implied warranty of
17+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18+
GNU General Public License for more details.
19+
20+
You should have received a copy of the GNU General Public License
21+
along with this program; if not, write to the Free Software
22+
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23+
*/
24+
25+
#include <gmock/gmock.h>
26+
#include <gtest/gtest.h>
27+
28+
#include "test/helpers.h"
29+
30+
#include "mysql/harness/tls_server_context.h"
31+
#include "mysql/harness/tls_types.h"
32+
#include "mysql/harness/utility/string.h" // join()
33+
#include "openssl_version.h"
34+
35+
using ::testing::AnyOf;
36+
using ::testing::Contains;
37+
using ::testing::Not;
38+
39+
class TlsServerContextTest : public ::testing::Test {
40+
public:
41+
TlsLibraryContext m_tls_lib_ctx;
42+
};
43+
44+
// Check .cipher_list() returns mandatory ciphers with default ciphers
45+
TEST_F(TlsServerContextTest, CiphersMandatory) {
46+
TlsServerContext ctx;
47+
48+
// set default cipher_list
49+
std::string ciphers =
50+
mysql_harness::join(TlsServerContext::default_ciphers(), ":");
51+
ctx.cipher_list(ciphers);
52+
53+
// Get the filtered cipher list
54+
mysql_harness::Ssl ssl{SSL_new(ctx.get())};
55+
int prio = 0;
56+
std::vector<std::string> r{};
57+
while (auto c = SSL_get_cipher_list(ssl.get(), prio++)) {
58+
r.emplace_back(c);
59+
}
60+
61+
// Require at least one of the mandatory ciphers
62+
EXPECT_THAT(r, AnyOf(Contains("ECDHE-ECDSA-AES128-GCM-SHA256"),
63+
Contains("ECDHE-ECDSA-AES256-GCM-SHA384"),
64+
Contains("ECDHE-RSA-AES128-GCM-SHA256")));
65+
}
66+
67+
class CiphersAcceptable : public TlsServerContextTest,
68+
public ::testing::WithParamInterface<std::string> {};
69+
70+
// Check .cipher_list() returns acceptable ciphers if used
71+
TEST_P(CiphersAcceptable, CiphersAcceptableParam) {
72+
std::string cipher = GetParam();
73+
74+
TlsServerContext ctx;
75+
76+
// set cipher_list to cipher
77+
ctx.cipher_list(cipher);
78+
79+
// Get the filtered cipher list
80+
mysql_harness::Ssl ssl{SSL_new(ctx.get())};
81+
int prio = 0;
82+
std::vector<std::string> r{};
83+
while (auto c = SSL_get_cipher_list(ssl.get(), prio++)) {
84+
r.emplace_back(c);
85+
}
86+
87+
EXPECT_THAT(r, Contains(cipher));
88+
}
89+
90+
static const std::string acceptable_ciphers_test_data[] = {
91+
#if OPENSSL_VERSION_NUMBER >= ROUTER_OPENSSL_VERSION(1, 1, 1)
92+
// TLSv1.3
93+
{"TLS_AES_128_GCM_SHA256"},
94+
{"TLS_AES_256_GCM_SHA384"},
95+
{"TLS_CHACHA20_POLY1305_SHA256"},
96+
#if 0 // embedded
97+
{"TLS_AES_128_CCM_SHA256"},
98+
#endif
99+
#endif
100+
// TLSv1.2
101+
{"ECDHE-RSA-AES256-GCM-SHA384"},
102+
{"DHE-RSA-AES128-GCM-SHA256"},
103+
{"DHE-RSA-AES256-GCM-SHA384"},
104+
#if OPENSSL_VERSION_NUMBER >= ROUTER_OPENSSL_VERSION(1, 1, 0)
105+
{"ECDHE-ECDSA-CHACHA20-POLY1305"},
106+
{"ECDHE-RSA-CHACHA20-POLY1305"},
107+
{"DHE-RSA-CHACHA20-POLY1305"},
108+
#endif
109+
#if 0 // embedded
110+
{"ECDHE-ECDSA-AES256-CCM"},
111+
{"ECDHE-ECDSA-AES128-CCM"},
112+
{"DHE-RSA-AES256-CCM"},
113+
{"DHE-RSA-AES128-CCM"},
114+
#endif
115+
};
116+
117+
INSTANTIATE_TEST_SUITE_P(CiphersAcceptableParam, CiphersAcceptable,
118+
::testing::ValuesIn(acceptable_ciphers_test_data));
119+
120+
class CiphersDeprecated : public TlsServerContextTest,
121+
public ::testing::WithParamInterface<std::string> {};
122+
123+
// Check .cipher_list() returns deprecated ciphers if used
124+
TEST_P(CiphersDeprecated, CiphersDeprecatedParam) {
125+
std::string cipher = GetParam();
126+
127+
TlsServerContext ctx;
128+
129+
// set cipher_list to cipher
130+
ctx.cipher_list(cipher);
131+
132+
// Get the filtered cipher list
133+
mysql_harness::Ssl ssl{SSL_new(ctx.get())};
134+
int prio = 0;
135+
std::vector<std::string> r{};
136+
while (auto c = SSL_get_cipher_list(ssl.get(), prio++)) {
137+
r.emplace_back(c);
138+
}
139+
140+
EXPECT_THAT(r, Contains(cipher));
141+
}
142+
143+
static const std::string deprecated_ciphers_test_data[] = {
144+
{"ECDHE-ECDSA-AES128-SHA256"},
145+
{"ECDHE-RSA-AES128-SHA256"},
146+
{"ECDHE-ECDSA-AES256-SHA384"},
147+
{"ECDHE-RSA-AES256-SHA384"},
148+
{"DHE-DSS-AES256-GCM-SHA384"},
149+
{"DHE-DSS-AES128-GCM-SHA256"},
150+
{"DHE-DSS-AES128-SHA256"},
151+
{"DHE-DSS-AES256-SHA256"},
152+
{"DHE-RSA-AES256-SHA256"},
153+
{"DHE-RSA-AES128-SHA256"},
154+
{"AES128-GCM-SHA256"},
155+
{"AES256-GCM-SHA384"},
156+
{"AES128-SHA256"},
157+
{"AES256-SHA256"},
158+
#if OPENSSL_VERSION_NUMBER >= ROUTER_OPENSSL_VERSION(1, 1, 0)
159+
{"DHE-RSA-CAMELLIA128-SHA256"},
160+
{"DHE-RSA-CAMELLIA256-SHA256"},
161+
{"ECDHE-RSA-AES128-SHA"},
162+
{"ECDHE-ECDSA-AES128-SHA"},
163+
{"ECDHE-RSA-AES256-SHA"},
164+
{"ECDHE-ECDSA-AES256-SHA"},
165+
#endif
166+
#if OPENSSL_VERSION_NUMBER == ROUTER_OPENSSL_VERSION(1, 1, 0)
167+
{"DHE-RSA-CAMELLIA128-SHA"},
168+
{"ECDH-ECDSA-AES128-SHA256"},
169+
{"ECDH-RSA-AES128-SHA256"},
170+
{"ECDH-RSA-AES256-SHA384"},
171+
{"ECDH-ECDSA-AES256-SHA384"},
172+
{"ECDH-ECDSA-AES128-SHA"},
173+
{"DHE-RSA-AES128-SHA"},
174+
{"DHE-RSA-AES256-SHA"},
175+
{"DHE-DSS-AES256-SHA"},
176+
{"DHE-RSA-CAMELLIA256-SHA"},
177+
{"ECDH-ECDSA-AES256-SHA"},
178+
{"ECDH-RSA-AES128-SHA"},
179+
{"ECDH-RSA-AES256-SHA"},
180+
{"AES128-SHA"},
181+
{"AES256-SHA"},
182+
{"CAMELLIA256-SHA"},
183+
{"CAMELLIA128-SHA"},
184+
#endif
185+
#if OPENSSL_VERSION_NUMBER <= ROUTER_OPENSSL_VERSION(1, 1, 0)
186+
{"ECDH-ECDSA-AES128-GCM-SHA256"},
187+
{"ECDH-ECDSA-AES256-GCM-SHA384"},
188+
{"ECDH-RSA-AES128-GCM-SHA256"},
189+
{"ECDH-RSA-AES256-GCM-SHA384"},
190+
#endif
191+
#if 0 // embedded
192+
{"TLS_AES_128_CCM_8_SHA256"},
193+
{"ECDHE-ECDSA-AES256-CCM8"},
194+
{"ECDHE-ECDSA-AES128-CCM8"},
195+
{"DHE-RSA-AES256-CCM8"},
196+
{"DHE-RSA-AES128-CCM8"},
197+
{"AES128-CCM"},
198+
{"AES128-CCM8"},
199+
{"AES256-CCM"},
200+
{"AES256-CCM8"},
201+
#endif
202+
// All TLS-SRP ciphers
203+
};
204+
205+
INSTANTIATE_TEST_SUITE_P(CiphersDeprecatedParam, CiphersDeprecated,
206+
::testing::ValuesIn(deprecated_ciphers_test_data));
207+
208+
class CiphersUnacceptable : public TlsServerContextTest,
209+
public ::testing::WithParamInterface<std::string> {
210+
};
211+
212+
// Check .cipher_list() does not return unacceptable ciphers if used
213+
TEST_P(CiphersUnacceptable, CiphersUnacceptableParam) {
214+
std::string cipher = GetParam();
215+
216+
TlsServerContext ctx;
217+
218+
// set cipher_list to cipher, then get the modified and filtered list
219+
ctx.cipher_list(cipher);
220+
221+
// Get the filtered cipher list
222+
mysql_harness::Ssl ssl{SSL_new(ctx.get())};
223+
int prio = 0;
224+
std::vector<std::string> r{};
225+
while (auto c = SSL_get_cipher_list(ssl.get(), prio++)) {
226+
r.emplace_back(c);
227+
}
228+
229+
EXPECT_THAT(r, Not(Contains(cipher)));
230+
}
231+
232+
static const std::string unacceptable_ciphers_test_data[] = {
233+
{"AECDH-NULL-SHA"},
234+
{"ECDHE-RSA-NULL-SHA"},
235+
{"ECDHE-ECDSA-NULL-SHA"},
236+
{"GOST94-NULL-GOST94"},
237+
{"GOST2001-GOST89-GOST89"},
238+
{"ECDH-RSA-NULL-SHA"},
239+
{"ECDH-ECDSA-NULL-SHA"},
240+
{"NULL-SHA256"},
241+
{"NULL-SHA"},
242+
{"NULL-MD5"},
243+
{"AECDH-AES256-SHA"},
244+
{"ADH-AES256-GCM-SHA384"},
245+
{"ADH-AES256-SHA256"},
246+
{"ADH-AES256-SHA"},
247+
{"ADH-CAMELLIA256-SHA256"},
248+
{"ADH-CAMELLIA256-SHA"},
249+
{"AECDH-AES128-SHA"},
250+
{"ADH-AES128-GCM-SHA256"},
251+
{"ADH-AES128-SHA256"},
252+
{"ADH-AES128-SHA"},
253+
{"ADH-CAMELLIA128-SHA256"},
254+
{"AADH-CAMELLIA128-SHA"},
255+
{"AECDH-RC4-SHA"},
256+
{"ADH-RC4-MD5"},
257+
{"AECDH-DES-CBC3-SHA"},
258+
{"ADH-DES-CBC3-SHA"},
259+
{"ADH-DES-CBC-SHA"},
260+
{"EXP-RC4-MD5"},
261+
{"EXP-RC2-CBC-MD5"},
262+
{"EXP-DES-CBC-SHA"},
263+
// SSL_DH_DSS_EXPORT_WITH_DES40_CBC_SHA
264+
// SSL_DH_RSA_EXPORT_WITH_DES40_CBC_SHA
265+
{"EXP-DH-DSS-DES-CBC-SHA"},
266+
{"EXP-DH-RSA-DES-CBC-SHA"},
267+
{"EXP-EDH-DSS-DES-CBC-SHA"},
268+
{"EXP-EDH-RSA-DES-CBC-SHA"},
269+
{"EXP-ADH-RC4-MD5"},
270+
{"EXP-ADH-DES-CBC-SHA"},
271+
{"EXP-KRB5-DES-CBC-SHA"},
272+
{"EXP-KRB5-RC2-CBC-SHA"},
273+
{"EXP-KRB5-RC4-SHA"},
274+
{"EXP-KRB5-DES-CBC-MD5"},
275+
{"EXP-KRB5-RC2-CBC-MD5"},
276+
{"EXP-KRB5-RC4-MD5"},
277+
{"EXP-RC4-MD5"},
278+
{"EXP-RC2-CBC-MD5"},
279+
{"TLS_RSA_EXPORT_WITH_DES40_CBC_SHA"},
280+
{"EXP-EDH-DSS-DES-CBC-SHA"},
281+
{"EXP-EDH-RSA-DES-CBC-SHA"},
282+
{"EXP-ADH-RC4-MD5"},
283+
{"EXP-ADH-DES-CBC-SHA"},
284+
{"EXP1024-DES-CBC-SHA"},
285+
{"EXP1024-RC4-SHA"},
286+
{"EXP1024-RC4-MD5"},
287+
{"EXP1024-RC2-CBC-MD5"},
288+
{"EXP1024-DHE-DSS-DES-CBC-SHA"},
289+
{"EXP1024-DHE-DSS-RC4-SHA"},
290+
{"EXP-RC4-MD5"},
291+
{"EXP-RC2-CBC-MD5"},
292+
{"EXP-RC2-MD5"},
293+
{"EDH-RSA-DES-CBC-SHA"},
294+
{"EDH-DSS-DES-CBC-SHA"},
295+
{"ADH-DES-CBC-SHA"},
296+
{"DES-CBC-SHA"},
297+
{"ADH-RC4-MD5"},
298+
{"RC4-MD5"},
299+
{"NULL-MD5"},
300+
{"ECDHE-RSA-RC4-SHA"},
301+
{"ECDHE-ECDSA-RC4-SHA"},
302+
{"AECDH-RC4-SHA"},
303+
{"ECDH-RSA-RC4-SHA"},
304+
{"ECDH-ECDSA-RC4-SHA"},
305+
{"RC4-SHA"},
306+
{"AECDH-NULL-SHA"},
307+
{"ECDH-RSA-NULL-SHA"},
308+
{"ECDH-ECDSA-NULL-SHA"},
309+
{"PSK-AES256-CBC-SHA"},
310+
{"PSK-AES128-CBC-SHA"},
311+
{"PSK-3DES-EDE-CBC-SHA"},
312+
{"PSK-RC4-SHA"},
313+
{"EXP-RC2-CBC-MD5"},
314+
{"EXP-KRB5-RC2-CBC-SHA"},
315+
{"EXP1024-RC2-CBC-MD5"},
316+
{"RC2-CBC-MD5"},
317+
{"EXP-RC2-CBC-MD5"},
318+
{"DH-RSA-AES128-SHA256"},
319+
{"DH-RSA-AES256-SHA256"},
320+
{"DH-DSS-AES128-SHA256"},
321+
{"DH-DSS-AES128-SHA"},
322+
{"DH-DSS-AES256-SHA"},
323+
{"DH-DSS-AES256-SHA256"},
324+
{"DH-RSA-AES128-SHA"},
325+
{"DH-RSA-AES256-SHA"},
326+
{"DH-DSS-AES128-GCM-SHA256"},
327+
{"DH-DSS-AES256-GCM-SHA384"},
328+
{"DH-RSA-AES128-GCM-SHA256"},
329+
{"DH-RSA-AES256-GCM-SHA384"},
330+
{"DH-DSS-DES-CBC3-SHA"},
331+
{"DH-RSA-DES-CBC3-SHA"},
332+
{"EDH-DSS-DES-CBC3-SHA"},
333+
{"EDH-RSA-DES-CBC3-SHA"},
334+
{"ECDH-RSA-DES-CBC3-SHA"},
335+
{"ECDH-ECDSA-DES-CBC3-SHA"},
336+
{"ECDHE-RSA-DES-CBC3-SHA"},
337+
{"ECDHE-ECDSA-DES-CBC3-SHA"},
338+
{"DES-CBC3-SHA"},
339+
};
340+
341+
INSTANTIATE_TEST_SUITE_P(CiphersUnacceptableParam, CiphersUnacceptable,
342+
::testing::ValuesIn(unacceptable_ciphers_test_data));
343+
344+
int main(int argc, char **argv) {
345+
::testing::InitGoogleTest(&argc, argv);
346+
return RUN_ALL_TESTS();
347+
}

0 commit comments

Comments
 (0)