net-ssh 6.1.0 → 7.3.0
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/.dockerignore +6 -0
- data/.github/FUNDING.yml +1 -0
- data/.github/config/rubocop_linter_action.yml +4 -0
- data/.github/workflows/ci-with-docker.yml +44 -0
- data/.github/workflows/ci.yml +94 -0
- data/.github/workflows/rubocop.yml +16 -0
- data/.gitignore +4 -0
- data/.rubocop.yml +12 -1
- data/.rubocop_todo.yml +475 -376
- data/CHANGES.txt +64 -3
- data/DEVELOPMENT.md +23 -0
- data/Dockerfile +29 -0
- data/Dockerfile.openssl3 +17 -0
- data/Gemfile +2 -0
- data/Gemfile.noed25519 +2 -0
- data/Gemfile.norbnacl +12 -0
- data/README.md +38 -22
- data/Rakefile +92 -0
- data/SECURITY.md +4 -0
- data/docker-compose.yml +25 -0
- data/lib/net/ssh/authentication/agent.rb +29 -13
- data/lib/net/ssh/authentication/certificate.rb +14 -11
- data/lib/net/ssh/authentication/constants.rb +0 -1
- data/lib/net/ssh/authentication/ed25519.rb +14 -11
- data/lib/net/ssh/authentication/ed25519_loader.rb +4 -7
- data/lib/net/ssh/authentication/key_manager.rb +65 -36
- data/lib/net/ssh/authentication/methods/abstract.rb +12 -3
- data/lib/net/ssh/authentication/methods/hostbased.rb +3 -5
- data/lib/net/ssh/authentication/methods/keyboard_interactive.rb +2 -2
- data/lib/net/ssh/authentication/methods/none.rb +6 -9
- data/lib/net/ssh/authentication/methods/password.rb +2 -3
- data/lib/net/ssh/authentication/methods/publickey.rb +57 -17
- data/lib/net/ssh/authentication/pageant.rb +97 -97
- data/lib/net/ssh/authentication/pub_key_fingerprint.rb +3 -3
- data/lib/net/ssh/authentication/session.rb +25 -17
- data/lib/net/ssh/buffer.rb +71 -51
- data/lib/net/ssh/buffered_io.rb +25 -26
- data/lib/net/ssh/config.rb +33 -20
- data/lib/net/ssh/connection/channel.rb +84 -82
- data/lib/net/ssh/connection/constants.rb +0 -4
- data/lib/net/ssh/connection/event_loop.rb +30 -24
- data/lib/net/ssh/connection/keepalive.rb +12 -12
- data/lib/net/ssh/connection/session.rb +109 -108
- data/lib/net/ssh/connection/term.rb +56 -58
- data/lib/net/ssh/errors.rb +12 -12
- data/lib/net/ssh/key_factory.rb +7 -8
- data/lib/net/ssh/known_hosts.rb +86 -18
- data/lib/net/ssh/loggable.rb +8 -9
- data/lib/net/ssh/packet.rb +1 -1
- data/lib/net/ssh/prompt.rb +9 -11
- data/lib/net/ssh/proxy/command.rb +1 -1
- data/lib/net/ssh/proxy/errors.rb +2 -4
- data/lib/net/ssh/proxy/http.rb +18 -20
- data/lib/net/ssh/proxy/https.rb +8 -10
- data/lib/net/ssh/proxy/jump.rb +8 -10
- data/lib/net/ssh/proxy/socks4.rb +2 -4
- data/lib/net/ssh/proxy/socks5.rb +3 -5
- data/lib/net/ssh/service/forward.rb +7 -7
- data/lib/net/ssh/test/channel.rb +24 -26
- data/lib/net/ssh/test/extensions.rb +35 -35
- data/lib/net/ssh/test/kex.rb +6 -8
- data/lib/net/ssh/test/local_packet.rb +0 -2
- data/lib/net/ssh/test/packet.rb +3 -3
- data/lib/net/ssh/test/remote_packet.rb +6 -8
- data/lib/net/ssh/test/script.rb +25 -27
- data/lib/net/ssh/test/socket.rb +12 -15
- data/lib/net/ssh/test.rb +4 -5
- data/lib/net/ssh/transport/aes128_gcm.rb +40 -0
- data/lib/net/ssh/transport/aes256_gcm.rb +40 -0
- data/lib/net/ssh/transport/algorithms.rb +51 -19
- data/lib/net/ssh/transport/chacha20_poly1305_cipher.rb +117 -0
- data/lib/net/ssh/transport/chacha20_poly1305_cipher_loader.rb +17 -0
- data/lib/net/ssh/transport/cipher_factory.rb +56 -29
- data/lib/net/ssh/transport/constants.rb +3 -3
- data/lib/net/ssh/transport/ctr.rb +7 -7
- data/lib/net/ssh/transport/gcm_cipher.rb +207 -0
- data/lib/net/ssh/transport/hmac/abstract.rb +20 -5
- data/lib/net/ssh/transport/hmac/md5.rb +0 -2
- data/lib/net/ssh/transport/hmac/md5_96.rb +0 -2
- data/lib/net/ssh/transport/hmac/none.rb +0 -2
- data/lib/net/ssh/transport/hmac/ripemd160.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1.rb +0 -2
- data/lib/net/ssh/transport/hmac/sha1_96.rb +0 -2
- data/lib/net/ssh/transport/hmac.rb +12 -12
- data/lib/net/ssh/transport/identity_cipher.rb +19 -13
- data/lib/net/ssh/transport/kex/abstract.rb +12 -5
- data/lib/net/ssh/transport/kex/abstract5656.rb +1 -1
- data/lib/net/ssh/transport/kex/curve25519_sha256.rb +2 -1
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha1.rb +4 -4
- data/lib/net/ssh/transport/kex/diffie_hellman_group14_sha256.rb +11 -0
- data/lib/net/ssh/transport/kex/diffie_hellman_group1_sha1.rb +21 -21
- data/lib/net/ssh/transport/kex/diffie_hellman_group_exchange_sha1.rb +1 -2
- data/lib/net/ssh/transport/kex/ecdh_sha2_nistp256.rb +2 -2
- data/lib/net/ssh/transport/kex.rb +8 -6
- data/lib/net/ssh/transport/key_expander.rb +7 -8
- data/lib/net/ssh/transport/openssl.rb +51 -26
- data/lib/net/ssh/transport/openssl_cipher_extensions.rb +8 -0
- data/lib/net/ssh/transport/packet_stream.rb +46 -26
- data/lib/net/ssh/transport/server_version.rb +17 -16
- data/lib/net/ssh/transport/session.rb +9 -7
- data/lib/net/ssh/transport/state.rb +44 -44
- data/lib/net/ssh/verifiers/accept_new.rb +0 -2
- data/lib/net/ssh/verifiers/accept_new_or_local_tunnel.rb +1 -2
- data/lib/net/ssh/verifiers/always.rb +6 -4
- data/lib/net/ssh/verifiers/never.rb +0 -2
- data/lib/net/ssh/version.rb +2 -2
- data/lib/net/ssh.rb +15 -8
- data/net-ssh-public_cert.pem +19 -18
- data/net-ssh.gemspec +7 -4
- data/support/ssh_tunnel_bug.rb +3 -3
- data.tar.gz.sig +0 -0
- metadata +76 -29
- metadata.gz.sig +0 -0
- data/.travis.yml +0 -52
|
@@ -21,10 +21,9 @@ end
|
|
|
21
21
|
|
|
22
22
|
require 'net/ssh/errors'
|
|
23
23
|
|
|
24
|
-
module Net
|
|
25
|
-
module SSH
|
|
24
|
+
module Net
|
|
25
|
+
module SSH
|
|
26
26
|
module Authentication
|
|
27
|
-
|
|
28
27
|
# This module encapsulates the implementation of a socket factory that
|
|
29
28
|
# uses the PuTTY "pageant" utility to obtain information about SSH
|
|
30
29
|
# identities.
|
|
@@ -33,36 +32,35 @@ module Net
|
|
|
33
32
|
# by Guillaume Marçais (guillaume.marcais@free.fr). It is used and
|
|
34
33
|
# relicensed by permission.
|
|
35
34
|
module Pageant
|
|
36
|
-
|
|
37
35
|
# From Putty pageant.c
|
|
38
36
|
AGENT_MAX_MSGLEN = 8192
|
|
39
37
|
AGENT_COPYDATA_ID = 0x804e50ba
|
|
40
|
-
|
|
38
|
+
|
|
41
39
|
# The definition of the Windows methods and data structures used in
|
|
42
40
|
# communicating with the pageant process.
|
|
43
41
|
module Win # rubocop:disable Metrics/ModuleLength
|
|
44
42
|
# Compatibility on initialization
|
|
45
43
|
if RUBY_VERSION < "1.9"
|
|
46
44
|
extend DL::Importable
|
|
47
|
-
|
|
45
|
+
|
|
48
46
|
dlload 'user32.dll'
|
|
49
47
|
dlload 'kernel32.dll'
|
|
50
48
|
dlload 'advapi32.dll'
|
|
51
|
-
|
|
49
|
+
|
|
52
50
|
SIZEOF_DWORD = DL.sizeof('L')
|
|
53
51
|
elsif RUBY_VERSION < "2.1"
|
|
54
52
|
extend DL::Importer
|
|
55
|
-
dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
|
|
53
|
+
dlload 'user32.dll', 'kernel32.dll', 'advapi32.dll'
|
|
56
54
|
include DL::Win32Types
|
|
57
|
-
|
|
55
|
+
|
|
58
56
|
SIZEOF_DWORD = DL::SIZEOF_LONG
|
|
59
57
|
else
|
|
60
58
|
extend Fiddle::Importer
|
|
61
|
-
dlload 'user32.dll','kernel32.dll', 'advapi32.dll'
|
|
59
|
+
dlload 'user32.dll', 'kernel32.dll', 'advapi32.dll'
|
|
62
60
|
include Fiddle::Win32Types
|
|
63
61
|
SIZEOF_DWORD = Fiddle::SIZEOF_LONG
|
|
64
62
|
end
|
|
65
|
-
|
|
63
|
+
|
|
66
64
|
if RUBY_ENGINE == "jruby"
|
|
67
65
|
typealias("HANDLE", "void *") # From winnt.h
|
|
68
66
|
typealias("PHANDLE", "void *") # From winnt.h
|
|
@@ -76,105 +74,105 @@ module Net
|
|
|
76
74
|
typealias("LPARAM", "long *") # From windef.h
|
|
77
75
|
typealias("PDWORD_PTR", "long *") # From basetsd.h
|
|
78
76
|
typealias("USHORT", "unsigned short") # From windef.h
|
|
79
|
-
|
|
77
|
+
|
|
80
78
|
# From winbase.h, winnt.h
|
|
81
79
|
INVALID_HANDLE_VALUE = -1
|
|
82
80
|
NULL = nil
|
|
83
81
|
PAGE_READWRITE = 0x0004
|
|
84
82
|
FILE_MAP_WRITE = 2
|
|
85
83
|
WM_COPYDATA = 74
|
|
86
|
-
|
|
84
|
+
|
|
87
85
|
SMTO_NORMAL = 0 # From winuser.h
|
|
88
|
-
|
|
86
|
+
|
|
89
87
|
SUFFIX = if RUBY_ENGINE == "jruby"
|
|
90
88
|
"A"
|
|
91
89
|
else
|
|
92
90
|
""
|
|
93
91
|
end
|
|
94
|
-
|
|
92
|
+
|
|
95
93
|
# args: lpClassName, lpWindowName
|
|
96
94
|
extern "HWND FindWindow#{SUFFIX}(LPCTSTR, LPCTSTR)"
|
|
97
|
-
|
|
95
|
+
|
|
98
96
|
# args: none
|
|
99
97
|
extern 'DWORD GetCurrentThreadId()'
|
|
100
|
-
|
|
98
|
+
|
|
101
99
|
# args: hFile, (ignored), flProtect, dwMaximumSizeHigh,
|
|
102
100
|
# dwMaximumSizeLow, lpName
|
|
103
101
|
extern "HANDLE CreateFileMapping#{SUFFIX}(HANDLE, void *, DWORD, " +
|
|
104
102
|
"DWORD, DWORD, LPCTSTR)"
|
|
105
|
-
|
|
103
|
+
|
|
106
104
|
# args: hFileMappingObject, dwDesiredAccess, dwFileOffsetHigh,
|
|
107
105
|
# dwfileOffsetLow, dwNumberOfBytesToMap
|
|
108
106
|
extern 'LPVOID MapViewOfFile(HANDLE, DWORD, DWORD, DWORD, DWORD)'
|
|
109
|
-
|
|
107
|
+
|
|
110
108
|
# args: lpBaseAddress
|
|
111
109
|
extern 'BOOL UnmapViewOfFile(LPCVOID)'
|
|
112
|
-
|
|
110
|
+
|
|
113
111
|
# args: hObject
|
|
114
112
|
extern 'BOOL CloseHandle(HANDLE)'
|
|
115
|
-
|
|
113
|
+
|
|
116
114
|
# args: hWnd, Msg, wParam, lParam, fuFlags, uTimeout, lpdwResult
|
|
117
115
|
extern "LRESULT SendMessageTimeout#{SUFFIX}(HWND, UINT, WPARAM, LPARAM, " +
|
|
118
116
|
"UINT, UINT, PDWORD_PTR)"
|
|
119
|
-
|
|
117
|
+
|
|
120
118
|
# args: none
|
|
121
119
|
extern 'DWORD GetLastError()'
|
|
122
|
-
|
|
120
|
+
|
|
123
121
|
# args: none
|
|
124
122
|
extern 'HANDLE GetCurrentProcess()'
|
|
125
|
-
|
|
123
|
+
|
|
126
124
|
# args: hProcessHandle, dwDesiredAccess, (out) phNewTokenHandle
|
|
127
125
|
extern 'BOOL OpenProcessToken(HANDLE, DWORD, PHANDLE)'
|
|
128
|
-
|
|
126
|
+
|
|
129
127
|
# args: hTokenHandle, uTokenInformationClass,
|
|
130
128
|
# (out) lpTokenInformation, dwTokenInformationLength
|
|
131
129
|
# (out) pdwInfoReturnLength
|
|
132
130
|
extern 'BOOL GetTokenInformation(HANDLE, UINT, LPVOID, DWORD, ' +
|
|
133
131
|
'PDWORD)'
|
|
134
|
-
|
|
132
|
+
|
|
135
133
|
# args: (out) lpSecurityDescriptor, dwRevisionLevel
|
|
136
134
|
extern 'BOOL InitializeSecurityDescriptor(LPVOID, DWORD)'
|
|
137
|
-
|
|
135
|
+
|
|
138
136
|
# args: (out) lpSecurityDescriptor, lpOwnerSid, bOwnerDefaulted
|
|
139
137
|
extern 'BOOL SetSecurityDescriptorOwner(LPVOID, LPVOID, BOOL)'
|
|
140
|
-
|
|
138
|
+
|
|
141
139
|
# args: pSecurityDescriptor
|
|
142
140
|
extern 'BOOL IsValidSecurityDescriptor(LPVOID)'
|
|
143
|
-
|
|
141
|
+
|
|
144
142
|
# Constants needed for security attribute retrieval.
|
|
145
143
|
# Specifies the access mask corresponding to the desired access
|
|
146
144
|
# rights.
|
|
147
145
|
TOKEN_QUERY = 0x8
|
|
148
|
-
|
|
146
|
+
|
|
149
147
|
# The value of TOKEN_USER from the TOKEN_INFORMATION_CLASS enum.
|
|
150
148
|
TOKEN_USER_INFORMATION_CLASS = 1
|
|
151
|
-
|
|
149
|
+
|
|
152
150
|
# The initial revision level assigned to the security descriptor.
|
|
153
151
|
REVISION = 1
|
|
154
|
-
|
|
152
|
+
|
|
155
153
|
# Structs for security attribute functions.
|
|
156
154
|
# Holds the retrieved user access token.
|
|
157
155
|
TOKEN_USER = struct ['void * SID', 'DWORD ATTRIBUTES']
|
|
158
|
-
|
|
156
|
+
|
|
159
157
|
# Contains the security descriptor, this gets passed to the
|
|
160
158
|
# function that constructs the shared memory map.
|
|
161
159
|
SECURITY_ATTRIBUTES = struct ['DWORD nLength',
|
|
162
160
|
'LPVOID lpSecurityDescriptor',
|
|
163
161
|
'BOOL bInheritHandle']
|
|
164
|
-
|
|
162
|
+
|
|
165
163
|
# The security descriptor holds security information.
|
|
166
164
|
SECURITY_DESCRIPTOR = struct ['UCHAR Revision', 'UCHAR Sbz1',
|
|
167
165
|
'USHORT Control', 'LPVOID Owner',
|
|
168
166
|
'LPVOID Group', 'LPVOID Sacl',
|
|
169
167
|
'LPVOID Dacl']
|
|
170
|
-
|
|
168
|
+
|
|
171
169
|
# The COPYDATASTRUCT is used to send WM_COPYDATA messages
|
|
172
170
|
COPYDATASTRUCT = if RUBY_ENGINE == "jruby"
|
|
173
171
|
struct ['ULONG_PTR dwData', 'DWORD cbData', 'LPVOID lpData']
|
|
174
172
|
else
|
|
175
173
|
struct ['uintptr_t dwData', 'DWORD cbData', 'LPVOID lpData']
|
|
176
174
|
end
|
|
177
|
-
|
|
175
|
+
|
|
178
176
|
# Compatibility for security attribute retrieval.
|
|
179
177
|
if RUBY_VERSION < "1.9"
|
|
180
178
|
# Alias functions to > 1.9 capitalization
|
|
@@ -196,15 +194,15 @@ module Net
|
|
|
196
194
|
alias_method new_name, name
|
|
197
195
|
module_function new_name
|
|
198
196
|
end
|
|
199
|
-
|
|
197
|
+
|
|
200
198
|
def self.malloc_ptr(size)
|
|
201
199
|
return DL.malloc(size)
|
|
202
200
|
end
|
|
203
|
-
|
|
201
|
+
|
|
204
202
|
def self.get_ptr(data)
|
|
205
203
|
return data.to_ptr
|
|
206
204
|
end
|
|
207
|
-
|
|
205
|
+
|
|
208
206
|
def self.set_ptr_data(ptr, data)
|
|
209
207
|
ptr[0] = data
|
|
210
208
|
end
|
|
@@ -220,15 +218,15 @@ module Net
|
|
|
220
218
|
attach_function :malloc, [:size_t], :pointer
|
|
221
219
|
attach_function :free, [:pointer], :void
|
|
222
220
|
end
|
|
223
|
-
|
|
221
|
+
|
|
224
222
|
def self.malloc_ptr(size)
|
|
225
223
|
Fiddle::Pointer.new(LibC.malloc(size), size, LibC.method(:free))
|
|
226
224
|
end
|
|
227
|
-
|
|
225
|
+
|
|
228
226
|
def self.get_ptr(ptr)
|
|
229
227
|
return data.address
|
|
230
228
|
end
|
|
231
|
-
|
|
229
|
+
|
|
232
230
|
def self.set_ptr_data(ptr, data)
|
|
233
231
|
ptr.write_string_length(data, data.size)
|
|
234
232
|
end
|
|
@@ -236,19 +234,19 @@ module Net
|
|
|
236
234
|
def self.malloc_ptr(size)
|
|
237
235
|
return DL::CPtr.malloc(size, DL::RUBY_FREE)
|
|
238
236
|
end
|
|
239
|
-
|
|
237
|
+
|
|
240
238
|
def self.get_ptr(data)
|
|
241
239
|
return DL::CPtr.to_ptr data
|
|
242
240
|
end
|
|
243
|
-
|
|
241
|
+
|
|
244
242
|
def self.set_ptr_data(ptr, data)
|
|
245
|
-
DL::CPtr.new(ptr)[0,data.size] = data
|
|
243
|
+
DL::CPtr.new(ptr)[0, data.size] = data
|
|
246
244
|
end
|
|
247
245
|
end
|
|
248
|
-
|
|
246
|
+
|
|
249
247
|
def self.get_security_attributes_for_user
|
|
250
248
|
user = get_current_user
|
|
251
|
-
|
|
249
|
+
|
|
252
250
|
psd_information = malloc_ptr(Win::SECURITY_DESCRIPTOR.size)
|
|
253
251
|
raise_error_if_zero(
|
|
254
252
|
Win.InitializeSecurityDescriptor(psd_information,
|
|
@@ -261,45 +259,46 @@ module Net
|
|
|
261
259
|
raise_error_if_zero(
|
|
262
260
|
Win.IsValidSecurityDescriptor(psd_information)
|
|
263
261
|
)
|
|
264
|
-
|
|
262
|
+
|
|
265
263
|
sa = Win::SECURITY_ATTRIBUTES.new(to_struct_ptr(malloc_ptr(Win::SECURITY_ATTRIBUTES.size)))
|
|
266
264
|
sa.nLength = Win::SECURITY_ATTRIBUTES.size
|
|
267
265
|
sa.lpSecurityDescriptor = psd_information.to_i
|
|
268
266
|
sa.bInheritHandle = 1
|
|
269
|
-
|
|
267
|
+
|
|
270
268
|
return sa
|
|
271
269
|
end
|
|
272
|
-
|
|
270
|
+
|
|
273
271
|
if RUBY_ENGINE == "jruby"
|
|
274
272
|
def self.ptr_to_s(ptr, size)
|
|
275
273
|
ret = ptr.to_s(size)
|
|
276
274
|
ret << "\x00" while ret.size < size
|
|
277
275
|
ret
|
|
278
276
|
end
|
|
279
|
-
|
|
277
|
+
|
|
280
278
|
def self.ptr_to_handle(phandle)
|
|
281
279
|
phandle.ptr
|
|
282
280
|
end
|
|
283
|
-
|
|
281
|
+
|
|
284
282
|
def self.ptr_to_dword(ptr)
|
|
285
283
|
first = ptr.ptr.to_i
|
|
286
|
-
second = ptr_to_s(ptr,Win::SIZEOF_DWORD).unpack('L')[0]
|
|
284
|
+
second = ptr_to_s(ptr, Win::SIZEOF_DWORD).unpack('L')[0]
|
|
287
285
|
raise "Error" unless first == second
|
|
286
|
+
|
|
288
287
|
first
|
|
289
288
|
end
|
|
290
|
-
|
|
289
|
+
|
|
291
290
|
def self.to_token_user(ptoken_information)
|
|
292
291
|
TOKEN_USER.new(ptoken_information.to_ptr)
|
|
293
292
|
end
|
|
294
|
-
|
|
293
|
+
|
|
295
294
|
def self.to_struct_ptr(ptr)
|
|
296
295
|
ptr.to_ptr
|
|
297
296
|
end
|
|
298
|
-
|
|
297
|
+
|
|
299
298
|
def self.get_sid(user)
|
|
300
|
-
ptr_to_s(user.to_ptr.ptr,Win::SIZEOF_DWORD).unpack('L')[0]
|
|
299
|
+
ptr_to_s(user.to_ptr.ptr, Win::SIZEOF_DWORD).unpack('L')[0]
|
|
301
300
|
end
|
|
302
|
-
|
|
301
|
+
|
|
303
302
|
def self.get_sid_ptr(user)
|
|
304
303
|
user.to_ptr.ptr
|
|
305
304
|
end
|
|
@@ -307,39 +306,39 @@ module Net
|
|
|
307
306
|
def self.get_sid(user)
|
|
308
307
|
user.SID
|
|
309
308
|
end
|
|
310
|
-
|
|
309
|
+
|
|
311
310
|
def self.ptr_to_handle(phandle)
|
|
312
311
|
phandle.ptr.to_i
|
|
313
312
|
end
|
|
314
|
-
|
|
313
|
+
|
|
315
314
|
def self.to_struct_ptr(ptr)
|
|
316
315
|
ptr
|
|
317
316
|
end
|
|
318
|
-
|
|
317
|
+
|
|
319
318
|
def self.ptr_to_dword(ptr)
|
|
320
319
|
ptr.to_s(Win::SIZEOF_DWORD).unpack('L')[0]
|
|
321
320
|
end
|
|
322
|
-
|
|
321
|
+
|
|
323
322
|
def self.to_token_user(ptoken_information)
|
|
324
323
|
TOKEN_USER.new(ptoken_information)
|
|
325
324
|
end
|
|
326
|
-
|
|
325
|
+
|
|
327
326
|
def self.get_sid_ptr(user)
|
|
328
327
|
user.SID
|
|
329
328
|
end
|
|
330
329
|
end
|
|
331
|
-
|
|
330
|
+
|
|
332
331
|
def self.get_current_user
|
|
333
332
|
token_handle = open_process_token(Win.GetCurrentProcess,
|
|
334
333
|
Win::TOKEN_QUERY)
|
|
335
334
|
token_user = get_token_information(token_handle,
|
|
336
|
-
|
|
335
|
+
Win::TOKEN_USER_INFORMATION_CLASS)
|
|
337
336
|
return token_user
|
|
338
337
|
end
|
|
339
|
-
|
|
338
|
+
|
|
340
339
|
def self.open_process_token(process_handle, desired_access)
|
|
341
340
|
ptoken_handle = malloc_ptr(Win::SIZEOF_DWORD)
|
|
342
|
-
|
|
341
|
+
|
|
343
342
|
raise_error_if_zero(
|
|
344
343
|
Win.OpenProcessToken(process_handle, desired_access,
|
|
345
344
|
ptoken_handle)
|
|
@@ -347,12 +346,12 @@ module Net
|
|
|
347
346
|
token_handle = ptr_to_handle(ptoken_handle)
|
|
348
347
|
return token_handle
|
|
349
348
|
end
|
|
350
|
-
|
|
349
|
+
|
|
351
350
|
def self.get_token_information(token_handle,
|
|
352
351
|
token_information_class)
|
|
353
352
|
# Hold the size of the information to be returned
|
|
354
353
|
preturn_length = malloc_ptr(Win::SIZEOF_DWORD)
|
|
355
|
-
|
|
354
|
+
|
|
356
355
|
# Going to throw an INSUFFICIENT_BUFFER_ERROR, but that is ok
|
|
357
356
|
# here. This is retrieving the size of the information to be
|
|
358
357
|
# returned.
|
|
@@ -360,7 +359,7 @@ module Net
|
|
|
360
359
|
token_information_class,
|
|
361
360
|
Win::NULL, 0, preturn_length)
|
|
362
361
|
ptoken_information = malloc_ptr(ptr_to_dword(preturn_length))
|
|
363
|
-
|
|
362
|
+
|
|
364
363
|
# This call is going to write the requested information to
|
|
365
364
|
# the memory location referenced by token_information.
|
|
366
365
|
raise_error_if_zero(
|
|
@@ -370,74 +369,76 @@ module Net
|
|
|
370
369
|
ptoken_information.size,
|
|
371
370
|
preturn_length)
|
|
372
371
|
)
|
|
373
|
-
|
|
372
|
+
|
|
374
373
|
return to_token_user(ptoken_information)
|
|
375
374
|
end
|
|
376
|
-
|
|
375
|
+
|
|
377
376
|
def self.raise_error_if_zero(result)
|
|
378
377
|
if result == 0
|
|
379
378
|
raise "Windows error: #{Win.GetLastError}"
|
|
380
379
|
end
|
|
381
380
|
end
|
|
382
|
-
|
|
381
|
+
|
|
383
382
|
# Get a null-terminated string given a string.
|
|
384
383
|
def self.get_cstr(str)
|
|
385
384
|
return str + "\000"
|
|
386
385
|
end
|
|
387
386
|
end
|
|
388
|
-
|
|
387
|
+
|
|
389
388
|
# This is the pseudo-socket implementation that mimics the interface of
|
|
390
389
|
# a socket, translating each request into a Windows messaging call to
|
|
391
390
|
# the pageant daemon. This allows pageant support to be implemented
|
|
392
391
|
# simply by replacing the socket factory used by the Agent class.
|
|
393
392
|
class Socket
|
|
394
393
|
private_class_method :new
|
|
395
|
-
|
|
394
|
+
|
|
396
395
|
# The factory method for creating a new Socket instance.
|
|
397
396
|
def self.open
|
|
398
397
|
new
|
|
399
398
|
end
|
|
400
|
-
|
|
399
|
+
|
|
401
400
|
# Create a new instance that communicates with the running pageant
|
|
402
401
|
# instance. If no such instance is running, this will cause an error.
|
|
403
402
|
def initialize
|
|
404
403
|
@win = Win.FindWindow("Pageant", "Pageant")
|
|
405
|
-
|
|
404
|
+
|
|
406
405
|
if @win.to_i == 0
|
|
407
406
|
raise Net::SSH::Exception,
|
|
408
|
-
|
|
407
|
+
"pageant process not running"
|
|
409
408
|
end
|
|
410
|
-
|
|
409
|
+
|
|
411
410
|
@input_buffer = Net::SSH::Buffer.new
|
|
412
411
|
@output_buffer = Net::SSH::Buffer.new
|
|
413
412
|
end
|
|
414
|
-
|
|
413
|
+
|
|
415
414
|
# Forwards the data to #send_query, ignoring any arguments after
|
|
416
415
|
# the first.
|
|
417
416
|
def send(data, *args)
|
|
418
417
|
@input_buffer.append(data)
|
|
419
|
-
|
|
418
|
+
|
|
420
419
|
ret = data.length
|
|
421
|
-
|
|
420
|
+
|
|
422
421
|
while true
|
|
423
422
|
return ret if @input_buffer.length < 4
|
|
423
|
+
|
|
424
424
|
msg_length = @input_buffer.read_long + 4
|
|
425
425
|
@input_buffer.reset!
|
|
426
|
-
|
|
426
|
+
|
|
427
427
|
return ret if @input_buffer.length < msg_length
|
|
428
|
+
|
|
428
429
|
msg = @input_buffer.read!(msg_length)
|
|
429
430
|
@output_buffer.append(send_query(msg))
|
|
430
431
|
end
|
|
431
432
|
end
|
|
432
|
-
|
|
433
|
+
|
|
433
434
|
# Reads +n+ bytes from the cached result of the last query. If +n+
|
|
434
435
|
# is +nil+, returns all remaining data from the last query.
|
|
435
436
|
def read(n = nil)
|
|
436
437
|
@output_buffer.read(n)
|
|
437
438
|
end
|
|
438
|
-
|
|
439
|
+
|
|
439
440
|
def close; end
|
|
440
|
-
|
|
441
|
+
|
|
441
442
|
# Packages the given query string and sends it to the pageant
|
|
442
443
|
# process via the Windows messaging subsystem. The result is
|
|
443
444
|
# cached, to be returned piece-wise when #read is called.
|
|
@@ -446,29 +447,29 @@ module Net
|
|
|
446
447
|
filemap = 0
|
|
447
448
|
ptr = nil
|
|
448
449
|
id = Win.malloc_ptr(Win::SIZEOF_DWORD)
|
|
449
|
-
|
|
450
|
+
|
|
450
451
|
mapname = "PageantRequest%08x" % Win.GetCurrentThreadId()
|
|
451
452
|
security_attributes = Win.get_ptr Win.get_security_attributes_for_user
|
|
452
|
-
|
|
453
|
+
|
|
453
454
|
filemap = Win.CreateFileMapping(Win::INVALID_HANDLE_VALUE,
|
|
454
455
|
security_attributes,
|
|
455
456
|
Win::PAGE_READWRITE, 0,
|
|
456
457
|
AGENT_MAX_MSGLEN, mapname)
|
|
457
|
-
|
|
458
|
+
|
|
458
459
|
if filemap == 0 || filemap == Win::INVALID_HANDLE_VALUE
|
|
459
460
|
raise Net::SSH::Exception,
|
|
460
|
-
|
|
461
|
+
"Creation of file mapping failed with error: #{Win.GetLastError}"
|
|
461
462
|
end
|
|
462
|
-
|
|
463
|
+
|
|
463
464
|
ptr = Win.MapViewOfFile(filemap, Win::FILE_MAP_WRITE, 0, 0,
|
|
464
465
|
0)
|
|
465
|
-
|
|
466
|
+
|
|
466
467
|
if ptr.nil? || ptr.null?
|
|
467
468
|
raise Net::SSH::Exception, "Mapping of file failed"
|
|
468
469
|
end
|
|
469
|
-
|
|
470
|
+
|
|
470
471
|
Win.set_ptr_data(ptr, query)
|
|
471
|
-
|
|
472
|
+
|
|
472
473
|
# using struct to achieve proper alignment and field size on 64-bit platform
|
|
473
474
|
cds = Win::COPYDATASTRUCT.new(Win.malloc_ptr(Win::COPYDATASTRUCT.size))
|
|
474
475
|
cds.dwData = AGENT_COPYDATA_ID
|
|
@@ -476,14 +477,14 @@ module Net
|
|
|
476
477
|
cds.lpData = Win.get_cstr(mapname)
|
|
477
478
|
succ = Win.SendMessageTimeout(@win, Win::WM_COPYDATA, Win::NULL,
|
|
478
479
|
cds.to_ptr, Win::SMTO_NORMAL, 5000, id)
|
|
479
|
-
|
|
480
|
+
|
|
480
481
|
if succ > 0
|
|
481
482
|
retlen = 4 + ptr.to_s(4).unpack("N")[0]
|
|
482
483
|
res = ptr.to_s(retlen)
|
|
483
484
|
else
|
|
484
485
|
raise Net::SSH::Exception, "Message failed with error: #{Win.GetLastError}"
|
|
485
486
|
end
|
|
486
|
-
|
|
487
|
+
|
|
487
488
|
return res
|
|
488
489
|
ensure
|
|
489
490
|
Win.UnmapViewOfFile(ptr) unless ptr.nil? || ptr.null?
|
|
@@ -491,7 +492,6 @@ module Net
|
|
|
491
492
|
end
|
|
492
493
|
end
|
|
493
494
|
end
|
|
494
|
-
|
|
495
495
|
end
|
|
496
496
|
end
|
|
497
497
|
end
|
|
@@ -22,17 +22,17 @@ module Net
|
|
|
22
22
|
# returned by OpenSSH's <tt>`ssh-add -l -E SHA256`</tt>, i.e.,
|
|
23
23
|
# trailing base64 padding '=' characters are stripped and the
|
|
24
24
|
# literal string +SHA256:+ is prepended.
|
|
25
|
-
def fingerprint(algorithm='MD5')
|
|
25
|
+
def fingerprint(algorithm = 'MD5')
|
|
26
26
|
@fingerprint ||= {}
|
|
27
27
|
@fingerprint[algorithm] ||= PubKeyFingerprint.fingerprint(to_blob, algorithm)
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def self.fingerprint(blob, algorithm='MD5')
|
|
30
|
+
def self.fingerprint(blob, algorithm = 'MD5')
|
|
31
31
|
case algorithm.to_s.upcase
|
|
32
32
|
when 'MD5'
|
|
33
33
|
OpenSSL::Digest.hexdigest(algorithm, blob).scan(/../).join(":")
|
|
34
34
|
when 'SHA256'
|
|
35
|
-
"SHA256:#{
|
|
35
|
+
"SHA256:#{[OpenSSL::Digest.digest(algorithm, blob)].pack('m').chomp.gsub(/=+\z/, '')}"
|
|
36
36
|
else
|
|
37
37
|
raise OpenSSL::Digest::DigestError, "unsupported ssh key digest #{algorithm}"
|
|
38
38
|
end
|
|
@@ -11,7 +11,6 @@ require 'net/ssh/authentication/methods/keyboard_interactive'
|
|
|
11
11
|
module Net
|
|
12
12
|
module SSH
|
|
13
13
|
module Authentication
|
|
14
|
-
|
|
15
14
|
# Raised if the current authentication method is not allowed
|
|
16
15
|
class DisallowedMethod < Net::SSH::Exception
|
|
17
16
|
end
|
|
@@ -42,7 +41,7 @@ module Net
|
|
|
42
41
|
|
|
43
42
|
# Instantiates a new Authentication::Session object over the given
|
|
44
43
|
# transport layer abstraction.
|
|
45
|
-
def initialize(transport, options={})
|
|
44
|
+
def initialize(transport, options = {})
|
|
46
45
|
self.logger = transport.logger
|
|
47
46
|
@transport = transport
|
|
48
47
|
|
|
@@ -55,7 +54,7 @@ module Net
|
|
|
55
54
|
# Attempts to authenticate the given user, in preparation for the next
|
|
56
55
|
# service request. Returns true if an authentication method succeeds in
|
|
57
56
|
# authenticating the user, and false otherwise.
|
|
58
|
-
def authenticate(next_service, username, password=nil)
|
|
57
|
+
def authenticate(next_service, username, password = nil)
|
|
59
58
|
debug { "beginning authentication of `#{username}'" }
|
|
60
59
|
|
|
61
60
|
transport.send_message(transport.service_request("ssh-userauth"))
|
|
@@ -64,28 +63,30 @@ module Net
|
|
|
64
63
|
key_manager = KeyManager.new(logger, options)
|
|
65
64
|
keys.each { |key| key_manager.add(key) } unless keys.empty?
|
|
66
65
|
keycerts.each { |keycert| key_manager.add_keycert(keycert) } unless keycerts.empty?
|
|
66
|
+
keycert_data.each { |data| key_manager.add_keycert_data(data) } unless keycert_data.empty?
|
|
67
67
|
key_data.each { |key2| key_manager.add_key_data(key2) } unless key_data.empty?
|
|
68
68
|
default_keys.each { |key| key_manager.add(key) } unless options.key?(:keys) || options.key?(:key_data)
|
|
69
69
|
|
|
70
70
|
attempted = []
|
|
71
71
|
|
|
72
72
|
@auth_methods.each do |name|
|
|
73
|
+
next unless @allowed_auth_methods.include?(name)
|
|
74
|
+
|
|
75
|
+
attempted << name
|
|
76
|
+
|
|
77
|
+
debug { "trying #{name}" }
|
|
73
78
|
begin
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
rescue NameError
|
|
82
|
-
debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
|
|
83
|
-
next
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
return true if method.authenticate(next_service, username, password)
|
|
87
|
-
rescue Net::SSH::Authentication::DisallowedMethod
|
|
79
|
+
auth_class = Methods.const_get(name.split(/\W+/).map { |p| p.capitalize }.join)
|
|
80
|
+
method = auth_class.new(self,
|
|
81
|
+
key_manager: key_manager, password_prompt: options[:password_prompt],
|
|
82
|
+
pubkey_algorithms: options[:pubkey_algorithms] || nil)
|
|
83
|
+
rescue NameError
|
|
84
|
+
debug {"Mechanism #{name} was requested, but isn't a known type. Ignoring it."}
|
|
85
|
+
next
|
|
88
86
|
end
|
|
87
|
+
|
|
88
|
+
return true if method.authenticate(next_service, username, password)
|
|
89
|
+
rescue Net::SSH::Authentication::DisallowedMethod
|
|
89
90
|
end
|
|
90
91
|
|
|
91
92
|
error { "all authorization methods failed (tried #{attempted.join(', ')})" }
|
|
@@ -129,6 +130,7 @@ module Net
|
|
|
129
130
|
def expect_message(type)
|
|
130
131
|
message = next_message
|
|
131
132
|
raise Net::SSH::Exception, "expected #{type}, got #{message.type} (#{message})" unless message.type == type
|
|
133
|
+
|
|
132
134
|
message
|
|
133
135
|
end
|
|
134
136
|
|
|
@@ -153,6 +155,12 @@ module Net
|
|
|
153
155
|
Array(options[:keycerts])
|
|
154
156
|
end
|
|
155
157
|
|
|
158
|
+
# Returns an array of the keycert data that should be used when
|
|
159
|
+
# attempting any key-based authentication mechanism.
|
|
160
|
+
def keycert_data
|
|
161
|
+
Array(options[:keycert_data])
|
|
162
|
+
end
|
|
163
|
+
|
|
156
164
|
# Returns an array of the key data that should be used when
|
|
157
165
|
# attempting any key-based authentication mechanism.
|
|
158
166
|
def key_data
|