mirror of
https://github.com/bitwarden/browser
synced 2025-12-15 07:43:35 +00:00
[BEEEP|PM-25164] Prevent memory dumping on renderer on Linux (#16136)
* Implement libmemory_security
* Cleanup and add script
* Remove duplicate build for flatpak
* Rename to process isolation
* Move to desktop native
* Undo changes in gitignore
* Remove after-pack changes
* Run cargo fmt
* Sort deps
* Attempt to fix windows build
* Update apps/desktop/desktop_native/process_isolation/Cargo.toml
Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>
* Revert "Remove after-pack changes"
This reverts commit c441025587.
* Fix lib process isolation not being included in build
* Fix build
* Attempt to fix build
* Attempt to fix build
* Undo
* Fix library not being included
---------
Co-authored-by: Daniel García <dani-garcia@users.noreply.github.com>
This commit is contained in:
42
apps/desktop/desktop_native/Cargo.lock
generated
42
apps/desktop/desktop_native/Cargo.lock
generated
@@ -754,6 +754,22 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctor"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb"
|
||||||
|
dependencies = [
|
||||||
|
"ctor-proc-macro",
|
||||||
|
"dtor",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ctor-proc-macro"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ctr"
|
name = "ctr"
|
||||||
version = "0.9.2"
|
version = "0.9.2"
|
||||||
@@ -1049,6 +1065,21 @@ version = "1.2.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtor"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "e58a0764cddb55ab28955347b45be00ade43d4d6f3ba4bf3dc354e4ec9432934"
|
||||||
|
dependencies = [
|
||||||
|
"dtor-proc-macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dtor-proc-macro"
|
||||||
|
version = "0.0.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecdsa"
|
name = "ecdsa"
|
||||||
version = "0.16.9"
|
version = "0.16.9"
|
||||||
@@ -1886,7 +1917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3"
|
checksum = "55740c4ae1d8696773c78fdafd5d0e5fe9bc9f1b071c7ba493ba5c413a9184f3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"ctor",
|
"ctor 0.2.9",
|
||||||
"napi-derive",
|
"napi-derive",
|
||||||
"napi-sys",
|
"napi-sys",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
@@ -2559,6 +2590,15 @@ dependencies = [
|
|||||||
"unicode-ident",
|
"unicode-ident",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "process_isolation"
|
||||||
|
version = "0.0.0"
|
||||||
|
dependencies = [
|
||||||
|
"ctor 0.5.0",
|
||||||
|
"desktop_core",
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quick-xml"
|
name = "quick-xml"
|
||||||
version = "0.37.5"
|
version = "0.37.5"
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ members = [
|
|||||||
"core",
|
"core",
|
||||||
"macos_provider",
|
"macos_provider",
|
||||||
"napi",
|
"napi",
|
||||||
|
"process_isolation",
|
||||||
"proxy",
|
"proxy",
|
||||||
"windows_plugin_authenticator"
|
"windows_plugin_authenticator"
|
||||||
]
|
]
|
||||||
@@ -27,6 +28,7 @@ byteorder = "=1.5.0"
|
|||||||
bytes = "=1.10.1"
|
bytes = "=1.10.1"
|
||||||
cbc = "=0.1.2"
|
cbc = "=0.1.2"
|
||||||
core-foundation = "=0.10.1"
|
core-foundation = "=0.10.1"
|
||||||
|
ctor = "=0.5.0"
|
||||||
dirs = "=6.0.0"
|
dirs = "=6.0.0"
|
||||||
ed25519 = "=2.2.3"
|
ed25519 = "=2.2.3"
|
||||||
embed_plist = "=1.2.2"
|
embed_plist = "=1.2.2"
|
||||||
|
|||||||
@@ -45,6 +45,20 @@ function buildProxyBin(target, release = true) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function buildProcessIsolation() {
|
||||||
|
if (process.platform !== "linux") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
child_process.execSync(`cargo build --release`, {
|
||||||
|
stdio: 'inherit',
|
||||||
|
cwd: path.join(__dirname, "process_isolation")
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("Copying process isolation library to dist folder");
|
||||||
|
fs.copyFileSync(path.join(__dirname, "target", "release", "libprocess_isolation.so"), path.join(__dirname, "dist", `libprocess_isolation.so`));
|
||||||
|
}
|
||||||
|
|
||||||
function installTarget(target) {
|
function installTarget(target) {
|
||||||
child_process.execSync(`rustup target add ${target}`, { stdio: 'inherit', cwd: __dirname });
|
child_process.execSync(`rustup target add ${target}`, { stdio: 'inherit', cwd: __dirname });
|
||||||
}
|
}
|
||||||
@@ -53,6 +67,7 @@ if (!crossPlatform && !target) {
|
|||||||
console.log(`Building native modules in ${mode} mode for the native architecture`);
|
console.log(`Building native modules in ${mode} mode for the native architecture`);
|
||||||
buildNapiModule(false, mode === "release");
|
buildNapiModule(false, mode === "release");
|
||||||
buildProxyBin(false, mode === "release");
|
buildProxyBin(false, mode === "release");
|
||||||
|
buildProcessIsolation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,6 +76,7 @@ if (target) {
|
|||||||
installTarget(target);
|
installTarget(target);
|
||||||
buildNapiModule(target, mode === "release");
|
buildNapiModule(target, mode === "release");
|
||||||
buildProxyBin(target, mode === "release");
|
buildProxyBin(target, mode === "release");
|
||||||
|
buildProcessIsolation();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -78,4 +94,5 @@ platformTargets.forEach(([target, _]) => {
|
|||||||
installTarget(target);
|
installTarget(target);
|
||||||
buildNapiModule(target);
|
buildNapiModule(target);
|
||||||
buildProxyBin(target);
|
buildProxyBin(target);
|
||||||
|
buildProcessIsolation();
|
||||||
});
|
});
|
||||||
|
|||||||
14
apps/desktop/desktop_native/process_isolation/Cargo.toml
Normal file
14
apps/desktop/desktop_native/process_isolation/Cargo.toml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
[package]
|
||||||
|
name = "process_isolation"
|
||||||
|
edition = { workspace = true }
|
||||||
|
license = { workspace = true }
|
||||||
|
version = { workspace = true }
|
||||||
|
publish = { workspace = true }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
ctor = { workspace = true }
|
||||||
|
desktop_core = { path = "../core" }
|
||||||
|
libc = { workspace = true }
|
||||||
46
apps/desktop/desktop_native/process_isolation/src/lib.rs
Normal file
46
apps/desktop/desktop_native/process_isolation/src/lib.rs
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#![cfg(target_os = "linux")]
|
||||||
|
|
||||||
|
//! This library compiles to a pre-loadable shared object. When preloaded, it
|
||||||
|
//! immediately isolates the process using the methods available on the platform.
|
||||||
|
//! On Linux, this is PR_SET_DUMPABLE to prevent debuggers from attaching, the env
|
||||||
|
//! from being read and the memory from being stolen.
|
||||||
|
|
||||||
|
use desktop_core::process_isolation;
|
||||||
|
use std::{ffi::c_char, sync::LazyLock};
|
||||||
|
|
||||||
|
static ORIGINAL_UNSETENV: LazyLock<unsafe extern "C" fn(*const c_char) -> i32> =
|
||||||
|
LazyLock::new(|| unsafe {
|
||||||
|
std::mem::transmute(libc::dlsym(libc::RTLD_NEXT, c"unsetenv".as_ptr()))
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Hooks unsetenv to fix a bug in zypak-wrapper.
|
||||||
|
/// Zypak unsets the env in Flatpak as a side-effect, which means that only the top level
|
||||||
|
/// processes would be hooked. With this work-around all processes in the tree are hooked
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
unsafe extern "C" fn unsetenv(name: *const c_char) -> i32 {
|
||||||
|
unsafe {
|
||||||
|
let Ok(name_str) = std::ffi::CStr::from_ptr(name).to_str() else {
|
||||||
|
return ORIGINAL_UNSETENV(name);
|
||||||
|
};
|
||||||
|
|
||||||
|
if name_str == "LD_PRELOAD" {
|
||||||
|
// This env variable is provided by the flatpak configuration
|
||||||
|
let ld_preload = std::env::var("PROCESS_ISOLATION_LD_PRELOAD").unwrap_or_default();
|
||||||
|
std::env::set_var("LD_PRELOAD", ld_preload);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ORIGINAL_UNSETENV(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hooks the shared object being loaded into the process
|
||||||
|
#[ctor::ctor]
|
||||||
|
fn preload_init() {
|
||||||
|
let pid = unsafe { libc::getpid() };
|
||||||
|
unsafe {
|
||||||
|
println!("[Process Isolation] Enabling memory security for process {pid}");
|
||||||
|
process_isolation::isolate_process();
|
||||||
|
process_isolation::disable_coredumps();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script tests the memory isolation status of bitwarden-desktop processes. The script will print "isolated"
|
||||||
|
# if the memory is not accessible by other processes.
|
||||||
|
|
||||||
|
CURRENT_USER=$(whoami)
|
||||||
|
|
||||||
|
# Find processes with "bitwarden" in the command
|
||||||
|
pids=$(pgrep -f bitwarden)
|
||||||
|
|
||||||
|
if [[ -z "$pids" ]]; then
|
||||||
|
echo "No bitwarden processes found."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
for pid in $pids; do
|
||||||
|
# Get process info: command, PPID, RSS memory
|
||||||
|
read cmd ppid rss <<<$(ps -o comm=,ppid=,rss= -p "$pid")
|
||||||
|
|
||||||
|
# Explicitly skip if the command line does not contain "bitwarden"
|
||||||
|
if ! grep -q "bitwarden" <<<"$cmd"; then
|
||||||
|
continue
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check ownership of /proc/$pid/environ
|
||||||
|
owner=$(stat -c "%U" /proc/$pid/environ 2>/dev/null)
|
||||||
|
|
||||||
|
if [[ "$owner" == "root" ]]; then
|
||||||
|
status="isolated"
|
||||||
|
elif [[ "$owner" == "$CURRENT_USER" ]]; then
|
||||||
|
status="insecure"
|
||||||
|
else
|
||||||
|
status="unknown-owner:$owner"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Convert memory to MB
|
||||||
|
mem_mb=$((rss / 1024))
|
||||||
|
|
||||||
|
echo "PID: $pid | CMD: $cmd | Mem: ${mem_mb}MB | Owner: $owner | Status: $status"
|
||||||
|
done
|
||||||
@@ -106,6 +106,10 @@
|
|||||||
{
|
{
|
||||||
"from": "desktop_native/dist/desktop_proxy.${platform}-${arch}",
|
"from": "desktop_native/dist/desktop_proxy.${platform}-${arch}",
|
||||||
"to": "desktop_proxy"
|
"to": "desktop_proxy"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"from": "desktop_native/dist/libprocess_isolation.so",
|
||||||
|
"to": "libprocess_isolation.so"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"target": ["deb", "freebsd", "rpm", "AppImage", "snap"],
|
"target": ["deb", "freebsd", "rpm", "AppImage", "snap"],
|
||||||
|
|||||||
@@ -35,9 +35,10 @@
|
|||||||
"build:renderer:watch": "cross-env NODE_ENV=development webpack --config webpack.config.js --config-name renderer --watch",
|
"build:renderer:watch": "cross-env NODE_ENV=development webpack --config webpack.config.js --config-name renderer --watch",
|
||||||
"electron": "node ./scripts/start.js",
|
"electron": "node ./scripts/start.js",
|
||||||
"electron:ignore": "node ./scripts/start.js --ignore-certificate-errors",
|
"electron:ignore": "node ./scripts/start.js --ignore-certificate-errors",
|
||||||
|
"flatpak:dev": "npm run clean:dist && electron-builder --dir -p never && flatpak-builder --force-clean --install --user ../../.flatpak/ ./resources/com.bitwarden.desktop.devel.yaml && flatpak run com.bitwarden.desktop",
|
||||||
"clean:dist": "rimraf ./dist",
|
"clean:dist": "rimraf ./dist",
|
||||||
"pack:dir": "npm run clean:dist && electron-builder --dir -p never",
|
"pack:dir": "npm run clean:dist && electron-builder --dir -p never",
|
||||||
"pack:lin:flatpak": "npm run clean:dist && electron-builder --dir -p never && flatpak-builder --repo=build/.repo build/.flatpak ./resources/com.bitwarden.desktop.devel.yaml --install-deps-from=flathub --force-clean && flatpak build-bundle ./build/.repo/ ./dist/com.bitwarden.desktop.flatpak com.bitwarden.desktop",
|
"pack:lin:flatpak": "flatpak-builder --repo=../../.flatpak-repo ../../.flatpak ./resources/com.bitwarden.desktop.devel.yaml --install-deps-from=flathub --force-clean && flatpak build-bundle ../../.flatpak-repo/ ./dist/com.bitwarden.desktop.flatpak com.bitwarden.desktop",
|
||||||
"pack:lin": "npm run clean:dist && electron-builder --linux --x64 -p never && export SNAP_FILE=$(realpath ./dist/bitwarden_*.snap) && unsquashfs -d ./dist/tmp-snap/ $SNAP_FILE && mkdir -p ./dist/tmp-snap/meta/polkit/ && cp ./resources/com.bitwarden.desktop.policy ./dist/tmp-snap/meta/polkit/polkit.com.bitwarden.desktop.policy && rm $SNAP_FILE && snap pack --compression=lzo ./dist/tmp-snap/ && mv ./*.snap ./dist/ && rm -rf ./dist/tmp-snap/",
|
"pack:lin": "npm run clean:dist && electron-builder --linux --x64 -p never && export SNAP_FILE=$(realpath ./dist/bitwarden_*.snap) && unsquashfs -d ./dist/tmp-snap/ $SNAP_FILE && mkdir -p ./dist/tmp-snap/meta/polkit/ && cp ./resources/com.bitwarden.desktop.policy ./dist/tmp-snap/meta/polkit/polkit.com.bitwarden.desktop.policy && rm $SNAP_FILE && snap pack --compression=lzo ./dist/tmp-snap/ && mv ./*.snap ./dist/ && rm -rf ./dist/tmp-snap/",
|
||||||
"pack:lin:arm64": "npm run clean:dist && electron-builder --dir -p never && tar -czvf ./dist/bitwarden_desktop_arm64.tar.gz -C ./dist/linux-arm64-unpacked/ .",
|
"pack:lin:arm64": "npm run clean:dist && electron-builder --dir -p never && tar -czvf ./dist/bitwarden_desktop_arm64.tar.gz -C ./dist/linux-arm64-unpacked/ .",
|
||||||
"pack:mac": "npm run clean:dist && electron-builder --mac --universal -p never",
|
"pack:mac": "npm run clean:dist && electron-builder --mac --universal -p never",
|
||||||
|
|||||||
@@ -46,4 +46,6 @@ modules:
|
|||||||
commands:
|
commands:
|
||||||
- ulimit -c 0
|
- ulimit -c 0
|
||||||
- export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID"
|
- export TMPDIR="$XDG_RUNTIME_DIR/app/$FLATPAK_ID"
|
||||||
|
- export ZYPAK_LD_PRELOAD="/app/bin/libprocess_isolation.so"
|
||||||
|
- export PROCESS_ISOLATION_LD_PRELOAD="/app/bin/libprocess_isolation.so"
|
||||||
- exec zypak-wrapper /app/bin/bitwarden-app "$@"
|
- exec zypak-wrapper /app/bin/bitwarden-app "$@"
|
||||||
|
|||||||
@@ -7,12 +7,19 @@ ulimit -c 0
|
|||||||
RAW_PATH=$(readlink -f "$0")
|
RAW_PATH=$(readlink -f "$0")
|
||||||
APP_PATH=$(dirname $RAW_PATH)
|
APP_PATH=$(dirname $RAW_PATH)
|
||||||
|
|
||||||
# force use of base image libdus in snap
|
# force use of base image libdbus in snap
|
||||||
if [ -e "/usr/lib/x86_64-linux-gnu/libdbus-1.so.3" ]
|
if [ -e "/usr/lib/x86_64-linux-gnu/libdbus-1.so.3" ]; then
|
||||||
then
|
|
||||||
export LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libdbus-1.so.3"
|
export LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libdbus-1.so.3"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# If running in non-snap, add libprocess_isolation.so from app path to LD_PRELOAD
|
||||||
|
# This prevents debugger / memory dumping on all desktop processes
|
||||||
|
if [ -z "$SNAP" ] && [ -f "$APP_PATH/libprocess_isolation.so" ]; then
|
||||||
|
LIBPROCESS_ISOLATION_SO="$APP_PATH/libprocess_isolation.so"
|
||||||
|
LD_PRELOAD="$LIBPROCESS_ISOLATION_SO${LD_PRELOAD:+:$LD_PRELOAD}"
|
||||||
|
export LD_PRELOAD
|
||||||
|
fi
|
||||||
|
|
||||||
PARAMS="--enable-features=UseOzonePlatform,WaylandWindowDecorations --ozone-platform-hint=auto"
|
PARAMS="--enable-features=UseOzonePlatform,WaylandWindowDecorations --ozone-platform-hint=auto"
|
||||||
if [ "$USE_X11" = "true" ]; then
|
if [ "$USE_X11" = "true" ]; then
|
||||||
PARAMS=""
|
PARAMS=""
|
||||||
|
|||||||
Reference in New Issue
Block a user