@electerm/ssh2 1.11.2 → 1.14.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 +137 -8
- package/lib/index.js +1 -0
- package/lib/keygen.js +582 -0
- package/lib/protocol/Protocol.js +65 -13
- package/lib/protocol/SFTP.js +189 -1
- package/lib/protocol/constants.js +5 -2
- package/lib/protocol/handlers.misc.js +76 -5
- package/lib/protocol/kex.js +56 -18
- package/lib/protocol/keyParser.js +6 -5
- package/lib/server.js +9 -0
- package/package.json +4 -4
package/lib/client.js
CHANGED
|
@@ -297,6 +297,7 @@ class Client extends EventEmitter {
|
|
|
297
297
|
const DEBUG_HANDLER = (!debug ? undefined : (p, display, msg) => {
|
|
298
298
|
debug(`Debug output from server: ${JSON.stringify(msg)}`);
|
|
299
299
|
});
|
|
300
|
+
let serverSigAlgs;
|
|
300
301
|
const proto = this._protocol = new Protocol({
|
|
301
302
|
ident: this.config.ident,
|
|
302
303
|
offer: (allOfferDefaults ? undefined : algorithms),
|
|
@@ -348,6 +349,17 @@ class Client extends EventEmitter {
|
|
|
348
349
|
if (name === 'ssh-userauth')
|
|
349
350
|
tryNextAuth();
|
|
350
351
|
},
|
|
352
|
+
EXT_INFO: (p, exts) => {
|
|
353
|
+
if (serverSigAlgs === undefined) {
|
|
354
|
+
for (const ext of exts) {
|
|
355
|
+
if (ext.name === 'server-sig-algs') {
|
|
356
|
+
serverSigAlgs = ext.algs;
|
|
357
|
+
return;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
serverSigAlgs = null;
|
|
361
|
+
}
|
|
362
|
+
},
|
|
351
363
|
USERAUTH_BANNER: (p, msg) => {
|
|
352
364
|
this.emit('banner', msg);
|
|
353
365
|
},
|
|
@@ -360,6 +372,51 @@ class Client extends EventEmitter {
|
|
|
360
372
|
this.emit('ready');
|
|
361
373
|
},
|
|
362
374
|
USERAUTH_FAILURE: (p, authMethods, partialSuccess) => {
|
|
375
|
+
// For key-based authentication, check if we should retry the current
|
|
376
|
+
// key with a different algorithm first
|
|
377
|
+
if (curAuth.keyAlgos) {
|
|
378
|
+
const oldKeyAlgo = curAuth.keyAlgos[0][0];
|
|
379
|
+
if (debug)
|
|
380
|
+
debug(`Client: ${curAuth.type} (${oldKeyAlgo}) auth failed`);
|
|
381
|
+
curAuth.keyAlgos.shift();
|
|
382
|
+
if (curAuth.keyAlgos.length) {
|
|
383
|
+
const [keyAlgo, hashAlgo] = curAuth.keyAlgos[0];
|
|
384
|
+
switch (curAuth.type) {
|
|
385
|
+
case 'agent':
|
|
386
|
+
proto.authPK(
|
|
387
|
+
curAuth.username,
|
|
388
|
+
curAuth.agentCtx.currentKey(),
|
|
389
|
+
keyAlgo
|
|
390
|
+
);
|
|
391
|
+
return;
|
|
392
|
+
case 'publickey':
|
|
393
|
+
proto.authPK(curAuth.username, curAuth.key, keyAlgo);
|
|
394
|
+
return;
|
|
395
|
+
case 'hostbased':
|
|
396
|
+
proto.authHostbased(curAuth.username,
|
|
397
|
+
curAuth.key,
|
|
398
|
+
curAuth.localHostname,
|
|
399
|
+
curAuth.localUsername,
|
|
400
|
+
keyAlgo,
|
|
401
|
+
(buf, cb) => {
|
|
402
|
+
const signature = curAuth.key.sign(buf, hashAlgo);
|
|
403
|
+
if (signature instanceof Error) {
|
|
404
|
+
signature.message =
|
|
405
|
+
`Error while signing with key: ${signature.message}`;
|
|
406
|
+
signature.level = 'client-authentication';
|
|
407
|
+
this.emit('error', signature);
|
|
408
|
+
return tryNextAuth();
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
cb(signature);
|
|
412
|
+
});
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
415
|
+
} else {
|
|
416
|
+
curAuth.keyAlgos = undefined;
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
363
420
|
if (curAuth.type === 'agent') {
|
|
364
421
|
const pos = curAuth.agentCtx.pos();
|
|
365
422
|
debug && debug(`Client: Agent key #${pos + 1} failed`);
|
|
@@ -386,10 +443,15 @@ class Client extends EventEmitter {
|
|
|
386
443
|
}
|
|
387
444
|
},
|
|
388
445
|
USERAUTH_PK_OK: (p) => {
|
|
446
|
+
let keyAlgo;
|
|
447
|
+
let hashAlgo;
|
|
448
|
+
if (curAuth.keyAlgos)
|
|
449
|
+
[keyAlgo, hashAlgo] = curAuth.keyAlgos[0];
|
|
389
450
|
if (curAuth.type === 'agent') {
|
|
390
451
|
const key = curAuth.agentCtx.currentKey();
|
|
391
|
-
proto.authPK(curAuth.username, key, (buf, cb) => {
|
|
392
|
-
|
|
452
|
+
proto.authPK(curAuth.username, key, keyAlgo, (buf, cb) => {
|
|
453
|
+
const opts = { hash: hashAlgo };
|
|
454
|
+
curAuth.agentCtx.sign(key, buf, opts, (err, signed) => {
|
|
393
455
|
if (err) {
|
|
394
456
|
err.level = 'agent';
|
|
395
457
|
this.emit('error', err);
|
|
@@ -947,16 +1009,42 @@ class Client extends EventEmitter {
|
|
|
947
1009
|
case 'password':
|
|
948
1010
|
proto.authPassword(username, curAuth.password);
|
|
949
1011
|
break;
|
|
950
|
-
case 'publickey':
|
|
951
|
-
|
|
1012
|
+
case 'publickey': {
|
|
1013
|
+
let keyAlgo;
|
|
1014
|
+
curAuth.keyAlgos = getKeyAlgos(this, curAuth.key, serverSigAlgs);
|
|
1015
|
+
if (curAuth.keyAlgos) {
|
|
1016
|
+
if (curAuth.keyAlgos.length) {
|
|
1017
|
+
keyAlgo = curAuth.keyAlgos[0][0];
|
|
1018
|
+
} else {
|
|
1019
|
+
return skipAuth(
|
|
1020
|
+
'Skipping key authentication (no mutual hash algorithm)'
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
proto.authPK(username, curAuth.key, keyAlgo);
|
|
952
1025
|
break;
|
|
953
|
-
|
|
1026
|
+
}
|
|
1027
|
+
case 'hostbased': {
|
|
1028
|
+
let keyAlgo;
|
|
1029
|
+
let hashAlgo;
|
|
1030
|
+
curAuth.keyAlgos = getKeyAlgos(this, curAuth.key, serverSigAlgs);
|
|
1031
|
+
if (curAuth.keyAlgos) {
|
|
1032
|
+
if (curAuth.keyAlgos.length) {
|
|
1033
|
+
[keyAlgo, hashAlgo] = curAuth.keyAlgos[0];
|
|
1034
|
+
} else {
|
|
1035
|
+
return skipAuth(
|
|
1036
|
+
'Skipping hostbased authentication (no mutual hash algorithm)'
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
}
|
|
1040
|
+
|
|
954
1041
|
proto.authHostbased(username,
|
|
955
1042
|
curAuth.key,
|
|
956
1043
|
curAuth.localHostname,
|
|
957
1044
|
curAuth.localUsername,
|
|
1045
|
+
keyAlgo,
|
|
958
1046
|
(buf, cb) => {
|
|
959
|
-
const signature = curAuth.key.sign(buf);
|
|
1047
|
+
const signature = curAuth.key.sign(buf, hashAlgo);
|
|
960
1048
|
if (signature instanceof Error) {
|
|
961
1049
|
signature.message =
|
|
962
1050
|
`Error while signing with key: ${signature.message}`;
|
|
@@ -968,6 +1056,7 @@ class Client extends EventEmitter {
|
|
|
968
1056
|
cb(signature);
|
|
969
1057
|
});
|
|
970
1058
|
break;
|
|
1059
|
+
}
|
|
971
1060
|
case 'agent':
|
|
972
1061
|
curAuth.agentCtx.init((err) => {
|
|
973
1062
|
if (err) {
|
|
@@ -1012,8 +1101,21 @@ class Client extends EventEmitter {
|
|
|
1012
1101
|
tryNextAuth();
|
|
1013
1102
|
} else {
|
|
1014
1103
|
const pos = curAuth.agentCtx.pos();
|
|
1104
|
+
let keyAlgo;
|
|
1105
|
+
curAuth.keyAlgos = getKeyAlgos(this, key, serverSigAlgs);
|
|
1106
|
+
if (curAuth.keyAlgos) {
|
|
1107
|
+
if (curAuth.keyAlgos.length) {
|
|
1108
|
+
keyAlgo = curAuth.keyAlgos[0][0];
|
|
1109
|
+
} else {
|
|
1110
|
+
debug && debug(
|
|
1111
|
+
`Agent: Skipping key #${pos + 1} (no mutual hash algorithm)`
|
|
1112
|
+
);
|
|
1113
|
+
tryNextAgentKey();
|
|
1114
|
+
return;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1015
1117
|
debug && debug(`Agent: Trying key #${pos + 1}`);
|
|
1016
|
-
proto.authPK(curAuth.username, key);
|
|
1118
|
+
proto.authPK(curAuth.username, key, keyAlgo);
|
|
1017
1119
|
}
|
|
1018
1120
|
}
|
|
1019
1121
|
};
|
|
@@ -1044,7 +1146,6 @@ class Client extends EventEmitter {
|
|
|
1044
1146
|
localAddress: this.config.localAddress,
|
|
1045
1147
|
localPort: this.config.localPort
|
|
1046
1148
|
});
|
|
1047
|
-
sock.setNoDelay(true);
|
|
1048
1149
|
sock.setMaxListeners(0);
|
|
1049
1150
|
sock.setTimeout(typeof cfg.timeout === 'number' ? cfg.timeout : 0);
|
|
1050
1151
|
};
|
|
@@ -1524,6 +1625,13 @@ class Client extends EventEmitter {
|
|
|
1524
1625
|
|
|
1525
1626
|
return this;
|
|
1526
1627
|
}
|
|
1628
|
+
|
|
1629
|
+
setNoDelay(noDelay) {
|
|
1630
|
+
if (this._sock && typeof this._sock.setNoDelay === 'function')
|
|
1631
|
+
this._sock.setNoDelay(noDelay);
|
|
1632
|
+
|
|
1633
|
+
return this;
|
|
1634
|
+
}
|
|
1527
1635
|
}
|
|
1528
1636
|
|
|
1529
1637
|
function openChannel(self, type, opts, cb) {
|
|
@@ -2021,4 +2129,25 @@ function hostKeysProve(client, keys_, cb) {
|
|
|
2021
2129
|
);
|
|
2022
2130
|
}
|
|
2023
2131
|
|
|
2132
|
+
function getKeyAlgos(client, key, serverSigAlgs) {
|
|
2133
|
+
switch (key.type) {
|
|
2134
|
+
case 'ssh-rsa':
|
|
2135
|
+
if (client._protocol._compatFlags & COMPAT.IMPLY_RSA_SHA2_SIGALGS) {
|
|
2136
|
+
if (!Array.isArray(serverSigAlgs))
|
|
2137
|
+
serverSigAlgs = ['rsa-sha2-256', 'rsa-sha2-512'];
|
|
2138
|
+
else
|
|
2139
|
+
serverSigAlgs = ['rsa-sha2-256', 'rsa-sha2-512', ...serverSigAlgs];
|
|
2140
|
+
}
|
|
2141
|
+
if (Array.isArray(serverSigAlgs)) {
|
|
2142
|
+
if (serverSigAlgs.indexOf('rsa-sha2-256') !== -1)
|
|
2143
|
+
return [['rsa-sha2-256', 'sha256']];
|
|
2144
|
+
if (serverSigAlgs.indexOf('rsa-sha2-512') !== -1)
|
|
2145
|
+
return [['rsa-sha2-512', 'sha512']];
|
|
2146
|
+
if (serverSigAlgs.indexOf('ssh-rsa') === -1)
|
|
2147
|
+
return [];
|
|
2148
|
+
}
|
|
2149
|
+
return [['ssh-rsa', 'sha1']];
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
|
|
2024
2153
|
module.exports = Client;
|