]> git.proxmox.com Git - proxmox-backup.git/commitdiff
src/backup/manifest.rs: include signature inside the manifest
authorDietmar Maurer <dietmar@proxmox.com>
Wed, 8 Jul 2020 14:07:14 +0000 (16:07 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Wed, 8 Jul 2020 14:23:26 +0000 (16:23 +0200)
This is more flexible, because we can choose what fileds we want to sign.

src/backup/manifest.rs
src/bin/proxmox-backup-client.rs
src/bin/proxmox_backup_client/catalog.rs
src/bin/proxmox_backup_client/mount.rs
src/client/backup_reader.rs

index ec7d310492a3cebe5737d1ab38bf46051bf20388..0dbd6558ef6585d0a1e67073413648bf0ab9baff 100644 (file)
@@ -4,7 +4,7 @@ use std::path::Path;
 
 use serde_json::{json, Value};
 
-use crate::backup::{BackupDir, CryptMode};
+use crate::backup::{BackupDir, CryptMode, CryptConfig};
 
 pub const MANIFEST_BLOB_NAME: &str = "index.json.blob";
 pub const CLIENT_LOG_BLOB_NAME: &str = "client.log.blob";
@@ -84,8 +84,41 @@ impl BackupManifest {
         Ok(())
     }
 
-    pub fn into_json(self) -> Value {
-        json!({
+    pub fn signature(&self, crypt_config: &CryptConfig) -> [u8; 32] {
+
+        let mut data = String::new();
+
+        data.push_str(self.snapshot.group().backup_type());
+        data.push('\n');
+        data.push_str(self.snapshot.group().backup_id());
+        data.push('\n');
+        data.push_str(&format!("{}", self.snapshot.backup_time().timestamp()));
+        data.push('\n');
+        data.push('\n');
+
+        for info in self.files.iter() {
+            data.push_str(&info.filename);
+            data.push('\n');
+            data.push_str(match info.crypt_mode {
+                CryptMode::None => "None",
+                CryptMode::SignOnly => "SignOnly",
+                CryptMode::Encrypt => "Encrypt",
+            });
+            data.push('\n');
+            data.push_str(&format!("{}", info.size));
+            data.push('\n');
+            data.push_str(&proxmox::tools::digest_to_hex(&info.csum));
+            data.push('\n');
+
+            data.push('\n');
+        }
+
+        crypt_config.compute_auth_tag(data.as_bytes())
+    }
+
+    pub fn into_json(self, crypt_config: Option<&CryptConfig>) -> Value {
+
+        let mut manifest = json!({
             "backup-type": self.snapshot.group().backup_type(),
             "backup-id": self.snapshot.group().backup_id(),
             "backup-time": self.snapshot.backup_time().timestamp(),
@@ -99,7 +132,14 @@ impl BackupManifest {
                     }));
                     acc
                 })
-        })
+        });
+
+        if let Some(crypt_config) = crypt_config {
+            let sig = self.signature(crypt_config);
+            manifest["signature"] = proxmox::tools::digest_to_hex(&sig).into();
+        }
+
+        manifest
     }
 
 }
index a5dd214600d724544edae99b37ae171245c00fd0..6794068bd60c6b515404eef4ba05a423235b2ab9 100644 (file)
@@ -1081,7 +1081,7 @@ async fn create_backup(
     }
 
     // create manifest (index.json)
-    let manifest = manifest.into_json();
+    let manifest = manifest.into_json(crypt_config.as_ref().map(Arc::as_ref));
 
     println!("Upload index.json to '{:?}'", repo);
     let manifest = serde_json::to_string_pretty(&manifest)?.into();
@@ -1272,18 +1272,17 @@ async fn restore(param: Value) -> Result<Value, Error> {
         true,
     ).await?;
 
-    let manifest = client.download_manifest().await?;
+    let (manifest, backup_index_data) = client.download_manifest().await?;
 
     let (archive_name, archive_type) = parse_archive_type(archive_name);
 
     if archive_name == MANIFEST_BLOB_NAME {
-        let backup_index_data = manifest.into_json().to_string();
         if let Some(target) = target {
-            replace_file(target, backup_index_data.as_bytes(), CreateOptions::new())?;
+            replace_file(target, &backup_index_data, CreateOptions::new())?;
         } else {
             let stdout = std::io::stdout();
             let mut writer = stdout.lock();
-            writer.write_all(backup_index_data.as_bytes())
+            writer.write_all(&backup_index_data)
                 .map_err(|err| format_err!("unable to pipe data - {}", err))?;
         }
 
index ecc1278be267f43ff86f1041d0eab0ccbabb25f2..73ad6ad5ffc5ba62efcc95f075bfd05b3d75ff0b 100644 (file)
@@ -82,7 +82,7 @@ async fn dump_catalog(param: Value) -> Result<Value, Error> {
         true,
     ).await?;
 
-    let manifest = client.download_manifest().await?;
+    let (manifest, _) = client.download_manifest().await?;
 
     let index = client.download_dynamic_index(&manifest, CATALOG_NAME).await?;
 
@@ -181,7 +181,7 @@ async fn catalog_shell(param: Value) -> Result<(), Error> {
         .custom_flags(libc::O_TMPFILE)
         .open("/tmp")?;
 
-    let manifest = client.download_manifest().await?;
+    let (manifest, _) = client.download_manifest().await?;
 
     let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
     let most_used = index.find_most_used_chunks(8);
index 15cd663c89511be5fadbf8698c8153057ecf749a..73bb8d4c2c38d6c6f4352393ce3e3bc899b7d774 100644 (file)
@@ -139,7 +139,7 @@ async fn mount_do(param: Value, pipe: Option<RawFd>) -> Result<Value, Error> {
         true,
     ).await?;
 
-    let manifest = client.download_manifest().await?;
+    let (manifest, _) = client.download_manifest().await?;
 
     if server_archive_name.ends_with(".didx") {
         let index = client.download_dynamic_index(&manifest, &server_archive_name).await?;
index 9c9e14d15185e8a963c132f141d03ce5adf69ae2..0f121b4f7c803f786e25665f21255d0ba6d6b7e6 100644 (file)
@@ -1,4 +1,4 @@
-use anyhow::{format_err, Error};
+use anyhow::{bail, format_err, Error};
 use std::io::{Read, Write, Seek, SeekFrom};
 use std::fs::File;
 use std::sync::Arc;
@@ -123,7 +123,9 @@ impl BackupReader {
     }
 
     /// Download backup manifest (index.json)
-    pub async fn download_manifest(&self) -> Result<BackupManifest, Error> {
+    ///
+    /// The manifest signature is verified if we have a crypt_config.
+    pub async fn download_manifest(&self) -> Result<(BackupManifest, Vec<u8>), Error> {
 
         use std::convert::TryFrom;
 
@@ -131,10 +133,25 @@ impl BackupReader {
         self.download(MANIFEST_BLOB_NAME, &mut raw_data).await?;
         let blob = DataBlob::from_raw(raw_data)?;
         blob.verify_crc()?;
-        let data = blob.decode(self.crypt_config.as_ref().map(Arc::as_ref))?;
+        let data = blob.decode(None)?;
         let json: Value = serde_json::from_slice(&data[..])?;
 
-        BackupManifest::try_from(json)
+        let signature = json["signature"].as_str().map(String::from);
+
+        let manifest = BackupManifest::try_from(json)?;
+
+        if let Some(ref crypt_config) = self.crypt_config {
+            if let Some(signature) = signature {
+                let expected_signature = proxmox::tools::digest_to_hex(&manifest.signature(crypt_config));
+                if signature != expected_signature {
+                    bail!("wrong signature in manifest");
+                }
+            } else {
+                // warn/fail?
+            }
+        }
+
+        Ok((manifest, data))
     }
 
     /// Download a .blob file