net-ssh 2.9.1 → 2.9.2.beta

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e4b83671580de0bd2d63e112c605feaefe41a5b0
4
+ data.tar.gz: f29b3c1c885a9868b09f18d03d988683e1c351d5
5
+ SHA512:
6
+ metadata.gz: 97088cea6fb2f3b47c09e6911c2b52d0e79b97a1b1dc7e6033b44f44531dbef8751677acb7f03b63a0c4ff9978422c20beeafdb61ab8e36435e20e1aafbf0d5b
7
+ data.tar.gz: 228621bcee2649a0e05d38483ce4f74376116544e6db2a0edc326c63d9be9ea85e4a5e828f0a171382792cdda4763b9a5f3771ab480b8b5ae4b67a7572a7ced5
checksums.yaml.gz.sig ADDED
@@ -0,0 +1 @@
1
+ ) �������{&���k��rÐ���E���z�{o�4���y5�a|�M��糮Q�[K~��h�y�! �x���0g��T0 ѭsM��u�kq����^�ύS��=�������-Z�; h���f= �_<z=|���:k�2*��R��†lj}G�E�����T�fWn�n`�FL���o����~��c��q�����В�
data.tar.gz.sig CHANGED
Binary file
data/CHANGES.txt CHANGED
@@ -1,4 +1,9 @@
1
+ === 2.9.2-beta
1
2
 
3
+ * Remove advertised algorithms that were not working (ssh-rsa-cert-* *ed25519 acm*-gcm@openssh.com) [mfazekas]
4
+ * Unkown algorithms now ignored instead of failed [mfazekas]
5
+ * Asks for password with password auth (up to number_of_password_prompts) [mfazekas]
6
+ * Removed warnings [amatsuda]
2
7
 
3
8
  === 2.9.1 / 13 May 2014
4
9
 
data/README.rdoc CHANGED
@@ -171,6 +171,11 @@ Run a single test file like this:
171
171
 
172
172
  * JRuby 1.5: 99% tests pass (448 tests, 1846 assertions, 1 failures)
173
173
 
174
+ === BUILDING GEM
175
+
176
+ Since building the gem requires the private key if you want to build a .gem locally please use the NET_SSH_NOKEY=1 envirnoment variable:
177
+
178
+ rake build NET_SSH_NOKEY=1
174
179
 
175
180
  === PORT FORWARDING TESTS
176
181
 
data/Rakefile CHANGED
@@ -34,10 +34,16 @@ begin
34
34
 
35
35
  s.license = "MIT"
36
36
 
37
- s.signing_key = File.join('/mnt/gem/', 'gem-private_key.pem')
38
- s.cert_chain = ['gem-public_cert.pem']
37
+ unless ENV['NET_SSH_NOKEY']
38
+ signing_key = File.join('/mnt/gem/', 'net-ssh-private_key.pem')
39
+ s.signing_key = File.join('/mnt/gem/', 'net-ssh-private_key.pem')
40
+ s.cert_chain = ['net-ssh-public_cert.pem']
41
+ unless (Rake.application.top_level_tasks & ['build','install']).empty?
42
+ raise "No key found at #{signing_key} for signing, use rake <taskname> NET_SSH_NOKEY=1 to build without key" unless File.exist?(signing_key)
43
+ end
44
+ end
39
45
  end
40
- Jeweler::GemcutterTasks.new
46
+ Jeweler::RubygemsDotOrgTasks.new
41
47
  rescue LoadError
42
48
  puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
43
49
  end
data/lib/net/ssh.rb CHANGED
@@ -63,7 +63,7 @@ module Net
63
63
  VALID_OPTIONS = [
64
64
  :auth_methods, :bind_address, :compression, :compression_level, :config,
65
65
  :encryption, :forward_agent, :hmac, :host_key,
66
- :keepalive, :keepalive_interval, :kex, :keys, :key_data,
66
+ :keepalive, :keepalive_interval, :keepalive_maxcount, :kex, :keys, :key_data,
67
67
  :languages, :logger, :paranoid, :password, :port, :proxy,
68
68
  :rekey_blocks_limit,:rekey_limit, :rekey_packet_limit, :timeout, :verbose,
69
69
  :global_known_hosts_file, :user_known_hosts_file, :host_key_alias,
@@ -130,6 +130,7 @@ module Net
130
130
  # the keepalive_interval seconds. Defaults to +false+.
131
131
  # :keepalive_interval => the interval seconds for keepalive.
132
132
  # Defaults to +300+ seconds.
133
+ # :keepalive_countmax => the maximun number of keepalive packet miss allowed.
133
134
  # * :kex => the key exchange algorithm (or algorithms) to use
134
135
  # * :keys => an array of file names of private keys to use for publickey
135
136
  # and hostbased authentication
@@ -94,10 +94,16 @@ module Net; module SSH; module Authentication
94
94
 
95
95
  identities = []
96
96
  body.read_long.times do
97
- key = Buffer.new(body.read_string).read_key
98
- key.extend(Comment)
99
- key.comment = body.read_string
100
- identities.push key
97
+ key_str = body.read_string
98
+ comment_str = body.read_string
99
+ begin
100
+ key = Buffer.new(key_str).read_key
101
+ key.extend(Comment)
102
+ key.comment = comment_str
103
+ identities.push key
104
+ rescue NotImplementedError => e
105
+ error { "ignoring unimplemented key:#{e.message} #{comment_str}" }
106
+ end
101
107
  end
102
108
 
103
109
  return identities
@@ -1,4 +1,5 @@
1
1
  require 'net/ssh/errors'
2
+ require 'net/ssh/prompt'
2
3
  require 'net/ssh/authentication/methods/abstract'
3
4
 
4
5
  module Net
@@ -8,25 +9,36 @@ module Net
8
9
 
9
10
  # Implements the "password" SSH authentication method.
10
11
  class Password < Abstract
12
+ include Prompt
13
+
11
14
  # Attempt to authenticate the given user for the given service. If
12
- # the password parameter is nil, this will never do anything except
13
- # return false.
15
+ # the password parameter is nil, this will ask for password
14
16
  def authenticate(next_service, username, password=nil)
15
- return false unless password
17
+ retries = 0
18
+ max_retries = get_max_retries
19
+ return false if !password && max_retries == 0
16
20
 
17
- send_message(userauth_request(username, next_service, "password", false, password))
18
- message = session.next_message
21
+ begin
22
+ password_to_send = password || ask_password(username)
19
23
 
20
- case message.type
21
- when USERAUTH_SUCCESS
22
- debug { "password succeeded" }
23
- return true
24
- when USERAUTH_FAILURE
24
+ send_message(userauth_request(username, next_service, "password", false, password_to_send))
25
+ message = session.next_message
26
+ retries += 1
27
+
28
+ if message.type == USERAUTH_FAILURE
25
29
  debug { "password failed" }
26
30
 
27
31
  raise Net::SSH::Authentication::DisallowedMethod unless
28
32
  message[:authentications].split(/,/).include? 'password'
33
+ password = nil
34
+ end
35
+ end until (message.type != USERAUTH_FAILURE || retries >= max_retries)
29
36
 
37
+ case message.type
38
+ when USERAUTH_SUCCESS
39
+ debug { "password succeeded" }
40
+ return true
41
+ when USERAUTH_FAILURE
30
42
  return false
31
43
  when USERAUTH_PASSWD_CHANGEREQ
32
44
  debug { "password change request received, failing" }
@@ -35,6 +47,19 @@ module Net
35
47
  raise Net::SSH::Exception, "unexpected reply to USERAUTH_REQUEST: #{message.type} (#{message.inspect})"
36
48
  end
37
49
  end
50
+
51
+ private
52
+
53
+ NUMBER_OF_PASSWORD_PROMPTS = 3
54
+
55
+ def ask_password(username)
56
+ echo = false
57
+ prompt("#{username}@#{session.transport.host}'s password:", echo)
58
+ end
59
+
60
+ def get_max_retries
61
+ (session.transport.options||{})[:number_of_password_prompts] || NUMBER_OF_PASSWORD_PROMPTS
62
+ end
38
63
  end
39
64
 
40
65
  end
@@ -255,7 +255,7 @@ module Net; module SSH
255
255
  key.e = read_bignum
256
256
  key.n = read_bignum
257
257
 
258
- when /^ecdsa\-sha2\-(\w*)$/, /^ssh-ed25519(-cert-v01@openssh.com)?$/
258
+ when /^ecdsa\-sha2\-(\w*)$/
259
259
  unless defined?(OpenSSL::PKey::EC)
260
260
  raise NotImplementedError, "unsupported key type `#{type}'"
261
261
  else
@@ -31,6 +31,7 @@ module Net; module SSH
31
31
  # * RekeyLimit => :rekey_limit
32
32
  # * User => :user
33
33
  # * UserKnownHostsFile => :user_known_hosts_file
34
+ # * NumberOfPasswordPrompts => :number_of_password_prompts
34
35
  #
35
36
  # Note that you will never need to use this class directly--you can control
36
37
  # whether the OpenSSH configuration files are read by passing the :config
@@ -59,7 +60,7 @@ module Net; module SSH
59
60
  # #default_files), translates the resulting hash into the options
60
61
  # recognized by Net::SSH, and returns them.
61
62
  def for(host, files=default_files)
62
- hash = translate(files.inject({}) { |settings, file|
63
+ translate(files.inject({}) { |settings, file|
63
64
  load(file, host, settings)
64
65
  })
65
66
  end
@@ -76,7 +77,6 @@ module Net; module SSH
76
77
 
77
78
  globals = {}
78
79
  matched_host = nil
79
- multi_host = []
80
80
  seen_host = false
81
81
  IO.foreach(file) do |line|
82
82
  next if line =~ /^\s*(?:#.*)?$/
@@ -220,6 +220,8 @@ module Net; module SSH
220
220
  when 'sendenv'
221
221
  multi_send_env = value.to_s.split(/\s+/)
222
222
  hash[:send_env] = multi_send_env.map { |e| Regexp.new pattern2regex(e).source, false }
223
+ when 'numberofpasswordprompts'
224
+ hash[:number_of_password_prompts] = value.to_i
223
225
  end
224
226
  hash
225
227
  end
@@ -0,0 +1,48 @@
1
+ module Net; module SSH; module Connection
2
+
3
+ module Keepalive
4
+ # Default IO.select timeout threshold
5
+ DEFAULT_IO_SELECT_TIMEOUT = 300
6
+
7
+ def initialize_keepalive
8
+ @last_keepalive_sent_at = nil
9
+ @unresponded_keepalive_count = 0
10
+ end
11
+
12
+ def keepalive_enabled?
13
+ options[:keepalive]
14
+ end
15
+
16
+ def keepalive_interval
17
+ options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT
18
+ end
19
+
20
+ def should_send_keepalive?
21
+ return false unless keepalive_enabled?
22
+ return true unless @last_keepalive_sent_at
23
+ Time.now - @last_keepalive_sent_at >= keepalive_interval
24
+ end
25
+
26
+ def keepalive_maxcount
27
+ (options[:keepalive_maxcount] || 3).to_i
28
+ end
29
+
30
+ def send_keepalive_as_needed(readers, writers)
31
+ return unless readers.nil? && writers.nil?
32
+ return unless should_send_keepalive?
33
+ info { "sending keepalive #{@unresponded_keepalive_count}" }
34
+
35
+ @unresponded_keepalive_count += 1
36
+ send_global_request("keepalive@openssh.com") { |success, response|
37
+ puts "before zero => #{@unresponded_keepalive_count}"
38
+ @unresponded_keepalive_count = 0
39
+ }
40
+ @last_keepalive_sent_at = Time.now
41
+ if keepalive_maxcount > 0 && @unresponded_keepalive_count > keepalive_maxcount
42
+ error { "Timeout, server #{host} not responding. Missed #{@unresponded_keepalive_count-1} timeouts." }
43
+ raise Net::SSH::Timeout, "Timeout, server #{host} not responding."
44
+ end
45
+ end
46
+ end
47
+
48
+ end; end; end
@@ -3,6 +3,7 @@ require 'net/ssh/ruby_compat'
3
3
  require 'net/ssh/connection/channel'
4
4
  require 'net/ssh/connection/constants'
5
5
  require 'net/ssh/service/forward'
6
+ require 'net/ssh/connection/keepalive'
6
7
 
7
8
  module Net; module SSH; module Connection
8
9
 
@@ -23,10 +24,7 @@ module Net; module SSH; module Connection
23
24
  # ssh.exec! "/etc/init.d/some_process start"
24
25
  # end
25
26
  class Session
26
- include Constants, Loggable
27
-
28
- # Default IO.select timeout threshold
29
- DEFAULT_IO_SELECT_TIMEOUT = 300
27
+ include Constants, Loggable, Keepalive
30
28
 
31
29
  # The underlying transport layer abstraction (see Net::SSH::Transport::Session).
32
30
  attr_reader :transport
@@ -79,7 +77,7 @@ module Net; module SSH; module Connection
79
77
  @max_pkt_size = (options.has_key?(:max_pkt_size) ? options[:max_pkt_size] : 0x8000)
80
78
  @max_win_size = (options.has_key?(:max_win_size) ? options[:max_win_size] : 0x20000)
81
79
 
82
- @last_keepalive_sent_at = nil
80
+ initialize_keepalive
83
81
  end
84
82
 
85
83
  # Retrieves a custom property from this instance. This can be used to
@@ -598,29 +596,10 @@ module Net; module SSH; module Connection
598
596
 
599
597
  def io_select_wait(wait)
600
598
  return wait if wait
601
- return wait unless options[:keepalive]
599
+ return wait unless keepalive_enabled?
602
600
  keepalive_interval
603
601
  end
604
602
 
605
- def keepalive_interval
606
- options[:keepalive_interval] || DEFAULT_IO_SELECT_TIMEOUT
607
- end
608
-
609
- def should_send_keepalive?
610
- return false unless options[:keepalive]
611
- return true unless @last_keepalive_sent_at
612
- Time.now - @last_keepalive_sent_at >= keepalive_interval
613
- end
614
-
615
- def send_keepalive_as_needed(readers, writers)
616
- return unless readers.nil? && writers.nil?
617
- return unless should_send_keepalive?
618
- info { "sending keepalive" }
619
- msg = Net::SSH::Buffer.from(:byte, Packet::IGNORE, :string, "keepalive")
620
- send_message(msg)
621
- @last_keepalive_sent_at = Time.now
622
- end
623
-
624
603
  MAP = Constants.constants.inject({}) do |memo, name|
625
604
  value = const_get(name)
626
605
  next unless Integer === value
@@ -14,6 +14,10 @@ module Net; module SSH
14
14
  # unexpectedly.
15
15
  class Disconnect < Exception; end
16
16
 
17
+ # This exception is raised when the remote host has disconnected/
18
+ # timeouted unexpectedly.
19
+ class Timeout < Disconnect; end
20
+
17
21
  # This exception is primarily used internally, but if you have a channel
18
22
  # request handler (see Net::SSH::Connection::Channel#on_request) that you
19
23
  # want to fail in such a way that the server knows it failed, you can
@@ -15,17 +15,9 @@ module Net; module SSH
15
15
  SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
16
16
  ecdsa-sha2-nistp256
17
17
  ecdsa-sha2-nistp384
18
- ecdsa-sha2-nistp521
19
- ssh-ed25519-cert-v01@openssh.com
20
- ssh-rsa-cert-v01@openssh.com
21
- ssh-rsa-cert-v00@openssh.com
22
- ssh-ed25519
23
- )
18
+ ecdsa-sha2-nistp521)
24
19
  else
25
- SUPPORTED_TYPE = %w(ssh-rsa ssh-dss
26
- ssh-rsa-cert-v01@openssh.com
27
- ssh-rsa-cert-v00@openssh.com
28
- )
20
+ SUPPORTED_TYPE = %w(ssh-rsa ssh-dss)
29
21
  end
30
22
 
31
23
 
@@ -88,13 +88,13 @@ module Net; module SSH; module Service
88
88
  end
89
89
 
90
90
  prepare_client(client, channel, :local)
91
-
91
+
92
92
  channel.on_open_failed do |ch, code, description|
93
93
  channel.error { "could not establish direct channel: #{description} (#{code})" }
94
94
  channel[:socket].close
95
95
  end
96
96
  end
97
-
97
+
98
98
  local_port
99
99
  end
100
100
 
@@ -131,22 +131,52 @@ module Net; module SSH; module Service
131
131
  # the port number. The assigned port will show up in the # #active_remotes
132
132
  # list.
133
133
  #
134
+ # remote_host is interpreted by the server per RFC 4254, which has these
135
+ # special values:
136
+ #
137
+ # - "" means that connections are to be accepted on all protocol
138
+ # families supported by the SSH implementation.
139
+ # - "0.0.0.0" means to listen on all IPv4 addresses.
140
+ # - "::" means to listen on all IPv6 addresses.
141
+ # - "localhost" means to listen on all protocol families supported by
142
+ # the SSH implementation on loopback addresses only ([RFC3330] and
143
+ # [RFC3513]).
144
+ # - "127.0.0.1" and "::1" indicate listening on the loopback
145
+ # interfaces for IPv4 and IPv6, respectively.
146
+ #
147
+ # You may pass a block that will be called when the the port forward
148
+ # request receives a response. This block will be passed the remote_port
149
+ # that was actually bound to, or nil if the binding failed. If the block
150
+ # returns :no_exception, the "failed binding" exception will not be thrown.
151
+ #
134
152
  # If you want to block until the port is active, you could do something
135
153
  # like this:
136
154
  #
137
- # old_active_remotes = ssh.forward.active_remotes
138
- # ssh.forward.remote(80, "www.google.com", 0, "0.0.0.0")
139
- # ssh.loop { !(ssh.forward.active_remotes.length > old_active_remotes.length) }
140
- # assigned_port = (ssh.forward.active_remotes - old_active_remotes).first[0]
155
+ # got_remote_port = nil
156
+ # remote(port, host, remote_port, remote_host) do |actual_remote_port|
157
+ # got_remote_port = actual_remote_port || :error
158
+ # :no_exception # will yield the exception on my own thread
159
+ # end
160
+ # session.loop { !got_remote_port }
161
+ # if got_remote_port == :error
162
+ # raise Net::SSH::Exception, "remote forwarding request failed"
163
+ # end
164
+ #
141
165
  def remote(port, host, remote_port, remote_host="127.0.0.1")
142
166
  session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response|
143
167
  if success
144
168
  remote_port = response.read_long if remote_port == 0
145
169
  debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" }
146
170
  @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port)
171
+ yield remote_port, remote_host if block_given?
147
172
  else
148
- error { "remote forwarding request failed" }
149
- raise Net::SSH::Exception, "remote forwarding request failed"
173
+ instruction = if block_given?
174
+ yield :error
175
+ end
176
+ unless instruction == :no_exception
177
+ error { "remote forwarding request failed" }
178
+ raise Net::SSH::Exception, "remote forwarding request failed"
179
+ end
150
180
  end
151
181
  end
152
182
  end
@@ -183,6 +213,16 @@ module Net; module SSH; module Service
183
213
  @remote_forwarded_ports.keys
184
214
  end
185
215
 
216
+ # Returns all active remote forwarded ports and where they forward to. The
217
+ # returned value is a hash from [<forwarding port on the local host>, <local forwarding address>]
218
+ # to [<port on the remote host>, <remote bind address>].
219
+ def active_remote_destinations
220
+ @remote_forwarded_ports.inject({}) do |result, (remote, local)|
221
+ result[[local.port, local.host]] = remote
222
+ result
223
+ end
224
+ end
225
+
186
226
  # Enables SSH agent forwarding on the given channel. The forwarded agent
187
227
  # will remain active even after the channel closes--the channel is only
188
228
  # used as the transport for enabling the forwarded connection. You should
@@ -216,7 +256,7 @@ module Net; module SSH; module Service
216
256
  end
217
257
 
218
258
  private
219
-
259
+
220
260
  # Perform setup operations that are common to all forwarded channels.
221
261
  # +client+ is a socket, +channel+ is the channel that was just created,
222
262
  # and +type+ is an arbitrary string describing the type of the channel.
@@ -228,11 +268,11 @@ module Net; module SSH; module Service
228
268
  session.listen_to(client)
229
269
  channel[:socket] = client
230
270
 
231
- channel.on_data do |ch, data|
271
+ channel.on_data do |ch, data|
232
272
  debug { "data:#{data.length} on #{type} forwarded channel" }
233
273
  ch[:socket].enqueue(data)
234
274
  end
235
-
275
+
236
276
  # Handles server close on the sending side by Miklós Fazekas
237
277
  channel.on_eof do |ch|
238
278
  debug { "eof #{type} on #{type} forwarded channel" }
@@ -251,7 +291,7 @@ module Net; module SSH; module Service
251
291
  debug { "enotconn in on_eof => shallowing exception:#{e}" }
252
292
  end
253
293
  end
254
-
294
+
255
295
  channel.on_close do |ch|
256
296
  debug { "closing #{type} forwarded channel" }
257
297
  ch[:socket].close if !client.closed?