net-ssh 2.9.1 → 2.9.2.beta

Sign up to get free protection for your applications and to get access to all the features.
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?