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: ssl.rb 41812 2013-07-06 17:05:08Z nagachika $
15=end
16
17require "openssl/buffering"
18require "fcntl"
19
20module OpenSSL
21  module SSL
22    class SSLContext
23      DEFAULT_PARAMS = {
24        :ssl_version => "SSLv23",
25        :verify_mode => OpenSSL::SSL::VERIFY_PEER,
26        :ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
27        :options => defined?(OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS) ?
28          OpenSSL::SSL::OP_ALL & ~OpenSSL::SSL::OP_DONT_INSERT_EMPTY_FRAGMENTS :
29          OpenSSL::SSL::OP_ALL,
30      }
31
32      DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
33      DEFAULT_CERT_STORE.set_default_paths
34      if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL)
35        DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
36      end
37
38      ##
39      # Sets the parameters for this SSL context to the values in +params+.
40      # The keys in +params+ must be assignment methods on SSLContext.
41      #
42      # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
43      # cert_store are not set then the system default certificate store is
44      # used.
45
46      def set_params(params={})
47        params = DEFAULT_PARAMS.merge(params)
48        params.each{|name, value| self.__send__("#{name}=", value) }
49        if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
50          unless self.ca_file or self.ca_path or self.cert_store
51            self.cert_store = DEFAULT_CERT_STORE
52          end
53        end
54        return params
55      end
56    end
57
58    module SocketForwarder
59      def addr
60        to_io.addr
61      end
62
63      def peeraddr
64        to_io.peeraddr
65      end
66
67      def setsockopt(level, optname, optval)
68        to_io.setsockopt(level, optname, optval)
69      end
70
71      def getsockopt(level, optname)
72        to_io.getsockopt(level, optname)
73      end
74
75      def fcntl(*args)
76        to_io.fcntl(*args)
77      end
78
79      def closed?
80        to_io.closed?
81      end
82
83      def do_not_reverse_lookup=(flag)
84        to_io.do_not_reverse_lookup = flag
85      end
86    end
87
88    module Nonblock
89      def initialize(*args)
90        flag = File::NONBLOCK
91        flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL)
92        @io.fcntl(Fcntl::F_SETFL, flag)
93        super
94      end
95    end
96
97    def verify_certificate_identity(cert, hostname)
98      should_verify_common_name = true
99      cert.extensions.each{|ext|
100        next if ext.oid != "subjectAltName"
101        ostr = OpenSSL::ASN1.decode(ext.to_der).value.last
102        sequence = OpenSSL::ASN1.decode(ostr.value)
103        sequence.value.each{|san|
104          case san.tag
105          when 2 # dNSName in GeneralName (RFC5280)
106            should_verify_common_name = false
107            reg = Regexp.escape(san.value).gsub(/\\\*/, "[^.]+")
108            return true if /\A#{reg}\z/i =~ hostname
109          when 7 # iPAddress in GeneralName (RFC5280)
110            should_verify_common_name = false
111            # follows GENERAL_NAME_print() in x509v3/v3_alt.c
112            if san.value.size == 4
113              return true if san.value.unpack('C*').join('.') == hostname
114            elsif san.value.size == 16
115              return true if san.value.unpack('n*').map { |e| sprintf("%X", e) }.join(':') == hostname
116            end
117          end
118        }
119      }
120      if should_verify_common_name
121        cert.subject.to_a.each{|oid, value|
122          if oid == "CN"
123            reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
124            return true if /\A#{reg}\z/i =~ hostname
125          end
126        }
127      end
128      return false
129    end
130    module_function :verify_certificate_identity
131
132    class SSLSocket
133      include Buffering
134      include SocketForwarder
135      include Nonblock
136
137      def post_connection_check(hostname)
138        unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
139          raise SSLError, "hostname \"#{hostname}\" does not match the server certificate"
140        end
141        return true
142      end
143
144      def session
145        SSL::Session.new(self)
146      rescue SSL::Session::SessionError
147        nil
148      end
149    end
150
151    class SSLServer
152      include SocketForwarder
153      attr_accessor :start_immediately
154
155      def initialize(svr, ctx)
156        @svr = svr
157        @ctx = ctx
158        unless ctx.session_id_context
159          # see #6137 - session id may not exceed 32 bytes
160          prng = ::Random.new($0.hash)
161          session_id = prng.bytes(16).unpack('H*')[0]
162          @ctx.session_id_context = session_id
163        end
164        @start_immediately = true
165      end
166
167      def to_io
168        @svr
169      end
170
171      def listen(backlog=5)
172        @svr.listen(backlog)
173      end
174
175      def shutdown(how=Socket::SHUT_RDWR)
176        @svr.shutdown(how)
177      end
178
179      def accept
180        sock = @svr.accept
181        begin
182          ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
183          ssl.sync_close = true
184          ssl.accept if @start_immediately
185          ssl
186        rescue SSLError => ex
187          sock.close
188          raise ex
189        end
190      end
191
192      def close
193        @svr.close
194      end
195    end
196  end
197end
198