ssl_scan 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,526 @@
1
+ # -*- coding: binary -*-
2
+ require 'singleton'
3
+ require 'ssl_scan/socket'
4
+ require 'ssl_scan/socket/tcp'
5
+ require 'ssl_scan/socket/ssl_tcp'
6
+ require 'ssl_scan/socket/ssl_tcp_server'
7
+ require 'ssl_scan/socket/udp'
8
+ require 'ssl_scan/socket/ip'
9
+ require 'timeout'
10
+
11
+ ###
12
+ #
13
+ # Local communication class factory.
14
+ #
15
+ ###
16
+ class SSLScan::Socket::Comm::Local
17
+
18
+ include Singleton
19
+ include SSLScan::Socket::Comm
20
+
21
+ #
22
+ # Creates an instance of a socket using the supplied parameters.
23
+ #
24
+ def self.create(param)
25
+
26
+ # Work around jRuby socket implementation issues
27
+ if(RUBY_PLATFORM == 'java')
28
+ return self.create_jruby(param)
29
+ end
30
+
31
+ case param.proto
32
+ when 'tcp'
33
+ return create_by_type(param, ::Socket::SOCK_STREAM, ::Socket::IPPROTO_TCP)
34
+ when 'udp'
35
+ return create_by_type(param, ::Socket::SOCK_DGRAM, ::Socket::IPPROTO_UDP)
36
+ when 'ip'
37
+ return create_ip(param)
38
+ else
39
+ raise SSLScan::UnsupportedProtocol.new(param.proto), caller
40
+ end
41
+ end
42
+
43
+ #
44
+ # Creates an instance of a socket using the supplied parameters.
45
+ # Use various hacks to make this work with jRuby
46
+ #
47
+ def self.create_jruby(param)
48
+ sock = nil
49
+
50
+ # Notify handlers of the before socket create event.
51
+ self.instance.notify_before_socket_create(self, param)
52
+
53
+ case param.proto
54
+ when 'tcp'
55
+ if (param.server?)
56
+ sock = TCPServer.new(param.localport, param.localhost)
57
+ klass = SSLScan::Socket::TcpServer
58
+ if (param.ssl)
59
+ klass = SSLScan::Socket::SslTcpServer
60
+ end
61
+ sock.extend(klass)
62
+
63
+ else
64
+ sock = TCPSocket.new(param.peerhost, param.peerport)
65
+ klass = SSLScan::Socket::Tcp
66
+ if (param.ssl)
67
+ klass = SSLScan::Socket::SslTcp
68
+ end
69
+ sock.extend(klass)
70
+ end
71
+ when 'udp'
72
+ if (param.server?)
73
+ sock = UDPServer.new(param.localport, param.localhost)
74
+ klass = SSLScan::Socket::UdpServer
75
+ sock.extend(klass)
76
+ else
77
+ sock = UDPSocket.new(param.peerhost, param.peerport)
78
+ klass = SSLScan::Socket::Udp
79
+ sock.extend(klass)
80
+ end
81
+ else
82
+ raise SSLScan::UnsupportedProtocol.new(param.proto), caller
83
+ end
84
+
85
+ sock.initsock(param)
86
+ self.instance.notify_socket_created(self, sock, param)
87
+ return sock
88
+ end
89
+
90
+
91
+ #
92
+ # Creates a raw IP socket using the supplied Parameter instance.
93
+ # Special-cased because of how different it is from UDP/TCP
94
+ #
95
+ def self.create_ip(param)
96
+ self.instance.notify_before_socket_create(self, param)
97
+
98
+ sock = ::Socket.open(::Socket::PF_INET, ::Socket::SOCK_RAW, ::Socket::IPPROTO_RAW)
99
+ sock.setsockopt(::Socket::IPPROTO_IP, ::Socket::IP_HDRINCL, 1)
100
+
101
+ # Configure broadcast support
102
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
103
+
104
+ if (param.bare? == false)
105
+ sock.extend(::SSLScan::Socket::Ip)
106
+ sock.initsock(param)
107
+ end
108
+
109
+ self.instance.notify_socket_created(self, sock, param)
110
+
111
+ sock
112
+ end
113
+
114
+
115
+ #
116
+ # Creates a socket using the supplied Parameter instance.
117
+ #
118
+ def self.create_by_type(param, type, proto = 0)
119
+
120
+ # Whether to use IPv6 addressing
121
+ usev6 = false
122
+
123
+ # Detect IPv6 addresses and enable IPv6 accordingly
124
+ if ( SSLScan::Socket.support_ipv6?())
125
+
126
+ # Allow the caller to force IPv6
127
+ if (param.v6)
128
+ usev6 = true
129
+ end
130
+
131
+ # Force IPv6 mode for non-connected UDP sockets
132
+ if (type == ::Socket::SOCK_DGRAM and not param.peerhost)
133
+ # FreeBSD allows IPv6 socket creation, but throws an error on sendto()
134
+ # Windows 7 SP1 and newer also fail to sendto with IPv6 udp sockets
135
+ unless SSLScan::Compat.is_freebsd or SSLScan::Compat.is_windows
136
+ usev6 = true
137
+ end
138
+ end
139
+
140
+ local = SSLScan::Socket.resolv_nbo(param.localhost) if param.localhost
141
+ peer = SSLScan::Socket.resolv_nbo(param.peerhost) if param.peerhost
142
+
143
+ if (local and local.length == 16)
144
+ usev6 = true
145
+ end
146
+
147
+ if (peer and peer.length == 16)
148
+ usev6 = true
149
+ end
150
+
151
+ if (usev6)
152
+ if (local and local.length == 4)
153
+ if (local == "\x00\x00\x00\x00")
154
+ param.localhost = '::'
155
+ elsif (local == "\x7f\x00\x00\x01")
156
+ param.localhost = '::1'
157
+ else
158
+ param.localhost = '::ffff:' + SSLScan::Socket.getaddress(param.localhost, true)
159
+ end
160
+ end
161
+
162
+ if (peer and peer.length == 4)
163
+ if (peer == "\x00\x00\x00\x00")
164
+ param.peerhost = '::'
165
+ elsif (peer == "\x7f\x00\x00\x01")
166
+ param.peerhost = '::1'
167
+ else
168
+ param.peerhost = '::ffff:' + SSLScan::Socket.getaddress(param.peerhost, true)
169
+ end
170
+ end
171
+
172
+ param.v6 = true
173
+ end
174
+ else
175
+ # No IPv6 support
176
+ param.v6 = false
177
+ end
178
+
179
+ # Notify handlers of the before socket create event.
180
+ self.instance.notify_before_socket_create(self, param)
181
+
182
+ # Create the socket
183
+ sock = nil
184
+ if (param.v6)
185
+ sock = ::Socket.new(::Socket::AF_INET6, type, proto)
186
+ else
187
+ sock = ::Socket.new(::Socket::AF_INET, type, proto)
188
+ end
189
+
190
+ # Bind to a given local address and/or port if they are supplied
191
+ if param.localport or param.localhost
192
+ begin
193
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_REUSEADDR, true)
194
+ sock.bind(SSLScan::Socket.to_sockaddr(param.localhost, param.localport))
195
+
196
+ rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
197
+ sock.close
198
+ raise SSLScan::AddressInUse.new(param.localhost, param.localport), caller
199
+ end
200
+ end
201
+
202
+ # Configure broadcast support for all datagram sockets
203
+ if (type == ::Socket::SOCK_DGRAM)
204
+ sock.setsockopt(::Socket::SOL_SOCKET, ::Socket::SO_BROADCAST, true)
205
+ end
206
+
207
+ # If a server TCP instance is being created...
208
+ if (param.server?)
209
+ sock.listen(256)
210
+
211
+ if (param.bare? == false)
212
+ klass = SSLScan::Socket::TcpServer
213
+ if (param.ssl)
214
+ klass = SSLScan::Socket::SslTcpServer
215
+ end
216
+ sock.extend(klass)
217
+
218
+ sock.initsock(param)
219
+ end
220
+ # Otherwise, if we're creating a client...
221
+ else
222
+ chain = []
223
+
224
+ # If we were supplied with host information
225
+ if (param.peerhost)
226
+
227
+ # A flag that indicates whether we need to try multiple scopes
228
+ retry_scopes = false
229
+
230
+ # Always retry with link-local IPv6 addresses
231
+ if SSLScan::Socket.is_ipv6?( param.peerhost ) and param.peerhost =~ /^fe80::/
232
+ retry_scopes = true
233
+ end
234
+
235
+ # Prepare a list of scope IDs to try when connecting to
236
+ # link-level addresses. Read from /proc if it is available,
237
+ # otherwise increment through the first 255 IDs.
238
+ @@ip6_lla_scopes ||= []
239
+
240
+ if @@ip6_lla_scopes.length == 0 and retry_scopes
241
+
242
+ # Linux specific interface lookup code
243
+ if ::File.exists?( "/proc/self/net/igmp6" )
244
+ ::File.open("/proc/self/net/igmp6") do |fd|
245
+ fd.each_line do |line|
246
+ line = line.strip
247
+ tscope, tint, junk = line.split(/\s+/, 3)
248
+ next if not tint
249
+
250
+ # Specifying lo in any connect call results in the socket
251
+ # being unusable, even if the correct interface is set.
252
+ next if tint == "lo"
253
+
254
+ @@ip6_lla_scopes << tscope
255
+ end
256
+ end
257
+ else
258
+ # Other Unix-like platforms should support a raw scope ID
259
+ [*(1 .. 255)].map{ |x| @@ip6_lla_scopes << x.to_s }
260
+ end
261
+ end
262
+
263
+ ip6_scope_idx = 0
264
+ ip = param.peerhost
265
+ port = param.peerport
266
+
267
+ if param.proxies
268
+ chain = param.proxies.dup
269
+ chain.push(['host',param.peerhost,param.peerport])
270
+ ip = chain[0][1]
271
+ port = chain[0][2].to_i
272
+ end
273
+
274
+ begin
275
+
276
+ begin
277
+ Timeout.timeout(param.timeout) do
278
+ sock.connect(SSLScan::Socket.to_sockaddr(ip, port))
279
+ end
280
+ rescue ::Timeout::Error
281
+ raise ::Errno::ETIMEDOUT
282
+ end
283
+
284
+ rescue ::Errno::EHOSTUNREACH,::Errno::ENETDOWN,::Errno::ENETUNREACH,::Errno::ENETRESET,::Errno::EHOSTDOWN,::Errno::EACCES,::Errno::EINVAL
285
+
286
+ # Rescue errors caused by a bad Scope ID for a link-local address
287
+ if retry_scopes and @@ip6_lla_scopes[ ip6_scope_idx ]
288
+ ip = param.peerhost + "%" + @@ip6_lla_scopes[ ip6_scope_idx ]
289
+ ip6_scope_idx += 1
290
+ retry
291
+ end
292
+
293
+ sock.close
294
+ raise SSLScan::HostUnreachable.new(param.peerhost, param.peerport), caller
295
+
296
+ rescue ::Errno::EADDRNOTAVAIL,::Errno::EADDRINUSE
297
+ sock.close
298
+ raise SSLScan::AddressInUse.new(param.peerhost, param.peerport), caller
299
+
300
+ rescue Errno::ETIMEDOUT
301
+ sock.close
302
+ raise SSLScan::ConnectionTimeout.new(param.peerhost, param.peerport), caller
303
+
304
+ rescue ::Errno::ECONNRESET,::Errno::ECONNREFUSED,::Errno::ENOTCONN,::Errno::ECONNABORTED
305
+ sock.close
306
+ raise SSLScan::ConnectionRefused.new(param.peerhost, param.peerport), caller
307
+ end
308
+ end
309
+
310
+ if (param.bare? == false)
311
+ case param.proto
312
+ when 'tcp'
313
+ klass = SSLScan::Socket::Tcp
314
+ sock.extend(klass)
315
+ sock.initsock(param)
316
+ when 'udp'
317
+ sock.extend(SSLScan::Socket::Udp)
318
+ sock.initsock(param)
319
+ end
320
+ end
321
+
322
+ if chain.size > 1
323
+ chain.each_with_index {
324
+ |proxy, i|
325
+ next_hop = chain[i + 1]
326
+ if next_hop
327
+ proxy(sock, proxy[0], next_hop[1], next_hop[2])
328
+ end
329
+ }
330
+ end
331
+
332
+ # Now extend the socket with SSL and perform the handshake
333
+ if(param.bare? == false and param.ssl)
334
+ klass = SSLScan::Socket::SslTcp
335
+ sock.extend(klass)
336
+ sock.initsock(param)
337
+ end
338
+
339
+
340
+ end
341
+
342
+ # Notify handlers that a socket has been created.
343
+ self.instance.notify_socket_created(self, sock, param)
344
+
345
+ sock
346
+ end
347
+
348
+ def self.proxy(sock, type, host, port)
349
+ case type.downcase
350
+ when 'sapni'
351
+ packet_type = 'NI_ROUTE'
352
+ route_info_version = 2
353
+ ni_version = 39
354
+ num_of_entries = 2
355
+ talk_mode = 1 # ref: http://help.sap.com/saphelp_dimp50/helpdata/En/f8/bb960899d743378ccb8372215bb767/content.htm
356
+ num_rest_nodes = 1
357
+
358
+ shost, sport = sock.peerinfo.split(":")
359
+ first_route_item = [shost, 0, sport, 0, 0].pack("A*CA*cc")
360
+ route_data = [first_route_item.length, first_route_item].pack("NA*")
361
+ route_data << [host, 0, port.to_s, 0, 0].pack("A*CA*cc")
362
+
363
+ ni_packet = [
364
+ packet_type,
365
+ 0,
366
+ route_info_version,
367
+ ni_version,
368
+ num_of_entries,
369
+ talk_mode,
370
+ 0,
371
+ 0,
372
+ num_rest_nodes
373
+ ].pack("A8c8")
374
+ # Add the data block, according to sap documentation:
375
+ # A 4-byte header precedes each data block. These 4 bytes give the
376
+ # length of the data block (length without leading 4 bytes)
377
+ # The data block (the route data)
378
+ ni_packet << [route_data.length - 4].pack('N') + route_data
379
+ # Now that we've built the whole packet, prepend its length before writing it to the wire
380
+ ni_packet = [ni_packet.length].pack('N') + ni_packet
381
+
382
+ size = sock.put(ni_packet)
383
+
384
+ if size != ni_packet.length
385
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
386
+ end
387
+
388
+ begin
389
+ ret_len = sock.get_once(4, 30).unpack('N')[0]
390
+ if ret_len and ret_len != 0
391
+ ret = sock.get_once(ret_len, 30)
392
+ end
393
+ rescue IOError
394
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
395
+ end
396
+
397
+ if ret and ret.length < 4
398
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
399
+ end
400
+
401
+ if ret =~ /NI_RTERR/
402
+ case ret
403
+ when /timed out/
404
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Connection to remote host #{host} timed out")
405
+ when /refused/
406
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Connection to remote port #{port} closed")
407
+ when /denied/
408
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} blocked by ACL")
409
+ else
410
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
411
+ end
412
+ elsif ret =~ /NI_PONG/
413
+ # success case
414
+ # would like to print this "[*] remote native connection to #{host}:#{port} established\n"
415
+ else
416
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Connection to #{host}:#{port} failed (Unknown fail)")
417
+ end
418
+
419
+ when 'http'
420
+ setup = "CONNECT #{host}:#{port} HTTP/1.0\r\n\r\n"
421
+ size = sock.put(setup)
422
+ if (size != setup.length)
423
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
424
+ end
425
+
426
+ begin
427
+ ret = sock.get_once(39,30)
428
+ rescue IOError
429
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
430
+ end
431
+
432
+ if ret.nil?
433
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
434
+ end
435
+
436
+ resp = SSLScan::Proto::Http::Response.new
437
+ resp.update_cmd_parts(ret.split(/\r?\n/)[0])
438
+
439
+ if resp.code != 200
440
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "The proxy returned a non-OK response"), caller
441
+ end
442
+ when 'socks4'
443
+ setup = [4,1,port.to_i].pack('CCn') + Socket.gethostbyname(host)[3] + SSLScan::Text.rand_text_alpha(rand(8)+1) + "\x00"
444
+ size = sock.put(setup)
445
+ if (size != setup.length)
446
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
447
+ end
448
+
449
+ begin
450
+ ret = sock.get_once(8, 30)
451
+ rescue IOError
452
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
453
+ end
454
+
455
+ if (ret.nil? or ret.length < 8)
456
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
457
+ end
458
+ if ret[1,1] != "\x5a"
459
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{ret[0,1].unpack("C")[0]}"), caller
460
+ end
461
+ when 'socks5'
462
+ auth_methods = [5,1,0].pack('CCC')
463
+ size = sock.put(auth_methods)
464
+ if (size != auth_methods.length)
465
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
466
+ end
467
+ ret = sock.get_once(2,30)
468
+ if (ret[1,1] == "\xff")
469
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "The proxy requires authentication"), caller
470
+ end
471
+
472
+ if (SSLScan::Socket.is_ipv4?(host))
473
+ addr = SSLScan::Socket.gethostbyname(host)[3]
474
+ setup = [5,1,0,1].pack('C4') + addr + [port.to_i].pack('n')
475
+ elsif (SSLScan::Socket.support_ipv6? and SSLScan::Socket.is_ipv6?(host))
476
+ # IPv6 stuff all untested
477
+ addr = SSLScan::Socket.gethostbyname(host)[3]
478
+ setup = [5,1,0,4].pack('C4') + addr + [port.to_i].pack('n')
479
+ else
480
+ # Then it must be a domain name.
481
+ # Unfortunately, it looks like the host has always been
482
+ # resolved by the time it gets here, so this code never runs.
483
+ setup = [5,1,0,3].pack('C4') + [host.length].pack('C') + host + [port.to_i].pack('n')
484
+ end
485
+
486
+ size = sock.put(setup)
487
+ if (size != setup.length)
488
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to send the entire request to the proxy"), caller
489
+ end
490
+
491
+ begin
492
+ response = sock.get_once(10, 30)
493
+ rescue IOError
494
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a response from the proxy"), caller
495
+ end
496
+
497
+ if (response.nil? or response.length < 10)
498
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Failed to receive a complete response from the proxy"), caller
499
+ end
500
+ if response[1,1] != "\x00"
501
+ raise SSLScan::ConnectionProxyError.new(host, port, type, "Proxy responded with error code #{response[1,1].unpack("C")[0]}"), caller
502
+ end
503
+ else
504
+ raise RuntimeError, "The proxy type specified is not valid", caller
505
+ end
506
+ end
507
+
508
+ ##
509
+ #
510
+ # Registration
511
+ #
512
+ ##
513
+
514
+ def self.register_event_handler(handler) # :nodoc:
515
+ self.instance.register_event_handler(handler)
516
+ end
517
+
518
+ def self.deregister_event_handler(handler) # :nodoc:
519
+ self.instance.deregister_event_handler(handler)
520
+ end
521
+
522
+ def self.each_event_handler(handler) # :nodoc:
523
+ self.instance.each_event_handler(handler)
524
+ end
525
+
526
+ end
@@ -0,0 +1,120 @@
1
+ # -*- coding: binary -*-
2
+ require 'ssl_scan/socket'
3
+
4
+ module SSLScan
5
+ module Socket
6
+
7
+ ###
8
+ #
9
+ # This mixin provides the basic interface that a derived class must implement
10
+ # in order to be a compatible comm class. The base comm class also supports
11
+ # registering event handlers that can be notified when sockets are being
12
+ # created and have been created. This allows code to extend sockets on
13
+ # creation from the single point that they are created.
14
+ #
15
+ ###
16
+ module Comm
17
+
18
+ ###
19
+ #
20
+ # This mixin provides stubs for event notification handlers that can be
21
+ # registered with a Comm factory to be called when various events occur,
22
+ # such as socket instantiation.
23
+ #
24
+ ###
25
+ module Events
26
+
27
+ #
28
+ # This callback is notified when a socket is being created and is passed
29
+ # the parameters that will be used to create it.
30
+ #
31
+ def on_before_socket_create(comm, param)
32
+ end
33
+
34
+ #
35
+ # This callback is notified when a new socket is created and the
36
+ # parameters that were used to create it. This provides the callback
37
+ # with a chance to extend or otherwise modify the socket before it's
38
+ # passed on to the actual requestor.
39
+ #
40
+ def on_socket_created(comm, sock, param)
41
+ end
42
+
43
+ end
44
+
45
+ #
46
+ # Creates a compatible socket based on the supplied uniform parameters.
47
+ #
48
+ def self.create(param)
49
+ raise NotImplementedError
50
+ end
51
+
52
+ #
53
+ # Indicates whether or not this comm can be chained with other chainable
54
+ # comms. This is particularly important for things like Proxy Comms that
55
+ # can be proxied through one another. The semantics of this are currently
56
+ # undefined and will probably need some more thought.
57
+ #
58
+ def chainable?
59
+ false
60
+ end
61
+
62
+ #
63
+ # Registers an event handler that implements the SSLScan::Socket::Comm::Event
64
+ # interface in at least some fashion. Event handlers are notified when
65
+ # sockets are created through the Comm instance that they register against.
66
+ #
67
+ def register_event_handler(handler)
68
+ if (handlers == nil)
69
+ self.handlers = []
70
+ end
71
+
72
+ self.handlers << handler
73
+ end
74
+
75
+ #
76
+ # Deregisters a previously registered event handler.
77
+ #
78
+ def deregister_event_handler(handler)
79
+ if (handlers)
80
+ handlers.delete(handler)
81
+ end
82
+ end
83
+
84
+ #
85
+ # Enumerates each registered event handler so that they can be notified of
86
+ # an event.
87
+ #
88
+ def each_event_handler(&block)
89
+ if (handlers)
90
+ handlers.each(&block)
91
+ end
92
+ end
93
+
94
+ #
95
+ # Notifies handlers of the before socket create event.
96
+ #
97
+ def notify_before_socket_create(comm, param)
98
+ each_event_handler() { |handler|
99
+ handler.on_before_socket_create(comm, param)
100
+ }
101
+ end
102
+
103
+ #
104
+ # Notifies handlers of the socket created event.
105
+ #
106
+ def notify_socket_created(comm, sock, param)
107
+ each_event_handler() { |handler|
108
+ handler.on_socket_created(comm, sock, param)
109
+ }
110
+ end
111
+
112
+ protected
113
+
114
+ attr_accessor :handlers # :nodoc:
115
+ attr_accessor :handlers_rwlock # :nodoc:
116
+
117
+ end
118
+
119
+ end
120
+ end