Skip to content

Commit

Permalink
[PM-14270] Use rust to access windows registry (#11413)
Browse files Browse the repository at this point in the history
  • Loading branch information
dani-garcia authored Nov 4, 2024
1 parent f6755da commit 2e6ed4a
Show file tree
Hide file tree
Showing 12 changed files with 113 additions and 134 deletions.
4 changes: 2 additions & 2 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@
"reviewers": ["team:team-admin-console-dev"]
},
{
"matchPackageNames": ["@types/node-ipc", "node-ipc", "qrious", "regedit"],
"matchPackageNames": ["@types/node-ipc", "node-ipc", "qrious"],
"description": "Auth owned dependencies",
"commitMessagePrefix": "[deps] Auth:",
"reviewers": ["team:team-auth-dev"]
Expand Down Expand Up @@ -258,5 +258,5 @@
"reviewers": ["team:team-vault-dev"]
}
],
"ignoreDeps": ["@types/koa-bodyparser", "bootstrap", "node-ipc", "node", "npm", "regedit"]
"ignoreDeps": ["@types/koa-bodyparser", "bootstrap", "node-ipc", "node", "npm"]
}
32 changes: 31 additions & 1 deletion apps/desktop/desktop_native/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions apps/desktop/desktop_native/napi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,5 +21,8 @@ napi-derive = "=2.16.12"
tokio = { version = "1.38.0" }
tokio-util = "0.7.11"

[target.'cfg(windows)'.dependencies]
windows-registry = "=0.3.0"

[build-dependencies]
napi-build = "=2.1.3"
4 changes: 4 additions & 0 deletions apps/desktop/desktop_native/napi/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ export declare namespace powermonitors {
export function onLock(callback: (err: Error | null, ) => any): Promise<void>
export function isLockMonitorAvailable(): Promise<boolean>
}
export declare namespace windows_registry {
export function createKey(key: string, subkey: string, value: string): Promise<void>
export function deleteKey(key: string, subkey: string): Promise<void>
}
export declare namespace ipc {
export interface IpcMessage {
clientId: number
Expand Down
18 changes: 18 additions & 0 deletions apps/desktop/desktop_native/napi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#[macro_use]
extern crate napi_derive;

mod registry;

#[napi]
pub mod passwords {
/// Fetch the stored password from the keychain.
Expand Down Expand Up @@ -190,6 +193,21 @@ pub mod powermonitors {

}

#[napi]
pub mod windows_registry {
#[napi]
pub async fn create_key(key: String, subkey: String, value: String) -> napi::Result<()> {
crate::registry::create_key(&key, &subkey, &value)
.map_err(|e| napi::Error::from_reason(e.to_string()))
}

#[napi]
pub async fn delete_key(key: String, subkey: String) -> napi::Result<()> {
crate::registry::delete_key(&key, &subkey)
.map_err(|e| napi::Error::from_reason(e.to_string()))
}
}

#[napi]
pub mod ipc {
use desktop_core::ipc::server::{Message, MessageType};
Expand Down
9 changes: 9 additions & 0 deletions apps/desktop/desktop_native/napi/src/registry/dummy.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use anyhow::{bail, Result};

pub fn create_key(_key: &str, _subkey: &str, _value: &str) -> Result<()> {
bail!("Not implemented")
}

pub fn delete_key(_key: &str, _subkey: &str) -> Result<()> {
bail!("Not implemented")
}
4 changes: 4 additions & 0 deletions apps/desktop/desktop_native/napi/src/registry/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#[cfg_attr(target_os = "windows", path = "windows.rs")]
#[cfg_attr(not(target_os = "windows"), path = "dummy.rs")]
mod internal;
pub use internal::*;
29 changes: 29 additions & 0 deletions apps/desktop/desktop_native/napi/src/registry/windows.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use anyhow::{bail, Result};

fn convert_key(key: &str) -> Result<&'static windows_registry::Key> {
Ok(match key.to_uppercase().as_str() {
"HKEY_CURRENT_USER" | "HKCU" => windows_registry::CURRENT_USER,
"HKEY_LOCAL_MACHINE" | "HKLM" => windows_registry::LOCAL_MACHINE,
"HKEY_CLASSES_ROOT" | "HKCR" => windows_registry::CLASSES_ROOT,
_ => bail!("Invalid key"),
})
}

pub fn create_key(key: &str, subkey: &str, value: &str) -> Result<()> {
let key = convert_key(key)?;

let subkey = key.create(subkey)?;

const DEFAULT: &str = "";
subkey.set_string(DEFAULT, value)?;

Ok(())
}

pub fn delete_key(key: &str, subkey: &str) -> Result<()> {
let key = convert_key(key)?;

key.remove_tree(subkey)?;

Ok(())
}
7 changes: 0 additions & 7 deletions apps/desktop/electron-builder.json
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,6 @@
"electronUpdaterCompatibility": ">=0.0.1",
"target": ["portable", "nsis-web", "appx"],
"sign": "./sign.js",
"extraResources": [
{
"from": "../../node_modules/regedit/vbs",
"to": "regedit/vbs",
"filter": ["**/*"]
}
],
"extraFiles": [
{
"from": "desktop_native/dist/desktop_proxy.${platform}-${arch}.exe",
Expand Down
70 changes: 13 additions & 57 deletions apps/desktop/src/main/native-messaging.main.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
import { existsSync, promises as fs } from "fs";
import { homedir, userInfo } from "os";
import * as path from "path";
import * as util from "util";

import { ipcMain } from "electron";

import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { ipc } from "@bitwarden/desktop-napi";
import { ipc, windows_registry } from "@bitwarden/desktop-napi";

import { isDev } from "../utils";

Expand Down Expand Up @@ -142,12 +141,12 @@ export class NativeMessagingMain {
await this.writeManifest(path.join(destination, "chrome.json"), chromeJson);

const nmhs = this.getWindowsNMHS();
for (const [key, value] of Object.entries(nmhs)) {
for (const [name, [key, subkey]] of Object.entries(nmhs)) {
let manifestPath = path.join(destination, "chrome.json");
if (key === "Firefox") {
if (name === "Firefox") {
manifestPath = path.join(destination, "firefox.json");
}
await this.createWindowsRegistry(value, manifestPath);
await windows_registry.createKey(key, subkey, manifestPath);
}
break;
}
Expand Down Expand Up @@ -225,8 +224,8 @@ export class NativeMessagingMain {
await this.removeIfExists(path.join(this.userPath, "browsers", "chrome.json"));

const nmhs = this.getWindowsNMHS();
for (const [, value] of Object.entries(nmhs)) {
await this.deleteWindowsRegistry(value);
for (const [, [key, subkey]] of Object.entries(nmhs)) {
await windows_registry.deleteKey(key, subkey);
}
break;
}
Expand Down Expand Up @@ -274,11 +273,14 @@ export class NativeMessagingMain {

private getWindowsNMHS() {
return {
Firefox: "HKCU\\SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden",
Chrome: "HKCU\\SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden",
Chromium: "HKCU\\SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden",
Firefox: ["HKCU", "SOFTWARE\\Mozilla\\NativeMessagingHosts\\com.8bit.bitwarden"],
Chrome: ["HKCU", "SOFTWARE\\Google\\Chrome\\NativeMessagingHosts\\com.8bit.bitwarden"],
Chromium: ["HKCU", "SOFTWARE\\Chromium\\NativeMessagingHosts\\com.8bit.bitwarden"],
// Edge uses the same registry key as Chrome as a fallback, but it's has its own separate key as well.
"Microsoft Edge": "HKCU\\SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden",
"Microsoft Edge": [
"HKCU",
"SOFTWARE\\Microsoft\\Edge\\NativeMessagingHosts\\com.8bit.bitwarden",
],
};
}

Expand Down Expand Up @@ -419,52 +421,6 @@ export class NativeMessagingMain {
return path.join(path.dirname(this.exePath), `desktop_proxy${ext}`);
}

private getRegeditInstance() {
// eslint-disable-next-line
const regedit = require("regedit");
regedit.setExternalVBSLocation(path.join(path.dirname(this.exePath), "resources/regedit/vbs"));

return regedit;
}

private async createWindowsRegistry(location: string, jsonFile: string) {
const regedit = this.getRegeditInstance();

const createKey = util.promisify(regedit.createKey);
const putValue = util.promisify(regedit.putValue);

this.logService.debug(`Adding registry: ${location}`);

await createKey(location);

// Insert path to manifest
const obj: any = {};
obj[location] = {
default: {
value: jsonFile,
type: "REG_DEFAULT",
},
};

return putValue(obj);
}

private async deleteWindowsRegistry(key: string) {
const regedit = this.getRegeditInstance();

const list = util.promisify(regedit.list);
const deleteKey = util.promisify(regedit.deleteKey);

this.logService.debug(`Removing registry: ${key}`);

try {
await list(key);
await deleteKey(key);
} catch {
this.logService.error(`Unable to delete registry key: ${key}`);
}
}

private homedir() {
if (process.platform === "darwin") {
return userInfo().homedir;
Expand Down
66 changes: 0 additions & 66 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 2e6ed4a

Please sign in to comment.