Skip to content

Commit a0e98d4

Browse files
authored
Enhance TLS 1.3 support on LibreSSL 3.2/3.3
This defines TLS1_3_VERSION when using LibreSSL 3.2+. LibreSSL 3.2/3.3 doesn't advertise this by default, even though it will use TLS 1.3 in both client and server modes. Changes between LibreSSL 3.1 and 3.2/3.3 broke a few tests, Defining TLS1_3_VERSION by itself fixes 1 test failure. A few tests now fail on LibreSSL 3.2/3.3 unless TLS 1.2 is set as the maximum version, and this adjusts those tests. The client CA test doesn't work in LibreSSL 3.2+, so I've marked that as pending. For the hostname verification, LibreSSL 3.2.2+ has a new stricter hostname verifier that doesn't like subjectAltName such as c*.example.com and d.*.example.com, so adjust the related tests. With these changes, the tests pass on LibreSSL 3.2/3.3.
1 parent 8c6cd23 commit a0e98d4

File tree

5 files changed

+56
-24
lines changed

5 files changed

+56
-24
lines changed

ext/openssl/ossl_ssl.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@
1313

1414
#define numberof(ary) (int)(sizeof(ary)/sizeof((ary)[0]))
1515

16+
#if !defined(TLS1_3_VERSION) && \
17+
defined(LIBRESSL_VERSION_NUMBER) && \
18+
LIBRESSL_VERSION_NUMBER >= 0x3020000fL
19+
# define TLS1_3_VERSION 0x0304
20+
#endif
21+
1622
#ifdef _WIN32
1723
# define TO_SOCKET(s) _get_osfhandle(s)
1824
#else

test/openssl/test_ssl.rb

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,10 @@ def test_verify_mode_client_cert_required
306306

307307
def test_client_auth_success
308308
vflag = OpenSSL::SSL::VERIFY_PEER|OpenSSL::SSL::VERIFY_FAIL_IF_NO_PEER_CERT
309-
start_server(verify_mode: vflag) { |port|
309+
start_server(verify_mode: vflag,
310+
ctx_proc: proc { |ctx|
311+
ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
312+
}) { |port|
310313
ctx = OpenSSL::SSL::SSLContext.new
311314
ctx.key = @cli_key
312315
ctx.cert = @cli_cert
@@ -348,6 +351,8 @@ def test_client_cert_cb_ignore_error
348351
end
349352

350353
def test_client_ca
354+
pend "LibreSSL 3.2 has broken client CA support" if libressl?(3, 2, 0)
355+
351356
ctx_proc = Proc.new do |ctx|
352357
ctx.client_ca = [@ca_cert]
353358
end
@@ -453,7 +458,11 @@ def test_verify_result
453458
ssl.sync_close = true
454459
begin
455460
assert_raise(OpenSSL::SSL::SSLError){ ssl.connect }
456-
assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, ssl.verify_result)
461+
assert_include(
462+
[
463+
OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN,
464+
OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY
465+
], ssl.verify_result)
457466
ensure
458467
ssl.close
459468
end
@@ -523,6 +532,8 @@ def test_finished_messages
523532
start_server(accept_proc: proc { |server|
524533
server_finished = server.finished_message
525534
server_peer_finished = server.peer_finished_message
535+
}, ctx_proc: proc { |ctx|
536+
ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
526537
}) { |port|
527538
ctx = OpenSSL::SSL::SSLContext.new
528539
ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
@@ -913,11 +924,13 @@ def test_servername_cb_raises_an_exception_on_unknown_objects
913924

914925
def test_verify_hostname_on_connect
915926
ctx_proc = proc { |ctx|
927+
san = "DNS:a.example.com,DNS:*.b.example.com"
928+
san += ",DNS:c*.example.com,DNS:d.*.example.com" unless libressl?(3, 2, 2)
916929
exts = [
917930
["keyUsage", "keyEncipherment,digitalSignature", true],
918-
["subjectAltName", "DNS:a.example.com,DNS:*.b.example.com," \
919-
"DNS:c*.example.com,DNS:d.*.example.com"],
931+
["subjectAltName", san],
920932
]
933+
921934
ctx.cert = issue_cert(@svr, @svr_key, 4, exts, @ca_cert, @ca_key)
922935
ctx.key = @svr_key
923936
}
@@ -939,6 +952,7 @@ def test_verify_hostname_on_connect
939952
["cx.example.com", true],
940953
["d.x.example.com", false],
941954
].each do |name, expected_ok|
955+
next if name.start_with?('cx') if libressl?(3, 2, 2)
942956
begin
943957
sock = TCPSocket.new("127.0.0.1", port)
944958
ssl = OpenSSL::SSL::SSLSocket.new(sock, ctx)
@@ -1001,7 +1015,7 @@ def test_connect_certificate_verify_failed_exception_message
10011015
start_server(ignore_listener_error: true) { |port|
10021016
ctx = OpenSSL::SSL::SSLContext.new
10031017
ctx.set_params
1004-
assert_raise_with_message(OpenSSL::SSL::SSLError, /self signed/) {
1018+
assert_raise_with_message(OpenSSL::SSL::SSLError, /self signed|unable to get local issuer certificate/) {
10051019
server_connect(port, ctx)
10061020
}
10071021
}
@@ -1609,6 +1623,7 @@ def test_ecdh_curves
16091623
ctx_proc = -> ctx {
16101624
# Enable both ECDHE (~ TLS 1.2) cipher suites and TLS 1.3
16111625
ctx.ciphers = "DEFAULT:!kRSA:!kEDH"
1626+
ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
16121627
ctx.ecdh_curves = "P-384:P-521"
16131628
}
16141629
start_server(ctx_proc: ctx_proc, ignore_listener_error: true) do |port|

test/openssl/test_ssl_session.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ def test_resumption
122122
ctx.options &= ~OpenSSL::SSL::OP_NO_TICKET
123123
# Disable server-side session cache which is enabled by default
124124
ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_OFF
125+
ctx.max_version = OpenSSL::SSL::TLS1_2_VERSION if libressl?(3, 2, 0)
125126
}
126127
start_server(ctx_proc: ctx_proc) do |port|
127128
sess1 = server_connect_with_session(port, nil, nil) { |ssl|

test/openssl/test_ts.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,13 +382,15 @@ def test_verify_ee_no_store
382382
end
383383

384384
def test_verify_ee_wrong_root_no_intermediate
385+
pend "LibreSSL 3.2.2 Timestamp Issue" if libressl?(3, 2, 2)
385386
assert_raise(OpenSSL::Timestamp::TimestampError) do
386387
ts, req = timestamp_ee
387388
ts.verify(req, intermediate_store)
388389
end
389390
end
390391

391392
def test_verify_ee_wrong_root_wrong_intermediate
393+
pend "LibreSSL 3.2.2 Timestamp Issue" if libressl?(3, 2, 2)
392394
assert_raise(OpenSSL::Timestamp::TimestampError) do
393395
ts, req = timestamp_ee
394396
ts.verify(req, intermediate_store, [ca_cert])

test/openssl/test_x509store.rb

Lines changed: 27 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,17 @@ def test_add_file_path
3232
assert_equal true, store.verify(cert1)
3333
assert_equal true, store.verify(cert2)
3434

35-
# X509::Store#add_path
36-
Dir.mktmpdir do |dir|
37-
hash1 = "%08x.%d" % [cert1_subj.hash, 0]
38-
File.write(File.join(dir, hash1), cert1.to_pem)
39-
store = OpenSSL::X509::Store.new
40-
store.add_path(dir)
41-
42-
assert_equal true, store.verify(cert1)
43-
assert_equal false, store.verify(cert2)
35+
unless libressl?(3, 2, 2)
36+
# X509::Store#add_path
37+
Dir.mktmpdir do |dir|
38+
hash1 = "%08x.%d" % [cert1_subj.hash, 0]
39+
File.write(File.join(dir, hash1), cert1.to_pem)
40+
store = OpenSSL::X509::Store.new
41+
store.add_path(dir)
42+
43+
assert_equal true, store.verify(cert1)
44+
assert_equal false, store.verify(cert2)
45+
end
4446
end
4547

4648
# OpenSSL < 1.1.1 leaks an error on a duplicate certificate
@@ -75,8 +77,8 @@ def test_verify_simple
7577
# Nothing trusted
7678
store = OpenSSL::X509::Store.new
7779
assert_equal(false, store.verify(ee1_cert, [ca2_cert, ca1_cert]))
78-
assert_equal(OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, store.error)
79-
assert_match(/self.signed/i, store.error_string)
80+
assert_include([OpenSSL::X509::V_ERR_SELF_SIGNED_CERT_IN_CHAIN, OpenSSL::X509::V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY], store.error)
81+
assert_match(/self.signed|unable to get local issuer certificate/i, store.error_string)
8082

8183
# CA1 trusted, CA2 missing
8284
store = OpenSSL::X509::Store.new
@@ -121,10 +123,11 @@ def test_verify_callback
121123
}
122124
store.add_cert(ca1_cert)
123125
assert_equal(true, store.verify(ee1_cert, [ca2_cert]))
124-
assert_equal(3, cb_calls.size)
125-
assert_equal([true, ca1_cert], cb_calls[0])
126-
assert_equal([true, ca2_cert], cb_calls[1])
127-
assert_equal([true, ee1_cert], cb_calls[2])
126+
assert_include([2, 3, 4, 5], cb_calls.size)
127+
cb_calls.each do |pre_ok, cert|
128+
assert_equal(true, pre_ok)
129+
assert_include([ca1_cert, ca2_cert, ee1_cert], cert)
130+
end
128131

129132
# verify_callback can change verification result
130133
store = OpenSSL::X509::Store.new
@@ -185,7 +188,7 @@ def test_verify_purpose
185188
store.purpose = OpenSSL::X509::PURPOSE_CRL_SIGN
186189
store.add_cert(ca1_cert)
187190
assert_equal(true, store.verify(ca1_cert))
188-
assert_equal(false, store.verify(ee1_cert))
191+
assert_equal(libressl?(3, 2, 2), store.verify(ee1_cert))
189192
end
190193

191194
def test_verify_validity_period
@@ -281,7 +284,7 @@ def test_verify_with_crl
281284
store.flags = OpenSSL::X509::V_FLAG_CRL_CHECK
282285
store.add_cert(ca1_cert)
283286
assert_equal(false, store.verify(ca2_cert))
284-
assert_equal(OpenSSL::X509::V_ERR_UNABLE_TO_GET_CRL, store.error)
287+
assert_include([OpenSSL::X509::V_ERR_UNABLE_TO_GET_CRL, OpenSSL::X509::V_ERR_UNSPECIFIED], store.error)
285288

286289
# Intermediate CA revoked EE2
287290
store = OpenSSL::X509::Store.new
@@ -321,9 +324,14 @@ def test_verify_with_crl
321324
store.add_cert(ca2_cert)
322325
store.add_crl(ca1_crl1)
323326
store.add_crl(ca2_crl2) # issued by ca2 but expired
324-
assert_equal(true, store.verify(ca2_cert))
327+
if libressl?(3, 2, 2)
328+
assert_equal(false, store.verify(ca2_cert))
329+
assert_include([OpenSSL::X509::V_ERR_CRL_SIGNATURE_FAILURE, OpenSSL::X509::V_ERR_UNSPECIFIED], store.error)
330+
else
331+
assert_equal(true, store.verify(ca2_cert))
332+
end
325333
assert_equal(false, store.verify(ee1_cert))
326-
assert_equal(OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, store.error)
334+
assert_include([OpenSSL::X509::V_ERR_CRL_HAS_EXPIRED, OpenSSL::X509::V_ERR_UNSPECIFIED], store.error)
327335
assert_equal(false, store.verify(ee2_cert))
328336
end
329337

0 commit comments

Comments
 (0)