tlspretense 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/.document +6 -0
  2. data/.gitignore +7 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +41 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.rdoc +231 -0
  8. data/Rakefile +44 -0
  9. data/bin/makeder.sh +6 -0
  10. data/bin/tlspretense +7 -0
  11. data/bin/view.sh +3 -0
  12. data/doc/general_setup.rdoc +288 -0
  13. data/doc/linux_setup.rdoc +64 -0
  14. data/lib/certmaker.rb +61 -0
  15. data/lib/certmaker/certificate_factory.rb +106 -0
  16. data/lib/certmaker/certificate_suite_generator.rb +120 -0
  17. data/lib/certmaker/ext_core/hash_indifferent_fetch.rb +12 -0
  18. data/lib/certmaker/runner.rb +27 -0
  19. data/lib/certmaker/tasks.rb +20 -0
  20. data/lib/packetthief.rb +167 -0
  21. data/lib/packetthief/handlers.rb +14 -0
  22. data/lib/packetthief/handlers/abstract_ssl_handler.rb +249 -0
  23. data/lib/packetthief/handlers/proxy_redirector.rb +26 -0
  24. data/lib/packetthief/handlers/ssl_client.rb +87 -0
  25. data/lib/packetthief/handlers/ssl_server.rb +174 -0
  26. data/lib/packetthief/handlers/ssl_smart_proxy.rb +143 -0
  27. data/lib/packetthief/handlers/ssl_transparent_proxy.rb +225 -0
  28. data/lib/packetthief/handlers/transparent_proxy.rb +183 -0
  29. data/lib/packetthief/impl.rb +11 -0
  30. data/lib/packetthief/impl/ipfw.rb +140 -0
  31. data/lib/packetthief/impl/manual.rb +54 -0
  32. data/lib/packetthief/impl/netfilter.rb +109 -0
  33. data/lib/packetthief/impl/pf_divert.rb +168 -0
  34. data/lib/packetthief/impl/pf_rdr.rb +192 -0
  35. data/lib/packetthief/logging.rb +49 -0
  36. data/lib/packetthief/redirect_rule.rb +29 -0
  37. data/lib/packetthief/util.rb +36 -0
  38. data/lib/ssl_test.rb +21 -0
  39. data/lib/ssl_test/app_context.rb +17 -0
  40. data/lib/ssl_test/certificate_manager.rb +33 -0
  41. data/lib/ssl_test/config.rb +79 -0
  42. data/lib/ssl_test/ext_core/io_raw_input.rb +31 -0
  43. data/lib/ssl_test/input_handler.rb +35 -0
  44. data/lib/ssl_test/runner.rb +110 -0
  45. data/lib/ssl_test/runner_options.rb +68 -0
  46. data/lib/ssl_test/ssl_test_case.rb +46 -0
  47. data/lib/ssl_test/ssl_test_report.rb +24 -0
  48. data/lib/ssl_test/ssl_test_result.rb +30 -0
  49. data/lib/ssl_test/test_listener.rb +140 -0
  50. data/lib/ssl_test/test_manager.rb +116 -0
  51. data/lib/tlspretense.rb +13 -0
  52. data/lib/tlspretense/app.rb +52 -0
  53. data/lib/tlspretense/init_runner.rb +115 -0
  54. data/lib/tlspretense/skel/ca/goodcacert.pem +19 -0
  55. data/lib/tlspretense/skel/ca/goodcakey.pem +27 -0
  56. data/lib/tlspretense/skel/config.yml +523 -0
  57. data/lib/tlspretense/version.rb +3 -0
  58. data/packetthief_examples/em_ssl_test.rb +73 -0
  59. data/packetthief_examples/redirector.rb +29 -0
  60. data/packetthief_examples/setup_iptables.sh +24 -0
  61. data/packetthief_examples/ssl_client_simple.rb +27 -0
  62. data/packetthief_examples/ssl_server_simple.rb +44 -0
  63. data/packetthief_examples/ssl_smart_proxy.rb +115 -0
  64. data/packetthief_examples/ssl_transparent_proxy.rb +97 -0
  65. data/packetthief_examples/transparent_proxy.rb +56 -0
  66. data/spec/packetthief/impl/ipfw_spec.rb +98 -0
  67. data/spec/packetthief/impl/manual_spec.rb +65 -0
  68. data/spec/packetthief/impl/netfilter_spec.rb +66 -0
  69. data/spec/packetthief/impl/pf_divert_spec.rb +82 -0
  70. data/spec/packetthief/impl/pf_rdr_spec.rb +133 -0
  71. data/spec/packetthief/logging_spec.rb +78 -0
  72. data/spec/packetthief_spec.rb +47 -0
  73. data/spec/spec_helper.rb +53 -0
  74. data/spec/ssl_test/certificate_manager_spec.rb +222 -0
  75. data/spec/ssl_test/config_spec.rb +76 -0
  76. data/spec/ssl_test/runner_spec.rb +360 -0
  77. data/spec/ssl_test/ssl_test_case_spec.rb +113 -0
  78. data/spec/ssl_test/test_listener_spec.rb +199 -0
  79. data/spec/ssl_test/test_manager_spec.rb +324 -0
  80. data/tlspretense.gemspec +35 -0
  81. metadata +262 -0
@@ -0,0 +1,14 @@
1
+ require 'eventmachine'
2
+
3
+ module PacketThief
4
+ module Handlers
5
+ autoload :AbstractSSLHandler, 'packetthief/handlers/abstract_ssl_handler'
6
+ autoload :SSLClient, 'packetthief/handlers/ssl_client'
7
+ autoload :SSLServer, 'packetthief/handlers/ssl_server'
8
+ autoload :SSLSmartProxy, 'packetthief/handlers/ssl_smart_proxy'
9
+ autoload :SSLTransparentProxy, 'packetthief/handlers/ssl_transparent_proxy'
10
+ autoload :TransparentProxy, 'packetthief/handlers/transparent_proxy'
11
+ autoload :ProxyRedirector, 'packetthief/handlers/proxy_redirector'
12
+ end
13
+ end
14
+
@@ -0,0 +1,249 @@
1
+ require 'openssl'
2
+
3
+ module PacketThief
4
+ module Handlers
5
+
6
+ # Parent class for both SSLServer and SSLClient.
7
+ #
8
+ # TODO: get_peer_cert, get_peername, etc.
9
+ class AbstractSSLHandler < ::EM::Connection
10
+ include Logging
11
+
12
+ # The OpenSSL::SSL::SSLContext. Modify this in post_init or in the
13
+ # initializing code block to add certificates, etc.
14
+ attr_accessor :ctx
15
+
16
+ # The TCPSocket that the SSLSocket will be created from. It is added by
17
+ # #initialize.
18
+ attr_accessor :tcpsocket
19
+
20
+ # The SSLSocket. It is not available until #tls_begin creates it, after
21
+ # post_init and the initializing code block.
22
+ attr_accessor :sslsocket
23
+
24
+ # (Used by SSLClient only) The hostname that the SNI TLS extension should
25
+ # request. Set it in post_init or in the initializing code block --- it
26
+ # is applied to the SSLSocket during #tls_begin.
27
+ attr_accessor :sni_hostname
28
+
29
+ def initialize(tcpsocket, logger=nil)
30
+ @logger = logger
31
+ logdebug "initialize"
32
+ # Set up initial values
33
+ @tcpsocket = tcpsocket
34
+ @ctx = OpenSSL::SSL::SSLContext.new
35
+
36
+ @close_after_writing = false
37
+ @state = :new
38
+ end
39
+
40
+ # Creates _sslsocket_ from _tcpsocket_ and _ctx_, and initializes the
41
+ # handler's internal state. Called from the class method that creates the
42
+ # object, after post_init and the optional code block.
43
+ #
44
+ # @note (SSLClient only) If @sni_hostname exists on the handler at this
45
+ # point, it will be added to the SSLSocket in order to enable sending a
46
+ # hostname in the SNI TLS extension.
47
+ def tls_begin
48
+ logdebug "tls begin", :sni_hostname => @sni_hostname
49
+ @sslsocket = OpenSSL::SSL::SSLSocket.new(@tcpsocket, @ctx)
50
+ if @sni_hostname
51
+ if @sslsocket.respond_to? :hostname
52
+ @sslsocket.hostname = @sni_hostname
53
+ else
54
+ logwarn "#{@sslsocket.class} does not support setting an SNI hostname! This requires Ruby 1.9.x built against OpenSSL with SNI support.",
55
+ :ruby_version => RUBY_VERSION
56
+ end
57
+ end
58
+ @state = :initialized
59
+ end
60
+
61
+ # Calls accept_nonblock/connect_nonblock, read_nonblock, or
62
+ # write_nonblock based on the current state of the connection.
63
+ def notify_readable
64
+ logdebug "notify_readable", :state => @state
65
+ case @state
66
+ when :initialized
67
+ attempt_connection
68
+ when :ready_to_read
69
+ attempt_read
70
+ when :write_needs_to_read
71
+ attempt_write
72
+ end
73
+ end
74
+
75
+ # We only care about notify_writable if we are waiting to write for some
76
+ # reason.
77
+ def notify_writable
78
+ logdebug "notify_writable", :state => @state
79
+ notify_writable = false # disable it now. if we still need it, we'll renabled it.
80
+ case @state
81
+ when :initialized
82
+ attempt_connection
83
+ when :read_needs_to_write
84
+ attempt_read
85
+ when :write_needs_to_write
86
+ attempt_write
87
+ end
88
+
89
+ # if we waiting to close and are not longer waiting to write, we can flush and close the connection.
90
+ if @close_after_writing and not notify_writable?
91
+ @sslsock.flush
92
+ close_connection
93
+ end
94
+ end
95
+
96
+ private
97
+ def attempt_connection
98
+ begin
99
+ # Client usess connect_nonblock, while server uses accept_nonblock.
100
+ connection_action
101
+ @state = :ready_to_read
102
+ tls_successful_handshake
103
+ attempt_write if write_buffer.length > 0
104
+ rescue IO::WaitReadable
105
+ # accept_nonblock needs to wait until it can read again.
106
+ notify_readable = true
107
+ rescue IO::WaitWritable
108
+ # accept_nonblock needs to wait until it can write again.
109
+ notify_writable = true
110
+ rescue OpenSSL::SSL::SSLError, Errno::ECONNREFUSED => e
111
+ # ssl handshake failed. Likely due to client rejecting our certificate!
112
+ tls_failed_handshake(e)
113
+ close_connection
114
+ end
115
+ end
116
+
117
+ private
118
+ def attempt_read
119
+ begin
120
+ data = @sslsocket.read_nonblock 4096 # much more than a network packet...
121
+ receive_data(data)
122
+ notify_writable = false
123
+ rescue EOFError, Errno::ECONNRESET
124
+ # remote closed. time to wrap up
125
+ close_connection
126
+ rescue IO::WaitReadable
127
+ # we had no data to read.
128
+ notify_readable = true
129
+ rescue IO::WaitWritable
130
+ # we ran out of buffer to send (yes, SSLSocket#read_nonblock can
131
+ # trigger this)
132
+ @state = :read_needs_to_write
133
+ notify_writable = true
134
+ rescue OpenSSL::SSL::SSLError => e
135
+ logerror "attempt_read: #{e} (#{e.class})"
136
+ close_connection
137
+ else
138
+ @state = :ready_to_read
139
+ end
140
+ end
141
+
142
+ public
143
+ def write_buffer
144
+ @write_buffer ||= ""
145
+ end
146
+
147
+ public
148
+ def write_buffer=(rhs)
149
+ @write_buffer = rhs
150
+ end
151
+
152
+ private
153
+ def attempt_write(data=nil)
154
+ logdebug "attempt_write"
155
+ write_buffer << data if data
156
+ # do not attempt to write until we are ready!
157
+ return if @state == :initialized or @state == :new
158
+ begin
159
+ count_written = @sslsocket.write_nonblock write_buffer
160
+ rescue IO::WaitWritable
161
+ notify_writable = true
162
+ rescue IO::WaitReadable
163
+ @state = :write_needs_to_read
164
+ rescue OpenSSL::SSL::SSLError, IOError => e
165
+ logerror "attempt_write: #{e} (#{e.class})"
166
+ close_connection
167
+ else
168
+ # shrink the buf
169
+ #
170
+ # byteslice was added in ruby 1.9.x. in ruby 1.8.7, bytesize is
171
+ # aliased to length, implying that a character coresponds to a
172
+ # byte.
173
+ @write_buffer = if write_buffer.respond_to?(:byteslice)
174
+ write_buffer.byteslice(count_written..-1)
175
+ else
176
+ write_buffer.slice(count_written..-1)
177
+ end
178
+ # if we didn't write everything, wait for writable.
179
+ notify_writable = true if write_buffer.bytesize > 0
180
+ end
181
+ end
182
+
183
+ ####
184
+
185
+ public
186
+
187
+ # Call this to send data to the other end of the connection.
188
+ def send_data(data)
189
+ logdebug "send_data:", :data => data
190
+ attempt_write(data)
191
+ end
192
+
193
+ def close_connection
194
+ detach
195
+ @sslsocket.close if @sslsocket and not @sslsocket.closed?
196
+ @tcpsocket.close if not @tcpsocket.closed?
197
+ # unbind
198
+ end
199
+
200
+ def close_connection_after_writing
201
+ @close_after_writing = true
202
+ # if we aren't waiting to write, then we can flush and close.
203
+ if not notify_writable?
204
+ @sslsocket.flush
205
+ close_connection
206
+ end
207
+
208
+ end
209
+
210
+
211
+ # Note that post_init dos not have access to the _sslsocket_. The
212
+ # _sslsocket_ is not added until tls_begin is called, after the code
213
+ # block.
214
+ #
215
+ # #post_init gives you a chance to manipulate the SSLContext.
216
+ def post_init
217
+ end
218
+
219
+
220
+ # Called right after the SSL handshake succeeds. This is your "new"
221
+ # #post_init.
222
+ def tls_successful_handshake
223
+ logdebug "Succesful handshake!"
224
+ end
225
+
226
+ # Called right after accept_nonblock fails for some unknown reason. The
227
+ # only parameter contains the OpenSSL::SSL::SSLError object that was
228
+ # thrown.
229
+ #
230
+ # The connection will be closed after this.
231
+ def tls_failed_handshake(e)
232
+ logerror "tls_failed_handshake: Failed to accept: #{e} (#{e.class})"
233
+ end
234
+
235
+ # Override this to do something with the unecrypted data.
236
+ def receive_data(data)
237
+ logdebug "receive_data:", :data => data
238
+ end
239
+
240
+ # Override this to do something when the socket is finished.
241
+ def unbind
242
+ logdebug "unbind"
243
+ end
244
+
245
+ end
246
+ end
247
+ end
248
+
249
+
@@ -0,0 +1,26 @@
1
+ module PacketThief
2
+ module Handlers
3
+
4
+ # Instead of forwarding the connection to the original host, forwards it to
5
+ # a configured host instead.
6
+ class ProxyRedirector < TransparentProxy
7
+
8
+ def initialize(proxy_host, proxy_port, log=nil)
9
+ super(log)
10
+
11
+ @proxy_host = proxy_host
12
+ @proxy_port = proxy_port
13
+ end
14
+
15
+ # Instead of using the original destination, use the configured destination.
16
+ def client_connected
17
+ @dest_host = @proxy_host
18
+ @dest_port = @proxy_port
19
+ connect_to_dest
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
26
+
@@ -0,0 +1,87 @@
1
+ module PacketThief
2
+ module Handlers
3
+
4
+ # Basic SSL/TLS Client built on Ruby's OpenSSL objects instead of on
5
+ # EventMachine's start_tls. This allows you to manipulate the SSLContext
6
+ # and other details of the connection that EM normally doesn't let you
7
+ # touch.
8
+ #
9
+ # Subclass it and override any of the methods in the following example to
10
+ # use the the functionality.
11
+ #
12
+ # You can #send_data to send encrypted data to the other side, and
13
+ # #receive_data will be called when there is data for the handler.
14
+ #
15
+ # EM.run {
16
+ # SSLClient.connect "www.isecpartners.com", 443 do |p|
17
+ #
18
+ # # Note: this code block is actually too late to set up a new
19
+ # # #post_init since it runs just after post_init. You can use
20
+ # # #post_init on a subclass though.
21
+ # def p.post_init
22
+ # # modify p.ctx to configure your certificates, key, etc.
23
+ # end
24
+ #
25
+ # # The following makes more sense for the initialization block.
26
+ # h.ctx.verify_mode = OpenSSL::SSL::VERIFY_NONE
27
+ #
28
+ # def p.tls_successful_handshake
29
+ # # the handshake succeeded
30
+ # end
31
+ #
32
+ # def p.tls_failed_handshake(e)
33
+ # # the ssl handshake failed, probably due to the client rejecting
34
+ # # your certificate. =)
35
+ # end
36
+ #
37
+ # def p.unbind
38
+ # # unbind handler, called regardless of handshake success
39
+ # end
40
+ #
41
+ # def p.receive_data(data)
42
+ # # do something with the unencrypted stream
43
+ # p.send_data("some message") # data to be encrypted then sent to the client
44
+ # end
45
+ #
46
+ # end
47
+ # }
48
+ #
49
+ # Note: During #initialize and #post_init, this class
50
+ # does not have access to its socket yet. Instead, use #tls_pre_start or
51
+ # the code block you pass to .start to initialize the SSLContext, and use
52
+ # #tls_successful_handshake to do anything once the SSL handshake has
53
+ # completed.
54
+ class SSLClient < AbstractSSLHandler
55
+
56
+ def self.connect(host, port, *args, &block)
57
+ ssl_class = self
58
+
59
+ sock = TCPSocket.new host, port
60
+
61
+ ::EM.watch sock, ssl_class, sock, *args do |h|
62
+ h.notify_readable = true
63
+ # h.notify_writable = true
64
+ block.call(h) if block
65
+ h.tls_begin
66
+ end
67
+ end
68
+
69
+ ####
70
+
71
+ private
72
+ # SSLClient uses connect_nonblock instead of accept_nonblock.
73
+ def connection_action
74
+ @sslsocket.connect_nonblock
75
+ end
76
+
77
+ ####
78
+
79
+ public
80
+ def tls_begin
81
+ super
82
+ attempt_connection
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,174 @@
1
+ module PacketThief
2
+ module Handlers
3
+
4
+ # Basic SSL/TLS Server built on Ruby's OpenSSL objects instead of on
5
+ # EventMachine's start_tls. This allows you to manipulate the SSLContext
6
+ # and other details of the connection that EM normally doesn't let you
7
+ # touch.
8
+ #
9
+ # Subclass it and override any of the methods in the following example to
10
+ # use the the functionality.
11
+ #
12
+ # You can #send_data to send encrypted data to the other side, and
13
+ # #receive_data will be called when there is data for the handler.
14
+ #
15
+ # EM.run {
16
+ # # Leave the hostname blank for Linux's netfilter.
17
+ # SSLServer.start '', 54321 do |p|
18
+ #
19
+ # # Note: this code block is actually too late to set up a new
20
+ # # #post_init since it runs just after post_init. Instead, you would
21
+ # # use post_init in a subclass.
22
+ # def p.post_init
23
+ # # modify p.ctx to configure your certificates, key, etc.
24
+ # end
25
+ #
26
+ # # In this example, the following would work in this initialization
27
+ # # block:
28
+ # h.ctx.cert = cert
29
+ # h.ctx.extra_chain_cert = chain
30
+ # h.ctx.key = key
31
+ #
32
+ # def servername_cb(sock, hostname)
33
+ # # implement your own SNI handling callback. The default will
34
+ # # return the originally configured context.
35
+ # end
36
+ #
37
+ # def p.tls_successful_handshake
38
+ # # the handshake succeeded
39
+ # end
40
+ #
41
+ # def p.tls_failed_handshake(e)
42
+ # # the ssl handshake failed, probably due to the client rejecting
43
+ # # your certificate. =)
44
+ # end
45
+ #
46
+ # def p.unbind
47
+ # # unbind handler, called regardless of handshake success
48
+ # end
49
+ #
50
+ # def p.receive_data(data)
51
+ # # do something with the unencrypted stream
52
+ # p.send_data("some message") # data to be encrypted then sent to the client
53
+ # end
54
+ #
55
+ # end
56
+ # }
57
+ #
58
+ # Note: During #initialize and #post_init, this class
59
+ # does not have access to its socket yet. Instead, use #tls_pre_start or
60
+ # the code block you pass to .start to initialize the SSLContext, and use
61
+ # #tls_post_accept to do anything once the SSL handshake has completed. You
62
+ # can also override #servername_cb to perform the SNI callback.
63
+ class SSLServer < AbstractSSLHandler
64
+
65
+ # reference to the InitialServer that created the current handler. exists
66
+ # so you can call #stop_server or do something else to it directly.
67
+ attr_accessor :server_handler
68
+
69
+ def self.start(host, port, *args, &block)
70
+ ssl_class = self
71
+
72
+ serv = TCPServer.new host, port
73
+
74
+ # We use InitialServer to listen for incoming connections. It will then
75
+ # create the actual SSLServer.
76
+ initialserver = ::EM.watch serv, InitialServer, serv, ssl_class, args, block do |h|
77
+ h.notify_readable = true
78
+ end
79
+
80
+ initialserver
81
+ end
82
+
83
+ ####
84
+
85
+ # Handles the initial listening socket. We can't seem to use
86
+ # EM.start_server -> EM.detach -> em.watch without triggering
87
+ # (in EventMachine 1.0.0):
88
+ #
89
+ # Assertion failed: (sd != INVALID_SOCKET), function _RunSelectOnce, file em.cpp, line 893.
90
+ #
91
+ # So we handle the server muckery ourselves.
92
+ module InitialServer
93
+ include Logging
94
+
95
+ def initialize(servsocket, ssl_class, args, block)
96
+ @servsocket = servsocket
97
+ @ssl_class = ssl_class
98
+ @args = args
99
+ @block = block
100
+ end
101
+
102
+ def notify_readable
103
+ logdebug "(#{@ssl_class}): Received a new connection, spawning a #{@ssl_class}"
104
+ sock = @servsocket.accept_nonblock
105
+
106
+ ::EM.watch sock, @ssl_class, sock, *@args, @logger do |h|
107
+ logdebug "after initialize"
108
+ h.server_handler = self
109
+ h.notify_readable = true
110
+ h.logger = @logger
111
+ # Now call the caller's block.
112
+ @block.call(h) if @block
113
+ # And finally finish initialization by applying the context to an
114
+ # SSLSocket, and setting the internal state.
115
+ h.tls_begin unless h.tcpsocket.closed?
116
+ end
117
+
118
+ end
119
+
120
+ def notify_writable
121
+ logdebug "(#{@ssl_class}): Server socket notify writable"
122
+ end
123
+
124
+ # This must be called explicitly. EM doesn't seem to have a callback for when the EM::run call ends.
125
+ def stop_server
126
+ unless @servsocket.closed?
127
+ detach
128
+ @servsocket.close
129
+ end
130
+ end
131
+
132
+ def unbind
133
+ logdebug "(#{@ssl_class}): Stopping server socket"
134
+ end
135
+ end
136
+
137
+ ####
138
+
139
+ private
140
+ # SSLServer uses accept_nonblock instead of connect_nonblock.
141
+ def connection_action
142
+ @sslsocket.accept_nonblock
143
+ end
144
+
145
+ ####
146
+
147
+ public
148
+ def initialize(tcpsocket,logger=nil)
149
+ super(tcpsocket, logger)
150
+ @ctx.servername_cb = proc {|sslsocket, hostname| self.servername_cb(sslsocket, hostname) }
151
+ end
152
+
153
+
154
+ # Called when the client sends a hostname using the SNI TLS extension.
155
+ #
156
+ # This method should return an OpenSSL::SSL::SSLContext. It gives you an
157
+ # opportunity to pick or generate a different server certificate or
158
+ # certificate chain based on the hostname requested by the client.
159
+ #
160
+ # The default implementation does nothing by just returning the original
161
+ # SSLContext.
162
+ def servername_cb(sslsock, hostname)
163
+ sslsock.context
164
+ end
165
+
166
+ # Stops the InitialListener sever handler that spawned this handler. Due
167
+ # to our use of EM.watch, we can't rely on EM to close the socket.
168
+ def stop_server
169
+ @server_handler.stop_server
170
+ end
171
+
172
+ end
173
+ end
174
+ end