From dc21475820ff148fb42963752db0bfa6a23f5e1e Mon Sep 17 00:00:00 2001 From: Alex Eckermann Date: Fri, 31 Aug 2012 14:22:43 +0930 Subject: [PATCH] Added SSLContext support for specifying SSL verification params. Cleaned up the documentation in SSL. Pass the SSL options to the SSL#connection method. --- lib/moped/connection.rb | 3 +- lib/moped/sockets/ssl.rb | 73 +++++++++++++++++++++++++++++++++++++--- 2 files changed, 71 insertions(+), 5 deletions(-) diff --git a/lib/moped/connection.rb b/lib/moped/connection.rb index 035c5de..23534bd 100644 --- a/lib/moped/connection.rb +++ b/lib/moped/connection.rb @@ -30,10 +30,11 @@ def alive? # @since 1.0.0 def connect @sock = if !!@options[:ssl] - Sockets::SSL.connect @host, @port, @timeout + Sockets::SSL.connect @host, @port, @timeout, @options[:ssl] else Sockets::TCP.connect @host, @port, @timeout end + end # Is the connection connected? diff --git a/lib/moped/sockets/ssl.rb b/lib/moped/sockets/ssl.rb index 8e55d89..8b345c1 100644 --- a/lib/moped/sockets/ssl.rb +++ b/lib/moped/sockets/ssl.rb @@ -9,16 +9,19 @@ class SSL < TCP # Initialize the new TCPSocket with SSL. # # @example Initialize the socket. - # SSL.new("127.0.0.1", 27017) + # SSL.new("127.0.0.1", 27017, OpenSSL::SSL::SSLContext.new) # # @param [ String ] host The host. # @param [ Integer ] port The port. + # @param [ OpenSSL::SSL::SSLContext ] context The SSLContext. # # @since 1.2.0 - def initialize(host, port, *args) - super - @ssl = OpenSSL::SSL::SSLSocket.new(self) + def initialize(host, port, context, *args) + super host, port, *args + + @ssl = OpenSSL::SSL::SSLSocket.new(self, context) @ssl.sync_close = true + handle_socket_errors { @ssl.connect } end @@ -62,6 +65,68 @@ def handle_socket_errors raise Errors::ConnectionFailure, "SSL Error '#{e.to_s}' for connection to Mongo on #{host}:#{port}" end + class << self + + # Connect to the tcp server. + # + # @example Connect to the server. + # SSL.connect("127.0.0.1", 27017, 30, true) + # + # @param [ String ] host The host to connect to. + # @param [ Integer ] post The server port. + # @param [ Integer ] timeout The connection timeout. + # @param [ Boolean ] ssl_options If boolean then assume default options. + # @param [ Hash ] ssl_options Supply custom SSL options to SSLContext. + # + # @option ssl_options [ Boolean ] :verify_peer Flag for SSLContext.verify_mode + # @option ssl_options [ String ] :ca_path + # @option ssl_options [ String ] :ca_file + # @option ssl_options [ String ] :client_cert + # @option ssl_options [ String ] :client_key + # + # @return [ TCPSocket ] The socket. + # + # @since 1.0.0 + def connect(host, port, timeout, ssl_options = true) + Timeout::timeout(timeout) do + + context = OpenSSL::SSL::SSLContext.new + + if ssl_options.is_a?(Hash) + + if !!ssl_options[:verify_peer] + context.verify_mode = OpenSSL::SSL::VERIFY_PEER + + if ssl_options[:ca_path] + context.ca_path = ssl_options[:ca_path] + elsif ssl_options[:ca_file] + context.ca_file = ssl_options[:ca_file] + else + store = OpenSSL::X509::Store.new + store.set_default_paths + context.cert_store = store + end + else + context.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + + if ssl_options.has_key?(:client_cert) && ssl_options.has_key?(:client_key) + context.cert = OpenSSL::X509::Certificate.new(File.read(ssl_options[:client_cert])) + context.key = OpenSSL::PKey::RSA.new(File.read(ssl_options[:client_key])) + end + + else + context.verify_mode = OpenSSL::SSL::VERIFY_NONE + end + + sock = new(host, port, context) + sock.set_encoding('binary') + sock.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1) + sock + end + end + end + end end end