eximius-net-ssh 6.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/.dockerignore +6 -0
  3. data/.github/config/rubocop_linter_action.yml +4 -0
  4. data/.github/workflows/ci-with-docker.yml +44 -0
  5. data/.github/workflows/ci.yml +87 -0
  6. data/.github/workflows/rubocop.yml +13 -0
  7. data/.gitignore +13 -0
  8. data/.rubocop.yml +22 -0
  9. data/.rubocop_todo.yml +1072 -0
  10. data/CHANGES.txt +698 -0
  11. data/Dockerfile +27 -0
  12. data/Dockerfile.openssl3 +17 -0
  13. data/Gemfile +13 -0
  14. data/Gemfile.noed25519 +12 -0
  15. data/ISSUE_TEMPLATE.md +30 -0
  16. data/LICENSE.txt +19 -0
  17. data/Manifest +132 -0
  18. data/README.md +293 -0
  19. data/Rakefile +105 -0
  20. data/THANKS.txt +110 -0
  21. data/appveyor.yml +58 -0
  22. data/docker-compose.yml +23 -0
  23. data/lib/net/ssh/authentication/agent.rb +284 -0
  24. data/lib/net/ssh/authentication/certificate.rb +183 -0
  25. data/lib/net/ssh/authentication/constants.rb +20 -0
  26. data/lib/net/ssh/authentication/ed25519.rb +185 -0
  27. data/lib/net/ssh/authentication/ed25519_loader.rb +31 -0
  28. data/lib/net/ssh/authentication/key_manager.rb +310 -0
  29. data/lib/net/ssh/authentication/methods/abstract.rb +79 -0
  30. data/lib/net/ssh/authentication/methods/hostbased.rb +72 -0
  31. data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +77 -0
  32. data/lib/net/ssh/authentication/methods/none.rb +34 -0
  33. data/lib/net/ssh/authentication/methods/password.rb +80 -0
  34. data/lib/net/ssh/authentication/methods/publickey.rb +137 -0
  35. data/lib/net/ssh/authentication/pageant.rb +497 -0
  36. data/lib/net/ssh/authentication/pub_key_fingerprint.rb +43 -0
  37. data/lib/net/ssh/authentication/session.rb +165 -0
  38. data/lib/net/ssh/buffer.rb +440 -0
  39. data/lib/net/ssh/buffered_io.rb +202 -0
  40. data/lib/net/ssh/config.rb +406 -0
  41. data/lib/net/ssh/connection/channel.rb +695 -0
  42. data/lib/net/ssh/connection/constants.rb +33 -0
  43. data/lib/net/ssh/connection/event_loop.rb +123 -0
  44. data/lib/net/ssh/connection/keepalive.rb +59 -0
  45. data/lib/net/ssh/connection/session.rb +712 -0
  46. data/lib/net/ssh/connection/term.rb +180 -0
  47. data/lib/net/ssh/errors.rb +106 -0
  48. data/lib/net/ssh/key_factory.rb +218 -0
  49. data/lib/net/ssh/known_hosts.rb +265 -0
  50. data/lib/net/ssh/loggable.rb +62 -0
  51. data/lib/net/ssh/packet.rb +106 -0
  52. data/lib/net/ssh/prompt.rb +62 -0
  53. data/lib/net/ssh/proxy/command.rb +123 -0
  54. data/lib/net/ssh/proxy/errors.rb +16 -0
  55. data/lib/net/ssh/proxy/http.rb +98 -0
  56. data/lib/net/ssh/proxy/https.rb +50 -0
  57. data/lib/net/ssh/proxy/jump.rb +54 -0
  58. data/lib/net/ssh/proxy/socks4.rb +67 -0
  59. data/lib/net/ssh/proxy/socks5.rb +140 -0
  60. data/lib/net/ssh/service/forward.rb +426 -0
  61. data/lib/net/ssh/test/channel.rb +147 -0
  62. data/lib/net/ssh/test/extensions.rb +173 -0
  63. data/lib/net/ssh/test/kex.rb +46 -0
  64. data/lib/net/ssh/test/local_packet.rb +53 -0
  65. data/lib/net/ssh/test/packet.rb +101 -0
  66. data/lib/net/ssh/test/remote_packet.rb +40 -0
  67. data/lib/net/ssh/test/script.rb +180 -0
  68. data/lib/net/ssh/test/socket.rb +65 -0
  69. data/lib/net/ssh/test.rb +94 -0
  70. data/lib/net/ssh/transport/algorithms.rb +502 -0
  71. data/lib/net/ssh/transport/cipher_factory.rb +103 -0
  72. data/lib/net/ssh/transport/constants.rb +40 -0
  73. data/lib/net/ssh/transport/ctr.rb +115 -0
  74. data/lib/net/ssh/transport/hmac/abstract.rb +97 -0
  75. data/lib/net/ssh/transport/hmac/md5.rb +10 -0
  76. data/lib/net/ssh/transport/hmac/md5_96.rb +9 -0
  77. data/lib/net/ssh/transport/hmac/none.rb +13 -0
  78. data/lib/net/ssh/transport/hmac/ripemd160.rb +11 -0
  79. data/lib/net/ssh/transport/hmac/sha1.rb +11 -0
  80. data/lib/net/ssh/transport/hmac/sha1_96.rb +9 -0
  81. data/lib/net/ssh/transport/hmac/sha2_256.rb +11 -0
  82. data/lib/net/ssh/transport/hmac/sha2_256_96.rb +9 -0
  83. data/lib/net/ssh/transport/hmac/sha2_256_etm.rb +12 -0
  84. data/lib/net/ssh/transport/hmac/sha2_512.rb +11 -0
  85. data/lib/net/ssh/transport/hmac/sha2_512_96.rb +9 -0
  86. data/lib/net/ssh/transport/hmac/sha2_512_etm.rb +12 -0
  87. data/lib/net/ssh/transport/hmac.rb +47 -0
  88. data/lib/net/ssh/transport/identity_cipher.rb +57 -0
  89. data/lib/net/ssh/transport/kex/abstract.rb +130 -0
  90. data/lib/net/ssh/transport/kex/abstract5656.rb +72 -0
  91. data/lib/net/ssh/transport/kex/curve25519_sha256.rb +39 -0
  92. data/lib/net/ssh/transport/kex/curve25519_sha256_loader.rb +30 -0
  93. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +37 -0
  94. data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
  95. data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +122 -0
  96. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +72 -0
  97. data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha256.rb +11 -0
  98. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +39 -0
  99. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp384.rb +21 -0
  100. data/lib/net/ssh/transport/kex/ecdh_sha2_nistp521.rb +21 -0
  101. data/lib/net/ssh/transport/kex.rb +31 -0
  102. data/lib/net/ssh/transport/key_expander.rb +30 -0
  103. data/lib/net/ssh/transport/openssl.rb +262 -0
  104. data/lib/net/ssh/transport/packet_stream.rb +280 -0
  105. data/lib/net/ssh/transport/server_version.rb +77 -0
  106. data/lib/net/ssh/transport/session.rb +354 -0
  107. data/lib/net/ssh/transport/state.rb +208 -0
  108. data/lib/net/ssh/verifiers/accept_new.rb +33 -0
  109. data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +33 -0
  110. data/lib/net/ssh/verifiers/always.rb +58 -0
  111. data/lib/net/ssh/verifiers/never.rb +19 -0
  112. data/lib/net/ssh/version.rb +70 -0
  113. data/lib/net/ssh.rb +330 -0
  114. data/net-ssh-public_cert.pem +20 -0
  115. data/net-ssh.gemspec +44 -0
  116. data/support/ssh_tunnel_bug.rb +65 -0
  117. metadata +277 -0
@@ -0,0 +1,98 @@
1
+ require 'socket'
2
+ require 'net/ssh/proxy/errors'
3
+
4
+ module Net
5
+ module SSH
6
+ module Proxy
7
+ # An implementation of an HTTP proxy. To use it, instantiate it, then
8
+ # pass the instantiated object via the :proxy key to Net::SSH.start:
9
+ #
10
+ # require 'net/ssh/proxy/http'
11
+ #
12
+ # proxy = Net::SSH::Proxy::HTTP.new('proxy_host', proxy_port)
13
+ # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
14
+ # ...
15
+ # end
16
+ #
17
+ # If the proxy requires authentication, you can pass :user and :password
18
+ # to the proxy's constructor:
19
+ #
20
+ # proxy = Net::SSH::Proxy::HTTP.new('proxy_host', proxy_port,
21
+ # :user => "user", :password => "password")
22
+ #
23
+ # Note that HTTP digest authentication is not supported; Basic only at
24
+ # this point.
25
+ class HTTP
26
+ # The hostname or IP address of the HTTP proxy.
27
+ attr_reader :proxy_host
28
+
29
+ # The port number of the proxy.
30
+ attr_reader :proxy_port
31
+
32
+ # The map of additional options that were given to the object at
33
+ # initialization.
34
+ attr_reader :options
35
+
36
+ # Create a new socket factory that tunnels via the given host and
37
+ # port. The +options+ parameter is a hash of additional settings that
38
+ # can be used to tweak this proxy connection. Specifically, the following
39
+ # options are supported:
40
+ #
41
+ # * :user => the user name to use when authenticating to the proxy
42
+ # * :password => the password to use when authenticating
43
+ def initialize(proxy_host, proxy_port = 80, options = {})
44
+ @proxy_host = proxy_host
45
+ @proxy_port = proxy_port
46
+ @options = options
47
+ end
48
+
49
+ # Return a new socket connected to the given host and port via the
50
+ # proxy that was requested when the socket factory was instantiated.
51
+ def open(host, port, connection_options)
52
+ socket = establish_connection(connection_options[:timeout])
53
+ socket.write "CONNECT #{host}:#{port} HTTP/1.1\r\n"
54
+ socket.write "Host: #{host}:#{port}\r\n"
55
+
56
+ if options[:user]
57
+ credentials = ["#{options[:user]}:#{options[:password]}"].pack("m*").gsub(/\s/, "")
58
+ socket.write "Proxy-Authorization: Basic #{credentials}\r\n"
59
+ end
60
+
61
+ socket.write "\r\n"
62
+
63
+ resp = parse_response(socket)
64
+
65
+ return socket if resp[:code] == 200
66
+
67
+ socket.close
68
+ raise ConnectError, resp.inspect
69
+ end
70
+
71
+ protected
72
+
73
+ def establish_connection(connect_timeout)
74
+ Socket.tcp(proxy_host, proxy_port, nil, nil,
75
+ connect_timeout: connect_timeout)
76
+ end
77
+
78
+ def parse_response(socket)
79
+ version, code, reason = socket.gets.chomp.split(/ /, 3)
80
+ headers = {}
81
+
82
+ while (line = socket.gets) && (line.chomp! != "")
83
+ name, value = line.split(/:/, 2)
84
+ headers[name.strip] = value.strip
85
+ end
86
+
87
+ body = socket.read(headers["Content-Length"].to_i) if headers["Content-Length"]
88
+
89
+ return { version: version,
90
+ code: code.to_i,
91
+ reason: reason,
92
+ headers: headers,
93
+ body: body }
94
+ end
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,50 @@
1
+ require 'socket'
2
+ require 'openssl'
3
+ require 'net/ssh/proxy/errors'
4
+ require 'net/ssh/proxy/http'
5
+
6
+ module Net
7
+ module SSH
8
+ module Proxy
9
+ # A specialization of the HTTP proxy which encrypts the whole connection
10
+ # using OpenSSL. This has the advantage that proxy authentication
11
+ # information is not sent in plaintext.
12
+ class HTTPS < HTTP
13
+ # Create a new socket factory that tunnels via the given host and
14
+ # port. The +options+ parameter is a hash of additional settings that
15
+ # can be used to tweak this proxy connection. In addition to the options
16
+ # taken by Net::SSH::Proxy::HTTP it supports:
17
+ #
18
+ # * :ssl_context => the SSL configuration to use for the connection
19
+ def initialize(proxy_host, proxy_port = 80, options = {})
20
+ @ssl_context = options.delete(:ssl_context) ||
21
+ OpenSSL::SSL::SSLContext.new
22
+ super(proxy_host, proxy_port, options)
23
+ end
24
+
25
+ protected
26
+
27
+ # Shim to make OpenSSL::SSL::SSLSocket behave like a regular TCPSocket
28
+ # for all intents and purposes of Net::SSH::BufferedIo
29
+ module SSLSocketCompatibility
30
+ def self.extended(object) # :nodoc:
31
+ object.define_singleton_method(:recv, object.method(:sysread))
32
+ object.sync_close = true
33
+ end
34
+
35
+ def send(data, _opts)
36
+ syswrite(data)
37
+ end
38
+ end
39
+
40
+ def establish_connection(connect_timeout)
41
+ plain_socket = super(connect_timeout)
42
+ OpenSSL::SSL::SSLSocket.new(plain_socket, @ssl_context).tap do |socket|
43
+ socket.extend(SSLSocketCompatibility)
44
+ socket.connect
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ require 'uri'
2
+ require 'net/ssh/proxy/command'
3
+
4
+ module Net
5
+ module SSH
6
+ module Proxy
7
+ # An implementation of a jump proxy. To use it, instantiate it,
8
+ # then pass the instantiated object via the :proxy key to
9
+ # Net::SSH.start:
10
+ #
11
+ # require 'net/ssh/proxy/jump'
12
+ #
13
+ # proxy = Net::SSH::Proxy::Jump.new('user@proxy')
14
+ # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
15
+ # ...
16
+ # end
17
+ class Jump < Command
18
+ # The jump proxies
19
+ attr_reader :jump_proxies
20
+
21
+ # Create a new socket factory that tunnels via multiple jump proxes as
22
+ # [user@]host[:port].
23
+ def initialize(jump_proxies)
24
+ @jump_proxies = jump_proxies
25
+ end
26
+
27
+ # Return a new socket connected to the given host and port via the jump
28
+ # proxy that was requested when the socket factory was instantiated.
29
+ def open(host, port, connection_options = nil)
30
+ build_proxy_command_equivalent(connection_options)
31
+ super
32
+ end
33
+
34
+ # We cannot build the ProxyCommand template until we know if the :config
35
+ # option was specified during `Net::SSH.start`.
36
+ def build_proxy_command_equivalent(connection_options = nil)
37
+ first_jump, extra_jumps = jump_proxies.split(",", 2)
38
+ config = connection_options && connection_options[:config]
39
+ uri = URI.parse("ssh://#{first_jump}")
40
+
41
+ template = "ssh".dup
42
+ template << " -l #{uri.user}" if uri.user
43
+ template << " -p #{uri.port}" if uri.port
44
+ template << " -J #{extra_jumps}" if extra_jumps
45
+ template << " -F #{config}" if config != true && config
46
+ template << " -W %h:%p "
47
+ template << uri.host
48
+
49
+ @command_line_template = template
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,67 @@
1
+ require 'socket'
2
+ require 'resolv'
3
+ require 'ipaddr'
4
+ require 'net/ssh/proxy/errors'
5
+
6
+ module Net
7
+ module SSH
8
+ module Proxy
9
+ # An implementation of a SOCKS4 proxy. To use it, instantiate it, then
10
+ # pass the instantiated object via the :proxy key to Net::SSH.start:
11
+ #
12
+ # require 'net/ssh/proxy/socks4'
13
+ #
14
+ # proxy = Net::SSH::Proxy::SOCKS4.new('proxy.host', proxy_port, :user => 'user')
15
+ # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
16
+ # ...
17
+ # end
18
+ class SOCKS4
19
+ # The SOCKS protocol version used by this class
20
+ VERSION = 4
21
+
22
+ # The packet type for connection requests
23
+ CONNECT = 1
24
+
25
+ # The status code for a successful connection
26
+ GRANTED = 90
27
+
28
+ # The proxy's host name or IP address, as given to the constructor.
29
+ attr_reader :proxy_host
30
+
31
+ # The proxy's port number.
32
+ attr_reader :proxy_port
33
+
34
+ # The additional options that were given to the proxy's constructor.
35
+ attr_reader :options
36
+
37
+ # Create a new proxy connection to the given proxy host and port.
38
+ # Optionally, a :user key may be given to identify the username
39
+ # with which to authenticate.
40
+ def initialize(proxy_host, proxy_port = 1080, options = {})
41
+ @proxy_host = proxy_host
42
+ @proxy_port = proxy_port
43
+ @options = options
44
+ end
45
+
46
+ # Return a new socket connected to the given host and port via the
47
+ # proxy that was requested when the socket factory was instantiated.
48
+ def open(host, port, connection_options)
49
+ socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
50
+ connect_timeout: connection_options[:timeout])
51
+ ip_addr = IPAddr.new(Resolv.getaddress(host))
52
+
53
+ packet = [VERSION, CONNECT, port.to_i, ip_addr.to_i, options[:user]].pack("CCnNZ*")
54
+ socket.send packet, 0
55
+
56
+ version, status, port, ip = socket.recv(8).unpack("CCnN")
57
+ if status != GRANTED
58
+ socket.close
59
+ raise ConnectError, "error connecting to proxy (#{status})"
60
+ end
61
+
62
+ return socket
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,140 @@
1
+ require 'socket'
2
+ require 'net/ssh/proxy/errors'
3
+
4
+ module Net
5
+ module SSH
6
+ module Proxy
7
+ # An implementation of a SOCKS5 proxy. To use it, instantiate it, then
8
+ # pass the instantiated object via the :proxy key to Net::SSH.start:
9
+ #
10
+ # require 'net/ssh/proxy/socks5'
11
+ #
12
+ # proxy = Net::SSH::Proxy::SOCKS5.new('proxy.host', proxy_port,
13
+ # :user => 'user', :password => "password")
14
+ # Net::SSH.start('host', 'user', :proxy => proxy) do |ssh|
15
+ # ...
16
+ # end
17
+ class SOCKS5
18
+ # The SOCKS protocol version used by this class
19
+ VERSION = 5
20
+
21
+ # The SOCKS authentication type for requests without authentication
22
+ METHOD_NO_AUTH = 0
23
+
24
+ # The SOCKS authentication type for requests via username/password
25
+ METHOD_PASSWD = 2
26
+
27
+ # The SOCKS authentication type for when there are no supported
28
+ # authentication methods.
29
+ METHOD_NONE = 0xFF
30
+
31
+ # The SOCKS packet type for requesting a proxy connection.
32
+ CMD_CONNECT = 1
33
+
34
+ # The SOCKS address type for connections via IP address.
35
+ ATYP_IPV4 = 1
36
+
37
+ # The SOCKS address type for connections via domain name.
38
+ ATYP_DOMAIN = 3
39
+
40
+ # The SOCKS response code for a successful operation.
41
+ SUCCESS = 0
42
+
43
+ # The proxy's host name or IP address
44
+ attr_reader :proxy_host
45
+
46
+ # The proxy's port number
47
+ attr_reader :proxy_port
48
+
49
+ # The map of options given at initialization
50
+ attr_reader :options
51
+
52
+ # Create a new proxy connection to the given proxy host and port.
53
+ # Optionally, :user and :password options may be given to
54
+ # identify the username and password with which to authenticate.
55
+ def initialize(proxy_host, proxy_port = 1080, options = {})
56
+ @proxy_host = proxy_host
57
+ @proxy_port = proxy_port
58
+ @options = options
59
+ end
60
+
61
+ # Return a new socket connected to the given host and port via the
62
+ # proxy that was requested when the socket factory was instantiated.
63
+ def open(host, port, connection_options)
64
+ socket = Socket.tcp(proxy_host, proxy_port, nil, nil,
65
+ connect_timeout: connection_options[:timeout])
66
+
67
+ methods = [METHOD_NO_AUTH]
68
+ methods << METHOD_PASSWD if options[:user]
69
+
70
+ packet = [VERSION, methods.size, *methods].pack("C*")
71
+ socket.send packet, 0
72
+
73
+ version, method = socket.recv(2).unpack("CC")
74
+ if version != VERSION
75
+ socket.close
76
+ raise Net::SSH::Proxy::Error, "invalid SOCKS version (#{version})"
77
+ end
78
+
79
+ if method == METHOD_NONE
80
+ socket.close
81
+ raise Net::SSH::Proxy::Error, "no supported authorization methods"
82
+ end
83
+
84
+ negotiate_password(socket) if method == METHOD_PASSWD
85
+
86
+ packet = [VERSION, CMD_CONNECT, 0].pack("C*")
87
+
88
+ if host =~ /^(\d+)\.(\d+)\.(\d+)\.(\d+)$/
89
+ packet << [ATYP_IPV4, $1.to_i, $2.to_i, $3.to_i, $4.to_i].pack("C*")
90
+ else
91
+ packet << [ATYP_DOMAIN, host.length, host].pack("CCA*")
92
+ end
93
+
94
+ packet << [port].pack("n")
95
+ socket.send packet, 0
96
+
97
+ version, reply, = socket.recv(2).unpack("C*")
98
+ socket.recv(1)
99
+ address_type = socket.recv(1).getbyte(0)
100
+ case address_type
101
+ when 1
102
+ socket.recv(4) # get four bytes for IPv4 address
103
+ when 3
104
+ len = socket.recv(1).getbyte(0)
105
+ hostname = socket.recv(len)
106
+ when 4
107
+ ipv6addr hostname = socket.recv(16)
108
+ else
109
+ socket.close
110
+ raise ConnectError, "Illegal response type"
111
+ end
112
+ portnum = socket.recv(2)
113
+
114
+ unless reply == SUCCESS
115
+ socket.close
116
+ raise ConnectError, "#{reply}"
117
+ end
118
+
119
+ return socket
120
+ end
121
+
122
+ private
123
+
124
+ # Simple username/password negotiation with the SOCKS5 server.
125
+ def negotiate_password(socket)
126
+ packet = [0x01, options[:user].length, options[:user],
127
+ options[:password].length, options[:password]].pack("CCA*CA*")
128
+ socket.send packet, 0
129
+
130
+ version, status = socket.recv(2).unpack("CC")
131
+
132
+ if status != SUCCESS
133
+ socket.close
134
+ raise UnauthorizedError, "could not authorize user"
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end