1- =begin
2- = $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL
3-
4- = Info
5- 'OpenSSL for Ruby 2' project
6- Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
7- All rights reserved.
8-
9- = Licence
10- This program is licenced under the same licence as Ruby.
11- (See the file 'LICENCE'.)
12-
13- = Version
14- $Id$
15- =end
16-
17- require "openssl/buffering"
18- require 'fcntl' # used by OpenSSL::SSL::Nonblock (if loaded)
19-
20- module OpenSSL
21- module SSL
22-
23- # FIXME: Using the old non-ASN1 logic here because our ASN1 appears to
24- # return the wrong types for some decoded objects.
25- # @see https://github.com/jruby/jruby/issues/1102
26- # @private
27- def verify_certificate_identity ( cert , hostname )
28- should_verify_common_name = true
29- cert . extensions . each { |ext |
30- next if ext . oid != "subjectAltName"
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)
34- if /\A DNS:(.*)/ =~ general_name
35- should_verify_common_name = false
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)
39- elsif /\A IP(?: Address)?:(.*)/ =~ general_name
40- should_verify_common_name = false
41- 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
49- end
50- }
51- }
52- if should_verify_common_name
53- cert . subject . to_a . each { |oid , value |
54- if oid == "CN"
55- return true if verify_hostname ( hostname , value )
56- end
57- }
58- end
59- return false
60- end
61- module_function :verify_certificate_identity
62-
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-
114- class SSLSocket
115- include Buffering
116- include SocketForwarder
117- include Nonblock
118-
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.
124- def post_connection_check ( hostname )
125- unless OpenSSL ::SSL . verify_certificate_identity ( peer_cert , hostname )
126- raise SSLError , "hostname \" #{ hostname } \" does not match the server certificate"
127- end
128- return true
129- end
130-
131- end
132-
133- ##
134- # SSLServer represents a TCP/IP server socket with Secure Sockets Layer.
135- class SSLServer
136- include SocketForwarder
137- # When true then #accept works exactly the same as TCPServer#accept
138- attr_accessor :start_immediately
139-
140- # Creates a new instance of SSLServer.
141- # * +srv+ is an instance of TCPServer.
142- # * +ctx+ is an instance of OpenSSL::SSL::SSLContext.
143- def initialize ( svr , ctx )
144- @svr = svr
145- @ctx = ctx
146- unless ctx . session_id_context
147- # see #6137 - session id may not exceed 32 bytes
148- prng = ::Random . new ( $0. hash )
149- session_id = prng . bytes ( 16 ) . unpack ( 'H*' ) [ 0 ]
150- @ctx . session_id_context = session_id
151- end
152- @start_immediately = true
153- end
154-
155- # Returns the TCPServer passed to the SSLServer when initialized.
156- def to_io
157- @svr
158- end
159-
160- # See TCPServer#listen for details.
161- def listen ( backlog = 5 )
162- @svr . listen ( backlog )
163- end
164-
165- # See BasicSocket#shutdown for details.
166- def shutdown ( how = Socket ::SHUT_RDWR )
167- @svr . shutdown ( how )
168- end
169-
170- # Works similar to TCPServer#accept.
171- def accept
172- # Socket#accept returns [socket, addrinfo].
173- # TCPServer#accept returns a socket.
174- # The following comma strips addrinfo.
175- sock , = @svr . accept
176- begin
177- ssl = OpenSSL ::SSL ::SSLSocket . new ( sock , @ctx )
178- ssl . sync_close = true
179- ssl . accept if @start_immediately
180- ssl
181- rescue SSLError => ex
182- sock . close
183- raise ex
184- end
185- end
186-
187- # See IO#close for details.
188- def close
189- @svr . close
190- end
191- end
192- end
193- end
1+ load 'jopenssl22/openssl/ssl.rb'
0 commit comments