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,360 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','spec_helper'))
2
+
3
+ module SSLTest
4
+ describe Runner do
5
+ let(:args) { [] }
6
+ let(:stdin) { double("stdin") }
7
+ let(:stdout) { double("stdout", :puts => nil) }
8
+
9
+ let(:test_foo_data) do
10
+ {
11
+ 'alias' => 'foo',
12
+ 'name' => 'Baseline Happy Test',
13
+ 'certchain' => ['baseline', 'goodca'],
14
+ 'expected_result' => 'connect'
15
+ }
16
+ end
17
+ let(:test_bar_data) do
18
+ {
19
+ 'alias' => 'bar',
20
+ 'name' => 'Baseline Happy Test',
21
+ 'certchain' => ['baseline', 'goodca'],
22
+ 'expected_result' => 'connect'
23
+ }
24
+ end
25
+ let(:test_data_list) { [test_foo_data, test_bar_data] }
26
+
27
+ let(:test_foo) { SSLTestCase.new(test_foo_data) }
28
+ let(:test_bar) { SSLTestCase.new(test_bar_data) }
29
+ let(:test_list) { [test_foo, test_bar] }
30
+
31
+ let(:test_listener) { double('test listener', :logger= => nil) }
32
+ let(:test_manager) { double('test manager') }
33
+
34
+ let(:test_wrongcname) { double('test wrongcname') }
35
+ let(:conf_certs) { double('conf certs') }
36
+ let(:config) do
37
+ double(
38
+ "config",
39
+ :listener_port => 54321,
40
+ :tests => test_data_list,
41
+ 'certs' => conf_certs,
42
+ 'action' => :runtests,
43
+ 'pause?' => false,
44
+ 'loglevel' => Logger::INFO,
45
+ 'logfile' => nil,
46
+ 'packetthief' => {},
47
+ 'hosttotest' => double('hosttotest')
48
+ )
49
+ end
50
+ let(:cert_manager) { double("certificate manager") }
51
+ let(:report) { double('report', :print_results => nil, :add_result => nil) }
52
+ let(:testcaseresult) { double('test case result') }
53
+ let(:testcase) { double('test case', :run => testcaseresult) }
54
+ let(:appcontext) { double('context',
55
+ :config => config,
56
+ :test_manager => test_manager,
57
+ :test_manager= => nil,
58
+ ) }
59
+ let(:logger) { Logger.new(nil) }
60
+
61
+ let(:conf_data) do
62
+ {
63
+ 'certs' => conf_certs,
64
+ 'tests' => test_data_list,
65
+ 'packetthief' => {},
66
+ }
67
+ end
68
+
69
+ before(:each) do
70
+ YAML.stub(:load_file).and_return(conf_data)
71
+ CertificateManager.stub(:new).and_return(cert_manager)
72
+ SSLTestCase.stub(:new).and_return(testcase)
73
+ SSLTestReport.stub(:new).and_return(report)
74
+ AppContext.stub(:new).and_return(appcontext)
75
+ SSLTestCase.stub(:factory).and_return(test_list)
76
+ end
77
+
78
+ after(:each) do
79
+ PacketThief.instance_variable_set(:@implementation, nil)
80
+ end
81
+
82
+ subject { Runner.new(args, stdin, stdout) }
83
+
84
+ describe "#initialize" do
85
+ before(:each) do
86
+ Config.stub(:new).and_return(config)
87
+ end
88
+
89
+ context "when ARGS is empty" do
90
+ let(:args) { [] }
91
+ it "loads the config from the default location" do
92
+ Config.should_receive(:new).and_return(config)
93
+
94
+ subject
95
+ end
96
+
97
+ it "initializes a certificate manager" do
98
+ CertificateManager.should_receive(:new).with(conf_certs).and_return(cert_manager)
99
+
100
+ subject
101
+ end
102
+
103
+ it "initializes a logger" do
104
+ @logger = Logger.new(nil) # logger on the line below is called after Logger.should_receive(:new), meaning that logger() triggers the expectation, or something.
105
+ Logger.should_receive(:new).and_return(@logger)
106
+
107
+ subject
108
+ end
109
+
110
+ it "creates an application context" do
111
+ AppContext.should_receive(:new).with(config, cert_manager, kind_of(Logger)).and_return(appcontext)
112
+
113
+ subject
114
+ end
115
+
116
+ end
117
+ end
118
+
119
+ describe "#run" do
120
+ before(:each) do
121
+ @logger = Logger.new(nil)
122
+ Logger.stub(:new).and_return(@logger)
123
+ Config.stub(:new).and_return(config)
124
+ subject.stub(:start_packetthief)
125
+ subject.stub(:stop_packetthief)
126
+ subject.stub(:run_tests).and_return(report)
127
+ end
128
+
129
+ context "when the action is runtests" do
130
+ before(:each) do
131
+ PacketThief.stub_chain(:redirect, :where, :run)
132
+ PacketThief.stub(:revert)
133
+
134
+ config.stub(:action).and_return(:runtests)
135
+
136
+ end
137
+
138
+ context "when ARGS is empty" do
139
+ let(:args) { [] }
140
+
141
+ it "creates a default list of test cases" do
142
+ SSLTestCase.should_receive(:factory).with(appcontext, config.tests,[]).and_return(double('list of SSLTestCases', :length => 10))
143
+
144
+ subject.run
145
+ end
146
+
147
+ it "runs the tests" do
148
+ subject.should_receive(:run_tests).with(test_list).and_return(report)
149
+
150
+ subject.run
151
+ end
152
+
153
+ it "starts PacketThief before running the tests" do
154
+ subject.should_receive(:start_packetthief).ordered
155
+ subject.should_receive(:run_tests).ordered.and_return(report)
156
+
157
+ subject.run
158
+ end
159
+
160
+
161
+ it "stops PacketThief after running the tests" do
162
+ subject.should_receive(:run_tests).ordered.and_return(report)
163
+ subject.should_receive(:stop_packetthief).ordered
164
+
165
+ subject.run
166
+ end
167
+ end
168
+
169
+ context "when ARGS is ['wrongcname']" do
170
+ let(:args) { ['wrongcname'] }
171
+ it "it tells the SSLTestCase factory to just return the test called 'wrongcname'" do
172
+ SSLTestCase.should_receive(:factory).with(appcontext, config.tests,['wrongcname']).and_return(double('list with just wrongcname', :length => 1))
173
+
174
+ subject.run
175
+ end
176
+ end
177
+
178
+ end
179
+
180
+ end
181
+
182
+ describe "#run_tests" do
183
+ before(:each) do
184
+ Config.stub(:new).and_return(config)
185
+ EM.stub(:run).and_yield
186
+ EM.stub(:add_periodic_timer)
187
+ EM.stub(:open_keyboard)
188
+ EM.stub(:stop_event_loop)
189
+ TestListener.stub(:start).and_return(test_listener)
190
+ TestManager.stub(:new).and_return(test_manager)
191
+ @logger = logger
192
+ Logger.should_receive(:new).and_return(logger)
193
+ end
194
+
195
+ it "configures a new TestManager" do
196
+ TestManager.should_receive(:new).with(appcontext, test_list, report, logger).and_return(test_manager)
197
+
198
+ subject.run_tests test_list
199
+ end
200
+
201
+ it "starts an EventMachine event loop" do
202
+ EM.should_receive(:run)
203
+
204
+ subject.run_tests test_list
205
+ end
206
+
207
+ it "starts the TestListener" do
208
+ TestListener.should_receive(:start).with('', 54321, test_manager).and_return(test_listener)
209
+
210
+ subject.run_tests test_list
211
+ end
212
+ end
213
+
214
+ describe "logger options" do
215
+ context "when the command line sets the log level to WARN" do
216
+ let(:args) { %w{--log-level=WARN} }
217
+ it "sets the logger's level to WARN" do
218
+ @logger = Logger.new(nil)
219
+
220
+ Logger.stub(:new).and_return(@logger)
221
+
222
+ @logger.should_receive(:level=).with(Logger::WARN)
223
+
224
+ subject
225
+ end
226
+ end
227
+
228
+ context "when the command line sets the log file to 'foo.txt'" do
229
+ let(:args) { %w{--log-file=foo.txt} }
230
+ it "sets the log file to foo.txt" do
231
+ @logger = Logger.new(nil)
232
+
233
+ Logger.should_receive(:new).with('foo.txt').and_return(@logger)
234
+
235
+ subject
236
+ end
237
+ end
238
+
239
+ end
240
+
241
+ describe "configuring packetthief" do
242
+ before(:each) do
243
+ PacketThief.instance_variable_set(:@implementation, nil)
244
+ Config.stub(:new).and_return(config)
245
+ end
246
+ after(:each) do
247
+ PacketThief.instance_variable_set(:@implementation, nil)
248
+ end
249
+ context "when the config does not specify an implementation" do
250
+ before(:each) do
251
+ config.stub(:packetthief).and_return( {} )
252
+ end
253
+
254
+ it "does not explicitly configure a firewall implementation" do
255
+ PacketThief.should_not_receive(:implementation=)
256
+
257
+ subject
258
+ end
259
+ end
260
+
261
+ context "when the config specifies netfilter" do
262
+ before(:each) do
263
+ config.stub(:packetthief).and_return( {'implementation' => 'netfilter'} )
264
+ end
265
+
266
+ it "sets the firewall to :netfilter" do
267
+ PacketThief.should_receive(:implementation=).with('netfilter')
268
+
269
+ subject
270
+ end
271
+ end
272
+
273
+ context "when the config specifies manual(somehost)" do
274
+ before(:each) do
275
+ config.stub(:packetthief).and_return( {'implementation' => 'manual(somehost)', 'dest_port' => 654} )
276
+ PacketThief.stub(:set_dest)
277
+ end
278
+
279
+ it "sets the firewall to :manual" do
280
+ PacketThief.should_receive(:implementation=).with(:manual)
281
+
282
+ subject
283
+ end
284
+
285
+ it "sets the manual destination to the given hostname and dest_port" do
286
+ PacketThief.should_receive(:set_dest).with('somehost', 654)
287
+
288
+ subject
289
+ end
290
+ end
291
+
292
+ describe "the 'external' pseudo-implementation" do
293
+ context "when the config specifies external(netfilter)" do
294
+ before(:each) do
295
+ config.stub(:packetthief).and_return( {'implementation' => 'external(netfilter)'} )
296
+ PacketThief.stub(:set_dest)
297
+ end
298
+
299
+ it "it sets the firewall implementation to netfilter" do
300
+ PacketThief.should_receive(:implementation=).with(/netfilter/i)
301
+
302
+ subject
303
+ end
304
+
305
+ # Not enabling packetthief currently needs to happen in the test runner
306
+ end
307
+ end
308
+
309
+ end
310
+
311
+ describe "running packet thief" do
312
+ before(:each) do
313
+ Config.stub(:new).and_return(config)
314
+ end
315
+
316
+ describe "#start_packetthief" do
317
+ before(:each) do
318
+ config.stub(:listener_port).and_return(54321)
319
+ config.stub(:packetthief).and_return({
320
+ :protocol => 'tcp',
321
+ :dest_port => 443,
322
+ :in_interface => 'en1'
323
+ })
324
+ end
325
+
326
+ it "launches packet thief with configuration values" do
327
+ @ptrule = double("ptrule")
328
+ PacketThief.should_receive(:redirect).with(:to_ports => 54321).and_return(@ptrule)
329
+ @ptrule.should_receive(:where).with(:protocol => 'tcp', :dest_port => 443, :in_interface => 'en1').ordered.and_return(@ptrule)
330
+ @ptrule.should_receive(:run).ordered
331
+
332
+ subject.start_packetthief
333
+ end
334
+
335
+ context "when the packetthief implementation is 'external(netfilter)'" do
336
+ before(:each) do
337
+ config.stub(:packetthief).and_return( { 'implementation' => 'external(netfilter)' } )
338
+ end
339
+ it "does not redirect traffic itself" do
340
+ PacketThief.should_not_receive(:redirect)
341
+
342
+ subject.start_packetthief
343
+ end
344
+ end
345
+
346
+ end
347
+
348
+ describe "#stop_packetthief" do
349
+ it "reverts PacketThief" do
350
+ PacketThief.should_receive(:revert)
351
+
352
+ subject.stop_packetthief
353
+ end
354
+ end
355
+
356
+ end
357
+
358
+
359
+ end
360
+ end
@@ -0,0 +1,113 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__),'..','spec_helper'))
2
+
3
+ module SSLTest
4
+ describe SSLTestCase do
5
+ let(:certchain) { double('certchain') }
6
+ let(:keychain) { [ double('firstkey'), double('secondkey'), double('cakey') ] }
7
+ let(:goodcacert) { double('goodcacert') }
8
+ let(:goodcakey) { double('goodcakey') }
9
+ let(:cert_manager) do
10
+ @cert_manager = double(
11
+ "cert_manager",
12
+ :get_chain => certchain,
13
+ :get_keychain => keychain,
14
+ :get_cert => double('cert'),
15
+ :get_key => double('key')
16
+ )
17
+ @cert_manager.stub(:get_cert).with('goodca').and_return(goodcacert)
18
+ @cert_manager.stub(:get_key).with('goodca').and_return(goodcakey)
19
+ @cert_manager
20
+ end
21
+ let(:config) do
22
+ double("config",
23
+ :dest_port => 443,
24
+ :listener_port => 54321,
25
+ :hosttotest => "my.hostname.com",
26
+ :packetthief => {
27
+ :protocol => 'tcp',
28
+ :dest_port => 443,
29
+ :in_interface => 'en1'
30
+ },
31
+ :testing_method => 'tlshandshake'
32
+ )
33
+ end
34
+ let(:testdesc) do
35
+ {
36
+ 'alias' => 'baseline',
37
+ 'name' => 'Baseline Happy Test',
38
+ 'certchain' => ['baseline', 'goodca'],
39
+ 'expected_result' => 'connect'
40
+ }
41
+ end
42
+
43
+ let(:foo_test_data) do
44
+ {
45
+ 'alias' => 'foo',
46
+ 'name' => 'test foo',
47
+ 'certchain' => [ 'a', 'b' ]
48
+ }
49
+ end
50
+ let(:bar_test_data) do
51
+ {
52
+ 'alias' => 'bar',
53
+ 'name' => 'test bar',
54
+ 'certchain' => [ 'c', 'd' ]
55
+ }
56
+ end
57
+ let(:conf_tests_data) { [ foo_test_data, bar_test_data ] }
58
+
59
+
60
+
61
+ let(:listener) { double("listener", :logger= => nil, :stop_server => nil) }
62
+ let(:report) { double("report", :add_result => nil) }
63
+ let(:logger) { Logger.new(nil) }
64
+ let(:app_context) { AppContext.new(config, cert_manager, logger) }
65
+
66
+
67
+ before(:each) do
68
+ PacketThief.stub_chain(:redirect, :where, :run)
69
+ PacketThief.stub(:revert)
70
+ EM.stub(:run).and_yield
71
+ EM.stub(:add_periodic_timer)
72
+ EM.stub(:open_keyboard)
73
+ EM.stub(:stop_event_loop)
74
+ TestListener.stub(:start).and_return(listener)
75
+ end
76
+
77
+
78
+ # list of configs, list to generate
79
+ describe ".factory" do
80
+ subject { SSLTestCase }
81
+ context "when an empty list of tests to perform is passed" do
82
+ it "returns SSLTestCases for all of the tests in the config data, in the original order" do
83
+ @tests = subject.factory(app_context, conf_tests_data, [])
84
+
85
+ @tests.length.should == conf_tests_data.length
86
+ @tests.each do |test|
87
+ test.class.should == subject
88
+ end
89
+ end
90
+ end
91
+
92
+ context "when ['foo'] is passed as an argument" do
93
+ it "returns a list with just a 'foo' test case" do
94
+ @tests = subject.factory(app_context, conf_tests_data, ['foo'])
95
+
96
+ @tests.length.should == 1
97
+ end
98
+ end
99
+ context "when ['bar', 'foo'] is passed as an argument" do
100
+ it "returns a list with 'bar', then 'foo'" do
101
+ @tests = subject.factory(app_context, conf_tests_data, ['bar', 'foo'])
102
+
103
+ @tests.length.should == 2
104
+ @tests[0].id.should == 'bar'
105
+ @tests[1].id.should == 'foo'
106
+ end
107
+ end
108
+ end
109
+
110
+ end
111
+ end
112
+
113
+