Skip to main content

auths_cli/core/
fs.rs

1use anyhow::Result;
2use std::path::Path;
3
4/// Write data to a file with restrictive permissions (0o600 on Unix).
5///
6/// Uses atomic temp-file-then-rename to avoid TOCTOU races where the file
7/// exists with default permissions before being restricted.
8///
9/// Args:
10/// * `path` - Destination file path.
11/// * `data` - Bytes to write.
12pub fn write_sensitive_file(path: &Path, data: impl AsRef<[u8]>) -> Result<()> {
13    #[cfg(unix)]
14    {
15        use std::io::Write;
16        use std::os::unix::fs::PermissionsExt;
17
18        let parent = path
19            .parent()
20            .ok_or_else(|| anyhow::anyhow!("No parent directory for {:?}", path))?;
21
22        let mut tmp = tempfile::NamedTempFile::new_in(parent)?;
23        // NamedTempFile creates with 0o600 by default on most systems,
24        // but we set it explicitly via the persisted file permissions.
25        tmp.write_all(data.as_ref())?;
26        tmp.flush()?;
27
28        let tmp_path = tmp.into_temp_path();
29        std::fs::set_permissions(&tmp_path, std::fs::Permissions::from_mode(0o600))?;
30        tmp_path.persist(path)?;
31
32        Ok(())
33    }
34
35    #[cfg(not(unix))]
36    {
37        log::warn!("Restrictive file permissions not enforced on this platform");
38        std::fs::write(path, data)?;
39        Ok(())
40    }
41}
42
43/// Create a directory with restrictive permissions (0o700 on Unix).
44///
45/// Args:
46/// * `path` - Directory path to create (including parents).
47pub fn create_restricted_dir(path: &Path) -> Result<()> {
48    #[cfg(unix)]
49    {
50        use std::os::unix::fs::{DirBuilderExt, PermissionsExt};
51        // Create parents with default permissions first
52        if let Some(parent) = path.parent() {
53            std::fs::create_dir_all(parent)?;
54        }
55        std::fs::DirBuilder::new()
56            .mode(0o700)
57            .create(path)
58            .or_else(|e| {
59                if e.kind() == std::io::ErrorKind::AlreadyExists {
60                    // Tighten permissions on existing directory
61                    std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o700))?;
62                    Ok(())
63                } else {
64                    Err(e.into())
65                }
66            })
67    }
68
69    #[cfg(not(unix))]
70    {
71        log::warn!("Restrictive directory permissions not enforced on this platform");
72        std::fs::create_dir_all(path)?;
73        Ok(())
74    }
75}