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,11 @@
1
+ module PacketThief
2
+ # PacketThief implementations. Each one contains the implementation details
3
+ # for working with a given firewall.
4
+ module Impl
5
+ autoload :Manual, 'packetthief/impl/manual'
6
+ autoload :Netfilter, 'packetthief/impl/netfilter'
7
+ autoload :Ipfw, 'packetthief/impl/ipfw'
8
+ autoload :PFDivert, 'packetthief/impl/pf_divert'
9
+ autoload :PFRdr, 'packetthief/impl/pf_rdr'
10
+ end
11
+ end
@@ -0,0 +1,140 @@
1
+
2
+
3
+ module PacketThief
4
+ module Impl
5
+ # Use Ipfw to redirect traffic.
6
+ #
7
+ # Needed in at least Mac OS X 10.6 and later[1]?:
8
+ # sysctl -w net.inet.ip.scopedroute=0
9
+ #
10
+ # [1]: https://trac.macports.org/wiki/howto/SetupInterceptionSquid
11
+ #
12
+ # Sample rule:
13
+ #
14
+ # sudo ipfw add 1013 fwd 127.0.0.1,3129 tcp from any to any 80 recv INTERFACE
15
+ #
16
+ # Note that in Mac OS X 10.7 Lion, Apple still includes ipfw, but they are
17
+ # using pfctl to do network firewalling and routing.
18
+ class Ipfw
19
+ module IpfwRuleHandler
20
+
21
+ include Logging
22
+
23
+ attr_accessor :active_rules
24
+
25
+ # Executes a rule and holds onto it for later removal.
26
+ def run(rule)
27
+ @active_rules ||= []
28
+
29
+ args = ['/sbin/ipfw', 'add', 'set', '30'] # TODO: make the rule number customizable
30
+
31
+ args.concat rule.to_ipfw_command
32
+
33
+ # Lion claims net.inet.ip.scopedroute is read only. According to: http://pastebin.com/NzAARKVG it is possible to set it at boot time:
34
+ # /Library/Preferences/SystemConfiguration/com.apple.Boot.plist:
35
+ # <dict>
36
+ # <key>Kernel Flags</key>
37
+ # <string>net.inet.ip.scopedroute=0</string>
38
+ # </dict>
39
+ if /darwin/ === RUBY_PLATFORM
40
+ unless system(*%W{/usr/sbin/sysctl -w net.inet.ip.scopedroute=0})
41
+ if /darwin1[1-9]/ === RUBY_PLATFORM
42
+ logerror "Failed to set net.inet.ip.scopedroute=0. As of Lion, this is marked read-only after boot. However, you might be able to get IPFW working by setting the sysctl in /Library/Preferences/SystemConfiguration/com.apple.Boot.plist"
43
+ else
44
+ raise "Command /usr/sbin/sysctl -w net.inet.ip.scopedroute=0 exited with error code #{$?.inspect}."
45
+ end
46
+ end
47
+ end
48
+
49
+ # run the command
50
+ unless system(*args)
51
+ raise "Command #{args.inspect} exited with error code #{$?.inspect}"
52
+ end
53
+
54
+ @active_rules << rule
55
+ end
56
+
57
+ # Reverts all executed rules that this handler knows about.
58
+ def revert
59
+ return if @active_rules == nil or @active_rules.empty?
60
+
61
+ # @active_rules.each do |rule|
62
+ args = ['/sbin/ipfw', 'del', 'set', '30']
63
+ # args.concat rule.to_ipfw_command
64
+ unless system(*args)
65
+ raise "Command #{args.inspect} exited with error code #{$?.inspect}"
66
+ end
67
+ # end
68
+
69
+ @active_rules = []
70
+ end
71
+ end
72
+ extend IpfwRuleHandler
73
+
74
+ class IpfwRule < RedirectRule
75
+
76
+ attr_accessor :rule_number
77
+
78
+ def initialize(handler, rule_number=nil)
79
+ super(handler)
80
+ @rule_number = rule_number
81
+ end
82
+
83
+ def to_ipfw_command
84
+ args = []
85
+
86
+ if self.redirectspec
87
+ if self.redirectspec.has_key? :to_ports
88
+ args << 'fwd'
89
+ args << "127.0.0.1,#{self.redirectspec[:to_ports].to_s}"
90
+ else
91
+ raise "Rule lacks a valid redirect: #{self.inspect}"
92
+ end
93
+ end
94
+
95
+ if self.rulespec
96
+ args << self.rulespec.fetch(:protocol,'ip').to_s
97
+
98
+ args << 'from'
99
+ args << self.rulespec.fetch(:source_address, 'any').to_s
100
+ args << self.rulespec[:source_port].to_s if self.rulespec.has_key? :source_port
101
+
102
+ args << 'to'
103
+ args << self.rulespec.fetch(:dest_address, 'any').to_s
104
+ args << 'dst-port' << self.rulespec[:dest_port].to_s if self.rulespec.has_key? :dest_port
105
+
106
+ args << 'recv' << self.rulespec[:in_interface].to_s if self.rulespec.has_key? :in_interface
107
+ end
108
+
109
+ args
110
+ end
111
+ end
112
+
113
+ def self.redirect(args={})
114
+ rule = IpfwRule.new(self)
115
+ rule.redirect(args)
116
+ end
117
+
118
+ # Returns the [port, host] for the original destination of +sock+.
119
+ #
120
+ # +Sock+ can be a Ruby socket or an EventMachine::Connection (including
121
+ # handler modules, which are mixed in to an anonymous descendent of
122
+ # EM::Connection).
123
+ #
124
+ # When Ipfw uses a fwd/forward rule to redirect a connection to a local
125
+ # socket, the destination address remains unchanged, meaning that C's
126
+ # getsockname() will return the original destination.
127
+ def self.original_dest(sock)
128
+ if sock.respond_to? :getsockname
129
+ sockname = sock.getsockname
130
+ elsif sock.respond_to? :get_sockname
131
+ sockname = sock.get_sockname
132
+ else
133
+ raise ArgumentError, "#{sock.inspect} supports neither :getsockname nor :get_sockname!"
134
+ end
135
+ Socket::unpack_sockaddr_in(sockname)
136
+ end
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,54 @@
1
+
2
+
3
+ module PacketThief
4
+ module Impl
5
+ # PacketThief implementation that does apply any firewall rules. Furthermore,
6
+ # Manual.original_dest will always return a pre-configured destination address.
7
+ class Manual
8
+ module NullRuleHandler
9
+
10
+ include Logging
11
+
12
+ attr_accessor :active_rules
13
+
14
+ # Executes a rule and holds onto it for later removal.
15
+ def run(rule)
16
+ end
17
+
18
+ # Reverts all executed rules that this handler knows about.
19
+ def revert
20
+ end
21
+ end
22
+ extend NullRuleHandler
23
+
24
+ class NullRule < RedirectRule
25
+ def initialize(handler, rule_number=nil)
26
+ super(handler)
27
+ end
28
+ end
29
+
30
+ def self.redirect(args={})
31
+ rule = NullRule.new(self)
32
+ rule.redirect(args)
33
+ end
34
+
35
+ def self.set_dest(host, port)
36
+ @dest_host = host
37
+ @dest_port = port
38
+ end
39
+
40
+ # Returns the [port, host] for the original destination of +sock+.
41
+ #
42
+ # The Manual implementation only returns a preconfigured original
43
+ # destination. Making it only good for testing clients that only talk to a
44
+ # single remote host.
45
+ def self.original_dest(sock)
46
+ raise "You must call .set_dest(host,port) to set the original_dest in the Manual PacketThief implementation!" if @dest_host == nil or @dest_port == nil
47
+ return [ @dest_port, @dest_host ]
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,109 @@
1
+
2
+ module PacketThief
3
+ module Impl
4
+ # PacketThief implemented using the Linux kernel's Netfilter.
5
+ #
6
+ # This is roughly equivalent to:
7
+ #
8
+ # echo 1 > /proc/sys/net/ipv4/ip_forward
9
+ # iptables -t nat -A PREROUTING -p tcp --destination-port <DEST> -j REDIRECT --to-ports <LISTENER>
10
+ #
11
+ # Currently only implements IPv4.
12
+ #
13
+ # Note that the listening socket must have a blank hostname. If it is set to
14
+ # 127.0.0.1, then the socket will only run on the loopback device, and
15
+ # traffic that gets redirected from another device won't reach it.
16
+ class Netfilter
17
+
18
+ # Manages IPTablesRules. It actually runs the rule, and it tracks the rule
19
+ # so it can be deleted later.
20
+ module IPTablesRuleHandler
21
+ attr_accessor :active_rules
22
+
23
+ # Executes a rule and holds onto it for later removal.
24
+ def run(rule)
25
+ @active_rules ||= []
26
+
27
+ args = ['/sbin/iptables', '-t', rule.table, '-A', rule.chain]
28
+
29
+ args.concat rule.to_netfilter_command
30
+
31
+ unless system(*args)
32
+ raise "Command #{args.inspect} exited with error code #{$?.inspect}"
33
+ end
34
+
35
+ @active_rules << rule
36
+ end
37
+
38
+ # Reverts all executed rules that this handler knows about.
39
+ def revert
40
+ return if @active_rules == nil or @active_rules.empty?
41
+
42
+ @active_rules.each do |rule|
43
+ args = ['/sbin/iptables', '-t', rule.table, '-D', rule.chain]
44
+ args.concat rule.to_netfilter_command
45
+
46
+ unless system(*args)
47
+ raise "Command #{args.inspect} exited with error code #{$?.inspect}"
48
+ end
49
+ end
50
+
51
+ @active_rules = []
52
+ end
53
+ end
54
+ extend IPTablesRuleHandler
55
+
56
+ # Adds IPTables specific details to a Redirectrule.
57
+ class IPTablesRule < RedirectRule
58
+
59
+ attr_accessor :table
60
+ attr_accessor :chain
61
+
62
+ def initialize(handler, table, chain)
63
+ super(handler)
64
+ @table = table
65
+ @chain = chain
66
+ end
67
+
68
+ def to_netfilter_command
69
+ args = []
70
+
71
+ if self.rulespec
72
+ args << '-p' << self.rulespec[:protocol].to_s if self.rulespec.has_key? :protocol
73
+ args << '--destination-port' << self.rulespec[:dest_port].to_s if self.rulespec.has_key? :dest_port
74
+ args << '--in-interface' << self.rulespec[:in_interface].to_s if self.rulespec.has_key? :in_interface
75
+ end
76
+
77
+ if self.redirectspec
78
+ args << '-j' << 'REDIRECT'
79
+ args << '--to-ports' << self.redirectspec[:to_ports].to_s if self.redirectspec.has_key? :to_ports
80
+ end
81
+
82
+ args
83
+ end
84
+
85
+ end
86
+
87
+ def self.redirect(args={})
88
+ rule = IPTablesRule.new(self,'nat','PREROUTING')
89
+ rule.redirect(args)
90
+ end
91
+
92
+ #/usr/include/linux/netfilter_ipv4.h:#define SO_ORIGINAL_DST 80
93
+ SO_ORIGINAL_DST = 80
94
+
95
+ # Returns the [port, host] for a socket or EM::Connection that whose
96
+ # connection was redirected by netfilter
97
+ def self.original_dest(socket)
98
+ if socket.respond_to? :getsockopt
99
+ sockname = socket.getsockopt(Socket::IPPROTO_IP, SO_ORIGINAL_DST)
100
+ elsif socket.respond_to? :get_sock_opt
101
+ sockname = socket.get_sock_opt(Socket::IPPROTO_IP, SO_ORIGINAL_DST)
102
+ end
103
+ Socket::unpack_sockaddr_in(sockname)
104
+ end
105
+
106
+ end
107
+
108
+ end
109
+ end
@@ -0,0 +1,168 @@
1
+
2
+
3
+ module PacketThief
4
+ module Impl
5
+ # Untested and likely broken PacketThief implementation that uses PF's
6
+ # divert-to to redirect traffic. It is currently untested, and it requires a
7
+ # newer version of PF to redirect traffic than is available in Mac OS X 10.7
8
+ # (and probably 10.8)
9
+ #
10
+ # == Capturing Traffic
11
+ #
12
+ # It works by dynamically changing the rules in the "packetthief" anchor. To
13
+ # use it, you must add the following to your /etc/pf.conf file in the
14
+ # "Packet Filtering" section::
15
+ #
16
+ # anchor "packetthief"
17
+ #
18
+ # Then you must reload your pf config by rebooting or by executing:
19
+ #
20
+ # sudo pfctl -f /etc/pf.conf
21
+ #
22
+ # When PacketThief adds a rule, it constructs a new rule set for the
23
+ # packetthief anchor and replaces the current ruleset in the anchor by
24
+ # calling:
25
+ #
26
+ # echo "#{our rules}" | pfctl -a packetthief -f -
27
+ #
28
+ # Rules look something like:
29
+ #
30
+ # pass in on en1 proto tcp from any to any port 443 divert-to 127.0.0.1 port 54321
31
+ #
32
+ # == Acquiring the original destination
33
+ #
34
+ # According to [1], if we use a divert-to rule, we can get the original
35
+ # destination using getsockname(2) instead of having to perform ioctl
36
+ # operations on the /dev/pf pseudo-device.
37
+ #
38
+ # [1]: http://www.openbsd.org/cgi-bin/man.cgi?query=pf.conf&sektion=5&arch=&apropos=0&manpath=OpenBSD+Current
39
+ #
40
+ #
41
+ # == Alternative implementations
42
+ #
43
+ # TODO:
44
+ #
45
+ # divert-to is too new of Mac OS X 10.7 (Lion is using a relatively "old"
46
+ # implementation of PF).
47
+ #
48
+ # A possible alternative is to use rdr rules:
49
+ #
50
+ # rdr on en1 proto tcp from any to any port 443 -> 127.0.0.1 port 54321
51
+ #
52
+ # This also requires:
53
+ #
54
+ # rdr-anchor "packetthief"
55
+ #
56
+ # in /etc/pf.conf in the "Translation" section of /etc/pf.conf. We can then
57
+ # use the ioctl with DIOCNATLOOK approach that Squid uses to get a
58
+ # pfioc_natlook data structure to get the original destination of the connection.
59
+ class PFDivert
60
+ module PFDivertRuleHandler
61
+ attr_accessor :active_rules
62
+
63
+ # Executes a rule and holds onto it for later removal.
64
+ def run(rule)
65
+ @active_rules ||= []
66
+
67
+ # args = ['pfctl', 'add', 'set', '30'] # TODO: make the rule number customizable
68
+ args = ['echo']
69
+
70
+ @active_rules << rule
71
+ args << @active_rules.map { |r| r.to_pf_command.join(" ") }.join("\n")
72
+
73
+ args = args + %w{| pfctl -a packetthief -f -}
74
+
75
+ # run the command
76
+ unless system(*args)
77
+ raise "Command #{args.inspect} exited with error code #{$?.inspect}"
78
+ end
79
+
80
+ end
81
+
82
+ # Reverts all executed rules that this handler knows about.
83
+ def revert
84
+ return if @active_rules == nil or @active_rules.empty?
85
+
86
+ args = %W{pfctl -a packetthief -F rules}
87
+ unless system(*args)
88
+ raise "Command #{args.inspect} exited with error code #{$?.inspect}"
89
+ end
90
+ # end
91
+
92
+ @active_rules = []
93
+ end
94
+ end
95
+ extend PFDivertRuleHandler
96
+
97
+ class PFDivertRule < RedirectRule
98
+
99
+ attr_accessor :rule_number
100
+
101
+ def initialize(handler, rule_number=nil)
102
+ super(handler)
103
+ @rule_number = rule_number
104
+ end
105
+
106
+ def to_pf_command
107
+ args = []
108
+
109
+ args << "pass" << "in"
110
+
111
+ if self.rulespec
112
+ args << 'on' << self.rulespec[:in_interface].to_s if self.rulespec.has_key? :in_interface
113
+
114
+ args << "proto" << self.rulespec.fetch(:protocol,'ip').to_s
115
+
116
+ args << 'from'
117
+ args << self.rulespec.fetch(:source_address, 'any').to_s
118
+ args << 'port' << self.rulespec[:source_port].to_s if self.rulespec.has_key? :source_port
119
+
120
+ args << 'to'
121
+ args << self.rulespec.fetch(:dest_address, 'any').to_s
122
+ args << 'port' << self.rulespec[:dest_port].to_s if self.rulespec.has_key? :dest_port
123
+ end
124
+
125
+ if self.redirectspec
126
+ if self.redirectspec.has_key? :to_ports
127
+ args << 'divert-to'
128
+ args << "127.0.0.1"
129
+ args << 'port' << self.redirectspec[:to_ports].to_s if self.redirectspec.has_key? :to_ports
130
+ else
131
+ raise "Rule lacks a valid redirect: #{self.inspect}"
132
+ end
133
+ end
134
+
135
+
136
+ args
137
+ end
138
+ end
139
+
140
+ def self.redirect(args={})
141
+ rule = PFDivertRule.new(self)
142
+ rule.redirect(args)
143
+ end
144
+
145
+ # Returns the [port, host] for the original destination of +sock+.
146
+ #
147
+ # +Sock+ can be a Ruby socket or an EventMachine::Connection (including
148
+ # handler modules, which are mixed in to an anonymous descendent of
149
+ # EM::Connection).
150
+ #
151
+ # When PF uses a divert-to[1] rule to redirect a connection to a local socket,
152
+ # the destination address remains unchanged, meaning that C's getsockname()
153
+ # will return the original destination.
154
+ # [1]: http://www.openbsd.org/cgi-bin/man.cgi?query=pf.conf&sektion=5&arch=&apropos=0&manpath=OpenBSD+Current
155
+ def self.original_dest(sock)
156
+ if sock.respond_to? :getsockname
157
+ sockname = sock.getsockname
158
+ elsif sock.respond_to? :get_sockname
159
+ sockname = sock.get_sockname
160
+ else
161
+ raise ArgumentError, "#{sock.inspect} supports neither :getsockname nor :get_sockname!"
162
+ end
163
+ Socket::unpack_sockaddr_in(sockname)
164
+ end
165
+ end
166
+
167
+ end
168
+ end