More unit tests (XMPP)

v1
Aeris 7 years ago
parent 5aa9a975fe
commit f1c14eef39
  1. 2
      bin/check_xmpp.rb
  2. 2
      lib/cryptcheck/tls/server.rb
  3. 8
      lib/cryptcheck/tls/xmpp.rb
  4. 8
      lib/cryptcheck/tls/xmpp/server.rb
  5. 19
      spec/cryptcheck/https_spec.rb
  6. 20
      spec/cryptcheck/support/analysis.rb
  7. 7
      spec/cryptcheck/tls_spec.rb
  8. 72
      spec/cryptcheck/xmpp_spec.rb
  9. 135
      spec/helpers.rb

@ -11,5 +11,5 @@ if ::File.exist? file
::CryptCheck::Tls::Xmpp.analyze_file file, "output/#{name}.html"
else
::CryptCheck::Logger.level = ENV['LOG'] || :info
::CryptCheck::Tls::Xmpp.analyze ARGV[0], type: (ARGV[1] || :s2s).to_sym
::CryptCheck::Tls::Xmpp.analyze_domain ARGV[0], type: (ARGV[1] || :s2s).to_sym
end

@ -24,7 +24,7 @@ module CryptCheck
end
class TLSTimeout < Timeout
end
class ConnectionError < Exception
class ConnectionError < ::StandardError
end
attr_reader :family, :ip, :port, :hostname, :prefered_ciphers, :cert, :cert_valid, :cert_trusted, :dh

@ -6,13 +6,7 @@ module CryptCheck
module Xmpp
def self.analyze(host, port=nil, domain: nil, type: :s2s)
domain ||= host
::CryptCheck.analyze host, port do |family, ip, host|
s = Server.new family, ip, port, hostname: host, type: type, domain: domain
g = Grade.new s
Logger.info { '' }
g.display
g
end
::CryptCheck.analyze host, port, Server, Grade, domain: domain, type: type
end
def self.analyze_domain(domain, type: :s2s)

@ -17,7 +17,7 @@ module CryptCheck
when :c2s
5222
end unless port
super family, ip, port, hostname: hostname
super hostname, family, ip, port
Logger.info { '' }
Logger.info { self.required? ? 'Required'.colorize(:green) : 'Not required'.colorize(:yellow) }
end
@ -29,13 +29,13 @@ module CryptCheck
when :c2s then
'jabber:client'
end
socket.write "<?xml version='1.0' ?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='#{type}' to='#{@domain}' version='1.0'>"
socket.puts "<?xml version='1.0' ?><stream:stream xmlns:stream='http://etherx.jabber.org/streams' xmlns='#{type}' to='#{@domain}' version='1.0'>"
response = ''
loop do
response += socket.recv 1024
xml = ::Nokogiri::XML response
error = xml.xpath '//stream:error'
raise Exception, error.text unless error.empty?
raise ConnectionError, error.first.child.to_s unless error.empty?
unless xml.xpath('//stream:features').empty?
response = xml
break
@ -43,7 +43,7 @@ module CryptCheck
end
starttls = response.xpath '//tls:starttls', tls: TLS_NAMESPACE
raise TLSNotAvailableException unless starttls
@required = !starttls.xpath('//tls:required', tls: TLS_NAMESPACE).nil?
@required = !starttls.xpath('//tls:required', tls: TLS_NAMESPACE).empty?
socket.write "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />\r\n"
response = ::Nokogiri::XML socket.recv 4096
raise TLSNotAvailableException unless response.xpath '//tls:proceed', tls: TLS_NAMESPACE

@ -1,13 +1,10 @@
describe CryptCheck::Tls::Https do
def process
proc do |socket|
socket.print [
'HTTP/1.1 200 OK',
'Content-Type: text/plain',
'Content-Length: 0',
'Connection: close'
].join "\r\n"
end
def server(*args, **kargs, &block)
tls_serv *args, **kargs, &block
end
def plain_server(*args, **kargs, &block)
plain_serv *args, **kargs, &block
end
def analyze(*args)
@ -18,7 +15,7 @@ describe CryptCheck::Tls::Https do
describe '#hsts?' do
it 'has no hsts' do
grades = server host: '127.0.0.1', process: process do
grades = server host: '127.0.0.1' do
analyze '127.0.0.1', 5000
end
@ -48,7 +45,7 @@ describe CryptCheck::Tls::Https do
describe '#hsts_long?' do
it 'has no hsts' do
grades = server host: '127.0.0.1', process: process do
grades = server host: '127.0.0.1' do
analyze '127.0.0.1', 5000
end

@ -1,7 +1,7 @@
RSpec.shared_examples :analysis do
describe '#analyze' do
it 'return 1 grade with IPv4' do
grades = server host: '127.0.0.1', process: process do
grades = server host: '127.0.0.1' do
analyze '127.0.0.1', 5000
end
@ -10,7 +10,7 @@ RSpec.shared_examples :analysis do
end
it 'return 1 grade with IPv6' do
grades = server host: '::1', process: process do
grades = server host: '::1' do
analyze '::1', 5000
end
@ -24,7 +24,7 @@ RSpec.shared_examples :analysis do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end
grades = server host: '::', process: process do
grades = server host: '::' do
analyze 'localhost', 5000
end
@ -36,7 +36,7 @@ RSpec.shared_examples :analysis do
allow(Addrinfo).to receive(:getaddrinfo).with('localhost', nil, nil, :STREAM)
.and_raise SocketError, 'getaddrinfo: Name or service not known'
grades = server process: process do
grades = server do
analyze 'localhost', 5000
end
@ -48,7 +48,7 @@ RSpec.shared_examples :analysis do
stub_const 'CryptCheck::MAX_ANALYSIS_DURATION', 1
allow(CryptCheck::Tls::Server).to receive(:new) { sleep 2 }
grades = server process: process do
grades = server do
analyze 'localhost', 5000
end
@ -62,7 +62,7 @@ RSpec.shared_examples :analysis do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end
grades = server host: '::1', process: process do
grades = server host: '::1' do
analyze 'localhost', 5000
end
@ -84,7 +84,7 @@ RSpec.shared_examples :analysis do
original.call *args, &block
end
grades = server host: '::', process: process do
grades = server host: '::' do
analyze 'localhost', 5000
end
@ -106,7 +106,7 @@ RSpec.shared_examples :analysis do
original.call *args, &block
end
grades = server host: '::', process: process do
grades = server host: '::' do
analyze 'localhost', 5000
end
@ -122,8 +122,8 @@ RSpec.shared_examples :analysis do
addresses.collect { |a| Addrinfo.new Socket.sockaddr_in(nil, a) }
end
grades = plain_server host: '127.0.0.1', process: process do
server host: '::1', process: process do
grades = plain_server host: '127.0.0.1' do
server host: '::1' do
analyze 'localhost', 5000
end
end

@ -1,5 +1,10 @@
describe CryptCheck::Tls do
def process
def server(*args, &block)
tls_serv *args, &block
end
def plain_server(*args, &block)
plain_serv *args, &block
end
def analyze(*args)

@ -0,0 +1,72 @@
describe CryptCheck::Tls::Xmpp do
def server(*args, **kargs, &block)
kargs[:plain_process] = proc do |socket|
socket.gets
socket.puts "<?xml version='1.0'?><stream:stream xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='localhost' id='' xml:lang='en' xmlns='jabber:server'><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls><dialback xmlns='urn:xmpp:features:dialback'/></stream:features>"
socket.gets
socket.puts "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />"
true
end unless kargs.include? :plain_process
starttls_serv *args, **kargs, &block
end
def plain_server(*args, **kargs, &block)
kargs[:plain_process] = proc do |socket|
socket.gets
socket.puts "<?xml version='1.0'?><stream:stream xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='localhost' id='' xml:lang='en' xmlns='jabber:server'><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'></starttls><dialback xmlns='urn:xmpp:features:dialback'/></stream:features>"
socket.gets
socket.puts "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />"
false
end unless kargs.include? :plain_process
starttls_serv *args, **kargs, &block
end
def analyze(*args)
CryptCheck::Tls::Xmpp.analyze *args, type: :s2s
end
include_examples :analysis do
it 'return error on XMPP error' do
plain_process = proc do |socket|
socket.gets
socket.puts "<?xml version='1.0'?><stream:stream xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='localhost' id='' xml:lang='en' xmlns='jabber:server'><stream:error><invalid-namespace xmlns='urn:ietf:params:xml:ns:xmpp-streams'/></stream:error>"
false
end
grades = server host: '127.0.0.1', plain_process: plain_process do
analyze '127.0.0.1', 5000
end
expect_grade_error grades, '127.0.0.1', '127.0.0.1', 5000,
'<invalid-namespace xmlns="urn:ietf:params:xml:ns:xmpp-streams"/>'
end
end
describe '#required?' do
it 'has TLS not required' do
grades = server host: '127.0.0.1' do
analyze '127.0.0.1', 5000
end
_, server = expect_grade grades, '127.0.0.1', '127.0.0.1', 5000, :ipv4
expect(server.required?).to be false
end
it 'has TLS required' do
plain_process = proc do |socket|
socket.gets
socket.puts "<?xml version='1.0'?><stream:stream xmlns:db='jabber:server:dialback' xmlns:stream='http://etherx.jabber.org/streams' version='1.0' from='localhost' id='' xml:lang='en' xmlns='jabber:server'><stream:features><starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'><required/></starttls><dialback xmlns='urn:xmpp:features:dialback'/></stream:features>"
socket.gets
socket.puts "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls' />"
true
end
grades = server host: '127.0.0.1', plain_process: plain_process do
analyze '127.0.0.1', 5000
end
_, server = expect_grade grades, '127.0.0.1', '127.0.0.1', 5000, :ipv4
expect(server.required?).to be true
end
end
end

@ -52,28 +52,7 @@ module Helpers
cert.sign key, OpenSSL::Digest::SHA512.new
end
def server(key: 'rsa-1024', domain: 'localhost', # Key & certificate
host: '127.0.0.1', port: 5000, # Binding
version: :TLSv1_2, ciphers: 'AES128-SHA', # TLS version and ciphers
dh: 1024, ecdh: 'secp256r1', # DHE & ECDHE
process: nil)
key = key key
cert = certificate key, domain
context = OpenSSL::SSL::SSLContext.new version
context.cert = cert
context.key = key
context.ciphers = ciphers
if dh
dh = dh dh
context.tmp_dh_callback = proc { dh }
end
if ecdh
ecdh = key ecdh
context.tmp_ecdh_callback = proc { ecdh }
end
def serv(server, process, &block)
IO.pipe do |stop_pipe_r, stop_pipe_w|
threads = []
@ -81,16 +60,14 @@ module Helpers
started = ConditionVariable.new
threads << Thread.start do
tcp_server = TCPServer.new host, port
ssl_server = OpenSSL::SSL::SSLServer.new tcp_server, context
mutex.synchronize { started.signal }
loop do
readable, = IO.select [ssl_server, stop_pipe_r]
readable, = IO.select [server, stop_pipe_r]
break if readable.include? stop_pipe_r
begin
socket = ssl_server.accept
socket = server.accept
begin
process.call socket if process
ensure
@ -99,13 +76,12 @@ module Helpers
rescue
end
end
ssl_server.close
tcp_server.close
server.close
end
mutex.synchronize { started.wait mutex }
begin
yield
block.call if block
ensure
stop_pipe_w.close
threads.each &:join
@ -113,41 +89,82 @@ module Helpers
end
end
def plain_server(host: '127.0.0.1', port: 5000, process: nil)
IO.pipe do |stop_pipe_r, stop_pipe_w|
threads = []
def context(key: 'rsa-1024', domain: 'localhost', # Key & certificate
version: :TLSv1_2, ciphers: 'AES128-SHA', # TLS version and ciphers
dh: 1024, ecdh: 'secp256r1') # DHE & ECDHE
key = key key
cert = certificate key, domain
mutex = Mutex.new
started = ConditionVariable.new
context = OpenSSL::SSL::SSLContext.new version
context.cert = cert
context.key = key
context.ciphers = ciphers
threads << Thread.start do
tcp_server = TCPServer.new host, port
mutex.synchronize { started.signal }
if dh
dh = dh dh
context.tmp_dh_callback = proc { dh }
end
if ecdh
ecdh = key ecdh
context.tmp_ecdh_callback = proc { ecdh }
end
loop do
readable, = IO.select [tcp_server, stop_pipe_r]
break if readable.include? stop_pipe_r
context
end
begin
socket = tcp_server.accept
begin
process.call socket if process
ensure
socket.close
end
rescue
end
def tls_serv(key: 'rsa-1024', domain: 'localhost', # Key & certificate
version: :TLSv1_2, ciphers: 'AES128-SHA', # TLS version and ciphers
dh: 1024, ecdh: 'secp256r1', # DHE & ECDHE
host: '127.0.0.1', port: 5000, # Binding
process: nil, &block)
context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh)
tcp_server = TCPServer.new host, port
tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
begin
serv tls_server, process, &block
ensure
tls_server.close
tcp_server.close
end
end
def plain_serv(host: '127.0.0.1', port: 5000, process: nil, &block)
tcp_server = TCPServer.new host, port
begin
serv tcp_server, process, &block
ensure
tcp_server.close
end
end
def starttls_serv(key: 'rsa-1024', domain: 'localhost', # Key & certificate
version: :TLSv1_2, ciphers: 'AES128-SHA', # TLS version and ciphers
dh: 1024, ecdh: 'secp256r1', # DHE & ECDHE
host: '127.0.0.1', port: 5000, # Binding
plain_process: nil, process: nil, &block)
context = context(key: key, domain: domain, version: version, ciphers: ciphers, dh: dh, ecdh: ecdh)
tcp_server = TCPServer.new host, port
tls_server = OpenSSL::SSL::SSLServer.new tcp_server, context
tls_server.start_immediately = false
internal_process = proc do |socket|
accept = false
accept = plain_process.call socket if plain_process
if accept
tls_socket = socket.accept
begin
process.call tls_socket if process
ensure
socket.close
end
tcp_server.close
end
end
mutex.synchronize { started.wait mutex }
begin
yield
ensure
stop_pipe_w.close
threads.each &:join
end
begin
serv tls_server, internal_process, &block
ensure
tls_server.close
tcp_server.close
end
end
@ -157,7 +174,7 @@ module Helpers
def expect_grade(grades, host, ip, port, family)
grade = grade grades, host, ip, port
expect(grade).to_not be nil
expect(grade).to be_a CryptCheck::Tls::Grade
server = grade.server
expect(server).to be_a CryptCheck::Tls::Server
expect(server.hostname).to eq host

Loading…
Cancel
Save