1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

net/http, net/ftp: fix session resumption with TLS 1.3

When TLS 1.3 is in use, the session ticket may not have been sent yet
even though a handshake has finished. Also, the ticket could change if
multiple session ticket messages are sent by the server. Use
SSLContext#session_new_cb instead of calling SSLSocket#session
immediately after a handshake. This way also works with earlier protocol
versions.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@64234 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
rhe 2018-08-08 14:13:55 +00:00
parent a0f292bbcd
commit 1dfc377ae3
3 changed files with 19 additions and 28 deletions

View file

@ -230,6 +230,10 @@ module Net
if defined?(VerifyCallbackProc) if defined?(VerifyCallbackProc)
@ssl_context.verify_callback = VerifyCallbackProc @ssl_context.verify_callback = VerifyCallbackProc
end end
@ssl_context.session_cache_mode =
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
@ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
@ssl_session = nil @ssl_session = nil
if options[:private_data_connection].nil? if options[:private_data_connection].nil?
@private_data_connection = true @private_data_connection = true
@ -349,7 +353,6 @@ module Net
if @ssl_context.verify_mode != VERIFY_NONE if @ssl_context.verify_mode != VERIFY_NONE
ssl_sock.post_connection_check(@host) ssl_sock.post_connection_check(@host)
end end
@ssl_session = ssl_sock.session
return ssl_sock return ssl_sock
end end
private :start_tls_session private :start_tls_session

View file

@ -983,6 +983,10 @@ module Net #:nodoc:
end end
@ssl_context = OpenSSL::SSL::SSLContext.new @ssl_context = OpenSSL::SSL::SSLContext.new
@ssl_context.set_params(ssl_parameters) @ssl_context.set_params(ssl_parameters)
@ssl_context.session_cache_mode =
OpenSSL::SSL::SSLContext::SESSION_CACHE_CLIENT |
OpenSSL::SSL::SSLContext::SESSION_CACHE_NO_INTERNAL_STORE
@ssl_context.session_new_cb = proc {|sock, sess| @ssl_session = sess }
D "starting SSL for #{conn_address}:#{conn_port}..." D "starting SSL for #{conn_address}:#{conn_port}..."
s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context) s = OpenSSL::SSL::SSLSocket.new(s, @ssl_context)
s.sync_close = true s.sync_close = true
@ -990,13 +994,12 @@ module Net #:nodoc:
s.hostname = @address if s.respond_to? :hostname= s.hostname = @address if s.respond_to? :hostname=
if @ssl_session and if @ssl_session and
Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout Process.clock_gettime(Process::CLOCK_REALTIME) < @ssl_session.time.to_f + @ssl_session.timeout
s.session = @ssl_session if @ssl_session s.session = @ssl_session
end end
ssl_socket_connect(s, @open_timeout) ssl_socket_connect(s, @open_timeout)
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
s.post_connection_check(@address) s.post_connection_check(@address)
end end
@ssl_session = s.session
D "SSL established" D "SSL established"
end end
@socket = BufferedIO.new(s, read_timeout: @read_timeout, @socket = BufferedIO.new(s, read_timeout: @read_timeout,

View file

@ -71,20 +71,11 @@ class TestNetHTTPS < Test::Unit::TestCase
http.get("/") http.get("/")
http.finish http.finish
http.start
http.get("/")
http.finish # three times due to possible bug in OpenSSL 0.9.8
sid = http.instance_variable_get(:@ssl_session).id
http.start http.start
http.get("/") http.get("/")
socket = http.instance_variable_get(:@socket).io socket = http.instance_variable_get(:@socket).io
assert_equal true, socket.session_reused?
assert socket.session_reused?
assert_equal sid, http.instance_variable_get(:@ssl_session).id
http.finish http.finish
rescue SystemCallError rescue SystemCallError
@ -101,16 +92,12 @@ class TestNetHTTPS < Test::Unit::TestCase
http.get("/") http.get("/")
http.finish http.finish
sid = http.instance_variable_get(:@ssl_session).id
http.start http.start
http.get("/") http.get("/")
socket = http.instance_variable_get(:@socket).io socket = http.instance_variable_get(:@socket).io
assert_equal false, socket.session_reused? assert_equal false, socket.session_reused?
assert_not_equal sid, http.instance_variable_get(:@ssl_session).id
http.finish http.finish
rescue SystemCallError rescue SystemCallError
skip $! skip $!
@ -160,15 +147,16 @@ class TestNetHTTPS < Test::Unit::TestCase
end end
def test_identity_verify_failure def test_identity_verify_failure
# the certificate's subject has CN=localhost
http = Net::HTTP.new("127.0.0.1", config("port")) http = Net::HTTP.new("127.0.0.1", config("port"))
http.use_ssl = true http.use_ssl = true
http.verify_callback = Proc.new do |preverify_ok, store_ctx| http.cert_store = TEST_STORE
true @log_tester = lambda {|_| }
end
ex = assert_raise(OpenSSL::SSL::SSLError){ ex = assert_raise(OpenSSL::SSL::SSLError){
http.request_get("/") {|res| } http.request_get("/") {|res| }
} }
assert_match(/hostname \"127.0.0.1\" does not match/, ex.message) re_msg = /certificate verify failed|hostname \"127.0.0.1\" does not match/
assert_match(re_msg, ex.message)
end end
def test_timeout_during_SSL_handshake def test_timeout_during_SSL_handshake
@ -193,16 +181,13 @@ class TestNetHTTPS < Test::Unit::TestCase
end end
def test_min_version def test_min_version
http = Net::HTTP.new("127.0.0.1", config("port")) http = Net::HTTP.new("localhost", config("port"))
http.use_ssl = true http.use_ssl = true
http.min_version = :TLS1 http.min_version = :TLS1
http.verify_callback = Proc.new do |preverify_ok, store_ctx| http.cert_store = TEST_STORE
true http.request_get("/") {|res|
end assert_equal($test_net_http_data, res.body)
ex = assert_raise(OpenSSL::SSL::SSLError){
http.request_get("/") {|res| }
} }
assert_match(/hostname \"127.0.0.1\" does not match/, ex.message)
end end
def test_max_version def test_max_version