Bladeren bron

XMPP TLS check

master
Nicolas Vinot 4 jaren geleden
bovenliggende
commit
6a5f1747f2

+ 16
- 0
bin/check_xmpp Bestand weergeven

@@ -0,0 +1,16 @@
1
+#!/usr/bin/env ruby
2
+$:.unshift 'lib'
3
+require 'logging'
4
+require 'cryptcheck'
5
+
6
+name = ARGV[0]
7
+if name
8
+	::Logging.logger.root.appenders = ::Logging.appenders.stdout
9
+	::Logging.logger.root.level = :warn
10
+
11
+	server = ::CryptCheck::Tls::Xmpp::Server.new(name, ARGV[1] || :s2s)
12
+	p grade = ::CryptCheck::Tls::Xmpp::Grade.new(server)
13
+else
14
+	::CryptCheck::Tls::Xmpp.analyze_from_file 'output/xmpp.yml', 'output/xmpp.html'
15
+end
16
+

+ 4
- 3
lib/cryptcheck.rb Bestand weergeven

@@ -3,10 +3,11 @@ module CryptCheck
3 3
 		autoload :Server, 'cryptcheck/tls/server'
4 4
 		autoload :Grade, 'cryptcheck/tls/grade'
5 5
 		autoload :Https, 'cryptcheck/tls/https'
6
+		autoload :Xmpp, 'cryptcheck/tls/xmpp'
6 7
 
7
-		module Https
8
-			autoload :Server, 'cryptcheck/tls/https/server'
9
-			autoload :Grade, 'cryptcheck/tls/https/grade'
8
+		module Xmpp
9
+			autoload :Server, 'cryptcheck/tls/xmpp/server'
10
+			autoload :Grade, 'cryptcheck/tls/xmpp/grade'
10 11
 		end
11 12
 	end
12 13
 end

+ 1
- 1
lib/cryptcheck/tls/grade.rb Bestand weergeven

@@ -65,7 +65,7 @@ module CryptCheck
65 65
 			def all_warning
66 66
 				ALL_WARNING
67 67
 			end
68
-			ALL_SUCCESS = %i(pfs hsts hsts_long)
68
+			ALL_SUCCESS = %i(pfs)
69 69
 			def all_success
70 70
 				ALL_SUCCESS
71 71
 			end

+ 11
- 11
lib/cryptcheck/tls/https.rb Bestand weergeven

@@ -21,17 +21,16 @@ module CryptCheck
21 21
 			def self.analyze(hosts, output, groups = nil)
22 22
 				results = {}
23 23
 				semaphore = ::Mutex.new
24
-				::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS,
25
-				              finish: lambda { |item, _, _| puts item[1] } do |description, host|
26
-					              result = grade host.strip
27
-					              semaphore.synchronize do
28
-						              if results.include? description
29
-							              results[description] << result
30
-						              else
31
-							              results[description] = [result]
32
-						              end
33
-					              end
34
-				              end
24
+				::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item[1] } do |description, host|
25
+					result = grade host.strip
26
+					semaphore.synchronize do
27
+						if results.include? description
28
+							results[description] << result
29
+						else
30
+							results[description] = [result]
31
+						end
32
+					end
33
+				end
35 34
 
36 35
 				results = ::Hash[groups.collect { |g| [g, results[g]] }] if groups
37 36
 
@@ -65,6 +64,7 @@ module CryptCheck
65 64
 
66 65
 			private
67 66
 			SCORES = %w(A+ A A- B C D E F T M X)
67
+
68 68
 			def self.score(a)
69 69
 				SCORES.index a.grade
70 70
 			end

+ 1
- 1
lib/cryptcheck/tls/https/server.rb Bestand weergeven

@@ -8,7 +8,7 @@ module CryptCheck
8 8
 			class Server < Tls::Server
9 9
 				attr_reader :hsts
10 10
 
11
-				def initialize(hostname, port=443, methods: EXISTING_METHODS)
11
+				def initialize(hostname, port=443)
12 12
 					super
13 13
 					fetch_hsts
14 14
 				end

+ 4
- 6
lib/cryptcheck/tls/server.rb Bestand weergeven

@@ -32,16 +32,14 @@ module CryptCheck
32 32
 
33 33
 			attr_reader :hostname, :port, :prefered_ciphers, :cert, :cert_valid, :cert_trusted
34 34
 
35
-			def initialize(hostname, port, methods: EXISTING_METHODS)
35
+			def initialize(hostname, port)
36 36
 				@log = Logging.logger[hostname]
37 37
 				@hostname = hostname
38 38
 				@port = port
39
-				@methods = methods
40 39
 				@log.error { "Begin analysis" }
41 40
 				extract_cert
42 41
 				fetch_prefered_ciphers
43 42
 				check_supported_cipher
44
-				fetch_hsts
45 43
 				@log.error { "End analysis" }
46 44
 			end
47 45
 
@@ -228,7 +226,7 @@ module CryptCheck
228 226
 			end
229 227
 
230 228
 			def extract_cert
231
-				@methods.each do |method|
229
+				EXISTING_METHODS.each do |method|
232 230
 					next unless SUPPORTED_METHODS.include? method
233 231
 					begin
234 232
 						@cert, @chain = ssl_client(method) { |s| [s.peer_cert, s.peer_cert_chain] }
@@ -254,7 +252,7 @@ module CryptCheck
254 252
 
255 253
 			def fetch_prefered_ciphers
256 254
 				@prefered_ciphers = {}
257
-				@methods.each do |method|
255
+				EXISTING_METHODS.each do |method|
258 256
 					next unless SUPPORTED_METHODS.include? method
259 257
 					@prefered_ciphers[method] = prefered_cipher method
260 258
 				end
@@ -276,7 +274,7 @@ module CryptCheck
276 274
 
277 275
 			def check_supported_cipher
278 276
 				@supported_ciphers = {}
279
-				@methods.each do |method|
277
+				EXISTING_METHODS.each do |method|
280 278
 					next unless SUPPORTED_METHODS.include? method and @prefered_ciphers[method]
281 279
 					@supported_ciphers[method] = available_ciphers(method).select { |cipher| supported_cipher? method, cipher }
282 280
 				end

+ 55
- 0
lib/cryptcheck/tls/xmpp.rb Bestand weergeven

@@ -0,0 +1,55 @@
1
+require 'erb'
2
+require 'logging'
3
+require 'parallel'
4
+
5
+module CryptCheck
6
+	module Tls
7
+		module Xmpp
8
+			MAX_ANALYSIS_DURATION = 600
9
+			PARALLEL_ANALYSIS = 10
10
+			@@log = ::Logging.logger[Https]
11
+
12
+			def self.grade(hostname, type=:s2s)
13
+				timeout MAX_ANALYSIS_DURATION do
14
+					Grade.new Server.new hostname, type
15
+				end
16
+			rescue ::Exception => e
17
+				@@log.error { "Error during #{hostname}:#{type} analysis : #{e}" }
18
+				TlsNotSupportedGrade.new TlsNotSupportedServer.new hostname, type
19
+			end
20
+
21
+			def self.analyze(hosts, output)
22
+				servers = []
23
+				semaphore = ::Mutex.new
24
+				::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item } do |host|
25
+										 result = grade host.strip
26
+										 semaphore.synchronize { servers << result }
27
+									 end
28
+				servers.sort! do |a, b|
29
+					cmp = score(a) <=> score(b)
30
+					if cmp == 0
31
+						cmp = b.score <=> a.score
32
+						if cmp == 0
33
+							cmp = a.server.hostname <=> b.server.hostname
34
+						end
35
+					end
36
+					cmp
37
+				end
38
+
39
+				::File.write output, ::ERB.new(::File.read('output/xmpp.erb')).result(binding)
40
+			end
41
+
42
+			def self.analyze_from_file(file, output)
43
+				hosts = ::YAML.load_file file
44
+				self.analyze hosts, output
45
+			end
46
+
47
+			private
48
+			SCORES = %w(A+ A A- B C D E F T M X)
49
+
50
+			def self.score(a)
51
+				SCORES.index a.grade
52
+			end
53
+		end
54
+	end
55
+end

+ 16
- 0
lib/cryptcheck/tls/xmpp/grade.rb Bestand weergeven

@@ -0,0 +1,16 @@
1
+module CryptCheck
2
+	module Tls
3
+		module Xmpp
4
+			class Grade < Tls::Grade
5
+				def success
6
+					super
7
+					@success << :required if @server.required?
8
+				end
9
+
10
+				def all_success
11
+					super + %i(required)
12
+				end
13
+			end
14
+		end
15
+	end
16
+end

+ 50
- 0
lib/cryptcheck/tls/xmpp/server.rb Bestand weergeven

@@ -0,0 +1,50 @@
1
+require 'socket'
2
+require 'openssl'
3
+require 'nokogiri'
4
+require 'resolv'
5
+
6
+module CryptCheck
7
+	module Tls
8
+		module Xmpp
9
+			TLS_NAMESPACE = 'urn:ietf:params:xml:ns:xmpp-tls'
10
+			RESOLVER = Resolv::DNS.new
11
+
12
+			class Server < Tls::Server
13
+				attr_reader :domain
14
+
15
+				def initialize(domain, type=:s2s, hostname: nil)
16
+					service, port = case type
17
+								  when :s2s then ['_xmpp-server', 5269]
18
+								  when :c2s then ['_xmpp-client', 5222]
19
+							  end
20
+					@domain = domain
21
+					unless hostname
22
+						srv = RESOLVER.getresources("#{service}._tcp.#{domain}", Resolv::DNS::Resource::IN::SRV).sort_by(&:priority).first
23
+						if srv
24
+							hostname, port = srv.target.to_s, srv.port
25
+						else # DNS is not correctly set, guess config…
26
+							hostname = domain
27
+						end
28
+					end
29
+					super hostname, port
30
+				end
31
+
32
+				def ssl_connect(socket, context, method, &block)
33
+					socket.write "<?xml version='1.0' ?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='jabber:client' to='#{@domain}' version='1.0'>"
34
+					response = ::Nokogiri::XML socket.recv 4096
35
+					starttls = response.xpath '//tls:starttls', tls: TLS_NAMESPACE
36
+					raise TLSNotAvailableException unless starttls
37
+					@required = !starttls.xpath('//tls:required', tls: TLS_NAMESPACE).nil?
38
+					socket.write "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />\r\n"
39
+					response = ::Nokogiri::XML socket.recv 4096
40
+					raise TLSNotAvailableException unless response.xpath '//tls:proceed', tls: TLS_NAMESPACE
41
+					super
42
+				end
43
+
44
+				def required?
45
+					@required
46
+				end
47
+			end
48
+		end
49
+	end
50
+end

+ 1
- 4
output/https.erb Bestand weergeven

@@ -1,6 +1,3 @@
1
-<%
2
-
3
-%>
4 1
 <!DOCTYPE html>
5 2
 <html lang="fr">
6 3
 	<head>
@@ -84,7 +81,7 @@
84 81
 										<%= s.hostname %>
85 82
 									</a>
86 83
 								</th>
87
-								<% if s.is_a? SSLCheck::TlsNotSupportedServer %>
84
+								<% if s.is_a? Tls::TlsNotSupportedServer %>
88 85
 								<td class="critical" colspan="16">
89 86
 									No SSL/TLS
90 87
 								</td>

+ 1
- 0
output/index.yml Bestand weergeven

@@ -15,6 +15,7 @@
15 15
   - pfag.me
16 16
   - komic.eu
17 17
   - apericraft.ovh
18
+  - nicolas.legland.fr
18 19
 - description: Associations
19 20
   hostnames:
20 21
   - april.org

+ 178
- 0
output/xmpp.erb Bestand weergeven

@@ -0,0 +1,178 @@
1
+<!DOCTYPE html>
2
+<html lang="fr">
3
+	<head>
4
+		<meta charset="utf-8">
5
+		<meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+		<meta name="viewport" content="width=device-width, initial-scale=1">
7
+		<title>XMPP</title>
8
+		<link rel="stylesheet" href="bootstrap.min.css">
9
+		<style>
10
+			body {
11
+				margin-top: 10px;
12
+			}
13
+
14
+			td {
15
+				text-align: center;
16
+			}
17
+
18
+			.critical {
19
+				background-color: #000;
20
+				color: #fff;
21
+			}
22
+
23
+			tr:hover > td.critical, td:hover.critical {
24
+				background-color: #333 !important;
25
+			}
26
+		</style>
27
+	</head>
28
+	<body>
29
+		<div class="container-fluid">
30
+			<div class="row">
31
+				<div class="col-md-12">
32
+					<table class="table table-bordered table-hover table-condensed">
33
+						<thead>
34
+							<tr>
35
+								<th rowspan="2">Host</th>
36
+								<td rowspan="2">Grade</td>
37
+								<td colspan="2">Certificate</td>
38
+								<td colspan="4">Protocols</td>
39
+								<td colspan="5">Ciphers</td>
40
+								<td colspan="2">Best practices</td>
41
+							</tr>
42
+							<tr>
43
+								<td>Key size (bits)</td>
44
+								<td class="warning">SHA1 sig</td>
45
+
46
+								<td class="critical">SSL v2</td>
47
+								<td class="critical">SSL v3</td>
48
+								<td class="success">TLS 1.2</td>
49
+								<td class="info">TLS</td>
50
+
51
+								<td>Strength (bits)</td>
52
+								<td class="critical">MD5</td>
53
+								<td class="warning">SHA1</td>
54
+								<td class="critical">DES/RC4</td>
55
+								<td class="danger">3DES</td>
56
+
57
+								<td class="info">PFS</td>
58
+								<td class="success">Required</td>
59
+							</tr>
60
+                        </thead>
61
+						<tfoot>
62
+							<tr>
63
+								<th rowspan="2">Host</th>
64
+								<td rowspan="2">Grade</td>
65
+								<td colspan="2">Certificate</td>
66
+								<td colspan="4">Protocols</td>
67
+								<td colspan="5">Ciphers</td>
68
+								<td colspan="2">Best practices</td>
69
+							</tr>
70
+							<tr>
71
+								<td>Key size (bits)</td>
72
+								<td class="warning">SHA1 sig</td>
73
+
74
+								<td class="critical">SSL v2</td>
75
+								<td class="critical">SSL v3</td>
76
+								<td class="success">TLS 1.2</td>
77
+								<td class="info">TLS</td>
78
+
79
+								<td>Strength (bits)</td>
80
+								<td class="critical">MD5</td>
81
+								<td class="warning">SHA1</td>
82
+								<td class="critical">DES/RC4</td>
83
+								<td class="danger">3DES</td>
84
+
85
+								<td class="info">PFS</td>
86
+								<td class="success">Required</td>
87
+							</tr>
88
+						</tfoot>
89
+						<tbody>
90
+							<% servers.each do |n|
91
+								s = n.server
92
+							%>
93
+							<tr>
94
+								<% if s.is_a? Tls::TlsNotSupportedServer %>
95
+								<th id="<%= s.hostname %>"><%= s.hostname %></th>
96
+								<td class="critical" colspan="16">
97
+									No SSL/TLS
98
+								</td>
99
+								<% else
100
+										rank_color = case n.grade
101
+											when 'A+' then :info
102
+											when 'A', 'A-' then :success
103
+											when 'B', 'C' then :warning
104
+											when 'T', 'M' then :critical
105
+											else :danger
106
+										end %>
107
+								<th id="<%= s.domain %>"><%= s.domain %></th>
108
+								<td class="<%= rank_color %>">
109
+									<%= n.grade %>
110
+								</td>
111
+
112
+								<td class="<%= s.key_size < 2048 ? :danger : s.key_size < 4096 ? :warning : :success %>">
113
+									<% type, size = s.key %>
114
+									<%= "#{size} (#{type.to_s.upcase})" %>
115
+									<span class="sr-only">(<%= s.key_size < 2048 ? '☹' : '☺' %>)</span>
116
+								</td>
117
+								<td class="<%= s.sha1_sig? ? :warning : :success %>">
118
+									<%= s.sha1_sig? ? '✓' : '✗' %>
119
+									<span class="sr-only">(<%= s.sha1_sig? ? '☹' : '☺' %>)</span>
120
+								</td>
121
+
122
+								<td class="<%= s.sslv2? ? :critical : :success %>">
123
+									<%= s.sslv2? ? '✓' : '✗' %>
124
+									<span class="sr-only">(<%= s.sslv2? ? '☹' : '☺' %>)</span>
125
+								</td>
126
+								<td class="<%= s.sslv3? ? :critical : :success %>">
127
+									<%= s.sslv3? ? '✓' : '✗' %>
128
+									<span class="sr-only">(<%= s.sslv3? ? '☹' : '☺' %>)</span>
129
+								</td>
130
+								<td class="<%= s.tlsv1_2? ? :success : :danger %>">
131
+									<%= s.tlsv1_2? ? '✓' : '✗' %>
132
+									<span class="sr-only">(<%= s.tlsv1_2? ? '☺' : '☹' %>)</span>
133
+								</td>
134
+								<td class="<%= s.tls? ? (s.tls_only? ? :info : :success) : :danger %>">
135
+									<%= s.tls? ? '✓' : '✗' %>
136
+									<span class="sr-only">(<%= s.tls? ? '☺' : '☹' %>)</span>
137
+								</td>
138
+
139
+								<% cipher_size = s.cipher_size[:worst] %>
140
+								<td class="<%= cipher_size < 112 ? :danger : cipher_size < 128 ? :warning : :success %>">
141
+									<%= cipher_size %>
142
+									<span class="sr-only">(<%= cipher_size < 128 ? '☹' : '☺' %>)</span>
143
+								</td>
144
+								<td class="<%= s.md5? ? :critical : :success %>">
145
+									<%= s.md5? ? '✓' : '✗' %>
146
+									<span class="sr-only">(<%= s.md5? ? '☹' : '☺' %>)</span>
147
+								</td>
148
+								<td class="<%= s.sha1? ? :warning : :success %>">
149
+									<%= s.sha1? ? '✓' : '✗' %>
150
+									<span class="sr-only">(<%= s.sha1? ? '☹' : '☺' %>)</span>
151
+								</td>
152
+								<td class="<%= (s.rc4? or s.des?) ? :critical : :success %>">
153
+									<%= (s.rc4? or s.des?) ? '✓' : '✗' %>
154
+									<span class="sr-only">(<%= (s.rc4? or s.des?) ? '☹' : '☺' %>)</span>
155
+								</td>
156
+								<td class="<%= s.des3? ? :danger : :success %>">
157
+									<%= s.des3? ? '✓' : '✗' %>
158
+									<span class="sr-only">(<%= s.des3? ? '☹' : '☺' %>)</span>
159
+								</td>
160
+
161
+								<td class="<%= s.pfs? ? (s.pfs_only? ? :info : :success) : :danger %>">
162
+									<%= s.pfs? ? '✓' : '✗' %>
163
+									<span class="sr-only">(<%= s.pfs? ? '☺' : '☹' %>)</span>
164
+								</td>
165
+								<td class="<%= s.required? ? :success : :danger %>">
166
+									<%= s.required? ? '✓' : '✗' %>
167
+									<span class="sr-only">(<%= s.required? ? '☺' : '☹' %>)</span>
168
+								</td>
169
+								<% end %>
170
+							</tr>
171
+							<% end %>
172
+						</tbody>
173
+					</table>
174
+				</div>
175
+			</div>
176
+		</div>
177
+	</body>
178
+</html>

+ 17
- 0
output/xmpp.yml Bestand weergeven

@@ -0,0 +1,17 @@
1
+- imirhil.fr
2
+- magicbox.okhin.fr
3
+- cyphercat.eu
4
+- jabber.ccc.de
5
+- jbfavre.im
6
+- axelsimon.net
7
+- google.com
8
+- ecuri.es
9
+- dattaz.fr
10
+- jabber.lqdn.fr
11
+- mailfr.com
12
+- arysthaar.pw
13
+- startcom.org
14
+- riseup.net
15
+- citronna.de
16
+- matlink.fr
17
+- verry.org

Laden…
Annuleren
Opslaan