Skip to main content

futu_backend/auth/device/
permission_tighten.rs

1#[cfg(unix)]
2use super::storage::try_futu_opend_dir;
3
4/// 启动时扫 `~/.futu-opend-rs/` 把已存在的 secret 文件 (credentials-*.json
5/// / device-*.dat) 收紧到 0600. v1.4.101 及以前版本可能创建了 0644 文件,
6/// 此 fn 是迁移路径.
7///
8/// 由 `init_auth_state` (或类似入口) 在 daemon 启动早期调用. 失败 best-effort
9/// (warn but don't fail), 因为 chmod 失败 != 凭据本身失效.
10pub fn tighten_secret_files_at_startup() {
11    #[cfg(unix)]
12    {
13        use std::os::unix::fs::PermissionsExt;
14        let dir = match try_futu_opend_dir() {
15            Ok(dir) => dir,
16            Err(err) => {
17                tracing::warn!(
18                    error = %err,
19                    "auth secret-file permission tightening skipped because store dir is unavailable"
20                );
21                return;
22            }
23        };
24        let Ok(entries) = std::fs::read_dir(&dir) else {
25            return;
26        };
27        for entry in entries.flatten() {
28            let path = entry.path();
29            let Some(name) = path.file_name().and_then(|n| n.to_str()) else {
30                continue;
31            };
32            // 仅收紧 secret 文件: credentials-<hash>.json / device-<hash>.dat.
33            // 不收紧 keys.json (用户可能想 ACL 给 systemd group), 不收紧 logs/ etc.
34            //
35            // v1.4.104 external reviewer P2-004 (P2) fix: 扩展到 sidecar 文件 (`.backup` / `.bak` /
36            // `.tmp` / `.swp`). 编辑器 vim / git rebase / external backup tool 等
37            // 可能创建 `credentials-<hash>.json.backup` 0644 文件, 同样含 secret
38            // 不能放任. 任何 name 以 `credentials-` 或 `device-` 开头都收紧.
39            let is_secret = name.starts_with("credentials-") || name.starts_with("device-");
40            if !is_secret {
41                continue;
42            }
43            let Ok(meta) = entry.metadata() else { continue };
44            let mode = meta.permissions().mode() & 0o777;
45            if mode != 0o600 {
46                tracing::warn!(
47                    path = %path.display(),
48                    actual_mode = format!("0{:o}", mode),
49                    "v1.4.102 BUG-012 fix: secret file has loose permissions; tightening to 0600"
50                );
51                if let Err(e) =
52                    std::fs::set_permissions(&path, std::fs::Permissions::from_mode(0o600))
53                {
54                    tracing::warn!(
55                        path = %path.display(),
56                        error = %e,
57                        "failed to tighten secret file permissions; please chmod 0600 manually"
58                    );
59                }
60            }
61        }
62    }
63    #[cfg(not(unix))]
64    {
65        // Windows: ACL 模型不同, 此 fn 不展开.
66    }
67}