/ext/openssl/lib/openssl/ssl.rb
https://github.com/jlamberg/ruby · Ruby · 185 lines · 139 code · 24 blank · 22 comment · 15 complexity · 973415ea217657718eb13a26f2bed3ec MD5 · raw file
- =begin
- = $RCSfile$ -- Ruby-space definitions that completes C-space funcs for SSL
- = Info
- 'OpenSSL for Ruby 2' project
- Copyright (C) 2001 GOTOU YUUZOU <gotoyuzo@notwork.org>
- All rights reserved.
- = Licence
- This program is licenced under the same licence as Ruby.
- (See the file 'LICENCE'.)
- = Version
- $Id$
- =end
- require "openssl/buffering"
- require "fcntl"
- module OpenSSL
- module SSL
- class SSLContext
- DEFAULT_PARAMS = {
- :ssl_version => "SSLv23",
- :verify_mode => OpenSSL::SSL::VERIFY_PEER,
- :ciphers => "ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW",
- :options => OpenSSL::SSL::OP_ALL,
- }
- DEFAULT_CERT_STORE = OpenSSL::X509::Store.new
- DEFAULT_CERT_STORE.set_default_paths
- if defined?(OpenSSL::X509::V_FLAG_CRL_CHECK_ALL)
- DEFAULT_CERT_STORE.flags = OpenSSL::X509::V_FLAG_CRL_CHECK_ALL
- end
- ##
- # Sets the parameters for this SSL context to the values in +params+.
- # The keys in +params+ must be assignment methods on SSLContext.
- #
- # If the verify_mode is not VERIFY_NONE and ca_file, ca_path and
- # cert_store are not set then the system default certificate store is
- # used.
- def set_params(params={})
- params = DEFAULT_PARAMS.merge(params)
- params.each{|name, value| self.__send__("#{name}=", value) }
- if self.verify_mode != OpenSSL::SSL::VERIFY_NONE
- unless self.ca_file or self.ca_path or self.cert_store
- self.cert_store = DEFAULT_CERT_STORE
- end
- end
- return params
- end
- end
- module SocketForwarder
- def addr
- to_io.addr
- end
- def peeraddr
- to_io.peeraddr
- end
- def setsockopt(level, optname, optval)
- to_io.setsockopt(level, optname, optval)
- end
- def getsockopt(level, optname)
- to_io.getsockopt(level, optname)
- end
- def fcntl(*args)
- to_io.fcntl(*args)
- end
- def closed?
- to_io.closed?
- end
- def do_not_reverse_lookup=(flag)
- to_io.do_not_reverse_lookup = flag
- end
- end
- module Nonblock
- def initialize(*args)
- flag = File::NONBLOCK
- flag |= @io.fcntl(Fcntl::F_GETFL) if defined?(Fcntl::F_GETFL)
- @io.fcntl(Fcntl::F_SETFL, flag)
- super
- end
- end
- def verify_certificate_identity(cert, hostname)
- should_verify_common_name = true
- cert.extensions.each{|ext|
- next if ext.oid != "subjectAltName"
- ext.value.split(/,\s+/).each{|general_name|
- if /\ADNS:(.*)/ =~ general_name
- should_verify_common_name = false
- reg = Regexp.escape($1).gsub(/\\\*/, "[^.]+")
- return true if /\A#{reg}\z/i =~ hostname
- elsif /\AIP Address:(.*)/ =~ general_name
- should_verify_common_name = false
- return true if $1 == hostname
- end
- }
- }
- if should_verify_common_name
- cert.subject.to_a.each{|oid, value|
- if oid == "CN"
- reg = Regexp.escape(value).gsub(/\\\*/, "[^.]+")
- return true if /\A#{reg}\z/i =~ hostname
- end
- }
- end
- return false
- end
- module_function :verify_certificate_identity
- class SSLSocket
- include Buffering
- include SocketForwarder
- include Nonblock
- def post_connection_check(hostname)
- unless OpenSSL::SSL.verify_certificate_identity(peer_cert, hostname)
- raise SSLError, "hostname does not match the server certificate"
- end
- return true
- end
- def session
- SSL::Session.new(self)
- rescue SSL::Session::SessionError
- nil
- end
- end
- class SSLServer
- include SocketForwarder
- attr_accessor :start_immediately
- def initialize(svr, ctx)
- @svr = svr
- @ctx = ctx
- unless ctx.session_id_context
- session_id = OpenSSL::Digest::MD5.hexdigest($0)
- @ctx.session_id_context = session_id
- end
- @start_immediately = true
- end
- def to_io
- @svr
- end
- def listen(backlog=5)
- @svr.listen(backlog)
- end
- def shutdown(how=Socket::SHUT_RDWR)
- @svr.shutdown(how)
- end
- def accept
- sock = @svr.accept
- begin
- ssl = OpenSSL::SSL::SSLSocket.new(sock, @ctx)
- ssl.sync_close = true
- ssl.accept if @start_immediately
- ssl
- rescue SSLError => ex
- sock.close
- raise ex
- end
- end
- def close
- @svr.close
- end
- end
- end
- end