From 9c81ee50a50a0ac65389cd39f5c556527d6c5d6d Mon Sep 17 00:00:00 2001 From: Braintree Date: Thu, 13 May 2010 11:56:56 -0500 Subject: [PATCH] Fixed ssl verification to raise an exception when the ca certificate is incorrect. --- Rakefile | 11 ++++++++++- history.md | 1 + lib/restclient/exceptions.rb | 8 ++++++-- lib/restclient/request.rb | 7 +++++++ spec/integration/certs/equifax.crt | 19 +++++++++++++++++++ spec/integration/certs/verisign.crt | 14 ++++++++++++++ spec/integration/request_spec.rb | 25 +++++++++++++++++++++++++ spec/request_spec.rb | 1 + 8 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 spec/integration/certs/equifax.crt create mode 100644 spec/integration/certs/verisign.crt create mode 100644 spec/integration/request_spec.rb diff --git a/Rakefile b/Rakefile index 59679f0..c0a58d2 100644 --- a/Rakefile +++ b/Rakefile @@ -25,11 +25,20 @@ Jeweler::RubyforgeTasks.new require 'spec/rake/spectask' desc "Run all specs" -Spec::Rake::SpecTask.new('spec') do |t| +task :spec => ["spec:unit", "spec:integration"] + +desc "Run unit specs" +Spec::Rake::SpecTask.new('spec:unit') do |t| t.spec_opts = ['--colour --format progress --loadby mtime --reverse'] t.spec_files = FileList['spec/*_spec.rb'] end +desc "Run integration specs" +Spec::Rake::SpecTask.new('spec:integration') do |t| + t.spec_opts = ['--colour --format progress --loadby mtime --reverse'] + t.spec_files = FileList['spec/integration/*_spec.rb'] +end + desc "Print specdocs" Spec::Rake::SpecTask.new(:doc) do |t| t.spec_opts = ["--format", "specdoc", "--dry-run"] diff --git a/history.md b/history.md index 4b37dad..360e06f 100644 --- a/history.md +++ b/history.md @@ -4,6 +4,7 @@ - use CGI for cookie parsing instead of custom code - unescape user and password before using them (patch provided by Lars Gierth) - expand ~ in ~/.restclientrc (patch provided by Mike Fletcher) +- ssl verification raise an exception when the ca certificate is incorrect (patch provided by Braintree) # 1.5.0 diff --git a/lib/restclient/exceptions.rb b/lib/restclient/exceptions.rb index 6f44f2a..c82424d 100644 --- a/lib/restclient/exceptions.rb +++ b/lib/restclient/exceptions.rb @@ -134,8 +134,12 @@ module RestClient message = 'Server broke connection' end - - + class SSLCertificateNotVerified < Exception + def initialize(message) + super(nil) + self.message = message + end + end end # backwards compatibility diff --git a/lib/restclient/request.rb b/lib/restclient/request.rb index 836a3ca..6e7370b 100644 --- a/lib/restclient/request.rb +++ b/lib/restclient/request.rb @@ -116,6 +116,13 @@ module RestClient 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 diff --git a/spec/integration/certs/equifax.crt b/spec/integration/certs/equifax.crt new file mode 100644 index 0000000..ed0bd76 --- /dev/null +++ b/spec/integration/certs/equifax.crt @@ -0,0 +1,19 @@ +-----BEGIN CERTIFICATE----- +MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV +UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy +dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 +MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx +dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B +AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f +BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A +cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC +AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ +MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm +aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw +ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj +IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF +MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA +A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y +7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh +1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 +-----END CERTIFICATE----- diff --git a/spec/integration/certs/verisign.crt b/spec/integration/certs/verisign.crt new file mode 100644 index 0000000..fd78678 --- /dev/null +++ b/spec/integration/certs/verisign.crt @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG +A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz +cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2 +MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV +BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt +YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN +ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE +BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is +I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G +CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do +lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc +AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k +-----END CERTIFICATE----- diff --git a/spec/integration/request_spec.rb b/spec/integration/request_spec.rb new file mode 100644 index 0000000..88284ec --- /dev/null +++ b/spec/integration/request_spec.rb @@ -0,0 +1,25 @@ +require File.dirname(__FILE__) + '/../base' + +describe RestClient::Request do + describe "ssl verification" do + it "is successful with the correct ca_file" do + request = RestClient::Request.new( + :method => :get, + :url => 'https://www.google.com', + :verify_ssl => OpenSSL::SSL::VERIFY_PEER, + :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "verisign.crt") + ) + expect { request.execute }.to_not raise_error + end + + it "is unsuccessful with an incorrect ca_file" do + request = RestClient::Request.new( + :method => :get, + :url => 'https://www.google.com', + :verify_ssl => OpenSSL::SSL::VERIFY_PEER, + :ssl_ca_file => File.join(File.dirname(__FILE__), "certs", "equifax.crt") + ) + expect { request.execute }.to raise_error(RestClient::SSLCertificateNotVerified) + end + end +end diff --git a/spec/request_spec.rb b/spec/request_spec.rb index e4dd035..3c2377b 100644 --- a/spec/request_spec.rb +++ b/spec/request_spec.rb @@ -412,6 +412,7 @@ describe RestClient::Request do :payload => 'payload', :verify_ssl => mode ) @net.should_receive(:verify_mode=).with(mode) + @net.should_receive(:verify_callback=) @http.stub!(:request) @request.stub!(:process_result) @request.stub!(:response_log)