net-ssh 2.9.0 → 2.9.1
Sign up to get free protection for your applications and to get access to all the features.
- data.tar.gz.sig +0 -0
- data/.travis.yml +0 -1
- data/CHANGES.txt +6 -0
- data/lib/net/ssh/authentication/pageant.rb +197 -68
- data/lib/net/ssh/buffer.rb +1 -1
- data/lib/net/ssh/config.rb +12 -2
- data/lib/net/ssh/transport/cipher_factory.rb +3 -0
- data/lib/net/ssh/version.rb +1 -1
- data/net-ssh.gemspec +4 -2
- data/test/configs/negative_match +6 -0
- data/test/manual/test_pageant.rb +37 -0
- data/test/test_config.rb +6 -0
- metadata +4 -2
- metadata.gz.sig +0 -0
data.tar.gz.sig
CHANGED
Binary file
|
data/.travis.yml
CHANGED
data/CHANGES.txt
CHANGED
@@ -27,15 +27,21 @@ module Net; module SSH; module Authentication
|
|
27
27
|
# The definition of the Windows methods and data structures used in
|
28
28
|
# communicating with the pageant process.
|
29
29
|
module Win
|
30
|
+
# Compatibility on initialization
|
30
31
|
if RUBY_VERSION < "1.9"
|
31
32
|
extend DL::Importable
|
32
33
|
|
33
34
|
dlload 'user32'
|
34
35
|
dlload 'kernel32'
|
36
|
+
dlload 'advapi32'
|
37
|
+
|
38
|
+
SIZEOF_DWORD = DL.sizeof('L')
|
35
39
|
else
|
36
40
|
extend DL::Importer
|
37
|
-
dlload 'user32','kernel32'
|
41
|
+
dlload 'user32','kernel32', 'advapi32'
|
38
42
|
include DL::Win32Types
|
43
|
+
|
44
|
+
SIZEOF_DWORD = DL::SIZEOF_LONG
|
39
45
|
end
|
40
46
|
|
41
47
|
typealias("LPCTSTR", "char *") # From winnt.h
|
@@ -45,6 +51,7 @@ module Net; module SSH; module Authentication
|
|
45
51
|
typealias("WPARAM", "unsigned int *") # From windef.h
|
46
52
|
typealias("LPARAM", "long *") # From windef.h
|
47
53
|
typealias("PDWORD_PTR", "long *") # From basetsd.h
|
54
|
+
typealias("USHORT", "unsigned short") # From windef.h
|
48
55
|
|
49
56
|
# From winbase.h, winnt.h
|
50
57
|
INVALID_HANDLE_VALUE = -1
|
@@ -63,8 +70,8 @@ module Net; module SSH; module Authentication
|
|
63
70
|
|
64
71
|
# args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
|
65
72
|
# dwMaximumSizeLow, lpName
|
66
|
-
extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD,
|
67
|
-
'DWORD, LPCTSTR)'
|
73
|
+
extern 'HANDLE CreateFileMapping(HANDLE, void *, DWORD, ' +
|
74
|
+
'DWORD, DWORD, LPCTSTR)'
|
68
75
|
|
69
76
|
# args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
|
70
77
|
# dwfileOffsetLow, dwNumberOfBytesToMap
|
@@ -79,9 +86,180 @@ module Net; module SSH; module Authentication
|
|
79
86
|
# args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
|
80
87
|
extern 'LRESULT SendMessageTimeout(HWND, UINT, WPARAM, LPARAM, ' +
|
81
88
|
'UINT, UINT, PDWORD_PTR)'
|
89
|
+
|
90
|
+
# args: none
|
91
|
+
extern 'DWORD GetLastError()'
|
92
|
+
|
93
|
+
# args: none
|
94
|
+
extern 'HANDLE GetCurrentProcess()'
|
95
|
+
|
96
|
+
# args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle
|
97
|
+
extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)'
|
98
|
+
|
99
|
+
# args: hTokenHandle, uTokenInformationClass,
|
100
|
+
# (out) lpTokenInformation, dwTokenInformationLength
|
101
|
+
# (out) pdwInfoReturnLength
|
102
|
+
extern 'BOOL GetTokenInformation(HANDLE, UINT, LPVOID, DWORD, ' +
|
103
|
+
'PDWORD)'
|
104
|
+
|
105
|
+
# args: (out) lpSecurityDescriptor, dwRevisionLevel
|
106
|
+
extern 'BOOL InitializeSecurityDescriptor(LPVOID, DWORD)'
|
107
|
+
|
108
|
+
# args: (out) lpSecurityDescriptor, lpOwnerSid, bOwnerDefaulted
|
109
|
+
extern 'BOOL SetSecurityDescriptorOwner(LPVOID, LPVOID, BOOL)'
|
110
|
+
|
111
|
+
# args: pSecurityDescriptor
|
112
|
+
extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
|
113
|
+
|
114
|
+
# Constants needed for security attribute retrieval.
|
115
|
+
# Specifies the access mask corresponding to the desired access
|
116
|
+
# rights.
|
117
|
+
TOKEN_QUERY = 0x8
|
118
|
+
|
119
|
+
# The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
|
120
|
+
TOKEN_USER_INFORMATION_CLASS = 1
|
121
|
+
|
122
|
+
# The initial revision level assigned to the security descriptor.
|
123
|
+
REVISION = 1
|
124
|
+
|
125
|
+
# Structs for security attribute functions.
|
126
|
+
# Holds the retrieved user access token.
|
127
|
+
TOKEN_USER = struct ['void * SID', 'DWORD ATTRIBUTES']
|
128
|
+
|
129
|
+
# Contains the security descriptor, this gets passed to the
|
130
|
+
# function that constructs the shared memory map.
|
131
|
+
SECURITY_ATTRIBUTES = struct ['DWORD nLength',
|
132
|
+
'LPVOID lpSecurityDescriptor',
|
133
|
+
'BOOL bInheritHandle']
|
134
|
+
|
135
|
+
# The security descriptor holds security information.
|
136
|
+
SECURITY_DESCRIPTOR = struct ['UCHAR Revision', 'UCHAR Sbz1',
|
137
|
+
'USHORT Control', 'LPVOID Owner',
|
138
|
+
'LPVOID Group', 'LPVOID Sacl',
|
139
|
+
'LPVOID Dacl']
|
140
|
+
|
141
|
+
# Compatibility for security attribute retrieval.
|
82
142
|
if RUBY_VERSION < "1.9"
|
83
|
-
|
84
|
-
|
143
|
+
# Alias functions to > 1.9 capitalization
|
144
|
+
%w(findWindow
|
145
|
+
getCurrentProcess
|
146
|
+
initializeSecurityDescriptor
|
147
|
+
setSecurityDescriptorOwner
|
148
|
+
isValidSecurityDescriptor
|
149
|
+
openProcessToken
|
150
|
+
getTokenInformation
|
151
|
+
getLastError
|
152
|
+
getCurrentThreadId
|
153
|
+
createFileMapping
|
154
|
+
mapViewOfFile
|
155
|
+
sendMessageTimeout
|
156
|
+
unmapViewOfFile
|
157
|
+
closeHandle).each do |name|
|
158
|
+
new_name = name[0].chr.upcase + name[1..name.length]
|
159
|
+
alias_method new_name, name
|
160
|
+
module_function new_name
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.malloc_ptr(size)
|
164
|
+
return DL.malloc(size)
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.get_ptr(data)
|
168
|
+
return data.to_ptr
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.set_ptr_data(ptr, data)
|
172
|
+
ptr[0] = data
|
173
|
+
end
|
174
|
+
else
|
175
|
+
def self.malloc_ptr(size)
|
176
|
+
return DL::CPtr.malloc(size, DL::RUBY_FREE)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.get_ptr(data)
|
180
|
+
return DL::CPtr.to_ptr data
|
181
|
+
end
|
182
|
+
|
183
|
+
def self.set_ptr_data(ptr, data)
|
184
|
+
DL::CPtr.new(ptr)[0,data.size] = data
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def self.get_security_attributes_for_user
|
189
|
+
user = get_current_user
|
190
|
+
|
191
|
+
psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size)
|
192
|
+
raise_error_if_zero(
|
193
|
+
Win.InitializeSecurityDescriptor(psd_information,
|
194
|
+
Win::REVISION))
|
195
|
+
raise_error_if_zero(
|
196
|
+
Win.SetSecurityDescriptorOwner(psd_information, user.SID,
|
197
|
+
0))
|
198
|
+
raise_error_if_zero(
|
199
|
+
Win.IsValidSecurityDescriptor(psd_information))
|
200
|
+
|
201
|
+
nLength = Win::SECURITY_ATTRIBUTES.size
|
202
|
+
lpSecurityDescriptor = psd_information
|
203
|
+
bInheritHandle = 1
|
204
|
+
sa = [nLength, lpSecurityDescriptor.to_i,
|
205
|
+
bInheritHandle].pack("LLC")
|
206
|
+
|
207
|
+
return sa
|
208
|
+
end
|
209
|
+
|
210
|
+
def self.get_current_user
|
211
|
+
token_handle = open_process_token(Win.GetCurrentProcess,
|
212
|
+
Win::TOKEN_QUERY)
|
213
|
+
token_user = get_token_information(token_handle,
|
214
|
+
Win::TOKEN_USER_INFORMATION_CLASS)
|
215
|
+
return token_user
|
216
|
+
end
|
217
|
+
|
218
|
+
def self.open_process_token(process_handle, desired_access)
|
219
|
+
ptoken_handle = malloc_ptr(Win::SIZEOF_DWORD)
|
220
|
+
|
221
|
+
raise_error_if_zero(
|
222
|
+
Win.OpenProcessToken(process_handle, desired_access,
|
223
|
+
ptoken_handle))
|
224
|
+
token_handle = ptoken_handle.ptr.to_i
|
225
|
+
|
226
|
+
return token_handle
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.get_token_information(token_handle,
|
230
|
+
token_information_class)
|
231
|
+
# Hold the size of the information to be returned
|
232
|
+
preturn_length = malloc_ptr(Win::SIZEOF_DWORD)
|
233
|
+
|
234
|
+
# Going to throw an INSUFFICIENT_BUFFER_ERROR, but that is ok
|
235
|
+
# here. This is retrieving the size of the information to be
|
236
|
+
# returned.
|
237
|
+
Win.GetTokenInformation(token_handle,
|
238
|
+
token_information_class,
|
239
|
+
Win::NULL, 0, preturn_length)
|
240
|
+
ptoken_information = malloc_ptr(preturn_length.ptr.to_i)
|
241
|
+
|
242
|
+
# This call is going to write the requested information to
|
243
|
+
# the memory location referenced by token_information.
|
244
|
+
raise_error_if_zero(
|
245
|
+
Win.GetTokenInformation(token_handle,
|
246
|
+
token_information_class,
|
247
|
+
ptoken_information,
|
248
|
+
ptoken_information.size,
|
249
|
+
preturn_length))
|
250
|
+
|
251
|
+
return TOKEN_USER.new(ptoken_information)
|
252
|
+
end
|
253
|
+
|
254
|
+
def self.raise_error_if_zero(result)
|
255
|
+
if result == 0
|
256
|
+
raise "Windows error: #{Win.GetLastError}"
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
# Get a null-terminated string given a string.
|
261
|
+
def self.get_cstr(str)
|
262
|
+
return str + "\000"
|
85
263
|
end
|
86
264
|
end
|
87
265
|
|
@@ -140,78 +318,27 @@ module Net; module SSH; module Authentication
|
|
140
318
|
|
141
319
|
def close
|
142
320
|
end
|
143
|
-
|
144
|
-
def send_query(query)
|
145
|
-
if RUBY_VERSION < "1.9"
|
146
|
-
send_query_18(query)
|
147
|
-
else
|
148
|
-
send_query_19(query)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Packages the given query string and sends it to the pageant
|
153
|
-
# process via the Windows messaging subsystem. The result is
|
154
|
-
# cached, to be returned piece-wise when #read is called.
|
155
|
-
def send_query_18(query)
|
156
|
-
res = nil
|
157
|
-
filemap = 0
|
158
|
-
ptr = nil
|
159
|
-
id = DL::PtrData.malloc(DL.sizeof("L"))
|
160
|
-
|
161
|
-
mapname = "PageantRequest%08x\000" % Win.getCurrentThreadId()
|
162
|
-
filemap = Win.createFileMapping(Win::INVALID_HANDLE_VALUE,
|
163
|
-
Win::NULL,
|
164
|
-
Win::PAGE_READWRITE, 0,
|
165
|
-
AGENT_MAX_MSGLEN, mapname)
|
166
|
-
if filemap == 0
|
167
|
-
raise Net::SSH::Exception,
|
168
|
-
"Creation of file mapping failed"
|
169
|
-
end
|
170
|
-
|
171
|
-
ptr = Win.mapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
|
172
|
-
AGENT_MAX_MSGLEN)
|
173
|
-
|
174
|
-
if ptr.nil? || ptr.null?
|
175
|
-
raise Net::SSH::Exception, "Mapping of file failed"
|
176
|
-
end
|
177
|
-
|
178
|
-
ptr[0] = query
|
179
|
-
|
180
|
-
cds = [AGENT_COPYDATA_ID, mapname.size + 1, mapname].
|
181
|
-
pack("LLp").to_ptr
|
182
|
-
succ = Win.sendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
|
183
|
-
cds, Win::SMTO_NORMAL, 5000, id)
|
184
|
-
|
185
|
-
if succ > 0
|
186
|
-
retlen = 4 + ptr.to_s(4).unpack("N")[0]
|
187
|
-
res = ptr.to_s(retlen)
|
188
|
-
end
|
189
|
-
|
190
|
-
return res
|
191
|
-
ensure
|
192
|
-
Win.unmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
|
193
|
-
Win.closeHandle(filemap) if filemap != 0
|
194
|
-
end
|
195
321
|
|
196
322
|
# Packages the given query string and sends it to the pageant
|
197
323
|
# process via the Windows messaging subsystem. The result is
|
198
324
|
# cached, to be returned piece-wise when #read is called.
|
199
|
-
def
|
325
|
+
def send_query(query)
|
200
326
|
res = nil
|
201
327
|
filemap = 0
|
202
328
|
ptr = nil
|
203
|
-
id =
|
329
|
+
id = Win.malloc_ptr(Win::SIZEOF_DWORD)
|
204
330
|
|
205
|
-
mapname = "PageantRequest%08x
|
331
|
+
mapname = "PageantRequest%08x" % Win.GetCurrentThreadId()
|
332
|
+
security_attributes = Win.get_ptr Win.get_security_attributes_for_user
|
206
333
|
|
207
|
-
filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
|
208
|
-
|
209
|
-
Win::PAGE_READWRITE, 0,
|
334
|
+
filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
|
335
|
+
security_attributes,
|
336
|
+
Win::PAGE_READWRITE, 0,
|
210
337
|
AGENT_MAX_MSGLEN, mapname)
|
211
338
|
|
212
339
|
if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
|
213
340
|
raise Net::SSH::Exception,
|
214
|
-
"Creation of file mapping failed"
|
341
|
+
"Creation of file mapping failed with error: #{Win.GetLastError}"
|
215
342
|
end
|
216
343
|
|
217
344
|
ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
|
@@ -221,17 +348,19 @@ module Net; module SSH; module Authentication
|
|
221
348
|
raise Net::SSH::Exception, "Mapping of file failed"
|
222
349
|
end
|
223
350
|
|
224
|
-
|
351
|
+
Win.set_ptr_data(ptr, query)
|
225
352
|
|
226
|
-
cds =
|
227
|
-
|
353
|
+
cds = Win.get_ptr [AGENT_COPYDATA_ID, mapname.size + 1,
|
354
|
+
Win.get_cstr(mapname)].pack("LLp")
|
228
355
|
succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
|
229
356
|
cds, Win::SMTO_NORMAL, 5000, id)
|
230
357
|
|
231
358
|
if succ > 0
|
232
359
|
retlen = 4 + ptr.to_s(4).unpack("N")[0]
|
233
360
|
res = ptr.to_s(retlen)
|
234
|
-
|
361
|
+
else
|
362
|
+
raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}"
|
363
|
+
end
|
235
364
|
|
236
365
|
return res
|
237
366
|
ensure
|
data/lib/net/ssh/buffer.rb
CHANGED
@@ -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*)
|
258
|
+
when /^ecdsa\-sha2\-(\w*)$/, /^ssh-ed25519(-cert-v01@openssh.com)?$/
|
259
259
|
unless defined?(OpenSSL::PKey::EC)
|
260
260
|
raise NotImplementedError, "unsupported key type `#{type}'"
|
261
261
|
else
|
data/lib/net/ssh/config.rb
CHANGED
@@ -103,8 +103,18 @@ module Net; module SSH
|
|
103
103
|
if key == 'host'
|
104
104
|
# Support "Host host1 host2 hostN".
|
105
105
|
# See http://github.com/net-ssh/net-ssh/issues#issue/6
|
106
|
-
|
107
|
-
|
106
|
+
negative_hosts, positive_hosts = value.to_s.split(/\s+/).partition { |h| h.start_with?('!') }
|
107
|
+
|
108
|
+
# Check for negative patterns first. If the host matches, that overrules any other positive match.
|
109
|
+
# The host substring code is used to strip out the starting "!" so the regexp will be correct.
|
110
|
+
negative_match = negative_hosts.select { |h| host =~ pattern2regex(h[1..-1]) }.first
|
111
|
+
|
112
|
+
if negative_match
|
113
|
+
matched_host = nil
|
114
|
+
else
|
115
|
+
matched_host = positive_hosts.select { |h| host =~ pattern2regex(h) }.first
|
116
|
+
end
|
117
|
+
|
108
118
|
seen_host = true
|
109
119
|
settings[key] = host
|
110
120
|
elsif !seen_host
|
@@ -41,6 +41,9 @@ module Net; module SSH; module Transport
|
|
41
41
|
"camellia192-ctr@openssh.org" => "camellia-192-ecb",
|
42
42
|
"camellia256-ctr@openssh.org" => "camellia-256-ecb",
|
43
43
|
|
44
|
+
"aes256-gcm@openssh.com" => "aes-256-gcm",
|
45
|
+
"aes128-gcm@openssh.com" => "aes-128-gcm",
|
46
|
+
|
44
47
|
"none" => "none",
|
45
48
|
}
|
46
49
|
|
data/lib/net/ssh/version.rb
CHANGED
data/net-ssh.gemspec
CHANGED
@@ -5,12 +5,12 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "net-ssh"
|
8
|
-
s.version = "2.9.
|
8
|
+
s.version = "2.9.1"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Jamis Buck", "Delano Mandelbaum"]
|
12
12
|
s.cert_chain = ["gem-public_cert.pem"]
|
13
|
-
s.date = "2014-
|
13
|
+
s.date = "2014-05-13"
|
14
14
|
s.description = "Net::SSH: a pure-Ruby implementation of the SSH2 client protocol. It allows you to write programs that invoke and interact with processes on remote servers, via SSH2."
|
15
15
|
s.email = "net-ssh@solutious.com"
|
16
16
|
s.extra_rdoc_files = [
|
@@ -129,6 +129,7 @@ Gem::Specification.new do |s|
|
|
129
129
|
"test/configs/exact_match",
|
130
130
|
"test/configs/host_plus",
|
131
131
|
"test/configs/multihost",
|
132
|
+
"test/configs/negative_match",
|
132
133
|
"test/configs/nohost",
|
133
134
|
"test/configs/numeric_host",
|
134
135
|
"test/configs/send_env",
|
@@ -138,6 +139,7 @@ Gem::Specification.new do |s|
|
|
138
139
|
"test/connection/test_session.rb",
|
139
140
|
"test/known_hosts/github",
|
140
141
|
"test/manual/test_forward.rb",
|
142
|
+
"test/manual/test_pageant.rb",
|
141
143
|
"test/start/test_connection.rb",
|
142
144
|
"test/start/test_options.rb",
|
143
145
|
"test/start/test_transport.rb",
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#
|
2
|
+
# Tests for communication capability with Pageant (or KeeAgent)
|
3
|
+
# process, to include the case where it is running in different UAC
|
4
|
+
# context.
|
5
|
+
#
|
6
|
+
# To run:
|
7
|
+
# - Ensure that Pageant is running (not as administrator).
|
8
|
+
# - Open two command prompts, one as an administrator and one limited
|
9
|
+
# (normal).
|
10
|
+
# - Within each, from the root net/ssh project directory, execute:
|
11
|
+
# ruby -Ilib -Itest -rrubygems test/manual/test_pageant.rb
|
12
|
+
#
|
13
|
+
|
14
|
+
require 'common'
|
15
|
+
require 'net/ssh/authentication/agent'
|
16
|
+
|
17
|
+
module Authentication
|
18
|
+
|
19
|
+
class TestPageant < Test::Unit::TestCase
|
20
|
+
|
21
|
+
def test_agent_should_be_able_to_negotiate
|
22
|
+
assert_nothing_raised(Net::SSH::Authentication::AgentNotAvailable) { agent.negotiate! }
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def agent(auto=:connect)
|
28
|
+
@agent ||= begin
|
29
|
+
agent = Net::SSH::Authentication::Agent.new
|
30
|
+
agent.connect! if auto == :connect
|
31
|
+
agent
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
data/test/test_config.rb
CHANGED
@@ -35,6 +35,12 @@ class TestConfig < Test::Unit::TestCase
|
|
35
35
|
assert !config.key?('rekeylimit')
|
36
36
|
end
|
37
37
|
|
38
|
+
def test_load_with_wild_card_and_negative_pattern_does_not_match
|
39
|
+
config = Net::SSH::Config.load(config(:negative_match), "test.host")
|
40
|
+
assert_equal 9876, config['port']
|
41
|
+
assert !config.key?('compression')
|
42
|
+
end
|
43
|
+
|
38
44
|
def test_for_should_load_all_files_and_translate_to_net_ssh_options
|
39
45
|
config = Net::SSH::Config.for("test.host", [config(:exact_match), config(:wild_cards)])
|
40
46
|
assert_equal 1234, config[:port]
|
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: 2.9.
|
4
|
+
version: 2.9.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -38,7 +38,7 @@ cert_chain:
|
|
38
38
|
bkh3ZjNJSVVQZzdsc01DUnpPa1EvRkQ4N0JJM1YzU25GTm9UQ2RHZ25HajNq
|
39
39
|
Zlc0elJsTAppRnlhcmVGUEE4NGJ0UT09Ci0tLS0tRU5EIENFUlRJRklDQVRF
|
40
40
|
LS0tLS0K
|
41
|
-
date: 2014-
|
41
|
+
date: 2014-05-13 00:00:00.000000000 Z
|
42
42
|
dependencies:
|
43
43
|
- !ruby/object:Gem::Dependency
|
44
44
|
name: test-unit
|
@@ -193,6 +193,7 @@ files:
|
|
193
193
|
- test/configs/exact_match
|
194
194
|
- test/configs/host_plus
|
195
195
|
- test/configs/multihost
|
196
|
+
- test/configs/negative_match
|
196
197
|
- test/configs/nohost
|
197
198
|
- test/configs/numeric_host
|
198
199
|
- test/configs/send_env
|
@@ -202,6 +203,7 @@ files:
|
|
202
203
|
- test/connection/test_session.rb
|
203
204
|
- test/known_hosts/github
|
204
205
|
- test/manual/test_forward.rb
|
206
|
+
- test/manual/test_pageant.rb
|
205
207
|
- test/start/test_connection.rb
|
206
208
|
- test/start/test_options.rb
|
207
209
|
- test/start/test_transport.rb
|
metadata.gz.sig
CHANGED
Binary file
|