From 1d951b1e95b06f8f21c068dd12c4988492db9441 Mon Sep 17 00:00:00 2001 From: Bernd Schoolmann Date: Thu, 28 Aug 2025 05:24:53 +0200 Subject: [PATCH] Turn on v2 linux --- .../core/src/secure_memory/memfd_secret.rs | 4 + .../biometrics/main-biometrics.service.ts | 6 +- .../os-biometrics-linux-v2.service.ts | 106 ++++++++++++++++++ 3 files changed, 111 insertions(+), 5 deletions(-) create mode 100644 apps/desktop/src/key-management/biometrics/os-biometrics-linux-v2.service.ts diff --git a/apps/desktop/desktop_native/core/src/secure_memory/memfd_secret.rs b/apps/desktop/desktop_native/core/src/secure_memory/memfd_secret.rs index c25306a8b7d..ce3d7d21de1 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/memfd_secret.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/memfd_secret.rs @@ -1,5 +1,7 @@ use std::{collections::HashMap, ptr::NonNull, sync::LazyLock}; +use std::marker::Send; + use crate::secure_memory::SecureMemoryStore; /// https://man.archlinux.org/man/memfd_secret.2.en @@ -11,6 +13,8 @@ pub(crate) struct MemfdSecretKVStore { map: HashMap>, } +unsafe impl Send for MemfdSecretKVStore {} + impl MemfdSecretKVStore { pub(crate) fn new() -> Self { MemfdSecretKVStore { diff --git a/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts b/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts index 1de8e3cd12d..246561889ad 100644 --- a/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts +++ b/apps/desktop/src/key-management/biometrics/main-biometrics.service.ts @@ -43,12 +43,8 @@ export class MainBiometricsService extends DesktopBiometricsService { this.osBiometricsService = new OsBiometricsServiceMac(this.i18nService, this.logService); } else if (platform === "linux") { // eslint-disable-next-line - const OsBiometricsServiceLinux = require("./os-biometrics-linux.service").default; + const OsBiometricsServiceLinux = require("./os-biometrics-linux-v2.service").default; this.osBiometricsService = new OsBiometricsServiceLinux( - this.biometricStateService, - this.encryptService, - this.cryptoFunctionService, - this.logService, ); } else { throw new Error("Unsupported platform"); diff --git a/apps/desktop/src/key-management/biometrics/os-biometrics-linux-v2.service.ts b/apps/desktop/src/key-management/biometrics/os-biometrics-linux-v2.service.ts new file mode 100644 index 00000000000..6b873f3a45a --- /dev/null +++ b/apps/desktop/src/key-management/biometrics/os-biometrics-linux-v2.service.ts @@ -0,0 +1,106 @@ +import { spawn } from "child_process"; + +import { CryptoFunctionService } from "@bitwarden/common/key-management/crypto/abstractions/crypto-function.service"; +import { EncryptService } from "@bitwarden/common/key-management/crypto/abstractions/encrypt.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { SymmetricCryptoKey } from "@bitwarden/common/platform/models/domain/symmetric-crypto-key"; +import { UserId } from "@bitwarden/common/types/guid"; +import { biometrics, biometrics_v2, passwords } from "@bitwarden/desktop-napi"; +import { BiometricsStatus, BiometricStateService } from "@bitwarden/key-management"; + +import { isFlatpak, isLinux, isSnapStore } from "../../utils"; + +import { OsBiometricService } from "./os-biometrics.service"; + +const polkitPolicy = ` + + + + + Unlock Bitwarden + Authenticate to unlock Bitwarden + + no + no + auth_self + + +`; +const policyFileName = "com.bitwarden.Bitwarden.policy"; +const policyPath = "/usr/share/polkit-1/actions/"; + +export default class OsBiometricsServiceLinux implements OsBiometricService { + private biometricsSystem = biometrics_v2.initBiometricSystem(); + + constructor( + ) {} + + async setBiometricKey(userId: UserId, key: SymmetricCryptoKey): Promise { + biometrics_v2.provideKey(this.biometricsSystem, userId, Buffer.from(key.toEncoded().buffer)); + } + + async deleteBiometricKey(userId: UserId): Promise { + } + + async getBiometricKey(userId: UserId): Promise { + const hwnd = Buffer.from(""); + const result = await biometrics_v2.unlock(this.biometricsSystem, userId, hwnd); + return result ? new SymmetricCryptoKey(Uint8Array.from(result)) : null; + } + + async authenticateBiometric(): Promise { + const hwnd = Buffer.from(""); + return await biometrics_v2.authenticate(this.biometricsSystem, hwnd, "Authenticate to unlock"); + } + + async supportsBiometrics(): Promise { + // We assume all linux distros have some polkit implementation + // that either has bitwarden set up or not, which is reflected in osBiomtricsNeedsSetup. + // Snap does not have access at the moment to polkit + // This could be dynamically detected on dbus in the future. + // We should check if a libsecret implementation is available on the system + // because otherwise we cannot offlod the protected userkey to secure storage. + return await passwords.isAvailable(); + } + + async needsSetup(): Promise { + if (isSnapStore()) { + return false; + } + + // check whether the polkit policy is loaded via dbus call to polkit + return !(await biometrics.available()); + } + + async canAutoSetup(): Promise { + // We cannot auto setup on snap or flatpak since the filesystem is sandboxed. + // The user needs to manually set up the polkit policy outside of the sandbox + // since we allow access to polkit via dbus for the sandboxed clients, the authentication works from + // the sandbox, once the policy is set up outside of the sandbox. + return isLinux() && !isSnapStore() && !isFlatpak(); + } + + async runSetup(): Promise { + const process = spawn("pkexec", [ + "bash", + "-c", + `echo '${polkitPolicy}' > ${policyPath + policyFileName} && chown root:root ${policyPath + policyFileName} && chcon system_u:object_r:usr_t:s0 ${policyPath + policyFileName}`, + ]); + + await new Promise((resolve, reject) => { + process.on("close", (code) => { + if (code !== 0) { + reject("Failed to set up polkit policy"); + } else { + resolve(null); + } + }); + }); + } + + async getBiometricsFirstUnlockStatusForUser(userId: UserId): Promise { + return biometrics_v2.unlockAvailable(this.biometricsSystem, userId) ? BiometricsStatus.Available : BiometricsStatus.UnlockNeeded; + } +}