From 280c421ae4ab0f4c01178cc882e96ff757f56082 Mon Sep 17 00:00:00 2001 From: Eugene Pankov Date: Fri, 3 Jan 2020 18:40:43 +0100 Subject: [PATCH] use node-sshpk for better key parsing (fixes #1956, fixes #1612, fixes #1191, fixes #1788, fixes #1300) --- terminus-ssh/package.json | 1 + terminus-ssh/src/services/ssh.service.ts | 45 +++++++++++-------- terminus-ssh/yarn.lock | 55 ++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 23 deletions(-) diff --git a/terminus-ssh/package.json b/terminus-ssh/package.json index 8eea8e3d..be5cf666 100644 --- a/terminus-ssh/package.json +++ b/terminus-ssh/package.json @@ -21,6 +21,7 @@ "@types/ssh2": "^0.5.35", "ssh2": "^0.8.2", "ssh2-streams": "^0.4.2", + "sshpk": "^1.16.1", "terminus-terminal": "^1.0.98-nightly.0" }, "peerDependencies": { diff --git a/terminus-ssh/src/services/ssh.service.ts b/terminus-ssh/src/services/ssh.service.ts index 37cf81ab..d7c8256b 100644 --- a/terminus-ssh/src/services/ssh.service.ts +++ b/terminus-ssh/src/services/ssh.service.ts @@ -3,6 +3,7 @@ import { NgbModal } from '@ng-bootstrap/ng-bootstrap' import { Client } from 'ssh2' import * as fs from 'mz/fs' import * as path from 'path' +import * as sshpk from 'sshpk' import { ToastrService } from 'ngx-toastr' import { AppService, HostAppService, Platform, Logger, LogService } from 'terminus-core' import { SSHConnection, SSHSession } from '../api' @@ -50,7 +51,6 @@ export class SSHService { async connectSession (session: SSHSession, logCallback?: (s: any) => void): Promise { let privateKey: string|null = null - let privateKeyPassphrase: string|null = null let privateKeyPath = session.connection.privateKey if (!logCallback) { @@ -71,6 +71,7 @@ export class SSHService { } if (privateKeyPath) { + log(`Loading private key from ${privateKeyPath}`) try { privateKey = (await fs.readFile(privateKeyPath)).toString() } catch (error) { @@ -79,24 +80,31 @@ export class SSHService { } if (privateKey) { - log(`Loading private key from ${privateKeyPath}`) + let parsedKey: any = null + try { + parsedKey = sshpk.parsePrivateKey(privateKey, 'auto') + } catch (e) { + if (e instanceof sshpk.KeyEncryptedError) { + const modal = this.ngbModal.open(PromptModalComponent) + log('Key requires passphrase') + modal.componentInstance.prompt = 'Private key passphrase' + modal.componentInstance.password = true + let passphrase = '' + try { + const result = await modal.result + passphrase = result?.value + } catch (e) { } + parsedKey = sshpk.parsePrivateKey( + privateKey, + 'auto', + { passphrase: passphrase } + ) + } else { + throw e + } + } - let encrypted = privateKey.includes('ENCRYPTED') - if (privateKeyPath.toLowerCase().endsWith('.ppk')) { - encrypted = encrypted || privateKey.includes('Encryption:') && !privateKey.includes('Encryption: none') - } - if (encrypted) { - const modal = this.ngbModal.open(PromptModalComponent) - log('Key requires passphrase') - modal.componentInstance.prompt = 'Private key passphrase' - modal.componentInstance.password = true - try { - const result = await modal.result - if (result) { - privateKeyPassphrase = result.value - } - } catch (e) { } - } + privateKey = parsedKey!.toString('ssh') } } @@ -167,7 +175,6 @@ export class SSHService { username: session.connection.user, password: session.connection.privateKey ? undefined : '', privateKey: privateKey || undefined, - passphrase: privateKeyPassphrase || undefined, tryKeyboard: true, agent: agent || undefined, agentForward: !!agent, diff --git a/terminus-ssh/yarn.lock b/terminus-ssh/yarn.lock index e4288fb2..2432fb51 100644 --- a/terminus-ssh/yarn.lock +++ b/terminus-ssh/yarn.lock @@ -27,21 +27,53 @@ "@types/node" "*" "@types/ssh2-streams" "*" -asn1@~0.2.0: +asn1@~0.2.0, asn1@~0.2.3: version "0.2.4" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.4.tgz#8d2475dfab553bb33e77b54e59e880bb8ce23136" integrity sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg== dependencies: safer-buffer "~2.1.0" -bcrypt-pbkdf@^1.0.2: +assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + integrity sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU= + +bcrypt-pbkdf@^1.0.0, bcrypt-pbkdf@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz#a4301d389b6a43f9b67ff3ca11a3f6637e360e9e" integrity sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4= dependencies: tweetnacl "^0.14.3" -safer-buffer@~2.1.0: +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + integrity sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA= + dependencies: + assert-plus "^1.0.0" + +ecc-jsbn@~0.1.1: + version "0.1.2" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" + integrity sha1-OoOpBOVDUyh4dMVkt1SThoSamMk= + dependencies: + jsbn "~0.1.0" + safer-buffer "^2.1.0" + +getpass@^0.1.1: + version "0.1.7" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.7.tgz#5eff8e3e684d569ae4cb2b1282604e8ba62149fa" + integrity sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo= + dependencies: + assert-plus "^1.0.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= + +safer-buffer@^2.0.2, safer-buffer@^2.1.0, safer-buffer@~2.1.0: version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -62,6 +94,21 @@ ssh2@^0.8.2: dependencies: ssh2-streams "~0.4.8" +sshpk@^1.16.1: + version "1.16.1" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.16.1.tgz#fb661c0bef29b39db40769ee39fa70093d6f6877" + integrity sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg== + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + bcrypt-pbkdf "^1.0.0" + dashdash "^1.12.0" + ecc-jsbn "~0.1.1" + getpass "^0.1.1" + jsbn "~0.1.0" + safer-buffer "^2.0.2" + tweetnacl "~0.14.0" + streamsearch@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-0.1.2.tgz#808b9d0e56fc273d809ba57338e929919a1a9f1a" @@ -72,7 +119,7 @@ terminus-terminal@^1.0.98-nightly.0: resolved "https://registry.yarnpkg.com/terminus-terminal/-/terminus-terminal-1.0.98-nightly.0.tgz#10df71b0a81adf76a076fb21a91c859dd2f8bef7" integrity sha512-JLxkeoQkORcfe6cRW6BJF5ZPSbvKA8IWUAb7fzBONVmNfRKj2Mq/uYPy76UXsdmb9F1n+rYIg+DShNp57asMKA== -tweetnacl@^0.14.3: +tweetnacl@^0.14.3, tweetnacl@~0.14.0: version "0.14.5" resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" integrity sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=