mirror of
https://git.proxmox.com/git/proxmox
synced 2025-08-13 13:40:15 +00:00
product-config: add rust API type for configuration digest
Signed-off-by: Dietmar Maurer <dietmar@proxmox.com>
This commit is contained in:
parent
9fc48d96d2
commit
e64575b6a7
@ -11,6 +11,11 @@ exclude.workspace = true
|
||||
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
hex.workspace = true
|
||||
log.workspace = true
|
||||
nix.workspace = true
|
||||
openssl.workspace = true
|
||||
serde.workspace = true
|
||||
serde_plain.workspace = true
|
||||
proxmox-sys = { workspace = true, features = [ "timer" ] }
|
||||
proxmox-schema = { workspace = true, features = [ "api-types" ] }
|
117
proxmox-product-config/src/digest.rs
Normal file
117
proxmox-product-config/src/digest.rs
Normal file
@ -0,0 +1,117 @@
|
||||
use anyhow::{bail, Error};
|
||||
use openssl::sha;
|
||||
|
||||
use proxmox_schema::api_types::SHA256_HEX_REGEX;
|
||||
use proxmox_schema::ApiStringFormat;
|
||||
use proxmox_schema::ApiType;
|
||||
use proxmox_schema::Schema;
|
||||
use proxmox_schema::StringSchema;
|
||||
|
||||
pub const PROXMOX_CONFIG_DIGEST_FORMAT: ApiStringFormat =
|
||||
ApiStringFormat::Pattern(&SHA256_HEX_REGEX);
|
||||
|
||||
pub const PROXMOX_CONFIG_DIGEST_SCHEMA: Schema = StringSchema::new(
|
||||
"Prevent changes if current configuration file has different \
|
||||
SHA256 digest. This can be used to prevent concurrent \
|
||||
modifications.",
|
||||
)
|
||||
.format(&PROXMOX_CONFIG_DIGEST_FORMAT)
|
||||
.schema();
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
/// A configuration digest - a SHA256 hash.
|
||||
pub struct ConfigDigest([u8; 32]);
|
||||
|
||||
impl ConfigDigest {
|
||||
pub fn to_hex(&self) -> String {
|
||||
hex::encode(&self.0[..])
|
||||
}
|
||||
|
||||
pub fn from_slice<T: AsRef<[u8]>>(data: T) -> ConfigDigest {
|
||||
let digest = sha::sha256(data.as_ref());
|
||||
ConfigDigest(digest)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApiType for ConfigDigest {
|
||||
const API_SCHEMA: Schema = PROXMOX_CONFIG_DIGEST_SCHEMA;
|
||||
}
|
||||
|
||||
impl From<[u8; 32]> for ConfigDigest {
|
||||
#[inline]
|
||||
fn from(digest: [u8; 32]) -> Self {
|
||||
Self(digest)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ConfigDigest> for [u8; 32] {
|
||||
#[inline]
|
||||
fn from(digest: ConfigDigest) -> Self {
|
||||
digest.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for ConfigDigest {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8; 32]> for ConfigDigest {
|
||||
fn as_ref(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for ConfigDigest {
|
||||
type Target = [u8; 32];
|
||||
|
||||
fn deref(&self) -> &[u8; 32] {
|
||||
&self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::DerefMut for ConfigDigest {
|
||||
fn deref_mut(&mut self) -> &mut [u8; 32] {
|
||||
&mut self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for ConfigDigest {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.to_hex())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::str::FromStr for ConfigDigest {
|
||||
type Err = hex::FromHexError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, hex::FromHexError> {
|
||||
let mut digest = [0u8; 32];
|
||||
hex::decode_to_slice(s, &mut digest)?;
|
||||
Ok(ConfigDigest(digest))
|
||||
}
|
||||
}
|
||||
|
||||
serde_plain::derive_deserialize_from_fromstr!(ConfigDigest, "valid configuration digest");
|
||||
serde_plain::derive_serialize_from_display!(ConfigDigest);
|
||||
|
||||
/// Detect modified configuration files
|
||||
///
|
||||
/// This function fails with a reasonable error message if checksums do not match.
|
||||
pub fn detect_modified_configuration_file(
|
||||
user_digest: Option<&[u8; 32]>,
|
||||
config_digest: &[u8; 32],
|
||||
) -> Result<(), Error> {
|
||||
use hex::FromHex;
|
||||
|
||||
let user_digest = match user_digest {
|
||||
Some(digest) => <[u8; 32]>::from_hex(digest)?,
|
||||
None => return Ok(()),
|
||||
};
|
||||
|
||||
if user_digest != *config_digest {
|
||||
bail!("detected modified configuration - file changed by other user? Try again.");
|
||||
}
|
||||
Ok(())
|
||||
}
|
@ -7,6 +7,12 @@ use nix::fcntl::OFlag;
|
||||
use nix::sys::stat::Mode;
|
||||
use nix::unistd::{Gid, Uid};
|
||||
|
||||
mod digest;
|
||||
pub use digest::{
|
||||
detect_modified_configuration_file, ConfigDigest, PROXMOX_CONFIG_DIGEST_FORMAT,
|
||||
PROXMOX_CONFIG_DIGEST_SCHEMA,
|
||||
};
|
||||
|
||||
static mut PRODUCT_CONFIG: Option<ProxmoxProductConfig> = None;
|
||||
|
||||
/// Initialize the global product configuration.
|
||||
@ -43,8 +49,6 @@ impl ProxmoxProductConfig {
|
||||
path.push(rel_path);
|
||||
path
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Check file/directory permissions
|
||||
@ -104,7 +108,6 @@ pub fn mkdir_permissions(dir: &str, uid: Uid, gid: Gid, mode: u32) -> Result<(),
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
/// Atomically write data to file owned by `root:api-user` with permission `0640`
|
||||
///
|
||||
/// Only the superuser can write those files, but group 'api-user' can read them.
|
||||
|
Loading…
Reference in New Issue
Block a user