From e5fa144f8d21050dd1fc15a4dc8aa34ac6f30602 Mon Sep 17 00:00:00 2001 From: Ilya Grigorik Date: Mon, 1 Jun 2020 08:07:13 -0700 Subject: [PATCH] Merge TLS verification patch from Faraday (#340) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #339, CVE-2020-13482 Credit to Mislav Marohnić for original implementation, merged from Faraday. --- lib/em-http.rb | 1 + lib/em-http/http_connection.rb | 56 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/lib/em-http.rb b/lib/em-http.rb index 221c240c..34a4d54f 100644 --- a/lib/em-http.rb +++ b/lib/em-http.rb @@ -5,6 +5,7 @@ require 'base64' require 'socket' +require 'openssl' require 'em-http/core_ext/bytesize' require 'em-http/http_connection' diff --git a/lib/em-http/http_connection.rb b/lib/em-http/http_connection.rb index e9cb4e87..4a07c46e 100644 --- a/lib/em-http/http_connection.rb +++ b/lib/em-http/http_connection.rb @@ -36,6 +36,62 @@ def connection_completed def unbind(reason=nil) @parent.unbind(reason) end + + # TLS verification support, original implementation by Mislav Marohnić + # https://github.com/lostisland/faraday/blob/63cf47c95b573539f047c729bd9ad67560bc83ff/lib/faraday/adapter/em_http_ssl_patch.rb + def ssl_verify_peer(cert_string) + cert = nil + begin + cert = OpenSSL::X509::Certificate.new(cert_string) + rescue OpenSSL::X509::CertificateError + return false + end + + @last_seen_cert = cert + + if certificate_store.verify(@last_seen_cert) + begin + certificate_store.add_cert(@last_seen_cert) + rescue OpenSSL::X509::StoreError => e + raise e unless e.message == 'cert already in hash table' + end + true + else + raise OpenSSL::SSL::SSLError.new(%(unable to verify the server certificate for "#{host}")) + end + end + + def ssl_handshake_completed + unless verify_peer? + warn "[WARNING; em-http-request] TLS hostname validation is disabled (use 'tls: {verify_peer: true}'), see" + + " CVE-2020-13482 and https://github.com/igrigorik/em-http-request/issues/339 for details" + return true + end + + unless OpenSSL::SSL.verify_certificate_identity(@last_seen_cert, host) + raise OpenSSL::SSL::SSLError.new(%(host "#{host}" does not match the server certificate)) + else + true + end + end + + def verify_peer? + parent.connopts.tls[:verify_peer] + end + + def host + parent.connopts.host + end + + def certificate_store + @certificate_store ||= begin + store = OpenSSL::X509::Store.new + store.set_default_paths + ca_file = parent.connopts.tls[:cert_chain_file] + store.add_file(ca_file) if ca_file + store + end + end end class HttpConnection