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,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