[pve-devel] [PATCH proxmox-backup-qemu] api: add master key support

Fabian Grünbichler f.gruenbichler at proxmox.com
Mon Feb 8 14:08:32 CET 2021


this is a breaking change/API extension.

Signed-off-by: Fabian Grünbichler <f.gruenbichler at proxmox.com>
---

Notes:
    requires appropriate Breaks on old pve-qemu-kvm, and versioned build and
    runtime dep from pve-qemu-kvm on bumped libproxmox-backup-qemu.
    
    backwards compat with outdated QEMU + lib versions is handled in qemu-server

 current-api.h   |  1 +
 src/backup.rs   | 45 ++++++++++++++++++++++++++++++++++++---------
 src/commands.rs | 10 ++++++++++
 src/lib.rs      |  9 +++++++++
 4 files changed, 56 insertions(+), 9 deletions(-)

diff --git a/current-api.h b/current-api.h
index abe7e89..ddf65d5 100644
--- a/current-api.h
+++ b/current-api.h
@@ -176,6 +176,7 @@ ProxmoxBackupHandle *proxmox_backup_new(const char *repo,
                                         const char *password,
                                         const char *keyfile,
                                         const char *key_password,
+                                        const char *master_keyfile,
                                         bool compress,
                                         bool encrypt,
                                         const char *fingerprint,
diff --git a/src/backup.rs b/src/backup.rs
index e2062e7..8f64979 100644
--- a/src/backup.rs
+++ b/src/backup.rs
@@ -8,9 +8,11 @@ use futures::future::{Future, Either, FutureExt};
 use tokio::runtime::Runtime;
 
 use proxmox_backup::tools::runtime::get_runtime_with_builder;
-use proxmox_backup::backup::{CryptConfig, CryptMode, BackupDir, BackupManifest, load_and_decrypt_key};
+use proxmox_backup::backup::{CryptConfig, CryptMode, BackupDir, BackupManifest, KeyConfig, load_and_decrypt_key, rsa_encrypt_key_config};
 use proxmox_backup::client::{HttpClient, HttpClientOptions, BackupWriter};
 
+use proxmox::tools::fs::file_get_contents;
+
 use super::BackupSetup;
 use crate::capi_types::*;
 use crate::registry::Registry;
@@ -22,6 +24,7 @@ pub(crate) struct BackupTask {
     compress: bool,
     crypt_mode: CryptMode,
     crypt_config: Option<Arc<CryptConfig>>,
+    rsa_encrypted_key: Option<Vec<u8>>,
     writer: OnceCell<Arc<BackupWriter>>,
     last_manifest: OnceCell<Arc<BackupManifest>>,
     manifest: Arc<Mutex<BackupManifest>>,
@@ -44,16 +47,28 @@ impl BackupTask {
         runtime: Arc<Runtime>
     ) -> Result<Self, Error> {
 
-        let crypt_config = match setup.keyfile {
-            None => None,
+        let (crypt_config, rsa_encrypted_key) = match setup.keyfile {
+            None => (None, None),
             Some(ref path) => {
-                let (key, _, _) = load_and_decrypt_key(path, & || {
+                let (key, created, _) = load_and_decrypt_key(path, & || {
                     match setup.key_password {
                         Some(ref key_password) => Ok(key_password.as_bytes().to_vec()),
                         None => bail!("no key_password specified"),
                     }
                 })?;
-                Some(Arc::new(CryptConfig::new(key)?))
+                let rsa_encrypted_key = match setup.master_keyfile {
+                    Some(ref master_keyfile) => {
+                        let pem = file_get_contents(master_keyfile)?;
+                        let rsa = openssl::rsa::Rsa::public_key_from_pem(&pem)?;
+
+                        let mut key_config = KeyConfig::without_password(key)?;
+                        key_config.created = created; // keep original value
+
+                        Some(rsa_encrypt_key_config(rsa, &key_config)?)
+                    },
+                    None => None,
+                };
+                (Some(Arc::new(CryptConfig::new(key)?)), rsa_encrypted_key)
             }
         };
 
@@ -65,10 +80,21 @@ impl BackupTask {
         let registry = Arc::new(Mutex::new(Registry::<ImageUploadInfo>::new()));
         let known_chunks = Arc::new(Mutex::new(HashSet::new()));
 
-        Ok(Self { runtime, setup, compress, crypt_mode, crypt_config, abort,
-                  registry, manifest, known_chunks,
-                  writer: OnceCell::new(), last_manifest: OnceCell::new(),
-                  aborted: OnceCell::new() })
+        Ok(Self {
+            runtime,
+            setup,
+            compress,
+            crypt_mode,
+            crypt_config,
+            rsa_encrypted_key,
+            abort,
+            registry,
+            manifest,
+            known_chunks,
+            writer: OnceCell::new(),
+            last_manifest: OnceCell::new(),
+            aborted: OnceCell::new()
+        })
     }
 
     pub fn new(setup: BackupSetup, compress: bool, crypt_mode: CryptMode) -> Result<Self, Error> {
@@ -258,6 +284,7 @@ impl BackupTask {
         let command_future = finish_backup(
             self.need_writer()?,
             self.crypt_config.clone(),
+            self.rsa_encrypted_key.clone(),
             Arc::clone(&self.manifest),
         );
 
diff --git a/src/commands.rs b/src/commands.rs
index c63c4f7..5fdf318 100644
--- a/src/commands.rs
+++ b/src/commands.rs
@@ -438,8 +438,18 @@ pub(crate) async fn write_data(
 pub(crate) async fn finish_backup(
     client: Arc<BackupWriter>,
     crypt_config: Option<Arc<CryptConfig>>,
+    rsa_encrypted_key: Option<Vec<u8>>,
     manifest: Arc<Mutex<BackupManifest>>,
 ) -> Result<c_int, Error> {
+    if let Some(rsa_encrypted_key) = rsa_encrypted_key {
+        let target = ENCRYPTED_KEY_BLOB_NAME;
+        let options = UploadOptions { compress: false, encrypt: false, ..UploadOptions::default() };
+        let stats = client
+            .upload_blob_from_data(rsa_encrypted_key, target, options)
+            .await?;
+        manifest.lock().unwrap().add_file(target.to_string(), stats.size, stats.csum, CryptMode::Encrypt)?;
+    };
+
 
     let manifest = {
         let guard = manifest.lock().unwrap();
diff --git a/src/lib.rs b/src/lib.rs
index fd7c064..05d7b58 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -137,6 +137,7 @@ pub(crate) struct BackupSetup {
     pub password: String,
     pub keyfile: Option<std::path::PathBuf>,
     pub key_password: Option<String>,
+    pub master_keyfile: Option<std::path::PathBuf>,
     pub fingerprint: Option<String>,
 }
 
@@ -208,6 +209,7 @@ pub extern "C" fn proxmox_backup_new(
     password: *const c_char,
     keyfile: *const c_char,
     key_password: *const c_char,
+    master_keyfile: *const c_char,
     compress: bool,
     encrypt: bool,
     fingerprint: *const c_char,
@@ -227,6 +229,11 @@ pub extern "C" fn proxmox_backup_new(
         let keyfile = tools::utf8_c_string(keyfile)?.map(std::path::PathBuf::from);
         let key_password = tools::utf8_c_string(key_password)?;
         let fingerprint = tools::utf8_c_string(fingerprint)?;
+        let master_keyfile = tools::utf8_c_string(master_keyfile)?.map(std::path::PathBuf::from);
+
+        if master_keyfile.is_some() && keyfile.is_none() {
+            return Err(format_err!("can't use master keyfile without keyfile"));
+        }
 
         let crypt_mode = if keyfile.is_some() {
             if encrypt { CryptMode::Encrypt } else {  CryptMode::SignOnly }
@@ -246,6 +253,7 @@ pub extern "C" fn proxmox_backup_new(
             backup_time: backup_time as i64,
             keyfile,
             key_password,
+            master_keyfile,
             fingerprint,
         };
 
@@ -720,6 +728,7 @@ pub extern "C" fn proxmox_restore_new(
             backup_time,
             keyfile,
             key_password,
+            master_keyfile: None,
             fingerprint,
         };
 
-- 
2.20.1






More information about the pve-devel mailing list