mirror of
https://github.com/HoneyryderChuck/httpx.git
synced 2025-10-04 00:00:37 -04:00
updated verify certs callback, inorder only to verify the hostname when dealing with the leaf certificate
This commit is contained in:
parent
384d273715
commit
da21b4a342
@ -197,7 +197,7 @@ module Faraday
|
||||
response_headers.merge!(response.headers)
|
||||
end
|
||||
@app.call(env)
|
||||
rescue OpenSSL::SSL::SSLError => e
|
||||
rescue ::HTTPX::TLSError => e
|
||||
raise SSL_ERROR, e
|
||||
rescue Errno::ECONNABORTED,
|
||||
Errno::ECONNREFUSED,
|
||||
|
@ -444,7 +444,7 @@ module HTTPX
|
||||
throw(:jump_tick)
|
||||
rescue Errno::ECONNREFUSED,
|
||||
Errno::EADDRNOTAVAIL,
|
||||
OpenSSL::SSL::SSLError => e
|
||||
TLSError => e
|
||||
# connect errors, exit gracefully
|
||||
handle_error(e)
|
||||
@state = :closed
|
||||
|
@ -149,7 +149,7 @@ module RubyTls
|
||||
attach_function :X509_STORE_CTX_get_current_cert, [:pointer], :x509
|
||||
attach_function :SSL_get_ex_data_X509_STORE_CTX_idx, [], :int
|
||||
attach_function :X509_STORE_CTX_get_ex_data, %i[pointer int], :ssl
|
||||
attach_function :PEM_write_bio_X509, %i[bio x509], :int
|
||||
attach_function :PEM_write_bio_X509, %i[bio x509], :bool
|
||||
|
||||
# SSL Context Class
|
||||
# OpenSSL before 1.1.0 do not have these methods
|
||||
@ -219,9 +219,10 @@ module RubyTls
|
||||
|
||||
attach_function :SSL_ctrl, %i[ssl int long pointer], :long
|
||||
SSL_CTRL_SET_TLSEXT_HOSTNAME = 55
|
||||
|
||||
def self.SSL_set_tlsext_host_name(ssl, host_name)
|
||||
name = FFI::MemoryPointer.from_string(host_name)
|
||||
SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name)
|
||||
name_ptr = FFI::MemoryPointer.from_string(host_name)
|
||||
raise "error setting SNI hostname" if SSL_ctrl(ssl, SSL_CTRL_SET_TLSEXT_HOSTNAME, TLSEXT_NAMETYPE_host_name, name_ptr) == 0
|
||||
end
|
||||
|
||||
# Server Name Indication (SNI) Support
|
||||
@ -781,22 +782,29 @@ module RubyTls
|
||||
end
|
||||
end
|
||||
|
||||
VerifyCB = FFI::Function.new(:int, %i[int pointer]) do |_preverify_ok, x509_store|
|
||||
x509 = SSL.X509_STORE_CTX_get_current_cert(x509_store)
|
||||
ssl = SSL.X509_STORE_CTX_get_ex_data(x509_store, SSL.SSL_get_ex_data_X509_STORE_CTX_idx)
|
||||
VerifyCB = FFI::Function.new(:int, %i[int pointer]) do |preverify_ok, x509_store|
|
||||
if preverify_ok.zero?
|
||||
1
|
||||
else
|
||||
x509 = SSL.X509_STORE_CTX_get_current_cert(x509_store)
|
||||
ssl = SSL.X509_STORE_CTX_get_ex_data(x509_store, SSL.SSL_get_ex_data_X509_STORE_CTX_idx)
|
||||
|
||||
bio_out = SSL.BIO_new(SSL.BIO_s_mem)
|
||||
SSL.PEM_write_bio_X509(bio_out, x509)
|
||||
bio_out = SSL.BIO_new(SSL.BIO_s_mem)
|
||||
ret = SSL.PEM_write_bio_X509(bio_out, x509)
|
||||
unless ret
|
||||
SSL.BIO_free(bio_out)
|
||||
raise "Error reading certificate"
|
||||
end
|
||||
|
||||
len = SSL.BIO_pending(bio_out)
|
||||
buffer = FFI::MemoryPointer.new(:char, len, false)
|
||||
size = SSL.BIO_read(bio_out, buffer, len)
|
||||
len = SSL.BIO_pending(bio_out)
|
||||
buffer = FFI::MemoryPointer.new(:char, len, false)
|
||||
size = SSL.BIO_read(bio_out, buffer, len)
|
||||
|
||||
# THis is the callback into the ruby class
|
||||
result = InstanceLookup[ssl.address].verify(buffer.read_string(size))
|
||||
|
||||
SSL.BIO_free(bio_out)
|
||||
result
|
||||
# THis is the callback into the ruby class
|
||||
cert = buffer.read_string(size)
|
||||
SSL.BIO_free(bio_out)
|
||||
InstanceLookup[ssl.address].verify(cert)
|
||||
end
|
||||
end
|
||||
|
||||
def pending_data(bio)
|
||||
|
@ -3,6 +3,8 @@
|
||||
require "openssl"
|
||||
|
||||
module HTTPX
|
||||
TLSError = OpenSSL::SSL::SSLError
|
||||
|
||||
class SSL < TCP
|
||||
TLS_OPTIONS = if OpenSSL::SSL::SSLContext.instance_methods.include?(:alpn_protocols)
|
||||
{ alpn_protocols: %w[h2 http/1.1] }
|
||||
|
@ -4,6 +4,7 @@ require "httpx/io/ruby-tls"
|
||||
require "openssl"
|
||||
|
||||
module HTTPX
|
||||
TLSError = Class.new(StandardError)
|
||||
class SSL < TCP
|
||||
def initialize(_, _, options)
|
||||
super
|
||||
@ -96,8 +97,8 @@ module HTTPX
|
||||
#
|
||||
# signals TLS invalid status / shutdown.
|
||||
def close_cb
|
||||
log { "TLS closing" }
|
||||
transport_close
|
||||
log { "Error, TLS closing" }
|
||||
raise TLSError, "SSL Error"
|
||||
end
|
||||
|
||||
# TLS callback.
|
||||
@ -115,10 +116,11 @@ module HTTPX
|
||||
# passed the peer +cert+ to be verified.
|
||||
#
|
||||
def verify_cb(cert)
|
||||
raise "Peer verification enabled, but no certificate received." if cert.nil?
|
||||
raise TLSError, "Peer verification enabled, but no certificate received." if cert.nil?
|
||||
|
||||
log { "TLS verifying #{cert}" }
|
||||
@peer_cert = OpenSSL::X509::Certificate.new(cert)
|
||||
|
||||
# by default one doesn't verify client certificates in the server
|
||||
verify_hostname(@sni_hostname)
|
||||
end
|
||||
@ -126,8 +128,10 @@ module HTTPX
|
||||
# copied from:
|
||||
# https://github.com/ruby/ruby/blob/8cbf2dae5aadfa5d6241b0df2bf44d55db46704f/ext/openssl/lib/openssl/ssl.rb#L395-L409
|
||||
#
|
||||
def verify_hostname(peer_cert)
|
||||
OpenSSL::SSL.verify_certificate_identity(peer_cert, @hostname)
|
||||
def verify_hostname(host)
|
||||
return false unless @ctx.verify_peer && @peer_cert
|
||||
|
||||
OpenSSL::SSL.verify_certificate_identity(@peer_cert, host)
|
||||
end
|
||||
|
||||
private
|
||||
@ -147,8 +151,18 @@ module HTTPX
|
||||
options = {}
|
||||
options[:verify_peer] = !ssl_options.key?(:verify_mode) || ssl_options[:verify_mode] != OpenSSL::SSL::VERIFY_NONE
|
||||
options[:version] = ssl_options[:ssl_version] if ssl_options.key?(:ssl_version)
|
||||
# options[:private_key] = tls[:key] if tls.key?(:key)
|
||||
options[:cert_chain] = ssl_options[:cert_store] if ssl_options.key?(:cert_store)
|
||||
|
||||
if ssl_options.key?(:key)
|
||||
private_key = ssl_options[:key]
|
||||
private_key = private_key.to_pem if private_key.respond_to?(:to_pem)
|
||||
options[:private_key] = private_key
|
||||
end
|
||||
|
||||
if ssl_options.key?(:ca_path) || ssl_options.key?(:ca_file)
|
||||
ca_path = ssl_options[:ca_path] || ssl_options[:ca_file].path
|
||||
options[:cert_chain] = ca_path
|
||||
end
|
||||
|
||||
options[:ciphers] = ssl_options[:ciphers] if ssl_options.key?(:ciphers)
|
||||
options[:protocols] = ssl_options.fetch(:alpn_protocols, %w[h2 http/1.1])
|
||||
options[:fallback] = "http/1.1"
|
||||
|
@ -17,7 +17,7 @@ module HTTPX
|
||||
Errno::ECONNRESET,
|
||||
Errno::ECONNABORTED,
|
||||
Errno::EPIPE,
|
||||
(OpenSSL::SSL::SSLError if defined?(OpenSSL)),
|
||||
(TLSError if defined?(TLSError)),
|
||||
TimeoutError,
|
||||
Parser::Error,
|
||||
Errno::EINVAL,
|
||||
|
@ -34,9 +34,9 @@ class FaradayTest < Minitest::Test
|
||||
assert JSON.parse(res.body.to_s)["gzipped"]
|
||||
end
|
||||
|
||||
SYSTEM_CERT_STORE_DIR = "/usr/share/ca-certificates/mozilla"
|
||||
def test_adapter_get_ssl_fails_with_bad_cert
|
||||
fake_store = OpenSSL::X509::Store.new
|
||||
conn = create_connection(ssl: { cert_store: fake_store, verify: OpenSSL::SSL::VERIFY_PEER })
|
||||
conn = create_connection(ssl: { ca_path: SYSTEM_CERT_STORE_DIR, verify: OpenSSL::SSL::VERIFY_PEER })
|
||||
err = assert_raises Faraday::Adapter::HTTPX::SSL_ERROR do
|
||||
conn.get(build_path("/get"))
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user