priv handling: use DatastoreWithNamespace

Signed-off-by: Fabian Grünbichler <f.gruenbichler@proxmox.com>
This commit is contained in:
Fabian Grünbichler 2022-05-24 10:46:37 +02:00 committed by Thomas Lamprecht
parent 99e1399729
commit 210ded9803
4 changed files with 50 additions and 36 deletions

View File

@ -82,14 +82,10 @@ fn get_group_note_path(
} }
// TODO: move somewhere we can reuse it from (namespace has its own copy atm.) // 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<u64, Error> { fn get_ns_privs(store_with_ns: &DatastoreWithNamespace, auth_id: &Authid) -> Result<u64, Error> {
let user_info = CachedUserInfo::new()?; let user_info = CachedUserInfo::new()?;
Ok(if ns.is_root() { Ok(user_info.lookup_privs(auth_id, &store_with_ns.acl_path()))
user_info.lookup_privs(auth_id, &["datastore", store])
} else {
user_info.lookup_privs(auth_id, &["datastore", store, &ns.to_string()])
})
} }
// asserts that either either `full_access_privs` or `partial_access_privs` are fulfilled, // asserts that either either `full_access_privs` or `partial_access_privs` are fulfilled,
@ -101,7 +97,11 @@ fn check_ns_privs(
full_access_privs: u64, full_access_privs: u64,
partial_access_privs: u64, partial_access_privs: u64,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let privs = get_ns_privs(store, ns, auth_id)?; let store_with_ns = DatastoreWithNamespace {
store: store.to_string(),
ns: ns.clone(),
};
let privs = get_ns_privs(&store_with_ns, auth_id)?;
if full_access_privs != 0 && (privs & full_access_privs) != 0 { if full_access_privs != 0 && (privs & full_access_privs) != 0 {
return Ok(false); return Ok(false);
@ -1199,7 +1199,11 @@ fn can_access_any_ns(store: Arc<DataStore>, auth_id: &Authid, user_info: &Cached
PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP; PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_READ | PRIV_DATASTORE_BACKUP;
let name = store.name(); let name = store.name();
iter.any(|ns| -> bool { iter.any(|ns| -> bool {
let user_privs = user_info.lookup_privs(&auth_id, &["datastore", name, &ns.to_string()]); let store_with_ns = DatastoreWithNamespace {
store: name.to_string(),
ns: ns,
};
let user_privs = user_info.lookup_privs(&auth_id, &store_with_ns.acl_path());
user_privs & wanted != 0 user_privs & wanted != 0
}) })
} }

View File

@ -6,21 +6,18 @@ use proxmox_router::{http_bail, ApiMethod, Permission, Router, RpcEnvironment};
use proxmox_schema::*; use proxmox_schema::*;
use pbs_api_types::{ use pbs_api_types::{
Authid, BackupNamespace, NamespaceListItem, Operation, DATASTORE_SCHEMA, NS_MAX_DEPTH_SCHEMA, Authid, BackupNamespace, DatastoreWithNamespace, NamespaceListItem, Operation,
PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, PROXMOX_SAFE_ID_FORMAT, DATASTORE_SCHEMA, NS_MAX_DEPTH_SCHEMA, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
PRIV_DATASTORE_MODIFY, PROXMOX_SAFE_ID_FORMAT,
}; };
use pbs_datastore::DataStore; use pbs_datastore::DataStore;
// TODO: move somewhere we can reuse it from (datastore has its own copy atm.) // TODO: move somewhere we can reuse it from (datastore has its own copy atm.)
fn get_ns_privs(store: &str, ns: &BackupNamespace, auth_id: &Authid) -> Result<u64, Error> { fn get_ns_privs(store_with_ns: &DatastoreWithNamespace, auth_id: &Authid) -> Result<u64, Error> {
let user_info = CachedUserInfo::new()?; let user_info = CachedUserInfo::new()?;
Ok(if ns.is_root() { Ok(user_info.lookup_privs(auth_id, &store_with_ns.acl_path()))
user_info.lookup_privs(auth_id, &["datastore", store])
} else {
user_info.lookup_privs(auth_id, &["datastore", store, &ns.to_string()])
})
} }
#[api( #[api(
@ -59,7 +56,12 @@ pub fn create_namespace(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let parent = parent.unwrap_or_default(); let parent = parent.unwrap_or_default();
if get_ns_privs(&store, &parent, &auth_id)? & PRIV_DATASTORE_MODIFY == 0 { let store_with_parent = DatastoreWithNamespace {
store: store.clone(),
ns: parent.clone(),
};
if get_ns_privs(&store_with_parent, &auth_id)? & PRIV_DATASTORE_MODIFY == 0 {
proxmox_router::http_bail!(FORBIDDEN, "permission check failed"); proxmox_router::http_bail!(FORBIDDEN, "permission check failed");
} }
@ -104,7 +106,12 @@ pub fn list_namespaces(
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
const PRIVS_OK: u64 = PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP | PRIV_DATASTORE_AUDIT; const PRIVS_OK: u64 = PRIV_DATASTORE_MODIFY | PRIV_DATASTORE_BACKUP | PRIV_DATASTORE_AUDIT;
// first do a base check to avoid leaking if a NS exists or not // first do a base check to avoid leaking if a NS exists or not
if get_ns_privs(&store, &parent, &auth_id)? & PRIVS_OK == 0 { let store_with_parent = DatastoreWithNamespace {
store: store.clone(),
ns: parent.clone(),
};
if get_ns_privs(&store_with_parent, &auth_id)? & PRIVS_OK == 0 {
proxmox_router::http_bail!(FORBIDDEN, "permission check failed"); proxmox_router::http_bail!(FORBIDDEN, "permission check failed");
} }
let user_info = CachedUserInfo::new()?; let user_info = CachedUserInfo::new()?;
@ -161,7 +168,11 @@ pub fn delete_namespace(
}; };
let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?;
let parent = ns.parent(); // must have MODIFY permission on parent to allow deletion let parent = ns.parent(); // must have MODIFY permission on parent to allow deletion
if get_ns_privs(&store, &parent, &auth_id)? & PRIV_DATASTORE_MODIFY == 0 { let store_with_parent = DatastoreWithNamespace {
store: store.clone(),
ns: parent.clone(),
};
if get_ns_privs(&store_with_parent, &auth_id)? & PRIV_DATASTORE_MODIFY == 0 {
http_bail!(FORBIDDEN, "permission check failed"); http_bail!(FORBIDDEN, "permission check failed");
} }

View File

@ -17,9 +17,10 @@ use proxmox_schema::*;
use proxmox_sys::sortable; use proxmox_sys::sortable;
use pbs_api_types::{ use pbs_api_types::{
Authid, BackupNamespace, BackupType, Operation, SnapshotVerifyState, VerifyState, Authid, BackupNamespace, BackupType, DatastoreWithNamespace, Operation, SnapshotVerifyState,
BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA, BACKUP_TIME_SCHEMA, VerifyState, BACKUP_ARCHIVE_NAME_SCHEMA, BACKUP_ID_SCHEMA, BACKUP_NAMESPACE_SCHEMA,
BACKUP_TYPE_SCHEMA, CHUNK_DIGEST_SCHEMA, DATASTORE_SCHEMA, PRIV_DATASTORE_BACKUP, BACKUP_TIME_SCHEMA, BACKUP_TYPE_SCHEMA, CHUNK_DIGEST_SCHEMA, DATASTORE_SCHEMA,
PRIV_DATASTORE_BACKUP,
}; };
use pbs_config::CachedUserInfo; use pbs_config::CachedUserInfo;
use pbs_datastore::index::IndexFile; use pbs_datastore::index::IndexFile;
@ -81,15 +82,15 @@ fn upgrade_to_backup_protocol(
let store = required_string_param(&param, "store")?.to_owned(); let store = required_string_param(&param, "store")?.to_owned();
let backup_ns = optional_ns_param(&param)?; let backup_ns = optional_ns_param(&param)?;
let store_with_ns = DatastoreWithNamespace {
store: store.clone(),
ns: backup_ns.clone(),
};
let backup_dir_arg = pbs_api_types::BackupDir::deserialize(&param)?; let backup_dir_arg = pbs_api_types::BackupDir::deserialize(&param)?;
let user_info = CachedUserInfo::new()?; let user_info = CachedUserInfo::new()?;
let privs = if backup_ns.is_root() { let privs = user_info.lookup_privs(&auth_id, &store_with_ns.acl_path());
user_info.lookup_privs(&auth_id, &["datastore", &store])
} else {
user_info.lookup_privs(&auth_id, &["datastore", &store, &backup_ns.to_string()])
};
if privs & PRIV_DATASTORE_BACKUP == 0 { if privs & PRIV_DATASTORE_BACKUP == 0 {
proxmox_router::http_bail!(FORBIDDEN, "permission check failed"); proxmox_router::http_bail!(FORBIDDEN, "permission check failed");
} }

View File

@ -3,8 +3,8 @@ use std::sync::Arc;
use anyhow::Error; use anyhow::Error;
use pbs_api_types::{ use pbs_api_types::{
Authid, BackupNamespace, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_MODIFY, Authid, BackupNamespace, DatastoreWithNamespace, PRIV_DATASTORE_AUDIT, PRIV_DATASTORE_BACKUP,
PRIV_DATASTORE_READ, PRIV_DATASTORE_MODIFY, PRIV_DATASTORE_READ,
}; };
use pbs_config::CachedUserInfo; use pbs_config::CachedUserInfo;
use pbs_datastore::{backup_info::BackupGroup, DataStore, ListGroups, ListNamespacesRecursive}; use pbs_datastore::{backup_info::BackupGroup, DataStore, ListGroups, ListNamespacesRecursive};
@ -100,14 +100,12 @@ impl<'a> Iterator for ListAccessibleBackupGroups<'a> {
let mut override_owner = false; let mut override_owner = false;
if let Some(auth_id) = &self.auth_id { if let Some(auth_id) = &self.auth_id {
let info = &self.user_info; let info = &self.user_info;
let privs = if ns.is_root() { let store_with_ns = DatastoreWithNamespace {
info.lookup_privs(&auth_id, &["datastore", self.store.name()]) store: self.store.name().to_string(),
} else { ns: ns.clone(),
info.lookup_privs(
&auth_id,
&["datastore", self.store.name(), &ns.to_string()],
)
}; };
let privs = info.lookup_privs(&auth_id, &store_with_ns.acl_path());
if privs & NS_PRIVS_OK == 0 { if privs & NS_PRIVS_OK == 0 {
continue; continue;
} }