Refactor logging

v1
Nicolas Vinot 8 years ago
parent c3bb0213bd
commit 4fa9497a0d
  1. 7
      Gemfile
  2. 6
      Makefile
  3. 10
      bin/check_https.rb
  4. 3
      bin/check_https_alexa.rb
  5. 15
      bin/check_smtp.rb
  6. 9
      bin/check_xmpp.rb
  7. 3
      lib/cryptcheck.rb
  8. 28
      lib/cryptcheck/logger.rb
  9. 56
      lib/cryptcheck/tls.rb
  10. 158
      lib/cryptcheck/tls/grade.rb
  11. 2
      lib/cryptcheck/tls/https/grade.rb
  12. 19
      lib/cryptcheck/tls/https/server.rb
  13. 135
      lib/cryptcheck/tls/server.rb
  14. 4
      lib/cryptcheck/tls/xmpp.rb
  15. 2
      lib/cryptcheck/tls/xmpp/grade.rb
  16. 2
      lib/cryptcheck/tls/xmpp/server.rb

@ -1,5 +1,6 @@
source 'https://rubygems.org'
gem 'rake'
gem 'httparty'
gem 'nokogiri'
gem 'net-ssh', '>= 2.9.2.beta'
@ -8,9 +9,9 @@ gem 'tcp_timeout'
gem 'parallel'
gem 'ruby-progressbar'
gem 'logging'
gem 'activerecord'
gem 'sqlite3'
gem 'rake'
#gem 'activerecord'
#gem 'sqlite3'
gem 'colorize'
group :test do
gem 'rspec'

@ -34,13 +34,15 @@ $(OPENSSL_DIR)/:
$(OPENSSL_DIR)/Makefile: | $(OPENSSL_DIR)/
cd $(OPENSSL_DIR); ./config shared
$(OPENSSL_DIR)/libssl.so.1.0.0 $(OPENSSL_DIR)/libcrypto.so.1.0.0: $(OPENSSL_DIR)/Makefile
$(OPENSSL_DIR)/libssl.so $(OPENSSL_DIR)/libcrypto.so: $(OPENSSL_DIR)/Makefile
$(MAKE) -C $(OPENSSL_DIR) depend build_libs
lib/%.so: $(OPENSSL_DIR)/%.so
cp $< $@
lib/%.so.1.0.0: $(OPENSSL_DIR)/%.so.1.0.0
cp $< $@
libs: lib/libssl.so.1.0.0 lib/libcrypto.so.1.0.0
libs: lib/libssl.so lib/libcrypto.so lib/libssl.so.1.0.0 lib/libcrypto.so.1.0.0
$(RUBY_DIR)/:
wget http://cache.ruby-lang.org/pub/ruby/$(RUBY_MAJOR_VERSION)/$(RUBY_DIR).tar.gz

@ -2,18 +2,18 @@
$:.unshift File.expand_path File.join File.dirname(__FILE__), '../lib'
require 'rubygems'
require 'bundler/setup'
require 'logging'
require 'cryptcheck'
name = ARGV[0] || 'index'
file = ::File.join 'output', "#{name}.yml"
if ::File.exist? file
::CryptCheck::Logger.level = :none
::CryptCheck::Tls::Https.analyze_from_file "output/#{name}.yml", "output/#{name}.html"
else
::Logging.logger.root.appenders = ::Logging.appenders.stdout
::Logging.logger.root.level = :warn
::CryptCheck::Logger.level = :info
server = ::CryptCheck::Tls::Https::Server.new(ARGV[0], ARGV[1] || 443)
p grade = ::CryptCheck::Tls::Https::Grade.new(server)
grade = ::CryptCheck::Tls::Https::Grade.new server
::CryptCheck::Logger.info { '' }
grade.display
end

@ -7,8 +7,7 @@ require 'cryptcheck'
GROUP_NAME = 'Top 100 Alexa'
::Logging.logger.root.appenders = ::Logging.appenders.stdout
::Logging.logger.root.level = :error
::CryptCheck::Logger.level = :none
hosts = []
::File.open('top-1m.csv', 'r') do |file|

@ -6,14 +6,15 @@ require 'logging'
require 'cryptcheck'
name = ARGV[0]
unless name
::CryptCheck::Tls::Smtp.analyze_from_file 'output/smtp.yml', 'output/smtp.html'
else
::Logging.logger.root.appenders = ::Logging.appenders.stdout
::Logging.logger.root.level = :warn
if name
::CryptCheck::Logger.level = :info
server = ::CryptCheck::Tls::Smtp::Server.new(ARGV[0], ARGV[1] || 25)
p grade = ::CryptCheck::Tls::Smtp::Grade.new(server)
grade = ::CryptCheck::Tls::Smtp::Grade.new server
::CryptCheck::Logger.info { '' }
grade.display
else
::CryptCheck::Logger.level = :none
::CryptCheck::Tls::Smtp.analyze_from_file 'output/smtp.yml', 'output/smtp.html'
end

@ -7,12 +7,13 @@ require 'cryptcheck'
name = ARGV[0]
if name
::Logging.logger.root.appenders = ::Logging.appenders.stdout
::Logging.logger.root.level = :warn
::CryptCheck::Logger.level = :info
server = ::CryptCheck::Tls::Xmpp::Server.new(name, ARGV[1] || :s2s)
p grade = ::CryptCheck::Tls::Xmpp::Grade.new(server)
grade = ::CryptCheck::Tls::Xmpp::Grade.new(server)
::CryptCheck::Logger.info { '' }
grade.display
else
::CryptCheck::Logger.level = :none
::CryptCheck::Tls::Xmpp.analyze_from_file 'output/xmpp.yml', 'output/xmpp.html'
end

@ -1,4 +1,7 @@
require 'colorize'
module CryptCheck
autoload :Logger, 'cryptcheck/logger'
autoload :Tls, 'cryptcheck/tls'
module Tls
autoload :Server, 'cryptcheck/tls/server'

@ -0,0 +1,28 @@
module CryptCheck
class Logger
LEVELS = %i(trace debug info warning error fatal none)
@@level = :info
def self.level=(level)
@@level = level
end
def self.log(level, string=nil, output: $stdout, &block)
return unless enabled? level
output.puts(string ? string : block.call)
end
LEVELS.each do |level|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def self.#{level}(string=nil, output: $stdout, &block)
self.log :#{level}, string, output: output, &block
end
RUBY_EVAL
end
private
def self.enabled?(level)
LEVELS.index(level) >= LEVELS.index(@@level)
end
end
end

@ -5,20 +5,46 @@ require 'parallel'
module CryptCheck
module Tls
MAX_ANALYSIS_DURATION = 600
PARALLEL_ANALYSIS = 10
@@log = ::Logging.logger[Tls]
PARALLEL_ANALYSIS = 10
TYPES = {
md5: %w(MD5),
sha1: %w(SHA),
psk: %w(PSK),
srp: %w(SRP),
anonymous: %w(ADH AECDH),
dss: %w(DSS),
null: %w(NULL),
export: %w(EXP),
des: %w(DES-CBC),
rc4: %w(RC4),
des3: %w(3DES DES-CBC3),
pfs: %w(DHE EDH ECDHE ECDH)
}
TYPES.each do |name, ciphers|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def self.#{name}?(cipher)
#{ciphers}.any? { |c| /(^|-)#\{c\}(-|$)/ =~ cipher }
end
RUBY_EVAL
end
def self.grade(hostname, port, server_class:, grade_class:)
timeout MAX_ANALYSIS_DURATION do
grade_class.new server_class.new hostname, port
end
rescue ::Exception => e
@@log.error { "Error during #{hostname}:#{port} analysis : #{e}" }
@Logger.error { "Error during #{hostname}:#{port} analysis : #{e}" }
TlsNotSupportedGrade.new TlsNotSupportedServer.new hostname, port
end
def self.analyze(hosts, template, output, groups = nil, port:, server_class:, grade_class:)
results = {}
results = {}
semaphore = ::Mutex.new
::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item[1] } do |description, host|
result = grade host.strip, port, server_class: server_class, grade_class: grade_class
@ -51,7 +77,7 @@ module CryptCheck
def self.analyze_from_file(file, template, output, port:, server_class:, grade_class:)
config = ::YAML.load_file file
hosts = []
hosts = []
groups = []
config.each do |c|
d, hs = c['description'], c['hostnames']
@ -61,6 +87,26 @@ module CryptCheck
self.analyze hosts, template, output, groups, port: port, server_class: server_class, grade_class: grade_class
end
def self.colorize(cipher)
colors = case
when /^SSL/ =~ cipher,
dss?(cipher),
anonymous?(cipher),
null?(cipher),
export?(cipher),
md5?(cipher),
des?(cipher),
rc4?(cipher)
{ color: :white, background: :red }
when des3?(cipher)
{ color: :yellow }
when :TLSv1_2 == cipher,
pfs?(cipher)
{ color: :green }
end
cipher.to_s.colorize colors
end
private
SCORES = %w(A+ A A- B C D E F T M X)

@ -2,109 +2,173 @@ module CryptCheck
module Tls
class TlsNotSupportedGrade
attr_reader :server, :score, :grade
def initialize(server)
@server, @score, @grade = server, -1, 'X'
end
end
class Grade
attr_reader :server, :score, :grade, :warning, :success
attr_reader :server, :protocol_score, :key_exchange_score, :cipher_strengths_score, :score, :grade, :error, :warning, :success
def initialize(server)
@server = server
protocol_score
key_exchange_score
cipher_strengths_score
@score = @protocol_score*0.3 + @key_exchange_score*0.3 + @cipher_strengths_score*0.4
calculate_protocol_score
calculate_key_exchange_score
calculate_cipher_strengths_score
@score = @protocol_score*0.3 + @key_exchange_score*0.3 + @cipher_strengths_score*0.4
calculate_error
calculate_warning
calculate_success
calculate_grade
warning
success
perfect
calculate_perfect
end
def display
color = case self.grade
when 'A+'
:blue
when 'A'
:green
when 'B', 'C'
:yellow
when 'E', 'F'
:red
when 'M', 'T'
{ color: :white, background: :red }
end
Logger.info { "Grade : #{self.grade.colorize color }" }
Logger.info { '' }
Logger.info { "Protocole : #{self.protocol_score} / 100" }
Logger.info { "Key exchange : #{self.key_exchange_score} / 100" }
Logger.info { "Ciphers strength : #{self.cipher_strengths_score} / 100" }
Logger.info { "Overall score : #{self.score} / 100" }
Logger.info { '' }
Logger.info { "Errors : #{self.error.join(' ').colorize :red }" } unless self.error.empty?
Logger.info { "Warnings : #{self.warning.join(' ').colorize :yellow }" } unless self.warning.empty?
Logger.info { "Best practices : #{self.success.join(' ').colorize :green }" } unless self.success.empty?
end
private
def calculate_grade
@grade = case @score
when 0...20 then 'F'
when 20...35 then 'E'
when 35...50 then 'D'
when 50...65 then 'C'
when 65...80 then 'B'
else 'A'
end
when 0...20 then
'F'
when 20...35 then
'E'
when 35...50 then
'D'
when 50...65 then
'C'
when 65...80 then
'B'
else
'A'
end
@grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048
@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 = [@grade, 'F'].max unless @error.empty?
@grade = 'M' unless @server.cert_valid
@grade = 'T' unless @server.cert_trusted
end
def warning
def calculate_error
@error = []
@error << :md5_sig if @server.md5_sig?
@error << :md5 if @server.md5?
@error << :anonymous if @server.anonymous?
@error << :dss if @server.dss?
@error << :null if @server.null?
@error << :export if @server.export?
@error << :des if @server.des?
@error << :rc4 if @server.rc4?
end
def calculate_warning
@warning = []
@warning << :md5_sig if @server.md5_sig?
@warning << :sha1_sig if @server.sha1_sig?
@warning << :md5 if @server.md5?
#@warning << :sha1 if @server.sha1?
@warning << :rc4 if @server.rc4?
@warning << :des if @server.des?
@warning << :des3 if @server.des3?
end
def success
def calculate_success
@success = []
@success << :pfs if @server.pfs_only?
end
ALL_WARNING = %i(md5_sig md5 rc4 des)
ALL_ERROR = %i(md5_sig md5 anonymous dss null export des rc4)
ALL_WARNING = %i(sha1_sig des3)
ALL_SUCCESS = %i(pfs)
def all_error
ALL_ERROR
end
def all_warning
ALL_WARNING
end
ALL_SUCCESS = %i(pfs)
def all_success
ALL_SUCCESS
end
def perfect
@grade = 'A+' if @grade == 'A' and (all_warning & @warning).empty? and (all_success & @success) == all_success
def calculate_perfect
@grade = 'A+' if @grade == 'A' and @error.empty? and @warning.empty? and (ALL_SUCCESS & @success) == ALL_SUCCESS
end
METHODS_SCORES = { SSLv2: 0, SSLv3: 80, TLSv1: 90, TLSv1_1: 95, TLSv1_2: 100 }
def protocol_score
methods = @server.supported_methods
worst, best = methods[:worst], methods[:best]
def calculate_protocol_score
methods = @server.supported_methods
worst, best = methods[:worst], methods[:best]
@protocol_score = (METHODS_SCORES[worst] + METHODS_SCORES[best]) / 2
end
def key_exchange_score
def calculate_key_exchange_score
@key_exchange_score = case @server.key_size
when 0 then 0
when 0...512 then 20
when 512...1024 then 40
when 1024...2048 then 80
when 2048...4096 then 90
else 100
end
when 0 then
0
when 0...512 then
20
when 512...1024 then
40
when 1024...2048 then
80
when 2048...4096 then
90
else
100
end
end
def cipher_strength_score(cipher_strength)
def calculate_cipher_strength_score(cipher_strength)
case cipher_strength
when 0 then 0
when 0...128 then 20
when 128...256 then 80
else 100
when 0 then
0
when 0...128 then
20
when 128...256 then
80
else
100
end
end
def cipher_strengths_score
strength = @server.cipher_size
worst, best = strength[:min], strength[:max]
@cipher_strengths_score = (cipher_strength_score(worst) + cipher_strength_score(best)) / 2
def calculate_cipher_strengths_score
strength = @server.cipher_size
worst, best = strength[:min], strength[:max]
@cipher_strengths_score = (calculate_cipher_strength_score(worst) + calculate_cipher_strength_score(best)) / 2
end
end
end

@ -3,7 +3,7 @@ module CryptCheck
module Https
class Grade < Tls::Grade
private
def success
def calculate_success
super
@success << :hsts if @server.hsts?
@success << :hsts_long if @server.hsts_long?

@ -14,28 +14,17 @@ module CryptCheck
def fetch_hsts
port = @port == 443 ? '' : ":#{@port}"
response = nil
EXISTING_METHODS.each do |method|
begin
next unless SUPPORTED_METHODS.include? method
@log.debug { "Check HSTS with #{method}" }
response = ::HTTParty.head "https://#{@hostname}#{port}/", { follow_redirects: false, verify: false, ssl_version: method, timeout: SSL_TIMEOUT }
break
rescue Exception => e
@log.debug { "#{method} not supported : #{e}" }
end
end
if response and header = response.headers['strict-transport-security']
response = ::HTTParty.head "https://#{@hostname}#{port}/", { follow_redirects: false, verify: false, timeout: SSL_TIMEOUT }
if header = response.headers['strict-transport-security']
name, value = header.split '='
if name == 'max-age'
@hsts = value.to_i
@log.info { "HSTS : #{@hsts}" }
Logger.info { "HSTS : #{@hsts.to_s.colorize hsts_long? ? :green : nil}" }
return
end
end
@log.info { 'No HSTS' }
Logger.info { 'No HSTS'.colorize :yellow }
@hsts = nil
end

@ -13,9 +13,9 @@ module CryptCheck
end
class Server
TCP_TIMEOUT = 10
SSL_TIMEOUT = 2*TCP_TIMEOUT
EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
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 TLSException < ::Exception
end
@ -33,20 +33,18 @@ module CryptCheck
attr_reader :hostname, :port, :prefered_ciphers, :cert, :cert_valid, :cert_trusted
def initialize(hostname, port)
@log = Logging.logger[hostname]
@hostname = hostname
@port = port
@log.error { "Begin analysis" }
@port = port
Logger.info { "#{hostname}:#{port}".colorize :blue }
extract_cert
#@prefered_ciphers = @supported_ciphers = Hash[SUPPORTED_METHODS.collect { |m| [m, []]}]
fetch_prefered_ciphers
check_supported_cipher
@log.error { "End analysis" }
end
def supported_methods
worst = EXISTING_METHODS.find { |method| !@prefered_ciphers[method].nil? }
best = EXISTING_METHODS.reverse.find { |method| !@prefered_ciphers[method].nil? }
best = EXISTING_METHODS.reverse.find { |method| !@prefered_ciphers[method].nil? }
{ worst: worst, best: best }
end
@ -66,24 +64,24 @@ module CryptCheck
type, size = self.key
if type == :ecc
size = case size
when 160 then
1024
when 224 then
2048
when 256 then
3072
when 384 then
7680
when 521 then
15360
end
when 160 then
1024
when 224 then
2048
when 256 then
3072
when 384 then
7680
when 521 then
15360
end
end
size
end
def cipher_size
cipher_strengths = supported_ciphers.collect { |c| c[2] }.uniq.sort
worst, best = cipher_strengths.first, cipher_strengths.last
worst, best = cipher_strengths.first, cipher_strengths.last
{ worst: worst, best: best }
end
@ -96,29 +94,22 @@ module CryptCheck
end
{
md2: %w(md2WithRSAEncryption),
md5: %w(md5WithRSAEncryption md5WithRSA),
md2: %w(md2WithRSAEncryption),
md5: %w(md5WithRSAEncryption md5WithRSA),
sha1: %w(sha1WithRSAEncryption sha1WithRSA dsaWithSHA1 dsaWithSHA1_2 ecdsa_with_SHA1)
}.each do |name, signature|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}_sig?
#{signature}.include? @cert.signature_algorithm
end
def #{name}_sig?
#{signature}.include? @cert.signature_algorithm
end
RUBY_EVAL
end
{
md5: %w(MD5),
sha1: %w(SHA),
rc4: %w(RC4),
des3: %w(3DES DES-CBC3),
des: %w(DES-CBC)
}.each do |name, ciphers|
Tls::TYPES.each do |type, _|
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
def #{name}?
supported_ciphers.any? { |supported| #{ciphers}.any? { |available| /(^|-)#\{available\}(-|$)/ =~ supported[0] } }
end
def #{type}?
supported_ciphers.any? { |s| Tls.#{type}? s.first }
end
RUBY_EVAL
end
@ -134,14 +125,12 @@ module CryptCheck
tls? and !ssl?
end
PFS_CIPHERS = [/^DHE-RSA-/, /^DHE-DSS-/, /^ECDHE-RSA-/, /^ECDHE-ECDSA-/]
def pfs?
supported_ciphers.any? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
supported_ciphers.any? { |c| Tls.pfs? c.first }
end
def pfs_only?
supported_ciphers.all? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
supported_ciphers.all? { |c| Tls.pfs? c.first }
end
def supported_ciphers
@ -154,21 +143,21 @@ module CryptCheck
private
def connect(family, host, port, &block)
socket = ::Socket.new family, sock_type
socket = ::Socket.new family, sock_type
sockaddr = ::Socket.sockaddr_in port, host
@log.debug { "Connecting to #{host}:#{port}" }
Logger.trace { "Connecting to #{host}:#{port}" }
begin
status = socket.connect_nonblock sockaddr
@log.debug { "Connecting to #{host}:#{port} status : #{status}" }
Logger.trace { "Connecting to #{host}:#{port} status : #{status}" }
raise ConnectionError, status unless status == 0
@log.debug { "Connected to #{host}:#{port}" }
Logger.trace { "Connected to #{host}:#{port}" }
block_given? ? block.call(socket) : nil
rescue ::IO::WaitReadable
@log.debug { "Waiting for read to #{host}:#{port}" }
Logger.trace { "Waiting for read to #{host}:#{port}" }
raise Timeout unless IO.select [socket], nil, nil, TCP_TIMEOUT
retry
rescue ::IO::WaitWritable
@log.debug { "Waiting for write to #{host}:#{port}" }
Logger.trace { "Waiting for write to #{host}:#{port}" }
raise Timeout unless IO.select nil, [socket], nil, TCP_TIMEOUT
retry
ensure
@ -177,39 +166,39 @@ module CryptCheck
end
def ssl_connect(socket, context, method, &block)
ssl_socket = ::OpenSSL::SSL::SSLSocket.new socket, context
ssl_socket = ::OpenSSL::SSL::SSLSocket.new socket, context
ssl_socket.hostname = @hostname unless method == :SSLv2
@log.debug { "SSL connecting to #{@hostname}:#{@port}" }
Logger.trace { "SSL connecting to #{@hostname}:#{@port}" }
begin
ssl_socket.connect_nonblock
@log.debug { "SSL connected to #{@hostname}:#{@port}" }
Logger.trace { "SSL connected to #{@hostname}:#{@port}" }
return block_given? ? block.call(ssl_socket) : nil
rescue ::IO::WaitReadable
@log.debug { "Waiting for SSL read to #{@hostname}:#{@port}" }
Logger.trace { "Waiting for SSL read to #{@hostname}:#{@port}" }
raise TLSTimeout unless IO.select [socket], nil, nil, SSL_TIMEOUT
retry
rescue ::IO::WaitWritable
@log.debug { "Waiting for SSL write to #{@hostname}:#{@port}" }
Logger.trace { "Waiting for SSL write to #{@hostname}:#{@port}" }
raise TLSTimeout unless IO.select nil, [socket], nil, SSL_TIMEOUT
retry
rescue => e
raise TLSException, e
rescue => e
raise TLSNotAvailableException, e
ensure
ssl_socket.close
end
end
def ssl_client(method, ciphers = nil, &block)
ssl_context = ::OpenSSL::SSL::SSLContext.new method
ssl_context = ::OpenSSL::SSL::SSLContext.new method
ssl_context.ciphers = ciphers if ciphers
@log.debug { "Try #{method} connection with #{ciphers}" }
Logger.trace { "Try #{method} connection with #{ciphers}" }
[::Socket::AF_INET, ::Socket::AF_INET6].each do |family|
@log.debug { "Try connection for family #{family}" }
Logger.trace { "Try connection for family #{family}" }
addrs = begin
::Socket.getaddrinfo @hostname, nil, family, :STREAM
rescue ::SocketError => e
@log.debug { "Unable to resolv #{@hostname} : #{e}" }
Logger.error { "Unable to resolv #{@hostname} : #{e}" }
next
end
@ -222,7 +211,7 @@ module CryptCheck
end
end
@log.debug { "No SSL available on #{@hostname}" }
Logger.debug { "No SSL available on #{@hostname}" }
raise CipherNotAvailable
end
@ -231,27 +220,28 @@ module CryptCheck
next unless SUPPORTED_METHODS.include? method
begin
@cert, @chain = ssl_client(method) { |s| [s.peer_cert, s.peer_cert_chain] }
@log.warn { "Certificate #{@cert.subject}" }
Logger.debug { "Certificate #{@cert.subject}" }
break
rescue TLSException => e
@log.info { "Method #{method} not supported : #{e}" }
Logger.trace { "Method #{Tls.colorize method} not supported : #{e}" }
end
end
raise TLSNotAvailableException unless @cert
@cert_valid = ::OpenSSL::SSL.verify_certificate_identity @cert, @hostname
@cert_valid = ::OpenSSL::SSL.verify_certificate_identity @cert, @hostname
@cert_trusted = verify_trust @chain, @cert
end
def prefered_cipher(method)
cipher = ssl_client(method, %w(ALL:COMPLEMENTOFALL)) { |s| s.cipher }
@log.warn { "Prefered cipher for #{method} : #{cipher[0]}" }
cipher = ssl_client(method, 'ALL:COMPLEMENTOFALL') { |s| s.cipher }
Logger.info { "Prefered cipher for #{Tls.colorize method} : #{Tls.colorize cipher.first}" }
cipher
rescue Exception => e
@log.info { "Method #{method} not supported : #{e}" }
rescue Exception
Logger.debug { "Method #{Tls.colorize method} not supported" }
nil
end
def fetch_prefered_ciphers
Logger.info { '' }
@prefered_ciphers = {}
EXISTING_METHODS.each do |method|
next unless SUPPORTED_METHODS.include? method
@ -261,28 +251,33 @@ module CryptCheck
end
def available_ciphers(method)
::OpenSSL::SSL::SSLContext.new(method).ciphers
context = ::OpenSSL::SSL::SSLContext.new method
context.ciphers = 'ALL:COMPLEMENTOFALL'
context.ciphers
end
def supported_cipher?(method, cipher)
ssl_client method, [cipher]
@log.warn { "Verify #{method} / #{cipher[0]} : OK" }
Logger.info { "#{Tls.colorize method} / #{Tls.colorize cipher[0]} : Supported" }
true
rescue TLSException => e
@log.info { "Verify #{method} / #{cipher[0]} : NOK (#{e})" }
Logger.debug { "#{Tls.colorize method} / #{Tls.colorize cipher[0]} : Not supported" }
false
end
def check_supported_cipher
Logger.info { '' }
@supported_ciphers = {}
EXISTING_METHODS.each do |method|
next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method]
@supported_ciphers[method] = available_ciphers(method).select { |cipher| supported_cipher? method, cipher }
ciphers = available_ciphers(method).select { |cipher| supported_cipher? method, cipher }
@supported_ciphers[method] = ciphers
Logger.info { '' } unless ciphers.empty?
end
end
def verify_trust(chain, cert)
store = ::OpenSSL::X509::Store.new
store = ::OpenSSL::X509::Store.new
store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
store.set_default_paths

@ -7,14 +7,14 @@ module CryptCheck
module Xmpp
MAX_ANALYSIS_DURATION = 600
PARALLEL_ANALYSIS = 10
@@log = ::Logging.logger[Xmpp]
@Logger = ::Logging.logger[Xmpp]
def self.grade(hostname, type=:s2s)
timeout MAX_ANALYSIS_DURATION do
Grade.new Server.new hostname, type
end
rescue ::Exception => e
@@log.error { "Error during #{hostname}:#{type} analysis : #{e}" }
@Logger.error { "Error during #{hostname}:#{type} analysis : #{e}" }
TlsNotSupportedGrade.new TlsNotSupportedServer.new hostname, type
end

@ -2,7 +2,7 @@ module CryptCheck
module Tls
module Xmpp
class Grade < Tls::Grade
def success
def calculate_success
super
@success << :required if @server.required?
end

@ -25,6 +25,8 @@ module CryptCheck
end
end
super hostname, port
Logger.info { '' }
Logger.info { self.required? ? 'Required'.colorize(:green) : 'Not required'.colorize(:yellow) }
end
def ssl_connect(socket, context, method, &block)

Loading…
Cancel
Save