Przeglądaj źródła

Refactor logging

master
Nicolas Vinot 4 lat temu
rodzic
commit
4fa9497a0d

+ 4
- 3
Gemfile Wyświetl plik

@@ -1,5 +1,6 @@
1 1
 source 'https://rubygems.org'
2 2
 
3
+gem 'rake'
3 4
 gem 'httparty'
4 5
 gem 'nokogiri'
5 6
 gem 'net-ssh', '>= 2.9.2.beta'
@@ -8,9 +9,9 @@ gem 'tcp_timeout'
8 9
 gem 'parallel'
9 10
 gem 'ruby-progressbar'
10 11
 gem 'logging'
11
-gem 'activerecord'
12
-gem 'sqlite3'
13
-gem 'rake'
12
+#gem 'activerecord'
13
+#gem 'sqlite3'
14
+gem 'colorize'
14 15
 
15 16
 group :test do
16 17
 	gem 'rspec'

+ 4
- 2
Makefile Wyświetl plik

@@ -34,13 +34,15 @@ $(OPENSSL_DIR)/:
34 34
 $(OPENSSL_DIR)/Makefile: | $(OPENSSL_DIR)/
35 35
 	cd $(OPENSSL_DIR); ./config shared
36 36
 
37
-$(OPENSSL_DIR)/libssl.so.1.0.0 $(OPENSSL_DIR)/libcrypto.so.1.0.0: $(OPENSSL_DIR)/Makefile
37
+$(OPENSSL_DIR)/libssl.so $(OPENSSL_DIR)/libcrypto.so: $(OPENSSL_DIR)/Makefile
38 38
 	$(MAKE) -C $(OPENSSL_DIR) depend build_libs
39 39
 
40
+lib/%.so: $(OPENSSL_DIR)/%.so
41
+	cp $< $@
40 42
 lib/%.so.1.0.0: $(OPENSSL_DIR)/%.so.1.0.0
41 43
 	cp $< $@
42 44
 
43
-libs: lib/libssl.so.1.0.0 lib/libcrypto.so.1.0.0
45
+libs: lib/libssl.so lib/libcrypto.so lib/libssl.so.1.0.0 lib/libcrypto.so.1.0.0
44 46
 
45 47
 $(RUBY_DIR)/:
46 48
 	wget http://cache.ruby-lang.org/pub/ruby/$(RUBY_MAJOR_VERSION)/$(RUBY_DIR).tar.gz

+ 5
- 5
bin/check_https.rb Wyświetl plik

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

+ 1
- 2
bin/check_https_alexa.rb Wyświetl plik

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

+ 8
- 7
bin/check_smtp.rb Wyświetl plik

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

+ 5
- 4
bin/check_xmpp.rb Wyświetl plik

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

+ 3
- 0
lib/cryptcheck.rb Wyświetl plik

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

+ 28
- 0
lib/cryptcheck/logger.rb Wyświetl plik

@@ -0,0 +1,28 @@
1
+module CryptCheck
2
+	class Logger
3
+		LEVELS = %i(trace debug info warning error fatal none)
4
+		@@level = :info
5
+
6
+		def self.level=(level)
7
+			@@level = level
8
+		end
9
+
10
+		def self.log(level, string=nil, output: $stdout, &block)
11
+			return unless enabled? level
12
+			output.puts(string ? string : block.call)
13
+		end
14
+
15
+		LEVELS.each do |level|
16
+			class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
17
+				def self.#{level}(string=nil, output: $stdout, &block)
18
+					self.log :#{level}, string, output: output, &block
19
+				end
20
+			RUBY_EVAL
21
+		end
22
+
23
+		private
24
+		def self.enabled?(level)
25
+			LEVELS.index(level) >= LEVELS.index(@@level)
26
+		end
27
+	end
28
+end

+ 51
- 5
lib/cryptcheck/tls.rb Wyświetl plik

@@ -5,20 +5,46 @@ require 'parallel'
5 5
 module CryptCheck
6 6
 	module Tls
7 7
 		MAX_ANALYSIS_DURATION = 600
8
-		PARALLEL_ANALYSIS = 10
9
-		@@log = ::Logging.logger[Tls]
8
+		PARALLEL_ANALYSIS     = 10
9
+
10
+		TYPES = {
11
+				md5:       %w(MD5),
12
+				sha1:      %w(SHA),
13
+
14
+				psk:       %w(PSK),
15
+				srp:       %w(SRP),
16
+				anonymous: %w(ADH AECDH),
17
+
18
+				dss:       %w(DSS),
19
+
20
+				null:      %w(NULL),
21
+				export:    %w(EXP),
22
+				des:       %w(DES-CBC),
23
+				rc4:       %w(RC4),
24
+				des3:      %w(3DES DES-CBC3),
25
+
26
+				pfs:       %w(DHE EDH ECDHE ECDH)
27
+		}
28
+
29
+		TYPES.each do |name, ciphers|
30
+			class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
31
+				def self.#{name}?(cipher)
32
+					#{ciphers}.any? { |c| /(^|-)#\{c\}(-|$)/ =~ cipher }
33
+				end
34
+			RUBY_EVAL
35
+		end
10 36
 
11 37
 		def self.grade(hostname, port, server_class:, grade_class:)
12 38
 			timeout MAX_ANALYSIS_DURATION do
13 39
 				grade_class.new server_class.new hostname, port
14 40
 			end
15 41
 		rescue ::Exception => e
16
-			@@log.error { "Error during #{hostname}:#{port} analysis : #{e}" }
42
+			@Logger.error { "Error during #{hostname}:#{port} analysis : #{e}" }
17 43
 			TlsNotSupportedGrade.new TlsNotSupportedServer.new hostname, port
18 44
 		end
19 45
 
20 46
 		def self.analyze(hosts, template, output, groups = nil, port:, server_class:, grade_class:)
21
-			results = {}
47
+			results   = {}
22 48
 			semaphore = ::Mutex.new
23 49
 			::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item[1] } do |description, host|
24 50
 									 result = grade host.strip, port, server_class: server_class, grade_class: grade_class
@@ -51,7 +77,7 @@ module CryptCheck
51 77
 
52 78
 		def self.analyze_from_file(file, template, output, port:, server_class:, grade_class:)
53 79
 			config = ::YAML.load_file file
54
-			hosts = []
80
+			hosts  = []
55 81
 			groups = []
56 82
 			config.each do |c|
57 83
 				d, hs = c['description'], c['hostnames']
@@ -61,6 +87,26 @@ module CryptCheck
61 87
 			self.analyze hosts, template, output, groups, port: port, server_class: server_class, grade_class: grade_class
62 88
 		end
63 89
 
90
+		def self.colorize(cipher)
91
+			colors = case
92
+						 when /^SSL/ =~ cipher,
93
+								 dss?(cipher),
94
+								 anonymous?(cipher),
95
+								 null?(cipher),
96
+								 export?(cipher),
97
+								 md5?(cipher),
98
+								 des?(cipher),
99
+								 rc4?(cipher)
100
+							 { color: :white, background: :red }
101
+						 when des3?(cipher)
102
+							 { color: :yellow }
103
+						 when :TLSv1_2 == cipher,
104
+								 pfs?(cipher)
105
+							 { color: :green }
106
+					 end
107
+			cipher.to_s.colorize colors
108
+		end
109
+
64 110
 		private
65 111
 		SCORES = %w(A+ A A- B C D E F T M X)
66 112
 

+ 111
- 47
lib/cryptcheck/tls/grade.rb Wyświetl plik

@@ -2,109 +2,173 @@ module CryptCheck
2 2
 	module Tls
3 3
 		class TlsNotSupportedGrade
4 4
 			attr_reader :server, :score, :grade
5
+
5 6
 			def initialize(server)
6 7
 				@server, @score, @grade = server, -1, 'X'
7 8
 			end
8 9
 		end
9 10
 
10 11
 		class Grade
11
-			attr_reader :server, :score, :grade, :warning, :success
12
+			attr_reader :server, :protocol_score, :key_exchange_score, :cipher_strengths_score, :score, :grade, :error, :warning, :success
12 13
 
13 14
 			def initialize(server)
14 15
 				@server = server
15
-				protocol_score
16
-				key_exchange_score
17
-				cipher_strengths_score
18
-				@score = @protocol_score*0.3 +  @key_exchange_score*0.3 + @cipher_strengths_score*0.4
16
+				calculate_protocol_score
17
+				calculate_key_exchange_score
18
+				calculate_cipher_strengths_score
19
+				@score = @protocol_score*0.3 + @key_exchange_score*0.3 + @cipher_strengths_score*0.4
20
+				calculate_error
21
+				calculate_warning
22
+				calculate_success
19 23
 				calculate_grade
20
-				warning
21
-				success
22
-				perfect
24
+				calculate_perfect
25
+			end
26
+
27
+			def display
28
+				color = case self.grade
29
+							when 'A+'
30
+								:blue
31
+							when 'A'
32
+								:green
33
+							when 'B', 'C'
34
+								:yellow
35
+							when 'E', 'F'
36
+								:red
37
+							when 'M', 'T'
38
+								{ color: :white, background: :red }
39
+						end
40
+
41
+				Logger.info { "Grade : #{self.grade.colorize color }" }
42
+				Logger.info { '' }
43
+				Logger.info { "Protocole : #{self.protocol_score} / 100" }
44
+				Logger.info { "Key exchange : #{self.key_exchange_score} / 100" }
45
+				Logger.info { "Ciphers strength : #{self.cipher_strengths_score} / 100" }
46
+				Logger.info { "Overall score : #{self.score} / 100" }
47
+				Logger.info { '' }
48
+				Logger.info { "Errors : #{self.error.join(' ').colorize :red }" } unless self.error.empty?
49
+				Logger.info { "Warnings : #{self.warning.join(' ').colorize :yellow }" } unless self.warning.empty?
50
+				Logger.info { "Best practices : #{self.success.join(' ').colorize :green }" } unless self.success.empty?
23 51
 			end
24 52
 
25 53
 			private
26 54
 			def calculate_grade
27 55
 				@grade = case @score
28
-					         when 0...20 then 'F'
29
-					         when 20...35 then 'E'
30
-					         when 35...50 then 'D'
31
-					         when 50...65 then 'C'
32
-					         when 65...80 then 'B'
33
-					         else 'A'
34
-				         end
56
+							 when 0...20 then
57
+								 'F'
58
+							 when 20...35 then
59
+								 'E'
60
+							 when 35...50 then
61
+								 'D'
62
+							 when 50...65 then
63
+								 'C'
64
+							 when 65...80 then
65
+								 'B'
66
+							 else
67
+								 'A'
68
+						 end
35 69
 
36 70
 				@grade = [@grade, 'B'].max if !@server.tlsv1_2? or @server.key_size < 2048
37 71
 				@grade = [@grade, 'C'].max if @server.des3?
38
-				@grade = [@grade, 'E'].max if @server.rc4? or @server.des?
39
-				@grade = [@grade, 'F'].max if @server.ssl? or @server.key_size < 1024
72
+				@grade = [@grade, 'F'].max unless @error.empty?
40 73
 
41 74
 				@grade = 'M' unless @server.cert_valid
42 75
 				@grade = 'T' unless @server.cert_trusted
43 76
 			end
44 77
 
45
-			def warning
78
+			def calculate_error
79
+				@error = []
80
+
81
+				@error << :md5_sig if @server.md5_sig?
82
+
83
+				@error << :md5 if @server.md5?
84
+
85
+				@error << :anonymous if @server.anonymous?
86
+
87
+				@error << :dss if @server.dss?
88
+
89
+				@error << :null if @server.null?
90
+				@error << :export if @server.export?
91
+				@error << :des if @server.des?
92
+				@error << :rc4 if @server.rc4?
93
+			end
94
+
95
+			def calculate_warning
46 96
 				@warning = []
47 97
 
48
-				@warning << :md5_sig if @server.md5_sig?
49 98
 				@warning << :sha1_sig if @server.sha1_sig?
50 99
 
51
-				@warning << :md5 if @server.md5?
52 100
 				#@warning << :sha1 if @server.sha1?
53 101
 
54
-				@warning << :rc4 if @server.rc4?
55
-				@warning << :des if @server.des?
56 102
 				@warning << :des3 if @server.des3?
57 103
 			end
58 104
 
59
-			def success
105
+			def calculate_success
60 106
 				@success = []
61 107
 				@success << :pfs if @server.pfs_only?
62 108
 			end
63 109
 
64
-			ALL_WARNING = %i(md5_sig md5 rc4 des)
110
+			ALL_ERROR   = %i(md5_sig md5 anonymous dss null export des rc4)
111
+			ALL_WARNING = %i(sha1_sig des3)
112
+			ALL_SUCCESS = %i(pfs)
113
+
114
+			def all_error
115
+				ALL_ERROR
116
+			end
117
+
65 118
 			def all_warning
66 119
 				ALL_WARNING
67 120
 			end
68
-			ALL_SUCCESS = %i(pfs)
121
+
69 122
 			def all_success
70 123
 				ALL_SUCCESS
71 124
 			end
72 125
 
73
-			def perfect
74
-				@grade = 'A+' if @grade == 'A' and (all_warning & @warning).empty? and (all_success & @success) == all_success
126
+			def calculate_perfect
127
+				@grade = 'A+' if @grade == 'A' and @error.empty? and @warning.empty? and (ALL_SUCCESS & @success) == ALL_SUCCESS
75 128
 			end
76 129
 
77 130
 			METHODS_SCORES = { SSLv2: 0, SSLv3: 80, TLSv1: 90, TLSv1_1: 95, TLSv1_2: 100 }
78
-			def protocol_score
79
-				methods = @server.supported_methods
80
-				worst, best = methods[:worst], methods[:best]
131
+
132
+			def calculate_protocol_score
133
+				methods         = @server.supported_methods
134
+				worst, best     = methods[:worst], methods[:best]
81 135
 				@protocol_score = (METHODS_SCORES[worst] + METHODS_SCORES[best]) / 2
82 136
 			end
83 137
 
84
-			def key_exchange_score
138
+			def calculate_key_exchange_score
85 139
 				@key_exchange_score = case @server.key_size
86
-					                      when 0 then 0
87
-					                      when 0...512 then 20
88
-					                      when 512...1024 then 40
89
-					                      when 1024...2048 then 80
90
-					                      when 2048...4096 then 90
91
-					                      else 100
92
-				                      end
140
+										  when 0 then
141
+											  0
142
+										  when 0...512 then
143
+											  20
144
+										  when 512...1024 then
145
+											  40
146
+										  when 1024...2048 then
147
+											  80
148
+										  when 2048...4096 then
149
+											  90
150
+										  else
151
+											  100
152
+									  end
93 153
 			end
94 154
 
95
-			def cipher_strength_score(cipher_strength)
155
+			def calculate_cipher_strength_score(cipher_strength)
96 156
 				case cipher_strength
97
-					when 0 then 0
98
-					when 0...128 then 20
99
-					when 128...256 then 80
100
-					else 100
157
+					when 0 then
158
+						0
159
+					when 0...128 then
160
+						20
161
+					when 128...256 then
162
+						80
163
+					else
164
+						100
101 165
 				end
102 166
 			end
103 167
 
104
-			def cipher_strengths_score
105
-				strength = @server.cipher_size
106
-				worst, best = strength[:min], strength[:max]
107
-				@cipher_strengths_score = (cipher_strength_score(worst) + cipher_strength_score(best)) / 2
168
+			def calculate_cipher_strengths_score
169
+				strength                = @server.cipher_size
170
+				worst, best             = strength[:min], strength[:max]
171
+				@cipher_strengths_score = (calculate_cipher_strength_score(worst) + calculate_cipher_strength_score(best)) / 2
108 172
 			end
109 173
 		end
110 174
 	end

+ 1
- 1
lib/cryptcheck/tls/https/grade.rb Wyświetl plik

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

+ 4
- 15
lib/cryptcheck/tls/https/server.rb Wyświetl plik

@@ -14,28 +14,17 @@ module CryptCheck
14 14
 				def fetch_hsts
15 15
 					port = @port == 443 ? '' : ":#{@port}"
16 16
 
17
-					response = nil
18
-					EXISTING_METHODS.each do |method|
19
-						begin
20
-							next unless SUPPORTED_METHODS.include? method
21
-							@log.debug { "Check HSTS with #{method}" }
22
-							response = ::HTTParty.head "https://#{@hostname}#{port}/", { follow_redirects: false, verify: false, ssl_version: method, timeout: SSL_TIMEOUT }
23
-							break
24
-						rescue Exception => e
25
-							@log.debug { "#{method} not supported : #{e}" }
26
-						end
27
-					end
28
-
29
-					if response and header = response.headers['strict-transport-security']
17
+					response = ::HTTParty.head "https://#{@hostname}#{port}/", { follow_redirects: false, verify: false, timeout: SSL_TIMEOUT }
18
+					if header = response.headers['strict-transport-security']
30 19
 						name, value = header.split '='
31 20
 						if name == 'max-age'
32 21
 							@hsts = value.to_i
33
-							@log.info { "HSTS : #{@hsts}" }
22
+							Logger.info { "HSTS : #{@hsts.to_s.colorize hsts_long? ? :green : nil}" }
34 23
 							return
35 24
 						end
36 25
 					end
37 26
 
38
-					@log.info { 'No HSTS' }
27
+					Logger.info { 'No HSTS'.colorize :yellow }
39 28
 					@hsts = nil
40 29
 				end
41 30
 

+ 65
- 70
lib/cryptcheck/tls/server.rb Wyświetl plik

@@ -13,9 +13,9 @@ module CryptCheck
13 13
 		end
14 14
 
15 15
 		class Server
16
-			TCP_TIMEOUT = 10
17
-			SSL_TIMEOUT = 2*TCP_TIMEOUT
18
-			EXISTING_METHODS = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
16
+			TCP_TIMEOUT       = 10
17
+			SSL_TIMEOUT       = 2*TCP_TIMEOUT
18
+			EXISTING_METHODS  = %i(TLSv1_2 TLSv1_1 TLSv1 SSLv3 SSLv2)
19 19
 			SUPPORTED_METHODS = ::OpenSSL::SSL::SSLContext::METHODS
20 20
 			class TLSException < ::Exception
21 21
 			end
@@ -33,20 +33,18 @@ module CryptCheck
33 33
 			attr_reader :hostname, :port, :prefered_ciphers, :cert, :cert_valid, :cert_trusted
34 34
 
35 35
 			def initialize(hostname, port)
36
-				@log = Logging.logger[hostname]
37 36
 				@hostname = hostname
38
-				@port = port
39
-				@log.error { "Begin analysis" }
37
+				@port     = port
38
+				Logger.info { "#{hostname}:#{port}".colorize :blue }
40 39
 				extract_cert
41 40
 				#@prefered_ciphers = @supported_ciphers = Hash[SUPPORTED_METHODS.collect { |m| [m, []]}]
42 41
 				fetch_prefered_ciphers
43 42
 				check_supported_cipher
44
-				@log.error { "End analysis" }
45 43
 			end
46 44
 
47 45
 			def supported_methods
48 46
 				worst = EXISTING_METHODS.find { |method| !@prefered_ciphers[method].nil? }
49
-				best = EXISTING_METHODS.reverse.find { |method| !@prefered_ciphers[method].nil? }
47
+				best  = EXISTING_METHODS.reverse.find { |method| !@prefered_ciphers[method].nil? }
50 48
 				{ worst: worst, best: best }
51 49
 			end
52 50
 
@@ -66,24 +64,24 @@ module CryptCheck
66 64
 				type, size = self.key
67 65
 				if type == :ecc
68 66
 					size = case size
69
-						       when 160 then
70
-							       1024
71
-						       when 224 then
72
-							       2048
73
-						       when 256 then
74
-							       3072
75
-						       when 384 then
76
-							       7680
77
-						       when 521 then
78
-							       15360
79
-					       end
67
+							   when 160 then
68
+								   1024
69
+							   when 224 then
70
+								   2048
71
+							   when 256 then
72
+								   3072
73
+							   when 384 then
74
+								   7680
75
+							   when 521 then
76
+								   15360
77
+						   end
80 78
 				end
81 79
 				size
82 80
 			end
83 81
 
84 82
 			def cipher_size
85 83
 				cipher_strengths = supported_ciphers.collect { |c| c[2] }.uniq.sort
86
-				worst, best = cipher_strengths.first, cipher_strengths.last
84
+				worst, best      = cipher_strengths.first, cipher_strengths.last
87 85
 				{ worst: worst, best: best }
88 86
 			end
89 87
 
@@ -96,29 +94,22 @@ module CryptCheck
96 94
 			end
97 95
 
98 96
 			{
99
-					md2: %w(md2WithRSAEncryption),
100
-					md5: %w(md5WithRSAEncryption md5WithRSA),
97
+					md2:  %w(md2WithRSAEncryption),
98
+					md5:  %w(md5WithRSAEncryption md5WithRSA),
101 99
 					sha1: %w(sha1WithRSAEncryption sha1WithRSA dsaWithSHA1 dsaWithSHA1_2 ecdsa_with_SHA1)
102 100
 			}.each do |name, signature|
103 101
 				class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
104
-				def #{name}_sig?
105
-					#{signature}.include? @cert.signature_algorithm
106
-				end
102
+					def #{name}_sig?
103
+						#{signature}.include? @cert.signature_algorithm
104
+					end
107 105
 				RUBY_EVAL
108 106
 			end
109 107
 
110
-			{
111
-					md5: %w(MD5),
112
-					sha1: %w(SHA),
113
-
114
-					rc4: %w(RC4),
115
-					des3: %w(3DES DES-CBC3),
116
-					des: %w(DES-CBC)
117
-			}.each do |name, ciphers|
108
+			Tls::TYPES.each do |type, _|
118 109
 				class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
119
-				def #{name}?
120
-					supported_ciphers.any? { |supported| #{ciphers}.any? { |available| /(^|-)#\{available\}(-|$)/ =~ supported[0] } }
121
-				end
110
+					def #{type}?
111
+						supported_ciphers.any? { |s| Tls.#{type}? s.first  }
112
+					end
122 113
 				RUBY_EVAL
123 114
 			end
124 115
 
@@ -134,14 +125,12 @@ module CryptCheck
134 125
 				tls? and !ssl?
135 126
 			end
136 127
 
137
-			PFS_CIPHERS = [/^DHE-RSA-/, /^DHE-DSS-/, /^ECDHE-RSA-/, /^ECDHE-ECDSA-/]
138
-
139 128
 			def pfs?
140
-				supported_ciphers.any? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
129
+				supported_ciphers.any? { |c| Tls.pfs? c.first }
141 130
 			end
142 131
 
143 132
 			def pfs_only?
144
-				supported_ciphers.all? { |cipher| PFS_CIPHERS.any? { |pc| pc =~ cipher[0] } }
133
+				supported_ciphers.all? { |c| Tls.pfs? c.first }
145 134
 			end
146 135
 
147 136
 			def supported_ciphers
@@ -154,21 +143,21 @@ module CryptCheck
154 143
 
155 144
 			private
156 145
 			def connect(family, host, port, &block)
157
-				socket = ::Socket.new family, sock_type
146
+				socket   = ::Socket.new family, sock_type
158 147
 				sockaddr = ::Socket.sockaddr_in port, host
159
-				@log.debug { "Connecting to #{host}:#{port}" }
148
+				Logger.trace { "Connecting to #{host}:#{port}" }
160 149
 				begin
161 150
 					status = socket.connect_nonblock sockaddr
162
-					@log.debug { "Connecting to #{host}:#{port} status : #{status}" }
151
+					Logger.trace { "Connecting to #{host}:#{port} status : #{status}" }
163 152
 					raise ConnectionError, status unless status == 0
164
-					@log.debug { "Connected to #{host}:#{port}" }
153
+					Logger.trace { "Connected to #{host}:#{port}" }
165 154
 					block_given? ? block.call(socket) : nil
166 155
 				rescue ::IO::WaitReadable
167
-					@log.debug { "Waiting for read to #{host}:#{port}" }
156
+					Logger.trace { "Waiting for read to #{host}:#{port}" }
168 157
 					raise Timeout unless IO.select [socket], nil, nil, TCP_TIMEOUT
169 158
 					retry
170 159
 				rescue ::IO::WaitWritable
171
-					@log.debug { "Waiting for write to #{host}:#{port}" }
160
+					Logger.trace { "Waiting for write to #{host}:#{port}" }
172 161
 					raise Timeout unless IO.select nil, [socket], nil, TCP_TIMEOUT
173 162
 					retry
174 163
 				ensure
@@ -177,39 +166,39 @@ module CryptCheck
177 166
 			end
178 167
 
179 168
 			def ssl_connect(socket, context, method, &block)
180
-				ssl_socket = ::OpenSSL::SSL::SSLSocket.new socket, context
169
+				ssl_socket          = ::OpenSSL::SSL::SSLSocket.new socket, context
181 170
 				ssl_socket.hostname = @hostname unless method == :SSLv2
182
-				@log.debug { "SSL connecting to #{@hostname}:#{@port}" }
171
+				Logger.trace { "SSL connecting to #{@hostname}:#{@port}" }
183 172
 				begin
184 173
 					ssl_socket.connect_nonblock
185
-					@log.debug { "SSL connected to #{@hostname}:#{@port}" }
174
+					Logger.trace { "SSL connected to #{@hostname}:#{@port}" }
186 175
 					return block_given? ? block.call(ssl_socket) : nil
187 176
 				rescue ::IO::WaitReadable
188
-					@log.debug { "Waiting for SSL read to #{@hostname}:#{@port}" }
177
+					Logger.trace { "Waiting for SSL read to #{@hostname}:#{@port}" }
189 178
 					raise TLSTimeout unless IO.select [socket], nil, nil, SSL_TIMEOUT
190 179
 					retry
191 180
 				rescue ::IO::WaitWritable
192
-					@log.debug { "Waiting for SSL write to #{@hostname}:#{@port}" }
181
+					Logger.trace { "Waiting for SSL write to #{@hostname}:#{@port}" }
193 182
 					raise TLSTimeout unless IO.select nil, [socket], nil, SSL_TIMEOUT
194 183
 					retry
195
-                rescue => e
196
-                    raise TLSException, e
184
+				rescue => e
185
+					raise TLSNotAvailableException, e
197 186
 				ensure
198 187
 					ssl_socket.close
199 188
 				end
200 189
 			end
201 190
 
202 191
 			def ssl_client(method, ciphers = nil, &block)
203
-				ssl_context = ::OpenSSL::SSL::SSLContext.new method
192
+				ssl_context         = ::OpenSSL::SSL::SSLContext.new method
204 193
 				ssl_context.ciphers = ciphers if ciphers
205
-				@log.debug { "Try #{method} connection with #{ciphers}" }
194
+				Logger.trace { "Try #{method} connection with #{ciphers}" }
206 195
 
207 196
 				[::Socket::AF_INET, ::Socket::AF_INET6].each do |family|
208
-					@log.debug { "Try connection for family #{family}" }
197
+					Logger.trace { "Try connection for family #{family}" }
209 198
 					addrs = begin
210 199
 						::Socket.getaddrinfo @hostname, nil, family, :STREAM
211 200
 					rescue ::SocketError => e
212
-						@log.debug { "Unable to resolv #{@hostname} : #{e}" }
201
+						Logger.error { "Unable to resolv #{@hostname} : #{e}" }
213 202
 						next
214 203
 					end
215 204
 
@@ -222,7 +211,7 @@ module CryptCheck
222 211
 					end
223 212
 				end
224 213
 
225
-				@log.debug { "No SSL available on #{@hostname}" }
214
+				Logger.debug { "No SSL available on #{@hostname}" }
226 215
 				raise CipherNotAvailable
227 216
 			end
228 217
 
@@ -231,27 +220,28 @@ module CryptCheck
231 220
 					next unless SUPPORTED_METHODS.include? method
232 221
 					begin
233 222
 						@cert, @chain = ssl_client(method) { |s| [s.peer_cert, s.peer_cert_chain] }
234
-						@log.warn { "Certificate #{@cert.subject}" }
223
+						Logger.debug { "Certificate #{@cert.subject}" }
235 224
 						break
236 225
 					rescue TLSException => e
237
-						@log.info { "Method #{method} not supported : #{e}" }
226
+						Logger.trace { "Method #{Tls.colorize method} not supported : #{e}" }
238 227
 					end
239 228
 				end
240 229
 				raise TLSNotAvailableException unless @cert
241
-				@cert_valid = ::OpenSSL::SSL.verify_certificate_identity @cert, @hostname
230
+				@cert_valid   = ::OpenSSL::SSL.verify_certificate_identity @cert, @hostname
242 231
 				@cert_trusted = verify_trust @chain, @cert
243 232
 			end
244 233
 
245 234
 			def prefered_cipher(method)
246
-				cipher = ssl_client(method, %w(ALL:COMPLEMENTOFALL)) { |s| s.cipher }
247
-				@log.warn { "Prefered cipher for #{method} : #{cipher[0]}" }
235
+				cipher = ssl_client(method, 'ALL:COMPLEMENTOFALL') { |s| s.cipher }
236
+				Logger.info { "Prefered cipher for #{Tls.colorize method} : #{Tls.colorize cipher.first}" }
248 237
 				cipher
249
-			rescue Exception => e
250
-				@log.info { "Method #{method} not supported : #{e}" }
238
+			rescue Exception
239
+				Logger.debug { "Method #{Tls.colorize method} not supported" }
251 240
 				nil
252 241
 			end
253 242
 
254 243
 			def fetch_prefered_ciphers
244
+				Logger.info { '' }
255 245
 				@prefered_ciphers = {}
256 246
 				EXISTING_METHODS.each do |method|
257 247
 					next unless SUPPORTED_METHODS.include? method
@@ -261,28 +251,33 @@ module CryptCheck
261 251
 			end
262 252
 
263 253
 			def available_ciphers(method)
264
-				::OpenSSL::SSL::SSLContext.new(method).ciphers
254
+				context         = ::OpenSSL::SSL::SSLContext.new method
255
+				context.ciphers = 'ALL:COMPLEMENTOFALL'
256
+				context.ciphers
265 257
 			end
266 258
 
267 259
 			def supported_cipher?(method, cipher)
268 260
 				ssl_client method, [cipher]
269
-				@log.warn { "Verify #{method} / #{cipher[0]} : OK" }
261
+				Logger.info { "#{Tls.colorize method} / #{Tls.colorize cipher[0]} : Supported" }
270 262
 				true
271 263
 			rescue TLSException => e
272
-				@log.info { "Verify #{method} / #{cipher[0]} : NOK (#{e})" }
264
+				Logger.debug { "#{Tls.colorize method} / #{Tls.colorize cipher[0]} : Not supported" }
273 265
 				false
274 266
 			end
275 267
 
276 268
 			def check_supported_cipher
269
+				Logger.info { '' }
277 270
 				@supported_ciphers = {}
278 271
 				EXISTING_METHODS.each do |method|
279 272
 					next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method]
280
-					@supported_ciphers[method] = available_ciphers(method).select { |cipher| supported_cipher? method, cipher }
273
+					ciphers = available_ciphers(method).select { |cipher| supported_cipher? method, cipher }
274
+					@supported_ciphers[method] = ciphers
275
+					Logger.info { '' } unless ciphers.empty?
281 276
 				end
282 277
 			end
283 278
 
284 279
 			def verify_trust(chain, cert)
285
-				store = ::OpenSSL::X509::Store.new
280
+				store         = ::OpenSSL::X509::Store.new
286 281
 				store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT
287 282
 				store.set_default_paths
288 283
 

+ 2
- 2
lib/cryptcheck/tls/xmpp.rb Wyświetl plik

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

+ 1
- 1
lib/cryptcheck/tls/xmpp/grade.rb Wyświetl plik

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

+ 2
- 0
lib/cryptcheck/tls/xmpp/server.rb Wyświetl plik

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

Ładowanie…
Anuluj
Zapisz