diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs b/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs index 2656bd3fdf9..9dabc6973f8 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/linux.rs @@ -21,6 +21,8 @@ use zbus_polkit::policykit1::{AuthorityProxy, CheckAuthorizationFlags, Subject}; use crate::secure_memory::{encrypted_memory_store::EncryptedMemoryStore, SecureMemoryStore as _}; +/// Biometric lock system using Polkit for authentication and secure memory to hold the key on +/// Linux. pub struct BiometricLockSystem { // The userkeys that are held in memory MUST be protected from memory dumping attacks, to // ensure locked vaults cannot be unlocked @@ -28,6 +30,7 @@ pub struct BiometricLockSystem { } impl BiometricLockSystem { + /// Creates a new biometric lock system with secure memory storage. pub fn new() -> Self { Self { secure_memory: Arc::new(Mutex::new(EncryptedMemoryStore::default())), diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs b/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs index d577a2a0c0b..dd692108b14 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/mod.rs @@ -1,3 +1,8 @@ +//! Biometric unlock module +//! +//! This modules can protect a key, either in-memory or in persisted operating system APIs +//! and release it only after authenticating via biometrics. + use anyhow::Result; #[allow(clippy::module_inception)] @@ -11,6 +16,7 @@ pub mod windows_focus; pub use biometric_v2::BiometricLockSystem; +/// Platform-specific biometric-protected key storage #[allow(async_fn_in_trait)] pub trait BiometricTrait: Send + Sync { /// Authenticate the user diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/unimplemented.rs b/apps/desktop/desktop_native/core/src/biometric_v2/unimplemented.rs index 02ac435da3c..1a9612b6d26 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/unimplemented.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/unimplemented.rs @@ -1,6 +1,8 @@ +/// Biometric lock system (unimplemented on this platform). pub struct BiometricLockSystem {} impl BiometricLockSystem { + /// Creates a new biometric lock system. pub fn new() -> Self { Self {} } diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs b/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs index 914bf50c20e..3fa5ea46f95 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/windows.rs @@ -82,6 +82,7 @@ pub struct BiometricLockSystem { } impl BiometricLockSystem { + /// Creates a new instance of the Windows biometric lock system. pub fn new() -> Self { Self { secure_memory: Arc::new(Mutex::new( diff --git a/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs b/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs index bf303c88e01..9b1ad5a6d79 100644 --- a/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs +++ b/apps/desktop/desktop_native/core/src/biometric_v2/windows_focus.rs @@ -1,3 +1,8 @@ +//! Windows-specific window focus management for biometric prompts. +//! +//! A large part of this is hacks to get around limitations with the Windows-Hello API used, +//! since it does not bring the authentication prompt into focus automatically. + use windows::{ core::s, Win32::{ diff --git a/apps/desktop/desktop_native/core/src/error.rs b/apps/desktop/desktop_native/core/src/error.rs index c8d3ec02332..3288e4983b1 100644 --- a/apps/desktop/desktop_native/core/src/error.rs +++ b/apps/desktop/desktop_native/core/src/error.rs @@ -1,8 +1,12 @@ +//! Error types for desktop_core operations. + use std::fmt::Debug; use thiserror::Error; +/// Errors that can occur in desktop_core operations. #[derive(Error, Debug)] +#[allow(missing_docs)] pub enum Error { #[error("Error parsing CipherString: {0}")] InvalidCipherString(#[from] CSParseError), @@ -11,7 +15,9 @@ pub enum Error { Crypto(#[from] CryptoError), } +/// Errors during cipher string parsing. #[derive(Debug, Error)] +#[allow(missing_docs)] pub enum CSParseError { #[error("No type detected, missing '.' separator")] NoType, @@ -23,16 +29,21 @@ pub enum CSParseError { InvalidBase64Length { expected: usize, got: usize }, } +/// Errors during cryptographic operations. #[derive(Debug, Error)] +#[allow(missing_docs)] pub enum CryptoError { #[error("Error while decrypting cipher string")] KeyDecrypt, } +/// Errors during KDF parameter validation. #[derive(Debug, Error)] +#[allow(missing_docs)] pub enum KdfParamError { #[error("Invalid KDF parameters: {0}")] InvalidParams(String), } +/// Convenience Result type using [`Error`]. pub type Result = std::result::Result; diff --git a/apps/desktop/desktop_native/core/src/ipc/client.rs b/apps/desktop/desktop_native/core/src/ipc/client.rs index 45b19e72bb8..7b23e6ddd72 100644 --- a/apps/desktop/desktop_native/core/src/ipc/client.rs +++ b/apps/desktop/desktop_native/core/src/ipc/client.rs @@ -1,3 +1,5 @@ +//! IPC client for connecting to and communicating with the IPC server. + use std::path::PathBuf; use futures::{SinkExt, StreamExt}; @@ -7,6 +9,7 @@ use interprocess::local_socket::{ }; use tracing::{error, info}; +/// Connects to an IPC server and handles bidirectional message passing. pub async fn connect( path: PathBuf, send: tokio::sync::mpsc::Sender, diff --git a/apps/desktop/desktop_native/core/src/ipc/mod.rs b/apps/desktop/desktop_native/core/src/ipc/mod.rs index f806e395d10..06a3f025022 100644 --- a/apps/desktop/desktop_native/core/src/ipc/mod.rs +++ b/apps/desktop/desktop_native/core/src/ipc/mod.rs @@ -1,3 +1,5 @@ +//! Inter-process communication for native messaging and IPC server/client. + use tokio::io::{AsyncRead, AsyncWrite}; use tokio_util::codec::{Framed, LengthDelimitedCodec}; diff --git a/apps/desktop/desktop_native/core/src/ipc/server.rs b/apps/desktop/desktop_native/core/src/ipc/server.rs index a65638303f1..41cdadda551 100644 --- a/apps/desktop/desktop_native/core/src/ipc/server.rs +++ b/apps/desktop/desktop_native/core/src/ipc/server.rs @@ -1,3 +1,5 @@ +//! IPC server for handling multiple client connections. + use std::{ error::Error, path::{Path, PathBuf}, @@ -15,22 +17,29 @@ use tracing::{error, info}; use super::MESSAGE_CHANNEL_BUFFER; +/// Message received from or sent to an IPC client. #[derive(Debug)] pub struct Message { + /// Unique identifier for the client connection. pub client_id: u32, + /// Type of message. pub kind: MessageType, - // This value should be Some for MessageType::Message and None for the rest + /// Message payload (Some for MessageType::Message, None otherwise). pub message: Option, } +/// Type of IPC message. #[derive(Debug)] +#[allow(missing_docs)] pub enum MessageType { Connected, Disconnected, Message, } +/// IPC server that listens for client connections. pub struct Server { + /// Path to the IPC socket. pub path: PathBuf, cancel_token: CancellationToken, server_to_clients_send: broadcast::Sender, diff --git a/apps/desktop/desktop_native/core/src/lib.rs b/apps/desktop/desktop_native/core/src/lib.rs index b6a558b7152..f78432361fb 100644 --- a/apps/desktop/desktop_native/core/src/lib.rs +++ b/apps/desktop/desktop_native/core/src/lib.rs @@ -1,15 +1,30 @@ +//! Desktop native core functionality for Bitwarden. +//! +//! Modules in this crate should fall into one of these categories: +//! * infrastructure to interface with the Electron client +//! * core functionality for the Desktop app that is not feature-specific +//! * library code that is used internally by other desktop_native crates. + +#![warn(missing_docs)] + +#[allow(missing_docs)] pub mod autofill; +#[allow(missing_docs)] pub mod autostart; +#[allow(missing_docs)] // staged to be removed pub mod biometric; pub mod biometric_v2; +#[allow(missing_docs)] pub mod clipboard; pub(crate) mod crypto; pub mod error; pub mod ipc; pub mod password; +#[allow(missing_docs)] pub mod powermonitor; pub mod process_isolation; pub mod secure_memory; +#[allow(missing_docs)] // staged to be removed pub mod ssh_agent; use zeroizing_alloc::ZeroAlloc; diff --git a/apps/desktop/desktop_native/core/src/password/macos.rs b/apps/desktop/desktop_native/core/src/password/macos.rs index 72d8ebeb425..d451aa0f9af 100644 --- a/apps/desktop/desktop_native/core/src/password/macos.rs +++ b/apps/desktop/desktop_native/core/src/password/macos.rs @@ -1,3 +1,5 @@ +//! macOS Keychain password operations. + use anyhow::Result; use security_framework::passwords::{ delete_generic_password, get_generic_password, set_generic_password, @@ -5,6 +7,7 @@ use security_framework::passwords::{ use crate::password::PASSWORD_NOT_FOUND; +/// Retrieves a password from the macOS Keychain. #[allow(clippy::unused_async)] pub async fn get_password(service: &str, account: &str) -> Result { let password = get_generic_password(service, account).map_err(convert_error)?; @@ -12,18 +15,21 @@ pub async fn get_password(service: &str, account: &str) -> Result { Ok(result) } +/// Stores a password in the macOS Keychain. #[allow(clippy::unused_async)] pub async fn set_password(service: &str, account: &str, password: &str) -> Result<()> { set_generic_password(service, account, password.as_bytes())?; Ok(()) } +/// Deletes a password from the macOS Keychain. #[allow(clippy::unused_async)] pub async fn delete_password(service: &str, account: &str) -> Result<()> { delete_generic_password(service, account).map_err(convert_error)?; Ok(()) } +/// Checks if Keychain access is available. #[allow(clippy::unused_async)] pub async fn is_available() -> Result { Ok(true) diff --git a/apps/desktop/desktop_native/core/src/password/mod.rs b/apps/desktop/desktop_native/core/src/password/mod.rs index 93c91072dd2..471fb371188 100644 --- a/apps/desktop/desktop_native/core/src/password/mod.rs +++ b/apps/desktop/desktop_native/core/src/password/mod.rs @@ -1,3 +1,6 @@ +//! Secure password storage and retrieval using platform keychains. + +/// Error message returned when a password is not found in the system keychain. pub const PASSWORD_NOT_FOUND: &str = "Password not found."; #[allow(clippy::module_inception)] diff --git a/apps/desktop/desktop_native/core/src/password/unix.rs b/apps/desktop/desktop_native/core/src/password/unix.rs index 57b71adefed..94ca98d20b8 100644 --- a/apps/desktop/desktop_native/core/src/password/unix.rs +++ b/apps/desktop/desktop_native/core/src/password/unix.rs @@ -6,6 +6,7 @@ use tracing::info; use crate::password::PASSWORD_NOT_FOUND; +/// Retrieves a password from the Linux Secret Service keyring. pub async fn get_password(service: &str, account: &str) -> Result { match get_password_new(service, account).await { Ok(res) => Ok(res), @@ -51,6 +52,7 @@ async fn get_password_legacy(service: &str, account: &str) -> Result { } } +/// Stores a password in the Linux Secret Service keyring. pub async fn set_password(service: &str, account: &str, password: &str) -> Result<()> { let keyring = oo7::Keyring::new().await?; let _ = try_prompt(&keyring).await; diff --git a/apps/desktop/desktop_native/core/src/password/windows.rs b/apps/desktop/desktop_native/core/src/password/windows.rs index 645620b444e..c644c22dbdd 100644 --- a/apps/desktop/desktop_native/core/src/password/windows.rs +++ b/apps/desktop/desktop_native/core/src/password/windows.rs @@ -15,6 +15,7 @@ use crate::password::PASSWORD_NOT_FOUND; const CRED_FLAGS_NONE: u32 = 0; +/// Retrieves a password from the Windows Credential Manager. #[allow(clippy::unused_async)] pub async fn get_password(service: &str, account: &str) -> Result { let target_name = U16CString::from_str(target_name(service, account))?; @@ -48,6 +49,7 @@ pub async fn get_password(service: &str, account: &str) -> Result { Ok(password) } +/// Stores a password in the Windows Credential Manager. #[allow(clippy::unused_async)] pub async fn set_password(service: &str, account: &str, password: &str) -> Result<()> { let mut target_name = U16CString::from_str(target_name(service, account))?; @@ -80,6 +82,7 @@ pub async fn set_password(service: &str, account: &str, password: &str) -> Resul Ok(()) } +/// Deletes a password from the Windows Credential Manager. #[allow(clippy::unused_async)] pub async fn delete_password(service: &str, account: &str) -> Result<()> { let target_name = U16CString::from_str(target_name(service, account))?; @@ -91,6 +94,7 @@ pub async fn delete_password(service: &str, account: &str) -> Result<()> { Ok(()) } +/// Checks if the Windows Credential Manager is available. Always returns true on Windows. #[allow(clippy::unused_async)] pub async fn is_available() -> Result { Ok(true) diff --git a/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs b/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs index 8fa3707ab2f..d023ac12e51 100644 --- a/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs +++ b/apps/desktop/desktop_native/core/src/powermonitor/unimplemented.rs @@ -1,9 +1,9 @@ -#[allow(clippy::unused_async)] +#[allow(missing_docs, clippy::unused_async)] pub async fn on_lock(_: tokio::sync::mpsc::Sender<()>) -> Result<(), Box> { unimplemented!(); } -#[allow(clippy::unused_async)] +#[allow(missing_docs, clippy::unused_async)] pub async fn is_lock_monitor_available() -> bool { false } diff --git a/apps/desktop/desktop_native/core/src/process_isolation/linux.rs b/apps/desktop/desktop_native/core/src/process_isolation/linux.rs index 263cc10b716..80ce9306c66 100644 --- a/apps/desktop/desktop_native/core/src/process_isolation/linux.rs +++ b/apps/desktop/desktop_native/core/src/process_isolation/linux.rs @@ -16,6 +16,8 @@ const RLIMIT_CORE: c_uint = 4; // https://github.com/torvalds/linux/blob/a38297e3fb012ddfa7ce0321a7e5a8daeb1872b6/include/uapi/linux/prctl.h#L14 const PR_SET_DUMPABLE: c_int = 4; +/// Disables core dumps by setting RLIMIT_CORE to prevent memory from being +/// persisted to disk on crashes. pub fn disable_coredumps() -> Result<()> { let rlimit = libc::rlimit { rlim_cur: 0, @@ -34,6 +36,7 @@ pub fn disable_coredumps() -> Result<()> { Ok(()) } +/// Checks if core dumping is disabled by verifying that RLIMIT_CORE is set to 0. pub fn is_core_dumping_disabled() -> Result { let mut rlimit = libc::rlimit { rlim_cur: 0, @@ -47,6 +50,8 @@ pub fn is_core_dumping_disabled() -> Result { Ok(rlimit.rlim_cur == 0 && rlimit.rlim_max == 0) } +/// Prevents other processes from dumping this process's memory or attaching a +/// debugger by setting PR_SET_DUMPABLE. pub fn isolate_process() -> Result<()> { let pid = std::process::id(); info!( diff --git a/apps/desktop/desktop_native/core/src/process_isolation/macos.rs b/apps/desktop/desktop_native/core/src/process_isolation/macos.rs index 928eac749c0..a2d0e41a370 100644 --- a/apps/desktop/desktop_native/core/src/process_isolation/macos.rs +++ b/apps/desktop/desktop_native/core/src/process_isolation/macos.rs @@ -1,14 +1,17 @@ use anyhow::{bail, Result}; use tracing::info; +#[allow(missing_docs)] pub fn disable_coredumps() -> Result<()> { bail!("Not implemented on Mac") } +#[allow(missing_docs)] pub fn is_core_dumping_disabled() -> Result { bail!("Not implemented on Mac") } +#[allow(missing_docs)] pub fn isolate_process() -> Result<()> { let pid: u32 = std::process::id(); info!(pid, "Disabling ptrace on main process via PT_DENY_ATTACH."); diff --git a/apps/desktop/desktop_native/core/src/process_isolation/windows.rs b/apps/desktop/desktop_native/core/src/process_isolation/windows.rs index fddea8bc53a..10137435e69 100644 --- a/apps/desktop/desktop_native/core/src/process_isolation/windows.rs +++ b/apps/desktop/desktop_native/core/src/process_isolation/windows.rs @@ -1,14 +1,18 @@ use anyhow::{bail, Result}; use tracing::info; +#[allow(missing_docs)] pub fn disable_coredumps() -> Result<()> { bail!("Not implemented on Windows") } +#[allow(missing_docs)] pub fn is_core_dumping_disabled() -> Result { bail!("Not implemented on Windows") } +/// Prevents other processes from accessing this process's memory by hardening the +/// process using DACL (Discretionary Access Control List). pub fn isolate_process() -> Result<()> { let pid: u32 = std::process::id(); info!(pid, "Isolating main process via DACL."); diff --git a/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs b/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs index 8961b63ccee..2aa449f6639 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/encrypted_memory_store.rs @@ -29,6 +29,7 @@ impl EncryptedMemoryStore where K: std::cmp::Ord + std::fmt::Display + std::clone::Clone, { + /// Creates a new encrypted memory store with a fresh encryption key. #[must_use] pub fn new() -> Self { EncryptedMemoryStore { diff --git a/apps/desktop/desktop_native/core/src/secure_memory/mod.rs b/apps/desktop/desktop_native/core/src/secure_memory/mod.rs index b5c3bcdccd9..a8409f753d1 100644 --- a/apps/desktop/desktop_native/core/src/secure_memory/mod.rs +++ b/apps/desktop/desktop_native/core/src/secure_memory/mod.rs @@ -1,3 +1,5 @@ +//! Secure memory management with platform-specific protections. + #[cfg(target_os = "windows")] pub(crate) mod dpapi; @@ -13,6 +15,7 @@ use crate::secure_memory::secure_key::DecryptionError; /// platform-specific protections are applied to prevent memory dumps or debugger access from /// reading the stored values. pub trait SecureMemoryStore { + /// Key type used to identify stored values. type KeyType; /// Stores a copy of the provided value in secure memory.