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