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,64 @@
1
+ = Setting Up Linux for Use With TLSPretense
2
+
3
+ To run TLSPretense on Linux, you need a system that has two network interfaces.
4
+ This Linux system could run from a virtual machine, or it could run on a laptop
5
+ and use the laptop's wifi card as a wireless access point to test.
6
+
7
+ The following setup will use a Linux host to run TLSPretense, and it will
8
+ configure netfilter to be a NAT with statically managed IP address on its
9
+ internal network.
10
+
11
+ TODO: How to host a DHCP server on certain systems.
12
+
13
+ == Configuring Linux to act as a NAT/router/gateway
14
+
15
+ The first step is to configure the Linux system's routing with IPTables. The
16
+ following script will turn the system into a NAT router. TLSPretense will then
17
+ later add and remove iptables rules as needed in order to intercept network
18
+ traffic during testing.
19
+
20
+ In this script, we assume +eth0+ is the external interface that can connect to
21
+ the original destination, and +eth1+ is the internal interface that the client
22
+ system will connect to.
23
+
24
+ #!/bin/sh
25
+ external=eth0
26
+ internal=eth1
27
+
28
+ iptables --flush
29
+ iptables --table nat --flush
30
+ iptables --delete-chain
31
+ iptables --table nat --delete-chain
32
+
33
+
34
+ echo "Manually setup the internal network's nic"
35
+ ifconfig $internal 192.168.42.1 netmask 255.255.255.0
36
+
37
+ echo "Enable packet forwarding"
38
+ echo 1 > /proc/sys/net/ipv4/ip_forward
39
+
40
+ echo "Apply basic iptables rules to create a NAT"
41
+ iptables -t nat -A POSTROUTING -o $external -j MASQUERADE
42
+
43
+ echo "Done! TLSPretense will automatically add and remove the rules for redirecting traffic."
44
+
45
+ Here we choose 192.168.42.* as the internal network IP address range.
46
+
47
+ Check to make sure that the TLSPretense host is able to access the internet
48
+ over the external interface, and that it can look up hostnames.
49
+
50
+ TODO: handling wireless ({although Mallory already has a
51
+ guide}[https://bitbucket.org/IntrepidusGroup/mallory/wiki/Wifi_Hotspot_Setup])
52
+
53
+ == Configure the Client's Host
54
+
55
+ Next, the system that will run the client code needs to be configured to use
56
+ the TLSPretense host as its gateway. This usually involves configuring the
57
+ system's network connection with information like the following:
58
+
59
+ Address: 192.168.42.100
60
+ Gateway: 192.168.42.1
61
+ Subnet mask: 255.255.255.0
62
+
63
+ You will also need to configure the client's DNS with a known good DNS server.
64
+
data/lib/certmaker.rb ADDED
@@ -0,0 +1,61 @@
1
+
2
+ require 'fileutils'
3
+ require 'yaml'
4
+ require 'openssl'
5
+
6
+ require 'certmaker/ext_core/hash_indifferent_fetch'
7
+ require 'certmaker/certificate_factory'
8
+ require 'certmaker/certificate_suite_generator'
9
+
10
+ module CertMaker
11
+
12
+
13
+ # Generate certificates and keys using +config+.
14
+ #
15
+ # Config is usually a data structure derived from parsing a YAML file.
16
+ def make_certs(config, verbose=false)
17
+ FileUtils.mkdir_p config['certmaker']['outdir'], :verbose => verbose
18
+
19
+ certs = CertificateSuiteGenerator.new(config['certs'], config['hostname'], config['certmaker']).certificates
20
+
21
+ certs.each do |calias, ck|
22
+ File.open(File.join(config['certmaker']['outdir'],calias+"cert.pem"),"wb") { |f| f.write ck[:cert] }
23
+ File.open(File.join(config['certmaker']['outdir'],calias+"key.pem"),"wb") { |f| f.write ck[:key] }
24
+ end
25
+
26
+ end
27
+ module_function :make_certs
28
+
29
+ # Ensure that the custom ca exists.
30
+ def make_ca(config, verbose=false)
31
+ unless config['certmaker'].has_key? 'customgoodca'
32
+ puts "certmaker does not have a 'customgoodca' entry, so a CA will be regenerated every time."
33
+ return
34
+ end
35
+
36
+ cacert = config['certmaker']['customgoodca']['certfile']
37
+ cakey = config['certmaker']['customgoodca']['keyfile']
38
+
39
+ if File.exist? cacert and File.exist? cakey
40
+ puts "CA and CA's key already exist."
41
+ elsif File.exist? cacert and not File.exist? cakey
42
+ raise "CA certificate exists, but the key file does not exist?!"
43
+ elsif not File.exist? cacert and File.exist? cakey
44
+ raise "CA certificate does not exist, but the key file exists?!"
45
+ else
46
+ puts "Generating a new CA"
47
+ cacertdir = File.dirname(config['certmaker']['customgoodca']['certfile'])
48
+ FileUtils.mkdir_p cacertdir, :verbose => verbose
49
+ cakeydir = File.dirname(config['certmaker']['customgoodca']['keyfile'])
50
+ FileUtils.mkdir_p cakeydir, :verbose => verbose
51
+ csg = CertificateSuiteGenerator.new(config['certs'], config['hostname'], config['certmaker'])
52
+ csg.generate_certificate('goodca',config['certs']['goodca'])
53
+ cadata = csg.certificates['goodca']
54
+ File.open(cacert,"wb") { |f| f.write cadata[:cert] }
55
+ File.open(cakey,"wb") { |f| f.write cadata[:key] }
56
+ puts "New CA generated."
57
+ puts "Make sure you remove or comment out the passphrase in config.yml if you had one previously set!"
58
+ end
59
+ end
60
+ module_function :make_ca
61
+ end
@@ -0,0 +1,106 @@
1
+ module CertMaker
2
+ class CertificateFactory
3
+
4
+ attr_accessor :ca, :ca_key
5
+ attr_accessor :subject
6
+ attr_accessor :not_before, :not_after
7
+ attr_accessor :extensions
8
+ attr_accessor :key_type
9
+ attr_accessor :key_size
10
+ attr_accessor :signing_alg
11
+
12
+ def initialize
13
+ self.ca = :self
14
+ self.extensions = []
15
+ self.key_type = OpenSSL::PKey::RSA
16
+ self.key_size = 2048
17
+ self.signing_alg = :SHA1
18
+ end
19
+
20
+ # Handle special time values.
21
+ #
22
+ # now Time.now
23
+ # 30 30 days from now
24
+ # -30 30 days before now
25
+ def derive_time(timeval)
26
+ if ['now',:now].include? timeval
27
+ Time.now
28
+ elsif timeval.kind_of? Numeric
29
+ Time.now + timeval*24*60*60
30
+ else
31
+ raise "Unhandled time value: #{timeval}"
32
+ end
33
+ end
34
+
35
+ # Returns an array containing the certificate and associated key with the configured attributes, plus with the
36
+ # overridden attrs.
37
+ def create(args={})
38
+
39
+ # Make a key
40
+ kt = args.indifferent_fetch(:key_type, self.key_type)
41
+ begin
42
+ kt = OpenSSL::PKey.const_get(kt)
43
+ rescue TypeError
44
+ end
45
+ nk = kt.new self.key_size
46
+
47
+ # Certificate basics
48
+ nc = OpenSSL::X509::Certificate.new
49
+ nc.version = 2
50
+ nc.serial = args.indifferent_fetch(:serial, 1)
51
+ nc.subject = OpenSSL::X509::Name.parse(args.indifferent_fetch(:subject, self.subject))
52
+ nc.public_key = nk.public_key
53
+
54
+ # "now" is a special time value
55
+ nc.not_before = derive_time(args.indifferent_fetch(:not_before,self.not_before))
56
+ nc.not_after = derive_time(args.indifferent_fetch(:not_after,self.not_after))
57
+
58
+ # Prep for extensions
59
+ ef = OpenSSL::X509::ExtensionFactory.new
60
+ ef.subject_certificate = nc
61
+
62
+ self.ca = args.indifferent_fetch(:ca,self.ca)
63
+ # Issuer handling
64
+ if ['self',:self].include? self.ca
65
+ nc.issuer = nc.subject
66
+ ef.issuer_certificate = nc
67
+ signing_key = nk
68
+ else
69
+ nc.issuer = self.ca.subject
70
+ ef.issuer_certificate = self.ca
71
+ signing_key = args.indifferent_fetch(:ca_key,self.ca_key)
72
+ end
73
+
74
+ # Copy the extensions (we don't want to modify the original array later)
75
+ exts = args.indifferent_fetch(:extensions, self.extensions).dup
76
+ # filter out blocked extension patterns
77
+ if args.indifferent_fetch(:blockextensions, false)
78
+ args.indifferent_fetch(:blockextensions, nil).each do |badext|
79
+ exts = exts.select { |ext| ext.scan(badext).empty? }
80
+ end
81
+ end
82
+ # add any additional extensions
83
+ exts.concat args.indifferent_fetch(:addextensions,[])
84
+
85
+ # Add the extensions
86
+ exts.each do |ext|
87
+ nc.add_extension(ef.create_ext_from_string(ext))
88
+ end
89
+
90
+ # Look up the signing algorithm. If it is set to a symbol or string,
91
+ # we'll be able to look up a class. Otherwise we assume that the current
92
+ # signing_alg is a class symbol.
93
+ sa = args.indifferent_fetch(:signing_alg, self.signing_alg)
94
+ begin
95
+ sa = OpenSSL::Digest.const_get(sa)
96
+ rescue TypeError
97
+ end
98
+
99
+ nc.sign(signing_key, sa.new)
100
+
101
+ return [nc, nk]
102
+ end
103
+
104
+ end
105
+
106
+ end
@@ -0,0 +1,120 @@
1
+
2
+ module CertMaker
3
+ # Generates a suite of certificates from a configuration.
4
+ #
5
+ # Config should be a hash-like with each key being the alias to a hash-like
6
+ # that describes the certificate. Certificates can then refer to issuers or
7
+ # signing_keys by alias, and CertificateSuiteGenerator will ensure that they
8
+ # are generated in the correct order.
9
+ class CertificateSuiteGenerator
10
+ attr_accessor :certificates
11
+
12
+ # certinfos should be a hash-like where each entry describes a certificate.
13
+ # defaulthostname should be a string that will be inserted into %HOSTNAME%
14
+ # in certificate subject lines.
15
+ def initialize(certinfos, defaulthostname, config={})
16
+ @config = config
17
+ @defaulthostname = defaulthostname
18
+ @parenthostname = defaulthostname.sub(/^[\w-]+\./, '') # remove left-most label
19
+ @certinfos = certinfos
20
+ @certificates = {}
21
+ end
22
+
23
+ def certificates
24
+ generate_certificates if @certificates.empty?
25
+ @certificates
26
+ end
27
+
28
+ def generate_certificates
29
+ @certinfos.each_pair do |calias, certinfo|
30
+ puts ''
31
+ puts ''
32
+ puts calias
33
+ p certinfo
34
+ generate_certificate(calias, certinfo) unless @certificates.has_key? calias
35
+ end
36
+ end
37
+
38
+ def generate_certificate(calias, certinfo)
39
+ puts "Generating #{calias}..."
40
+
41
+ # Check for customgoodca.
42
+ if calias == 'goodca' and @config.has_key? 'customgoodca'
43
+ certfile = @config['customgoodca']['certfile']
44
+ keyfile = @config['customgoodca']['keyfile']
45
+ keypass = @config['customgoodca'].fetch('keypass',nil)
46
+ if File.exist? certfile and File.exist? keyfile
47
+ puts "Customgoodca defined and the CA's files exist. We will use it instead of generating a new goodca."
48
+ rawcert = File.read(certfile)
49
+ rawkey = File.read(keyfile)
50
+ goodcert = OpenSSL::X509::Certificate.new(rawcert)
51
+ goodkey = OpenSSL::PKey.read(rawkey, keypass)
52
+ @certificates[calias] = { :cert => goodcert, :key => goodkey }
53
+ return
54
+ else
55
+ puts "#{certfile} or #{keyfile} does not exist! We will generate a new one."
56
+ end
57
+ end
58
+
59
+ cf = CertificateFactory.new
60
+
61
+ # configure the issuer
62
+ if certinfo['issuer'] == 'self'
63
+ puts "self signed"
64
+ cf.ca = :self
65
+ cf.ca_key = :self
66
+ else
67
+ issueralias = certinfo['issuer']
68
+ puts "issued by #{issueralias.inspect}"
69
+ raise "Issuer #{issueralias} is not in the list of certificates!" unless @certinfos.has_key? issueralias
70
+ generate_certificate(issueralias,@certinfos[issueralias]) unless @certificates.has_key? issueralias
71
+ ca = @certificates[issueralias]
72
+ cf.ca = ca[:cert]
73
+ cf.ca_key = ca[:key] # Use the issuer's key...
74
+ end
75
+ # ... but override the signing key if it is explicitly specified.
76
+ if certinfo.has_key? 'signing_key'
77
+ signeralias = certinfo['signing_key']
78
+ puts "Signed by #{signeralias.inspect}"
79
+ raise "Signer #{signeralias} is not in the list of certificates!" unless @certinfos.has_key? signeralias
80
+ generate_certificate(signeralias,@certinfos[signeralias]) unless @certificates.has_key? signeralias
81
+ cf.ca_key = @certificates[signeralias][:key]
82
+ end
83
+ # doctor the certinfo's subject line and any extensions.
84
+ certinfo['subject'] = certinfo['subject'].gsub(/%HOSTNAME%/, @defaulthostname).gsub(/%PARENTHOSTNAME%/, @parenthostname)
85
+ if certinfo.has_key? 'extensions'
86
+ certinfo['extensions'] = certinfo['extensions'].map { |ext| ext.gsub(/%HOSTNAME%/, @defaulthostname).gsub(/%PARENTHOSTNAME%/, @parenthostname) }
87
+ end
88
+ if certinfo.has_key? 'addextensions'
89
+ certinfo['addextensions'] = certinfo['addextensions'].map { |ext| ext.gsub(/%HOSTNAME%/, @defaulthostname).gsub(/%PARENTHOSTNAME%/, @parenthostname) }
90
+ end
91
+ # doctor the serial number.
92
+ if @config.has_key? 'missing_serial_generation'
93
+ unless certinfo.has_key? 'serial'
94
+ case @config['missing_serial_generation']
95
+ when "random"
96
+ certinfo['serial'] = randomserial
97
+ else
98
+ certinfo['serial'] = @config['missing_serial_generation']
99
+ end
100
+ end
101
+ end
102
+
103
+ cert, key = cf.create(certinfo)
104
+ puts "Created #{calias}"
105
+
106
+ @certificates[calias] = { :cert => cert, :key => key }
107
+ end
108
+
109
+ def randomserial
110
+ range = 2**30
111
+ begin
112
+ require 'securerandom'
113
+ SecureRandom.random_number range
114
+ rescue LoadError # no securerandom, so use weaker rand.
115
+ rand(range)
116
+ end
117
+ end
118
+
119
+ end
120
+ end
@@ -0,0 +1,12 @@
1
+
2
+ class Hash
3
+ # Light extension to hash to add indifferent fetching. Meant to be more
4
+ # lightweight than depending on ActiveSupport for HashWithIndifferentAccess.
5
+ def indifferent_fetch(key, *extra)
6
+ if key.class == Symbol
7
+ self.fetch(key, self.fetch(key.to_s, *extra))
8
+ else
9
+ self.fetch(key, self.fetch(key.to_sym, *extra))
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,27 @@
1
+ require 'certmaker'
2
+ require 'yaml'
3
+ require 'fileutils'
4
+
5
+ module CertMaker
6
+ class Runner
7
+ include FileUtils
8
+
9
+ #Checks for a CA, and generates it if needed.
10
+ def ca
11
+ y = YAML.load_file('config.yml')
12
+ CertMaker.make_ca y
13
+ end
14
+
15
+ #Generate a suite of test certificates
16
+ def certs
17
+ ca
18
+ y = YAML.load_file('config.yml')
19
+ CertMaker.make_certs y
20
+ end
21
+
22
+ #Clean up by deleting the 'certs' directory.
23
+ def clean
24
+ rm_r "certs"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+
2
+ namespace :certs do
3
+
4
+ desc "Checks for a CA, and generates it if needed."
5
+ task :ca do
6
+ require 'certmaker/runner'
7
+ CertMaker::Runner.new.ca
8
+ end
9
+
10
+ desc "Generate a suite of test certificates"
11
+ task :generate => [ 'certs:ca' ] do
12
+ require 'certmaker/runner'
13
+ CertMaker::Runner.new.certs
14
+ end
15
+
16
+ desc "Clean up by deleting the 'certs' directory."
17
+ task :clean do
18
+ rm_r "certs"
19
+ end
20
+ end
@@ -0,0 +1,167 @@
1
+ require 'socket'
2
+ require 'logger'
3
+
4
+ require 'eventmachine'
5
+
6
+ # Framework for intercepting packets, redirecting them to a handler, and doing
7
+ # something with the "stolen" connection.
8
+ #
9
+ # == Description
10
+ #
11
+ # PacketThief is a little Ruby framework for programatically intercepting network
12
+ # traffic. It provides an abstraction around configuring various OS firewalls and
13
+ # for gathering information about the original connection, and it offers several
14
+ # sample handler classes (for use with EventMachine) for intercepting and
15
+ # forwarding traffic. If you want a full fledged security tool for intercepting
16
+ # and analyzing network traffic, check out
17
+ # {Mallory}[http://intrepidusgroup.com/insight/mallory/] instead.
18
+ #
19
+ # PacketThief is currently intended to be run on a computer that should be
20
+ # configured as the gateway for whatever network traffic you wish to intercept.
21
+ # You then use PacketThief to configure your OS firewall and network routing to
22
+ # send specified network traffic to a socket. The socket handling code can then
23
+ # read the data, modify it, send it on to the original destination (although the
24
+ # new connection will originate from the gateway), etc.
25
+ #
26
+ # Currently, PacketThief supports basic redirection using Ipfw (Mac OS X, BSD)
27
+ # and Netfilter (Linux). It also more than likely requires your PacketThief-based
28
+ # script to be run as root in order to modify your system's firewall.
29
+ #
30
+ # == Usage
31
+ #
32
+ # First, you must configure the system that will run PacketThief to be able to
33
+ # intercept network traffic. If that host system has two network interfaces (such
34
+ # as a laptop with ethernet and wifi), then you can turn the system into a router
35
+ # by configure one interface to be an external interface that can communicate
36
+ # with your main network and the Internet, and the other interface can be
37
+ # configured as the gateway for one or more devices/client systems that you would
38
+ # like to test.
39
+ #
40
+ # NATing multiple network interfaces can be easily done in Mac OS X by enabling
41
+ # Internet Sharing in the Sharing System Preference pane, which will also let you
42
+ # configure your wifi to be a wireless access point. For Linux, take a look at
43
+ # the +examples/setup_iptables.sh+ script, or {search the Internet for tutorials
44
+ # on various ways to set up Mallory}[https://bitbucket.org/IntrepidusGroup/mallory/wiki/Home].
45
+ #
46
+ # Basic use:
47
+ #
48
+ # require 'packetthief'
49
+ # # redirect tcp traffic destined for port 443 to localhost port 65432:
50
+ # PacketThief.redirect(:to_ports => 65432).where(:protocol => :tcp, :dest_port => 443).run
51
+ # at_exit { PacketThief.revert } # Remove our firewall rules when we exit
52
+ #
53
+ # This will set up Firewall/routing rules to redirect packets matching the .where
54
+ # clause to the localhost port specified by the redirect() clause. The
55
+ # Kernel#at_exit handler calls the .revert method which will remove the firewall
56
+ # rules added by PacketThief.
57
+ #
58
+ # You can then capture network traffic in your script by opening a listening
59
+ # socket your +:to_ports+ destination. When you create the socket, set the
60
+ # hostname to a blank string to have it listen on all interfaces -- this winds up
61
+ # being more compatible with different firewalls.
62
+ #
63
+ # Using a TCPServer:
64
+ #
65
+ # TCPServer.new('', 65432)
66
+ #
67
+ # or (untested):
68
+ #
69
+ # TCPServer.new(65432)
70
+ #
71
+ # Using EventMachine:
72
+ #
73
+ # EM.run {
74
+ # start_server '', 65432, MyHandler
75
+ # }
76
+ #
77
+ #
78
+ # In your listener code you can recover the original destination by passing in an
79
+ # accepted socket or an EventMachine::Connection-based handler.
80
+ #
81
+ # PacketThief.original_dest(socket_or_em_connection)
82
+ #
83
+ # PacketThief also provides several EventMachine handlers to help build
84
+ # interceptors. For example, PacketThief::Handlers::TransparentProxy provides a
85
+ # class that allows you to view or mangle arbitrary TCP traffic before passing it
86
+ # on to the original destination. The package also includes several handlers for
87
+ # dealing with SSL-based traffic. Unlike EventMachine's built-in SSL support
88
+ # (#start_tls), PacketThief::Handlers::SSLClient and
89
+ # PacketThief::Handlers::SSLServer give you direct access to the
90
+ # OpenSSL::SSL::SSLContext to configure certificates and callbacks, and
91
+ # PacketThief::Handlers::SSLSmartProxy will connect to the original destination
92
+ # in order to acquire its host certificate, which it then modifies for use with a
93
+ # configured CA. See the documentation and the example directory for more
94
+ # information.
95
+ #
96
+ # == Mac OS X Setup example
97
+ #
98
+ # * Share your wifi over your ethernet. Mac OS X will run natd with your wifi as the lan.
99
+ # * Connect a mobile or wifi device to this wifi.
100
+ # * Run your PacketThief code, and specify `:in_interface => 'en1'` (assuming
101
+ # your Airport/wifi is on en1) in the .where clause. The specified :to_ports
102
+ # port should start receiving incoming TCP connections.
103
+ #
104
+ # Note that connections initiated both on the Mac and on any device from the
105
+ # network will hit your socket. In the future, you will be able to narrow down
106
+ # what traffic is caught.
107
+ #
108
+ module PacketThief
109
+ autoload :RedirectRule, 'packetthief/redirect_rule'
110
+ autoload :Impl, 'packetthief/impl'
111
+
112
+ autoload :Handlers, 'packetthief/handlers'
113
+ autoload :Logging, 'packetthief/logging'
114
+ autoload :Util, 'packetthief/util'
115
+
116
+ class << self
117
+ include Logging
118
+ end
119
+
120
+ def self.implementation; @implementation ; end
121
+
122
+ def self.implementation=(newimpl)
123
+ logdebug "Set implementation to: #{newimpl}"
124
+ if newimpl == nil
125
+ @implementation = nil
126
+ elsif newimpl.kind_of? Module
127
+ @implementation = newimpl
128
+ else
129
+ PacketThief::Impl.constants.each do |c|
130
+ if c.downcase.to_sym == newimpl.downcase.to_sym
131
+ @implementation = PacketThief::Impl.const_get c
132
+ return @implementation
133
+ end
134
+ end
135
+ raise AttributeError, "Unknown implementation"
136
+ end
137
+ end
138
+
139
+ def self.guess_implementation
140
+ case RUBY_PLATFORM
141
+ when /linux/
142
+ Impl::Netfilter
143
+ when /darwin(10|[0-9]($|[^0]))/ # Mac OS X 10.6 and earlier.
144
+ Impl::Ipfw
145
+ when /darwin(11)/ # Mac OS X 10.7
146
+ Impl::PFRdr
147
+ else
148
+ raise "Platform #{RUBY_PLATFORM} not yet supported! If you know your network implementation, call it directly."
149
+ end
150
+ end
151
+
152
+ # Pass the call on to @implementation, or an OS-specific default, if one is known.
153
+ def self.method_missing(m, *args, &block)
154
+ logdebug "method_missing: #{m}", :args => args, :block => block
155
+ if self.implementation == nil
156
+ self.implementation = guess_implementation
157
+ end
158
+ implementation.logger = @logger if implementation.respond_to? :logger=
159
+ self.implementation.send(m, *args, &block)
160
+ end
161
+
162
+ # Quietly ignore .revert calls if implementation is nil.
163
+ def self.revert
164
+ implementation.revert if implementation
165
+ end
166
+
167
+ end