@@ -21,78 +21,106 @@ module OpenSSL
2121 module SSL
2222
2323 # FIXME: Using the old non-ASN1 logic here because our ASN1 appears to
24- # return the wrong types for some decoded objects. See #1102
24+ # return the wrong types for some decoded objects.
25+ # @see https://github.com/jruby/jruby/issues/1102
26+ # @private
2527 def verify_certificate_identity ( cert , hostname )
2628 should_verify_common_name = true
27- cert . extensions . each { |ext |
29+ cert . extensions . each { |ext |
2830 next if ext . oid != "subjectAltName"
29- ext . value . split ( /,\s +/ ) . each { |general_name |
31+ ext . value . split ( /,\s +/ ) . each { |general_name |
32+ # MRI 1.9.3 (since we parse ASN.1 differently)
33+ # when 2 # dNSName in GeneralName (RFC5280)
3034 if /\A DNS:(.*)/ =~ general_name
3135 should_verify_common_name = false
32- reg = Regexp . escape ( $1) . gsub ( /\\ \* / , "[^.]+" )
33- return true if /\A #{ reg } \z /i =~ hostname
34- # NOTE: somehow we need the IP: canonical form
35- # seems there were failures elsewhere when not
36- # not sure how that's possible possible to-do!
36+ return true if verify_hostname ( hostname , $1)
37+ # MRI 1.9.3 (since we parse ASN.1 differently)
38+ # when 7 # iPAddress in GeneralName (RFC5280)
3739 elsif /\A IP(?: Address)?:(.*)/ =~ general_name
38- #elsif /\AIP Address:(.*)/ =~ general_name
3940 should_verify_common_name = false
4041 return true if $1 == hostname
42+ # NOTE: bellow logic makes little sense as we read exts differently
43+ #value = $1 # follows GENERAL_NAME_print() in x509v3/v3_alt.c
44+ #if value.size == 4
45+ # return true if value.unpack('C*').join('.') == hostname
46+ #elsif value.size == 16
47+ # return true if value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
48+ #end
4149 end
4250 }
4351 }
4452 if should_verify_common_name
45- cert . subject . to_a . each { |oid , value |
53+ cert . subject . to_a . each { |oid , value |
4654 if oid == "CN"
47- reg = Regexp . escape ( value ) . gsub ( /\\ \* / , "[^.]+" )
48- return true if /\A #{ reg } \z /i =~ hostname
55+ return true if verify_hostname ( hostname , value )
4956 end
5057 }
5158 end
5259 return false
5360 end
54- =begin
55- def verify_certificate_identity(cert, hostname)
56- should_verify_common_name = true
57- cert.extensions.each{|ext|
58- next if ext.oid != "subjectAltName"
59- ostr = OpenSSL::ASN1.decode(ext.to_der).value.last
60- sequence = OpenSSL::ASN1.decode(ostr.value)
61- sequence.value.each{|san|
62- case san.tag
63- when 2 # dNSName in GeneralName (RFC5280)
64- should_verify_common_name = false
65- reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+")
66- return true if /\A#{reg}\z/i =~ hostname
67- when 7 # iPAddress in GeneralName (RFC5280)
68- should_verify_common_name = false
69- # follows GENERAL_NAME_print() in x509v3/v3_alt.c
70- if san.value.size == 4
71- return true if san.value.unpack('C*').join('.') == hostname
72- elsif san.value.size == 16
73- return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
74- end
75- end
76- }
77- }
78- if should_verify_common_name
79- cert.subject.to_a.each{|oid, value|
80- if oid == "CN"
81- reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
82- return true if /\A#{reg}\z/i =~ hostname
83- end
84- }
85- end
86- return false
87- end
88- =end
8961 module_function :verify_certificate_identity
9062
63+ def verify_hostname ( hostname , san ) # :nodoc:
64+ # RFC 5280, IA5String is limited to the set of ASCII characters
65+ return false unless san . ascii_only?
66+ return false unless hostname . ascii_only?
67+
68+ # See RFC 6125, section 6.4.1
69+ # Matching is case-insensitive.
70+ san_parts = san . downcase . split ( "." )
71+
72+ # TODO: this behavior should probably be more strict
73+ return san == hostname if san_parts . size < 2
74+
75+ # Matching is case-insensitive.
76+ host_parts = hostname . downcase . split ( "." )
77+
78+ # RFC 6125, section 6.4.3, subitem 2.
79+ # If the wildcard character is the only character of the left-most
80+ # label in the presented identifier, the client SHOULD NOT compare
81+ # against anything but the left-most label of the reference
82+ # identifier (e.g., *.example.com would match foo.example.com but
83+ # not bar.foo.example.com or example.com).
84+ return false unless san_parts . size == host_parts . size
85+
86+ # RFC 6125, section 6.4.3, subitem 1.
87+ # The client SHOULD NOT attempt to match a presented identifier in
88+ # which the wildcard character comprises a label other than the
89+ # left-most label (e.g., do not match bar.*.example.net).
90+ return false unless verify_wildcard ( host_parts . shift , san_parts . shift )
91+
92+ san_parts . join ( "." ) == host_parts . join ( "." )
93+ end
94+ module_function :verify_hostname
95+
96+ def verify_wildcard ( domain_component , san_component ) # :nodoc:
97+ parts = san_component . split ( "*" , -1 )
98+
99+ return false if parts . size > 2
100+ return san_component == domain_component if parts . size == 1
101+
102+ # RFC 6125, section 6.4.3, subitem 3.
103+ # The client SHOULD NOT attempt to match a presented identifier
104+ # where the wildcard character is embedded within an A-label or
105+ # U-label of an internationalized domain name.
106+ return false if domain_component . start_with? ( "xn--" ) && san_component != "*"
107+
108+ parts [ 0 ] . length + parts [ 1 ] . length < domain_component . length &&
109+ domain_component . start_with? ( parts [ 0 ] ) &&
110+ domain_component . end_with? ( parts [ 1 ] )
111+ end
112+ module_function :verify_wildcard
113+
91114 class SSLSocket
92115 include Buffering
93116 include SocketForwarder
94117 include Nonblock
95118
119+ ##
120+ # Perform hostname verification after an SSL connection is established
121+ #
122+ # This method MUST be called after calling #connect to ensure that the
123+ # hostname of a remote peer has been verified.
96124 def post_connection_check ( hostname )
97125 unless OpenSSL ::SSL . verify_certificate_identity ( peer_cert , hostname )
98126 raise SSLError , "hostname \" #{ hostname } \" does not match the server certificate"
@@ -141,7 +169,10 @@ def shutdown(how=Socket::SHUT_RDWR)
141169
142170 # Works similar to TCPServer#accept.
143171 def accept
144- sock = @svr . accept
172+ # Socket#accept returns [socket, addrinfo].
173+ # TCPServer#accept returns a socket.
174+ # The following comma strips addrinfo.
175+ sock , = @svr . accept
145176 begin
146177 ssl = OpenSSL ::SSL ::SSLSocket . new ( sock , @ctx )
147178 ssl . sync_close = true
0 commit comments