mirror of
https://github.com/bitwarden/browser
synced 2025-12-10 05:13:29 +00:00
[BEEEP | PM-25358] Add process isolation on windows and mac desktop main process (#16156)
* Prevent memory dumping and debugger on windows and mac main process * Fix clippy * Only isolate process when isdev is false * Clean up * Add backticks around link
This commit is contained in:
26
apps/desktop/desktop_native/Cargo.lock
generated
26
apps/desktop/desktop_native/Cargo.lock
generated
@@ -922,6 +922,7 @@ dependencies = [
|
|||||||
"rsa",
|
"rsa",
|
||||||
"russh-cryptovec",
|
"russh-cryptovec",
|
||||||
"scopeguard",
|
"scopeguard",
|
||||||
|
"secmem-proc",
|
||||||
"security-framework",
|
"security-framework",
|
||||||
"security-framework-sys",
|
"security-framework-sys",
|
||||||
"sha2",
|
"sha2",
|
||||||
@@ -2769,6 +2770,16 @@ dependencies = [
|
|||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustix-linux-procfs"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2fc84bf7e9aa16c4f2c758f27412dc9841341e16aa682d9c7ac308fe3ee12056"
|
||||||
|
dependencies = [
|
||||||
|
"once_cell",
|
||||||
|
"rustix 1.0.7",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustversion"
|
name = "rustversion"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
@@ -2847,6 +2858,21 @@ dependencies = [
|
|||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "secmem-proc"
|
||||||
|
version = "0.3.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "473559b1d28f530c3a9b5f91a2866053e2b1c528a0e43dae83048139c99490c2"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"cfg-if",
|
||||||
|
"libc",
|
||||||
|
"rustix 1.0.7",
|
||||||
|
"rustix-linux-procfs",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"windows 0.61.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "security-framework"
|
name = "security-framework"
|
||||||
version = "3.1.0"
|
version = "3.1.0"
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ rand = "=0.9.1"
|
|||||||
rsa = "=0.9.6"
|
rsa = "=0.9.6"
|
||||||
russh-cryptovec = "=0.7.3"
|
russh-cryptovec = "=0.7.3"
|
||||||
scopeguard = "=1.2.0"
|
scopeguard = "=1.2.0"
|
||||||
|
secmem-proc = "=0.3.7"
|
||||||
security-framework = "=3.1.0"
|
security-framework = "=3.1.0"
|
||||||
security-framework-sys = "=2.13.0"
|
security-framework-sys = "=2.13.0"
|
||||||
serde = "=1.0.209"
|
serde = "=1.0.209"
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ rand = { workspace = true }
|
|||||||
rsa = { workspace = true }
|
rsa = { workspace = true }
|
||||||
russh-cryptovec = { workspace = true }
|
russh-cryptovec = { workspace = true }
|
||||||
scopeguard = { workspace = true }
|
scopeguard = { workspace = true }
|
||||||
|
secmem-proc = { workspace = true }
|
||||||
sha2 = { workspace = true }
|
sha2 = { workspace = true }
|
||||||
ssh-encoding = { workspace = true }
|
ssh-encoding = { workspace = true }
|
||||||
ssh-key = { workspace = true, features = [
|
ssh-key = { workspace = true, features = [
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ pub fn disable_coredumps() -> Result<()> {
|
|||||||
rlim_cur: 0,
|
rlim_cur: 0,
|
||||||
rlim_max: 0,
|
rlim_max: 0,
|
||||||
};
|
};
|
||||||
|
println!("[Process Isolation] Disabling core dumps via setrlimit");
|
||||||
|
|
||||||
if unsafe { libc::setrlimit(RLIMIT_CORE, &rlimit) } != 0 {
|
if unsafe { libc::setrlimit(RLIMIT_CORE, &rlimit) } != 0 {
|
||||||
let e = std::io::Error::last_os_error();
|
let e = std::io::Error::last_os_error();
|
||||||
return Err(anyhow::anyhow!(
|
return Err(anyhow::anyhow!(
|
||||||
@@ -44,11 +46,17 @@ pub fn is_core_dumping_disabled() -> Result<bool> {
|
|||||||
Ok(rlimit.rlim_cur == 0 && rlimit.rlim_max == 0)
|
Ok(rlimit.rlim_cur == 0 && rlimit.rlim_max == 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_memory_access() -> Result<()> {
|
pub fn isolate_process() -> Result<()> {
|
||||||
|
let pid = std::process::id();
|
||||||
|
println!(
|
||||||
|
"[Process Isolation] Disabling ptrace and memory access for main ({}) via PR_SET_DUMPABLE",
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
|
||||||
if unsafe { libc::prctl(PR_SET_DUMPABLE, 0) } != 0 {
|
if unsafe { libc::prctl(PR_SET_DUMPABLE, 0) } != 0 {
|
||||||
let e = std::io::Error::last_os_error();
|
let e = std::io::Error::last_os_error();
|
||||||
return Err(anyhow::anyhow!(
|
return Err(anyhow::anyhow!(
|
||||||
"failed to disable memory dumping, memory is dumpable by other processes {}",
|
"failed to disable memory dumping, memory may be accessible by other processes {}",
|
||||||
e
|
e
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ pub fn is_core_dumping_disabled() -> Result<bool> {
|
|||||||
bail!("Not implemented on Mac")
|
bail!("Not implemented on Mac")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_memory_access() -> Result<()> {
|
pub fn isolate_process() -> Result<()> {
|
||||||
bail!("Not implemented on Mac")
|
let pid: u32 = std::process::id();
|
||||||
|
println!(
|
||||||
|
"[Process Isolation] Disabling ptrace on main process ({}) via PT_DENY_ATTACH",
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
|
||||||
|
secmem_proc::harden_process().map_err(|e| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"failed to disable memory dumping, memory may be accessible by other processes {}",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,16 @@
|
|||||||
|
//! This module implements process isolation, which aims to protect
|
||||||
|
//! a process from dumping memory to disk when crashing, and from
|
||||||
|
//! userspace memory access.
|
||||||
|
//!
|
||||||
|
//! On Windows, by default most userspace apps can read the memory of all
|
||||||
|
//! other apps, and attach debuggers. On Mac, this is not possible, and only
|
||||||
|
//! after granting developer permissions can an app attach to processes via
|
||||||
|
//! ptrace / read memory. On Linux, this depends on the distro / configuration of yama
|
||||||
|
//! `https://linux-audit.com/protect-ptrace-processes-kernel-yama-ptrace_scope/`
|
||||||
|
//! For instance, ubuntu prevents ptrace of other processes by default.
|
||||||
|
//! On Fedora, there are change proposals but ptracing is still possible unless
|
||||||
|
//! otherwise configured.
|
||||||
|
|
||||||
#[allow(clippy::module_inception)]
|
#[allow(clippy::module_inception)]
|
||||||
#[cfg_attr(target_os = "linux", path = "linux.rs")]
|
#[cfg_attr(target_os = "linux", path = "linux.rs")]
|
||||||
#[cfg_attr(target_os = "windows", path = "windows.rs")]
|
#[cfg_attr(target_os = "windows", path = "windows.rs")]
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ pub fn is_core_dumping_disabled() -> Result<bool> {
|
|||||||
bail!("Not implemented on Windows")
|
bail!("Not implemented on Windows")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn disable_memory_access() -> Result<()> {
|
pub fn isolate_process() -> Result<()> {
|
||||||
bail!("Not implemented on Windows")
|
let pid: u32 = std::process::id();
|
||||||
|
println!(
|
||||||
|
"[Process Isolation] Isolating main process via DACL {}",
|
||||||
|
pid
|
||||||
|
);
|
||||||
|
|
||||||
|
secmem_proc::harden_process().map_err(|e| {
|
||||||
|
anyhow::anyhow!(
|
||||||
|
"failed to isolate process, memory may be accessible by other processes {}",
|
||||||
|
e
|
||||||
|
)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
2
apps/desktop/desktop_native/napi/index.d.ts
vendored
2
apps/desktop/desktop_native/napi/index.d.ts
vendored
@@ -82,7 +82,7 @@ export declare namespace sshagent {
|
|||||||
export declare namespace processisolations {
|
export declare namespace processisolations {
|
||||||
export function disableCoredumps(): Promise<void>
|
export function disableCoredumps(): Promise<void>
|
||||||
export function isCoreDumpingDisabled(): Promise<boolean>
|
export function isCoreDumpingDisabled(): Promise<boolean>
|
||||||
export function disableMemoryAccess(): Promise<void>
|
export function isolateProcess(): Promise<void>
|
||||||
}
|
}
|
||||||
export declare namespace powermonitors {
|
export declare namespace powermonitors {
|
||||||
export function onLock(callback: (err: Error | null, ) => any): Promise<void>
|
export function onLock(callback: (err: Error | null, ) => any): Promise<void>
|
||||||
|
|||||||
@@ -337,8 +337,8 @@ pub mod processisolations {
|
|||||||
|
|
||||||
#[allow(clippy::unused_async)] // FIXME: Remove unused async!
|
#[allow(clippy::unused_async)] // FIXME: Remove unused async!
|
||||||
#[napi]
|
#[napi]
|
||||||
pub async fn disable_memory_access() -> napi::Result<()> {
|
pub async fn isolate_process() -> napi::Result<()> {
|
||||||
desktop_core::process_isolation::disable_memory_access()
|
desktop_core::process_isolation::isolate_process()
|
||||||
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
.map_err(|e| napi::Error::from_reason(e.to_string()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export class WindowMain {
|
|||||||
private windowStateChangeTimer: NodeJS.Timeout;
|
private windowStateChangeTimer: NodeJS.Timeout;
|
||||||
private windowStates: { [key: string]: WindowState } = {};
|
private windowStates: { [key: string]: WindowState } = {};
|
||||||
private enableAlwaysOnTop = false;
|
private enableAlwaysOnTop = false;
|
||||||
private enableRendererProcessForceCrashReload = false;
|
private enableRendererProcessForceCrashReload = true;
|
||||||
session: Electron.Session;
|
session: Electron.Session;
|
||||||
|
|
||||||
readonly defaultWidth = 950;
|
readonly defaultWidth = 950;
|
||||||
@@ -149,28 +149,31 @@ export class WindowMain {
|
|||||||
// initialization and is ready to create browser windows.
|
// initialization and is ready to create browser windows.
|
||||||
// Some APIs can only be used after this event occurs.
|
// Some APIs can only be used after this event occurs.
|
||||||
app.on("ready", async () => {
|
app.on("ready", async () => {
|
||||||
if (isMac() || isWindows()) {
|
if (!isDev()) {
|
||||||
this.enableRendererProcessForceCrashReload = true;
|
// This currently breaks the file portal for snap https://github.com/flatpak/xdg-desktop-portal/issues/785
|
||||||
} else if (isLinux() && !isDev()) {
|
if (!isSnapStore()) {
|
||||||
if (await processisolations.isCoreDumpingDisabled()) {
|
this.logService.info(
|
||||||
this.logService.info("Coredumps are disabled in renderer process");
|
"[Process Isolation] Isolating process from debuggers and memory dumps",
|
||||||
this.enableRendererProcessForceCrashReload = true;
|
);
|
||||||
} else {
|
|
||||||
this.logService.info("Disabling coredumps in main process");
|
|
||||||
try {
|
try {
|
||||||
await processisolations.disableCoredumps();
|
await processisolations.isolateProcess();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
this.logService.error("Failed to disable coredumps", e);
|
this.logService.error("[Process Isolation] Failed to isolate main process", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// this currently breaks the file portal for snap https://github.com/flatpak/xdg-desktop-portal/issues/785
|
if (isLinux()) {
|
||||||
if (!isSnapStore()) {
|
if (await processisolations.isCoreDumpingDisabled()) {
|
||||||
this.logService.info("Disabling memory dumps in main process");
|
this.logService.info("Coredumps are disabled in renderer process");
|
||||||
try {
|
} else {
|
||||||
await processisolations.disableMemoryAccess();
|
this.enableRendererProcessForceCrashReload = false;
|
||||||
} catch (e) {
|
this.logService.info("Disabling coredumps in main process");
|
||||||
this.logService.error("Failed to disable memory dumps", e);
|
try {
|
||||||
|
await processisolations.disableCoredumps();
|
||||||
|
this.enableRendererProcessForceCrashReload = true;
|
||||||
|
} catch (e) {
|
||||||
|
this.logService.error("Failed to disable coredumps", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user