Browse Source

Check for TLS_FALLBACK_SCSV

master
aeris 2 years ago
parent
commit
8a1c4f8856
4 changed files with 106 additions and 2 deletions
  1. 1
    0
      Makefile
  2. 62
    0
      fallback_scsv.patch
  3. 8
    1
      lib/cryptcheck/tls/grade.rb
  4. 35
    1
      lib/cryptcheck/tls/server.rb

+ 1
- 0
Makefile View File

@@ -69,6 +69,7 @@ $(RUBY_DIR)/: build/$(RUBY_NAME).tar.gz
69 69
 $(RUBY_OPENSSL_EXT_DIR)/Makefile: libs | $(RUBY_DIR)/
70 70
 	patch -d $(RUBY_DIR)/ -p1 < tmp_key.patch
71 71
 	patch -d $(RUBY_DIR)/ -p1 < set_ecdh_curves.patch
72
+	patch -d $(RUBY_DIR)/ -p1 < fallback_scsv.patch
72 73
 	cd $(RUBY_OPENSSL_EXT_DIR) && ruby extconf.rb
73 74
 
74 75
 $(RUBY_OPENSSL_EXT_DIR)/openssl.so: libs $(RUBY_OPENSSL_EXT_DIR)/Makefile

+ 62
- 0
fallback_scsv.patch View File

@@ -0,0 +1,62 @@
1
+diff --git a/ext/openssl/lib/openssl/ssl.rb b/ext/openssl/lib/openssl/ssl.rb
2
+index 57519f2..c5b0c8b 100644
3
+--- a/ext/openssl/lib/openssl/ssl.rb
4
++++ b/ext/openssl/lib/openssl/ssl.rb
5
+@@ -105,11 +105,12 @@ class SSLContext
6
+       #    SSLContext.new("SSLv23_client") => ctx
7
+       #
8
+       # You can get a list of valid methods with OpenSSL::SSL::SSLContext::METHODS
9
+-      def initialize(version = nil)
10
++      def initialize(version = nil, fallback_scsv = false)
11
+         INIT_VARS.each { |v| instance_variable_set v, nil }
12
+         self.options = self.options | OpenSSL::SSL::OP_ALL
13
+         return unless version
14
+         self.ssl_version = version
15
++        self.enable_fallback_scsv if fallback_scsv
16
+       end
17
+
18
+       ##
19
+diff --git a/ext/openssl/ossl_ssl.c b/ext/openssl/ossl_ssl.c
20
+index bcc624f..0c1780b 100644
21
+--- a/ext/openssl/ossl_ssl.c
22
++++ b/ext/openssl/ossl_ssl.c
23
+@@ -978,6 +978,31 @@ ossl_sslctx_set_ciphers(VALUE self, VALUE v)
24
+     return v;
25
+ }
26
+
27
++/*
28
++ * call-seq:
29
++ *    ctx.enable_fallback_scsv() => nil
30
++ *
31
++ * Activate TLS_FALLBACK_SCSV for this context.
32
++ * See RFC 7507.
33
++ */
34
++static VALUE
35
++ossl_sslctx_enable_fallback_scsv(VALUE self)
36
++{
37
++    SSL_CTX *ctx;
38
++
39
++    GetSSLCTX(self, ctx);
40
++    if(!ctx){
41
++        rb_warning("SSL_CTX is not initialized.");
42
++        return Qnil;
43
++    }
44
++
45
++    long modes = SSL_CTX_get_mode(ctx);
46
++	modes |= SSL_MODE_SEND_FALLBACK_SCSV;
47
++	SSL_CTX_set_mode(ctx, modes);
48
++
49
++    return Qnil;
50
++}
51
++
52
+ #if !defined(OPENSSL_NO_EC)
53
+ /*
54
+  * call-seq:
55
+@@ -2330,6 +2355,7 @@ Init_ossl_ssl(void)
56
+     rb_define_method(cSSLContext, "ciphers",     ossl_sslctx_get_ciphers, 0);
57
+     rb_define_method(cSSLContext, "ciphers=",    ossl_sslctx_set_ciphers, 1);
58
+     rb_define_method(cSSLContext, "ecdh_curves=", ossl_sslctx_set_ecdh_curves, 1);
59
++    rb_define_method(cSSLContext, "enable_fallback_scsv", ossl_sslctx_enable_fallback_scsv, 0);
60
+
61
+     rb_define_method(cSSLContext, "setup", ossl_sslctx_setup, 0);
62
+

+ 8
- 1
lib/cryptcheck/tls/grade.rb View File

@@ -119,7 +119,14 @@ module CryptCheck
119 119
 			]
120 120
 
121 121
 			def checks
122
-				CHECKS
122
+				checks = CHECKS
123
+				unless @server.fallback_scsv? == nil
124
+					checks += [
125
+						[:no_fallback_scsv, Proc.new { |s| not s.fallback_scsv? }, :error],
126
+						[:fallback_scsv, Proc.new { |s| s.fallback_scsv? }, :good]
127
+					]
128
+				end
129
+				checks
123 130
 			end
124 131
 
125 132
 			def calculate_states

+ 35
- 1
lib/cryptcheck/tls/server.rb View File

@@ -1,7 +1,6 @@
1 1
 require 'socket'
2 2
 require 'openssl'
3 3
 require 'httparty'
4
-require 'awesome_print'
5 4
 
6 5
 module CryptCheck
7 6
 	module Tls
@@ -41,6 +40,7 @@ module CryptCheck
41 40
 				Logger.info { "Key : #{Tls.key_to_s self.key}" }
42 41
 				fetch_prefered_ciphers
43 42
 				check_supported_cipher
43
+				check_fallback_scsv
44 44
 				uniq_dh
45 45
 			end
46 46
 
@@ -174,6 +174,10 @@ module CryptCheck
174 174
 				supported_ciphers.any? { |c| c.sweet32? }
175 175
 			end
176 176
 
177
+			def fallback_scsv?
178
+				@fallback_scsv
179
+			end
180
+
177 181
 			private
178 182
 			def name
179 183
 				name = "#@ip:#@port"
@@ -229,6 +233,8 @@ module CryptCheck
229 233
 						when /state=error: no ciphers available$/,
230 234
 								/state=SSLv.* read server hello A: sslv.* alert handshake failure$/
231 235
 							raise CipherNotAvailable, e
236
+						when /state=SSLv.* read server hello A: tlsv.* alert inappropriate fallback$/
237
+							raise InappropriateFallback, e
232 238
 					end
233 239
 					raise
234 240
 				rescue ::SystemCallError => e
@@ -350,6 +356,34 @@ module CryptCheck
350 356
 				end
351 357
 			end
352 358
 
359
+			def check_fallback_scsv
360
+				@fallback_scsv = false
361
+
362
+				methods = @supported_ciphers.keys
363
+				if methods.size > 1
364
+					# We will try to connect to the not better supported method
365
+					method = methods[1]
366
+
367
+					begin
368
+						ssl_client method, fallback: true
369
+					rescue InappropriateFallback
370
+						@fallback_scsv = true
371
+					end
372
+				else
373
+					@fallback_scsv = nil
374
+				end
375
+
376
+				text, color = case @fallback_scsv
377
+								  when true
378
+									  ['Supported', :good]
379
+								  when false
380
+									  ['Not supported', :error]
381
+								  when nil
382
+									  ['Not applicable', :unknown]
383
+							  end
384
+				Logger.info { "Fallback SCSV : #{text.colorize color}" }
385
+			end
386
+
353 387
 			def verify_trust(chain, cert)
354 388
 				store         = ::OpenSSL::X509::Store.new
355 389
 				store.purpose = OpenSSL::X509::PURPOSE_SSL_CLIENT

Loading…
Cancel
Save