From 946b54f2e630495b13d9db527e31352ff491cc9c Mon Sep 17 00:00:00 2001 From: "Scott E. Graves" Date: Wed, 17 Apr 2019 21:16:57 -0500 Subject: [PATCH] #21: Add signature validation during installations [partial] --- public/logo.xcf => logo.xcf | Bin package.json | 3 +- public/electron.js | 111 +++++++++++++++++------------------- public/update_linux.sh | 8 +++ src/helpers.js | 36 +++++++++--- src/index.css | 2 +- 6 files changed, 92 insertions(+), 68 deletions(-) rename public/logo.xcf => logo.xcf (100%) create mode 100644 public/update_linux.sh diff --git a/public/logo.xcf b/logo.xcf similarity index 100% rename from public/logo.xcf rename to logo.xcf diff --git a/package.json b/package.json index 0f8be29..4575cdb 100644 --- a/package.json +++ b/package.json @@ -65,7 +65,8 @@ "build/**/*", "node_modules/**/*", "src/helpers.js", - "public/detect_linux.sh" + "public/detect_linux.sh", + "public/install_linux.sh" ], "linux": { "category": "Utility", diff --git a/public/electron.js b/public/electron.js index 3c86304..783f14f 100644 --- a/public/electron.js +++ b/public/electron.js @@ -16,6 +16,7 @@ require.extensions['.sh'] = function (module, filename) { module.exports = fs.readFileSync(filename, 'utf8'); }; const detectScript = require('./detect_linux.sh'); +const installScript = require('./update_linux.sh'); const publicKey = '-----BEGIN PUBLIC KEY-----\n' + 'MIIEIjANBgkqhkiG9w0BAQEFAAOCBA8AMIIECgKCBAEKfZmq5mMAtD4kSt2Gc/5J\n' + @@ -718,53 +719,63 @@ ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => { } }; - if (os.platform() === 'win32') { - const executeInstall = () => { - helpers - .executeAsync(data.Source) - .then(() => { - cleanupFiles(); - closeApplication(); - }) - .catch(error => { - cleanupFiles(); - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { - Source: data.Source, - }, error); - }); - }; - if (hasSignature) { - helpers - .verifySignature(data.Source, tempSig, tempPub) - .then(() => { - executeInstall(); - }) - .catch(() => { - cleanupFiles(); - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { - Source: data.Source, - }, 'Failed to verify installation package signature'); - }); - } else { // TODO Check Sha256 - executeInstall(); + let command; + let args; + const platform = os.platform(); + if (platform === 'win32') { + command = data.Source; + } else if (platform === 'darwin') { + command = 'open'; + args = ['-a', 'Finder', data.Source]; + } else if (platform === 'linux') { + try { + const execPath = path.join(os.tmpdir(), 'install_linux.sh'); + fs.writeFileSync(execPath, installScript); + fs.chmodSync(execPath, '750'); + command = execPath; + args = [data.Source]; + } catch (e) { + cleanupFiles(); + standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { + Source: data.Source, + }, e); } - } else if (data.Source.toLocaleLowerCase().endsWith('.dmg')) { + } else { + cleanupFiles(); + standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { + Source: data.Source, + }, Error('Platform not supported: ' + os.platform())); + } + + if (command) { const executeInstall = () => { helpers - .executeAsync('open', ['-a', 'Finder', data.Source]) - .then(() => { - cleanupFiles(); - closeApplication(); - }) - .catch(error => { - cleanupFiles(); - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { - Source: data.Source, - }, error); - }); + .executeAsync(command, args) + .then(() => { + cleanupFiles(); + closeApplication(); + }) + .catch(error => { + cleanupFiles(); + standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { + Source: data.Source, + }, error); + }); }; - if (hasHash) { + if (hasSignature) { + helpers + .verifySignature(data.Source, tempSig, tempPub) + .then(() => { + executeInstall(); + }) + .catch(() => { + cleanupFiles(); + standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { + Source: data.Source, + }, 'Failed to verify installation package signature'); + }); + } else if (hasHash) { helpers .verifyHash(data.Source, data.Sha256) .then(()=> { @@ -779,22 +790,6 @@ ipcMain.on(Constants.IPC_Install_Upgrade, (event, data) => { } else { executeInstall(); } - } else if (data.Source.toLocaleLowerCase().endsWith('.appimage')) { - cleanupFiles(); - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { - Source: data.Source, - }, Error('Not implemented upgrade: ' + data.Source)); - // TODO Generate and execute script with delay - /*helpers - .executeAsync(data.Source) - .then(() => { - closeApplication(); - }) - .catch(error => { - standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { - Source: data.Source, - }, error); - });*/ } else { cleanupFiles(); standardIPCReply(event, Constants.IPC_Install_Upgrade_Reply, { diff --git a/public/update_linux.sh b/public/update_linux.sh new file mode 100644 index 0000000..dcd771d --- /dev/null +++ b/public/update_linux.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +sleep 5 +chmod +x "$1" +"$1"& + +sleep 1 +rm -f "$0" \ No newline at end of file diff --git a/src/helpers.js b/src/helpers.js index 887b489..ec19112 100644 --- a/src/helpers.js +++ b/src/helpers.js @@ -3,6 +3,7 @@ const path = require('path'); const os = require('os'); const axios = require('axios/index'); const exec = require('child_process').exec; +const execFile = require('child_process').execFile; const spawn = require('child_process').spawn; const Constants = require('./constants'); const RandomString = require('randomstring'); @@ -547,8 +548,7 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => { return new Promise((resolve, reject) => { const executeVerify = openssl => { //openssl dgst -sha256 -verify $pubkeyfile -signature signature.sig file - const command = '"' + openssl + '" dgst -sha256 -verify "' + publicKeyFile + '" -signature "' + signatureFile + '"'; - exec(command, res => { + execFile(openssl, ['dgst', '-sha256', '-verify', publicKeyFile, '-signature', signatureFile], res => { if (res.code !== 0) { reject(res); } else { @@ -571,14 +571,15 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => { if (err) { reject(err); } else { - const openssl = path.join(item.value(), 'bin', 'openssl.exe'); - executeVerify(openssl); + executeVerify(path.join(item.value(), 'bin', 'openssl.exe')); } }); } else { reject('Failed to locate \'openssl.exe\''); } }); + } else if (os.platform() === 'linux') { + executeVerify('openssl'); } else { reject('Platform not supported: ' + os.platform()) } @@ -587,13 +588,32 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => { module.exports.verifyHash = (file, hash) => { return new Promise((resolve, reject) => { - if (os.platform() === 'darwin') { - reject('Not implemented'); - } else if (os.platform() === 'linux') { - reject('Not implemented'); + const platform = os.platform(); + let command; + let args; + if (platform === 'darwin') { + command = 'shasum'; + args = ['-b', '-a', '256', file]; + } else if (platform === 'linux') { + command = 'sha256sum'; + args = ['-b', file, '-z']; } else { reject('Platform not supported: ' + os.platform()) } + if (command) { + execFile(command, args, (err, stdout) => { + if (err) { + reject(err); + } else { + const hash2 = stdout.split(' ')[0].trim().toLowerCase(); + if (hash2 === hash.toLowerCase()) { + resolve(); + } else { + reject('Checksum failed for file'); + } + } + }); + } }); }; \ No newline at end of file diff --git a/src/index.css b/src/index.css index 8fd7ee9..6ab0a31 100644 --- a/src/index.css +++ b/src/index.css @@ -15,7 +15,7 @@ --heading_other_text_color: var(--heading_text_color); --text_color_transition: color 0.3s; - --default_font_size: 4vmin + --default_font_size: 4.8vmin } * {