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