diff --git a/lib/sslcheck/grade.rb b/lib/sslcheck/grade.rb index 65d2b21..a0bd8bb 100644 --- a/lib/sslcheck/grade.rb +++ b/lib/sslcheck/grade.rb @@ -36,9 +36,12 @@ module SSLCheck end @grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048 - @grade = [@grade, 'D'].max if @server.rc4? - @grade = [@grade, 'E'].max if @server.des3? + @grade = [@grade, 'C'].max if @server.des3? + @grade = [@grade, 'E'].max if @server.rc4? or @server.des? @grade = [@grade, 'F'].max if @server.ssl? or @server.key_size < 1024 + + @grade = 'M' unless @server.cert_valid + @grade = 'T' unless @server.cert_trusted end def warning @@ -62,9 +65,10 @@ module SSLCheck @success << :hsts_long if @server.hsts_long? end + ALL_WARNING = %i(md5_sig md5 rc4 des) ALL_SUCCESS = %i(pfs hsts hsts_long) def perfect - @grade = 'A+' if @grade == 'A' and @warning.empty? and (ALL_SUCCESS & @success) == ALL_SUCCESS + @grade = 'A+' if @grade == 'A' and (ALL_WARNING & @warning).empty? and (ALL_SUCCESS & @success) == ALL_SUCCESS end METHODS_SCORES = { SSLv2: 0, SSLv3: 80, TLSv1: 90, TLSv1_1: 95, TLSv1_2: 100 } diff --git a/lib/sslcheck/server.rb b/lib/sslcheck/server.rb index a5f9526..ac8d6a2 100644 --- a/lib/sslcheck/server.rb +++ b/lib/sslcheck/server.rb @@ -14,16 +14,18 @@ module SSLCheck end class Server - TCP_TIMEOUT = 60 + TCP_TIMEOUT = 10 SSL_TIMEOUT = 2*TCP_TIMEOUT EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2) SUPPORTED_METHODS = OpenSSL::SSL::SSLContext::METHODS - class TLSNotAvailableException < Exception; end - class CipherNotAvailable < Exception; end - class Timeout < Exception; end - class ConnectionError < Exception; end + class TLSException < Exception; end + class TLSNotAvailableException < TLSException; end + class CipherNotAvailable < TLSException; end + class Timeout < TLSException; end + class TLSTimeout < TLSException; end + class ConnectionError < TLSException; end - attr_reader :hostname, :port, :prefered_ciphers, :cert, :hsts + attr_reader :hostname, :port, :prefered_ciphers, :cert, :hsts, :cert_valid, :cert_trusted def initialize(hostname, port=443, methods: EXISTING_METHODS) @log = Logging.logger[hostname] @@ -111,10 +113,6 @@ module SSLCheck RUBY_EVAL end - def any_des? - des? or des3? - end - def ssl? sslv2? or sslv3? end @@ -187,12 +185,14 @@ module SSLCheck return block_given? ? block.call(ssl_socket) : nil rescue IO::WaitReadable @log.debug { "Waiting for SSL read to #{@hostname}:#{@port}" } - raise Timeout.new unless IO.select [socket], nil, nil, SSL_TIMEOUT + raise TLSTimeout.new unless IO.select [socket], nil, nil, SSL_TIMEOUT retry rescue IO::WaitWritable @log.debug { "Waiting for SSL write to #{@hostname}:#{@port}" } - raise Timeout.new unless IO.select nil, [socket], nil, SSL_TIMEOUT + raise TLSTimeout.new unless IO.select nil, [socket], nil, SSL_TIMEOUT retry + rescue OpenSSL::SSL::SSLError => e + raise TLSException, e ensure ssl_socket.close end @@ -229,14 +229,16 @@ module SSLCheck @methods.each do |method| next unless SUPPORTED_METHODS.include? method begin - @cert = ssl_client(method) { |s| s.peer_cert } + @cert, @chain = ssl_client(method) { |s| [s.peer_cert, s.peer_cert_chain] } @log.warn { "Certificate #{@cert.subject}" } break - rescue Exception => e + rescue TLSException => e @log.info { "Method #{method} not supported : #{e}" } end end raise TLSNotAvailableException.new unless @cert + @cert_valid = OpenSSL::SSL.verify_certificate_identity @cert, @hostname + @cert_trusted = verify_trust @chain, @cert end def prefered_cipher(method) @@ -265,7 +267,7 @@ module SSLCheck ssl_client method, [cipher] @log.warn { "Verify #{method} / #{cipher[0]} : OK" } true - rescue Exception => e + rescue TLSException => e @log.info { "Verify #{method} / #{cipher[0]} : NOK (#{e})" } false end @@ -305,5 +307,28 @@ module SSLCheck @log.info { 'No HSTS' } @hsts = nil end + + def verify_trust(chain, cert) + store = OpenSSL::X509::Store.new + #store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT + %w(mozilla cacert).each do |directory| + Dir.glob(File.join '/usr/share/ca-certificates', directory, '*').each do |file| + File.open file, 'r' do |file| + cert = OpenSSL::X509::Certificate.new file.read + begin + store.add_cert cert + rescue OpenSSL::X509::StoreError + end + end + end + end + chain.each do |cert| + begin + store.add_cert cert + rescue OpenSSL::X509::StoreError + end + end + store.verify cert + end end end