mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-07-11 07:13:43 +02:00
security: restrict secret files to owner-only (0600)
This commit is contained in:
@@ -190,6 +190,25 @@ pub fn set_test_cache_dir(dir: PathBuf) -> TestDirGuard {
|
||||
}
|
||||
}
|
||||
|
||||
/// Restrict a just-written file to owner-only read/write (`0600`) on Unix so
|
||||
/// other local users/processes can't read secret material (tokens, E2E
|
||||
/// password, encrypted vault files). Best-effort: the write already succeeded,
|
||||
/// so a permission failure is logged, not propagated. On Windows the per-user
|
||||
/// profile ACL already restricts access, so this is a no-op there.
|
||||
pub fn restrict_to_owner(path: &std::path::Path) {
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
if let Err(e) = std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)) {
|
||||
log::warn!("Failed to restrict permissions on {}: {e}", path.display());
|
||||
}
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
let _ = path;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
@@ -289,6 +289,7 @@ impl CloudAuthManager {
|
||||
file_data.extend_from_slice(&ciphertext);
|
||||
|
||||
fs::write(file_path, file_data).map_err(|e| format!("Failed to write file: {e}"))?;
|
||||
crate::app_dirs::restrict_to_owner(file_path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -409,7 +410,8 @@ impl CloudAuthManager {
|
||||
}
|
||||
let json =
|
||||
serde_json::to_string_pretty(state).map_err(|e| format!("Failed to serialize: {e}"))?;
|
||||
fs::write(path, json).map_err(|e| format!("Failed to write auth state: {e}"))?;
|
||||
fs::write(&path, json).map_err(|e| format!("Failed to write auth state: {e}"))?;
|
||||
crate::app_dirs::restrict_to_owner(&path);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -266,7 +266,8 @@ impl SettingsManager {
|
||||
file_data.extend_from_slice(&(ciphertext.len() as u32).to_le_bytes());
|
||||
file_data.extend_from_slice(&ciphertext);
|
||||
|
||||
std::fs::write(token_file, file_data)?;
|
||||
std::fs::write(&token_file, file_data)?;
|
||||
crate::app_dirs::restrict_to_owner(std::path::Path::new(&token_file));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -434,7 +435,8 @@ impl SettingsManager {
|
||||
file_data.extend_from_slice(&(ciphertext.len() as u32).to_le_bytes());
|
||||
file_data.extend_from_slice(&ciphertext);
|
||||
|
||||
std::fs::write(token_file, file_data)?;
|
||||
std::fs::write(&token_file, file_data)?;
|
||||
crate::app_dirs::restrict_to_owner(std::path::Path::new(&token_file));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -573,7 +575,8 @@ impl SettingsManager {
|
||||
file_data.extend_from_slice(&(ciphertext.len() as u32).to_le_bytes());
|
||||
file_data.extend_from_slice(&ciphertext);
|
||||
|
||||
std::fs::write(token_file, file_data)?;
|
||||
std::fs::write(&token_file, file_data)?;
|
||||
crate::app_dirs::restrict_to_owner(std::path::Path::new(&token_file));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ pub fn store_e2e_password(password: &str) -> Result<(), String> {
|
||||
|
||||
std::fs::write(&file_path, file_data)
|
||||
.map_err(|e| format!("Failed to write e2e password file: {e}"))?;
|
||||
crate::app_dirs::restrict_to_owner(std::path::Path::new(&file_path));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -69,6 +69,12 @@ impl VpnStorage {
|
||||
let storage_path = dir.join("vpn_configs.json");
|
||||
let key_path = dir.join(".vpn_key");
|
||||
|
||||
// Persist a freshly generated key with owner-only (0600) permissions so the
|
||||
// WireGuard-key-encrypting key isn't left world-readable.
|
||||
let write_key = |key: &[u8; 32]| {
|
||||
let _ = fs::write(&key_path, key);
|
||||
crate::app_dirs::restrict_to_owner(&key_path);
|
||||
};
|
||||
let encryption_key = if key_path.exists() {
|
||||
if let Ok(key_data) = fs::read(&key_path) {
|
||||
if key_data.len() == 32 {
|
||||
@@ -77,17 +83,17 @@ impl VpnStorage {
|
||||
key
|
||||
} else {
|
||||
let key: [u8; 32] = rand::rng().random();
|
||||
let _ = fs::write(&key_path, key);
|
||||
write_key(&key);
|
||||
key
|
||||
}
|
||||
} else {
|
||||
let key: [u8; 32] = rand::rng().random();
|
||||
let _ = fs::write(&key_path, key);
|
||||
write_key(&key);
|
||||
key
|
||||
}
|
||||
} else {
|
||||
let key: [u8; 32] = rand::rng().random();
|
||||
let _ = fs::write(&key_path, key);
|
||||
write_key(&key);
|
||||
key
|
||||
};
|
||||
|
||||
@@ -124,13 +130,7 @@ impl VpnStorage {
|
||||
// Generate a new key
|
||||
let key: [u8; 32] = rand::rng().random();
|
||||
let _ = fs::write(&key_path, key);
|
||||
|
||||
// Set restrictive permissions on Unix
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::fs::PermissionsExt;
|
||||
let _ = fs::set_permissions(&key_path, fs::Permissions::from_mode(0o600));
|
||||
}
|
||||
crate::app_dirs::restrict_to_owner(&key_path);
|
||||
|
||||
key
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user