net-ssh 2.2.0 → 2.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,9 @@
1
1
 
2
+ === 2.2.1 / 24 Aug 2011
3
+
4
+ * Do not prompt any passphrases before trying all identities from agent. [musybite]
5
+ (see: http://net-ssh.lighthouseapp.com/projects/36253-net-ssh/tickets/30)
6
+
2
7
  === 2.2.0 / 16 Aug 2011
3
8
 
4
9
  * Add support for forward a local UNIX domain socket to a remote TCP socket. [Mark Imbriaco]
@@ -95,12 +95,14 @@ module Net
95
95
  # from ssh-agent will be ignored unless it present in key_files or
96
96
  # key_data.
97
97
  def each_identity
98
- user_identities = load_identities_from_files + load_identities_from_data
98
+ prepared_identities = prepare_identities_from_files + prepare_identities_from_data
99
+
100
+ user_identities = load_identities(prepared_identities, false)
99
101
 
100
102
  if agent
101
103
  agent.identities.each do |key|
102
104
  corresponding_user_identity = user_identities.detect { |identity|
103
- identity[:public_key].to_pem == key.to_pem
105
+ identity[:public_key] && identity[:public_key].to_pem == key.to_pem
104
106
  }
105
107
  user_identities.delete(corresponding_user_identity) if corresponding_user_identity
106
108
 
@@ -111,6 +113,8 @@ module Net
111
113
  end
112
114
  end
113
115
 
116
+ user_identities = load_identities(user_identities, true)
117
+
114
118
  user_identities.each do |identity|
115
119
  key = identity.delete(:public_key)
116
120
  known_identities[key] = identity
@@ -134,7 +138,7 @@ module Net
134
138
 
135
139
  if info[:key].nil? && info[:from] == :file
136
140
  begin
137
- info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase])
141
+ info[:key] = KeyFactory.load_private_key(info[:file], options[:passphrase], true)
138
142
  rescue Exception, OpenSSL::OpenSSLError => e
139
143
  raise KeyManagerError, "the given identity is known, but the private key could not be loaded: #{e.class} (#{e.message})"
140
144
  end
@@ -179,37 +183,67 @@ module Net
179
183
 
180
184
  private
181
185
 
182
- # Extracts identities from user key_files, preserving their order and sources.
183
- def load_identities_from_files
186
+ # Prepares identities from user key_files for loading, preserving their order and sources.
187
+ def prepare_identities_from_files
184
188
  key_files.map do |file|
185
189
  public_key_file = file + ".pub"
186
190
  if File.readable?(public_key_file)
187
- begin
188
- key = KeyFactory.load_public_key(public_key_file)
189
- { :public_key => key, :from => :file, :file => file }
190
- rescue Exception => e
191
- error { "could not load public key file `#{public_key_file}': #{e.class} (#{e.message})" }
192
- nil
193
- end
191
+ { :load_from => :pubkey_file, :file => file }
194
192
  elsif File.readable?(file)
195
- begin
196
- private_key = KeyFactory.load_private_key(file, options[:passphrase])
193
+ { :load_from => :privkey_file, :file => file }
194
+ end
195
+ end.compact
196
+ end
197
+
198
+ # Prepared identities from user key_data, preserving their order and sources.
199
+ def prepare_identities_from_data
200
+ key_data.map do |data|
201
+ { :load_from => :data, :data => data }
202
+ end
203
+ end
204
+
205
+ # Load prepared identities. Private key decryption errors ignored if passphrase was not prompted.
206
+ def load_identities(identities, ask_passphrase)
207
+ identities.map do |identity|
208
+ begin
209
+ case identity[:load_from]
210
+ when :pubkey_file
211
+ key = KeyFactory.load_public_key(identity[:file] + ".pub")
212
+ { :public_key => key, :from => :file, :file => identity[:file] }
213
+ when :privkey_file
214
+ private_key = KeyFactory.load_private_key(identity[:file], options[:passphrase], ask_passphrase)
197
215
  key = private_key.send(:public_key)
198
- { :public_key => key, :from => :file, :file => file, :key => private_key }
199
- rescue Exception => e
200
- error { "could not load private key file `#{file}': #{e.class} (#{e.message})" }
216
+ { :public_key => key, :from => :file, :file => identity[:file], :key => private_key }
217
+ when :data
218
+ private_key = KeyFactory.load_data_private_key(identity[:data], options[:passphrase], ask_passphrase)
219
+ key = private_key.send(:public_key)
220
+ { :public_key => key, :from => :key_data, :data => identity[:data], :key => private_key }
221
+ else
222
+ identity
223
+ end
224
+
225
+ rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError => e
226
+ if ask_passphrase
227
+ process_identity_loading_error(identity, e)
201
228
  nil
229
+ else
230
+ identity
202
231
  end
232
+ rescue Exception => e
233
+ process_identity_loading_error(identity, e)
234
+ nil
203
235
  end
204
236
  end.compact
205
237
  end
206
238
 
207
- # Extraccts identities from user key_data, preserving their order and sources.
208
- def load_identities_from_data
209
- key_data.map do |data|
210
- private_key = KeyFactory.load_data_private_key(data)
211
- key = private_key.send(:public_key)
212
- { :public_key => key, :from => :key_data, :data => data, :key => private_key }
239
+ def process_identity_loading_error(identity, e)
240
+ case identity[:load_from]
241
+ when :pubkey_file
242
+ error { "could not load public key file `#{identity[:file]}': #{e.class} (#{e.message})" }
243
+ when :privkey_file
244
+ error { "could not load private key file `#{identity[:file]}': #{e.class} (#{e.message})" }
245
+ else
246
+ raise e
213
247
  end
214
248
  end
215
249
 
@@ -34,9 +34,9 @@ module Net; module SSH
34
34
  # appropriately. The new key is returned. If the key itself is
35
35
  # encrypted (requiring a passphrase to use), the user will be
36
36
  # prompted to enter their password unless passphrase works.
37
- def load_private_key(filename, passphrase=nil)
37
+ def load_private_key(filename, passphrase=nil, ask_passphrase=true)
38
38
  data = File.read(File.expand_path(filename))
39
- load_data_private_key(data, passphrase, filename)
39
+ load_data_private_key(data, passphrase, ask_passphrase, filename)
40
40
  end
41
41
 
42
42
  # Loads a private key. It will correctly determine
@@ -44,7 +44,7 @@ module Net; module SSH
44
44
  # appropriately. The new key is returned. If the key itself is
45
45
  # encrypted (requiring a passphrase to use), the user will be
46
46
  # prompted to enter their password unless passphrase works.
47
- def load_data_private_key(data, passphrase=nil, filename="")
47
+ def load_data_private_key(data, passphrase=nil, ask_passphrase=true, filename="")
48
48
  if data.match(/-----BEGIN DSA PRIVATE KEY-----/)
49
49
  key_type = OpenSSL::PKey::DSA
50
50
  elsif data.match(/-----BEGIN RSA PRIVATE KEY-----/)
@@ -61,7 +61,7 @@ module Net; module SSH
61
61
  begin
62
62
  return key_type.new(data, passphrase || 'invalid')
63
63
  rescue OpenSSL::PKey::RSAError, OpenSSL::PKey::DSAError => e
64
- if encrypted_key
64
+ if encrypted_key && ask_passphrase
65
65
  tries += 1
66
66
  if tries <= 3
67
67
  passphrase = prompt("Enter passphrase for #{filename}:", false)
@@ -51,7 +51,7 @@ module Net; module SSH
51
51
  MINOR = 2
52
52
 
53
53
  # The tiny component of this version of the Net::SSH library
54
- TINY = 0
54
+ TINY = 1
55
55
 
56
56
  # The current version of the Net::SSH library as a Version instance
57
57
  CURRENT = new(MAJOR, MINOR, TINY)
@@ -1,7 +1,7 @@
1
1
  @spec = Gem::Specification.new do |s|
2
2
  s.name = "net-ssh"
3
3
  s.rubyforge_project = 'net-ssh'
4
- s.version = "2.2.0"
4
+ s.version = "2.2.1"
5
5
  s.summary = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol."
6
6
  s.description = s.summary
7
7
  s.authors = ["Jamis Buck", "Delano Mandelbaum"]
@@ -31,8 +31,8 @@ module Authentication
31
31
  def test_each_identity_should_load_from_key_files
32
32
  manager.stubs(:agent).returns(nil)
33
33
 
34
- stub_file_key "/first", rsa
35
- stub_file_key "/second", dsa
34
+ stub_file_private_key "/first", rsa
35
+ stub_file_private_key "/second", dsa
36
36
 
37
37
  identities = []
38
38
  manager.each_identity { |identity| identities << identity }
@@ -62,7 +62,7 @@ module Authentication
62
62
  def test_only_identities_with_key_files_should_load_from_agent_of_keys_only_set
63
63
  manager(:keys_only => true).stubs(:agent).returns(agent)
64
64
 
65
- stub_file_key "/first", rsa
65
+ stub_file_private_key "/first", rsa
66
66
 
67
67
  identities = []
68
68
  manager.each_identity { |identity| identities << identity }
@@ -73,6 +73,22 @@ module Authentication
73
73
  assert_equal({:from => :agent}, manager.known_identities[rsa])
74
74
  end
75
75
 
76
+ def test_identities_without_public_key_files_should_not_be_touched_if_identity_loaded_from_agent
77
+ manager.stubs(:agent).returns(agent)
78
+
79
+ stub_file_public_key "/first", rsa
80
+ stub_file_private_key "/second", dsa, :passphrase => :should_not_be_asked
81
+
82
+ identities = []
83
+ manager.each_identity do |identity|
84
+ identities << identity
85
+ break if manager.known_identities[identity][:from] == :agent
86
+ end
87
+
88
+ assert_equal 1, identities.length
89
+ assert_equal rsa.to_blob, identities.first.to_blob
90
+ end
91
+
76
92
  def test_sign_with_agent_originated_key_should_request_signature_from_agent
77
93
  manager.stubs(:agent).returns(agent)
78
94
  manager.each_identity { |identity| } # preload the known_identities
@@ -82,7 +98,7 @@ module Authentication
82
98
 
83
99
  def test_sign_with_file_originated_key_should_load_private_key_and_sign_with_it
84
100
  manager.stubs(:agent).returns(nil)
85
- stub_file_key "/first", rsa(512)
101
+ stub_file_private_key "/first", rsa(512)
86
102
  rsa.expects(:ssh_do_sign).with("hello, world").returns("abcxyz123")
87
103
  manager.each_identity { |identity| } # preload the known_identities
88
104
  assert_equal "\0\0\0\assh-rsa\0\0\0\011abcxyz123", manager.sign(rsa, "hello, world")
@@ -100,12 +116,31 @@ module Authentication
100
116
 
101
117
  private
102
118
 
103
- def stub_file_key(name, key)
119
+ def stub_file_private_key(name, key, options = {})
104
120
  manager.add(name)
105
- File.expects(:readable?).with(name).returns(true)
106
- File.expects(:readable?).with(name + ".pub").returns(false)
107
- Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil).returns(key).at_least_once
108
- key.expects(:public_key).returns(key)
121
+ File.stubs(:readable?).with(name).returns(true)
122
+ File.stubs(:readable?).with(name + ".pub").returns(false)
123
+
124
+ case options.fetch(:passphrase, :indifferently)
125
+ when :should_be_asked
126
+ Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, false).raises(OpenSSL::PKey::RSAError).at_least_once
127
+ Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, true).returns(key).at_least_once
128
+ when :should_not_be_asked
129
+ Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, false).raises(OpenSSL::PKey::RSAError).at_least_once
130
+ Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, true).never
131
+ else # :indifferently
132
+ Net::SSH::KeyFactory.expects(:load_private_key).with(name, nil, any_of(true, false)).returns(key).at_least_once
133
+ end
134
+
135
+ key.stubs(:public_key).returns(key)
136
+ end
137
+
138
+ def stub_file_public_key(name, key)
139
+ manager.add(name)
140
+ File.stubs(:readable?).with(name).returns(false)
141
+ File.stubs(:readable?).with(name + ".pub").returns(true)
142
+
143
+ Net::SSH::KeyFactory.expects(:load_public_key).with(name + ".pub").returns(key).at_least_once
109
144
  end
110
145
 
111
146
  def rsa(size=512)
@@ -40,6 +40,12 @@ class TestKeyFactory < Test::Unit::TestCase
40
40
  assert_raises(OpenSSL::PKey::RSAError) { Net::SSH::KeyFactory.load_private_key("/key-file") }
41
41
  end
42
42
 
43
+ def test_load_encrypted_private_key_should_raise_exception_without_asking_passphrase
44
+ File.expects(:read).with("/key-file").returns(encrypted(rsa_key, "password"))
45
+ Net::SSH::KeyFactory.expects(:prompt).never
46
+ assert_raises(OpenSSL::PKey::RSAError) { Net::SSH::KeyFactory.load_private_key("/key-file", nil, false) }
47
+ end
48
+
43
49
  def test_load_public_rsa_key_should_return_key
44
50
  File.expects(:read).with("/key-file").returns(public(rsa_key))
45
51
  assert_equal rsa_key.to_blob, Net::SSH::KeyFactory.load_public_key("/key-file").to_blob
@@ -66,4 +72,4 @@ class TestKeyFactory < Test::Unit::TestCase
66
72
  result << [Net::SSH::Buffer.from(:key, key).to_s].pack("m*").strip.tr("\n\r\t ", "")
67
73
  result << " joe@host.test"
68
74
  end
69
- end
75
+ end
@@ -146,7 +146,7 @@ module Transport
146
146
  def test_allow_when_not_pending_should_be_true_for_all_packets
147
147
  (0..255).each do |type|
148
148
  packet = stub("packet", :type => type)
149
- assert algorithms.allow?(packet), type
149
+ assert algorithms.allow?(packet), type.to_s
150
150
  end
151
151
  end
152
152
 
@@ -299,4 +299,4 @@ module Transport
299
299
  end
300
300
  end
301
301
 
302
- end
302
+ end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: net-ssh
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 2.2.0
5
+ version: 2.2.1
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jamis Buck
@@ -11,7 +11,7 @@ autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
13
 
14
- date: 2011-08-17 00:00:00 -04:00
14
+ date: 2011-08-24 00:00:00 -04:00
15
15
  default_executable:
16
16
  dependencies: []
17
17