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.
- data/CHANGELOG.rdoc +5 -0
- data/lib/net/ssh/authentication/key_manager.rb +57 -23
- data/lib/net/ssh/key_factory.rb +4 -4
- data/lib/net/ssh/version.rb +1 -1
- data/net-ssh.gemspec +1 -1
- data/test/authentication/test_key_manager.rb +44 -9
- data/test/test_key_factory.rb +7 -1
- data/test/transport/test_algorithms.rb +2 -2
- metadata +2 -2
data/CHANGELOG.rdoc
CHANGED
@@ -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
|
-
|
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
|
-
#
|
183
|
-
def
|
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
|
-
|
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
|
-
|
196
|
-
|
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
|
-
|
200
|
-
|
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
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
{
|
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
|
|
data/lib/net/ssh/key_factory.rb
CHANGED
@@ -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)
|
data/lib/net/ssh/version.rb
CHANGED
data/net-ssh.gemspec
CHANGED
@@ -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.
|
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
|
-
|
35
|
-
|
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
|
-
|
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
|
-
|
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
|
119
|
+
def stub_file_private_key(name, key, options = {})
|
104
120
|
manager.add(name)
|
105
|
-
File.
|
106
|
-
File.
|
107
|
-
|
108
|
-
|
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)
|
data/test/test_key_factory.rb
CHANGED
@@ -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.
|
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-
|
14
|
+
date: 2011-08-24 00:00:00 -04:00
|
15
15
|
default_executable:
|
16
16
|
dependencies: []
|
17
17
|
|