Class: TLSPretense::TestHarness::TestListener
- Inherits:
-
PacketThief::Handlers::SSLSmartProxy
- Object
- EM::Connection
- PacketThief::Handlers::AbstractSSLHandler
- PacketThief::Handlers::SSLServer
- PacketThief::Handlers::SSLTransparentProxy
- PacketThief::Handlers::SSLSmartProxy
- TLSPretense::TestHarness::TestListener
- Defined in:
- lib/tlspretense/test_harness/test_listener.rb
Overview
TestListener is the real workhorse used by SSLTestCases. It builds on the SSLSmartProxy from PacketThief in order to intercept and forward SSL connections. It uses SSLSmartProxy because SSLSmartProxy provides a default behavior where it grabs the remote certificate from the destination and re-signs it before presenting it to the client.
TestListener expands on this by presenting the configured test chain instead of the re-signed remote certificate when the destination corresponds to the hostname the test suite is testing off of.
Instance Attribute Summary
Attributes inherited from PacketThief::Handlers::SSLSmartProxy
Attributes inherited from PacketThief::Handlers::SSLTransparentProxy
#buffer, #client, #client_host, #client_port, #closed, #dest, #dest_ctx, #dest_host, #dest_hostname, #dest_port
Attributes inherited from PacketThief::Handlers::SSLServer
Attributes inherited from PacketThief::Handlers::AbstractSSLHandler
#ctx, #sni_hostname, #sslsocket, #tcpsocket
Class Method Summary collapse
-
.cert_matches_host(cert, hostname) ⇒ Object
Return true if cert’s CNAME or subjectAltName matches hostname, otherwise return false.
Instance Method Summary collapse
-
#check_for_hosttotest(actx) ⇒ Object
Replaces the certificates used in the SSLContext with the test certificates if the destination matches the hostname we wish to test against.
-
#client_recv(data) ⇒ Object
client_recv means that the client sent data over the TLS connection, meaning they definately trusted our certificate chain.
-
#initialize(tcpsocket, test_manager) ⇒ TestListener
constructor
For all hosts that do not match hosttotest, we currently use the cacert and re-sign the original cert provided by the actual host.
-
#post_init ⇒ Object
Checks whether the initial original destination certificate (without SNI hostname) matches the test hostname.
-
#servername_cb(sslsock, hostname) ⇒ Object
Checks whether the original destination certificate after we handle the SNI hostname matches the test hostname.
-
#tls_failed_handshake(e) ⇒ Object
If the handshake failed, then the client rejected our cert chain.
-
#tls_successful_handshake ⇒ Object
If the client completes connecting, we might consider that trusting our certificate chain.
-
#unbind ⇒ Object
Report our result.
Methods inherited from PacketThief::Handlers::SSLSmartProxy
#doctor_cert, #lookup_cert, #preflight_for_cert
Methods inherited from PacketThief::Handlers::SSLTransparentProxy
#_send_buffer, #client_closed, #client_connected, #client_handshake_failed, #connect_to_dest, #dest_cert_chain, #dest_closed, #dest_connected, #dest_handshake_failed, #dest_recv, #receive_data, #send_to_client, #send_to_dest
Methods inherited from PacketThief::Handlers::SSLServer
Methods inherited from PacketThief::Handlers::AbstractSSLHandler
#close_connection, #close_connection_after_writing, #notify_readable, #notify_writable, #receive_data, #send_data, #tls_begin, #write_buffer, #write_buffer=
Methods included from PacketThief::Logging
Constructor Details
#initialize(tcpsocket, test_manager) ⇒ TestListener
For all hosts that do not match hosttotest, we currently use the cacert and re-sign the original cert provided by the actual host. This will cause issues with certificate revocation.
-
cacert [OpenSSL::X509::Certificate] A CA that the client should trust.
-
cakey [OpenSSL::PKey::PKey] The CA’s key, needed for resigning. It will also be the key used by the resigned certificates.
-
hosttotest [String] The hostname we want to apply the test chain to.
-
chaintotest [Array<OpenSSL::X509Certificate>] A chain of certs to present when the client attempts to connect to hostname.
-
keytotest [OpenSSL::PKey::PKey] The key corresponding to the leaf node in chaintotest.
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 28 def initialize(tcpsocket, test_manager) @test_manager = test_manager if @test_manager.paused? @paused = true else @paused = false @test = @test_manager.current_test @hosttotest = @test.hosttotest chain = @test.certchain.dup @hostcert = chain.shift @hostkey = @test.keychain[0] @extrachain = chain end # Use the goodca for hosts we don't care to test against. super(tcpsocket, @test_manager.goodcacert, @test_manager.goodcakey) @test_status = :running @testing_host = false end |
Class Method Details
.cert_matches_host(cert, hostname) ⇒ Object
Return true if cert’s CNAME or subjectAltName matches hostname, otherwise return false.
87 88 89 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 87 def self.cert_matches_host(cert, hostname) OpenSSL::SSL.verify_certificate_identity(cert, hostname) end |
Instance Method Details
#check_for_hosttotest(actx) ⇒ Object
Replaces the certificates used in the SSLContext with the test certificates if the destination matches the hostname we wish to test against. Otherwise, it leaves the context alone.
Additionally, if it matches, it sets @testing_host to true to check whether the test succeeds or not.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 70 def check_for_hosttotest(actx) if @paused logdebug "Testing is paused, not checking whether this is the host to test", :certcubject => actx.cert.subject elsif TestListener.cert_matches_host(actx.cert, @hosttotest) logdebug "Destination matches host-to-test", :hosttotest => @hosttotest, :certsubject => actx.cert.subject, :testname => @test.id actx.cert = @hostcert actx.key = @hostkey actx.extra_chain_cert = @extrachain @testing_host = true else logdebug "Destination does not match host-to-test", :hosttotest => @hosttotest, :certsubject => actx.cert.subject end actx end |
#client_recv(data) ⇒ Object
client_recv means that the client sent data over the TLS connection, meaning they definately trusted our certificate chain.
129 130 131 132 133 134 135 136 137 138 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 129 def client_recv(data) if @testing_host @test_status = :sentdata if @test_manager.testing_method == 'senddata' @test_manager.test_completed(@test, @test_status) @testing_host = false end end super(data) end |
#post_init ⇒ Object
Checks whether the initial original destination certificate (without SNI hostname) matches the test hostname. We do this with post_init to have the check happen after the parent class already added a re-signed certificate to @ctx.
53 54 55 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 53 def post_init check_for_hosttotest(@ctx) end |
#servername_cb(sslsock, hostname) ⇒ Object
Checks whether the original destination certificate after we handle the SNI hostname matches the test hostname. Super already replaced the context with a certificate based on the remote host’s certificate.
60 61 62 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 60 def servername_cb(sslsock, hostname) check_for_hosttotest(super(sslsock, hostname)) end |
#tls_failed_handshake(e) ⇒ Object
If the handshake failed, then the client rejected our cert chain.
107 108 109 110 111 112 113 114 115 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 107 def tls_failed_handshake(e) super logdebug "failed handshake" if @testing_host @test_status = :rejected @test_manager.test_completed(@test, @test_status) @testing_host = false end end |
#tls_successful_handshake ⇒ Object
If the client completes connecting, we might consider that trusting our certificate chain. However, at least Java’s SSL client classes don’t reject until after completing the handshake.
94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 94 def tls_successful_handshake super logdebug "successful handshake" if @testing_host @test_status = :connected if @test_manager.testing_method == 'tlshandshake' @test_manager.test_completed(@test, @test_status) @testing_host = false end end end |
#unbind ⇒ Object
Report our result.
118 119 120 121 122 123 124 125 |
# File 'lib/tlspretense/test_harness/test_listener.rb', line 118 def unbind super logdebug "unbind" if @testing_host @test_manager.test_completed(@test, @test_status) @testing_host = false end end |