1 # Fix based on upstream fix to Ruby 2.x |
|
2 # https://github.com/ruby/openssl/commit/e9a7bcb8bf2902f907c148a00bbcf21d3fa79596 |
|
3 |
|
4 diff -rup ruby-1.9.3-p551-orig/ext/openssl/lib/openssl/ssl-internal.rb ruby-1.9.3-p551/ext/openssl/lib/openssl/ssl-internal.rb |
|
5 --- ruby-1.9.3-p551-orig/ext/openssl/lib/openssl/ssl-internal.rb 2014-10-23 20:06:36.000000000 -0700 |
|
6 +++ ruby-1.9.3-p551/ext/openssl/lib/openssl/ssl-internal.rb 2015-05-21 16:31:27.271213113 -0700 |
|
7 @@ -135,8 +135,7 @@ module OpenSSL |
|
8 case san.tag |
|
9 when 2 # dNSName in GeneralName (RFC5280) |
|
10 should_verify_common_name = false |
|
11 - reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+") |
|
12 - return true if /\A#{reg}\z/i =~ hostname |
|
13 + return true if verify_hostname(hostname, san.value) |
|
14 when 7 # iPAddress in GeneralName (RFC5280) |
|
15 should_verify_common_name = false |
|
16 # follows GENERAL_NAME_print() in x509v3/v3_alt.c |
|
17 @@ -151,8 +150,7 @@ module OpenSSL |
|
18 if should_verify_common_name |
|
19 cert.subject.to_a.each{|oid, value| |
|
20 if oid == "CN" |
|
21 - reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+") |
|
22 - return true if /\A#{reg}\z/i =~ hostname |
|
23 + return true if verify_hostname(hostname, value) |
|
24 end |
|
25 } |
|
26 end |
|
27 @@ -160,6 +158,57 @@ module OpenSSL |
|
28 end |
|
29 module_function :verify_certificate_identity |
|
30 |
|
31 + def verify_hostname(hostname, san) # :nodoc: |
|
32 + # RFC 5280, IA5String is limited to the set of ASCII characters |
|
33 + return false unless san.ascii_only? |
|
34 + return false unless hostname.ascii_only? |
|
35 + |
|
36 + # See RFC 6125, section 6.4.1 |
|
37 + # Matching is case-insensitive. |
|
38 + san_parts = san.downcase.split(".") |
|
39 + |
|
40 + # TODO: this behavior should probably be more strict |
|
41 + return san == hostname if san_parts.size < 2 |
|
42 + |
|
43 + # Matching is case-insensitive. |
|
44 + host_parts = hostname.downcase.split(".") |
|
45 + |
|
46 + # RFC 6125, section 6.4.3, subitem 2. |
|
47 + # If the wildcard character is the only character of the left-most |
|
48 + # label in the presented identifier, the client SHOULD NOT compare |
|
49 + # against anything but the left-most label of the reference |
|
50 + # identifier (e.g., *.example.com would match foo.example.com but |
|
51 + # not bar.foo.example.com or example.com). |
|
52 + return false unless san_parts.size == host_parts.size |
|
53 + |
|
54 + # RFC 6125, section 6.4.3, subitem 1. |
|
55 + # The client SHOULD NOT attempt to match a presented identifier in |
|
56 + # which the wildcard character comprises a label other than the |
|
57 + # left-most label (e.g., do not match bar.*.example.net). |
|
58 + return false unless verify_wildcard(host_parts.shift, san_parts.shift) |
|
59 + |
|
60 + san_parts.join(".") == host_parts.join(".") |
|
61 + end |
|
62 + module_function :verify_hostname |
|
63 + |
|
64 + def verify_wildcard(domain_component, san_component) # :nodoc: |
|
65 + parts = san_component.split("*", -1) |
|
66 + |
|
67 + return false if parts.size > 2 |
|
68 + return san_component == domain_component if parts.size == 1 |
|
69 + |
|
70 + # RFC 6125, section 6.4.3, subitem 3. |
|
71 + # The client SHOULD NOT attempt to match a presented identifier |
|
72 + # where the wildcard character is embedded within an A-label or |
|
73 + # U-label of an internationalized domain name. |
|
74 + return false if domain_component.start_with?("xn--") && san_component != "*" |
|
75 + |
|
76 + parts[0].length + parts[1].length < domain_component.length && |
|
77 + domain_component.start_with?(parts[0]) && |
|
78 + domain_component.end_with?(parts[1]) |
|
79 + end |
|
80 + module_function :verify_wildcard |
|
81 + |
|
82 class SSLSocket |
|
83 include Buffering |
|
84 include SocketForwarder |
|
85 diff -rup ruby-1.9.3-p551-orig/test/openssl/test_ssl.rb ruby-1.9.3-p551/test/openssl/test_ssl.rb |
|
86 --- ruby-1.9.3-p551-orig/test/openssl/test_ssl.rb 2014-10-23 20:06:36.000000000 -0700 |
|
87 +++ ruby-1.9.3-p551/test/openssl/test_ssl.rb 2015-05-22 11:46:38.959996713 -0700 |
|
88 @@ -1,3 +1,4 @@ |
|
89 +# encoding: UTF-8 |
|
90 require_relative "utils" |
|
91 |
|
92 if defined?(OpenSSL) |
|
93 @@ -363,6 +364,156 @@ class OpenSSL::TestSSL < OpenSSL::SSLTes |
|
94 end |
|
95 end |
|
96 |
|
97 + def test_verify_hostname |
|
98 + assert_equal(true, OpenSSL::SSL.verify_hostname("www.example.com", "*.example.com")) |
|
99 + assert_equal(false, OpenSSL::SSL.verify_hostname("www.subdomain.example.com", "*.example.com")) |
|
100 + end |
|
101 + |
|
102 + def test_verify_wildcard |
|
103 + assert_equal(false, OpenSSL::SSL.verify_wildcard("foo", "x*")) |
|
104 + assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "foo")) |
|
105 + assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "f*")) |
|
106 + assert_equal(true, OpenSSL::SSL.verify_wildcard("foo", "*")) |
|
107 + assert_equal(false, OpenSSL::SSL.verify_wildcard("abc*bcd", "abcd")) |
|
108 + assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "x*")) |
|
109 + assert_equal(false, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "*--qdk4b9b")) |
|
110 + assert_equal(true, OpenSSL::SSL.verify_wildcard("xn--qdk4b9b", "xn--qdk4b9b")) |
|
111 + end |
|
112 + |
|
113 + # Comments in this test is excerpted from http://tools.ietf.org/html/rfc6125#page-27 |
|
114 + def test_post_connection_check_wildcard_san |
|
115 + # case-insensitive ASCII comparison |
|
116 + # RFC 6125, section 6.4.1 |
|
117 + # |
|
118 + # "..matching of the reference identifier against the presented identifier |
|
119 + # is performed by comparing the set of domain name labels using a |
|
120 + # case-insensitive ASCII comparison, as clarified by [DNS-CASE] (e.g., |
|
121 + # "WWW.Example.Com" would be lower-cased to "www.example.com" for |
|
122 + # comparison purposes) |
|
123 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
124 + create_cert_with_san('DNS:*.example.com'), 'www.example.com')) |
|
125 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
126 + create_cert_with_san('DNS:*.Example.COM'), 'www.example.com')) |
|
127 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
128 + create_cert_with_san('DNS:*.example.com'), 'WWW.Example.COM')) |
|
129 + # 1. The client SHOULD NOT attempt to match a presented identifier in |
|
130 + # which the wildcard character comprises a label other than the |
|
131 + # left-most label (e.g., do not match bar.*.example.net). |
|
132 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
133 + create_cert_with_san('DNS:www.*.com'), 'www.example.com')) |
|
134 + # 2. If the wildcard character is the only character of the left-most |
|
135 + # label in the presented identifier, the client SHOULD NOT compare |
|
136 + # against anything but the left-most label of the reference |
|
137 + # identifier (e.g., *.example.com would match foo.example.com but |
|
138 + # not bar.foo.example.com or example.com). |
|
139 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
140 + create_cert_with_san('DNS:*.example.com'), 'foo.example.com')) |
|
141 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
142 + create_cert_with_san('DNS:*.example.com'), 'bar.foo.example.com')) |
|
143 + # 3. The client MAY match a presented identifier in which the wildcard |
|
144 + # character is not the only character of the label (e.g., |
|
145 + # baz*.example.net and *baz.example.net and b*z.example.net would |
|
146 + # be taken to match baz1.example.net and foobaz.example.net and |
|
147 + # buzz.example.net, respectively). ... |
|
148 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
149 + create_cert_with_san('DNS:baz*.example.com'), 'baz1.example.com')) |
|
150 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
151 + create_cert_with_san('DNS:*baz.example.com'), 'foobaz.example.com')) |
|
152 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
153 + create_cert_with_san('DNS:b*z.example.com'), 'buzz.example.com')) |
|
154 + # Section 6.4.3 of RFC6125 states that client should NOT match identifier |
|
155 + # where wildcard is other than left-most label. |
|
156 + # |
|
157 + # Also implicitly mentions the wildcard character only in singular form, |
|
158 + # and discourages matching against more than one wildcard. |
|
159 + # |
|
160 + # See RFC 6125, section 7.2, subitem 2. |
|
161 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
162 + create_cert_with_san('DNS:*b*.example.com'), 'abc.example.com')) |
|
163 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
164 + create_cert_with_san('DNS:*b*.example.com'), 'ab.example.com')) |
|
165 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
166 + create_cert_with_san('DNS:*b*.example.com'), 'bc.example.com')) |
|
167 + # ... However, the client SHOULD NOT |
|
168 + # attempt to match a presented identifier where the wildcard |
|
169 + # character is embedded within an A-label or U-label [IDNA-DEFS] of |
|
170 + # an internationalized domain name [IDNA-PROTO]. |
|
171 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
172 + create_cert_with_san('DNS:xn*.example.com'), 'xn1ca.example.com')) |
|
173 + # part of A-label |
|
174 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
175 + create_cert_with_san('DNS:xn--*.example.com'), 'xn--1ca.example.com')) |
|
176 + # part of U-label |
|
177 + # dNSName in RFC5280 is an IA5String so U-label should NOT be allowed |
|
178 + # regardless of wildcard. |
|
179 + # |
|
180 + # See Section 7.2 of RFC 5280: |
|
181 + # IA5String is limited to the set of ASCII characters. |
|
182 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
183 + create_cert_with_san('DNS:á*.example.com'), 'á1.example.com')) |
|
184 + end |
|
185 + |
|
186 + def test_post_connection_check_wildcard_cn |
|
187 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
188 + create_cert_with_name('*.example.com'), 'www.example.com')) |
|
189 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
190 + create_cert_with_name('*.Example.COM'), 'www.example.com')) |
|
191 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
192 + create_cert_with_name('*.example.com'), 'WWW.Example.COM')) |
|
193 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
194 + create_cert_with_name('www.*.com'), 'www.example.com')) |
|
195 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
196 + create_cert_with_name('*.example.com'), 'foo.example.com')) |
|
197 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
198 + create_cert_with_name('*.example.com'), 'bar.foo.example.com')) |
|
199 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
200 + create_cert_with_name('baz*.example.com'), 'baz1.example.com')) |
|
201 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
202 + create_cert_with_name('*baz.example.com'), 'foobaz.example.com')) |
|
203 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
204 + create_cert_with_name('b*z.example.com'), 'buzz.example.com')) |
|
205 + # Section 6.4.3 of RFC6125 states that client should NOT match identifier |
|
206 + # where wildcard is other than left-most label. |
|
207 + # |
|
208 + # Also implicitly mentions the wildcard character only in singular form, |
|
209 + # and discourages matching against more than one wildcard. |
|
210 + # |
|
211 + # See RFC 6125, section 7.2, subitem 2. |
|
212 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
213 + create_cert_with_name('*b*.example.com'), 'abc.example.com')) |
|
214 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
215 + create_cert_with_name('*b*.example.com'), 'ab.example.com')) |
|
216 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
217 + create_cert_with_name('*b*.example.com'), 'bc.example.com')) |
|
218 + assert_equal(true, OpenSSL::SSL.verify_certificate_identity( |
|
219 + create_cert_with_name('xn*.example.com'), 'xn1ca.example.com')) |
|
220 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
221 + create_cert_with_name('xn--*.example.com'), 'xn--1ca.example.com')) |
|
222 + # part of U-label |
|
223 + # Subject in RFC5280 states case-insensitive ASCII comparison. |
|
224 + # |
|
225 + # See Section 7.2 of RFC 5280: |
|
226 + # IA5String is limited to the set of ASCII characters. |
|
227 + assert_equal(false, OpenSSL::SSL.verify_certificate_identity( |
|
228 + create_cert_with_name('á*.example.com'), 'á1.example.com')) |
|
229 + end |
|
230 + |
|
231 + def create_cert_with_san(san) |
|
232 + ef = OpenSSL::X509::ExtensionFactory.new |
|
233 + cert = OpenSSL::X509::Certificate.new |
|
234 + cert.subject = OpenSSL::X509::Name.parse("/DC=some/DC=site/CN=Some Site") |
|
235 + ext = ef.create_ext('subjectAltName', san) |
|
236 + cert.add_extension(ext) |
|
237 + cert |
|
238 + end |
|
239 + |
|
240 + def create_cert_with_name(name) |
|
241 + cert = OpenSSL::X509::Certificate.new |
|
242 + cert.subject = OpenSSL::X509::Name.new([['DC', 'some'], ['DC', 'site'], ['CN', name]]) |
|
243 + cert |
|
244 + end |
|
245 + |
|
246 + |
|
247 # Create NULL byte SAN certificate |
|
248 def create_null_byte_SAN_certificate(critical = false) |
|
249 ef = OpenSSL::X509::ExtensionFactory.new |
|