@electerm/ssh2 1.11.2 → 1.16.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.
- package/lib/client.js +141 -14
- package/lib/index.js +1 -0
- package/lib/keygen.js +582 -0
- package/lib/protocol/Protocol.js +72 -13
- package/lib/protocol/SFTP.js +190 -2
- package/lib/protocol/constants.js +5 -2
- package/lib/protocol/crypto/build/Makefile +347 -0
- package/lib/protocol/crypto/build/Release/.deps/Release/obj.target/sshcrypto/src/binding.o.d +247 -0
- package/lib/protocol/crypto/build/Release/.deps/Release/sshcrypto.node.d +1 -0
- package/lib/protocol/crypto/build/Release/obj.target/sshcrypto/src/binding.o +0 -0
- package/lib/protocol/crypto/build/Release/sshcrypto.node +0 -0
- package/lib/protocol/crypto/build/binding.Makefile +6 -0
- package/lib/protocol/crypto/build/gyp-mac-tool +772 -0
- package/lib/protocol/crypto/build/sshcrypto.target.mk +194 -0
- package/lib/protocol/handlers.misc.js +76 -5
- package/lib/protocol/kex.js +100 -23
- package/lib/protocol/keyParser.js +7 -6
- package/lib/protocol/zlib.js +6 -2
- package/lib/server.js +14 -1
- package/package.json +7 -4
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
# This file is generated by gyp; do not edit.
|
|
2
|
+
|
|
3
|
+
TOOLSET := target
|
|
4
|
+
TARGET := sshcrypto
|
|
5
|
+
DEFS_Debug := \
|
|
6
|
+
'-DNODE_GYP_MODULE_NAME=sshcrypto' \
|
|
7
|
+
'-DUSING_UV_SHARED=1' \
|
|
8
|
+
'-DUSING_V8_SHARED=1' \
|
|
9
|
+
'-DV8_DEPRECATION_WARNINGS=1' \
|
|
10
|
+
'-DV8_DEPRECATION_WARNINGS' \
|
|
11
|
+
'-DV8_IMMINENT_DEPRECATION_WARNINGS' \
|
|
12
|
+
'-D_GLIBCXX_USE_CXX11_ABI=1' \
|
|
13
|
+
'-D_DARWIN_USE_64_BIT_INODE=1' \
|
|
14
|
+
'-D_LARGEFILE_SOURCE' \
|
|
15
|
+
'-D_FILE_OFFSET_BITS=64' \
|
|
16
|
+
'-DOPENSSL_NO_PINSHARED' \
|
|
17
|
+
'-DOPENSSL_THREADS' \
|
|
18
|
+
'-DOPENSSL_API_COMPAT=0x10100000L' \
|
|
19
|
+
'-DREAL_OPENSSL_MAJOR=3' \
|
|
20
|
+
'-DBUILDING_NODE_EXTENSION' \
|
|
21
|
+
'-DDEBUG' \
|
|
22
|
+
'-D_DEBUG' \
|
|
23
|
+
'-DV8_ENABLE_CHECKS'
|
|
24
|
+
|
|
25
|
+
# Flags passed to all source files.
|
|
26
|
+
CFLAGS_Debug := \
|
|
27
|
+
-O0 \
|
|
28
|
+
-gdwarf-2 \
|
|
29
|
+
-mmacosx-version-min=10.15 \
|
|
30
|
+
-arch x86_64 \
|
|
31
|
+
-Wall \
|
|
32
|
+
-Wendif-labels \
|
|
33
|
+
-W \
|
|
34
|
+
-Wno-unused-parameter
|
|
35
|
+
|
|
36
|
+
# Flags passed to only C files.
|
|
37
|
+
CFLAGS_C_Debug := \
|
|
38
|
+
-fno-strict-aliasing
|
|
39
|
+
|
|
40
|
+
# Flags passed to only C++ files.
|
|
41
|
+
CFLAGS_CC_Debug := \
|
|
42
|
+
-std=gnu++17 \
|
|
43
|
+
-stdlib=libc++ \
|
|
44
|
+
-fno-rtti \
|
|
45
|
+
-fno-exceptions \
|
|
46
|
+
-fno-strict-aliasing
|
|
47
|
+
|
|
48
|
+
# Flags passed to only ObjC files.
|
|
49
|
+
CFLAGS_OBJC_Debug :=
|
|
50
|
+
|
|
51
|
+
# Flags passed to only ObjC++ files.
|
|
52
|
+
CFLAGS_OBJCC_Debug :=
|
|
53
|
+
|
|
54
|
+
INCS_Debug := \
|
|
55
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/include/node \
|
|
56
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/src \
|
|
57
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/config \
|
|
58
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/openssl/include \
|
|
59
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/uv/include \
|
|
60
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/zlib \
|
|
61
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/v8/include \
|
|
62
|
+
-I$(srcdir)/../../../node_modules/nan
|
|
63
|
+
|
|
64
|
+
DEFS_Release := \
|
|
65
|
+
'-DNODE_GYP_MODULE_NAME=sshcrypto' \
|
|
66
|
+
'-DUSING_UV_SHARED=1' \
|
|
67
|
+
'-DUSING_V8_SHARED=1' \
|
|
68
|
+
'-DV8_DEPRECATION_WARNINGS=1' \
|
|
69
|
+
'-DV8_DEPRECATION_WARNINGS' \
|
|
70
|
+
'-DV8_IMMINENT_DEPRECATION_WARNINGS' \
|
|
71
|
+
'-D_GLIBCXX_USE_CXX11_ABI=1' \
|
|
72
|
+
'-D_DARWIN_USE_64_BIT_INODE=1' \
|
|
73
|
+
'-D_LARGEFILE_SOURCE' \
|
|
74
|
+
'-D_FILE_OFFSET_BITS=64' \
|
|
75
|
+
'-DOPENSSL_NO_PINSHARED' \
|
|
76
|
+
'-DOPENSSL_THREADS' \
|
|
77
|
+
'-DOPENSSL_API_COMPAT=0x10100000L' \
|
|
78
|
+
'-DREAL_OPENSSL_MAJOR=3' \
|
|
79
|
+
'-DBUILDING_NODE_EXTENSION'
|
|
80
|
+
|
|
81
|
+
# Flags passed to all source files.
|
|
82
|
+
CFLAGS_Release := \
|
|
83
|
+
-O3 \
|
|
84
|
+
-gdwarf-2 \
|
|
85
|
+
-mmacosx-version-min=10.15 \
|
|
86
|
+
-arch x86_64 \
|
|
87
|
+
-Wall \
|
|
88
|
+
-Wendif-labels \
|
|
89
|
+
-W \
|
|
90
|
+
-Wno-unused-parameter
|
|
91
|
+
|
|
92
|
+
# Flags passed to only C files.
|
|
93
|
+
CFLAGS_C_Release := \
|
|
94
|
+
-fno-strict-aliasing
|
|
95
|
+
|
|
96
|
+
# Flags passed to only C++ files.
|
|
97
|
+
CFLAGS_CC_Release := \
|
|
98
|
+
-std=gnu++17 \
|
|
99
|
+
-stdlib=libc++ \
|
|
100
|
+
-fno-rtti \
|
|
101
|
+
-fno-exceptions \
|
|
102
|
+
-fno-strict-aliasing
|
|
103
|
+
|
|
104
|
+
# Flags passed to only ObjC files.
|
|
105
|
+
CFLAGS_OBJC_Release :=
|
|
106
|
+
|
|
107
|
+
# Flags passed to only ObjC++ files.
|
|
108
|
+
CFLAGS_OBJCC_Release :=
|
|
109
|
+
|
|
110
|
+
INCS_Release := \
|
|
111
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/include/node \
|
|
112
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/src \
|
|
113
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/config \
|
|
114
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/openssl/openssl/include \
|
|
115
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/uv/include \
|
|
116
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/zlib \
|
|
117
|
+
-I/Users/home/Library/Caches/node-gyp/18.17.1/deps/v8/include \
|
|
118
|
+
-I$(srcdir)/../../../node_modules/nan
|
|
119
|
+
|
|
120
|
+
OBJS := \
|
|
121
|
+
$(obj).target/$(TARGET)/src/binding.o
|
|
122
|
+
|
|
123
|
+
# Add to the list of files we specially track dependencies for.
|
|
124
|
+
all_deps += $(OBJS)
|
|
125
|
+
|
|
126
|
+
# CFLAGS et al overrides must be target-local.
|
|
127
|
+
# See "Target-specific Variable Values" in the GNU Make manual.
|
|
128
|
+
$(OBJS): TOOLSET := $(TOOLSET)
|
|
129
|
+
$(OBJS): GYP_CFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE))
|
|
130
|
+
$(OBJS): GYP_CXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE))
|
|
131
|
+
$(OBJS): GYP_OBJCFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_C_$(BUILDTYPE)) $(CFLAGS_OBJC_$(BUILDTYPE))
|
|
132
|
+
$(OBJS): GYP_OBJCXXFLAGS := $(DEFS_$(BUILDTYPE)) $(INCS_$(BUILDTYPE)) $(CFLAGS_$(BUILDTYPE)) $(CFLAGS_CC_$(BUILDTYPE)) $(CFLAGS_OBJCC_$(BUILDTYPE))
|
|
133
|
+
|
|
134
|
+
# Suffix rules, putting all outputs into $(obj).
|
|
135
|
+
|
|
136
|
+
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(srcdir)/%.cc FORCE_DO_CMD
|
|
137
|
+
@$(call do_cmd,cxx,1)
|
|
138
|
+
|
|
139
|
+
# Try building from generated source, too.
|
|
140
|
+
|
|
141
|
+
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj).$(TOOLSET)/%.cc FORCE_DO_CMD
|
|
142
|
+
@$(call do_cmd,cxx,1)
|
|
143
|
+
|
|
144
|
+
$(obj).$(TOOLSET)/$(TARGET)/%.o: $(obj)/%.cc FORCE_DO_CMD
|
|
145
|
+
@$(call do_cmd,cxx,1)
|
|
146
|
+
|
|
147
|
+
# End of this set of suffix rules
|
|
148
|
+
### Rules for final target.
|
|
149
|
+
LDFLAGS_Debug := \
|
|
150
|
+
-undefined dynamic_lookup \
|
|
151
|
+
-Wl,-search_paths_first \
|
|
152
|
+
-mmacosx-version-min=10.15 \
|
|
153
|
+
-arch x86_64 \
|
|
154
|
+
-L$(builddir) \
|
|
155
|
+
-stdlib=libc++
|
|
156
|
+
|
|
157
|
+
LIBTOOLFLAGS_Debug := \
|
|
158
|
+
-undefined dynamic_lookup \
|
|
159
|
+
-Wl,-search_paths_first
|
|
160
|
+
|
|
161
|
+
LDFLAGS_Release := \
|
|
162
|
+
-undefined dynamic_lookup \
|
|
163
|
+
-Wl,-search_paths_first \
|
|
164
|
+
-mmacosx-version-min=10.15 \
|
|
165
|
+
-arch x86_64 \
|
|
166
|
+
-L$(builddir) \
|
|
167
|
+
-stdlib=libc++
|
|
168
|
+
|
|
169
|
+
LIBTOOLFLAGS_Release := \
|
|
170
|
+
-undefined dynamic_lookup \
|
|
171
|
+
-Wl,-search_paths_first
|
|
172
|
+
|
|
173
|
+
LIBS :=
|
|
174
|
+
|
|
175
|
+
$(builddir)/sshcrypto.node: GYP_LDFLAGS := $(LDFLAGS_$(BUILDTYPE))
|
|
176
|
+
$(builddir)/sshcrypto.node: LIBS := $(LIBS)
|
|
177
|
+
$(builddir)/sshcrypto.node: GYP_LIBTOOLFLAGS := $(LIBTOOLFLAGS_$(BUILDTYPE))
|
|
178
|
+
$(builddir)/sshcrypto.node: TOOLSET := $(TOOLSET)
|
|
179
|
+
$(builddir)/sshcrypto.node: $(OBJS) FORCE_DO_CMD
|
|
180
|
+
$(call do_cmd,solink_module)
|
|
181
|
+
|
|
182
|
+
all_deps += $(builddir)/sshcrypto.node
|
|
183
|
+
# Add target alias
|
|
184
|
+
.PHONY: sshcrypto
|
|
185
|
+
sshcrypto: $(builddir)/sshcrypto.node
|
|
186
|
+
|
|
187
|
+
# Short alias for building this executable.
|
|
188
|
+
.PHONY: sshcrypto.node
|
|
189
|
+
sshcrypto.node: $(builddir)/sshcrypto.node
|
|
190
|
+
|
|
191
|
+
# Add executable to "all" target.
|
|
192
|
+
.PHONY: all
|
|
193
|
+
all: $(builddir)/sshcrypto.node
|
|
194
|
+
|
|
@@ -146,6 +146,48 @@ module.exports = {
|
|
|
146
146
|
const handler = self._handlers.SERVICE_ACCEPT;
|
|
147
147
|
handler && handler(self, name);
|
|
148
148
|
},
|
|
149
|
+
[MESSAGE.EXT_INFO]: (self, payload) => {
|
|
150
|
+
/*
|
|
151
|
+
byte SSH_MSG_EXT_INFO
|
|
152
|
+
uint32 nr-extensions
|
|
153
|
+
repeat the following 2 fields "nr-extensions" times:
|
|
154
|
+
string extension-name
|
|
155
|
+
string extension-value (binary)
|
|
156
|
+
*/
|
|
157
|
+
bufferParser.init(payload, 1);
|
|
158
|
+
const numExts = bufferParser.readUInt32BE();
|
|
159
|
+
let exts;
|
|
160
|
+
if (numExts !== undefined) {
|
|
161
|
+
exts = [];
|
|
162
|
+
for (let i = 0; i < numExts; ++i) {
|
|
163
|
+
const name = bufferParser.readString(true);
|
|
164
|
+
const data = bufferParser.readString();
|
|
165
|
+
if (data !== undefined) {
|
|
166
|
+
switch (name) {
|
|
167
|
+
case 'server-sig-algs': {
|
|
168
|
+
const algs = data.latin1Slice(0, data.length).split(',');
|
|
169
|
+
exts.push({ name, algs });
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
default:
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
// Malformed
|
|
177
|
+
exts = undefined;
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
bufferParser.clear();
|
|
182
|
+
|
|
183
|
+
if (exts === undefined)
|
|
184
|
+
return doFatalError(self, 'Inbound: Malformed EXT_INFO packet');
|
|
185
|
+
|
|
186
|
+
self._debug && self._debug('Inbound: Received EXT_INFO');
|
|
187
|
+
|
|
188
|
+
const handler = self._handlers.EXT_INFO;
|
|
189
|
+
handler && handler(self, exts);
|
|
190
|
+
},
|
|
149
191
|
|
|
150
192
|
// User auth protocol -- generic =============================================
|
|
151
193
|
[MESSAGE.USERAUTH_REQUEST]: (self, payload) => {
|
|
@@ -195,7 +237,21 @@ module.exports = {
|
|
|
195
237
|
const hasSig = bufferParser.readBool();
|
|
196
238
|
if (hasSig !== undefined) {
|
|
197
239
|
const keyAlgo = bufferParser.readString(true);
|
|
240
|
+
let realKeyAlgo = keyAlgo;
|
|
198
241
|
const key = bufferParser.readString();
|
|
242
|
+
|
|
243
|
+
let hashAlgo;
|
|
244
|
+
switch (keyAlgo) {
|
|
245
|
+
case 'rsa-sha2-256':
|
|
246
|
+
realKeyAlgo = 'ssh-rsa';
|
|
247
|
+
hashAlgo = 'sha256';
|
|
248
|
+
break;
|
|
249
|
+
case 'rsa-sha2-512':
|
|
250
|
+
realKeyAlgo = 'ssh-rsa';
|
|
251
|
+
hashAlgo = 'sha512';
|
|
252
|
+
break;
|
|
253
|
+
}
|
|
254
|
+
|
|
199
255
|
if (hasSig) {
|
|
200
256
|
const blobEnd = bufferParser.pos();
|
|
201
257
|
let signature = bufferParser.readString();
|
|
@@ -206,7 +262,7 @@ module.exports = {
|
|
|
206
262
|
signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
|
|
207
263
|
}
|
|
208
264
|
|
|
209
|
-
signature = sigSSHToASN1(signature,
|
|
265
|
+
signature = sigSSHToASN1(signature, realKeyAlgo);
|
|
210
266
|
if (signature) {
|
|
211
267
|
const sessionID = self._kex.sessionID;
|
|
212
268
|
const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
|
|
@@ -217,15 +273,16 @@ module.exports = {
|
|
|
217
273
|
4 + sessionID.length
|
|
218
274
|
);
|
|
219
275
|
methodData = {
|
|
220
|
-
keyAlgo,
|
|
276
|
+
keyAlgo: realKeyAlgo,
|
|
221
277
|
key,
|
|
222
278
|
signature,
|
|
223
279
|
blob,
|
|
280
|
+
hashAlgo,
|
|
224
281
|
};
|
|
225
282
|
}
|
|
226
283
|
}
|
|
227
284
|
} else {
|
|
228
|
-
methodData = { keyAlgo, key };
|
|
285
|
+
methodData = { keyAlgo: realKeyAlgo, key, hashAlgo };
|
|
229
286
|
methodDesc = 'publickey -- check';
|
|
230
287
|
}
|
|
231
288
|
}
|
|
@@ -241,10 +298,23 @@ module.exports = {
|
|
|
241
298
|
string signature
|
|
242
299
|
*/
|
|
243
300
|
const keyAlgo = bufferParser.readString(true);
|
|
301
|
+
let realKeyAlgo = keyAlgo;
|
|
244
302
|
const key = bufferParser.readString();
|
|
245
303
|
const localHostname = bufferParser.readString(true);
|
|
246
304
|
const localUsername = bufferParser.readString(true);
|
|
247
305
|
|
|
306
|
+
let hashAlgo;
|
|
307
|
+
switch (keyAlgo) {
|
|
308
|
+
case 'rsa-sha2-256':
|
|
309
|
+
realKeyAlgo = 'ssh-rsa';
|
|
310
|
+
hashAlgo = 'sha256';
|
|
311
|
+
break;
|
|
312
|
+
case 'rsa-sha2-512':
|
|
313
|
+
realKeyAlgo = 'ssh-rsa';
|
|
314
|
+
hashAlgo = 'sha512';
|
|
315
|
+
break;
|
|
316
|
+
}
|
|
317
|
+
|
|
248
318
|
const blobEnd = bufferParser.pos();
|
|
249
319
|
let signature = bufferParser.readString();
|
|
250
320
|
if (signature !== undefined) {
|
|
@@ -254,7 +324,7 @@ module.exports = {
|
|
|
254
324
|
signature = bufferSlice(signature, 4 + keyAlgo.length + 4);
|
|
255
325
|
}
|
|
256
326
|
|
|
257
|
-
signature = sigSSHToASN1(signature,
|
|
327
|
+
signature = sigSSHToASN1(signature, realKeyAlgo);
|
|
258
328
|
if (signature !== undefined) {
|
|
259
329
|
const sessionID = self._kex.sessionID;
|
|
260
330
|
const blob = Buffer.allocUnsafe(4 + sessionID.length + blobEnd);
|
|
@@ -265,12 +335,13 @@ module.exports = {
|
|
|
265
335
|
4 + sessionID.length
|
|
266
336
|
);
|
|
267
337
|
methodData = {
|
|
268
|
-
keyAlgo,
|
|
338
|
+
keyAlgo: realKeyAlgo,
|
|
269
339
|
key,
|
|
270
340
|
signature,
|
|
271
341
|
blob,
|
|
272
342
|
localHostname,
|
|
273
343
|
localUsername,
|
|
344
|
+
hashAlgo
|
|
274
345
|
};
|
|
275
346
|
}
|
|
276
347
|
}
|
package/lib/protocol/kex.js
CHANGED
|
@@ -222,12 +222,39 @@ function handleKexInit(self, payload) {
|
|
|
222
222
|
// Key exchange method =======================================================
|
|
223
223
|
debug && debug(`Handshake: (local) KEX method: ${localKex}`);
|
|
224
224
|
debug && debug(`Handshake: (remote) KEX method: ${remote.kex}`);
|
|
225
|
+
let remoteExtInfoEnabled;
|
|
225
226
|
if (self._server) {
|
|
226
227
|
serverList = localKex;
|
|
227
228
|
clientList = remote.kex;
|
|
229
|
+
remoteExtInfoEnabled = (clientList.indexOf('ext-info-c') !== -1);
|
|
228
230
|
} else {
|
|
229
231
|
serverList = remote.kex;
|
|
230
232
|
clientList = localKex;
|
|
233
|
+
remoteExtInfoEnabled = (serverList.indexOf('ext-info-s') !== -1);
|
|
234
|
+
}
|
|
235
|
+
if (self._strictMode === undefined) {
|
|
236
|
+
if (self._server) {
|
|
237
|
+
self._strictMode =
|
|
238
|
+
(clientList.indexOf('kex-strict-c-v00@openssh.com') !== -1);
|
|
239
|
+
} else {
|
|
240
|
+
self._strictMode =
|
|
241
|
+
(serverList.indexOf('kex-strict-s-v00@openssh.com') !== -1);
|
|
242
|
+
}
|
|
243
|
+
// Note: We check for seqno of 1 instead of 0 since we increment before
|
|
244
|
+
// calling the packet handler
|
|
245
|
+
if (self._strictMode) {
|
|
246
|
+
debug && debug('Handshake: strict KEX mode enabled');
|
|
247
|
+
if (self._decipher.inSeqno !== 1) {
|
|
248
|
+
if (debug)
|
|
249
|
+
debug('Handshake: KEXINIT not first packet in strict KEX mode');
|
|
250
|
+
return doFatalError(
|
|
251
|
+
self,
|
|
252
|
+
'Handshake failed: KEXINIT not first packet in strict KEX mode',
|
|
253
|
+
'handshake',
|
|
254
|
+
DISCONNECT_REASON.KEY_EXCHANGE_FAILED
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
231
258
|
}
|
|
232
259
|
// Check for agreeable key exchange algorithm
|
|
233
260
|
for (i = 0;
|
|
@@ -235,7 +262,7 @@ function handleKexInit(self, payload) {
|
|
|
235
262
|
++i);
|
|
236
263
|
if (i === clientList.length) {
|
|
237
264
|
// No suitable match found!
|
|
238
|
-
debug && debug('Handshake:
|
|
265
|
+
debug && debug('Handshake: no matching key exchange algorithm');
|
|
239
266
|
return doFatalError(
|
|
240
267
|
self,
|
|
241
268
|
'Handshake failed: no matching key exchange algorithm',
|
|
@@ -479,6 +506,7 @@ function handleKexInit(self, payload) {
|
|
|
479
506
|
}
|
|
480
507
|
|
|
481
508
|
self._kex = createKeyExchange(init, self, payload);
|
|
509
|
+
self._kex.remoteExtInfoEnabled = remoteExtInfoEnabled;
|
|
482
510
|
self._kex.start();
|
|
483
511
|
}
|
|
484
512
|
|
|
@@ -510,6 +538,7 @@ const createKeyExchange = (() => {
|
|
|
510
538
|
|
|
511
539
|
this.sessionID = (protocol._kex ? protocol._kex.sessionID : undefined);
|
|
512
540
|
this.negotiated = negotiated;
|
|
541
|
+
this.remoteExtInfoEnabled = false;
|
|
513
542
|
this._step = 1;
|
|
514
543
|
this._public = null;
|
|
515
544
|
this._dh = null;
|
|
@@ -527,7 +556,7 @@ const createKeyExchange = (() => {
|
|
|
527
556
|
this._dhData = undefined;
|
|
528
557
|
this._sig = undefined;
|
|
529
558
|
}
|
|
530
|
-
finish() {
|
|
559
|
+
finish(scOnly) {
|
|
531
560
|
if (this._finished)
|
|
532
561
|
return false;
|
|
533
562
|
this._finished = true;
|
|
@@ -778,9 +807,26 @@ const createKeyExchange = (() => {
|
|
|
778
807
|
this._protocol._packetRW.write.finalize(packet, true)
|
|
779
808
|
);
|
|
780
809
|
}
|
|
781
|
-
trySendNEWKEYS(this);
|
|
782
810
|
|
|
783
|
-
|
|
811
|
+
if (isServer || !scOnly)
|
|
812
|
+
trySendNEWKEYS(this);
|
|
813
|
+
|
|
814
|
+
let hsCipherConfig;
|
|
815
|
+
let hsWrite;
|
|
816
|
+
const completeHandshake = (partial) => {
|
|
817
|
+
if (hsCipherConfig) {
|
|
818
|
+
trySendNEWKEYS(this);
|
|
819
|
+
hsCipherConfig.outbound.seqno = this._protocol._cipher.outSeqno;
|
|
820
|
+
this._protocol._cipher.free();
|
|
821
|
+
this._protocol._cipher = createCipher(hsCipherConfig);
|
|
822
|
+
this._protocol._packetRW.write = hsWrite;
|
|
823
|
+
hsCipherConfig = undefined;
|
|
824
|
+
hsWrite = undefined;
|
|
825
|
+
this._protocol._onHandshakeComplete(negotiated);
|
|
826
|
+
|
|
827
|
+
return false;
|
|
828
|
+
}
|
|
829
|
+
|
|
784
830
|
if (!this.sessionID)
|
|
785
831
|
this.sessionID = exchangeHash;
|
|
786
832
|
|
|
@@ -863,9 +909,8 @@ const createKeyExchange = (() => {
|
|
|
863
909
|
macKey: (isServer ? scMacKey : csMacKey),
|
|
864
910
|
},
|
|
865
911
|
};
|
|
866
|
-
this._protocol.
|
|
867
|
-
|
|
868
|
-
this._protocol._cipher = createCipher(config);
|
|
912
|
+
this._protocol._decipher.free();
|
|
913
|
+
hsCipherConfig = config;
|
|
869
914
|
this._protocol._decipher = createDecipher(config);
|
|
870
915
|
|
|
871
916
|
const rw = {
|
|
@@ -932,7 +977,8 @@ const createKeyExchange = (() => {
|
|
|
932
977
|
}
|
|
933
978
|
this._protocol._packetRW.read.cleanup();
|
|
934
979
|
this._protocol._packetRW.write.cleanup();
|
|
935
|
-
this._protocol._packetRW = rw;
|
|
980
|
+
this._protocol._packetRW.read = rw.read;
|
|
981
|
+
hsWrite = rw.write;
|
|
936
982
|
|
|
937
983
|
// Cleanup/reset various state
|
|
938
984
|
this._public = null;
|
|
@@ -945,13 +991,16 @@ const createKeyExchange = (() => {
|
|
|
945
991
|
this._dhData = undefined;
|
|
946
992
|
this._sig = undefined;
|
|
947
993
|
|
|
948
|
-
|
|
949
|
-
|
|
994
|
+
if (!partial)
|
|
995
|
+
return completeHandshake();
|
|
950
996
|
return false;
|
|
951
997
|
};
|
|
998
|
+
|
|
999
|
+
if (isServer || scOnly)
|
|
1000
|
+
this.finish = completeHandshake;
|
|
1001
|
+
|
|
952
1002
|
if (!isServer)
|
|
953
|
-
return completeHandshake();
|
|
954
|
-
this.finish = completeHandshake;
|
|
1003
|
+
return completeHandshake(scOnly);
|
|
955
1004
|
}
|
|
956
1005
|
|
|
957
1006
|
start() {
|
|
@@ -1211,13 +1260,11 @@ const createKeyExchange = (() => {
|
|
|
1211
1260
|
'Inbound: NEWKEYS'
|
|
1212
1261
|
);
|
|
1213
1262
|
this._receivedNEWKEYS = true;
|
|
1263
|
+
if (this._protocol._strictMode)
|
|
1264
|
+
this._protocol._decipher.inSeqno = 0;
|
|
1214
1265
|
++this._step;
|
|
1215
|
-
if (this._protocol._server || this._hostVerified)
|
|
1216
|
-
return this.finish();
|
|
1217
1266
|
|
|
1218
|
-
|
|
1219
|
-
// for the next packet
|
|
1220
|
-
return false;
|
|
1267
|
+
return this.finish(!this._protocol._server && !this._hostVerified);
|
|
1221
1268
|
default:
|
|
1222
1269
|
return doFatalError(
|
|
1223
1270
|
this._protocol,
|
|
@@ -1378,7 +1425,7 @@ const createKeyExchange = (() => {
|
|
|
1378
1425
|
parse(payload) {
|
|
1379
1426
|
const type = payload[0];
|
|
1380
1427
|
switch (this._step) {
|
|
1381
|
-
case 1:
|
|
1428
|
+
case 1: {
|
|
1382
1429
|
if (this._protocol._server) {
|
|
1383
1430
|
if (type !== MESSAGE.KEXDH_GEX_REQUEST) {
|
|
1384
1431
|
return doFatalError(
|
|
@@ -1453,6 +1500,7 @@ const createKeyExchange = (() => {
|
|
|
1453
1500
|
|
|
1454
1501
|
++this._step;
|
|
1455
1502
|
break;
|
|
1503
|
+
}
|
|
1456
1504
|
case 2:
|
|
1457
1505
|
if (this._protocol._server) {
|
|
1458
1506
|
if (type !== MESSAGE.KEXDH_GEX_INIT) {
|
|
@@ -1734,11 +1782,20 @@ function onKEXPayload(state, payload) {
|
|
|
1734
1782
|
payload = this._packetRW.read.read(payload);
|
|
1735
1783
|
|
|
1736
1784
|
const type = payload[0];
|
|
1785
|
+
|
|
1786
|
+
if (!this._strictMode) {
|
|
1787
|
+
switch (type) {
|
|
1788
|
+
case MESSAGE.IGNORE:
|
|
1789
|
+
case MESSAGE.UNIMPLEMENTED:
|
|
1790
|
+
case MESSAGE.DEBUG:
|
|
1791
|
+
if (!MESSAGE_HANDLERS)
|
|
1792
|
+
MESSAGE_HANDLERS = require('./handlers.js');
|
|
1793
|
+
return MESSAGE_HANDLERS[type](this, payload);
|
|
1794
|
+
}
|
|
1795
|
+
}
|
|
1796
|
+
|
|
1737
1797
|
switch (type) {
|
|
1738
1798
|
case MESSAGE.DISCONNECT:
|
|
1739
|
-
case MESSAGE.IGNORE:
|
|
1740
|
-
case MESSAGE.UNIMPLEMENTED:
|
|
1741
|
-
case MESSAGE.DEBUG:
|
|
1742
1799
|
if (!MESSAGE_HANDLERS)
|
|
1743
1800
|
MESSAGE_HANDLERS = require('./handlers.js');
|
|
1744
1801
|
return MESSAGE_HANDLERS[type](this, payload);
|
|
@@ -1754,6 +1811,8 @@ function onKEXPayload(state, payload) {
|
|
|
1754
1811
|
state.firstPacket = false;
|
|
1755
1812
|
return handleKexInit(this, payload);
|
|
1756
1813
|
default:
|
|
1814
|
+
// Ensure packet is either an algorithm negotiation or KEX
|
|
1815
|
+
// algorithm-specific packet
|
|
1757
1816
|
if (type < 20 || type > 49) {
|
|
1758
1817
|
return doFatalError(
|
|
1759
1818
|
this,
|
|
@@ -1802,6 +1861,8 @@ function trySendNEWKEYS(kex) {
|
|
|
1802
1861
|
kex._protocol._packetRW.write.finalize(packet, true)
|
|
1803
1862
|
);
|
|
1804
1863
|
kex._sentNEWKEYS = true;
|
|
1864
|
+
if (kex._protocol._strictMode)
|
|
1865
|
+
kex._protocol._cipher.outSeqno = 0;
|
|
1805
1866
|
}
|
|
1806
1867
|
}
|
|
1807
1868
|
|
|
@@ -1809,8 +1870,24 @@ module.exports = {
|
|
|
1809
1870
|
KexInit,
|
|
1810
1871
|
kexinit,
|
|
1811
1872
|
onKEXPayload,
|
|
1812
|
-
|
|
1813
|
-
kex: DEFAULT_KEX,
|
|
1873
|
+
DEFAULT_KEXINIT_CLIENT: new KexInit({
|
|
1874
|
+
kex: DEFAULT_KEX.concat(['ext-info-c', 'kex-strict-c-v00@openssh.com']),
|
|
1875
|
+
serverHostKey: DEFAULT_SERVER_HOST_KEY,
|
|
1876
|
+
cs: {
|
|
1877
|
+
cipher: DEFAULT_CIPHER,
|
|
1878
|
+
mac: DEFAULT_MAC,
|
|
1879
|
+
compress: DEFAULT_COMPRESSION,
|
|
1880
|
+
lang: [],
|
|
1881
|
+
},
|
|
1882
|
+
sc: {
|
|
1883
|
+
cipher: DEFAULT_CIPHER,
|
|
1884
|
+
mac: DEFAULT_MAC,
|
|
1885
|
+
compress: DEFAULT_COMPRESSION,
|
|
1886
|
+
lang: [],
|
|
1887
|
+
},
|
|
1888
|
+
}),
|
|
1889
|
+
DEFAULT_KEXINIT_SERVER: new KexInit({
|
|
1890
|
+
kex: DEFAULT_KEX.concat(['kex-strict-s-v00@openssh.com']),
|
|
1814
1891
|
serverHostKey: DEFAULT_SERVER_HOST_KEY,
|
|
1815
1892
|
cs: {
|
|
1816
1893
|
cipher: DEFAULT_CIPHER,
|
|
@@ -446,7 +446,7 @@ const BaseKey = {
|
|
|
446
446
|
this.type === parsed.type
|
|
447
447
|
&& this[SYM_PRIV_PEM] === parsed[SYM_PRIV_PEM]
|
|
448
448
|
&& this[SYM_PUB_PEM] === parsed[SYM_PUB_PEM]
|
|
449
|
-
&& this[SYM_PUB_SSH]
|
|
449
|
+
&& this[SYM_PUB_SSH].equals(parsed[SYM_PUB_SSH])
|
|
450
450
|
);
|
|
451
451
|
},
|
|
452
452
|
};
|
|
@@ -512,7 +512,7 @@ OpenSSH_Private.prototype = BaseKey;
|
|
|
512
512
|
switch (kdfName) {
|
|
513
513
|
case 'none':
|
|
514
514
|
return new Error('Malformed OpenSSH private key');
|
|
515
|
-
case 'bcrypt':
|
|
515
|
+
case 'bcrypt': {
|
|
516
516
|
/*
|
|
517
517
|
string salt
|
|
518
518
|
uint32 rounds
|
|
@@ -534,6 +534,7 @@ OpenSSH_Private.prototype = BaseKey;
|
|
|
534
534
|
cipherKey = bufferSlice(gen, 0, encInfo.keyLen);
|
|
535
535
|
cipherIV = bufferSlice(gen, encInfo.keyLen, gen.length);
|
|
536
536
|
break;
|
|
537
|
+
}
|
|
537
538
|
}
|
|
538
539
|
} else if (kdfName !== 'none') {
|
|
539
540
|
return new Error('Malformed OpenSSH private key');
|
|
@@ -573,6 +574,7 @@ OpenSSH_Private.prototype = BaseKey;
|
|
|
573
574
|
cipherKey,
|
|
574
575
|
cipherIV,
|
|
575
576
|
options);
|
|
577
|
+
decipher.setAutoPadding(false);
|
|
576
578
|
if (encInfo.authLen > 0) {
|
|
577
579
|
if (data.length - data._pos < encInfo.authLen)
|
|
578
580
|
return new Error('Malformed OpenSSH private key');
|
|
@@ -932,7 +934,7 @@ OpenSSH_Old_Private.prototype = BaseKey;
|
|
|
932
934
|
}
|
|
933
935
|
algo = 'sha1';
|
|
934
936
|
break;
|
|
935
|
-
case 'EC':
|
|
937
|
+
case 'EC': {
|
|
936
938
|
let ecSSLName;
|
|
937
939
|
let ecPriv;
|
|
938
940
|
let ecOID;
|
|
@@ -981,6 +983,7 @@ OpenSSH_Old_Private.prototype = BaseKey;
|
|
|
981
983
|
pubPEM = genOpenSSLECDSAPub(ecOID, pubBlob);
|
|
982
984
|
pubSSH = genOpenSSHECDSAPub(ecOID, pubBlob);
|
|
983
985
|
break;
|
|
986
|
+
}
|
|
984
987
|
}
|
|
985
988
|
|
|
986
989
|
return new OpenSSH_Old_Private(type, '', privPEM, pubPEM, pubSSH, algo,
|
|
@@ -1056,9 +1059,7 @@ PPK_Private.prototype = BaseKey;
|
|
|
1056
1059
|
if (cipherKey.length > encInfo.keyLen)
|
|
1057
1060
|
cipherKey = bufferSlice(cipherKey, 0, encInfo.keyLen);
|
|
1058
1061
|
try {
|
|
1059
|
-
const decipher = createDecipheriv(encInfo.sslName,
|
|
1060
|
-
cipherKey,
|
|
1061
|
-
PPK_IV);
|
|
1062
|
+
const decipher = createDecipheriv(encInfo.sslName, cipherKey, PPK_IV);
|
|
1062
1063
|
decipher.setAutoPadding(false);
|
|
1063
1064
|
privBlob = combineBuffers(decipher.update(privBlob),
|
|
1064
1065
|
decipher.final());
|
package/lib/protocol/zlib.js
CHANGED
|
@@ -69,8 +69,9 @@ class Zlib {
|
|
|
69
69
|
|
|
70
70
|
writeSync(chunk, retChunks) {
|
|
71
71
|
const handle = this._handle;
|
|
72
|
-
if (!handle)
|
|
73
|
-
|
|
72
|
+
if (!handle) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
74
75
|
|
|
75
76
|
let availInBefore = chunk.length;
|
|
76
77
|
let availOutBefore = this._chunkSize - this._outOffset;
|
|
@@ -188,6 +189,9 @@ class ZlibPacketWriter {
|
|
|
188
189
|
finalize(payload, force) {
|
|
189
190
|
if (this._protocol._kexinit === undefined || force) {
|
|
190
191
|
const output = this._zlib.writeSync(payload, true);
|
|
192
|
+
if (!output) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
191
195
|
const packet = this._protocol._cipher.allocPacket(output.totalLen);
|
|
192
196
|
if (output.push === undefined) {
|
|
193
197
|
packet.set(output, 5);
|