-
-
Save djrobby/d2e342051457ec20267aeb02e59380c7 to your computer and use it in GitHub Desktop.
1.9.3-p551 patch (SSLv3, TLS 1.1/2 support, CVE-2015-1855 fix backport)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/ext/openssl/extconf.rb b/ext/openssl/extconf.rb | |
index bfb1ea4..eaa2557 100644 | |
--- a/ext/openssl/extconf.rb | |
+++ b/ext/openssl/extconf.rb | |
@@ -104,6 +104,15 @@ have_func("OPENSSL_cleanse") | |
have_func("SSLv2_method") | |
have_func("SSLv2_server_method") | |
have_func("SSLv2_client_method") | |
+have_func("SSLv3_method") | |
+have_func("SSLv3_server_method") | |
+have_func("SSLv3_client_method") | |
+have_func("TLSv1_1_method") | |
+have_func("TLSv1_1_server_method") | |
+have_func("TLSv1_1_client_method") | |
+have_func("TLSv1_2_method") | |
+have_func("TLSv1_2_server_method") | |
+have_func("TLSv1_2_client_method") | |
unless have_func("SSL_set_tlsext_host_name", ['openssl/ssl.h']) | |
have_macro("SSL_set_tlsext_host_name", ['openssl/ssl.h']) && $defs.push("-DHAVE_SSL_SET_TLSEXT_HOST_NAME") | |
end | |
diff --git a/ext/openssl/lib/openssl/ssl-internal.rb b/ext/openssl/lib/openssl/ssl-internal.rb | |
index 1615f7e..811b021 100644 | |
--- a/ext/openssl/lib/openssl/ssl-internal.rb | |
+++ b/ext/openssl/lib/openssl/ssl-internal.rb | |
@@ -135,8 +135,7 @@ module OpenSSL | |
case san.tag | |
when 2 # dNSName in GeneralName (RFC5280) | |
should_verify_common_name = false | |
- reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+") | |
- return true if /\A#{reg}\z/i =~ hostname | |
+ return true if verify_hostname(hostname, san.value) | |
when 7 # iPAddress in GeneralName (RFC5280) | |
should_verify_common_name = false | |
# follows GENERAL_NAME_print() in x509v3/v3_alt.c | |
@@ -151,8 +150,7 @@ module OpenSSL | |
if should_verify_common_name | |
cert.subject.to_a.each{|oid, value| | |
if oid == "CN" | |
- reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") | |
- return true if /\A#{reg}\z/i =~ hostname | |
+ return true if verify_hostname(hostname, value) | |
end | |
} | |
end | |
@@ -160,11 +158,67 @@ module OpenSSL | |
end | |
module_function :verify_certificate_identity | |
+ def verify_hostname(hostname, san) # :nodoc: | |
+ # RFC 5280, IA5String is limited to the set of ASCII characters | |
+ return false unless san.ascii_only? | |
+ return false unless hostname.ascii_only? | |
+ | |
+ # See RFC 6125, section 6.4.1 | |
+ # Matching is case-insensitive. | |
+ san_parts = san.downcase.split(".") | |
+ | |
+ # TODO: this behavior should probably be more strict | |
+ return san == hostname if san_parts.size < 2 | |
+ | |
+ # Matching is case-insensitive. | |
+ host_parts = hostname.downcase.split(".") | |
+ | |
+ # RFC 6125, section 6.4.3, subitem 2. | |
+ # If the wildcard character is the only character of the left-most | |
+ # label in the presented identifier, the client SHOULD NOT compare | |
+ # against anything but the left-most label of the reference | |
+ # identifier (e.g., *.example.com would match foo.example.com but | |
+ # not bar.foo.example.com or example.com). | |
+ return false unless san_parts.size == host_parts.size | |
+ | |
+ # RFC 6125, section 6.4.3, subitem 1. | |
+ # The client SHOULD NOT attempt to match a presented identifier in | |
+ # which the wildcard character comprises a label other than the | |
+ # left-most label (e.g., do not match bar.*.example.net). | |
+ return false unless verify_wildcard(host_parts.shift, san_parts.shift) | |
+ | |
+ san_parts.join(".") == host_parts.join(".") | |
+ end | |
+ module_function :verify_hostname | |
+ | |
+ def verify_wildcard(domain_component, san_component) # :nodoc: | |
+ parts = san_component.split("*", -1) | |
+ | |
+ return false if parts.size > 2 | |
+ return san_component == domain_component if parts.size == 1 | |
+ | |
+ # RFC 6125, section 6.4.3, subitem 3. | |
+ # The client SHOULD NOT attempt to match a presented identifier | |
+ # where the wildcard character is embedded within an A-label or | |
+ # U-label of an internationalized domain name. | |
+ return false if domain_component.start_with?("xn--") && san_component != "*" | |
+ | |
+ parts[0].length + parts[1].length < domain_component.length && | |
+ domain_component.start_with?(parts[0]) && | |
+ domain_component.end_with?(parts[1]) | |
+ end | |
+ module_function :verify_wildcard | |
+ | |
class SSLSocket | |
include Buffering | |
include SocketForwarder | |
include Nonblock | |
+ ## | |
+ # Perform hostname verification after an SSL connection is established | |
+ # | |
+ # This method MUST be called after calling #connect to ensure that the | |
+ # hostname of a remote peer has been verified. | |
def post_connection_check(hostname) | |
unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname) | |
raise SSLError, "hostname does not match the server certificate" | |
diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c | |
index 72e9350..9dd0bbe 100644 | |
--- a/ext/openssl/ossl_ssl.c | |
+++ b/ext/openssl/ossl_ssl.c | |
@@ -107,15 +107,30 @@ struct { | |
OSSL_SSL_METHOD_ENTRY(TLSv1), | |
OSSL_SSL_METHOD_ENTRY(TLSv1_server), | |
OSSL_SSL_METHOD_ENTRY(TLSv1_client), | |
+#if defined(HAVE_TLSV1_2_METHOD) && defined(HAVE_TLSV1_2_SERVER_METHOD) && \ | |
+ defined(HAVE_TLSV1_2_CLIENT_METHOD) | |
+ OSSL_SSL_METHOD_ENTRY(TLSv1_2), | |
+ OSSL_SSL_METHOD_ENTRY(TLSv1_2_server), | |
+ OSSL_SSL_METHOD_ENTRY(TLSv1_2_client), | |
+#endif | |
+#if defined(HAVE_TLSV1_1_METHOD) && defined(HAVE_TLSV1_1_SERVER_METHOD) && \ | |
+ defined(HAVE_TLSV1_1_CLIENT_METHOD) | |
+ OSSL_SSL_METHOD_ENTRY(TLSv1_1), | |
+ OSSL_SSL_METHOD_ENTRY(TLSv1_1_server), | |
+ OSSL_SSL_METHOD_ENTRY(TLSv1_1_client), | |
+#endif | |
#if defined(HAVE_SSLV2_METHOD) && defined(HAVE_SSLV2_SERVER_METHOD) && \ | |
defined(HAVE_SSLV2_CLIENT_METHOD) | |
OSSL_SSL_METHOD_ENTRY(SSLv2), | |
OSSL_SSL_METHOD_ENTRY(SSLv2_server), | |
OSSL_SSL_METHOD_ENTRY(SSLv2_client), | |
#endif | |
+#if defined(HAVE_SSLV3_METHOD) && defined(HAVE_SSLV3_SERVER_METHOD) && \ | |
+ defined(HAVE_SSLV3_CLIENT_METHOD) | |
OSSL_SSL_METHOD_ENTRY(SSLv3), | |
OSSL_SSL_METHOD_ENTRY(SSLv3_server), | |
OSSL_SSL_METHOD_ENTRY(SSLv3_client), | |
+#endif | |
OSSL_SSL_METHOD_ENTRY(SSLv23), | |
OSSL_SSL_METHOD_ENTRY(SSLv23_server), | |
OSSL_SSL_METHOD_ENTRY(SSLv23_client), | |
diff --git a/test/openssl/test_ssl.rb b/test/openssl/test_ssl.rb | |
index 0620490..da6917e 100644 | |
--- a/test/openssl/test_ssl.rb | |
+++ b/test/openssl/test_ssl.rb | |
@@ -1,3 +1,5 @@ | |
+# encoding: utf-8 | |
+ | |
require_relative "utils" | |
if defined?(OpenSSL) | |
@@ -363,6 +365,155 @@ class OpenSSL::TestSSL < OpenSSL::SSLTestCase | |
end | |
end | |
+ def test_verify_hostname | |
+ assert_equal(true, OpenSSL::SSL.verify_hostname("www.example.com", "*.example.com")) | |
+ assert_equal(false, OpenSSL::SSL.verify_hostname("www.subdomain.example.com", "*.example.com")) | |
+ end | |
+ | |
+ def test_verify_wildcard | |
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*")) | |
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "foo")) | |
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "f*")) | |
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "*")) | |
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd")) | |
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*")) | |
+ assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b")) | |
+ assert_equal(true, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b")) | |
+ end | |
+ | |
+ # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27 | |
+ def test_post_connection_check_wildcard_san | |
+ # case-insensitive ASCII comparison | |
+ # RFC 6125, section 6.4.1 | |
+ # | |
+ # "..matching of the reference identifier against the presented identifier | |
+ # is performed by comparing the set of domain name labels using a | |
+ # case-insensitive ASCII comparison, as clarified by [DNS-CASE] (e.g., | |
+ # "WWW.Example.Com" would be lower-cased to "www.example.com" for | |
+ # comparison purposes) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*.example.com'), 'www.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*.Example.COM'), 'www.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*.example.com'), 'WWW.Example.COM')) | |
+ # 1. The client SHOULD NOT attempt to match a presented identifier in | |
+ # which the wildcard character comprises a label other than the | |
+ # left-most label (e.g., do not match bar.*.example.net). | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:www.*.com'), 'www.example.com')) | |
+ # 2. If the wildcard character is the only character of the left-most | |
+ # label in the presented identifier, the client SHOULD NOT compare | |
+ # against anything but the left-most label of the reference | |
+ # identifier (e.g., *.example.com would match foo.example.com but | |
+ # not bar.foo.example.com or example.com). | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*.example.com'), 'foo.example.com')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*.example.com'), 'bar.foo.example.com')) | |
+ # 3. The client MAY match a presented identifier in which the wildcard | |
+ # character is not the only character of the label (e.g., | |
+ # baz*.example.net and *baz.example.net and b*z.example.net would | |
+ # be taken to match baz1.example.net and foobaz.example.net and | |
+ # buzz.example.net, respectively). ... | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com')) | |
+ # Section 6.4.3 of RFC6125 states that client should NOT match identifier | |
+ # where wildcard is other than left-most label. | |
+ # | |
+ # Also implicitly mentions the wildcard character only in singular form, | |
+ # and discourages matching against more than one wildcard. | |
+ # | |
+ # See RFC 6125, section 7.2, subitem 2. | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*b*.example.com'), 'abc.example.com')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*b*.example.com'), 'ab.example.com')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:*b*.example.com'), 'bc.example.com')) | |
+ # ... However, the client SHOULD NOT | |
+ # attempt to match a presented identifier where the wildcard | |
+ # character is embedded within an A-label or U-label [IDNA-DEFS] of | |
+ # an internationalized domain name [IDNA-PROTO]. | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:xn*.example.com'), 'xn1ca.example.com')) | |
+ # part of A-label | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_san('DNS:xn--*.example.com'), 'xn--1ca.example.com')) | |
+ # part of U-label | |
+ # dNSName in RFC5280 is an IA5String so U-label should NOT be allowed | |
+ # regardless of wildcard. | |
+ # | |
+ # See Section 7.2 of RFC 5280: | |
+ # IA5String is limited to the set of ASCII characters. | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('á*.example.com'), 'á1.example.com')) | |
+ end | |
+ | |
+ def test_post_connection_check_wildcard_cn | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*.example.com'), 'www.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*.Example.COM'), 'www.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*.example.com'), 'WWW.Example.COM')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('www.*.com'), 'www.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*.example.com'), 'foo.example.com')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*.example.com'), 'bar.foo.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('baz*.example.com'), 'baz1.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*baz.example.com'), 'foobaz.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('b*z.example.com'), 'buzz.example.com')) | |
+ # Section 6.4.3 of RFC6125 states that client should NOT match identifier | |
+ # where wildcard is other than left-most label. | |
+ # | |
+ # Also implicitly mentions the wildcard character only in singular form, | |
+ # and discourages matching against more than one wildcard. | |
+ # | |
+ # See RFC 6125, section 7.2, subitem 2. | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*b*.example.com'), 'abc.example.com')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*b*.example.com'), 'ab.example.com')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('*b*.example.com'), 'bc.example.com')) | |
+ assert_equal(true, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('xn*.example.com'), 'xn1ca.example.com')) | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('xn--*.example.com'), 'xn--1ca.example.com')) | |
+ # part of U-label | |
+ # Subject in RFC5280 states case-insensitive ASCII comparison. | |
+ # | |
+ # See Section 7.2 of RFC 5280: | |
+ # IA5String is limited to the set of ASCII characters. | |
+ assert_equal(false, OpenSSL::SSL.verify_certificate_identity( | |
+ create_cert_with_name('á*.example.com'), 'á1.example.com')) | |
+ end | |
+ | |
+ def create_cert_with_san(san) | |
+ ef = OpenSSL::X509::ExtensionFactory.new | |
+ cert = OpenSSL::X509::Certificate.new | |
+ cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site") | |
+ ext = ef.create_ext('subjectAltName', san) | |
+ cert.add_extension(ext) | |
+ cert | |
+ end | |
+ | |
+ def create_cert_with_name(name) | |
+ cert = OpenSSL::X509::Certificate.new | |
+ cert.subject = OpenSSL::X509::Name.new([['DC', 'some'], ['DC', 'site'], ['CN', name]]) | |
+ cert | |
+ end | |
+ | |
# Create NULL byte SAN certificate | |
def create_null_byte_SAN_certificate(critical = false) | |
ef = OpenSSL::X509::ExtensionFactory.new |
This is a combination of these 3 patches:
- https://gist.github.com/mislav/055441129184a1512bb5 to patch SSLv3
- https://github.com/rvm/rvm/blob/master/patches/ruby/1.9.3/CVE-2015-1855-p484.patch to fix CVE-2015-1855
- https://gist.github.com/kriansa/dd1b9a0d8dfec776fc91 for TLS (1.1, 1.2) support
PKG_CONFIG_PATH=/usr/lib/openssl-1.0/pkgconfig \ rbenv install --patch 1.9.3-p551 < <(curl -sSL https://git.io/JOtSv)
Great... Thank you. But since openssl 1.0 is out of support and 1.1.0 and afterwards has removed https://www.openssl.org/docs/man1.1.1/man3/OpenSSL_add_all_digests.html and https://www.openssl.org/docs/man1.1.1/man3/OPENSSL_init_ssl.html both removed even with this patch we can't compile against OpenSSL > 1.1.0
I have made following changes. It passes ruby extconf.rb step
# result &&= %w[crypto libeay32].any? {|lib| have_library(lib, "OpenSSL_add_all_digests")}
result &&= %w[crypto libeay32].any? {|lib| have_library(lib, "OPENSSL_init_crypto")}
result &&= %w[ssl ssleay32].any? {|lib| have_library(lib, "OPENSSL_init_ssl")}
# result &&= %w[ssl ssleay32].any? {|lib| have_library(lib, "SSL_library_init")}
but gives an error on compile
compiling ossl_x509req.c
In file included from ossl.h:57,
from ossl_x509req.c:11:
/usr/local/ssl/include/openssl/asn1_mac.h:10:2: error: #error "This file is obsolete; please update your software."
In file included from ossl.h:213,
from ossl_x509req.c:11:
openssl_missing.h:79:35: error: macro "EVP_MD_CTX_create" passed 1 arguments, but takes just 0
In file included from ossl.h:213,
from ossl_x509req.c:11:
openssl_missing.h:83: error: expected declaration specifiers or ‘...’ before ‘(’ token
openssl_missing.h:83: error: conflicting types for ‘EVP_MD_CTX_reset’
/usr/local/ssl/include/openssl/evp.h:539: note: previous declaration of ‘EVP_MD_CTX_reset’ was here
openssl_missing.h:91: error: expected declaration specifiers or ‘...’ before ‘(’ token
ossl_x509req.c: In function ‘ossl_x509req_get_signature_algorithm’:
ossl_x509req.c:297: error: dereferencing pointer to incomplete type
ossl_x509req.c: In function ‘ossl_x509req_set_attributes’:
ossl_x509req.c:412: error: dereferencing pointer to incomplete type
ossl_x509req.c:413: error: dereferencing pointer to incomplete type
make: *** [ossl_x509req.o] Error 1
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This is a combination of these 3 patches: