tlspretense 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+