|
2 | 2 | // SPDX-License-Identifier: Apache-2.0 |
3 | 3 |
|
4 | 4 | use super::{ |
5 | | - DraftChunkRecord, ObjectRecord, PersistenceResult, PolicyRecord, current_time_ms, map_db_error, |
6 | | - map_migrate_error, |
| 5 | + DraftChunkRecord, ObjectRecord, PersistenceError, PersistenceResult, PolicyRecord, |
| 6 | + current_time_ms, map_db_error, map_migrate_error, |
7 | 7 | }; |
8 | 8 | use crate::policy_store::{ |
9 | 9 | draft_chunk_payload_from_record, draft_chunk_record_from_parts, policy_payload_from_record, |
10 | 10 | policy_record_from_parts, |
11 | 11 | }; |
12 | 12 | use sqlx::sqlite::{SqliteConnectOptions, SqlitePoolOptions}; |
13 | 13 | use sqlx::{Row, SqlitePool}; |
| 14 | +use std::path::Path; |
14 | 15 | use std::str::FromStr; |
15 | 16 |
|
16 | 17 | static SQLITE_MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("./migrations/sqlite"); |
@@ -40,11 +41,20 @@ impl SqliteStore { |
40 | 41 | pool_options = pool_options.idle_timeout(None).max_lifetime(None); |
41 | 42 | } |
42 | 43 |
|
| 44 | + // Capture the on-disk path before `connect_with` consumes the options |
| 45 | + // so we can restrict the permissions after the database is connected. |
| 46 | + let db_path = (!is_in_memory).then(|| options.get_filename().to_path_buf()); |
| 47 | + |
43 | 48 | let pool = pool_options |
44 | 49 | .connect_with(options) |
45 | 50 | .await |
46 | 51 | .map_err(|e| map_db_error(&e))?; |
47 | 52 |
|
| 53 | + // Tighten the permissions of the database file to owner-only access (0o600). |
| 54 | + if let Some(path) = db_path { |
| 55 | + restrict_db_file_permissions(&path)?; |
| 56 | + } |
| 57 | + |
48 | 58 | Ok(Self { pool }) |
49 | 59 | } |
50 | 60 |
|
@@ -616,6 +626,26 @@ fn draft_chunk_dedup_key(chunk: &DraftChunkRecord) -> String { |
616 | 626 | format!("{}|{}|{}", chunk.host, chunk.port, chunk.binary) |
617 | 627 | } |
618 | 628 |
|
| 629 | +/// Restrict an on-disk `SQLite` database file to owner-only read/write (0o600). |
| 630 | +fn restrict_db_file_permissions(path: &Path) -> PersistenceResult<()> { |
| 631 | + #[cfg(unix)] |
| 632 | + { |
| 633 | + use std::os::unix::fs::PermissionsExt; |
| 634 | + std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)).map_err(|err| { |
| 635 | + PersistenceError::Database(format!( |
| 636 | + "failed to restrict permissions on {}: {err}", |
| 637 | + path.display() |
| 638 | + )) |
| 639 | + })?; |
| 640 | + } |
| 641 | + #[cfg(not(unix))] |
| 642 | + { |
| 643 | + let _ = path; |
| 644 | + warn!("restrict_db_file_permissions is a no-op on non-Unix platforms"); |
| 645 | + } |
| 646 | + Ok(()) |
| 647 | +} |
| 648 | + |
619 | 649 | fn row_to_object_record(row: sqlx::sqlite::SqliteRow) -> ObjectRecord { |
620 | 650 | ObjectRecord { |
621 | 651 | object_type: row.get("object_type"), |
|
0 commit comments