ssl_scan 0.0.5 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/ssl_scan.rb CHANGED
@@ -4,6 +4,7 @@ require "openssl"
4
4
  require "ssl_scan/version"
5
5
  require "ssl_scan/compat"
6
6
  require "ssl_scan/result"
7
+ require "ssl_scan/util"
7
8
  require "timeout"
8
9
  require "thread"
9
10
  require "ssl_scan/sync/thread_safe"
@@ -1,9 +1,8 @@
1
- require "stringio"
2
-
3
1
  module SSLScan
4
2
  module Commands
5
3
  class Command
6
4
  attr_accessor :results, :options, :stream, :errors
5
+ include FastGettext::Translation
7
6
 
8
7
  def initialize(results=[], stream=nil)
9
8
  @results = results
@@ -17,11 +16,11 @@ module SSLScan
17
16
 
18
17
  # Display Methods
19
18
  def write_header(host, port=443)
20
- stream.printf "\nTesting SSL server #{host} on port #{port}"
19
+ stream.printf _("\nTesting SSL server %{host} on port %{port}\n") % { host: host, port: port }
21
20
  end
22
21
 
23
22
  def write_preferred_ciphers(scanner)
24
- stream.printf("\nServer Preferred Cipher(s)\n")
23
+ stream.printf _("\nServer Preferred Cipher(s)\n")
25
24
  ciphers = scanner.get_preferred_ciphers
26
25
  ciphers.each do |c|
27
26
  if c.length > 1 && !c[1].empty?
@@ -32,7 +31,7 @@ module SSLScan
32
31
  end
33
32
 
34
33
  def write_ciphers(scanner=nil)
35
- stream.printf "\nSupported Server Cipher(s):\n"
34
+ stream.printf _("\nSupported Server Cipher(s):\n")
36
35
 
37
36
  sslv = options.only_ssl2 || options.only_ssl3 || options.only_tls1 || false
38
37
 
@@ -29,9 +29,14 @@ module SSLScan
29
29
  write_header(parts[0])
30
30
  end
31
31
 
32
- write_ciphers(scanner)
33
- write_preferred_ciphers(scanner)
34
- @results << scanner.results
32
+ if options.only_cert
33
+ scanner.get_first_valid_cert
34
+ @results << scanner.results
35
+ else
36
+ write_ciphers(scanner)
37
+ write_preferred_ciphers(scanner)
38
+ @results << scanner.results
39
+ end
35
40
  end
36
41
 
37
42
  end # Host
@@ -56,6 +56,10 @@ def self.is_linux
56
56
  @@is_linux = (RUBY_PLATFORM =~ /linux/) ? true : false
57
57
  end
58
58
 
59
+ def self.is_debian
60
+ return self.is_linux && File.exists?('/etc/debian_version')
61
+ end
62
+
59
63
  def self.is_bsdi
60
64
  return @@is_bsdi if @@is_bsdi
61
65
  @@is_bsdi = (RUBY_PLATFORM =~ /bsdi/i) ? true : false
data/lib/ssl_scan/main.rb CHANGED
@@ -1,11 +1,7 @@
1
- # require "ssl_scan/compat"
2
- # require "ssl_scan/version"
3
- # require "ssl_scan/scanner"
4
- # require "ssl_scan/result"
5
-
6
1
  require "ssl_scan/version"
7
2
  require "ssl_scan/compat"
8
3
  require "ssl_scan/result"
4
+ require "ssl_scan/util"
9
5
  require "timeout"
10
6
  require "thread"
11
7
  require "ssl_scan/sync/thread_safe"
@@ -18,19 +14,42 @@ require "ssl_scan/scanner"
18
14
  require "openssl"
19
15
  require "optparse"
20
16
  require "ostruct"
17
+ require "fast_gettext"
21
18
 
22
19
  require "ssl_scan/commands/command"
23
20
  require "ssl_scan/commands/host"
24
21
 
22
+ #
23
+ # Setup Translations
24
+ #
25
+ FastGettext.add_text_domain('ssl_scan', path: File.join(SSLScan::Util::ROOT, 'locale'))
26
+ FastGettext.text_domain = 'ssl_scan'
27
+ FastGettext.available_locales = ['en', 'de']
28
+ active_locale = "en"
29
+ ['LC_ALL', 'LANG', 'LANGUAGE'].each do |env_lang|
30
+ lang = ENV[env_lang]
31
+ if lang
32
+ lang = lang[0..1]
33
+ if FastGettext.available_locales.include?(lang)
34
+ active_locale = lang
35
+ break
36
+ end
37
+ end
38
+ end
39
+ FastGettext.locale = active_locale
40
+
41
+
25
42
  module SSLScan
26
43
  class Main
27
44
 
45
+ include FastGettext::Translation
46
+
28
47
  EXIT_SUCCESS = 0
29
48
  EXIT_FAILURE = 1
30
49
 
31
- SYNTAX = "ssl_scan [Options] [host:port | host]"
50
+ SYNTAX = _("ssl_scan [Options] [host:port | host]")
32
51
  WEBSITE = "https://www.testcloud.de"
33
- COPYRIGHT = "Copyright (C) John Faucett #{Time.now.year}"
52
+ COPYRIGHT = _("Copyright (C) John Faucett %{year}") % { year: Time.now.year }
34
53
 
35
54
  BANNER =<<EOH
36
55
  _
@@ -47,10 +66,10 @@ EOH
47
66
  def check_host(host, die_on_fail=true)
48
67
  valid = true
49
68
  port = 443
50
- error_msg = "Host invalid"
69
+ error_msg = _("Host invalid")
51
70
  begin
52
71
  if !host
53
- error_msg = "Host not given"
72
+ error_msg = _("Host not given")
54
73
  valid = false
55
74
  else
56
75
  host_parts = host.split(":")
@@ -64,7 +83,7 @@ EOH
64
83
  end
65
84
 
66
85
  unless valid
67
- printf("Error: %s\n", error_msg)
86
+ printf _("Error: %{error}\n") % { error: error_msg }
68
87
  exit(EXIT_FAILURE) unless !die_on_fail
69
88
  end
70
89
  return valid
@@ -106,7 +125,7 @@ EOH
106
125
  alias_method :run, :main
107
126
 
108
127
  def self.version_info
109
- sprintf("ssl_scan version %s\n%s\n%s\n", VERSION::STRING, WEBSITE, COPYRIGHT)
128
+ _("ssl_scan version %{version}\n%{web}\n%{copy}\n") % { version: VERSION::STRING, web: WEBSITE, copy: COPYRIGHT}
110
129
  end
111
130
 
112
131
  def show_results(host, results)
@@ -114,13 +133,18 @@ EOH
114
133
  unless result_set.empty?
115
134
  result_set.each do |result|
116
135
  show_certificate(result.cert)
136
+
137
+ # TODO: Implement certificate verification
138
+ printf _("Verify Certificate:")
139
+ printf _(" NOT IMPLEMENTED")
140
+ printf("\n")
117
141
  end
118
142
  end
119
143
  end
120
144
 
121
145
  def show_certificate(cert)
122
- printf("SSL Certificate:\n")
123
- printf(" Version: %d\n", cert.version)
146
+ printf _("SSL Certificate:\n")
147
+ printf _(" Version: %{version}\n") % { version: cert.version }
124
148
  printf(" Serial Number: %s\n", cert.serial.to_s(16))
125
149
  printf(" Signature Algorithm: %s\n", cert.signature_algorithm)
126
150
  printf(" Issuer: %s\n", cert.issuer.to_s)
@@ -129,7 +153,34 @@ EOH
129
153
  printf(" Subject: %s\n", cert.subject.to_s)
130
154
  printf(" %s", cert.public_key.to_text)
131
155
 
132
- # TODO: Implement extensions (see: cert.extensions)
156
+ unless cert.extensions.empty?
157
+ puts _("X509v3 Extensions:")
158
+ cert.extensions.each do |extension|
159
+ case extension.oid
160
+ when 'keyUsage'
161
+ puts _(" X509v3 Key Usage: critical") if extension.critical?
162
+ when 'certificatePolicies'
163
+ puts _(" X509v3 Certificate Policies:")
164
+ when 'subjectAltName'
165
+ puts _(" X509v3 Subject Alternative Name:")
166
+ when 'basicConstraints'
167
+ puts _(" X509v3 Basic Constraints:")
168
+ when 'extendedKeyUsage'
169
+ puts _(" X509v3 Extended Key Usage:")
170
+ when 'crlDistributionPoints'
171
+ puts _(" X509v3 CRL Distribution Points:")
172
+ when 'authorityInfoAccess'
173
+ puts _(" Authority Information Access:")
174
+ when 'subjectKeyIdentifier'
175
+ puts _(" X509v3 Subject Key Identifier:")
176
+ when 'authorityKeyIdentifier'
177
+ puts _(" X509v3 Authority Key Identifier:")
178
+ else
179
+ puts extension.oid
180
+ end
181
+ puts _(" %{value}") % { value: extension.value }
182
+ end
183
+ end
133
184
  end
134
185
 
135
186
  def show_command_errors(host, errors)
@@ -143,6 +194,7 @@ EOH
143
194
  options.only_ssl2 = false
144
195
  options.only_ssl3 = false
145
196
  options.only_tls1 = false
197
+ options.only_cert = false
146
198
 
147
199
  opts = OptionParser.new do |opts|
148
200
  opts.banner = sprintf("%s%s", BANNER, version_info)
@@ -155,8 +207,8 @@ EOH
155
207
 
156
208
  # File containing list of hosts to check
157
209
  opts.on( "-t",
158
- "--targets FILE",
159
- "A file containing a list of hosts to check with syntax ( host | host:port).") do |filename|
210
+ _("--targets FILE"),
211
+ _("A file containing a list of hosts to check with syntax ( host | host:port).")) do |filename|
160
212
  options.file = filename
161
213
  end
162
214
 
@@ -181,6 +233,12 @@ EOH
181
233
  options.only_tls1 = :TLSv1
182
234
  end
183
235
 
236
+ opts.on( "-c",
237
+ "--cert",
238
+ "Only get the server certificate") do
239
+ options.only_cert = true
240
+ end
241
+
184
242
  opts.on( "-d",
185
243
  "--debug",
186
244
  "Print any SSL errors to stderr.") do
@@ -7,10 +7,12 @@ module SSLScan
7
7
 
8
8
  attr_reader :ciphers
9
9
  attr_reader :supported_versions
10
+ attr_reader :peer_verified
10
11
 
11
12
  def initialize()
12
13
  @cert = nil
13
14
  @ciphers = Set.new
15
+ @peer_verified = false
14
16
  @supported_versions = [:SSLv2, :SSLv3, :TLSv1]
15
17
  end
16
18
 
@@ -88,8 +88,9 @@ class Scanner
88
88
  return scan_result
89
89
  end
90
90
 
91
- sslctx = OpenSSL::SSL::SSLContext.new(ssl_version)
92
- sslctx.ciphers.each do |cipher_name, ssl_ver, key_length, alg_length|
91
+ sslctx = OpenSSL::SSL::SSLContext.new(ssl_version)
92
+ sslctx.ciphers.each do |cipher_name, ssl_ver, key_length, alg_length|
93
+
93
94
  status = test_cipher(ssl_version, cipher_name)
94
95
  scan_result.add_cipher(ssl_version, cipher_name, key_length, status)
95
96
  if status == :accepted and scan_result.cert.nil?
@@ -121,6 +122,10 @@ class Scanner
121
122
  'Timeout' => @timeout
122
123
  )
123
124
  ssl_versions[ssl_version] = scan_client.cipher
125
+
126
+ if scan_client
127
+ scan_client.close
128
+ end
124
129
  rescue => ex
125
130
  ssl_versions.delete(ssl_version)
126
131
  end
@@ -128,6 +133,36 @@ class Scanner
128
133
  ssl_versions
129
134
  end
130
135
 
136
+
137
+ def get_first_valid_cert
138
+ scan_result = SSLScan::Result.new
139
+ scan_result.openssl_sslv2 = sslv2
140
+ @supported_versions.each do |ssl_version|
141
+ begin
142
+ scan_client = SSLScan::Socket::Tcp.create(
143
+ 'Context' => @context,
144
+ 'PeerHost' => @host,
145
+ 'PeerPort' => @port,
146
+ 'SSL' => true,
147
+ 'SSLVersion' => ssl_version,
148
+ 'Timeout' => @timeout
149
+ )
150
+ cipher_name = scan_client.cipher[0]
151
+ key_length = scan_client.cipher[3]
152
+ status = test_cipher(ssl_version, cipher_name)
153
+ scan_result.add_cipher(ssl_version, cipher_name, key_length, status)
154
+ if status == :accepted and scan_result.cert.nil?
155
+ scan_result.cert = get_cert(ssl_version, cipher_name)
156
+ break
157
+ end
158
+ rescue => ex
159
+ # noop
160
+ end
161
+ end
162
+ @results = scan_result
163
+ scan_result
164
+ end
165
+
131
166
  def test_ssl
132
167
  begin
133
168
  scan_client = SSLScan::Socket::Tcp.create(
@@ -0,0 +1,7 @@
1
+ module SSLScan
2
+ module Util
3
+
4
+ ROOT = File.expand_path("../../../", __FILE__)
5
+
6
+ end
7
+ end
@@ -2,7 +2,7 @@ module SSLScan
2
2
  module VERSION
3
3
  MAJOR = 0
4
4
  MINOR = 0
5
- PATCH = 5
5
+ PATCH = 6
6
6
 
7
7
  STRING = [MAJOR,MINOR,PATCH].join('.')
8
8
  end
Binary file
@@ -0,0 +1,46 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) 2014 John Faucett
3
+ # This file is distributed under the same license as the app package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: ssl_scan 0.0.5\n"
10
+ "Report-Msgid-Bugs-To: \n"
11
+ "POT-Creation-Date: 2013-09-29 17:49-0700\n"
12
+ "PO-Revision-Date: 2013-09-29 17:49-0700\n"
13
+ "Last-Translator: John Faucett <jwaterfaucett@gmail.com>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "Language: \n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=UTF-8\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
20
+
21
+ msgid "Host invalid"
22
+ msgstr "Host ungültig"
23
+
24
+ msgid "Error: %{error}\n"
25
+ msgstr "Fehler: %{error}\n"
26
+
27
+ msgid "Host not given"
28
+ msgstr "Host wurde nicht angegeben"
29
+
30
+ msgid "ssl_scan [Options] [host:port | host]"
31
+ msgstr "ssl_scan [Optionen] [host:port | host]"
32
+
33
+ msgid "Copyright (C) John Faucett %{year}"
34
+ msgstr "Copyright (C) John Faucett %{year}"
35
+
36
+ msgid "SSL Certificate:\n"
37
+ msgstr "SSL Zertifikat:\n"
38
+
39
+ msgid " Version: %{version}\n"
40
+ msgstr " Version: %{version}\n"
41
+
42
+ msgid "A file containing a list of hosts to check with syntax ( host | host:port)."
43
+ msgstr "Eine Datei die eine Liste der zu überprüfenden Hosts enthält ( host | host:port)."
44
+
45
+ msgid "--targets FILE"
46
+ msgstr "--targets DATEI"
Binary file
@@ -0,0 +1,34 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) 2014 John Faucett
3
+ # This file is distributed under the same license as the app package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ #, fuzzy
7
+ msgid ""
8
+ msgstr ""
9
+ "Project-Id-Version: ssl_scan 0.0.5\n"
10
+ "Report-Msgid-Bugs-To: \n"
11
+ "POT-Creation-Date: 2013-09-29 17:49-0700\n"
12
+ "PO-Revision-Date: 2013-09-29 17:49-0700\n"
13
+ "Last-Translator: John Faucett <jwaterfaucett@gmail.com>\n"
14
+ "Language-Team: LANGUAGE <LL@li.org>\n"
15
+ "Language: \n"
16
+ "MIME-Version: 1.0\n"
17
+ "Content-Type: text/plain; charset=UTF-8\n"
18
+ "Content-Transfer-Encoding: 8bit\n"
19
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
20
+
21
+ msgid "Host invalid"
22
+ msgstr "Host invalid"
23
+
24
+ msgid "Error: %{error}\n"
25
+ msgstr "Error: %{error}\n"
26
+
27
+ msgid "Host not given"
28
+ msgstr "Host not given"
29
+
30
+ msgid "ssl_scan [Options] [host:port | host]"
31
+ msgstr "ssl_scan [Options] [host:port | host]"
32
+
33
+ msgid "Copyright (C) John Faucett %{year}"
34
+ msgstr "Copyright (C) John Faucett %{year}"
data/sslscan.gemspec CHANGED
@@ -18,7 +18,10 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
+ spec.add_runtime_dependency "fast_gettext", "~> 0.8"
22
+
21
23
  spec.add_development_dependency "bundler", "~> 1.5"
22
24
  spec.add_development_dependency "rake"
23
- spec.add_development_dependency "rspec", "~> 3.0.0"
25
+ spec.add_development_dependency "rspec", "~> 3.0"
26
+ spec.add_development_dependency "gettext"
24
27
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ssl_scan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Faucett
@@ -10,6 +10,20 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2014-06-05 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: fast_gettext
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: '0.8'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: '0.8'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -44,14 +58,28 @@ dependencies:
44
58
  requirements:
45
59
  - - ~>
46
60
  - !ruby/object:Gem::Version
47
- version: 3.0.0
61
+ version: '3.0'
48
62
  type: :development
49
63
  prerelease: false
50
64
  version_requirements: !ruby/object:Gem::Requirement
51
65
  requirements:
52
66
  - - ~>
53
67
  - !ruby/object:Gem::Version
54
- version: 3.0.0
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: gettext
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
55
83
  description: An SSL Scanner Library and Utility in pure Ruby
56
84
  email:
57
85
  - jwaterfaucett@gmail.com
@@ -67,6 +95,7 @@ files:
67
95
  - README.md
68
96
  - Rakefile
69
97
  - bin/ssl_scan
98
+ - data/cacert.pem
70
99
  - lib/ssl_scan.rb
71
100
  - lib/ssl_scan/commands/command.rb
72
101
  - lib/ssl_scan/commands/host.rb
@@ -95,7 +124,12 @@ files:
95
124
  - lib/ssl_scan/socket/tcp_server.rb
96
125
  - lib/ssl_scan/socket/udp.rb
97
126
  - lib/ssl_scan/sync/thread_safe.rb
127
+ - lib/ssl_scan/util.rb
98
128
  - lib/ssl_scan/version.rb
129
+ - locale/de/LC_MESSAGES/ssl_scan.mo
130
+ - locale/de/ssl_scan.po
131
+ - locale/en/LC_MESSAGES/ssl_scan.mo
132
+ - locale/en/ssl_scan.po
99
133
  - spec/lib/ssl_scan/scanner_spec.rb
100
134
  - spec/spec_helper.rb
101
135
  - sslscan.gemspec