net-ssh 4.0.0.beta3 → 4.0.0.beta4
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 +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.travis.yml +2 -1
- data/CHANGES.txt +9 -0
- data/Gemfile +9 -1
- data/Gemfile.norbnacl +6 -1
- data/Gemfile.norbnacl.lock +4 -3
- data/ISSUE_TEMPLATE.md +30 -0
- data/README.rdoc +5 -1
- data/Rakefile +4 -0
- data/appveyor.yml +36 -5
- data/lib/net/ssh.rb +5 -5
- data/lib/net/ssh/authentication/agent.rb +174 -14
- data/lib/net/ssh/authentication/key_manager.rb +1 -1
- data/lib/net/ssh/authentication/pageant.rb +116 -19
- data/lib/net/ssh/buffered_io.rb +17 -12
- data/lib/net/ssh/connection/session.rb +29 -7
- data/lib/net/ssh/key_factory.rb +7 -7
- data/lib/net/ssh/proxy/http.rb +7 -4
- data/lib/net/ssh/proxy/https.rb +49 -0
- data/lib/net/ssh/test.rb +2 -2
- data/lib/net/ssh/test/extensions.rb +17 -0
- data/lib/net/ssh/transport/ctr.rb +7 -9
- data/lib/net/ssh/version.rb +2 -2
- data/net-ssh.gemspec +3 -4
- metadata +8 -21
- metadata.gz.sig +0 -0
- data/lib/net/ssh/authentication/agent/java_pageant.rb +0 -85
- data/lib/net/ssh/authentication/agent/socket.rb +0 -178
data/lib/net/ssh/proxy/http.rb
CHANGED
@@ -49,8 +49,7 @@ module Net; module SSH; module Proxy
|
|
49
49
|
# Return a new socket connected to the given host and port via the
|
50
50
|
# proxy that was requested when the socket factory was instantiated.
|
51
51
|
def open(host, port, connection_options)
|
52
|
-
socket =
|
53
|
-
connect_timeout: connection_options[:timeout])
|
52
|
+
socket = establish_connection(connection_options[:timeout])
|
54
53
|
socket.write "CONNECT #{host}:#{port} HTTP/1.0\r\n"
|
55
54
|
|
56
55
|
if options[:user]
|
@@ -68,7 +67,12 @@ module Net; module SSH; module Proxy
|
|
68
67
|
raise ConnectError, resp.inspect
|
69
68
|
end
|
70
69
|
|
71
|
-
|
70
|
+
protected
|
71
|
+
|
72
|
+
def establish_connection(connect_timeout)
|
73
|
+
Socket.tcp(proxy_host, proxy_port, nil, nil,
|
74
|
+
connect_timeout: connect_timeout)
|
75
|
+
end
|
72
76
|
|
73
77
|
def parse_response(socket)
|
74
78
|
version, code, reason = socket.gets.chomp.split(/ /, 3)
|
@@ -89,7 +93,6 @@ module Net; module SSH; module Proxy
|
|
89
93
|
:headers => headers,
|
90
94
|
:body => body }
|
91
95
|
end
|
92
|
-
|
93
96
|
end
|
94
97
|
|
95
98
|
end; end; end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'socket'
|
2
|
+
require 'openssl'
|
3
|
+
require 'net/ssh/proxy/errors'
|
4
|
+
require 'net/ssh/proxy/http'
|
5
|
+
|
6
|
+
module Net; module SSH; module Proxy
|
7
|
+
|
8
|
+
# A specialization of the HTTP proxy which encrypts the whole connection
|
9
|
+
# using OpenSSL. This has the advantage that proxy authentication
|
10
|
+
# information is not sent in plaintext.
|
11
|
+
class HTTPS < HTTP
|
12
|
+
|
13
|
+
# Create a new socket factory that tunnels via the given host and
|
14
|
+
# port. The +options+ parameter is a hash of additional settings that
|
15
|
+
# can be used to tweak this proxy connection. In addition to the options
|
16
|
+
# taken by Net::SSH::Proxy::HTTP it supports:
|
17
|
+
#
|
18
|
+
# * :ssl_context => the SSL configuration to use for the connection
|
19
|
+
def initialize(proxy_host, proxy_port=80, options={})
|
20
|
+
@ssl_context = options.delete(:ssl_context) ||
|
21
|
+
OpenSSL::SSL::SSLContext.new
|
22
|
+
super(proxy_host, proxy_port, options)
|
23
|
+
end
|
24
|
+
|
25
|
+
protected
|
26
|
+
|
27
|
+
# Shim to make OpenSSL::SSL::SSLSocket behave like a regular TCPSocket
|
28
|
+
# for all intents and purposes of Net::SSH::BufferedIo
|
29
|
+
module SSLSocketCompatibility
|
30
|
+
def self.extended(object) #:nodoc:
|
31
|
+
object.define_singleton_method(:recv, object.method(:sysread))
|
32
|
+
object.sync_close = true
|
33
|
+
end
|
34
|
+
|
35
|
+
def send(data, _opts)
|
36
|
+
syswrite(data)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def establish_connection(connect_timeout)
|
41
|
+
plain_socket = super(connect_timeout)
|
42
|
+
OpenSSL::SSL::SSLSocket.new(plain_socket, @ssl_context).tap do |socket|
|
43
|
+
socket.extend(SSLSocketCompatibility)
|
44
|
+
socket.connect
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end; end; end
|
data/lib/net/ssh/test.rb
CHANGED
@@ -50,7 +50,7 @@ module Net; module SSH
|
|
50
50
|
# If a block is given, yields the script for the test socket (#socket).
|
51
51
|
# Otherwise, simply returns the socket's script. See Net::SSH::Test::Script.
|
52
52
|
def story
|
53
|
-
yield socket.script if block_given?
|
53
|
+
Net::SSH::Test::Extensions::IO.with_test_extension { yield socket.script if block_given? }
|
54
54
|
return socket.script
|
55
55
|
end
|
56
56
|
|
@@ -81,7 +81,7 @@ module Net; module SSH
|
|
81
81
|
# the block passed to this assertion.
|
82
82
|
def assert_scripted
|
83
83
|
raise "there is no script to be processed" if socket.script.events.empty?
|
84
|
-
yield
|
84
|
+
Net::SSH::Test::Extensions::IO.with_test_extension { yield }
|
85
85
|
assert socket.script.events.empty?, "there should not be any remaining scripted events, but there are still #{socket.script.events.length} pending"
|
86
86
|
end
|
87
87
|
end
|
@@ -113,6 +113,22 @@ module Net; module SSH; module Test
|
|
113
113
|
base.extend(ClassMethods)
|
114
114
|
end
|
115
115
|
|
116
|
+
@extension_enabled = false
|
117
|
+
|
118
|
+
def self.with_test_extension(&block)
|
119
|
+
orig_value = @extension_enabled
|
120
|
+
@extension_enabled = true
|
121
|
+
begin
|
122
|
+
yield
|
123
|
+
ensure
|
124
|
+
@extension_enabled = orig_value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.extension_enabled?
|
129
|
+
@extension_enabled
|
130
|
+
end
|
131
|
+
|
116
132
|
module ClassMethods
|
117
133
|
def self.extended(obj) #:nodoc:
|
118
134
|
class <<obj
|
@@ -125,6 +141,7 @@ module Net; module SSH; module Test
|
|
125
141
|
# writers, and errors arrays are either nil, or contain only objects
|
126
142
|
# that mix in Net::SSH::Test::Extensions::BufferedIo.
|
127
143
|
def select_for_test(readers=nil, writers=nil, errors=nil, wait=nil)
|
144
|
+
return select_for_real(readers, writers, errors, wait) unless Net::SSH::Test::Extensions::IO.extension_enabled?
|
128
145
|
ready_readers = Array(readers).select { |r| r.select_for_read? }
|
129
146
|
ready_writers = Array(writers).select { |r| r.select_for_write? }
|
130
147
|
ready_errors = Array(errors).select { |r| r.select_for_error? }
|
@@ -12,12 +12,10 @@ module Net::SSH::Transport
|
|
12
12
|
@counter_len = orig.block_size
|
13
13
|
orig.encrypt
|
14
14
|
orig.padding = 0
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
private :_update
|
20
|
-
undef :update
|
15
|
+
|
16
|
+
singleton_class.send(:alias_method, :_update, :update)
|
17
|
+
singleton_class.send(:private, :_update)
|
18
|
+
singleton_class.send(:undef_method, :update)
|
21
19
|
|
22
20
|
def iv
|
23
21
|
@counter
|
@@ -73,13 +71,12 @@ module Net::SSH::Transport
|
|
73
71
|
s
|
74
72
|
end
|
75
73
|
|
76
|
-
private
|
77
|
-
|
78
74
|
def xor!(s1, s2)
|
79
75
|
s = []
|
80
76
|
s1.unpack('Q*').zip(s2.unpack('Q*')) {|a,b| s.push(a^b) }
|
81
77
|
s.pack('Q*')
|
82
78
|
end
|
79
|
+
singleton_class.send(:private, :xor!)
|
83
80
|
|
84
81
|
def increment_counter!
|
85
82
|
c = @counter_len
|
@@ -89,7 +86,8 @@ module Net::SSH::Transport
|
|
89
86
|
end
|
90
87
|
end
|
91
88
|
end
|
92
|
-
|
89
|
+
singleton_class.send(:private, :increment_counter!)
|
90
|
+
}
|
93
91
|
end
|
94
92
|
end
|
95
93
|
end
|
data/lib/net/ssh/version.rb
CHANGED
@@ -53,9 +53,9 @@ module Net; module SSH
|
|
53
53
|
# The tiny component of this version of the Net::SSH library
|
54
54
|
TINY = 0
|
55
55
|
|
56
|
-
# The prerelease component of this version of the Net::SSH library
|
56
|
+
# The prerelease component of this version of the Net::SSH library
|
57
57
|
# nil allowed
|
58
|
-
PRE = "
|
58
|
+
PRE = "beta4"
|
59
59
|
|
60
60
|
# The current version of the Net::SSH library as a Version instance
|
61
61
|
CURRENT = new(*[MAJOR, MINOR, TINY, PRE].compact)
|
data/net-ssh.gemspec
CHANGED
@@ -22,7 +22,7 @@ Gem::Specification.new do |spec|
|
|
22
22
|
"LICENSE.txt",
|
23
23
|
"README.rdoc"
|
24
24
|
]
|
25
|
-
|
25
|
+
|
26
26
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
27
27
|
spec.bindir = "exe"
|
28
28
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
@@ -31,7 +31,7 @@ Gem::Specification.new do |spec|
|
|
31
31
|
unless ENV['NET_SSH_NO_RBNACL']
|
32
32
|
spec.add_development_dependency("rbnacl-libsodium", "~> 1.0.10")
|
33
33
|
spec.add_development_dependency("rbnacl", "~> 3.4.0")
|
34
|
-
spec.add_development_dependency("bcrypt_pbkdf", "~> 1.0.0
|
34
|
+
spec.add_development_dependency("bcrypt_pbkdf", "~> 1.0.0") unless RUBY_PLATFORM == "java"
|
35
35
|
end
|
36
36
|
|
37
37
|
spec.add_development_dependency "bundler", "~> 1.11"
|
@@ -39,7 +39,6 @@ Gem::Specification.new do |spec|
|
|
39
39
|
spec.add_development_dependency "minitest", "~> 5.0"
|
40
40
|
spec.add_development_dependency "rubocop", "~> 0.39.0"
|
41
41
|
spec.add_development_dependency "mocha", ">= 1.1.0"
|
42
|
-
spec.add_development_dependency("byebug") if RUBY_ENGINE == "ruby"
|
43
42
|
|
44
|
-
spec.add_dependency('jruby-pageant', '>= 1.1.1') if
|
43
|
+
spec.add_dependency('jruby-pageant', '>= 1.1.1') if RUBY_ENGINE == 'jruby'
|
45
44
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: net-ssh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.0.
|
4
|
+
version: 4.0.0.beta4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jamis Buck
|
@@ -31,7 +31,7 @@ cert_chain:
|
|
31
31
|
s/ZUKye79ELwFYKJOhjW5g725OL3hy+llhEleytwKRwgXFQBPTC4f5UkdxZVVWGH
|
32
32
|
e2C9M1m/2odPZo8h
|
33
33
|
-----END CERTIFICATE-----
|
34
|
-
date: 2016-
|
34
|
+
date: 2016-11-25 00:00:00.000000000 Z
|
35
35
|
dependencies:
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: rbnacl-libsodium
|
@@ -67,14 +67,14 @@ dependencies:
|
|
67
67
|
requirements:
|
68
68
|
- - "~>"
|
69
69
|
- !ruby/object:Gem::Version
|
70
|
-
version: 1.0.0
|
70
|
+
version: 1.0.0
|
71
71
|
type: :development
|
72
72
|
prerelease: false
|
73
73
|
version_requirements: !ruby/object:Gem::Requirement
|
74
74
|
requirements:
|
75
75
|
- - "~>"
|
76
76
|
- !ruby/object:Gem::Version
|
77
|
-
version: 1.0.0
|
77
|
+
version: 1.0.0
|
78
78
|
- !ruby/object:Gem::Dependency
|
79
79
|
name: bundler
|
80
80
|
requirement: !ruby/object:Gem::Requirement
|
@@ -145,20 +145,6 @@ dependencies:
|
|
145
145
|
- - ">="
|
146
146
|
- !ruby/object:Gem::Version
|
147
147
|
version: 1.1.0
|
148
|
-
- !ruby/object:Gem::Dependency
|
149
|
-
name: byebug
|
150
|
-
requirement: !ruby/object:Gem::Requirement
|
151
|
-
requirements:
|
152
|
-
- - ">="
|
153
|
-
- !ruby/object:Gem::Version
|
154
|
-
version: '0'
|
155
|
-
type: :development
|
156
|
-
prerelease: false
|
157
|
-
version_requirements: !ruby/object:Gem::Requirement
|
158
|
-
requirements:
|
159
|
-
- - ">="
|
160
|
-
- !ruby/object:Gem::Version
|
161
|
-
version: '0'
|
162
148
|
description: 'Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It
|
163
149
|
allows you to write programs that invoke and interact with processes on remote servers,
|
164
150
|
via SSH2.'
|
@@ -178,6 +164,7 @@ files:
|
|
178
164
|
- Gemfile
|
179
165
|
- Gemfile.norbnacl
|
180
166
|
- Gemfile.norbnacl.lock
|
167
|
+
- ISSUE_TEMPLATE.md
|
181
168
|
- LICENSE.txt
|
182
169
|
- Manifest
|
183
170
|
- README.rdoc
|
@@ -186,8 +173,6 @@ files:
|
|
186
173
|
- appveyor.yml
|
187
174
|
- lib/net/ssh.rb
|
188
175
|
- lib/net/ssh/authentication/agent.rb
|
189
|
-
- lib/net/ssh/authentication/agent/java_pageant.rb
|
190
|
-
- lib/net/ssh/authentication/agent/socket.rb
|
191
176
|
- lib/net/ssh/authentication/constants.rb
|
192
177
|
- lib/net/ssh/authentication/ed25519.rb
|
193
178
|
- lib/net/ssh/authentication/ed25519_loader.rb
|
@@ -218,6 +203,7 @@ files:
|
|
218
203
|
- lib/net/ssh/proxy/command.rb
|
219
204
|
- lib/net/ssh/proxy/errors.rb
|
220
205
|
- lib/net/ssh/proxy/http.rb
|
206
|
+
- lib/net/ssh/proxy/https.rb
|
221
207
|
- lib/net/ssh/proxy/socks4.rb
|
222
208
|
- lib/net/ssh/proxy/socks5.rb
|
223
209
|
- lib/net/ssh/ruby_compat.rb
|
@@ -291,8 +277,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
291
277
|
version: 1.3.1
|
292
278
|
requirements: []
|
293
279
|
rubyforge_project:
|
294
|
-
rubygems_version: 2.
|
280
|
+
rubygems_version: 2.5.1
|
295
281
|
signing_key:
|
296
282
|
specification_version: 4
|
297
283
|
summary: 'Net::SSH: a pure-Ruby implementation of the SSH2 client protocol.'
|
298
284
|
test_files: []
|
285
|
+
has_rdoc:
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'jruby_pageant'
|
2
|
-
|
3
|
-
module Net; module SSH; module Authentication
|
4
|
-
|
5
|
-
# This class implements an agent for JRuby + Pageant.
|
6
|
-
#
|
7
|
-
# Written by Artūras Šlajus <arturas.slajus@gmail.com>
|
8
|
-
class Agent
|
9
|
-
include Loggable
|
10
|
-
include JRubyPageant
|
11
|
-
|
12
|
-
# A simple module for extending keys, to allow blobs and comments to be
|
13
|
-
# specified for them.
|
14
|
-
module Key
|
15
|
-
# :blob is used by OpenSSL::PKey::RSA#to_blob
|
16
|
-
attr_accessor :java_blob
|
17
|
-
attr_accessor :comment
|
18
|
-
end
|
19
|
-
|
20
|
-
# Instantiates a new agent object, connects to a running SSH agent,
|
21
|
-
# negotiates the agent protocol version, and returns the agent object.
|
22
|
-
def self.connect(logger=nil, agent_socket_factory)
|
23
|
-
agent = new(logger)
|
24
|
-
agent.connect!
|
25
|
-
agent
|
26
|
-
end
|
27
|
-
|
28
|
-
# Creates a new Agent object, using the optional logger instance to
|
29
|
-
# report status.
|
30
|
-
def initialize(logger=nil)
|
31
|
-
self.logger = logger
|
32
|
-
end
|
33
|
-
|
34
|
-
# Connect to the agent process using the socket factory and socket name
|
35
|
-
# given by the attribute writers. If the agent on the other end of the
|
36
|
-
# socket reports that it is an SSH2-compatible agent, this will fail
|
37
|
-
# (it only supports the ssh-agent distributed by OpenSSH).
|
38
|
-
def connect!
|
39
|
-
debug { "connecting to Pageant ssh-agent (via java connector)" }
|
40
|
-
@agent_proxy = JRubyPageant.create
|
41
|
-
unless @agent_proxy.is_running
|
42
|
-
raise AgentNotAvailable, "Pageant is not running!"
|
43
|
-
end
|
44
|
-
debug { "connection to Pageant ssh-agent (via java connector) succeeded" }
|
45
|
-
rescue AgentProxyException => e
|
46
|
-
error { "could not connect to Pageant ssh-agent (via java connector)" }
|
47
|
-
raise AgentNotAvailable, e.message, e.backtrace
|
48
|
-
end
|
49
|
-
|
50
|
-
# Return an array of all identities (public keys) known to the agent.
|
51
|
-
# Each key returned is augmented with a +comment+ property which is set
|
52
|
-
# to the comment returned by the agent for that key.
|
53
|
-
def identities
|
54
|
-
debug { "getting identities from Pageant" }
|
55
|
-
@agent_proxy.get_identities.map do |identity|
|
56
|
-
blob = identity.get_blob
|
57
|
-
key = Buffer.new(String.from_java_bytes(blob)).read_key
|
58
|
-
key.extend(Key)
|
59
|
-
key.java_blob = blob
|
60
|
-
key.comment = String.from_java_bytes(identity.get_comment)
|
61
|
-
key
|
62
|
-
end
|
63
|
-
rescue AgentProxyException => e
|
64
|
-
raise AgentError, "Cannot get identities: #{e.message}", e.backtrace
|
65
|
-
end
|
66
|
-
|
67
|
-
# Simulate agent close. This agent reference is no longer able to
|
68
|
-
# query the agent.
|
69
|
-
def close
|
70
|
-
@agent_proxy = nil
|
71
|
-
end
|
72
|
-
|
73
|
-
# Using the agent and the given public key, sign the given data. The
|
74
|
-
# signature is returned in SSH2 format.
|
75
|
-
def sign(key, data)
|
76
|
-
signed = @agent_proxy.sign(key.java_blob, data.to_java_bytes)
|
77
|
-
String.from_java_bytes(signed)
|
78
|
-
rescue AgentProxyException => e
|
79
|
-
raise AgentError,
|
80
|
-
"agent could not sign data with requested identity: #{e.message}",
|
81
|
-
e.backtrace
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
end; end; end
|
@@ -1,178 +0,0 @@
|
|
1
|
-
require 'net/ssh/transport/server_version'
|
2
|
-
|
3
|
-
# Only load pageant on Windows
|
4
|
-
if Net::SSH::Authentication::PLATFORM == :win32
|
5
|
-
require 'net/ssh/authentication/pageant'
|
6
|
-
end
|
7
|
-
|
8
|
-
module Net; module SSH; module Authentication
|
9
|
-
|
10
|
-
# This class implements a simple client for the ssh-agent protocol. It
|
11
|
-
# does not implement any specific protocol, but instead copies the
|
12
|
-
# behavior of the ssh-agent functions in the OpenSSH library (3.8).
|
13
|
-
#
|
14
|
-
# This means that although it behaves like a SSH1 client, it also has
|
15
|
-
# some SSH2 functionality (like signing data).
|
16
|
-
class Agent
|
17
|
-
include Loggable
|
18
|
-
|
19
|
-
# A simple module for extending keys, to allow comments to be specified
|
20
|
-
# for them.
|
21
|
-
module Comment
|
22
|
-
attr_accessor :comment
|
23
|
-
end
|
24
|
-
|
25
|
-
SSH2_AGENT_REQUEST_VERSION = 1
|
26
|
-
SSH2_AGENT_REQUEST_IDENTITIES = 11
|
27
|
-
SSH2_AGENT_IDENTITIES_ANSWER = 12
|
28
|
-
SSH2_AGENT_SIGN_REQUEST = 13
|
29
|
-
SSH2_AGENT_SIGN_RESPONSE = 14
|
30
|
-
SSH2_AGENT_FAILURE = 30
|
31
|
-
SSH2_AGENT_VERSION_RESPONSE = 103
|
32
|
-
|
33
|
-
SSH_COM_AGENT2_FAILURE = 102
|
34
|
-
|
35
|
-
SSH_AGENT_REQUEST_RSA_IDENTITIES = 1
|
36
|
-
SSH_AGENT_RSA_IDENTITIES_ANSWER1 = 2
|
37
|
-
SSH_AGENT_RSA_IDENTITIES_ANSWER2 = 5
|
38
|
-
SSH_AGENT_FAILURE = 5
|
39
|
-
|
40
|
-
# The underlying socket being used to communicate with the SSH agent.
|
41
|
-
attr_reader :socket
|
42
|
-
|
43
|
-
# Instantiates a new agent object, connects to a running SSH agent,
|
44
|
-
# negotiates the agent protocol version, and returns the agent object.
|
45
|
-
def self.connect(logger=nil, agent_socket_factory = nil)
|
46
|
-
agent = new(logger)
|
47
|
-
agent.connect!(agent_socket_factory)
|
48
|
-
agent.negotiate!
|
49
|
-
agent
|
50
|
-
end
|
51
|
-
|
52
|
-
# Creates a new Agent object, using the optional logger instance to
|
53
|
-
# report status.
|
54
|
-
def initialize(logger=nil)
|
55
|
-
self.logger = logger
|
56
|
-
end
|
57
|
-
|
58
|
-
# Connect to the agent process using the socket factory and socket name
|
59
|
-
# given by the attribute writers. If the agent on the other end of the
|
60
|
-
# socket reports that it is an SSH2-compatible agent, this will fail
|
61
|
-
# (it only supports the ssh-agent distributed by OpenSSH).
|
62
|
-
def connect!(agent_socket_factory = nil)
|
63
|
-
begin
|
64
|
-
debug { "connecting to ssh-agent" }
|
65
|
-
@socket = agent_socket_factory.nil? ? socket_class.open(ENV['SSH_AUTH_SOCK']) : agent_socket_factory.call
|
66
|
-
rescue
|
67
|
-
error { "could not connect to ssh-agent" }
|
68
|
-
raise AgentNotAvailable, $!.message
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
# Attempts to negotiate the SSH agent protocol version. Raises an error
|
73
|
-
# if the version could not be negotiated successfully.
|
74
|
-
def negotiate!
|
75
|
-
# determine what type of agent we're communicating with
|
76
|
-
type, body = send_and_wait(SSH2_AGENT_REQUEST_VERSION, :string, Transport::ServerVersion::PROTO_VERSION)
|
77
|
-
|
78
|
-
if type == SSH2_AGENT_VERSION_RESPONSE
|
79
|
-
raise AgentNotAvailable, "SSH2 agents are not yet supported"
|
80
|
-
elsif type == SSH2_AGENT_FAILURE
|
81
|
-
debug { "Unexpected response type==#{type}, this will be ignored" }
|
82
|
-
elsif type != SSH_AGENT_RSA_IDENTITIES_ANSWER1 && type != SSH_AGENT_RSA_IDENTITIES_ANSWER2
|
83
|
-
raise AgentNotAvailable, "unknown response from agent: #{type}, #{body.to_s.inspect}"
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# Return an array of all identities (public keys) known to the agent.
|
88
|
-
# Each key returned is augmented with a +comment+ property which is set
|
89
|
-
# to the comment returned by the agent for that key.
|
90
|
-
def identities
|
91
|
-
type, body = send_and_wait(SSH2_AGENT_REQUEST_IDENTITIES)
|
92
|
-
raise AgentError, "could not get identity count" if agent_failed(type)
|
93
|
-
raise AgentError, "bad authentication reply: #{type}" if type != SSH2_AGENT_IDENTITIES_ANSWER
|
94
|
-
|
95
|
-
identities = []
|
96
|
-
body.read_long.times do
|
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
|
107
|
-
end
|
108
|
-
|
109
|
-
return identities
|
110
|
-
end
|
111
|
-
|
112
|
-
# Closes this socket. This agent reference is no longer able to
|
113
|
-
# query the agent.
|
114
|
-
def close
|
115
|
-
@socket.close
|
116
|
-
end
|
117
|
-
|
118
|
-
# Using the agent and the given public key, sign the given data. The
|
119
|
-
# signature is returned in SSH2 format.
|
120
|
-
def sign(key, data)
|
121
|
-
type, reply = send_and_wait(SSH2_AGENT_SIGN_REQUEST, :string, Buffer.from(:key, key), :string, data, :long, 0)
|
122
|
-
|
123
|
-
if agent_failed(type)
|
124
|
-
raise AgentError, "agent could not sign data with requested identity"
|
125
|
-
elsif type != SSH2_AGENT_SIGN_RESPONSE
|
126
|
-
raise AgentError, "bad authentication response #{type}"
|
127
|
-
end
|
128
|
-
|
129
|
-
return reply.read_string
|
130
|
-
end
|
131
|
-
|
132
|
-
private
|
133
|
-
|
134
|
-
# Returns the agent socket factory to use.
|
135
|
-
def socket_class
|
136
|
-
if Net::SSH::Authentication::PLATFORM == :win32
|
137
|
-
Pageant::Socket
|
138
|
-
else
|
139
|
-
UNIXSocket
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# Send a new packet of the given type, with the associated data.
|
144
|
-
def send_packet(type, *args)
|
145
|
-
buffer = Buffer.from(*args)
|
146
|
-
data = [buffer.length + 1, type.to_i, buffer.to_s].pack("NCA*")
|
147
|
-
debug { "sending agent request #{type} len #{buffer.length}" }
|
148
|
-
@socket.send data, 0
|
149
|
-
end
|
150
|
-
|
151
|
-
# Read the next packet from the agent. This will return a two-part
|
152
|
-
# tuple consisting of the packet type, and the packet's body (which
|
153
|
-
# is returned as a Net::SSH::Buffer).
|
154
|
-
def read_packet
|
155
|
-
buffer = Net::SSH::Buffer.new(@socket.read(4))
|
156
|
-
buffer.append(@socket.read(buffer.read_long))
|
157
|
-
type = buffer.read_byte
|
158
|
-
debug { "received agent packet #{type} len #{buffer.length-4}" }
|
159
|
-
return type, buffer
|
160
|
-
end
|
161
|
-
|
162
|
-
# Send the given packet and return the subsequent reply from the agent.
|
163
|
-
# (See #send_packet and #read_packet).
|
164
|
-
def send_and_wait(type, *args)
|
165
|
-
send_packet(type, *args)
|
166
|
-
read_packet
|
167
|
-
end
|
168
|
-
|
169
|
-
# Returns +true+ if the parameter indicates a "failure" response from
|
170
|
-
# the agent, and +false+ otherwise.
|
171
|
-
def agent_failed(type)
|
172
|
-
type == SSH_AGENT_FAILURE ||
|
173
|
-
type == SSH2_AGENT_FAILURE ||
|
174
|
-
type == SSH_COM_AGENT2_FAILURE
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
|
-
end; end; end
|