Security gate for WAL key files: enforces no-symlink, regular-file, Unix mode bits (no group/world), and owner-UID checks. Mirrors the logic in `nodedb::control::security::keystore::key_file_security` but is duplicated here so that `nodedb-wal` has zero dependency on the `nodedb` application crate.
(path: &std::path::Path)
| 30 | /// but is duplicated here so that `nodedb-wal` has zero dependency on the |
| 31 | /// `nodedb` application crate. |
| 32 | fn check_key_file_wal(path: &std::path::Path) -> Result<()> { |
| 33 | let symlink_meta = std::fs::symlink_metadata(path).map_err(|e| WalError::EncryptionError { |
| 34 | detail: format!("cannot stat WAL key file {}: {e}", path.display()), |
| 35 | })?; |
| 36 | |
| 37 | if symlink_meta.file_type().is_symlink() { |
| 38 | return Err(WalError::EncryptionError { |
| 39 | detail: format!( |
| 40 | "WAL key file {} is a symlink, which is not permitted \ |
| 41 | (path traversal / TOCTOU risk)", |
| 42 | path.display() |
| 43 | ), |
| 44 | }); |
| 45 | } |
| 46 | |
| 47 | if !symlink_meta.is_file() { |
| 48 | return Err(WalError::EncryptionError { |
| 49 | detail: format!("WAL key file {} is not a regular file", path.display()), |
| 50 | }); |
| 51 | } |
| 52 | |
| 53 | #[cfg(unix)] |
| 54 | { |
| 55 | use std::os::unix::fs::MetadataExt as _; |
| 56 | |
| 57 | let mode = symlink_meta.mode(); |
| 58 | if mode & 0o077 != 0 { |
| 59 | return Err(WalError::EncryptionError { |
| 60 | detail: format!( |
| 61 | "WAL key file {} has insecure permissions: 0o{:03o} \ |
| 62 | (must be 0o400 or 0o600 — no group or world access)", |
| 63 | path.display(), |
| 64 | mode & 0o777, |
| 65 | ), |
| 66 | }); |
| 67 | } |
| 68 | |
| 69 | let file_uid = symlink_meta.uid(); |
| 70 | // SAFETY: geteuid() is always safe to call; it has no preconditions. |
| 71 | let process_uid = unsafe { libc::geteuid() }; |
| 72 | if file_uid != process_uid { |
| 73 | return Err(WalError::EncryptionError { |
| 74 | detail: format!( |
| 75 | "WAL key file {} is owned by UID {} but process runs as UID {} \ |
| 76 | — key files must be owned by the server process user", |
| 77 | path.display(), |
| 78 | file_uid, |
| 79 | process_uid, |
| 80 | ), |
| 81 | }); |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | // On Windows: ACL-based permission enforcement is not implemented. |
| 86 | // The symlink check above still applies on all platforms. |
| 87 | |
| 88 | Ok(()) |
| 89 | } |