From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 9A62C66554 for ; Fri, 6 Nov 2020 11:23:48 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 94C6F1FF1B for ; Fri, 6 Nov 2020 11:23:18 +0100 (CET) Received: from proxmox-new.maurer-it.com (proxmox-new.maurer-it.com [212.186.127.180]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by firstgate.proxmox.com (Proxmox) with ESMTPS id D3E441FF11 for ; Fri, 6 Nov 2020 11:23:17 +0100 (CET) Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id A262645FCD for ; Fri, 6 Nov 2020 11:23:17 +0100 (CET) From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= To: pbs-devel@lists.proxmox.com Date: Fri, 6 Nov 2020 11:23:07 +0100 Message-Id: <20201106102309.1458959-2-f.gruenbichler@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20201106102309.1458959-1-f.gruenbichler@proxmox.com> References: <20201106102309.1458959-1-f.gruenbichler@proxmox.com> MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.024 Adjusted score from AWL reputation of From: address KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_DNSWL_MED -2.3 Sender listed at https://www.dnswl.org/, medium trust SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [job.store, data.store, verify.rs] Subject: [pbs-devel] [PATCH proxmox-backup 1/3] verify: fix unprivileged verification jobs X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 06 Nov 2020 10:23:48 -0000 since the store is not a path parameter, we need to do manual instead of schema checks. also dropping Datastore.Backup here Signed-off-by: Fabian Grünbichler --- src/api2/config/verify.rs | 92 +++++++++++++++++++++++++++------------ 1 file changed, 64 insertions(+), 28 deletions(-) diff --git a/src/api2/config/verify.rs b/src/api2/config/verify.rs index 0c5a6460..2c03e33e 100644 --- a/src/api2/config/verify.rs +++ b/src/api2/config/verify.rs @@ -9,10 +9,11 @@ use crate::api2::types::*; use crate::config::acl::{ PRIV_DATASTORE_AUDIT, - PRIV_DATASTORE_BACKUP, PRIV_DATASTORE_VERIFY, }; +use crate::config::cached_user_info::CachedUserInfo; + use crate::config::verify::{self, VerificationJobConfig}; #[api( @@ -25,10 +26,8 @@ use crate::config::verify::{self, VerificationJobConfig}; items: { type: verify::VerificationJobConfig }, }, access: { - permission: &Permission::Privilege( - &["datastore", "{store}"], - PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP | PRIV_DATASTORE_VERIFY, - true), + permission: &Permission::Anybody, + description: "Requires Datastore.Audit or Datastore.Verify on datastore.", }, )] /// List all verification jobs @@ -36,11 +35,22 @@ pub fn list_verification_jobs( _param: Value, mut rpcenv: &mut dyn RpcEnvironment, ) -> Result, Error> { + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + let user_info = CachedUserInfo::new()?; + + let required_privs = PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_VERIFY; let (config, digest) = verify::config()?; let list = config.convert_to_typed_array("verification")?; + let list = list.into_iter() + .filter(|job: &VerificationJobConfig| { + let privs = user_info.lookup_privs(&auth_id, &["datastore", &job.store]); + + privs & required_privs != 00 + }).collect(); + rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); Ok(list) @@ -76,19 +86,24 @@ pub fn list_verification_jobs( } }, access: { - permission: &Permission::Privilege( - &["datastore", "{store}"], - PRIV_DATASTORE_VERIFY, - true), + permission: &Permission::Anybody, + description: "Requires Datastore.Verify on job's datastore.", }, )] /// Create a new verification job. -pub fn create_verification_job(param: Value) -> Result<(), Error> { - - let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; +pub fn create_verification_job( + param: Value, + rpcenv: &mut dyn RpcEnvironment +) -> Result<(), Error> { + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + let user_info = CachedUserInfo::new()?; let verification_job: verify::VerificationJobConfig = serde_json::from_value(param.clone())?; + user_info.check_privs(&auth_id, &["datastore", &verification_job.store], PRIV_DATASTORE_VERIFY, false)?; + + let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; + let (mut config, _digest) = verify::config()?; if let Some(_) = config.sections.get(&verification_job.id) { @@ -117,10 +132,8 @@ pub fn create_verification_job(param: Value) -> Result<(), Error> { type: verify::VerificationJobConfig, }, access: { - permission: &Permission::Privilege( - &["datastore", "{store}"], - PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_BACKUP | PRIV_DATASTORE_VERIFY, - true), + permission: &Permission::Anybody, + description: "Requires Datastore.Audit or Datastore.Verify on job's datastore.", }, )] /// Read a verification job configuration. @@ -128,9 +141,16 @@ pub fn read_verification_job( id: String, mut rpcenv: &mut dyn RpcEnvironment, ) -> Result { + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + let user_info = CachedUserInfo::new()?; + let (config, digest) = verify::config()?; - let verification_job = config.lookup("verification", &id)?; + let verification_job: verify::VerificationJobConfig = config.lookup("verification", &id)?; + + let required_privs = PRIV_DATASTORE_AUDIT | PRIV_DATASTORE_VERIFY; + user_info.check_privs(&auth_id, &["datastore", &verification_job.store], required_privs, true)?; + rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into(); Ok(verification_job) @@ -193,10 +213,8 @@ pub enum DeletableProperty { }, }, access: { - permission: &Permission::Privilege( - &["datastore", "{store}"], - PRIV_DATASTORE_VERIFY, - true), + permission: &Permission::Anybody, + description: "Requires Datastore.Verify on job's datastore.", }, )] /// Update verification job config. @@ -209,7 +227,10 @@ pub fn update_verification_job( schedule: Option, delete: Option>, digest: Option, + rpcenv: &mut dyn RpcEnvironment, ) -> Result<(), Error> { + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + let user_info = CachedUserInfo::new()?; let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; @@ -223,7 +244,10 @@ pub fn update_verification_job( let mut data: verify::VerificationJobConfig = config.lookup("verification", &id)?; - if let Some(delete) = delete { + // check existing store + user_info.check_privs(&auth_id, &["datastore", &data.store], PRIV_DATASTORE_VERIFY, true)?; + + if let Some(delete) = delete { for delete_prop in delete { match delete_prop { DeletableProperty::IgnoreVerified => { data.ignore_verified = None; }, @@ -243,7 +267,12 @@ pub fn update_verification_job( } } - if let Some(store) = store { data.store = store; } + if let Some(store) = store { + // check new store + user_info.check_privs(&auth_id, &["datastore", &store], PRIV_DATASTORE_VERIFY, true)?; + data.store = store; + } + if ignore_verified.is_some() { data.ignore_verified = ignore_verified; } if outdated_after.is_some() { data.outdated_after = outdated_after; } @@ -270,19 +299,26 @@ pub fn update_verification_job( }, }, access: { - permission: &Permission::Privilege( - &["datastore", "{store}"], - PRIV_DATASTORE_VERIFY, - true), + permission: &Permission::Anybody, + description: "Requires Datastore.Verify on job's datastore.", }, )] /// Remove a verification job configuration -pub fn delete_verification_job(id: String, digest: Option) -> Result<(), Error> { +pub fn delete_verification_job( + id: String, + digest: Option, + rpcenv: &mut dyn RpcEnvironment, +) -> Result<(), Error> { + let auth_id: Authid = rpcenv.get_auth_id().unwrap().parse()?; + let user_info = CachedUserInfo::new()?; let _lock = open_file_locked(verify::VERIFICATION_CFG_LOCKFILE, std::time::Duration::new(10, 0), true)?; let (mut config, expected_digest) = verify::config()?; + let job: verify::VerificationJobConfig = config.lookup("verification", &id)?; + user_info.check_privs(&auth_id, &["datastore", &job.store], PRIV_DATASTORE_VERIFY, true)?; + if let Some(ref digest) = digest { let digest = proxmox::tools::hex_to_digest(digest)?; crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; -- 2.20.1