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 73EC262DD0 for ; Mon, 8 Feb 2021 14:09:24 +0100 (CET) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 53B322235D for ; Mon, 8 Feb 2021 14:08:54 +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 C659822350 for ; Mon, 8 Feb 2021 14:08:52 +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 599C04557D for ; Mon, 8 Feb 2021 14:08:52 +0100 (CET) From: =?UTF-8?q?Fabian=20Gr=C3=BCnbichler?= To: pve-devel@lists.proxmox.com Date: Mon, 8 Feb 2021 14:08:32 +0100 Message-Id: <20210208130835.2512356-2-f.gruenbichler@proxmox.com> X-Mailer: git-send-email 2.20.1 In-Reply-To: <20210208130835.2512356-1-f.gruenbichler@proxmox.com> References: <20210208130835.2512356-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.026 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 Subject: [pve-devel] [PATCH proxmox-backup-qemu] api: add master key support X-BeenThere: pve-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox VE development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 08 Feb 2021 13:09:24 -0000 this is a breaking change/API extension. Signed-off-by: Fabian Grünbichler --- 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>, + rsa_encrypted_key: Option>, writer: OnceCell>, last_manifest: OnceCell>, manifest: Arc>, @@ -44,16 +47,28 @@ impl BackupTask { runtime: Arc ) -> Result { - 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::::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 { @@ -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, crypt_config: Option>, + rsa_encrypted_key: Option>, manifest: Arc>, ) -> Result { + 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, pub key_password: Option, + pub master_keyfile: Option, pub fingerprint: Option, } @@ -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