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