From a724f5fd478e8c2eb3e2fec16b0faae9213d86e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= Date: Mon, 9 May 2022 17:14:32 +0200 Subject: [PATCH] api: datastore: unify access checks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Fabian Grünbichler Signed-off-by: Thomas Lamprecht --- src/api2/admin/datastore.rs | 271 ++++++++++++++---------------------- 1 file changed, 103 insertions(+), 168 deletions(-) diff --git a/src/api2/admin/datastore.rs b/src/api2/admin/datastore.rs index a756e646..14d74378 100644 --- a/src/api2/admin/datastore.rs +++ b/src/api2/admin/datastore.rs @@ -77,30 +77,6 @@ fn get_group_note_path( note_path } -fn check_priv_or_backup_owner( - // FIXME: We could probably switch to pbs-datastore::BackupGroup here to replace all of store, - // ns and group. - store: &DataStore, - ns: &BackupNamespace, - group: &pbs_api_types::BackupGroup, - auth_id: &Authid, - required_privs: u64, -) -> Result<(), Error> { - let user_info = CachedUserInfo::new()?; - - let privs = if ns.is_root() { - user_info.lookup_privs(auth_id, &["datastore", store.name()]) - } else { - user_info.lookup_privs(auth_id, &["datastore", store.name(), &ns.to_string()]) - }; - - if privs & required_privs == 0 { - let owner = store.get_owner(ns, group)?; - check_backup_owner(&owner, auth_id)?; - } - Ok(()) -} - // TODO: move somewhere we can reuse it from (namespace has its own copy atm.) fn get_ns_privs(store: &str, ns: &BackupNamespace, auth_id: &Authid) -> Result { let user_info = CachedUserInfo::new()?; @@ -112,7 +88,7 @@ fn get_ns_privs(store: &str, ns: &BackupNamespace, auth_id: &Authid) -> Result Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_VERIFY | PRIV_DATASTORE_BACKUP, )?; + let owner_check_required = user_privs & PRIV_DATASTORE_VERIFY == 0; + let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; let ignore_verified = ignore_verified.unwrap_or(true); @@ -800,13 +770,10 @@ pub fn verify( let dir = datastore.backup_dir_from_parts(backup_ns, backup_type, backup_id, backup_time)?; - check_priv_or_backup_owner( - &datastore, - dir.backup_ns(), - dir.as_ref(), - &auth_id, - PRIV_DATASTORE_VERIFY, - )?; + if owner_check_required { + let owner = datastore.get_owner(dir.backup_ns(), dir.as_ref())?; + check_backup_owner(&owner, &auth_id)?; + } backup_dir = Some(dir); worker_type = "verify_snapshot"; @@ -821,13 +788,10 @@ pub fn verify( ); let group = pbs_api_types::BackupGroup::from((backup_type, backup_id)); - check_priv_or_backup_owner( - &datastore, - &backup_ns, - &group, - &auth_id, - PRIV_DATASTORE_VERIFY, - )?; + if owner_check_required { + let owner = datastore.get_owner(&backup_ns, &group)?; + check_backup_owner(&owner, &auth_id)?; + } backup_group = Some(datastore.backup_group(backup_ns, group)); worker_type = "verify_group"; @@ -868,14 +832,14 @@ pub fn verify( )?; failed_dirs } else { - let privs = CachedUserInfo::new()?.lookup_privs(&auth_id, &["datastore", &store]); - - let owner = if privs & PRIV_DATASTORE_VERIFY == 0 { + let owner = if owner_check_required { Some(auth_id) } else { None }; + // FIXME namespace missing here.. + verify_all_backups( &verify_worker, worker.upid(), @@ -944,7 +908,7 @@ pub fn prune( let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, @@ -953,15 +917,12 @@ pub fn prune( let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; - let group = datastore.backup_group(backup_ns, group); + if user_privs & PRIV_DATASTORE_MODIFY == 0 { + let owner = datastore.get_owner(&backup_ns, &group)?; + check_backup_owner(&owner, &auth_id)?; + } - check_priv_or_backup_owner( - &datastore, - group.backup_ns(), - group.as_ref(), - &auth_id, - PRIV_DATASTORE_MODIFY, - )?; + let group = datastore.backup_group(backup_ns, group); let worker_id = format!("{}:{}", store, group); @@ -1292,25 +1253,22 @@ pub fn download_file( let store = required_string_param(¶m, "store")?; let backup_ns = optional_ns_param(¶m)?; let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?; - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(store, Some(Operation::Read))?; + + if user_privs & PRIV_DATASTORE_READ == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; let file_name = required_string_param(¶m, "file-name")?.to_owned(); - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_READ, - )?; - println!( "Download {} from {} ({}/{})", file_name, store, backup_dir, file_name @@ -1378,25 +1336,23 @@ pub fn download_file_decoded( let store = required_string_param(¶m, "store")?; let backup_ns = optional_ns_param(¶m)?; let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?; - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(store, Some(Operation::Read))?; + + if user_privs & PRIV_DATASTORE_READ == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } + let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; let file_name = required_string_param(¶m, "file-name")?.to_owned(); - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_READ, - )?; - let (manifest, files) = read_backup_index(&datastore, &backup_dir)?; for file in files { if file.filename == file_name && file.crypt_mode == Some(CryptMode::Encrypt) { @@ -1581,7 +1537,7 @@ pub fn catalog( ) -> Result, Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, @@ -1589,15 +1545,13 @@ pub fn catalog( )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; - let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_READ, - )?; + if user_privs & PRIV_DATASTORE_READ == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } + + let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; let file_name = CATALOG_NAME; @@ -1667,27 +1621,25 @@ pub fn pxar_file_download( let store = required_string_param(¶m, "store")?; let backup_ns = optional_ns_param(¶m)?; let backup_dir: pbs_api_types::BackupDir = Deserialize::deserialize(¶m)?; - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + + if user_privs & PRIV_DATASTORE_READ == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } + let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; let filepath = required_string_param(¶m, "filepath")?.to_owned(); let tar = param["tar"].as_bool().unwrap_or(false); - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_READ, - )?; - let mut components = base64::decode(&filepath)?; if !components.is_empty() && components[0] == b'/' { components.remove(0); @@ -1878,7 +1830,7 @@ pub fn get_group_notes( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, @@ -1886,13 +1838,10 @@ pub fn get_group_notes( )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; - check_priv_or_backup_owner( - &datastore, - &backup_ns, - &backup_group, - &auth_id, - PRIV_DATASTORE_AUDIT, - )?; + if user_privs & PRIV_DATASTORE_AUDIT == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_group)?; + check_backup_owner(&owner, &auth_id)?; + } let note_path = get_group_note_path(&datastore, &backup_ns, &backup_group); Ok(file_read_optional_string(note_path)?.unwrap_or_else(|| "".to_owned())) @@ -1931,21 +1880,17 @@ pub fn set_group_notes( ) -> Result<(), Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; - - check_priv_or_backup_owner( - &datastore, - &backup_ns, - &backup_group, - &auth_id, - PRIV_DATASTORE_MODIFY, - )?; + if user_privs & PRIV_DATASTORE_MODIFY == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_group)?; + check_backup_owner(&owner, &auth_id)?; + } let note_path = get_group_note_path(&datastore, &backup_ns, &backup_group); replace_file(note_path, notes.as_bytes(), CreateOptions::new(), false)?; @@ -1982,24 +1927,20 @@ pub fn get_notes( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + if user_privs & PRIV_DATASTORE_AUDIT == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_AUDIT, - )?; - let (manifest, _) = backup_dir.load_manifest()?; let notes = manifest.unprotected["notes"].as_str().unwrap_or(""); @@ -2040,24 +1981,20 @@ pub fn set_notes( ) -> Result<(), Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + if user_privs & PRIV_DATASTORE_MODIFY == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_MODIFY, - )?; - backup_dir .update_manifest(|manifest| { manifest.unprotected["notes"] = notes.into(); @@ -2096,24 +2033,20 @@ pub fn get_protection( ) -> Result { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Read))?; + if user_privs & PRIV_DATASTORE_AUDIT == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_AUDIT, - )?; - Ok(backup_dir.is_protected()) } @@ -2150,24 +2083,20 @@ pub fn set_protection( ) -> Result<(), Error> { let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - get_ns_privs_checked( + let user_privs = get_ns_privs_checked( &store, &backup_ns, &auth_id, PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP, )?; let datastore = DataStore::lookup_datastore(&store, Some(Operation::Write))?; + if user_privs & PRIV_DATASTORE_MODIFY == 0 { + let owner = datastore.get_owner(&backup_ns, &backup_dir.group)?; + check_backup_owner(&owner, &auth_id)?; + } let backup_dir = datastore.backup_dir(backup_ns, backup_dir)?; - check_priv_or_backup_owner( - &datastore, - backup_dir.backup_ns(), - backup_dir.as_ref(), - &auth_id, - PRIV_DATASTORE_MODIFY, - )?; - datastore.update_protection(&backup_dir, protected) } @@ -2206,7 +2135,13 @@ pub fn set_backup_owner( let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let backup_ns = backup_ns.unwrap_or_default(); - let privs = get_ns_privs(&store, &backup_ns, &auth_id)?; + + let privs = get_ns_privs_checked( + &store, + &backup_ns, + &auth_id, + PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP, + )?; let backup_group = datastore.backup_group(backup_ns, backup_group); let allowed = if (privs & PRIV_DATASTORE_MODIFY) != 0 {