From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id E1CF11FF13C for ; Thu, 02 Apr 2026 15:41:09 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 7DE5E1B519; Thu, 2 Apr 2026 15:41:39 +0200 (CEST) From: Christian Ebner To: pbs-devel@lists.proxmox.com Subject: [PATCH proxmox-backup] pbs-config: refactor and move helper to detect config digest changes Date: Thu, 2 Apr 2026 15:41:10 +0200 Message-ID: <20260402134110.848575-1-c.ebner@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1775137230583 X-SPAM-LEVEL: Spam detection results: 0 AWL -1.432 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 1 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Message-ID-Hash: ZRXZES3WLTP7H3TRSSMYA6CLN6GLUMIF X-Message-ID-Hash: ZRXZES3WLTP7H3TRSSMYA6CLN6GLUMIF X-MailFrom: c.ebner@proxmox.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; emergency; member-moderation; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; digests; suspicious-header X-Mailman-Version: 3.3.10 Precedence: list List-Id: Proxmox Backup Server development discussion List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: This is such a common pattern for a huge number of API handlers that refactoring it into a common helper is waranted. As a positive side effect, direct dependencies on the hex crate for the API handlers are reduced. By moving the helper to pbs-config it can be used also from within pbs-config, which will be used to detect config changes for encrypton keys in the future. In that particular case, keeping all the locking and digest change detection logic inside that crate makes sense since the keys are stored separately, which is an implementation detail the API handler should not be concerned with. No functional changes intended. Signed-off-by: Christian Ebner --- While encountered when working on the config api for encrypted push sync jobs, this touches so many API handlers that sending this as dedicated patch for independent review already. pbs-config/Cargo.toml | 1 + pbs-config/src/lib.rs | 19 +++++++++++++++++- src/api2/access/acl.rs | 6 +----- src/api2/access/user.rs | 26 +++++-------------------- src/api2/config/access/ad.rs | 6 +----- src/api2/config/access/ldap.rs | 11 ++--------- src/api2/config/access/openid.rs | 11 ++--------- src/api2/config/access/pam.rs | 6 +----- src/api2/config/access/pbs.rs | 6 +----- src/api2/config/access/tfa.rs | 12 ++++-------- src/api2/config/changer.rs | 6 +----- src/api2/config/datastore.rs | 11 ++--------- src/api2/config/drive.rs | 6 +----- src/api2/config/metrics/influxdbhttp.rs | 11 ++--------- src/api2/config/metrics/influxdbudp.rs | 11 ++--------- src/api2/config/prune.rs | 11 ++--------- src/api2/config/remote.rs | 11 ++--------- src/api2/config/s3.rs | 11 ++--------- src/api2/config/sync.rs | 11 ++--------- src/api2/config/tape_backup_job.rs | 11 ++--------- src/api2/config/tape_encryption_keys.rs | 11 ++--------- src/api2/config/traffic_control.rs | 11 ++--------- src/api2/config/verify.rs | 11 ++--------- src/api2/node/config.rs | 4 +--- src/api2/node/network.rs | 11 ++--------- src/tools/mod.rs | 13 ------------- 26 files changed, 63 insertions(+), 202 deletions(-) diff --git a/pbs-config/Cargo.toml b/pbs-config/Cargo.toml index eb81ce004..c3f1660f6 100644 --- a/pbs-config/Cargo.toml +++ b/pbs-config/Cargo.toml @@ -9,6 +9,7 @@ rust-version.workspace = true [dependencies] anyhow.workspace = true const_format.workspace = true +hex.workspace = true libc.workspace = true nix.workspace = true once_cell.workspace = true diff --git a/pbs-config/src/lib.rs b/pbs-config/src/lib.rs index 1ed472385..8b900e11c 100644 --- a/pbs-config/src/lib.rs +++ b/pbs-config/src/lib.rs @@ -20,7 +20,8 @@ pub mod verify; mod config_version_cache; pub use config_version_cache::ConfigVersionCache; -use anyhow::{format_err, Error}; +use anyhow::{bail, format_err, Error}; +use hex::FromHex; use nix::unistd::{Gid, Group, Uid, User}; use proxmox_sys::fs::DirLockGuard; use std::os::unix::prelude::AsRawFd; @@ -148,3 +149,19 @@ pub fn replace_secret_config>(path: P, data: &[u8]) -> Ok(()) } + +/// Detect modified configuration files +/// +/// This function fails with a reasonable error message if checksums do not match. +pub fn detect_modified_configuration_file>( + digest_str: Option, + expected_digest: &[u8; 32], +) -> Result<(), Error> { + if let Some(digest_str) = digest_str { + let digest = <[u8; 32]>::from_hex(digest_str.as_ref())?; + if &digest != expected_digest { + bail!("detected modified configuration - file changed by other user? Try again."); + } + } + Ok(()) +} diff --git a/src/api2/access/acl.rs b/src/api2/access/acl.rs index ee474754a..adcb799e8 100644 --- a/src/api2/access/acl.rs +++ b/src/api2/access/acl.rs @@ -1,7 +1,6 @@ //! Manage Access Control Lists use anyhow::{bail, Error}; -use hex::FromHex; use proxmox_router::{Permission, Router, RpcEnvironment}; use proxmox_schema::api; @@ -218,10 +217,7 @@ pub fn update_acl( let (mut tree, expected_digest) = pbs_config::acl::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let propagate = propagate.unwrap_or(true); diff --git a/src/api2/access/user.rs b/src/api2/access/user.rs index 3b4cf1214..0ad2ed569 100644 --- a/src/api2/access/user.rs +++ b/src/api2/access/user.rs @@ -1,7 +1,6 @@ //! User Management use anyhow::{bail, format_err, Error}; -use hex::FromHex; use serde::{Deserialize, Serialize}; use serde_json::{json, Value}; use std::collections::HashMap; @@ -269,10 +268,7 @@ pub async fn update_user( let (mut config, expected_digest) = pbs_config::user::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: User = config.lookup("user", userid.as_str())?; @@ -358,10 +354,7 @@ pub fn delete_user(userid: Userid, digest: Option) -> Result<(), Error> let (mut config, expected_digest) = pbs_config::user::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if config.sections.remove(userid.as_str()).is_none() { bail!("user '{}' does not exist.", userid); @@ -496,10 +489,7 @@ pub fn generate_token( let (mut config, expected_digest) = pbs_config::user::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let tokenid = Authid::from((userid.clone(), Some(token_name.clone()))); let tokenid_string = tokenid.to_string(); @@ -613,10 +603,7 @@ pub fn update_token( let (mut config, expected_digest) = pbs_config::user::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let tokenid = Authid::from((userid, Some(token_name))); let tokenid_string = tokenid.to_string(); @@ -699,10 +686,7 @@ pub fn delete_token( let (mut user_config, expected_digest) = pbs_config::user::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let (mut acl_config, _digest) = pbs_config::acl::config()?; do_delete_token(token_name, &userid, &mut user_config, &mut acl_config)?; diff --git a/src/api2/config/access/ad.rs b/src/api2/config/access/ad.rs index 2afb16b86..d45ab448e 100644 --- a/src/api2/config/access/ad.rs +++ b/src/api2/config/access/ad.rs @@ -1,5 +1,4 @@ use anyhow::{bail, format_err, Error}; -use hex::FromHex; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -207,10 +206,7 @@ pub async fn update_ad_realm( let (mut domains, expected_digest) = domains::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut config: AdRealmConfig = domains.lookup("ad", &realm)?; diff --git a/src/api2/config/access/ldap.rs b/src/api2/config/access/ldap.rs index 6a93ece22..c4029d70c 100644 --- a/src/api2/config/access/ldap.rs +++ b/src/api2/config/access/ldap.rs @@ -1,7 +1,6 @@ use crate::auth::LdapAuthenticator; use ::serde::{Deserialize, Serialize}; use anyhow::{format_err, Error}; -use hex::FromHex; use serde_json::Value; use proxmox_ldap::Connection; @@ -119,10 +118,7 @@ pub fn delete_ldap_realm( let (mut domains, expected_digest) = domains::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if domains.sections.remove(&realm).is_none() { http_bail!(NOT_FOUND, "realm '{}' does not exist.", realm); @@ -242,10 +238,7 @@ pub fn update_ldap_realm( let (mut domains, expected_digest) = domains::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut config: LdapRealmConfig = domains.lookup("ldap", &realm)?; diff --git a/src/api2/config/access/openid.rs b/src/api2/config/access/openid.rs index 5b767fcca..ab05bfb68 100644 --- a/src/api2/config/access/openid.rs +++ b/src/api2/config/access/openid.rs @@ -1,7 +1,6 @@ use ::serde::{Deserialize, Serialize}; /// Configure OpenId realms use anyhow::Error; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, Permission, Router, RpcEnvironment}; @@ -103,10 +102,7 @@ pub fn delete_openid_realm( let (mut domains, expected_digest) = domains::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if domains.sections.remove(&realm).is_none() { http_bail!(NOT_FOUND, "realm '{}' does not exist.", realm); @@ -207,10 +203,7 @@ pub fn update_openid_realm( let (mut domains, expected_digest) = domains::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut config: OpenIdRealmConfig = domains.lookup("openid", &realm)?; diff --git a/src/api2/config/access/pam.rs b/src/api2/config/access/pam.rs index 04ae616b9..40a7b522d 100644 --- a/src/api2/config/access/pam.rs +++ b/src/api2/config/access/pam.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::Error; -use hex::FromHex; use proxmox_router::{Permission, Router, RpcEnvironment}; use proxmox_schema::api; @@ -82,10 +81,7 @@ pub fn update_pam_realm( let (mut domains, expected_digest) = domains::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut config: PamRealmConfig = domains.lookup("pam", "pam")?; diff --git a/src/api2/config/access/pbs.rs b/src/api2/config/access/pbs.rs index 2873eabbb..536e8c854 100644 --- a/src/api2/config/access/pbs.rs +++ b/src/api2/config/access/pbs.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::Error; -use hex::FromHex; use proxmox_router::{Permission, Router, RpcEnvironment}; use proxmox_schema::api; @@ -82,10 +81,7 @@ pub fn update_pbs_realm( let (mut domains, expected_digest) = domains::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut config: PbsRealmConfig = domains.lookup("pbs", "pbs")?; diff --git a/src/api2/config/access/tfa.rs b/src/api2/config/access/tfa.rs index 5cb17bba0..19824298b 100644 --- a/src/api2/config/access/tfa.rs +++ b/src/api2/config/access/tfa.rs @@ -2,7 +2,6 @@ //! If we add more, it should be moved into a sub module. use anyhow::{format_err, Error}; -use hex::FromHex; use serde::{Deserialize, Serialize}; use proxmox_router::list_subdirs_api_method; @@ -94,13 +93,10 @@ pub fn update_webauthn_config( let mut tfa = tfa::read()?; if let Some(wa) = &mut tfa.webauthn { - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file( - &digest, - &crate::config::tfa::webauthn_config_digest(wa)?, - )?; - } + pbs_config::detect_modified_configuration_file( + digest, + &crate::config::tfa::webauthn_config_digest(wa)?, + )?; if let Some(delete) = delete { for delete in delete { diff --git a/src/api2/config/changer.rs b/src/api2/config/changer.rs index 31a15abab..f827c2b52 100644 --- a/src/api2/config/changer.rs +++ b/src/api2/config/changer.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::Error; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, Permission, Router, RpcEnvironment}; @@ -183,10 +182,7 @@ pub fn update_changer( let (mut config, expected_digest) = pbs_config::drive::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: ScsiTapeChanger = config.lookup("changer", &name)?; diff --git a/src/api2/config/datastore.rs b/src/api2/config/datastore.rs index f845fe2d0..2c5472444 100644 --- a/src/api2/config/datastore.rs +++ b/src/api2/config/datastore.rs @@ -3,7 +3,6 @@ use std::sync::Arc; use ::serde::{Deserialize, Serialize}; use anyhow::{bail, format_err, Context, Error}; -use hex::FromHex; use http_body_util::BodyExt; use serde_json::Value; use tracing::{info, warn}; @@ -479,10 +478,7 @@ pub fn update_datastore( // pass/compare digest let (mut config, expected_digest) = pbs_config::datastore::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: DataStoreConfig = config.lookup("datastore", &name)?; @@ -696,10 +692,7 @@ pub async fn delete_datastore( let (config, expected_digest) = pbs_config::datastore::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if !config.sections.contains_key(&name) { http_bail!(NOT_FOUND, "datastore '{}' does not exist.", name); diff --git a/src/api2/config/drive.rs b/src/api2/config/drive.rs index 1222ab200..6f198acdd 100644 --- a/src/api2/config/drive.rs +++ b/src/api2/config/drive.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::{format_err, Error}; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, Permission, Router, RpcEnvironment}; @@ -185,10 +184,7 @@ pub fn update_drive( let (mut config, expected_digest) = pbs_config::drive::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: LtoTapeDrive = config.lookup("lto", &name)?; diff --git a/src/api2/config/metrics/influxdbhttp.rs b/src/api2/config/metrics/influxdbhttp.rs index bed292600..6df4569b4 100644 --- a/src/api2/config/metrics/influxdbhttp.rs +++ b/src/api2/config/metrics/influxdbhttp.rs @@ -1,5 +1,4 @@ use anyhow::{bail, format_err, Error}; -use hex::FromHex; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -122,10 +121,7 @@ pub fn delete_influxdb_http_server( let (mut metrics, expected_digest) = metrics::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if metrics.sections.remove(&name).is_none() { bail!("name '{}' does not exist.", name); @@ -227,10 +223,7 @@ pub async fn update_influxdb_http_server( let (mut metrics, expected_digest) = metrics::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut config: InfluxDbHttp = metrics.lookup("influxdb-http", &name)?; diff --git a/src/api2/config/metrics/influxdbudp.rs b/src/api2/config/metrics/influxdbudp.rs index c47a4e191..60ae8a1b7 100644 --- a/src/api2/config/metrics/influxdbudp.rs +++ b/src/api2/config/metrics/influxdbudp.rs @@ -1,5 +1,4 @@ use anyhow::{bail, format_err, Error}; -use hex::FromHex; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -109,10 +108,7 @@ pub fn delete_influxdb_udp_server( let (mut metrics, expected_digest) = metrics::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if metrics.sections.remove(&name).is_none() { bail!("name '{}' does not exist.", name); @@ -204,10 +200,7 @@ pub async fn update_influxdb_udp_server( let (mut metrics, expected_digest) = metrics::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut config: InfluxDbUdp = metrics.lookup("influxdb-udp", &name)?; diff --git a/src/api2/config/prune.rs b/src/api2/config/prune.rs index b433c248a..2f846fdf0 100644 --- a/src/api2/config/prune.rs +++ b/src/api2/config/prune.rs @@ -1,5 +1,4 @@ use anyhow::Error; -use hex::FromHex; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -224,10 +223,7 @@ pub fn update_prune_job( // pass/compare digest let (mut config, expected_digest) = prune::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: PruneJobConfig = config.lookup("prune", &id)?; @@ -369,10 +365,7 @@ pub fn delete_prune_job( user_info.check_privs(&auth_id, &job.acl_path(), PRIV_DATASTORE_MODIFY, true)?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if config.sections.remove(&id).is_none() { http_bail!(NOT_FOUND, "job '{}' does not exist.", id); diff --git a/src/api2/config/remote.rs b/src/api2/config/remote.rs index 1996ee91b..f6cbca977 100644 --- a/src/api2/config/remote.rs +++ b/src/api2/config/remote.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::{bail, format_err, Error}; -use hex::FromHex; use pbs_api_types::BackupNamespace; use pbs_api_types::NamespaceListItem; use proxmox_router::list_subdirs_api_method; @@ -192,10 +191,7 @@ pub fn update_remote( let (mut config, expected_digest) = pbs_config::remote::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: Remote = config.lookup("remote", &name)?; @@ -292,10 +288,7 @@ pub fn delete_remote(name: String, digest: Option) -> Result<(), Error> let (mut config, expected_digest) = pbs_config::remote::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if config.sections.remove(&name).is_none() { http_bail!(NOT_FOUND, "remote '{}' does not exist.", name); diff --git a/src/api2/config/s3.rs b/src/api2/config/s3.rs index 29ac1038a..7a338bc83 100644 --- a/src/api2/config/s3.rs +++ b/src/api2/config/s3.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::{bail, Context, Error}; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, Permission, Router, RpcEnvironment}; @@ -200,10 +199,7 @@ pub fn update_s3_client_config( let (mut config, expected_digest) = s3::config()?; // Secrets are not included in digest concurrent changes therefore not detected. - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: S3ClientConf = config.lookup(S3_CFG_TYPE_ID, &id)?; @@ -311,10 +307,7 @@ pub fn delete_s3_client_config( let _lock = s3::lock_config()?; let (mut config, expected_digest) = s3::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if let Some(datastore) = s3_client_in_use(&id).context("failed to check if s3 client is in-use")? diff --git a/src/api2/config/sync.rs b/src/api2/config/sync.rs index dff447cb6..67fa3182c 100644 --- a/src/api2/config/sync.rs +++ b/src/api2/config/sync.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::{bail, Error}; -use hex::FromHex; use pbs_api_types::SyncDirection; use serde_json::Value; @@ -398,10 +397,7 @@ pub fn update_sync_job( let (mut config, expected_digest) = sync::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: SyncJobConfig = config.lookup("sync", &id)?; @@ -617,10 +613,7 @@ pub fn delete_sync_job( let (mut config, expected_digest) = sync::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; match config.lookup("sync", &id) { Ok(job) => { diff --git a/src/api2/config/tape_backup_job.rs b/src/api2/config/tape_backup_job.rs index 38dbe9747..e69ab99bf 100644 --- a/src/api2/config/tape_backup_job.rs +++ b/src/api2/config/tape_backup_job.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::Error; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, Permission, Router, RpcEnvironment}; @@ -193,10 +192,7 @@ pub fn update_tape_backup_job( let mut data: TapeBackupJobConfig = config.lookup("backup", &id)?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if let Some(delete) = delete { for delete_prop in delete { @@ -328,10 +324,7 @@ pub fn delete_tape_backup_job( let (mut config, expected_digest) = pbs_config::tape_job::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; match config.lookup::("backup", &id) { Ok(_job) => { diff --git a/src/api2/config/tape_encryption_keys.rs b/src/api2/config/tape_encryption_keys.rs index 788ed0e7b..a450bf435 100644 --- a/src/api2/config/tape_encryption_keys.rs +++ b/src/api2/config/tape_encryption_keys.rs @@ -1,5 +1,4 @@ use anyhow::{bail, format_err, Error}; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, ApiMethod, Permission, Router, RpcEnvironment}; @@ -115,10 +114,7 @@ pub fn change_passphrase( let (mut config_map, expected_digest) = load_key_configs()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let key_config = match config_map.get(&fingerprint) { Some(key_config) => key_config, @@ -313,10 +309,7 @@ pub fn delete_key( let (mut config_map, expected_digest) = load_key_configs()?; let (mut key_map, _) = load_keys()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; match config_map.get(&fingerprint) { Some(_) => { diff --git a/src/api2/config/traffic_control.rs b/src/api2/config/traffic_control.rs index 49b1267e9..1b18c8b03 100644 --- a/src/api2/config/traffic_control.rs +++ b/src/api2/config/traffic_control.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::Error; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, ApiMethod, Permission, Router, RpcEnvironment}; @@ -161,10 +160,7 @@ pub fn update_traffic_control( let (mut config, expected_digest) = pbs_config::traffic_control::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: TrafficControlRule = config.lookup("rule", &name)?; @@ -261,10 +257,7 @@ pub fn delete_traffic_control(name: String, digest: Option) -> Result<() let (mut config, expected_digest) = pbs_config::traffic_control::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if config.sections.remove(&name).is_none() { http_bail!(NOT_FOUND, "traffic control rule '{}' does not exist.", name); diff --git a/src/api2/config/verify.rs b/src/api2/config/verify.rs index a88a3c322..90b2236ac 100644 --- a/src/api2/config/verify.rs +++ b/src/api2/config/verify.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::Error; -use hex::FromHex; use serde_json::Value; use proxmox_router::{http_bail, Permission, Router, RpcEnvironment}; @@ -202,10 +201,7 @@ pub fn update_verification_job( // pass/compare digest let (mut config, expected_digest) = verify::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let mut data: VerificationJobConfig = config.lookup("verification", &id)?; @@ -331,10 +327,7 @@ pub fn delete_verification_job( let job: VerificationJobConfig = config.lookup("verification", &id)?; user_info.check_privs(&auth_id, &job.acl_path(), PRIV_DATASTORE_VERIFY, true)?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; match config.sections.get(&id) { Some(_) => { diff --git a/src/api2/node/config.rs b/src/api2/node/config.rs index 19ede24b7..10682bdf5 100644 --- a/src/api2/node/config.rs +++ b/src/api2/node/config.rs @@ -1,6 +1,5 @@ use ::serde::{Deserialize, Serialize}; use anyhow::Error; -use hex::FromHex; use proxmox_router::{Permission, Router, RpcEnvironment}; use proxmox_schema::api; @@ -110,8 +109,7 @@ pub fn update_node_config( if let Some(digest) = digest { // FIXME: GUI doesn't handle our non-inlined digest part here properly... if !digest.is_empty() { - let digest = <[u8; 32]>::from_hex(&digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; + pbs_config::detect_modified_configuration_file(Some(digest), &expected_digest)?; } } diff --git a/src/api2/node/network.rs b/src/api2/node/network.rs index d0a04e59d..edae5064a 100644 --- a/src/api2/node/network.rs +++ b/src/api2/node/network.rs @@ -1,5 +1,4 @@ use anyhow::{bail, Error}; -use hex::FromHex; use serde::{Deserialize, Serialize}; use serde_json::{to_value, Value}; @@ -612,10 +611,7 @@ pub fn update_interface( let (mut config, expected_digest) = network::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; if gateway.is_some() { check_duplicate_gateway_v4(&config, &iface)?; @@ -824,10 +820,7 @@ pub fn delete_interface(iface: String, digest: Option) -> Result<(), Err let (mut config, expected_digest) = network::config()?; - if let Some(ref digest) = digest { - let digest = <[u8; 32]>::from_hex(digest)?; - crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?; - } + pbs_config::detect_modified_configuration_file(digest, &expected_digest)?; let _interface = config.lookup(&iface)?; // check if interface exists diff --git a/src/tools/mod.rs b/src/tools/mod.rs index 6a975bde2..51d9ad777 100644 --- a/src/tools/mod.rs +++ b/src/tools/mod.rs @@ -29,19 +29,6 @@ pub fn assert_if_modified(digest1: &str, digest2: &str) -> Result<(), Error> { Ok(()) } -/// Detect modified configuration files -/// -/// This function fails with a reasonable error message if checksums do not match. -pub fn detect_modified_configuration_file( - digest1: &[u8; 32], - digest2: &[u8; 32], -) -> Result<(), Error> { - if digest1 != digest2 { - bail!("detected modified configuration - file changed by other user? Try again."); - } - Ok(()) -} - /// The default 2 hours are far too long for PBS pub const PROXMOX_BACKUP_TCP_KEEPALIVE_TIME: u32 = 120; pub const DEFAULT_USER_AGENT_STRING: &str = "proxmox-backup-client/1.0"; -- 2.47.3