From 4e0e7e086bbfb79a1a1bf21d967125ed5b8e287c Mon Sep 17 00:00:00 2001 From: aeris Date: Sat, 22 Feb 2020 17:07:45 +0100 Subject: [PATCH] Refactor to allow SSH checks too --- lib/cryptcheck.rb | 82 ++++++----- lib/cryptcheck/grade.rb | 89 +++++------- lib/cryptcheck/host.rb | 84 +++++++++++ lib/cryptcheck/server.rb | 20 +++ lib/cryptcheck/ssh.rb | 15 +- lib/cryptcheck/ssh/grade.rb | 23 +-- lib/cryptcheck/ssh/host.rb | 11 ++ lib/cryptcheck/ssh/server.rb | 218 +++++++++++++++-------------- lib/cryptcheck/tls.rb | 52 +++---- lib/cryptcheck/tls/grade.rb | 31 ++++ lib/cryptcheck/tls/host.rb | 107 +------------- lib/cryptcheck/tls/https/host.rb | 2 +- lib/cryptcheck/tls/https/server.rb | 2 +- lib/cryptcheck/tls/server.rb | 16 +-- lib/cryptcheck/tls/smtp/host.rb | 2 +- lib/cryptcheck/tls/smtp/server.rb | 2 +- lib/cryptcheck/tls/xmpp/host.rb | 30 ++-- lib/cryptcheck/tls/xmpp/server.rb | 2 +- 18 files changed, 412 insertions(+), 376 deletions(-) create mode 100644 lib/cryptcheck/host.rb create mode 100644 lib/cryptcheck/server.rb create mode 100644 lib/cryptcheck/ssh/host.rb create mode 100644 lib/cryptcheck/tls/grade.rb diff --git a/lib/cryptcheck.rb b/lib/cryptcheck.rb index 1726e35..f124aee 100644 --- a/lib/cryptcheck.rb +++ b/lib/cryptcheck.rb @@ -4,47 +4,55 @@ require 'timeout' require 'yaml' module CryptCheck - autoload :State, 'cryptcheck/state' - autoload :Grade, 'cryptcheck/grade' - autoload :Logger, 'cryptcheck/logger' - autoload :Tls, 'cryptcheck/tls' - module Tls - autoload :Method, 'cryptcheck/tls/method' - autoload :Cipher, 'cryptcheck/tls/cipher' - autoload :Curve, 'cryptcheck/tls/curve' - autoload :Cert, 'cryptcheck/tls/cert' - autoload :CAA, 'cryptcheck/tls/caa' - autoload :Engine, 'cryptcheck/tls/engine' - autoload :Server, 'cryptcheck/tls/server' - autoload :TcpServer, 'cryptcheck/tls/server' - autoload :UdpServer, 'cryptcheck/tls/server' - autoload :Host, 'cryptcheck/tls/host' + autoload :State, 'cryptcheck/state' + autoload :Host, 'cryptcheck/host' + autoload :Server, 'cryptcheck/server' + autoload :TcpServer, 'cryptcheck/server' + autoload :UdpServer, 'cryptcheck/server' + autoload :Grade, 'cryptcheck/grade' + autoload :Logger, 'cryptcheck/logger' + autoload :Tls, 'cryptcheck/tls' + module Tls + autoload :Method, 'cryptcheck/tls/method' + autoload :Cipher, 'cryptcheck/tls/cipher' + autoload :Curve, 'cryptcheck/tls/curve' + autoload :Cert, 'cryptcheck/tls/cert' + autoload :CAA, 'cryptcheck/tls/caa' + autoload :Engine, 'cryptcheck/tls/engine' + autoload :Server, 'cryptcheck/tls/server' + autoload :TcpServer, 'cryptcheck/tls/server' + autoload :UdpServer, 'cryptcheck/tls/server' + autoload :Host, 'cryptcheck/tls/host' - autoload :Https, 'cryptcheck/tls/https' - module Https - autoload :Server, 'cryptcheck/tls/https/server' - autoload :Host, 'cryptcheck/tls/https/host' - end + autoload :Https, 'cryptcheck/tls/https' + module Https + autoload :Server, 'cryptcheck/tls/https/server' + autoload :Host, 'cryptcheck/tls/https/host' + end - autoload :Xmpp, 'cryptcheck/tls/xmpp.rb' - module Xmpp - autoload :Server, 'cryptcheck/tls/xmpp/server' - autoload :Host, 'cryptcheck/tls/xmpp/host' - end + autoload :Xmpp, 'cryptcheck/tls/xmpp.rb' + module Xmpp + autoload :Server, 'cryptcheck/tls/xmpp/server' + autoload :Host, 'cryptcheck/tls/xmpp/host' + end - autoload :Smtp, 'cryptcheck/tls/smtp' - module Smtp - autoload :Server, 'cryptcheck/tls/smtp/server' - autoload :Host, 'cryptcheck/tls/smtp/host' - end - end + autoload :Smtp, 'cryptcheck/tls/smtp' + module Smtp + autoload :Server, 'cryptcheck/tls/smtp/server' + autoload :Host, 'cryptcheck/tls/smtp/host' + end + end - autoload :Ssh, 'cryptcheck/ssh' - module Ssh - autoload :Packet, 'cryptcheck/ssh/packet' - autoload :Server, 'cryptcheck/ssh/server' - autoload :SshNotSupportedServer, 'cryptcheck/ssh/server' - end + autoload :Ssh, 'cryptcheck/ssh' + module Ssh + autoload :Packet, 'cryptcheck/ssh/packet' + autoload :Server, 'cryptcheck/ssh/server' + autoload :SshNotSupportedServer, 'cryptcheck/ssh/server' + end + + def self.dev? + @dev ||= !ENV['DEV_MODE'].nil? + end end require 'cryptcheck/fixture' diff --git a/lib/cryptcheck/grade.rb b/lib/cryptcheck/grade.rb index f6b5b41..a4ef409 100644 --- a/lib/cryptcheck/grade.rb +++ b/lib/cryptcheck/grade.rb @@ -1,61 +1,38 @@ module CryptCheck - module Grade - def grade - @grade ||= calculate_grade - end + module Grade + GRADES = %i(A+ A B+ B C+ C D E F G V T X) + GRADE_STATUS = { + :'A+' => :best, + A: :best, + :'B+' => :great, + B: :great, + :'C+' => :good, + C: :good, + D: nil, + E: :warning, + F: :error, + G: :critical, - GRADES = %i(A+ A B+ B C+ C D E F G V T X) - GRADE_STATUS = { - :'A+' => :best, - A: :best, - :'B+' => :great, - B: :great, - :'C+' => :good, - C: :good, - D: nil, - E: :warning, - F: :error, - G: :critical, + V: :critical, + T: :critical, + X: :critical + } + STATUS_GRADES = { + critical: :G, + error: :F, + warning: :E, + default: :D, + good: :C, + great: :B, + best: :A + } - V: :critical, - T: :critical, - X: :critical - } - STATUS_GRADES = { - critical: :G, - error: :F, - warning: :E, - default: :D, - good: :C, - great: :B, - best: :A - } + def grade + @grade ||= calculate_grade + end - def grade_status - GRADE_STATUS.fetch self.grade, :unknown - end - - private - def calculate_grade - return :V unless self.valid? - return :T unless self.trusted? - - states = self.states - states = State.collect { |s| [s, State.state(states, s)] }.to_h - - State::BADS.each do |s| - return STATUS_GRADES[s] if states[s] - end - - grade = STATUS_GRADES[:default] - State::GOODS.each do |s| - state = states[s] - return grade if state == false - grade = STATUS_GRADES[s] - return grade if state == :some - grade = "#{grade}+".to_sym - end - grade - end - end + def grade_status + GRADE_STATUS.fetch self.grade, :unknown + end + end end diff --git a/lib/cryptcheck/host.rb b/lib/cryptcheck/host.rb new file mode 100644 index 0000000..938dc96 --- /dev/null +++ b/lib/cryptcheck/host.rb @@ -0,0 +1,84 @@ +require 'awesome_print' +require 'timeout' + +module CryptCheck + class Host + MAX_ANALYSIS_DURATION = 600 + + attr_reader :servers, :error + + def initialize(hostname, port) + @hostname, @port = hostname, port + + first = true + @servers = resolve.collect do |args| + _, ip = args + first ? (first = false) : Logger.info { '' } + result = begin + server = ::Timeout.timeout MAX_ANALYSIS_DURATION do + server(*args) + end + Logger.info '' + Logger.info { "Grade : #{server.grade.to_s.colorize server.grade_status}" } + Logger.info { server.states.ai } + server + rescue => e + Logger.error { e } + raise if ENV['DEV_MODE'] + e + end + [[@hostname, ip, @port], result] + end.to_h + rescue => e + Logger.error { e } + @error = e + raise if CryptCheck.dev? + end + + def key + { hostname: @hostname, port: @port } + end + + def to_h + if @error + target = [self.key.merge(error: @error.to_s)] + else + target = @servers.collect do |host, server| + hostname, ip, port = host + host = { + hostname: hostname, + ip: ip, + port: port + } + case server + when Server + host[:handshakes] = server.to_h + host[:states] = server.states + host[:grade] = server.grade + else + host[:error] = server.to_s + end + host + end + end + target + end + + private + + def resolve + begin + begin + ip = IPAddr.new @hostname + return [[nil, ip.to_s, ip.family, @port]] + rescue IPAddr::InvalidAddressError + end + ::Addrinfo.getaddrinfo(@hostname, nil, nil, :STREAM) + .collect { |a| [@hostname, a.ip_address, a.afamily, @port] } + end.reject do |family, *_| + (ENV['DISABLE_IPv6'] && family == Socket::AF_INET6) || + (ENV['DISABLE_IPv4'] && family == Socket::AF_INET) + end + end + end +end diff --git a/lib/cryptcheck/server.rb b/lib/cryptcheck/server.rb new file mode 100644 index 0000000..c4c2a97 --- /dev/null +++ b/lib/cryptcheck/server.rb @@ -0,0 +1,20 @@ +module CryptCheck + class Server + end + + class TcpServer < Server + private + + def sock_type + ::Socket::SOCK_STREAM + end + end + + class UdpServer < Server + private + + def sock_type + ::Socket::SOCK_DGRAM + end + end +end diff --git a/lib/cryptcheck/ssh.rb b/lib/cryptcheck/ssh.rb index b5850da..a009588 100644 --- a/lib/cryptcheck/ssh.rb +++ b/lib/cryptcheck/ssh.rb @@ -1,7 +1,12 @@ module CryptCheck - module Ssh - def self.analyze(host, port=22) - ::CryptCheck.analyze host, port, Server - end - end + module Ssh + autoload :Host, 'cryptcheck/ssh/host' + autoload :Server, 'cryptcheck/ssh/server' + autoload :Grade, 'cryptcheck/ssh/grade' + + def self.analyze(host, port = 22) + host = Host.new host, port + Tls.aggregate host + end + end end diff --git a/lib/cryptcheck/ssh/grade.rb b/lib/cryptcheck/ssh/grade.rb index e00fb96..a96a02f 100644 --- a/lib/cryptcheck/ssh/grade.rb +++ b/lib/cryptcheck/ssh/grade.rb @@ -1,14 +1,17 @@ module CryptCheck - module Ssh - class Grade - attr_reader :server + module Ssh + module Grade + include CryptCheck::Grade - def initialize(server) - @server = server - end + def states + {} + end - def display - end - end - end + private + + def calculate_grade + :'-' + end + end + end end diff --git a/lib/cryptcheck/ssh/host.rb b/lib/cryptcheck/ssh/host.rb new file mode 100644 index 0000000..5534819 --- /dev/null +++ b/lib/cryptcheck/ssh/host.rb @@ -0,0 +1,11 @@ +module CryptCheck + module Ssh + class Host < CryptCheck::Host + private + + def server(*args) + Ssh::Server.new *args + end + end + end +end diff --git a/lib/cryptcheck/ssh/server.rb b/lib/cryptcheck/ssh/server.rb index 6d533b2..33780ec 100644 --- a/lib/cryptcheck/ssh/server.rb +++ b/lib/cryptcheck/ssh/server.rb @@ -1,117 +1,131 @@ require 'socket' module CryptCheck - module Ssh - class Server - TCP_TIMEOUT = 10 + module Ssh + class Server < CryptCheck::Server + include Ssh::Grade - attr_reader :ip, :port, :hostname, :kex, :encryption, :hmac, :compression, :key + TCP_TIMEOUT = 10 - KEX = { - 'curve25519-sha256@libssh.org' => :green, - 'ecdh-sha2-nistp521' => nil, # NIST - 'ecdh-sha2-nistp384' => nil, # NIST - 'ecdh-sha2-nistp256' => nil, # NIST - 'diffie-hellman-group-exchange-sha256' => :green, # DLP (PFS) - 'diffie-hellman-group-exchange-sha1' => :yellow, # DLP (PFS) - 'diffie-hellman-group14-sha1' => :yellow, # 2048 bits < 3072 bits - 'diffie-hellman-group1-sha1' => :red # 768 bits < 1024 bits - } + attr_reader :ip, :port, :hostname, :kex, :encryption, :hmac, :compression, :key - ENCRYPTION = { - 'chacha20-poly1305@openssh.com' => :green, - 'aes256-gcm@openssh.com' => :green, - 'aes128-gcm@openssh.com' => :green, - 'aes256-ctr' => nil, # CTR < GCM - 'aes192-ctr' => nil, # CTR < GCM - 'aes128-ctr' => nil, # CTR < GCM - 'aes256-cbc' => :yellow, # CBC - 'aes192-cbc' => :yellow, # CBC - 'aes128-cbc' => :yellow, # CBC - 'blowfish-cbc' => :yellow, # CBC - 'cast128-cbc' => :yellow, # CBC - '3des-cbc' => :red, # 3DES - 'arcfour' => :red, # RC4 - 'arcfour128' => :red, # RC4 - 'arcfour256' => :red # RC4 - } + KEX = { + 'curve25519-sha256@libssh.org' => :green, + 'ecdh-sha2-nistp521' => nil, # NIST + 'ecdh-sha2-nistp384' => nil, # NIST + 'ecdh-sha2-nistp256' => nil, # NIST + 'diffie-hellman-group-exchange-sha256' => :green, # DLP (PFS) + 'diffie-hellman-group-exchange-sha1' => :yellow, # DLP (PFS) + 'diffie-hellman-group14-sha1' => :yellow, # 2048 bits < 3072 bits + 'diffie-hellman-group1-sha1' => :red # 768 bits < 1024 bits + } - HMAC = { - 'hmac-sha2-512-etm@openssh.com' => :green, - 'hmac-sha2-256-etm@openssh.com' => :green, - 'hmac-sha2-512' => nil, - 'hmac-sha2-256' => nil, - 'hmac-sha1-etm@openssh.com' => :green, - 'hmac-sha1' => nil, - 'hmac-sha1-96-etm@openssh.com' => :red, # EXPORT - 'hmac-sha1-96' => :red, # EXPORT - 'hmac-ripemd160-etm@openssh.com' => :green, - 'hmac-ripemd160' => nil, - 'hmac-md5-etm@openssh.com' => :red, # MD5 - 'hmac-md5' => :red, # MD5 - 'hmac-md5-96-etm@openssh.com' => :red, # MD5 + EXPORT - 'hmac-md5-96' => :red, # MD5 + EXPORT - 'umac-128-etm@openssh.com' => :green, - 'umac-128@openssh.com' => nil, - 'umac-64-etm@openssh.com' => :red, # < 128 bits - 'umac-64@openssh.com' => :red # < 128 bits - } + ENCRYPTION = { + 'chacha20-poly1305@openssh.com' => :green, + 'aes256-gcm@openssh.com' => :green, + 'aes128-gcm@openssh.com' => :green, + 'aes256-ctr' => nil, # CTR < GCM + 'aes192-ctr' => nil, # CTR < GCM + 'aes128-ctr' => nil, # CTR < GCM + 'aes256-cbc' => :yellow, # CBC + 'aes192-cbc' => :yellow, # CBC + 'aes128-cbc' => :yellow, # CBC + 'blowfish-cbc' => :yellow, # CBC + 'cast128-cbc' => :yellow, # CBC + '3des-cbc' => :red, # 3DES + 'arcfour' => :red, # RC4 + 'arcfour128' => :red, # RC4 + 'arcfour256' => :red # RC4 + } - COMPRESSION = { - 'none' => nil, - 'zlib@openssh.com' => nil - } + HMAC = { + 'hmac-sha2-512-etm@openssh.com' => :green, + 'hmac-sha2-256-etm@openssh.com' => :green, + 'hmac-sha2-512' => nil, + 'hmac-sha2-256' => nil, + 'hmac-sha1-etm@openssh.com' => :red, # SHA-1 + 'hmac-sha1' => :red, # SHA-1 + 'hmac-sha1-96-etm@openssh.com' => :red, # EXPORT + SHA-1 + 'hmac-sha1-96' => :red, # EXPORT + SHA-1 + 'hmac-ripemd160-etm@openssh.com' => :green, + 'hmac-ripemd160' => nil, + 'hmac-md5-etm@openssh.com' => :red, # MD5 + 'hmac-md5' => :red, # MD5 + 'hmac-md5-96-etm@openssh.com' => :red, # EXPORT + MD5 + 'hmac-md5-96' => :red, # EXPORT + MD5 + 'umac-128-etm@openssh.com' => :green, + 'umac-128@openssh.com' => nil, + 'umac-64-etm@openssh.com' => :red, # < 128 bits + 'umac-64@openssh.com' => :red # < 128 bits + } - KEY = { - 'ssh-ed25519' => :green, - 'ssh-ed25519-cert-v01@openssh.com' => :green, - 'ecdsa-sha2-nistp256' => nil, # NIST - 'ecdsa-sha2-nistp384' => nil, # NIST - 'ecdsa-sha2-nistp521' => nil, # NIST - 'ssh-rsa' => :yellow, # RSA - 'ssh-dss' => :red, # DSA - 'ecdsa-sha2-nistp256-cert-v01@openssh.com' => nil, # NIST - 'ecdsa-sha2-nistp384-cert-v01@openssh.com' => nil, # NIST - 'ecdsa-sha2-nistp521-cert-v01@openssh.com' => nil, # NIST - 'ssh-rsa-cert-v01@openssh.com' => :yellow, # RSA - 'ssh-rsa-cert-v00@openssh.com' => :yellow, # RSA - 'ssh-dss-cert-v01@openssh.com' => :red, # DSA - 'ssh-dss-cert-v00@openssh.com' => :red, # DSA - } + COMPRESSION = { + 'none' => nil, + 'zlib@openssh.com' => nil + } - def initialize(hostname, _, ip, port=22) - @ip, @port, @hostname = ip, port, hostname + KEY = { + 'ssh-ed25519' => :green, + 'ssh-ed25519-cert-v01@openssh.com' => :green, + 'ecdsa-sha2-nistp256' => nil, # NIST + 'ecdsa-sha2-nistp384' => nil, # NIST + 'ecdsa-sha2-nistp521' => nil, # NIST + 'ssh-rsa' => :yellow, # RSA + 'ssh-dss' => :red, # DSA + 'ecdsa-sha2-nistp256-cert-v01@openssh.com' => nil, # NIST + 'ecdsa-sha2-nistp384-cert-v01@openssh.com' => nil, # NIST + 'ecdsa-sha2-nistp521-cert-v01@openssh.com' => nil, # NIST + 'ssh-rsa-cert-v01@openssh.com' => :yellow, # RSA + 'ssh-rsa-cert-v00@openssh.com' => :yellow, # RSA + 'ssh-dss-cert-v01@openssh.com' => :red, # DSA + 'ssh-dss-cert-v00@openssh.com' => :red, # DSA + } - Logger.info { name.colorize :blue } - kex = ::Socket.tcp ip, port, connect_timeout: TCP_TIMEOUT do |socket| - socket.readline - socket.write "SSH-2.0-CryptCheck\r\n" - Packet.read_kex_init socket - end + def initialize(hostname, ip, _, port = 22) + @ip, @port, @hostname = ip, port, hostname - @kex, @encryption, @hmac, @compression, @key = kex[:kex], kex[:encryption], kex[:mac], kex[:compression], kex[:host_key] + Logger.info { name.colorize :blue } + kex = ::Socket.tcp ip, port, connect_timeout: TCP_TIMEOUT do |socket| + socket.readline + socket.write "SSH-2.0-CryptCheck\r\n" + Packet.read_kex_init socket + end - Logger.info { '' } - @kex.each { |k| Logger.info { "Key exchange : #{k.colorize KEX[k]}" } } - Logger.info { '' } - @encryption.each { |e| Logger.info { "Encryption : #{e.colorize ENCRYPTION[e]}" } } - Logger.info { '' } - @hmac.each { |h| Logger.info { "HMAC : #{h.colorize HMAC[h]}" } } - Logger.info { '' } - @compression.each { |c| Logger.info { "Compression : #{c}" } } - Logger.info { '' } - @key.each { |k| Logger.info { "Key type : #{k.colorize KEY[k]}" } } - rescue => e - Logger.debug { "SSH not supportedĀ : #{e}" } - raise - end + @kex, @encryption, @hmac, @compression, @key = kex[:kex], kex[:encryption], + kex[:mac], kex[:compression], kex[:host_key] - private - def name - name = "#{@hostname || @ip}:#@port" - name += " [#@ip]" if @hostname - name - end - end - end + Logger.info { '' } + @kex.each { |k| Logger.info { "Key exchange : #{k.colorize KEX[k]}" } } + Logger.info { '' } + @encryption.each { |e| Logger.info { "Encryption : #{e.colorize ENCRYPTION[e]}" } } + Logger.info { '' } + @hmac.each { |h| Logger.info { "HMAC : #{h.colorize HMAC[h]}" } } + Logger.info { '' } + @compression.each { |c| Logger.info { "Compression : #{c}" } } + Logger.info { '' } + @key.each { |k| Logger.info { "Key type : #{k.colorize KEY[k]}" } } + rescue => e + Logger.debug { "SSH not supportedĀ : #{e}" } + raise + end + + def to_h + { + key: @kex, + encryption: @encryption, + hmac: @hmac, + compression: @compression, + key: @key + } + end + + private + + def name + name = "#{@hostname || @ip}:#@port" + name += " [#@ip]" if @hostname + name + end + end + end end diff --git a/lib/cryptcheck/tls.rb b/lib/cryptcheck/tls.rb index 94a7c4c..542fd24 100644 --- a/lib/cryptcheck/tls.rb +++ b/lib/cryptcheck/tls.rb @@ -1,30 +1,30 @@ -require 'erb' -require 'parallel' - module CryptCheck - module Tls - def self.analyze(hostname, port) - host = Host.new hostname, port - self.aggregate host - end + module Tls + autoload :Host, 'cryptcheck/tls/host' + autoload :Grade, 'cryptcheck/tls/grade' + + def self.analyze(hostname, port) + host = Host.new hostname, port + self.aggregate host + end - def self.aggregate(hosts) - hosts = [hosts] unless hosts.respond_to? :collect - hosts.inject([]) { |l, h| l + h.to_h } - end + def self.aggregate(hosts) + hosts = [hosts] unless hosts.respond_to? :collect + hosts.inject([]) { |l, h| l + h.to_h } + end - def self.key_to_s(key) - size, color = case key.type - when :ecc - ["#{key.group.curve_name} #{key.size}", :good] - when :rsa - [key.size, nil] - when :dsa - [key.size, :critical] - when :dh - [key.size, :warning] - end - "#{key.type.to_s.upcase.colorize color} #{size.to_s.colorize key.status} bits" - end - end + def self.key_to_s(key) + size, color = case key.type + when :ecc + ["#{key.group.curve_name} #{key.size}", :good] + when :rsa + [key.size, nil] + when :dsa + [key.size, :critical] + when :dh + [key.size, :warning] + end + "#{key.type.to_s.upcase.colorize color} #{size.to_s.colorize key.status} bits" + end + end end diff --git a/lib/cryptcheck/tls/grade.rb b/lib/cryptcheck/tls/grade.rb new file mode 100644 index 0000000..0492e46 --- /dev/null +++ b/lib/cryptcheck/tls/grade.rb @@ -0,0 +1,31 @@ +module CryptCheck + module Tls + module Grade + include CryptCheck::Grade + + private + + def calculate_grade + return :V unless self.valid? + return :T unless self.trusted? + + states = self.states + states = State.collect { |s| [s, State.state(states, s)] }.to_h + + State::BADS.each do |s| + return STATUS_GRADES[s] if states[s] + end + + grade = STATUS_GRADES[:default] + State::GOODS.each do |s| + state = states[s] + return grade if state == false + grade = STATUS_GRADES[s] + return grade if state == :some + grade = "#{grade}+".to_sym + end + grade + end + end + end +end diff --git a/lib/cryptcheck/tls/host.rb b/lib/cryptcheck/tls/host.rb index ebf4e8a..037e7a3 100644 --- a/lib/cryptcheck/tls/host.rb +++ b/lib/cryptcheck/tls/host.rb @@ -1,113 +1,10 @@ -require 'awesome_print' -AwesomePrint.force_colors = true -require 'timeout' - module CryptCheck module Tls - class AnalysisFailure - attr_reader :error - - def initialize(error) - @error = error - end - - def to_s - @error.to_s - end - end - - class TooLongAnalysis < AnalysisFailure - def initialize - super "Too long analysis (max #{Host::MAX_ANALYSIS_DURATION.humanize})" - end - end - - class Host - MAX_ANALYSIS_DURATION = 600 - - attr_reader :servers, :error - - def initialize(hostname, port) - @hostname, @port = hostname, port - - first = true - @servers = resolve.collect do |args| - _, ip = args - first ? (first = false) : Logger.info { '' } - result = begin - server = ::Timeout.timeout MAX_ANALYSIS_DURATION do - server(*args) - end - Logger.info '' - Logger.info { "Grade : #{server.grade.to_s.colorize server.grade_status}" } - Logger.info { server.states.ai } - server - rescue Engine::TLSException, Engine::ConnectionError, Engine::Timeout => e - # Logger.error { e.backtrace } - Logger.error { e } - { error: AnalysisFailure.new(e) } - rescue ::Timeout::Error - # Logger.error { e.backtrace } - Logger.error { e } - { error: TooLongAnalysis.new } - end - [[@hostname, ip, @port], result] - end.to_h - rescue => e - # Logger.error { e.backtrace } - Logger.error { e } - @error = e - # raise - end - - def key - { hostname: @hostname, port: @port } - end - - def to_h - if @error - target = [self.key.merge(error: @error.to_s)] - else - target = @servers.collect do |host, server| - hostname, ip, port = host - host = { - hostname: hostname, - ip: ip, - port: port - } - case server - when Server - host[:handshakes] = server.to_h - host[:states] = server.states - host[:grade] = server.grade - else - host[:error] = server[:error].to_s - end - host - end - end - target - end - + class Host < CryptCheck::Host private - def resolve - begin - begin - ip = IPAddr.new @hostname - return [[nil, ip.to_s, ip.family, @port]] - rescue IPAddr::InvalidAddressError - end - ::Addrinfo.getaddrinfo(@hostname, nil, nil, :STREAM) - .collect { |a| [@hostname, a.ip_address, a.afamily, @port] } - end.reject do |family, *_| - (ENV['DISABLE_IPv6'] && family == Socket::AF_INET6) || - (ENV['DISABLE_IPv4'] && family == Socket::AF_INET) - end - end - def server(*args) - TcpServer.new *args + Server.new *args end end end diff --git a/lib/cryptcheck/tls/https/host.rb b/lib/cryptcheck/tls/https/host.rb index 87ab4c7..83ccaf7 100644 --- a/lib/cryptcheck/tls/https/host.rb +++ b/lib/cryptcheck/tls/https/host.rb @@ -1,7 +1,7 @@ module CryptCheck module Tls module Https - class Host < Tls::Host + class Host < CryptCheck::Host private def server(*args) diff --git a/lib/cryptcheck/tls/https/server.rb b/lib/cryptcheck/tls/https/server.rb index 103c2e5..0f640f0 100644 --- a/lib/cryptcheck/tls/https/server.rb +++ b/lib/cryptcheck/tls/https/server.rb @@ -3,7 +3,7 @@ require 'httparty' module CryptCheck module Tls module Https - class Server < Tls::TcpServer + class Server < Tls::Server attr_reader :hsts def initialize(hostname, ip, family, port = 443) diff --git a/lib/cryptcheck/tls/server.rb b/lib/cryptcheck/tls/server.rb index 2b38c70..9253f64 100644 --- a/lib/cryptcheck/tls/server.rb +++ b/lib/cryptcheck/tls/server.rb @@ -1,6 +1,6 @@ module CryptCheck module Tls - class Server + class Server < CryptCheck::TcpServer Method.each do |method| class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1 def #{method.to_sym.downcase}? @@ -113,19 +113,5 @@ module CryptCheck include Engine include Grade end - - class TcpServer < Server - private - def sock_type - ::Socket::SOCK_STREAM - end - end - - class UdpServer < Server - private - def sock_type - ::Socket::SOCK_DGRAM - end - end end end diff --git a/lib/cryptcheck/tls/smtp/host.rb b/lib/cryptcheck/tls/smtp/host.rb index 7a244dd..f8d36e7 100644 --- a/lib/cryptcheck/tls/smtp/host.rb +++ b/lib/cryptcheck/tls/smtp/host.rb @@ -1,7 +1,7 @@ module CryptCheck module Tls module Smtp - class Host < Tls::Host + class Host < CryptCheck::Host private def server(*args) diff --git a/lib/cryptcheck/tls/smtp/server.rb b/lib/cryptcheck/tls/smtp/server.rb index 3ef179e..6c63d59 100644 --- a/lib/cryptcheck/tls/smtp/server.rb +++ b/lib/cryptcheck/tls/smtp/server.rb @@ -1,7 +1,7 @@ module CryptCheck module Tls module Smtp - class Server < Tls::TcpServer + class Server < Tls::Server def ssl_connect(socket, context, method, &block) socket.recv 1024 socket.write "EHLO #{Socket.gethostbyname(Socket.gethostname).first}\r\n" diff --git a/lib/cryptcheck/tls/xmpp/host.rb b/lib/cryptcheck/tls/xmpp/host.rb index 2ff2421..a71d7b3 100644 --- a/lib/cryptcheck/tls/xmpp/host.rb +++ b/lib/cryptcheck/tls/xmpp/host.rb @@ -1,20 +1,20 @@ module CryptCheck - module Tls - module Xmpp - class Host < Tls::Host - attr_reader :domain + module Tls + module Xmpp + class Host < CryptCheck::Host + attr_reader :domain - def initialize(*args, domain: nil, type: :s2s) - @domain, @type = domain, type - super *args - end + def initialize(*args, domain: nil, type: :s2s) + @domain, @type = domain, type + super *args + end - private + private - def server(*args) - Xmpp::Server.new *args, domain: @domain, type: @type - end - end - end - end + def server(*args) + Xmpp::Server.new *args, domain: @domain, type: @type + end + end + end + end end diff --git a/lib/cryptcheck/tls/xmpp/server.rb b/lib/cryptcheck/tls/xmpp/server.rb index 8e3d715..dacd976 100644 --- a/lib/cryptcheck/tls/xmpp/server.rb +++ b/lib/cryptcheck/tls/xmpp/server.rb @@ -3,7 +3,7 @@ require 'nokogiri' module CryptCheck module Tls module Xmpp - class Server < Tls::TcpServer + class Server < Tls::Server attr_reader :domain def initialize(hostname, ip, family, port = nil, domain: nil, type: :s2s)