選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

cryptcheck.rb 3.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. require 'colorize'
  2. require 'ipaddr'
  3. require 'timeout'
  4. require 'yaml'
  5. require 'cryptcheck/tls/fixture'
  6. module CryptCheck
  7. MAX_ANALYSIS_DURATION = 120
  8. PARALLEL_ANALYSIS = 10
  9. class AnalysisFailure
  10. attr_reader :error
  11. def initialize(error)
  12. @error = error
  13. end
  14. def to_s
  15. @error.to_s
  16. end
  17. end
  18. autoload :Logger, 'cryptcheck/logger'
  19. autoload :Tls, 'cryptcheck/tls'
  20. module Tls
  21. autoload :Cipher, 'cryptcheck/tls/cipher'
  22. autoload :Server, 'cryptcheck/tls/server'
  23. autoload :TcpServer, 'cryptcheck/tls/server'
  24. autoload :UdpServer, 'cryptcheck/tls/server'
  25. autoload :TlsNotSupportedServer, 'cryptcheck/tls/server'
  26. autoload :Grade, 'cryptcheck/tls/grade'
  27. autoload :TlsNotSupportedGrade, 'cryptcheck/tls/grade'
  28. autoload :Https, 'cryptcheck/tls/https'
  29. module Https
  30. autoload :Server, 'cryptcheck/tls/https/server'
  31. autoload :Grade, 'cryptcheck/tls/https/grade'
  32. end
  33. autoload :Xmpp, 'cryptcheck/tls/xmpp'
  34. module Xmpp
  35. autoload :Server, 'cryptcheck/tls/xmpp/server'
  36. autoload :Grade, 'cryptcheck/tls/xmpp/grade'
  37. end
  38. autoload :Smtp, 'cryptcheck/tls/smtp'
  39. module Smtp
  40. autoload :Server, 'cryptcheck/tls/smtp/server'
  41. autoload :Grade, 'cryptcheck/tls/smtp/grade'
  42. end
  43. end
  44. autoload :Ssh, 'cryptcheck/ssh'
  45. module Ssh
  46. autoload :Packet, 'cryptcheck/ssh/packet'
  47. autoload :Server, 'cryptcheck/ssh/server'
  48. autoload :SshNotSupportedServer, 'cryptcheck/ssh/server'
  49. autoload :Grade, 'cryptcheck/ssh/grade'
  50. end
  51. private
  52. def self.addresses(host)
  53. begin
  54. ip = IPAddr.new host
  55. return [[ip.family, ip.to_s, nil]]
  56. rescue IPAddr::InvalidAddressError
  57. end
  58. ::Addrinfo.getaddrinfo(host, nil, nil, :STREAM)
  59. .collect { |a| [a.afamily, a.ip_address, host] }
  60. end
  61. def self.analyze_addresses(host, addresses, port, server, grade, *args, **kargs)
  62. first = true
  63. addresses.collect do |family, ip|
  64. first ? (first = false) : Logger.info { '' }
  65. key = [host, ip, port]
  66. a = [host, family, ip, port, *args]
  67. begin
  68. ::Timeout::timeout MAX_ANALYSIS_DURATION do
  69. s = if kargs.empty?
  70. server.new *a
  71. else
  72. server.new *a, **kargs
  73. end
  74. g = grade.new s
  75. Logger.info { '' }
  76. g.display
  77. [key, g]
  78. end
  79. rescue Exception => e
  80. e = "Too long analysis (max #{MAX_ANALYSIS_DURATION.humanize})" if e.message == 'execution expired'
  81. Logger.error e
  82. [key, AnalysisFailure.new(e)]
  83. end
  84. end.to_h
  85. end
  86. def self.analyze(host, port, server, grade, *args, **kargs)
  87. addresses = begin
  88. addresses host
  89. rescue ::SocketError => e
  90. Logger::error e
  91. return AnalysisFailure.new "Unable to resolve #{host}"
  92. end
  93. analyze_addresses host, addresses, port, server, grade, *args, **kargs
  94. end
  95. def self.analyze_hosts(hosts, template, output, groups: nil, &block)
  96. results = {}
  97. semaphore = ::Mutex.new
  98. ::Parallel.each hosts, progress: 'Analysing', in_threads: PARALLEL_ANALYSIS, finish: lambda { |item, _, _| puts item[1] } do |description, host|
  99. #hosts.each do |description, host|
  100. result = block.call host.strip
  101. semaphore.synchronize do
  102. if results.include? description
  103. results[description] << result
  104. else
  105. results[description] = [result]
  106. end
  107. end
  108. end
  109. results = ::Hash[groups.collect { |g| [g, results[g]] }] if groups
  110. results.each do |d, _|
  111. results[d].sort! do |a, b|
  112. cmp = score(a) <=> score(b)
  113. if cmp == 0
  114. cmp = b.score <=> a.score
  115. if cmp == 0
  116. cmp = a.server.hostname <=> b.server.hostname
  117. end
  118. end
  119. cmp
  120. end
  121. end
  122. ::File.write output, ::ERB.new(::File.read template).result(binding)
  123. end
  124. def self.analyze_file(input, template, output, &block)
  125. config = ::YAML.load_file input
  126. hosts = []
  127. groups = []
  128. config.each do |c|
  129. d, hs = c['description'], c['hostnames']
  130. groups << d
  131. hs.each { |host| hosts << [d, host] }
  132. end
  133. self.analyze_hosts hosts, template, output, groups: groups, &block
  134. end
  135. private
  136. SCORES = %w(A+ A A- B C D E F T M X)
  137. def self.score(a)
  138. SCORES.index a.grade
  139. end
  140. end