diff --git a/lib/restclient/request.rb b/lib/restclient/request.rb index bc8b462..96d8579 100644 --- a/lib/restclient/request.rb +++ b/lib/restclient/request.rb @@ -23,12 +23,14 @@ module RestClient # * :verify_ssl enable ssl verification, possible values are constants from OpenSSL::SSL # * :timeout and :open_timeout passing in -1 will disable the timeout by setting the corresponding net timeout values to nil # * :ssl_client_cert, :ssl_client_key, :ssl_ca_file + # * :ssl_version specifies the SSL version for the underlying Net::HTTP connection (defaults to 'SSLv3') class Request attr_reader :method, :url, :headers, :cookies, :payload, :user, :password, :timeout, :max_redirects, :open_timeout, :raw_response, :verify_ssl, :ssl_client_cert, - :ssl_client_key, :ssl_ca_file, :processed_headers, :args + :ssl_client_key, :ssl_ca_file, :processed_headers, :args, + :ssl_version def self.execute(args, & block) new(args).execute(& block) @@ -54,6 +56,7 @@ module RestClient @ssl_client_cert = args[:ssl_client_cert] || nil @ssl_client_key = args[:ssl_client_key] || nil @ssl_ca_file = args[:ssl_ca_file] || nil + @ssl_version = args[:ssl_version] || 'SSLv3' @tf = nil # If you are a raw request, this is your tempfile @max_redirects = args[:max_redirects] || 10 @processed_headers = make_headers headers @@ -145,10 +148,18 @@ module RestClient net = net_http_class.new(uri.host, uri.port) net.use_ssl = uri.is_a?(URI::HTTPS) + net.ssl_version = @ssl_version if (@verify_ssl == false) || (@verify_ssl == OpenSSL::SSL::VERIFY_NONE) net.verify_mode = OpenSSL::SSL::VERIFY_NONE elsif @verify_ssl.is_a? Integer net.verify_mode = @verify_ssl + net.verify_callback = lambda do |preverify_ok, ssl_context| + if (!preverify_ok) || ssl_context.error != 0 + err_msg = "SSL Verification failed -- Preverify: #{preverify_ok}, Error: #{ssl_context.error_string} (#{ssl_context.error})" + raise SSLCertificateNotVerified.new(err_msg) + end + true + end end net.cert = @ssl_client_cert if @ssl_client_cert net.key = @ssl_client_key if @ssl_client_key @@ -179,11 +190,6 @@ module RestClient raise RestClient::ServerBrokeConnection rescue Timeout::Error raise RestClient::RequestTimeout - rescue OpenSSL::SSL::SSLError => error - # UGH. Not sure if this is needed at all. SSLCertificateNotVerified is not being used internally. - # I think it would be better to leave SSLError processing to the client (they'd have to do that anyway...) - raise SSLCertificateNotVerified.new(error.message) if error.message.include?("certificate verify failed") - raise error end def setup_credentials(req) @@ -315,4 +321,4 @@ module MIME end end end -end +end \ No newline at end of file diff --git a/spec/request_spec.rb b/spec/request_spec.rb index ccb84d0..e25348d 100644 --- a/spec/request_spec.rb +++ b/spec/request_spec.rb @@ -205,6 +205,7 @@ describe RestClient::Request do it "transmits the request with Net::HTTP" do @http.should_receive(:request).with('req', 'payload') + @net.should_receive(:ssl_version=).with('SSLv3') @request.should_receive(:process_result) @request.transmit(@uri, 'req', 'payload') end @@ -212,6 +213,7 @@ describe RestClient::Request do describe "payload" do it "sends nil payloads" do @http.should_receive(:request).with('req', nil) + @net.should_receive(:ssl_version=).with('SSLv3') @request.should_receive(:process_result) @request.stub!(:response_log) @request.transmit(@uri, 'req', nil) @@ -241,6 +243,8 @@ describe RestClient::Request do describe "credentials" do it "sets up the credentials prior to the request" do @http.stub!(:request) + @net.should_receive(:ssl_version=).with('SSLv3') + @request.stub!(:process_result) @request.stub!(:response_log) @@ -269,6 +273,7 @@ describe RestClient::Request do it "catches EOFError and shows the more informative ServerBrokeConnection" do @http.stub!(:request).and_raise(EOFError) + @net.should_receive(:ssl_version=).with('SSLv3') lambda { @request.transmit(@uri, 'req', nil) }.should raise_error(RestClient::ServerBrokeConnection) end @@ -375,23 +380,25 @@ describe RestClient::Request do describe "timeout" do it "set read_timeout" do - @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123) + @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :timeout => 123, :ssl_version => 'SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @net.should_receive(:read_timeout=).with(123) + @net.should_receive(:ssl_version=).with('SSLv3') @request.transmit(@uri, 'req', nil) end it "set open_timeout" do - @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123) + @request = RestClient::Request.new(:method => :put, :url => 'http://some/resource', :payload => 'payload', :open_timeout => 123, :ssl_version => 'SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @net.should_receive(:open_timeout=).with(123) + @net.should_receive(:ssl_version=).with('SSLv3') @request.transmit(@uri, 'req', nil) end @@ -401,6 +408,7 @@ describe RestClient::Request do it "uses SSL when the URI refers to a https address" do @uri.stub!(:is_a?).with(URI::HTTPS).and_return(true) @net.should_receive(:use_ssl=).with(true) + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -413,6 +421,7 @@ describe RestClient::Request do it "should set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is false" do @net.should_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE) + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -420,8 +429,9 @@ describe RestClient::Request do end it "should not set net.verify_mode to OpenSSL::SSL::VERIFY_NONE if verify_ssl is true" do - @request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true) + @request = RestClient::Request.new(:method => :put, :url => 'https://some/resource', :payload => 'payload', :verify_ssl => true, :ssl_version => 'SSLv3') @net.should_not_receive(:verify_mode=).with(OpenSSL::SSL::VERIFY_NONE) + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -433,8 +443,11 @@ describe RestClient::Request do @request = RestClient::Request.new( :method => :put, :url => 'https://some/resource', :payload => 'payload', + :ssl_version => 'SSLv3', :verify_ssl => mode ) @net.should_receive(:verify_mode=).with(mode) + @net.should_receive(:verify_callback=) + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -450,9 +463,11 @@ describe RestClient::Request do :method => :put, :url => 'https://some/resource', :payload => 'payload', + :ssl_version => 'SSLv3', :ssl_client_cert => "whatsupdoc!" ) @net.should_receive(:cert=).with("whatsupdoc!") + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -463,9 +478,11 @@ describe RestClient::Request do @request = RestClient::Request.new( :method => :put, :url => 'https://some/resource', + :ssl_version => 'SSLv3', :payload => 'payload' ) @net.should_not_receive(:cert=).with("whatsupdoc!") + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -481,9 +498,11 @@ describe RestClient::Request do :method => :put, :url => 'https://some/resource', :payload => 'payload', + :ssl_version => 'SSLv3', :ssl_client_key => "whatsupdoc!" ) @net.should_receive(:key=).with("whatsupdoc!") + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -494,9 +513,11 @@ describe RestClient::Request do @request = RestClient::Request.new( :method => :put, :url => 'https://some/resource', + :ssl_version => 'SSLv3', :payload => 'payload' ) @net.should_not_receive(:key=).with("whatsupdoc!") + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -512,9 +533,11 @@ describe RestClient::Request do :method => :put, :url => 'https://some/resource', :payload => 'payload', + :ssl_version => 'SSLv3', :ssl_ca_file => "Certificate Authority File" ) @net.should_receive(:ca_file=).with("Certificate Authority File") + @net.should_receive(:ssl_version=).with('SSLv3') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -525,9 +548,11 @@ describe RestClient::Request do @request = RestClient::Request.new( :method => :put, :url => 'https://some/resource', + :ssl_version => 'TSLv1', :payload => 'payload' ) @net.should_not_receive(:ca_file=).with("Certificate Authority File") + @net.should_receive(:ssl_version=).with('TSLv1') @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log) @@ -539,13 +564,15 @@ describe RestClient::Request do @request = RestClient::Request.new( :method => :put, :url => 'https://some/resource', + :ssl_version => 'SSLv3', :payload => 'payload' ) net_http_res = Net::HTTPNoContent.new("", "204", "No Content") net_http_res.stub!(:read_body).and_return(nil) @http.should_receive(:request).and_return(@request.fetch_body(net_http_res)) + @net.should_receive(:ssl_version=).with('SSLv3') response = @request.transmit(@uri, 'req', 'payload') response.should_not be_nil response.code.should == 204 end -end +end \ No newline at end of file