From 7751c840f459255121e1605e45e04594d5e13b65 Mon Sep 17 00:00:00 2001 From: MSP-Greg Date: Wed, 13 May 2020 10:58:20 -0500 Subject: [PATCH] lib/puma\minissl.rb - TLSv1.3 updates Changes to Puma::MiniSSL 1. Add HAS_TLS1_3 constant. 2. Add #bad_tlsv1_3? method, used to determine if an http connection to an https server has been made. TLSv1.3 behaves differently than previous TLS versions. 3. Change #engine_read_all to close http connections. 4. Add #ssl_version_state method, unused at present. --- lib/puma/minissl.rb | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/lib/puma/minissl.rb b/lib/puma/minissl.rb index 92ce973b3a..32fd17fd8e 100644 --- a/lib/puma/minissl.rb +++ b/lib/puma/minissl.rb @@ -5,8 +5,18 @@ rescue LoadError end +# need for Puma::MiniSSL::OPENSSL constants used in `HAS_TLS1_3` +require 'puma/puma_http11' + module Puma module MiniSSL + + # define constant at runtime, as it's easy to determine at built time, + # but Puma could (it shouldn't) be loaded with an older OpenSSL version + HAS_TLS1_3 = !IS_JRUBY || + (OPENSSL_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) != -1 || + (OPENSSL_LIBRARY_VERSION[/ \d+\.\d+\.\d+/].split('.').map(&:to_i) <=> [1,1,1]) !=-1 + class Socket def initialize(socket, engine) @socket = socket @@ -22,6 +32,24 @@ def closed? @socket.closed? end + # returns a two element array + # first is protocol version (SSL_get_version) + # second is 'handshake' state (SSL_state_string) + # + # used for dropping tcp connections to ssl + # see OpenSSL ssl/ssl_stat.c SSL_state_string for info + # + def ssl_version_state + IS_JRUBY ? [nil, nil] : @engine.ssl_vers_st + end + + # used to check the handshake status, in particular when a TCP connection + # is made with TLSv1.3 as an available protocol + def bad_tlsv1_3? + HAS_TLS1_3 && @engine.ssl_vers_st == ['TLSv1.3', 'SSLERR'] + end + private :bad_tlsv1_3? + def readpartial(size) while true output = @engine.read @@ -41,6 +69,7 @@ def readpartial(size) def engine_read_all output = @engine.read + raise SSLError.exception "HTTP connection?" if bad_tlsv1_3? while output and additional_output = @engine.read output << additional_output end @@ -167,7 +196,7 @@ def peercert end end - if defined?(JRUBY_VERSION) + if IS_JRUBY class SSLError < StandardError # Define this for jruby even though it isn't used. end @@ -184,7 +213,7 @@ def initialize @no_tlsv1_1 = false end - if defined?(JRUBY_VERSION) + if IS_JRUBY # jruby-specific Context properties: java uses a keystore and password pair rather than a cert/key pair attr_reader :keystore attr_accessor :keystore_pass