bitwarden-estensione-browser/apps/desktop/src/main/native-messaging.main.ts

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

357 lines
11 KiB
TypeScript
Raw Normal View History

2021-02-03 19:21:22 +01:00
import { existsSync, promises as fs } from "fs";
import { Socket } from "net";
2021-02-03 19:21:22 +01:00
import { homedir, userInfo } from "os";
import * as path from "path";
import * as util from "util";
2020-10-21 16:48:40 +02:00
import { ipcMain } from "electron";
2022-02-24 20:50:19 +01:00
import * as ipc from "node-ipc";
2022-06-14 17:10:53 +02:00
import { LogService } from "@bitwarden/common/abstractions/log.service";
import { WindowMain } from "./window.main";
export class NativeMessagingMain {
private connected: Socket[] = [];
private socket: any;
2021-12-20 15:47:17 +01:00
constructor(
private logService: LogService,
private windowMain: WindowMain,
private userPath: string,
private exePath: string
2021-12-20 15:47:17 +01:00
) {}
async listen() {
ipc.config.id = "bitwarden";
ipc.config.retry = 1500;
2021-01-26 19:11:36 +01:00
if (process.platform === "darwin") {
if (!existsSync(`${homedir()}/tmp`)) {
await fs.mkdir(`${homedir()}/tmp`);
2021-12-20 15:47:17 +01:00
}
ipc.config.socketRoot = `${homedir()}/tmp/`;
2021-12-20 15:47:17 +01:00
}
ipc.serve(() => {
ipc.server.on("message", (data: any, socket: any) => {
this.socket = socket;
this.windowMain.win.webContents.send("nativeMessaging", data);
2021-12-20 15:47:17 +01:00
});
2021-01-26 19:11:36 +01:00
ipcMain.on("nativeMessagingReply", (event, msg) => {
if (this.socket != null && msg != null) {
this.send(msg, this.socket);
2021-01-26 19:11:36 +01:00
}
2021-12-20 15:47:17 +01:00
});
ipc.server.on("connect", (socket: Socket) => {
this.connected.push(socket);
});
ipc.server.on("socket.disconnected", (socket, destroyedSocketID) => {
const index = this.connected.indexOf(socket);
if (index > -1) {
this.connected.splice(index, 1);
}
this.socket = null;
ipc.log("client " + destroyedSocketID + " has disconnected!");
2021-12-20 15:47:17 +01:00
});
});
ipc.server.start();
2021-12-20 15:47:17 +01:00
}
stop() {
ipc.server.stop();
// Kill all existing connections
this.connected.forEach((socket) => {
if (!socket.destroyed) {
socket.destroy();
2021-12-20 15:47:17 +01:00
}
});
}
send(message: object, socket: any) {
ipc.server.emit(socket, "message", message);
2021-12-20 15:47:17 +01:00
}
generateManifests() {
const baseJson = {
name: "com.8bit.bitwarden",
description: "Bitwarden desktop <-> browser bridge",
path: this.binaryPath(),
2021-12-20 15:47:17 +01:00
type: "stdio",
};
2020-11-25 15:25:18 +01:00
const firefoxJson = {
...baseJson,
...{ allowed_extensions: ["{446900e4-71c2-419f-a6a7-df9c091e268b}"] },
2021-12-20 15:47:17 +01:00
};
2020-11-25 15:25:18 +01:00
const chromeJson = {
...baseJson,
2021-12-20 15:47:17 +01:00
...{
2020-11-25 15:25:18 +01:00
allowed_origins: [
"chrome-extension://nngceckbapebfimnlniiiahkandclblb/",
"chrome-extension://jbkfoedolllekgbhcbcoahefnbanhhlh/",
2021-02-08 19:58:44 +01:00
"chrome-extension://ccnckbpmaceehanjmeomladnmlffdjgn/",
2021-12-20 15:47:17 +01:00
],
},
};
switch (process.platform) {
2022-02-24 20:50:19 +01:00
case "win32": {
2020-10-21 16:48:40 +02:00
const destination = path.join(this.userPath, "browsers");
this.writeManifest(path.join(destination, "firefox.json"), firefoxJson);
this.writeManifest(path.join(destination, "chrome.json"), chromeJson);
2021-12-20 15:47:17 +01:00
2021-01-04 20:31:33 +01:00
this.createWindowsRegistry(
"HKLM\\SOFTWARE\\Mozilla\\Firefox",
"HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
path.join(destination, "firefox.json")
2021-12-20 15:47:17 +01:00
);
2021-01-04 20:31:33 +01:00
this.createWindowsRegistry(
"HKCU\\SOFTWARE\\Google\\Chrome",
"HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden",
path.join(destination, "chrome.json")
2021-12-20 15:47:17 +01:00
);
break;
2022-02-24 20:50:19 +01:00
}
case "darwin": {
2021-03-15 05:11:56 +01:00
const nmhs = this.getDarwinNMHS();
for (const [key, value] of Object.entries(nmhs)) {
if (existsSync(value)) {
const p = path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json");
2021-12-20 15:47:17 +01:00
let manifest: any = chromeJson;
2021-03-15 05:11:56 +01:00
if (key === "Firefox") {
manifest = firefoxJson;
}
this.writeManifest(p, manifest).catch((e) =>
this.logService.error(`Error writing manifest for ${key}. ${e}`)
2021-02-03 19:21:22 +01:00
);
2020-11-25 15:25:18 +01:00
} else {
2021-01-04 20:31:33 +01:00
this.logService.warning(`${key} not found skipping.`);
2021-12-20 15:47:17 +01:00
}
}
break;
2022-02-24 20:50:19 +01:00
}
case "linux":
if (existsSync(`${this.homedir()}/.mozilla/`)) {
this.writeManifest(
`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`,
firefoxJson
2021-03-09 02:49:20 +01:00
);
}
if (existsSync(`${this.homedir()}/.config/google-chrome/`)) {
this.writeManifest(
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`,
chromeJson
2021-03-09 02:49:20 +01:00
);
}
if (existsSync(`${this.homedir()}/.config/microsoft-edge/`)) {
this.writeManifest(
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`,
chromeJson
2021-03-15 05:11:56 +01:00
);
}
2021-12-20 15:47:17 +01:00
break;
default:
break;
2021-03-15 05:11:56 +01:00
}
2021-12-20 15:47:17 +01:00
}
[SG-520] Native messaging handler (#3566) * [SG-523] Base test runner app for native messages (#3269) * Base test runner app for native messages * Remove default test script * Add case for canceled status * Modify to allow usage of libs crypto services and functions * Small adjustments * Handshake request (#3277) * Handshake request * Fix capitalization * Update info text * lock node-ipc to 9.2.1 * [SG-569] Native Messaging settings bug (#3285) * Fix bug where updating setting wasn't starting the native messaging listener * Update test runner error message * [SG-532] Implement Status command in Native Messaging Service (#3310) * Status command start * Refactor ipc test service and add status command * fixed linter errors * Move types into a model file * Cleanup and comments * Fix auth status condition * Remove .vscode settings file. Fix this in a separate work item * Add active field to status response * Extract native messaging types into their own files * Remove experimental decorators * Turn off no console lint rule for the test runner * Casing fix * Models import casing fixes * Remove in progress file (merge error) * Move models to their own folder and add index.ts * Remove file that got un-deleted * Remove file that will be added in separate command * Fix imports that got borked * [SG-533] Implement bw-credential-retrieval (#3334) * Status command start * Refactor ipc test service and add status command * fixed linter errors * Move types into a model file * Cleanup and comments * Fix auth status condition * Remove .vscode settings file. Fix this in a separate work item * Implement bw-credential-retrieval * Add active field to status response * Extract native messaging types into their own files * Remove experimental decorators * Turn off no console lint rule for the test runner * Casing fix * Models import casing fixes * Add error handling for passing a bad public key to handshake * [SG-534] and [SG-535] Implement Credential Create and Update commands (#3342) * Status command start * Refactor ipc test service and add status command * fixed linter errors * Move types into a model file * Cleanup and comments * Fix auth status condition * Remove .vscode settings file. Fix this in a separate work item * Implement bw-credential-retrieval * Add active field to status response * Add bw-credential-create * Better response handling in test runner * Extract native messaging types into their own files * Remove experimental decorators * Turn off no console lint rule for the test runner * Casing fix * Models import casing fixes * bw-cipher-create move type into its own file * Use LogUtils for all logging * Implement bw-credential-update * Give naming conventions for types * Rename file correctly * Update handleEncyptedMessage with EncString changes * [SG-626] Fix Desktop app not showing updated credentials from native messages (#3380) * Add MessagingService to send messages on login create and update * Add `not-active-user` error to create and update and other refactors * [SG-536] Implement bw-generate-password (#3370) * implement bw-generate-password * Fix merge conflict resolution errors * Update apps/desktop/native-messaging-test-runner/src/bw-generate-password.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Logging improvements * Add NativeMessagingVersion enum * Add version check in NativeMessagingHandler Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Refactor account status checks and check for locked state in generate command (#3461) * Add feawture flag to show/hide ddg setting (#3506) * [SG-649] Add confirmation dialog and tweak shared key retrieval (#3451) * Add confirmation dialog when completing handshake * Copy updates for dialog * HandshakeResponse type fixes * Add longer timeout for handshake command * [SG-663] RefactorNativeMessagingHandlerService and strengthen typing (#3551) * NativeMessageHandlerService refactor and additional types * Return empty array if no uri to retrieve command * Move commands from test runner into a separate folder * Fix bug where confirmation dialog messes with styling * Enable DDG feature * Fix generated password not saving to history * Take credentialId as parameter to update * Add applicationName to handshake payload * Add warning text to confirmation modal Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-09-23 21:47:17 +02:00
generateDdgManifests() {
const manifest = {
name: "com.8bit.bitwarden",
description: "Bitwarden desktop <-> DuckDuckGo bridge",
path: this.binaryPath(),
type: "stdio",
};
switch (process.platform) {
case "darwin": {
/* eslint-disable-next-line no-useless-escape */
const path = `${this.homedir()}/Library/Containers/com.duckduckgo.macos.browser/Data/Library/Application\ Support/NativeMessagingHosts/com.8bit.bitwarden.json`;
this.writeManifest(path, manifest).catch((e) =>
this.logService.error(`Error writing manifest for DuckDuckGo. ${e}`)
);
break;
}
default:
break;
}
}
removeManifests() {
switch (process.platform) {
case "win32":
2020-10-21 16:48:40 +02:00
fs.unlink(path.join(this.userPath, "browsers", "firefox.json"));
fs.unlink(path.join(this.userPath, "browsers", "chrome.json"));
this.deleteWindowsRegistry(
"HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden"
2021-12-20 15:47:17 +01:00
);
this.deleteWindowsRegistry(
2021-01-04 20:31:33 +01:00
"HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden"
2021-12-20 15:47:17 +01:00
);
break;
2022-02-24 20:50:19 +01:00
case "darwin": {
2021-03-15 05:11:56 +01:00
const nmhs = this.getDarwinNMHS();
2022-02-24 20:50:19 +01:00
for (const [, value] of Object.entries(nmhs)) {
const p = path.join(value, "NativeMessagingHosts", "com.8bit.bitwarden.json");
2021-03-15 05:11:56 +01:00
if (existsSync(p)) {
fs.unlink(p);
2021-12-20 15:47:17 +01:00
}
}
break;
2022-02-24 20:50:19 +01:00
}
case "linux":
2021-12-20 15:47:17 +01:00
if (
existsSync(`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`)
2021-12-20 15:47:17 +01:00
) {
fs.unlink(`${this.homedir()}/.mozilla/native-messaging-hosts/com.8bit.bitwarden.json`);
2021-12-20 15:47:17 +01:00
}
2021-03-15 05:11:56 +01:00
if (
existsSync(
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`
2021-12-20 15:47:17 +01:00
)
) {
fs.unlink(
`${this.homedir()}/.config/google-chrome/NativeMessagingHosts/com.8bit.bitwarden.json`
);
}
2021-12-20 15:47:17 +01:00
if (
2021-03-09 02:49:20 +01:00
existsSync(
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`
)
) {
fs.unlink(
`${this.homedir()}/.config/microsoft-edge/NativeMessagingHosts/com.8bit.bitwarden.json`
);
}
2021-12-20 15:47:17 +01:00
break;
default:
break;
}
}
[SG-520] Native messaging handler (#3566) * [SG-523] Base test runner app for native messages (#3269) * Base test runner app for native messages * Remove default test script * Add case for canceled status * Modify to allow usage of libs crypto services and functions * Small adjustments * Handshake request (#3277) * Handshake request * Fix capitalization * Update info text * lock node-ipc to 9.2.1 * [SG-569] Native Messaging settings bug (#3285) * Fix bug where updating setting wasn't starting the native messaging listener * Update test runner error message * [SG-532] Implement Status command in Native Messaging Service (#3310) * Status command start * Refactor ipc test service and add status command * fixed linter errors * Move types into a model file * Cleanup and comments * Fix auth status condition * Remove .vscode settings file. Fix this in a separate work item * Add active field to status response * Extract native messaging types into their own files * Remove experimental decorators * Turn off no console lint rule for the test runner * Casing fix * Models import casing fixes * Remove in progress file (merge error) * Move models to their own folder and add index.ts * Remove file that got un-deleted * Remove file that will be added in separate command * Fix imports that got borked * [SG-533] Implement bw-credential-retrieval (#3334) * Status command start * Refactor ipc test service and add status command * fixed linter errors * Move types into a model file * Cleanup and comments * Fix auth status condition * Remove .vscode settings file. Fix this in a separate work item * Implement bw-credential-retrieval * Add active field to status response * Extract native messaging types into their own files * Remove experimental decorators * Turn off no console lint rule for the test runner * Casing fix * Models import casing fixes * Add error handling for passing a bad public key to handshake * [SG-534] and [SG-535] Implement Credential Create and Update commands (#3342) * Status command start * Refactor ipc test service and add status command * fixed linter errors * Move types into a model file * Cleanup and comments * Fix auth status condition * Remove .vscode settings file. Fix this in a separate work item * Implement bw-credential-retrieval * Add active field to status response * Add bw-credential-create * Better response handling in test runner * Extract native messaging types into their own files * Remove experimental decorators * Turn off no console lint rule for the test runner * Casing fix * Models import casing fixes * bw-cipher-create move type into its own file * Use LogUtils for all logging * Implement bw-credential-update * Give naming conventions for types * Rename file correctly * Update handleEncyptedMessage with EncString changes * [SG-626] Fix Desktop app not showing updated credentials from native messages (#3380) * Add MessagingService to send messages on login create and update * Add `not-active-user` error to create and update and other refactors * [SG-536] Implement bw-generate-password (#3370) * implement bw-generate-password * Fix merge conflict resolution errors * Update apps/desktop/native-messaging-test-runner/src/bw-generate-password.ts Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Logging improvements * Add NativeMessagingVersion enum * Add version check in NativeMessagingHandler Co-authored-by: Addison Beck <addisonbeck1@gmail.com> * Refactor account status checks and check for locked state in generate command (#3461) * Add feawture flag to show/hide ddg setting (#3506) * [SG-649] Add confirmation dialog and tweak shared key retrieval (#3451) * Add confirmation dialog when completing handshake * Copy updates for dialog * HandshakeResponse type fixes * Add longer timeout for handshake command * [SG-663] RefactorNativeMessagingHandlerService and strengthen typing (#3551) * NativeMessageHandlerService refactor and additional types * Return empty array if no uri to retrieve command * Move commands from test runner into a separate folder * Fix bug where confirmation dialog messes with styling * Enable DDG feature * Fix generated password not saving to history * Take credentialId as parameter to update * Add applicationName to handshake payload * Add warning text to confirmation modal Co-authored-by: Addison Beck <addisonbeck1@gmail.com>
2022-09-23 21:47:17 +02:00
removeDdgManifests() {
switch (process.platform) {
case "darwin": {
/* eslint-disable-next-line no-useless-escape */
const path = `${this.homedir()}/Library/Containers/com.duckduckgo.macos.browser/Data/Library/Application\ Support/NativeMessagingHosts/com.8bit.bitwarden.json`;
if (existsSync(path)) {
fs.unlink(path);
}
break;
}
default:
break;
}
}
2021-03-15 05:11:56 +01:00
private getDarwinNMHS() {
2022-02-24 20:50:19 +01:00
/* eslint-disable no-useless-escape */
2021-12-20 15:47:17 +01:00
return {
Firefox: `${this.homedir()}/Library/Application\ Support/Mozilla/`,
Chrome: `${this.homedir()}/Library/Application\ Support/Google/Chrome/`,
"Chrome Beta": `${this.homedir()}/Library/Application\ Support/Google/Chrome\ Beta/`,
"Chrome Dev": `${this.homedir()}/Library/Application\ Support/Google/Chrome\ Dev/`,
"Chrome Canary": `${this.homedir()}/Library/Application\ Support/Google/Chrome\ Canary/`,
Chromium: `${this.homedir()}/Library/Application\ Support/Chromium/`,
"Microsoft Edge": `${this.homedir()}/Library/Application\ Support/Microsoft\ Edge/`,
"Microsoft Edge Beta": `${this.homedir()}/Library/Application\ Support/Microsoft\ Edge\ Beta/`,
"Microsoft Edge Dev": `${this.homedir()}/Library/Application\ Support/Microsoft\ Edge\ Dev/`,
"Microsoft Edge Canary": `${this.homedir()}/Library/Application\ Support/Microsoft\ Edge\ Canary/`,
Vivaldi: `${this.homedir()}/Library/Application\ Support/Vivaldi/`,
2021-12-20 15:47:17 +01:00
};
2022-02-24 20:50:19 +01:00
/* eslint-enable no-useless-escape */
2021-12-20 15:47:17 +01:00
}
private async writeManifest(destination: string, manifest: object) {
2021-03-09 02:49:20 +01:00
if (!existsSync(path.dirname(destination))) {
2021-01-04 20:31:33 +01:00
await fs.mkdir(path.dirname(destination));
2021-12-20 15:47:17 +01:00
}
fs.writeFile(destination, JSON.stringify(manifest, null, 2)).catch(this.logService.error);
2021-12-20 15:47:17 +01:00
}
private binaryPath() {
if (process.platform === "win32") {
return path.join(path.dirname(this.exePath), "resources", "native-messaging.bat");
}
return this.exePath;
2021-12-20 15:47:17 +01:00
}
private getRegeditInstance() {
2022-02-24 20:50:19 +01:00
// eslint-disable-next-line
const regedit = require("regedit");
regedit.setExternalVBSLocation(path.join(path.dirname(this.exePath), "resources/regedit/vbs"));
return regedit;
}
private async createWindowsRegistry(check: string, location: string, jsonFile: string) {
const regedit = this.getRegeditInstance();
2021-02-03 19:21:22 +01:00
const list = util.promisify(regedit.list);
const createKey = util.promisify(regedit.createKey);
const putValue = util.promisify(regedit.putValue);
2021-01-04 20:31:33 +01:00
this.logService.debug(`Adding registry: ${location}`);
// Check installed
try {
2021-02-03 19:21:22 +01:00
await list(check);
} catch {
2021-03-15 05:11:56 +01:00
this.logService.warning(`Not finding registry ${check} skipping.`);
2021-12-20 15:47:17 +01:00
return;
}
try {
await createKey(location);
2021-12-20 15:47:17 +01:00
// Insert path to manifest
const obj: any = {};
obj[location] = {
default: {
value: jsonFile,
type: "REG_DEFAULT",
},
2021-02-03 19:21:22 +01:00
};
2021-12-20 15:47:17 +01:00
return putValue(obj);
} catch (error) {
this.logService.error(error);
}
2021-12-20 15:47:17 +01:00
}
private async deleteWindowsRegistry(key: string) {
const regedit = this.getRegeditInstance();
const list = util.promisify(regedit.list);
const deleteKey = util.promisify(regedit.deleteKey);
2021-02-03 19:21:22 +01:00
this.logService.debug(`Removing registry: ${key}`);
try {
await list(key);
await deleteKey(key);
} catch {
this.logService.error(`Unable to delete registry key: ${key}`);
}
2021-12-20 15:47:17 +01:00
}
private homedir() {
if (process.platform === "darwin") {
return userInfo().homedir;
} else {
return homedir();
}
2021-12-20 15:47:17 +01:00
}
}