@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 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
- curAuth.agentCtx.sign(key, buf, {}, (err, signed) => {
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
- proto.authPK(username, curAuth.key);
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
- case 'hostbased':
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;
package/lib/index.js CHANGED
@@ -33,6 +33,7 @@ module.exports = {
33
33
  Server: require('./server.js'),
34
34
  utils: {
35
35
  parseKey,
36
+ ...require('./keygen.js'),
36
37
  sftp: {
37
38
  flagsToString,
38
39
  OPEN_MODE,