summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/custodian/protocoltest/ssl.rb78
1 files changed, 73 insertions, 5 deletions
diff --git a/lib/custodian/protocoltest/ssl.rb b/lib/custodian/protocoltest/ssl.rb
index 3e6ac18..0989ba9 100644
--- a/lib/custodian/protocoltest/ssl.rb
+++ b/lib/custodian/protocoltest/ssl.rb
@@ -145,6 +145,48 @@ class SSLCheck
end
#
+ # This is a fall-back method which is used to retrieve the certificate
+ # from the remote host in the case where fetching natively fails.
+ #
+ # It is obviously not a great method, because we shouldn't need to
+ # be shelling out to a command-line application over using our
+ # native/available SSL library.
+ #
+ # Beyond the ropy nature of this method there is another problem:
+ # we cannot fetch the bundle the remote-server might send us.
+ #
+ # So if this method is used `@fallback` is set to `true` such that
+ # we only validate the certificate is non-expired, and not that it
+ # is valid.
+ #
+ def certificate_fallback
+ cert = ""
+ in_cert = false
+
+ # Run the command.
+ out = `echo "" | openssl s_client -connect #{uri.host}:#{uri.port} 2>/dev/null`
+ # For each line of the output
+ out.split( /[\r\n]/ ).each do |line|
+
+ # Are we in a certificate?
+ in_cert = true if ( line =~ /BEGIN CERT/ )
+
+ # If so append the line.
+ if ( in_cert )
+ cert += line
+ cert += "\n"
+ end
+
+ # Are we at the end?
+ in_cert = false if ( line =~ /END CERT/ )
+ end
+
+ # Return the certificate
+ cert
+ end
+
+
+ #
# This connects to a host, and fetches its certificate and bundle
#
def certificate
@@ -198,12 +240,28 @@ class SSLCheck
if self.tests.empty?
verbose "All tests have been disabled for #{self.domain}"
return true
- elsif self.certificate.nil?
- self.errors << verbose("Failed to fetch certificate for #{self.domain}")
- return nil
- else
- return ![verify_subject, verify_valid_from, verify_valid_to, verify_signature].any? { |r| false == r }
end
+
+ # Did we fail to find the certificate?
+ if self.certificate.nil?
+
+ # Use our fallback method.
+ fallback = certificate_fallback()
+
+ # If we failed to fetch it then we cannot do anything useful.
+ if ( fallback.nil? )
+ self.errors << verbose("Failed to fetch certificate for #{self.domain}")
+ return nil
+ else
+ # Populate the certificate, and report that we used our
+ # fallback method - because we've no longer got access
+ # to the bundle the remote server might have sent us.
+ @fallback = true
+ @certificate = OpenSSL::X509::Certificate.new(fallback)
+ end
+ end
+
+ return ![verify_subject, verify_valid_from, verify_valid_to, verify_signature].any? { |r| false == r }
end
def verify_sslv3_disabled
@@ -308,6 +366,16 @@ class SSLCheck
end
def verify_signature
+ #
+ # If we used our fallback method we cannot verify that the
+ # signature is valid, because we're missing the bundle that
+ # the remote server should have sent us.
+ #
+ if ( @fallback )
+ verbose "Skipping certificate signature validation for #{self.domain} because fallback SSL-certificate had to be used and we think we'll fail"
+ return true;
+ end
+
unless self.tests.include?(:signature)
verbose "Skipping certificate signature validation for #{self.domain}"
return true