diff --git a/src/dbus_api/pool/mod.rs b/src/dbus_api/pool/mod.rs index 876878814e..bc2e55b08c 100644 --- a/src/dbus_api/pool/mod.rs +++ b/src/dbus_api/pool/mod.rs @@ -295,6 +295,7 @@ pub fn create_dbus_pool<'a>( .add_m(pool_3_3::grow_physical_device_method(&f)) .add_m(pool_3_7::get_metadata_method(&f)) .add_m(pool_3_8::encrypt_pool_method(&f)) + .add_m(pool_3_8::reencrypt_pool_method(&f)) .add_p(pool_3_0::name_property(&f)) .add_p(pool_3_0::uuid_property(&f)) .add_p(pool_3_0::encrypted_property(&f)) diff --git a/src/dbus_api/pool/pool_3_8/api.rs b/src/dbus_api/pool/pool_3_8/api.rs index b8f9488c29..f924a21183 100644 --- a/src/dbus_api/pool/pool_3_8/api.rs +++ b/src/dbus_api/pool/pool_3_8/api.rs @@ -4,7 +4,10 @@ use dbus_tree::{Factory, MTSync, Method}; -use crate::dbus_api::{pool::pool_3_8::methods::encrypt_pool, types::TData}; +use crate::dbus_api::{ + pool::pool_3_8::methods::{encrypt_pool, reencrypt_pool}, + types::TData, +}; pub fn encrypt_pool_method(f: &Factory, TData>) -> Method, TData> { f.method("EncryptPool", (), encrypt_pool) @@ -30,3 +33,13 @@ pub fn encrypt_pool_method(f: &Factory, TData>) -> Method, TData>) -> Method, TData> { + f.method("ReencryptPool", (), reencrypt_pool) + // b: true if successful + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} diff --git a/src/dbus_api/pool/pool_3_8/methods.rs b/src/dbus_api/pool/pool_3_8/methods.rs index 892992cdd2..d4047c977c 100644 --- a/src/dbus_api/pool/pool_3_8/methods.rs +++ b/src/dbus_api/pool/pool_3_8/methods.rs @@ -97,3 +97,36 @@ pub fn encrypt_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { }; Ok(vec![msg]) } + +pub fn reencrypt_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + + let dbus_context = m.tree.get_data(); + let object_path = m.path.get_name(); + let return_message = message.method_return(); + let default_return = false; + + let pool_path = m + .tree + .get(object_path) + .expect("implicit argument must be in tree"); + let pool_uuid = typed_uuid!( + get_data!(pool_path; default_return; return_message).uuid; + Pool; + default_return; + return_message + ); + + let mut guard = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let (_, _, pool) = guard.as_mut_tuple(); + + let result = handle_action!(pool.reencrypt_pool(), dbus_context, pool_path.get_name()); + let msg = match result { + Ok(_) => return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()), + Err(err) => { + let (rc, rs) = engine_to_dbus_err_tuple(&err); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} diff --git a/src/dbus_api/pool/pool_3_8/mod.rs b/src/dbus_api/pool/pool_3_8/mod.rs index b487bccef5..88c1a7af24 100644 --- a/src/dbus_api/pool/pool_3_8/mod.rs +++ b/src/dbus_api/pool/pool_3_8/mod.rs @@ -5,4 +5,4 @@ mod api; mod methods; -pub use api::encrypt_pool_method; +pub use api::{encrypt_pool_method, reencrypt_pool_method}; diff --git a/src/engine/engine.rs b/src/engine/engine.rs index fa45e9851d..5ed5cd5390 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -23,8 +23,8 @@ use crate::{ ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, LockedPoolsInfo, MappingCreateAction, MappingDeleteAction, Name, PoolDiff, - PoolEncryptionInfo, PoolIdentifier, PoolUuid, RegenAction, RenameAction, ReportType, - SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction, + PoolEncryptionInfo, PoolIdentifier, PoolUuid, Reencrypt, RegenAction, RenameAction, + ReportType, SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction, StoppedPoolsInfo, StratFilesystemDiff, StratSigblockVersion, UdevEngineEvent, UnlockMethod, }, @@ -351,6 +351,9 @@ pub trait Pool: Debug + Send + Sync { encryption_info: &EncryptionInfo, ) -> StratisResult>; + /// Reencrypt an encrypted pool. + fn reencrypt_pool(&mut self) -> StratisResult; + /// Return the metadata that would be written if metadata were written. fn current_metadata(&self, pool_name: &Name) -> StratisResult; diff --git a/src/engine/sim_engine/pool.rs b/src/engine/sim_engine/pool.rs index 1d4254339b..4d2228d063 100644 --- a/src/engine/sim_engine/pool.rs +++ b/src/engine/sim_engine/pool.rs @@ -25,8 +25,8 @@ use crate::{ types::{ ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, Name, - PoolDiff, PoolEncryptionInfo, PoolUuid, RegenAction, RenameAction, SetCreateAction, - SetDeleteAction, StratSigblockVersion, + PoolDiff, PoolEncryptionInfo, PoolUuid, Reencrypt, RegenAction, RenameAction, + SetCreateAction, SetDeleteAction, StratSigblockVersion, }, PropChangeAction, }, @@ -756,6 +756,10 @@ impl Pool for SimPool { Ok(CreateAction::Created(EncryptedDevice)) } + fn reencrypt_pool(&mut self) -> StratisResult { + Ok(Reencrypt) + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { serde_json::to_string(&self.record(pool_name)).map_err(|e| e.into()) } diff --git a/src/engine/strat_engine/backstore/backstore/v1.rs b/src/engine/strat_engine/backstore/backstore/v1.rs index 07066c85c5..a201616eb3 100644 --- a/src/engine/strat_engine/backstore/backstore/v1.rs +++ b/src/engine/strat_engine/backstore/backstore/v1.rs @@ -874,6 +874,26 @@ impl Backstore { } } + /// Reencrypt all encrypted devices in the pool. + /// + /// Returns: + /// * Ok(()) if successful + /// * Err(_) if an operation fails while reencrypting the devices. + pub fn reencrypt(&mut self) -> StratisResult<()> { + if self.encryption_info().is_none() { + return Err(StratisError::Msg( + "Requested pool does not appear to be encrypted".to_string(), + )); + }; + + // Keys are not the same but key description is present + operation_loop( + self.blockdevs_mut().into_iter().map(|(_, _, bd)| bd), + |blockdev| blockdev.reencrypt(), + )?; + Ok(()) + } + /// Regenerate the Clevis bindings with the block devices in this pool using /// the same configuration. /// diff --git a/src/engine/strat_engine/backstore/backstore/v2.rs b/src/engine/strat_engine/backstore/backstore/v2.rs index 1b55911e52..31a20cafc4 100644 --- a/src/engine/strat_engine/backstore/backstore/v2.rs +++ b/src/engine/strat_engine/backstore/backstore/v2.rs @@ -1126,6 +1126,20 @@ impl Backstore { Ok(()) } + pub fn reencrypt(&self) -> StratisResult<()> { + match self.enc { + Some(Either::Left(_)) => { + Err(StratisError::Msg("Encrypted pool where the encrypted device has not yet been created cannot be reencrypted".to_string())) + } + Some(Either::Right(ref handle)) => { + handle.reencrypt() + } + None => { + Err(StratisError::Msg("Unencrypted device cannot be reencrypted".to_string())) + } + } + } + /// A summary of block sizes pub fn block_size_summary(&self, tier: BlockDevTier) -> Option { match tier { diff --git a/src/engine/strat_engine/backstore/blockdev/v1.rs b/src/engine/strat_engine/backstore/blockdev/v1.rs index d6faceabd7..3f9953a7d1 100644 --- a/src/engine/strat_engine/backstore/blockdev/v1.rs +++ b/src/engine/strat_engine/backstore/blockdev/v1.rs @@ -327,6 +327,15 @@ impl StratBlockDev { } } + /// Reencrypt an individual block device in a pool. + pub fn reencrypt(&self) -> StratisResult<()> { + let crypt_handle = self + .underlying_device + .crypt_handle() + .expect("Checked that pool is encrypted"); + crypt_handle.reencrypt() + } + #[cfg(test)] pub fn invariant(&self) { assert!(self.total_size() == self.used.size()); diff --git a/src/engine/strat_engine/crypt/handle/v1.rs b/src/engine/strat_engine/crypt/handle/v1.rs index a667d1d0ff..374517b4f8 100644 --- a/src/engine/strat_engine/crypt/handle/v1.rs +++ b/src/engine/strat_engine/crypt/handle/v1.rs @@ -44,7 +44,8 @@ use crate::{ acquire_crypt_device, activate, add_keyring_keyslot, check_luks2_token, clevis_decrypt, clevis_info_from_metadata, device_from_physical_path, ensure_inactive, ensure_wiped, get_keyslot_number, interpret_clevis_config, - key_desc_from_metadata, luks2_token_type_is_valid, read_key, wipe_fallback, + key_desc_from_metadata, luks2_token_type_is_valid, read_key, reencrypt_shared, + wipe_fallback, }, }, dm::DEVICEMAPPER_PATH, @@ -1024,6 +1025,11 @@ impl CryptHandle { Ok(()) } + /// Encrypt an unencrypted pool. + pub fn reencrypt(&self) -> StratisResult<()> { + reencrypt_shared(self.luks2_device_path(), self.encryption_info()) + } + /// Rename the pool in the LUKS2 token. pub fn rename_pool_in_metadata(&mut self, pool_name: Name) -> StratisResult<()> { let mut device = self.acquire_crypt_device()?; diff --git a/src/engine/strat_engine/crypt/handle/v2.rs b/src/engine/strat_engine/crypt/handle/v2.rs index 68fd58f1df..53430ede5d 100644 --- a/src/engine/strat_engine/crypt/handle/v2.rs +++ b/src/engine/strat_engine/crypt/handle/v2.rs @@ -46,7 +46,8 @@ use crate::{ acquire_crypt_device, activate, add_keyring_keyslot, clevis_decrypt, clevis_info_from_metadata, device_from_physical_path, ensure_wiped, get_keyslot_number, get_passphrase, interpret_clevis_config, - key_desc_from_metadata, luks2_token_type_is_valid, wipe_fallback, + key_desc_from_metadata, luks2_token_type_is_valid, reencrypt_shared, + wipe_fallback, }, }, device::blkdev_size, @@ -855,6 +856,11 @@ impl CryptHandle { .map(|h| h.expect("should have crypt device after online encrypt")) } + /// Encrypt an unencrypted pool. + pub fn reencrypt(&self) -> StratisResult<()> { + reencrypt_shared(self.luks2_device_path(), self.encryption_info()) + } + /// Deactivate the device referenced by the current device handle. #[cfg(test)] pub fn deactivate(&self) -> StratisResult<()> { diff --git a/src/engine/strat_engine/crypt/shared.rs b/src/engine/strat_engine/crypt/shared.rs index f99dd677ba..813a935336 100644 --- a/src/engine/strat_engine/crypt/shared.rs +++ b/src/engine/strat_engine/crypt/shared.rs @@ -20,12 +20,14 @@ use devicemapper::{Bytes, DevId, DmName, DmOptions}; use libcryptsetup_rs::{ c_uint, consts::{ - flags::{CryptActivate, CryptVolumeKey, CryptWipe}, + flags::{CryptActivate, CryptReencrypt, CryptVolumeKey, CryptWipe}, vals::{ - CryptDebugLevel, CryptLogLevel, CryptStatusInfo, CryptWipePattern, EncryptionFormat, + CryptDebugLevel, CryptLogLevel, CryptReencryptDirectionInfo, CryptReencryptModeInfo, + CryptStatusInfo, CryptWipePattern, EncryptionFormat, }, }, - register, set_debug_level, set_log_callback, CryptDevice, CryptInit, LibcryptErr, + get_sector_size, register, set_debug_level, set_log_callback, CryptDevice, CryptInit, + CryptParamsLuks2, CryptParamsReencrypt, LibcryptErr, }; use crate::{ @@ -929,3 +931,48 @@ pub fn get_passphrase( }, } } + +pub fn reencrypt_shared(luks2_path: &Path, encryption_info: &EncryptionInfo) -> StratisResult<()> { + let mut device = CryptInit::init(luks2_path)?; + + let (keyslot, key) = get_passphrase(&mut device, encryption_info)?; + let new_keyslot = + device + .keyslot_handle() + .add_by_key(None, None, key.as_ref(), CryptVolumeKey::NO_SEGMENT)?; + + let cipher = device.status_handle().get_cipher()?; + let cipher_mode = device.status_handle().get_cipher_mode()?; + let sector_size = convert_int!(get_sector_size(Some(&mut device)), i32, u32)?; + device.reencrypt_handle().reencrypt_init_by_passphrase( + None, + key.as_ref(), + Some(keyslot), + new_keyslot, + (&cipher, &cipher_mode), + CryptParamsReencrypt { + mode: CryptReencryptModeInfo::Reencrypt, + direction: CryptReencryptDirectionInfo::Forward, + resilience: "checksum".to_string(), + hash: "sha256".to_string(), + data_shift: 0, + max_hotzone_size: 0, + device_size: 0, + luks2: CryptParamsLuks2 { + data_alignment: 0, + data_device: None, + integrity: None, + integrity_params: None, + pbkdf: None, + label: None, + sector_size, + subsystem: None, + }, + flags: CryptReencrypt::INITIALIZE_ONLY, + }, + )?; + + device.reencrypt_handle().reencrypt2::<()>(None, None)?; + + Ok(()) +} diff --git a/src/engine/strat_engine/pool/dispatch.rs b/src/engine/strat_engine/pool/dispatch.rs index cc27cc6227..5135533ef7 100644 --- a/src/engine/strat_engine/pool/dispatch.rs +++ b/src/engine/strat_engine/pool/dispatch.rs @@ -15,8 +15,8 @@ use crate::{ types::{ ActionAvailability, BlockDevTier, Clevis, CreateAction, DeleteAction, DevUuid, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, Name, - PoolDiff, PoolEncryptionInfo, PoolUuid, PropChangeAction, RegenAction, RenameAction, - SetCreateAction, SetDeleteAction, StratSigblockVersion, + PoolDiff, PoolEncryptionInfo, PoolUuid, PropChangeAction, Reencrypt, RegenAction, + RenameAction, SetCreateAction, SetDeleteAction, StratSigblockVersion, }, }, stratis::StratisResult, @@ -340,6 +340,13 @@ impl Pool for AnyPool { } } + fn reencrypt_pool(&mut self) -> StratisResult { + match self { + AnyPool::V1(p) => p.reencrypt_pool(), + AnyPool::V2(p) => p.reencrypt_pool(), + } + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { match self { AnyPool::V1(p) => p.current_metadata(pool_name), diff --git a/src/engine/strat_engine/pool/v1.rs b/src/engine/strat_engine/pool/v1.rs index 9edae8e1dd..142303c6ac 100644 --- a/src/engine/strat_engine/pool/v1.rs +++ b/src/engine/strat_engine/pool/v1.rs @@ -44,7 +44,7 @@ use crate::{ types::{ ActionAvailability, BlockDevTier, Clevis, Compare, CreateAction, DeleteAction, DevUuid, Diff, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, - Name, OffsetDirection, PoolDiff, PoolEncryptionInfo, PoolUuid, RegenAction, + Name, OffsetDirection, PoolDiff, PoolEncryptionInfo, PoolUuid, Reencrypt, RegenAction, RenameAction, SetCreateAction, SetDeleteAction, StratFilesystemDiff, StratPoolDiff, StratSigblockVersion, }, @@ -1297,6 +1297,11 @@ impl Pool for StratPool { )) } + fn reencrypt_pool(&mut self) -> StratisResult { + self.backstore.reencrypt()?; + Ok(Reencrypt) + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { serde_json::to_string(&self.record(pool_name)).map_err(|e| e.into()) } diff --git a/src/engine/strat_engine/pool/v2.rs b/src/engine/strat_engine/pool/v2.rs index 1deae32bc4..a164466769 100644 --- a/src/engine/strat_engine/pool/v2.rs +++ b/src/engine/strat_engine/pool/v2.rs @@ -39,7 +39,7 @@ use crate::{ ActionAvailability, BlockDevTier, Clevis, Compare, CreateAction, DeleteAction, DevUuid, Diff, EncryptedDevice, EncryptionInfo, FilesystemUuid, GrowAction, Key, KeyDescription, Name, OffsetDirection, PoolDiff, PoolEncryptionInfo, PoolUuid, PropChangeAction, - RegenAction, RenameAction, SetCreateAction, SetDeleteAction, SizedKeyMemory, + Reencrypt, RegenAction, RenameAction, SetCreateAction, SetDeleteAction, SizedKeyMemory, StratFilesystemDiff, StratPoolDiff, StratSigblockVersion, UnlockMethod, }, }, @@ -1230,6 +1230,14 @@ impl Pool for StratPool { } } + #[pool_mutating_action("NoRequests")] + fn reencrypt_pool(&mut self) -> StratisResult { + self.thin_pool.suspend()?; + let encrypt_res = self.backstore.reencrypt(); + self.thin_pool.resume()?; + encrypt_res.map(|_| Reencrypt) + } + fn current_metadata(&self, pool_name: &Name) -> StratisResult { serde_json::to_string(&self.record(pool_name)).map_err(|e| e.into()) } diff --git a/src/engine/types/actions.rs b/src/engine/types/actions.rs index 4a9c5b1029..8f625280d2 100644 --- a/src/engine/types/actions.rs +++ b/src/engine/types/actions.rs @@ -838,3 +838,12 @@ impl EngineAction for PropChangeAction { } } } + +/// Return value indicating a successful reencrypt operation on the pool +pub struct Reencrypt; + +impl Display for Reencrypt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "Reencryption operation was completed successfully") + } +} diff --git a/src/engine/types/mod.rs b/src/engine/types/mod.rs index b10a4048d2..3a28c575d3 100644 --- a/src/engine/types/mod.rs +++ b/src/engine/types/mod.rs @@ -24,8 +24,9 @@ pub use crate::engine::{ types::{ actions::{ Clevis, CreateAction, DeleteAction, EncryptedDevice, EngineAction, GrowAction, Key, - MappingCreateAction, MappingDeleteAction, PropChangeAction, RegenAction, RenameAction, - SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, StopAction, ToDisplay, + MappingCreateAction, MappingDeleteAction, PropChangeAction, Reencrypt, RegenAction, + RenameAction, SetCreateAction, SetDeleteAction, SetUnlockAction, StartAction, + StopAction, ToDisplay, }, diff::{ Compare, Diff, PoolDiff, StratBlockDevDiff, StratFilesystemDiff, StratPoolDiff,