From 2b191385eaf92c13bd51c2eddd3e090b39c6524a Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Wed, 20 Jan 2021 17:27:01 +0100 Subject: [PATCH] tape: use specialized encryption key per media-set --- src/api2/tape/drive.rs | 4 +++- src/bin/sg-tape-cmd.rs | 20 +++++++++++++++++++- src/tape/drive/linux_tape.rs | 27 +++++++++++++++++++++++---- src/tape/drive/mod.rs | 18 +++++++++++++++--- src/tape/pool_writer.rs | 10 +++++----- 5 files changed, 65 insertions(+), 14 deletions(-) diff --git a/src/api2/tape/drive.rs b/src/api2/tape/drive.rs index 5eb8b1cf..bc5b97c8 100644 --- a/src/api2/tape/drive.rs +++ b/src/api2/tape/drive.rs @@ -1053,7 +1053,9 @@ pub fn catalog_media( MediaCatalog::destroy(status_path, &media_id.label.uuid)?; return Ok(()); } - let encrypt_fingerprint = set.encryption_key_fingerprint.clone(); + let encrypt_fingerprint = set.encryption_key_fingerprint.clone() + .map(|fp| (fp, set.uuid.clone())); + drive.set_encryption(encrypt_fingerprint)?; set.pool.clone() diff --git a/src/bin/sg-tape-cmd.rs b/src/bin/sg-tape-cmd.rs index d63f595f..9cfc41a7 100644 --- a/src/bin/sg-tape-cmd.rs +++ b/src/bin/sg-tape-cmd.rs @@ -17,6 +17,7 @@ use proxmox::{ cli::*, RpcEnvironment, }, + tools::Uuid, }; use proxmox_backup::{ @@ -26,6 +27,7 @@ use proxmox_backup::{ LINUX_DRIVE_PATH_SCHEMA, DRIVE_NAME_SCHEMA, TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, + MEDIA_SET_UUID_SCHEMA, LinuxTapeDrive, }, tape::{ @@ -193,6 +195,10 @@ fn tape_alert_flags( schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA, optional: true, }, + uuid: { + schema: MEDIA_SET_UUID_SCHEMA, + optional: true, + }, drive: { schema: DRIVE_NAME_SCHEMA, optional: true, @@ -212,13 +218,25 @@ fn tape_alert_flags( /// Set or clear encryption key fn set_encryption( fingerprint: Option, + uuid: Option, param: Value, ) -> Result<(), Error> { let result = proxmox::try_block!({ let mut handle = get_tape_handle(¶m)?; - handle.set_encryption(fingerprint)?; + match (fingerprint, uuid) { + (Some(fingerprint), Some(uuid)) => { + handle.set_encryption(Some((fingerprint, uuid)))?; + } + (Some(_), None) => { + bail!("missing media set uuid"); + } + (None, _) => { + handle.set_encryption(None)?; + } + } + Ok(()) }).map_err(|err: Error| err.to_string()); diff --git a/src/tape/drive/linux_tape.rs b/src/tape/drive/linux_tape.rs index a23524a7..baa1f39c 100644 --- a/src/tape/drive/linux_tape.rs +++ b/src/tape/drive/linux_tape.rs @@ -7,6 +7,7 @@ use anyhow::{bail, format_err, Error}; use nix::fcntl::{fcntl, FcntlArg, OFlag}; use proxmox::sys::error::SysResult; +use proxmox::tools::Uuid; use crate::{ config, @@ -535,16 +536,33 @@ impl TapeDriver for LinuxTapeHandle { /// Note: Only 'root' user may run RAW SG commands, so we need to /// spawn setuid binary 'sg-tape-cmd'. Also, encryption key file /// is only readable by root. - fn set_encryption(&mut self, key_fingerprint: Option) -> Result<(), Error> { + fn set_encryption( + &mut self, + key_fingerprint: Option<(Fingerprint, Uuid)>, + ) -> Result<(), Error> { if nix::unistd::Uid::effective().is_root() { - if let Some(ref key_fingerprint) = key_fingerprint { + if let Some((ref key_fingerprint, ref uuid)) = key_fingerprint { let (key_map, _digest) = config::tape_encryption_keys::load_keys()?; match key_map.get(key_fingerprint) { Some(item) => { - return set_encryption(&mut self.file, Some(item.key)); + + // derive specialized key for each media-set + + let mut tape_key = [0u8; 32]; + + let uuid_bytes: [u8; 16] = uuid.as_bytes().clone(); + + openssl::pkcs5::pbkdf2_hmac( + &item.key, + &uuid_bytes, + 10, + openssl::hash::MessageDigest::sha256(), + &mut tape_key)?; + + return set_encryption(&mut self.file, Some(tape_key)); } None => bail!("unknown tape encryption key '{}'", key_fingerprint), } @@ -556,9 +574,10 @@ impl TapeDriver for LinuxTapeHandle { let mut command = std::process::Command::new( "/usr/lib/x86_64-linux-gnu/proxmox-backup/sg-tape-cmd"); command.args(&["encryption"]); - if let Some(fingerprint) = key_fingerprint { + if let Some((fingerprint, uuid)) = key_fingerprint { let fingerprint = crate::tools::format::as_fingerprint(fingerprint.bytes()); command.args(&["--fingerprint", &fingerprint]); + command.args(&["--uuid", &uuid.to_string()]); } command.args(&["--stdin"]); command.stdin(unsafe { std::process::Stdio::from_raw_fd(self.file.as_raw_fd())}); diff --git a/src/tape/drive/mod.rs b/src/tape/drive/mod.rs index b651f742..1747d065 100644 --- a/src/tape/drive/mod.rs +++ b/src/tape/drive/mod.rs @@ -22,8 +22,13 @@ use anyhow::{bail, format_err, Error}; use ::serde::{Deserialize}; use serde_json::Value; -use proxmox::tools::io::ReadExt; -use proxmox::api::section_config::SectionConfigData; +use proxmox::{ + tools::{ + Uuid, + io::ReadExt, + }, + api::section_config::SectionConfigData, +}; use crate::{ backup::{ @@ -190,7 +195,14 @@ pub trait TapeDriver { } /// Set or clear encryption key - fn set_encryption(&mut self, key_fingerprint: Option) -> Result<(), Error> { + /// + /// We use the media_set_uuid to XOR the secret key with the + /// uuid (first 16 bytes), so that each media set uses an uique + /// key for encryption. + fn set_encryption( + &mut self, + key_fingerprint: Option<(Fingerprint, Uuid)>, + ) -> Result<(), Error> { if key_fingerprint.is_some() { bail!("drive does not support encryption"); } diff --git a/src/tape/pool_writer.rs b/src/tape/pool_writer.rs index c085b0b5..24576428 100644 --- a/src/tape/pool_writer.rs +++ b/src/tape/pool_writer.rs @@ -231,12 +231,12 @@ impl PoolWriter { media.id(), )?; - let encrypt_fingerprint = media - .media_set_label() - .as_ref() - .unwrap() + let media_set = media.media_set_label().clone().unwrap(); + + let encrypt_fingerprint = media_set .encryption_key_fingerprint - .clone(); + .clone() + .map(|fp| (fp, media_set.uuid.clone())); drive.set_encryption(encrypt_fingerprint)?;