net-ssh 2.2.0 → 2.2.1
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.
- 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
|
|