diff options
Diffstat (limited to 'lib/vendor/excon/lib/excon/ssl_socket.rb')
-rw-r--r-- | lib/vendor/excon/lib/excon/ssl_socket.rb | 172 |
1 files changed, 172 insertions, 0 deletions
diff --git a/lib/vendor/excon/lib/excon/ssl_socket.rb b/lib/vendor/excon/lib/excon/ssl_socket.rb new file mode 100644 index 0000000..dc65d86 --- /dev/null +++ b/lib/vendor/excon/lib/excon/ssl_socket.rb @@ -0,0 +1,172 @@ +# frozen_string_literal: true +module Excon + class SSLSocket < Socket + HAVE_NONBLOCK = [:connect_nonblock, :read_nonblock, :write_nonblock].all? do |m| + OpenSSL::SSL::SSLSocket.public_method_defined?(m) + end + + def initialize(data = {}) + super + + # create ssl context + ssl_context = OpenSSL::SSL::SSLContext.new + + # disable less secure options, when supported + ssl_context_options = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:options] + if defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) + ssl_context_options &= ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS + end + + if defined?(OpenSSL::SSL::OP_NO_COMPRESSION) + ssl_context_options |= OpenSSL::SSL::OP_NO_COMPRESSION + end + ssl_context.options = ssl_context_options + + ssl_context.ciphers = @data[:ciphers] + if @data[:ssl_version] + ssl_context.ssl_version = @data[:ssl_version] + end + + if @data[:ssl_verify_peer] + # turn verification on + ssl_context.verify_mode = OpenSSL::SSL::VERIFY_PEER + + if ca_file = @data[:ssl_ca_file] || ENV['SSL_CERT_FILE'] + ssl_context.ca_file = ca_file + end + if ca_path = @data[:ssl_ca_path] || ENV['SSL_CERT_DIR'] + ssl_context.ca_path = ca_path + end + if cert_store = @data[:ssl_cert_store] + ssl_context.cert_store = cert_store + end + + # no defaults, fallback to bundled + unless ca_file || ca_path || cert_store + ssl_context.cert_store = OpenSSL::X509::Store.new + ssl_context.cert_store.set_default_paths + + # workaround issue #257 (JRUBY-6970) + ca_file = DEFAULT_CA_FILE + ca_file = ca_file.gsub(/^jar:/, '') if ca_file =~ /^jar:file:\// + + begin + ssl_context.cert_store.add_file(ca_file) + rescue + Excon.display_warning("Excon unable to add file to cert store, ignoring: #{ca_file}\n[#{$!.class}] #{$!.message}") + end + end + + if verify_callback = @data[:ssl_verify_callback] + ssl_context.verify_callback = verify_callback + end + else + # turn verification off + ssl_context.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + + if client_cert_data && client_key_data + ssl_context.cert = OpenSSL::X509::Certificate.new client_cert_data + if OpenSSL::PKey.respond_to? :read + ssl_context.key = OpenSSL::PKey.read(client_key_data, client_key_pass) + else + ssl_context.key = OpenSSL::PKey::RSA.new(client_key_data, client_key_pass) + end + elsif @data.key?(:certificate) && @data.key?(:private_key) + ssl_context.cert = OpenSSL::X509::Certificate.new(@data[:certificate]) + if OpenSSL::PKey.respond_to? :read + ssl_context.key = OpenSSL::PKey.read(@data[:private_key], client_key_pass) + else + ssl_context.key = OpenSSL::PKey::RSA.new(@data[:private_key], client_key_pass) + end + end + + if @data[:proxy] + request = "CONNECT #{@data[:host]}#{port_string(@data.merge(:omit_default_port => false))}#{Excon::HTTP_1_1}" + + "Host: #{@data[:host]}#{port_string(@data)}#{Excon::CR_NL}" + + if @data[:proxy][:password] || @data[:proxy][:user] + auth = ["#{@data[:proxy][:user]}:#{@data[:proxy][:password]}"].pack('m').delete(Excon::CR_NL) + request += "Proxy-Authorization: Basic #{auth}#{Excon::CR_NL}" + end + + request += "Proxy-Connection: Keep-Alive#{Excon::CR_NL}" + + request += Excon::CR_NL + + # write out the proxy setup request + @socket.write(request) + + # eat the proxy's connection response + Excon::Response.parse(self, :expects => 200, :method => 'CONNECT') + end + + # convert Socket to OpenSSL::SSL::SSLSocket + @socket = OpenSSL::SSL::SSLSocket.new(@socket, ssl_context) + @socket.sync_close = true + + # Server Name Indication (SNI) RFC 3546 + if @socket.respond_to?(:hostname=) + @socket.hostname = @data[:host] + end + + begin + if @nonblock + begin + @socket.connect_nonblock + rescue Errno::EAGAIN, Errno::EWOULDBLOCK, IO::WaitReadable + select_with_timeout(@socket, :connect_read) && retry + rescue IO::WaitWritable + select_with_timeout(@socket, :connect_write) && retry + end + else + @socket.connect + end + rescue Errno::ETIMEDOUT, Timeout::Error + raise Excon::Errors::Timeout.new('connect timeout reached') + end + + # verify connection + if @data[:ssl_verify_peer] + @socket.post_connection_check(@data[:ssl_verify_peer_host] || @data[:host]) + end + + @socket + end + + private + + def client_cert_data + @client_cert_data ||= if ccd = @data[:client_cert_data] + ccd + elsif path = @data[:client_cert] + File.read path + elsif path = @data[:certificate_path] + warn ":certificate_path is no longer supported and will be deprecated. Please use :client_cert or :client_cert_data" + File.read path + end + end + + def connect + # backwards compatability for things lacking nonblock + @nonblock = HAVE_NONBLOCK && @nonblock + super + end + + def client_key_data + @client_key_data ||= if ckd = @data[:client_key_data] + ckd + elsif path = @data[:client_key] + File.read path + elsif path = @data[:private_key_path] + warn ":private_key_path is no longer supported and will be deprecated. Please use :client_key or :client_key_data" + File.read path + end + end + + def client_key_pass + @data[:client_key_pass] || @data[:private_key_pass] + end + + end +end |