diff --git a/src/dbus_api/api/manager_3_8/api.rs b/src/dbus_api/api/manager_3_8/api.rs index 5b8e468599..23b7f130bb 100644 --- a/src/dbus_api/api/manager_3_8/api.rs +++ b/src/dbus_api/api/manager_3_8/api.rs @@ -20,7 +20,7 @@ pub fn start_pool_method(f: &Factory, TData>) -> Method, TData>) -> Method + .in_arg(("key_desc", "a((bi)s)")) + // Optional Clevis infos for binding on initialization. + // a: array of zero or more elements + // b: true if a token slot is specified + // i: token slot // s: pin name // s: JSON config for Clevis use // - // Rust representation: (bool, (String, String)) - .in_arg(("clevis_info", "(b(ss))")) + // Rust representation: Vec<((bool, u32), String, String)> + .in_arg(("clevis_info", "a((bi)ss)")) // Optional journal size for integrity metadata reservation. // b: true if the size should be specified. // false if the default should be used. diff --git a/src/dbus_api/api/manager_3_8/methods.rs b/src/dbus_api/api/manager_3_8/methods.rs index a8c50e245b..2845048f48 100644 --- a/src/dbus_api/api/manager_3_8/methods.rs +++ b/src/dbus_api/api/manager_3_8/methods.rs @@ -10,12 +10,12 @@ use dbus::{ }; use dbus_tree::{MTSync, MethodInfo, MethodResult}; use futures::executor::block_on; +use serde_json::from_str; use devicemapper::Bytes; use crate::{ dbus_api::{ - api::shared::EncryptionParams, blockdev::create_dbus_blockdev, filesystem::create_dbus_filesystem, pool::create_dbus_pool, @@ -24,11 +24,16 @@ use crate::{ }, engine::{ CreateAction, InputEncryptionInfo, IntegritySpec, IntegrityTagSpec, KeyDescription, Name, - PoolIdentifier, PoolUuid, StartAction, TokenUnlockMethod, UnlockMethod, + PoolIdentifier, PoolUuid, StartAction, TokenUnlockMethod, }, stratis::StratisError, }; +type EncryptionInfos<'a> = ( + Vec<((bool, u32), &'a str)>, + Vec<((bool, u32), (&'a str, &'a str))>, +); + pub fn start_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { let base_path = m.path.get_name(); let message: &Message = m.msg; @@ -64,29 +69,15 @@ pub fn start_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { } } }; - let unlock_method = { - let unlock_method_tup: (bool, &str) = get_next_arg(&mut iter, 2)?; - match tuple_to_option(unlock_method_tup) { - Some(unlock_method_str) => { - match UnlockMethod::try_from(unlock_method_str).map_err(|_| { - StratisError::Msg(format!("{unlock_method_str} is an invalid unlock method")) - }) { - Ok(um) => Some(um), - Err(e) => { - let (rc, rs) = engine_to_dbus_err_tuple(&e); - return Ok(vec![return_message.append3(default_return, rc, rs)]); - } - } - } - None => None, - } - }; + let unlock_method_tup: (bool, (bool, u32)) = get_next_arg(&mut iter, 2)?; + let unlock_method = + TokenUnlockMethod::from_options(tuple_to_option(unlock_method_tup).map(tuple_to_option)); let fd_opt: (bool, OwnedFd) = get_next_arg(&mut iter, 3)?; let fd = tuple_to_option(fd_opt); let ret = match handle_action!(block_on(dbus_context.engine.start_pool( id.clone(), - TokenUnlockMethod::from(unlock_method), + unlock_method, fd.map(|f| f.into_fd()), ))) { Ok(StartAction::Started(_)) => { @@ -153,10 +144,8 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { let name: &str = get_next_arg(&mut iter, 0)?; let devs: Array<'_, &str, _> = get_next_arg(&mut iter, 1)?; - let (key_desc_tuple, clevis_tuple): EncryptionParams = ( - Some(get_next_arg(&mut iter, 2)?), - Some(get_next_arg(&mut iter, 3)?), - ); + let (key_desc_array, clevis_array): EncryptionInfos<'_> = + (get_next_arg(&mut iter, 2)?, get_next_arg(&mut iter, 3)?); let journal_size_tuple: (bool, u64) = get_next_arg(&mut iter, 4)?; let tag_spec_tuple: (bool, String) = get_next_arg(&mut iter, 5)?; let allocate_superblock_tuple: (bool, bool) = get_next_arg(&mut iter, 6)?; @@ -166,26 +155,44 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { let default_return: (bool, (dbus::Path<'static>, Vec>)) = (false, (dbus::Path::default(), Vec::new())); - let key_desc = match key_desc_tuple.and_then(tuple_to_option) { - Some(kds) => match KeyDescription::try_from(kds) { - Ok(kd) => Some(kd), + let key_descs = + match key_desc_array + .into_iter() + .try_fold(Vec::new(), |mut vec, (ts_opt, kd_str)| { + let token_slot = tuple_to_option(ts_opt); + let kd = KeyDescription::try_from(kd_str.to_string())?; + vec.push((token_slot, kd)); + Ok(vec) + }) { + Ok(kds) => kds, Err(e) => { let (rc, rs) = engine_to_dbus_err_tuple(&e); return Ok(vec![return_message.append3(default_return, rc, rs)]); } - }, - None => None, - }; + }; - let clevis_info = match clevis_tuple.and_then(tuple_to_option) { - Some((pin, json_string)) => match serde_json::from_str(json_string.as_str()) { - Ok(j) => Some((pin, j)), + let clevis_infos = + match clevis_array + .into_iter() + .try_fold(Vec::new(), |mut vec, (ts_opt, (pin, json_str))| { + let token_slot = tuple_to_option(ts_opt); + let json = from_str(json_str)?; + vec.push((token_slot, (pin.to_owned(), json))); + Ok(vec) + }) { + Ok(cis) => cis, Err(e) => { - let (rc, rs) = engine_to_dbus_err_tuple(&StratisError::Serde(e)); + let (rc, rs) = engine_to_dbus_err_tuple(&e); return Ok(vec![return_message.append3(default_return, rc, rs)]); } - }, - None => None, + }; + + let ei = match InputEncryptionInfo::new(key_descs, clevis_infos) { + Ok(opt) => opt, + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return Ok(vec![return_message.append3(default_return, rc, rs)]); + } }; let journal_size = tuple_to_option(journal_size_tuple).map(Bytes::from); @@ -208,7 +215,7 @@ pub fn create_pool(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { let create_result = handle_action!(block_on(dbus_context.engine.create_pool( name, &devs.map(Path::new).collect::>(), - InputEncryptionInfo::new_legacy(key_desc, clevis_info).as_ref(), + ei.as_ref(), IntegritySpec { journal_size, tag_spec, diff --git a/src/dbus_api/consts.rs b/src/dbus_api/consts.rs index ada3d96bac..41b6391246 100644 --- a/src/dbus_api/consts.rs +++ b/src/dbus_api/consts.rs @@ -44,9 +44,11 @@ pub const POOL_HAS_CACHE_PROP: &str = "HasCache"; pub const POOL_ENCRYPTED_PROP: &str = "Encrypted"; pub const POOL_AVAIL_ACTIONS_PROP: &str = "AvailableActions"; pub const POOL_KEY_DESC_PROP: &str = "KeyDescription"; +pub const POOL_KEY_DESCS_PROP: &str = "KeyDescriptions"; pub const POOL_TOTAL_SIZE_PROP: &str = "TotalPhysicalSize"; pub const POOL_TOTAL_USED_PROP: &str = "TotalPhysicalUsed"; pub const POOL_CLEVIS_INFO_PROP: &str = "ClevisInfo"; +pub const POOL_CLEVIS_INFOS_PROP: &str = "ClevisInfos"; pub const POOL_ALLOC_SIZE_PROP: &str = "AllocatedSize"; pub const POOL_FS_LIMIT_PROP: &str = "FsLimit"; pub const POOL_OVERPROV_PROP: &str = "Overprovisioning"; diff --git a/src/dbus_api/pool/mod.rs b/src/dbus_api/pool/mod.rs index 57a63cf335..7c2cf69f92 100644 --- a/src/dbus_api/pool/mod.rs +++ b/src/dbus_api/pool/mod.rs @@ -19,6 +19,7 @@ mod pool_3_3; mod pool_3_5; mod pool_3_6; mod pool_3_7; +mod pool_3_8; pub mod prop_conv; mod shared; @@ -281,14 +282,14 @@ pub fn create_dbus_pool<'a>( .add_m(pool_3_7::destroy_filesystems_method(&f)) .add_m(pool_3_0::snapshot_filesystem_method(&f)) .add_m(pool_3_0::add_blockdevs_method(&f)) - .add_m(pool_3_0::bind_clevis_method(&f)) - .add_m(pool_3_0::unbind_clevis_method(&f)) + .add_m(pool_3_8::bind_clevis_method(&f)) + .add_m(pool_3_8::unbind_clevis_method(&f)) .add_m(pool_3_5::init_cache_method(&f)) .add_m(pool_3_0::add_cachedevs_method(&f)) - .add_m(pool_3_0::bind_keyring_method(&f)) - .add_m(pool_3_0::unbind_keyring_method(&f)) - .add_m(pool_3_0::rebind_keyring_method(&f)) - .add_m(pool_3_0::rebind_clevis_method(&f)) + .add_m(pool_3_8::bind_keyring_method(&f)) + .add_m(pool_3_8::unbind_keyring_method(&f)) + .add_m(pool_3_8::rebind_keyring_method(&f)) + .add_m(pool_3_8::rebind_clevis_method(&f)) .add_m(pool_3_0::rename_method(&f)) .add_m(pool_3_3::grow_physical_device_method(&f)) .add_m(pool_3_7::get_metadata_method(&f)) @@ -297,8 +298,8 @@ pub fn create_dbus_pool<'a>( .add_p(pool_3_0::uuid_property(&f)) .add_p(pool_3_0::encrypted_property(&f)) .add_p(pool_3_0::avail_actions_property(&f)) - .add_p(pool_3_0::key_desc_property(&f)) - .add_p(pool_3_0::clevis_info_property(&f)) + .add_p(pool_3_8::key_descs_property(&f)) + .add_p(pool_3_8::clevis_infos_property(&f)) .add_p(pool_3_0::has_cache_property(&f)) .add_p(pool_3_0::alloc_size_property(&f)) .add_p(pool_3_0::used_size_property(&f)) @@ -444,7 +445,7 @@ pub fn get_pool_properties( consts::POOL_UUID_PROP => uuid_to_string!(pool_uuid), consts::POOL_ENCRYPTED_PROP => shared::pool_enc_prop(pool), consts::POOL_AVAIL_ACTIONS_PROP => shared::pool_avail_actions_prop(pool), - consts::POOL_KEY_DESC_PROP => shared::pool_key_desc_prop(pool), + consts::POOL_KEY_DESC_PROP => shared::pool_key_descs_prop(pool), consts::POOL_CLEVIS_INFO_PROP => shared::pool_clevis_info_prop(pool), consts::POOL_HAS_CACHE_PROP => shared::pool_has_cache_prop(pool), consts::POOL_ALLOC_SIZE_PROP => shared::pool_allocated_size(pool), diff --git a/src/dbus_api/pool/pool_3_0/methods.rs b/src/dbus_api/pool/pool_3_0/methods.rs index 30b7a19df0..32997cd4bd 100644 --- a/src/dbus_api/pool/pool_3_0/methods.rs +++ b/src/dbus_api/pool/pool_3_0/methods.rs @@ -384,8 +384,11 @@ pub fn bind_clevis(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Ok(CreateAction::Created(_)) => { - dbus_context - .push_pool_clevis_info_change(pool_path.get_name(), pool.encryption_info_legacy()); + dbus_context.push_pool_clevis_info_change( + pool_path.get_name(), + pool.encryption_info() + .map(|either| either.map_left(|ei| (true, ei))), + ); return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Err(e) => { @@ -422,8 +425,11 @@ pub fn unbind_clevis(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Ok(DeleteAction::Deleted(_)) => { - dbus_context - .push_pool_clevis_info_change(pool_path.get_name(), pool.encryption_info_legacy()); + dbus_context.push_pool_clevis_info_change( + pool_path.get_name(), + pool.encryption_info() + .map(|either| either.map_left(|ei| (true, ei))), + ); return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Err(e) => { @@ -474,8 +480,11 @@ pub fn bind_keyring(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Ok(CreateAction::Created(_)) => { - dbus_context - .push_pool_key_desc_change(pool_path.get_name(), pool.encryption_info_legacy()); + dbus_context.push_pool_key_desc_change( + pool_path.get_name(), + pool.encryption_info() + .map(|either| either.map_left(|ei| (true, ei))), + ); return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Err(e) => { @@ -516,8 +525,11 @@ pub fn unbind_keyring(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Ok(DeleteAction::Deleted(_)) => { - dbus_context - .push_pool_key_desc_change(pool_path.get_name(), pool.encryption_info_legacy()); + dbus_context.push_pool_key_desc_change( + pool_path.get_name(), + pool.encryption_info() + .map(|either| either.map_left(|ei| (true, ei))), + ); return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Err(e) => { @@ -568,8 +580,11 @@ pub fn rebind_keyring(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Ok(RenameAction::Renamed(_)) => { - dbus_context - .push_pool_key_desc_change(pool_path.get_name(), pool.encryption_info_legacy()); + dbus_context.push_pool_key_desc_change( + pool_path.get_name(), + pool.encryption_info() + .map(|either| either.map_left(|ei| (true, ei))), + ); return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Ok(RenameAction::NoSource) => { @@ -610,8 +625,11 @@ pub fn rebind_clevis(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { let msg = match handle_action!(pool.rebind_clevis(None), dbus_context, pool_path.get_name()) { Ok(_) => { - dbus_context - .push_pool_clevis_info_change(pool_path.get_name(), pool.encryption_info_legacy()); + dbus_context.push_pool_clevis_info_change( + pool_path.get_name(), + pool.encryption_info() + .map(|either| either.map_left(|ei| (true, ei))), + ); return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) } Err(e) => { diff --git a/src/dbus_api/pool/pool_3_8/api.rs b/src/dbus_api/pool/pool_3_8/api.rs new file mode 100644 index 0000000000..705d3bf13c --- /dev/null +++ b/src/dbus_api/pool/pool_3_8/api.rs @@ -0,0 +1,103 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use dbus_tree::{Access, EmitsChangedSignal, Factory, MTSync, Method, Property}; + +use crate::dbus_api::{ + pool::{ + consts, + pool_3_8::{ + methods::{ + bind_clevis, bind_keyring, rebind_clevis, rebind_keyring, unbind_clevis, + unbind_keyring, + }, + props::{get_pool_clevis_infos, get_pool_key_descs}, + }, + }, + types::TData, +}; + +pub fn bind_clevis_method(f: &Factory, TData>) -> Method, TData> { + f.method("BindClevis", (), bind_clevis) + .in_arg(("pin", "s")) + .in_arg(("json", "s")) + .in_arg(("token_slot", "(bi)")) + // b: Indicates if new clevis bindings were added + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} + +pub fn unbind_clevis_method(f: &Factory, TData>) -> Method, TData> { + f.method("UnbindClevis", (), unbind_clevis) + .in_arg(("token_slot", "(bi)")) + // b: Indicates if clevis bindings were removed + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} + +pub fn bind_keyring_method(f: &Factory, TData>) -> Method, TData> { + f.method("BindKeyring", (), bind_keyring) + .in_arg(("key_desc", "s")) + .in_arg(("token_slot", "(b(bi))")) + // b: Indicates if new keyring bindings were added + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} + +pub fn unbind_keyring_method(f: &Factory, TData>) -> Method, TData> { + f.method("UnbindKeyring", (), unbind_keyring) + .in_arg(("token_slot", "(bi)")) + // b: Indicates if keyring bindings were removed + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} + +pub fn rebind_keyring_method(f: &Factory, TData>) -> Method, TData> { + f.method("RebindKeyring", (), rebind_keyring) + .in_arg(("key_desc", "s")) + .in_arg(("token_slot", "(bi)")) + // b: Indicates if keyring bindings were changed + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} + +pub fn rebind_clevis_method(f: &Factory, TData>) -> Method, TData> { + f.method("RebindClevis", (), rebind_clevis) + .in_arg(("token_slot", "(bi)")) + // b: Indicates if Clevis bindings were changed + // + // Rust representation: bool + .out_arg(("results", "b")) + .out_arg(("return_code", "q")) + .out_arg(("return_string", "s")) +} + +pub fn key_descs_property(f: &Factory, TData>) -> Property, TData> { + f.property::, _>(consts::POOL_KEY_DESCS_PROP, ()) + .access(Access::Read) + .emits_changed(EmitsChangedSignal::True) + .on_get(get_pool_key_descs) +} + +pub fn clevis_infos_property(f: &Factory, TData>) -> Property, TData> { + f.property::, _>(consts::POOL_CLEVIS_INFOS_PROP, ()) + .access(Access::Read) + .emits_changed(EmitsChangedSignal::True) + .on_get(get_pool_clevis_infos) +} diff --git a/src/dbus_api/pool/pool_3_8/methods.rs b/src/dbus_api/pool/pool_3_8/methods.rs new file mode 100644 index 0000000000..f1ac66b536 --- /dev/null +++ b/src/dbus_api/pool/pool_3_8/methods.rs @@ -0,0 +1,399 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use dbus::Message; +use dbus_tree::{MTSync, MethodInfo, MethodResult}; +use serde_json::Value; + +use crate::{ + dbus_api::{ + types::{DbusErrorEnum, TData, OK_STRING}, + util::{engine_to_dbus_err_tuple, get_next_arg, tuple_to_option}, + }, + engine::{CreateAction, DeleteAction, KeyDescription, OptionalTokenSlotInput, RenameAction}, + stratis::StratisError, +}; + +pub fn bind_clevis(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + let mut iter = message.iter_init(); + let pin: String = get_next_arg(&mut iter, 0)?; + let json_string: String = get_next_arg(&mut iter, 1)?; + let token_slot_tuple: (bool, u32) = get_next_arg(&mut iter, 2)?; + + 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 pool = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let lowest_token_slot = pool + .encryption_info() + .and_then(|either| either.left()) + .as_ref() + .and_then(|ei| ei.single_clevis_info()) + .map(|(token_slot, _)| token_slot); + + let json: Value = match serde_json::from_str(&json_string) { + Ok(j) => j, + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&StratisError::Serde(e)); + return Ok(vec![return_message.append3(default_return, rc, rs)]); + } + }; + + let token_slot = match tuple_to_option(token_slot_tuple) { + Some(t) => OptionalTokenSlotInput::Some(t), + None => OptionalTokenSlotInput::None, + }; + + let msg = match handle_action!( + pool.bind_clevis(token_slot, pin.as_str(), &json), + dbus_context, + pool_path.get_name() + ) { + Ok(CreateAction::Identity) => { + return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Ok(CreateAction::Created(_)) => { + dbus_context.push_pool_clevis_info_change( + pool_path.get_name(), + pool.encryption_info() + .map(|either| either.map_left(|ei| (lowest_token_slot.is_none(), ei))), + ); + return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} + +pub fn unbind_clevis(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + + let mut iter = message.iter_init(); + let token_slot_tuple: (bool, u32) = get_next_arg(&mut iter, 0)?; + + 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 token_slot = tuple_to_option(token_slot_tuple); + + 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 pool = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let lowest_token_slot = pool + .encryption_info() + .and_then(|either| either.left()) + .as_ref() + .and_then(|ei| ei.single_clevis_info()) + .map(|(token_slot, _)| token_slot); + + let msg = match handle_action!( + pool.unbind_clevis(token_slot), + dbus_context, + pool_path.get_name() + ) { + Ok(DeleteAction::Identity) => { + return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Ok(DeleteAction::Deleted(_)) => { + dbus_context.push_pool_clevis_info_change( + pool_path.get_name(), + pool.encryption_info().map(|either| { + either.map_left(|ei| { + (token_slot.is_some() && token_slot == lowest_token_slot, ei) + }) + }), + ); + return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} + +pub fn bind_keyring(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + let mut iter = message.iter_init(); + let key_desc_str: String = get_next_arg(&mut iter, 0)?; + let token_slot_tuple: (bool, u32) = get_next_arg(&mut iter, 1)?; + + 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 key_desc = match KeyDescription::try_from(key_desc_str) { + Ok(kd) => kd, + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return Ok(vec![return_message.append3(default_return, rc, rs)]); + } + }; + + let token_slot = match tuple_to_option(token_slot_tuple) { + Some(t) => OptionalTokenSlotInput::Some(t), + None => OptionalTokenSlotInput::None, + }; + + 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 pool = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let lowest_token_slot = pool + .encryption_info() + .and_then(|either| either.left()) + .as_ref() + .and_then(|ei| ei.single_key_description()) + .map(|(token_slot, _)| token_slot); + + let msg = match handle_action!( + pool.bind_keyring(token_slot, &key_desc), + dbus_context, + pool_path.get_name() + ) { + Ok(CreateAction::Identity) => { + return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Ok(CreateAction::Created(_)) => { + dbus_context.push_pool_key_desc_change( + pool_path.get_name(), + pool.encryption_info() + .map(|ei| ei.map_left(|e| (lowest_token_slot.is_none(), e))), + ); + return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} + +pub fn unbind_keyring(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + + let mut iter = message.iter_init(); + let token_slot_tuple: (bool, u32) = get_next_arg(&mut iter, 0)?; + + 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 token_slot = tuple_to_option(token_slot_tuple); + + 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 pool = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let lowest_token_slot = pool + .encryption_info() + .and_then(|either| either.left()) + .as_ref() + .and_then(|ei| ei.single_key_description()) + .map(|(token_slot, _)| token_slot); + + let msg = match handle_action!( + pool.unbind_keyring(token_slot), + dbus_context, + pool_path.get_name() + ) { + Ok(DeleteAction::Identity) => { + return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Ok(DeleteAction::Deleted(_)) => { + dbus_context.push_pool_key_desc_change( + pool_path.get_name(), + pool.encryption_info().map(|either| { + either.map_left(|ei| { + (token_slot.is_some() && token_slot == lowest_token_slot, ei) + }) + }), + ); + return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} + +pub fn rebind_keyring(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + let mut iter = message.iter_init(); + let key_desc_str: String = get_next_arg(&mut iter, 0)?; + let token_slot_tuple: (bool, u32) = get_next_arg(&mut iter, 1)?; + + 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 key_desc = match KeyDescription::try_from(key_desc_str) { + Ok(kd) => kd, + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return Ok(vec![return_message.append3(default_return, rc, rs)]); + } + }; + + let token_slot = tuple_to_option(token_slot_tuple); + + 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 pool = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let lowest_token_slot = pool + .encryption_info() + .and_then(|either| either.left()) + .as_ref() + .and_then(|ei| ei.single_key_description()) + .map(|(token_slot, _)| token_slot); + + let msg = match handle_action!( + pool.rebind_keyring(token_slot, &key_desc), + dbus_context, + pool_path.get_name() + ) { + Ok(RenameAction::Identity) => { + return_message.append3(false, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Ok(RenameAction::Renamed(_)) => { + dbus_context.push_pool_key_desc_change( + pool_path.get_name(), + pool.encryption_info().map(|either| { + either.map_left(|ei| { + (token_slot.is_some() && token_slot == lowest_token_slot, ei) + }) + }), + ); + return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Ok(RenameAction::NoSource) => { + let error_message = format!( + "pool with UUID {pool_uuid} is not currently bound to a keyring passphrase" + ); + let (rc, rs) = (DbusErrorEnum::ERROR as u16, error_message); + return_message.append3(default_return, rc, rs) + } + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + return_message.append3(default_return, rc, rs) + } + }; + Ok(vec![msg]) +} + +pub fn rebind_clevis(m: &MethodInfo<'_, MTSync, TData>) -> MethodResult { + let message: &Message = m.msg; + + let mut iter = message.iter_init(); + let token_slot_tuple: (bool, u32) = get_next_arg(&mut iter, 0)?; + + 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 token_slot = tuple_to_option(token_slot_tuple); + + 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 pool = get_mut_pool!(dbus_context.engine; pool_uuid; default_return; return_message); + let lowest_token_slot = pool + .encryption_info() + .and_then(|either| either.left()) + .as_ref() + .and_then(|ei| ei.single_clevis_info()) + .map(|(token_slot, _)| token_slot); + + let msg = match handle_action!( + pool.rebind_clevis(token_slot), + dbus_context, + pool_path.get_name() + ) { + Ok(_) => { + dbus_context.push_pool_clevis_info_change( + pool_path.get_name(), + pool.encryption_info().map(|either| { + either.map_left(|ei| { + (token_slot.is_some() && token_slot == lowest_token_slot, ei) + }) + }), + ); + return_message.append3(true, DbusErrorEnum::OK as u16, OK_STRING.to_string()) + } + Err(e) => { + let (rc, rs) = engine_to_dbus_err_tuple(&e); + 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 new file mode 100644 index 0000000000..4ec70ddccb --- /dev/null +++ b/src/dbus_api/pool/pool_3_8/mod.rs @@ -0,0 +1,12 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +mod api; +mod methods; +mod props; + +pub use api::{ + bind_clevis_method, bind_keyring_method, clevis_infos_property, key_descs_property, + rebind_clevis_method, rebind_keyring_method, unbind_clevis_method, unbind_keyring_method, +}; diff --git a/src/dbus_api/pool/pool_3_8/props.rs b/src/dbus_api/pool/pool_3_8/props.rs new file mode 100644 index 0000000000..c218f37f61 --- /dev/null +++ b/src/dbus_api/pool/pool_3_8/props.rs @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +use dbus::arg::IterAppend; +use dbus_tree::{MTSync, MethodErr, PropInfo}; + +use crate::dbus_api::{ + pool::shared::{self, get_pool_property}, + types::TData, +}; + +pub fn get_pool_key_descs( + i: &mut IterAppend<'_>, + p: &PropInfo<'_, MTSync, TData>, +) -> Result<(), MethodErr> { + get_pool_property(i, p, |(_, _, pool)| Ok(shared::pool_key_descs_prop(pool))) +} + +pub fn get_pool_clevis_infos( + i: &mut IterAppend<'_>, + p: &PropInfo<'_, MTSync, TData>, +) -> Result<(), MethodErr> { + get_pool_property(i, p, |(_, _, pool)| { + Ok(shared::pool_clevis_infos_prop(pool)) + }) +} diff --git a/src/dbus_api/pool/prop_conv.rs b/src/dbus_api/pool/prop_conv.rs index 512872cf9a..6143f77026 100644 --- a/src/dbus_api/pool/prop_conv.rs +++ b/src/dbus_api/pool/prop_conv.rs @@ -2,19 +2,26 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. +use dbus::arg::{RefArg, Variant}; +use either::Either; + use devicemapper::Bytes; use crate::{ dbus_api::util::option_to_tuple, - engine::{ActionAvailability, PoolEncryptionInfo}, + engine::{ActionAvailability, EncryptionInfo, PoolEncryptionInfo}, stratis::StratisResult, }; /// Convert an encryption information data structure to a /// D-Bus type. -fn enc_to_prop(ei: Option, f: F, default: T) -> (bool, (bool, T)) +fn enc_to_prop( + ei: Option>, + f: F, + default: T, +) -> (bool, (bool, T)) where - F: Fn(PoolEncryptionInfo) -> StratisResult>, + F: Fn(Either) -> StratisResult>, T: Clone, { option_to_tuple( @@ -28,29 +35,102 @@ where /// Fetch the key description and handle converting it into a /// D-Bus type. -pub fn key_desc_to_prop(ei: Option) -> (bool, (bool, String)) { +pub fn key_desc_to_prop( + ei: Option>, +) -> (bool, (bool, String)) { enc_to_prop( ei, |ei| { - ei.key_description() - .map(|kd_opt| kd_opt.map(|kd| kd.as_application_str().to_string())) + ei.either( + |ei| { + Ok(ei + .single_key_description() + .map(|(_, kd)| kd.as_application_str().to_owned())) + }, + |pei| { + pei.key_description() + .map(|opt| opt.map(|k| k.as_application_str().to_owned())) + }, + ) }, String::new(), ) } +/// Fetch the key descriptions and handle converting it into a +/// D-Bus type. +pub fn key_descs_to_prop( + eei: Option>, +) -> Variant> { + match eei { + Some(Either::Left(ei)) => Variant(Box::new( + ei.all_key_descriptions() + .map(|(i, kd)| (*i, kd.as_application_str().to_string())) + .collect::>(), + )), + Some(Either::Right(pei)) => Variant(Box::new(option_to_tuple( + pei.key_description() + .map(|kd_opt| { + option_to_tuple( + kd_opt.map(|kd| kd.as_application_str().to_string()), + String::new(), + ) + }) + .ok(), + (false, String::new()), + ))), + None => Variant(Box::new("Unencrypted".to_string())), + } +} + /// Fetch the Clevis information and handle converting it into a /// D-Bus type. -pub fn clevis_info_to_prop(ei: Option) -> (bool, (bool, (String, String))) { +pub fn clevis_info_to_prop( + ei: Option>, +) -> (bool, (bool, (String, String))) { enc_to_prop( ei, |ei| { - ei.clevis_info() - .map(|ci_opt| ci_opt.map(|(pin, cfg)| (pin.to_owned(), cfg.to_string()))) + ei.either( + |ei| { + Ok(ei + .single_clevis_info() + .map(|(_, (pin, ci))| (pin.to_owned(), ci.to_string()))) + }, + |pei| { + pei.clevis_info() + .map(|opt| opt.map(|(pin, ci)| (pin.to_owned(), ci.to_string()))) + }, + ) }, (String::new(), String::new()), ) } +/// Fetch the Clevis infos and handle converting it into a +/// D-Bus type. +pub fn clevis_infos_to_prop( + eei: Option>, +) -> Variant> { + match eei { + Some(Either::Left(ei)) => Variant(Box::new( + ei.all_clevis_infos() + .map(|(i, (pin, config))| (*i, (pin.to_owned(), config.to_string()))) + .collect::>(), + )), + Some(Either::Right(pei)) => Variant(Box::new(option_to_tuple( + pei.clevis_info() + .map(|ci_opt| { + option_to_tuple( + ci_opt.map(|(pin, config)| (pin.to_owned(), config.to_string())), + (String::new(), String::new()), + ) + }) + .ok(), + (false, (String::new(), String::new())), + ))), + None => Variant(Box::new("Unencrypted".to_string())), + } +} /// Generate D-Bus representation of pool state property. #[inline] diff --git a/src/dbus_api/pool/shared.rs b/src/dbus_api/pool/shared.rs index 41100fca0d..b77fa4c639 100644 --- a/src/dbus_api/pool/shared.rs +++ b/src/dbus_api/pool/shared.rs @@ -5,7 +5,7 @@ use std::{fmt::Display, path::Path}; use dbus::{ - arg::{Array, IterAppend}, + arg::{Array, IterAppend, RefArg, Variant}, Message, }; use dbus_tree::{MTSync, MethodErr, MethodInfo, MethodResult, PropInfo, Tree}; @@ -283,12 +283,22 @@ pub fn pool_avail_actions_prop(pool: &dyn Pool) -> String { /// Generate D-Bus representation of a pool key description property. pub fn pool_key_desc_prop(pool: &dyn Pool) -> (bool, (bool, String)) { - prop_conv::key_desc_to_prop(pool.encryption_info_legacy()) + prop_conv::key_desc_to_prop(pool.encryption_info()) +} + +/// Generate D-Bus representation of a pool key descriptions property. +pub fn pool_key_descs_prop(pool: &dyn Pool) -> Variant> { + prop_conv::key_descs_to_prop(pool.encryption_info()) } /// Generate D-Bus representation of a pool Clevis info property. pub fn pool_clevis_info_prop(pool: &dyn Pool) -> (bool, (bool, (String, String))) { - prop_conv::clevis_info_to_prop(pool.encryption_info_legacy()) + prop_conv::clevis_info_to_prop(pool.encryption_info()) +} + +/// Generate D-Bus representation of a pool Clevis infos property. +pub fn pool_clevis_infos_prop(pool: &dyn Pool) -> Variant> { + prop_conv::clevis_infos_to_prop(pool.encryption_info()) } /// Generate D-Bus representation of a boolean indicating whether the pool diff --git a/src/dbus_api/tree.rs b/src/dbus_api/tree.rs index 4ffab6c470..c094493c88 100644 --- a/src/dbus_api/tree.rs +++ b/src/dbus_api/tree.rs @@ -18,6 +18,7 @@ use dbus::{ Path, }; use dbus_tree::{MTSync, ObjectPath}; +use either::Either; use tokio::sync::{broadcast::Receiver, mpsc::UnboundedReceiver}; use devicemapper::{Bytes, Sectors}; @@ -44,12 +45,14 @@ use crate::{ util::{poll_exit_and_future, thread_safe_to_dbus_sendable}, }, engine::{ - ActionAvailability, DevUuid, FilesystemUuid, LockedPoolsInfo, PoolEncryptionInfo, PoolUuid, - StoppedPoolsInfo, StratisUuid, + ActionAvailability, DevUuid, EncryptionInfo, FilesystemUuid, LockedPoolsInfo, + PoolEncryptionInfo, PoolUuid, StoppedPoolsInfo, StratisUuid, }, stratis::{StratisError, StratisResult}, }; +use super::pool::prop_conv::{clevis_infos_to_prop, key_descs_to_prop}; + type PropertySignal = HashMap>>, Vec)>; /// Handler for a D-Bus tree. @@ -412,56 +415,76 @@ impl DbusTreeHandler { } /// Handle a change of the key description for a pool in the engine. - fn handle_pool_key_desc_change(&self, item: Path<'static>, ei: Option) { - let kd_prop = key_desc_to_prop(ei); + fn handle_pool_key_desc_change( + &self, + item: Path<'static>, + ei: Option>, + ) { + let kd_prop = key_desc_to_prop(ei.clone().map(|either| either.map_left(|(_, ei)| ei))); + if ei + .as_ref() + .map(|either| { + either.as_ref().left().map(|(b, _)| *b).unwrap_or(false) || either.is_right() + }) + .expect("Should be encrypted to send signal") + && self + .property_changed_invalidated_signal( + &item, + prop_hashmap! { + consts::POOL_INTERFACE_NAME_3_0 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_1 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_2 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_3 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_4 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_5 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_6 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_7 => { + Vec::new(), + consts::POOL_KEY_DESC_PROP.to_string() => + box_variant!(kd_prop.clone()) + } + }, + ) + .is_err() + { + warn!("Signal on pool key description change was not sent to the D-Bus client"); + } if self .property_changed_invalidated_signal( &item, prop_hashmap! { - consts::POOL_INTERFACE_NAME_3_0 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_1 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_2 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_3 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_4 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_5 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_6 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_7 => { - Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop.clone()) - }, consts::POOL_INTERFACE_NAME_3_8 => { Vec::new(), - consts::POOL_KEY_DESC_PROP.to_string() => - box_variant!(kd_prop) + consts::POOL_KEY_DESCS_PROP.to_string() => + key_descs_to_prop(ei.clone().map(|either| either.map_left(|(_, ei)| ei))) } }, ) @@ -471,57 +494,78 @@ impl DbusTreeHandler { } } - /// Handle a change of the key description for a pool in the engine. - fn handle_pool_clevis_info_change(&self, item: Path<'static>, ei: Option) { - let ci_prop = clevis_info_to_prop(ei); + /// Handle a change of the Clevis info for a pool in the engine. + fn handle_pool_clevis_info_change( + &self, + item: Path<'static>, + ei: Option>, + ) { + let ci_prop = clevis_info_to_prop(ei.clone().map(|either| either.map_left(|(_, ei)| ei))); + + if ei + .as_ref() + .map(|either| { + either.as_ref().left().map(|(b, _)| *b).unwrap_or(false) || either.is_right() + }) + .expect("Should be encrypted to send signal") + && self + .property_changed_invalidated_signal( + &item, + prop_hashmap! { + consts::POOL_INTERFACE_NAME_3_0 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_1 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_2 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_3 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_4 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_5 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_6 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + }, + consts::POOL_INTERFACE_NAME_3_7 => { + Vec::new(), + consts::POOL_CLEVIS_INFO_PROP.to_string() => + box_variant!(ci_prop.clone()) + } + }, + ) + .is_err() + { + warn!("Signal on pool Clevis information change was not sent to the D-Bus client"); + } if self .property_changed_invalidated_signal( &item, prop_hashmap! { - consts::POOL_INTERFACE_NAME_3_0 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_1 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_2 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_3 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_4 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_5 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_6 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, - consts::POOL_INTERFACE_NAME_3_7 => { - Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop.clone()) - }, consts::POOL_INTERFACE_NAME_3_8 => { Vec::new(), - consts::POOL_CLEVIS_INFO_PROP.to_string() => - box_variant!(ci_prop) + consts::POOL_CLEVIS_INFOS_PROP.to_string() => + clevis_infos_to_prop(ei.clone().map(|either| either.map_left(|(_, ei)| ei))) } }, ) diff --git a/src/dbus_api/types.rs b/src/dbus_api/types.rs index 328dbaf830..bfa2d66afc 100644 --- a/src/dbus_api/types.rs +++ b/src/dbus_api/types.rs @@ -18,6 +18,7 @@ use dbus::{ Path, }; use dbus_tree::{DataType, MTSync, ObjectPath, Tree}; +use either::Either; use tokio::sync::{ mpsc::UnboundedSender as TokioSender, OwnedRwLockReadGuard, OwnedRwLockWriteGuard, RwLock, }; @@ -27,10 +28,10 @@ use devicemapper::{Bytes, Sectors}; use crate::{ dbus_api::{connection::DbusConnectionHandler, tree::DbusTreeHandler, udev::DbusUdevHandler}, engine::{ - total_allocated, total_used, ActionAvailability, DevUuid, Diff, Engine, ExclusiveGuard, - FilesystemUuid, Lockable, LockedPoolsInfo, PoolDiff, PoolEncryptionInfo, PoolUuid, - SharedGuard, StoppedPoolsInfo, StratBlockDevDiff, StratFilesystemDiff, StratPoolDiff, - StratisUuid, ThinPoolDiff, + total_allocated, total_used, ActionAvailability, DevUuid, Diff, EncryptionInfo, Engine, + ExclusiveGuard, FilesystemUuid, Lockable, LockedPoolsInfo, PoolDiff, PoolEncryptionInfo, + PoolUuid, SharedGuard, StoppedPoolsInfo, StratBlockDevDiff, StratFilesystemDiff, + StratPoolDiff, StratisUuid, ThinPoolDiff, }, }; @@ -97,8 +98,14 @@ pub enum DbusAction { FsNameChange(Path<'static>, String), PoolNameChange(Path<'static>, String), PoolAvailActions(Path<'static>, ActionAvailability), - PoolKeyDescChange(Path<'static>, Option), - PoolClevisInfoChange(Path<'static>, Option), + PoolKeyDescChange( + Path<'static>, + Option>, + ), + PoolClevisInfoChange( + Path<'static>, + Option>, + ), PoolCacheChange(Path<'static>, bool), PoolFsLimitChange(Path<'static>, u64), PoolOverprovModeChange(Path<'static>, bool), @@ -297,7 +304,11 @@ impl DbusContext { } /// Send changed signal for KeyDesc property. - pub fn push_pool_key_desc_change(&self, item: &Path<'static>, ei: Option) { + pub fn push_pool_key_desc_change( + &self, + item: &Path<'static>, + ei: Option>, + ) { if let Err(e) = self .sender .send(DbusAction::PoolKeyDescChange(item.clone(), ei)) @@ -313,7 +324,7 @@ impl DbusContext { pub fn push_pool_clevis_info_change( &self, item: &Path<'static>, - ei: Option, + ei: Option>, ) { if let Err(e) = self .sender diff --git a/src/engine/engine.rs b/src/engine/engine.rs index 6d0f543825..03d6c815ff 100644 --- a/src/engine/engine.rs +++ b/src/engine/engine.rs @@ -193,7 +193,7 @@ pub trait Pool: Debug + Send + Sync { token_slot: OptionalTokenSlotInput, pin: &str, clevis_info: &Value, - ) -> StratisResult>; + ) -> StratisResult>; /// V1: Binds all devices in the pool to a given key description. /// * token_slot is always None @@ -207,7 +207,7 @@ pub trait Pool: Debug + Send + Sync { &mut self, token_slot: OptionalTokenSlotInput, key_desc: &KeyDescription, - ) -> StratisResult>; + ) -> StratisResult>; /// V1: Rebinds all devices in the pool to a given key description. /// * token_slot is always None diff --git a/src/engine/sim_engine/pool.rs b/src/engine/sim_engine/pool.rs index f652dab21f..587f79d370 100644 --- a/src/engine/sim_engine/pool.rs +++ b/src/engine/sim_engine/pool.rs @@ -310,7 +310,7 @@ impl Pool for SimPool { token_slot: OptionalTokenSlotInput, pin: &str, clevis_info: &Value, - ) -> StratisResult> { + ) -> StratisResult> { let encryption_info = match self.encryption_info.as_mut() { Some(ei) => ei, None => { @@ -360,14 +360,14 @@ impl Pool for SimPool { token_slot_to_add, UnlockMechanism::ClevisInfo((pin.to_owned(), clevis_info.to_owned())), )?; - Ok(CreateAction::Created(Clevis)) + Ok(CreateAction::Created((Clevis, token_slot_to_add))) } fn bind_keyring( &mut self, token_slot: OptionalTokenSlotInput, key_description: &KeyDescription, - ) -> StratisResult> { + ) -> StratisResult> { let encryption_info = match self.encryption_info.as_mut() { Some(ei) => ei, None => { @@ -423,7 +423,7 @@ impl Pool for SimPool { token_slot_to_add, UnlockMechanism::KeyDesc(key_description.to_owned()), )?; - Ok(CreateAction::Created(Key)) + Ok(CreateAction::Created((Key, token_slot_to_add))) } fn unbind_keyring(&mut self, token_slot: Option) -> StratisResult> { diff --git a/src/engine/strat_engine/backstore/backstore/v2.rs b/src/engine/strat_engine/backstore/backstore/v2.rs index 761a56b675..9510af2092 100644 --- a/src/engine/strat_engine/backstore/backstore/v2.rs +++ b/src/engine/strat_engine/backstore/backstore/v2.rs @@ -936,7 +936,7 @@ impl Backstore { token_slot: OptionalTokenSlotInput, pin: &str, clevis_info: &Value, - ) -> StratisResult { + ) -> StratisResult> { let handle = self .enc .as_mut() @@ -952,7 +952,7 @@ impl Backstore { let ci = handle.encryption_info().single_clevis_info(); if let Some((_, (existing_pin, existing_info))) = ci { if existing_pin.as_str() == pin { - Ok(false) + Ok(None) } else { Err(StratisError::Msg(format!( "Crypt device has already been bound with pin {existing_pin} and config {existing_info}; \ @@ -960,8 +960,7 @@ impl Backstore { ))) } } else { - handle.bind_clevis(None, pin, clevis_info)?; - Ok(true) + handle.bind_clevis(None, pin, clevis_info).map(Some) } } OptionalTokenSlotInput::Some(k) => { @@ -970,7 +969,7 @@ impl Backstore { let ci = handle.encryption_info().get_info(k); if let Some(UnlockMechanism::ClevisInfo((existing_pin, existing_info))) = ci { if existing_pin == pin { - Ok(false) + Ok(None) } else { Err(StratisError::Msg(format!( "Crypt device has already been bound with pin {existing_pin} and config {existing_info}; \ @@ -978,16 +977,14 @@ impl Backstore { ))) } } else { - handle.bind_clevis(Some(k), pin, clevis_info)?; - Ok(true) + handle.bind_clevis(Some(k), pin, clevis_info).map(Some) } } OptionalTokenSlotInput::None => { // Because idemptotence is checked based on pin, we can't reliably check whether // the binding has already been applied when no token slot is specified. As a // result, we default to adding the new config unless a token slot is specified. - handle.bind_clevis(None, pin, clevis_info)?; - Ok(true) + handle.bind_clevis(None, pin, clevis_info).map(Some) } } } @@ -1087,7 +1084,7 @@ impl Backstore { &mut self, token_slot: OptionalTokenSlotInput, key_desc: &KeyDescription, - ) -> StratisResult { + ) -> StratisResult> { let handle = self .enc .as_mut() @@ -1103,7 +1100,7 @@ impl Backstore { let info = handle.encryption_info().single_key_description(); if let Some((_, kd)) = info { if kd == key_desc { - Ok(false) + Ok(None) } else { Err(StratisError::Msg(format!( "Crypt device has already been bound with key description {}; \ @@ -1113,8 +1110,7 @@ impl Backstore { ))) } } else { - handle.bind_keyring(None, key_desc)?; - Ok(true) + handle.bind_keyring(None, key_desc).map(Some) } } OptionalTokenSlotInput::Some(k) => { @@ -1123,7 +1119,7 @@ impl Backstore { let info = handle.encryption_info().get_info(k); if let Some(UnlockMechanism::KeyDesc(ref kd)) = info { if kd == key_desc { - Ok(false) + Ok(None) } else { Err(StratisError::Msg(format!( "Crypt device has already been bound with key description {}; \ @@ -1133,8 +1129,7 @@ impl Backstore { ))) } } else { - handle.bind_keyring(Some(k), key_desc)?; - Ok(true) + handle.bind_keyring(Some(k), key_desc).map(Some) } } OptionalTokenSlotInput::None => { @@ -1145,10 +1140,9 @@ impl Backstore { .all_key_descriptions() .find(|(_, kd)| *kd == key_desc); if existing_config.is_some() { - Ok(false) + Ok(None) } else { - handle.bind_keyring(None, key_desc)?; - Ok(true) + handle.bind_keyring(None, key_desc).map(Some) } } } @@ -1587,7 +1581,7 @@ mod tests { "tang", &json!({"url": env::var("TANG_URL").expect("TANG_URL env var required")}) ), - Ok(false) + Ok(None) ); assert_matches!( @@ -1596,7 +1590,7 @@ mod tests { "tang", &json!({"url": env::var("TANG_URL").expect("TANG_URL env var required"), "stratis:tang:trust_url": true}) ), - Ok(false) + Ok(None) ); invariant(&backstore); @@ -1680,7 +1674,7 @@ mod tests { OptionalTokenSlotInput::Some(ci_slot), "tang", &json!({"url": env::var("TANG_URL").expect("TANG_URL env var required"), "stratis:tang:trust_url": true}), - ).unwrap() { + ).unwrap().is_some() { panic!( "Clevis bind idempotence test failed" ); @@ -1691,6 +1685,7 @@ mod tests { if backstore .bind_keyring(OptionalTokenSlotInput::None, key_desc) .unwrap() + .is_some() { panic!("Keyring bind idempotence test failed") } @@ -1698,6 +1693,7 @@ mod tests { if backstore .bind_keyring(OptionalTokenSlotInput::Some(kd_slot), key_desc) .unwrap() + .is_some() { panic!("Keyring bind idempotence test failed") } @@ -1722,11 +1718,11 @@ mod tests { invariant(&backstore); - if !backstore.bind_clevis( + if backstore.bind_clevis( OptionalTokenSlotInput::Some(10), "tang", &json!({"url": env::var("TANG_URL").expect("TANG_URL env var required"), "stratis:tang:trust_url": true}), - ).unwrap() { + ).unwrap().is_none() { panic!( "Clevis bind test failed" ); @@ -1752,16 +1748,18 @@ mod tests { invariant(&backstore); - if !backstore + if backstore .bind_keyring(OptionalTokenSlotInput::Some(11), key_desc) .unwrap() + .is_none() { panic!("Keyring bind test failed"); } - if !backstore + if backstore .bind_keyring(OptionalTokenSlotInput::Some(12), key_desc) .unwrap() + .is_none() { panic!("Keyring bind test failed"); } diff --git a/src/engine/strat_engine/crypt/handle/v2.rs b/src/engine/strat_engine/crypt/handle/v2.rs index a4ec1351ae..22ac9488e2 100644 --- a/src/engine/strat_engine/crypt/handle/v2.rs +++ b/src/engine/strat_engine/crypt/handle/v2.rs @@ -536,14 +536,11 @@ impl CryptHandle { token_slot: Option, pin: &str, json: &Value, - ) -> StratisResult<()> { + ) -> StratisResult { let mut json_owned = json.clone(); let yes = interpret_clevis_config(pin, &mut json_owned)?; - let t = match token_slot { - Some(t) => t, - None => self.metadata.encryption_info.free_token_slot(), - }; + let old_encryption_info = self.encryption_info(); let (_, key) = get_passphrase( &mut acquire_crypt_device(self.luks2_device_path())?, @@ -553,17 +550,25 @@ impl CryptHandle { clevis_luks_bind( self.luks2_device_path(), &Either::Right(key), - Some(t), + token_slot, pin, &json_owned, yes, )?; + + let encryption_info = + encryption_info_from_metadata(&mut acquire_crypt_device(self.luks2_device_path())?)?; + + let new_slot = encryption_info + .diff(old_encryption_info) + .expect("just added a token slot"); + self.metadata.encryption_info.add_info( - t, + new_slot, UnlockMechanism::ClevisInfo((pin.to_owned(), json.to_owned())), )?; - Ok(()) + Ok(new_slot) } /// Unbind the given device using clevis. @@ -629,7 +634,7 @@ impl CryptHandle { &mut self, token_slot: Option, key_desc: &KeyDescription, - ) -> StratisResult<()> { + ) -> StratisResult { let mut device = self.acquire_crypt_device()?; let (_, key) = get_passphrase(&mut device, self.encryption_info())?; @@ -638,12 +643,13 @@ impl CryptHandle { None => self.metadata.encryption_info.free_token_slot(), }; - add_keyring_keyslot(&mut device, Some(t), key_desc, Some(Either::Left(key)))?; + let token_slot = + add_keyring_keyslot(&mut device, Some(t), key_desc, Some(Either::Left(key)))?; self.metadata .encryption_info .add_info(t, UnlockMechanism::KeyDesc(key_desc.to_owned()))?; - Ok(()) + Ok(token_slot) } /// Remove keyring binding from the underlying LUKS2 volume. diff --git a/src/engine/strat_engine/crypt/shared.rs b/src/engine/strat_engine/crypt/shared.rs index a9264d6530..c604cd5441 100644 --- a/src/engine/strat_engine/crypt/shared.rs +++ b/src/engine/strat_engine/crypt/shared.rs @@ -102,7 +102,7 @@ pub fn add_keyring_keyslot( token_slot: Option, key_description: &KeyDescription, pass: Option>, -) -> StratisResult<()> { +) -> StratisResult { let key = key_desc_to_passphrase(key_description)?; let keyslot = match pass { Some(Either::Left(ref pass)) => { @@ -155,7 +155,7 @@ pub fn add_keyring_keyslot( "Failed to assign the LUKS2 keyring token to the Stratis keyslot" ); - Ok(()) + Ok(new_token_slot) } /// Create a device handle and load the LUKS2 header into memory from diff --git a/src/engine/strat_engine/pool/dispatch.rs b/src/engine/strat_engine/pool/dispatch.rs index 89e5d659ad..861114683c 100644 --- a/src/engine/strat_engine/pool/dispatch.rs +++ b/src/engine/strat_engine/pool/dispatch.rs @@ -48,7 +48,7 @@ impl Pool for AnyPool { token_slot: OptionalTokenSlotInput, pin: &str, clevis_info: &Value, - ) -> StratisResult> { + ) -> StratisResult> { match self { AnyPool::V1(p) => p.bind_clevis(token_slot, pin, clevis_info), AnyPool::V2(p) => p.bind_clevis(token_slot, pin, clevis_info), @@ -59,7 +59,7 @@ impl Pool for AnyPool { &mut self, token_slot: OptionalTokenSlotInput, key_description: &KeyDescription, - ) -> StratisResult> { + ) -> StratisResult> { match self { AnyPool::V1(p) => p.bind_keyring(token_slot, key_description), AnyPool::V2(p) => p.bind_keyring(token_slot, key_description), diff --git a/src/engine/strat_engine/pool/v1.rs b/src/engine/strat_engine/pool/v1.rs index 3d66415e82..ea1f8067f0 100644 --- a/src/engine/strat_engine/pool/v1.rs +++ b/src/engine/strat_engine/pool/v1.rs @@ -38,6 +38,7 @@ use crate::{ blockdev::{v1::StratBlockDev, InternalBlockDev}, ProcessedPathInfos, }, + crypt::{CLEVIS_LUKS_TOKEN_ID, LUKS2_TOKEN_ID}, liminal::DeviceSet, metadata::BDA, serde_structs::{FlexDevsSave, PoolSave, Recordable}, @@ -708,14 +709,14 @@ impl Pool for StratPool { token_slot: OptionalTokenSlotInput, pin: &str, clevis_info: &Value, - ) -> StratisResult> { + ) -> StratisResult> { if token_slot != OptionalTokenSlotInput::Legacy { return Err(StratisError::Msg("Specifying the token slot for binding is not supported in V1 pools; please migrate to V2 pools to use this feature".to_string())); } let changed = self.backstore.bind_clevis(pin, clevis_info)?; if changed { - Ok(CreateAction::Created(Clevis)) + Ok(CreateAction::Created((Clevis, CLEVIS_LUKS_TOKEN_ID))) } else { Ok(CreateAction::Identity) } @@ -727,14 +728,14 @@ impl Pool for StratPool { &mut self, token_slot: OptionalTokenSlotInput, key_description: &KeyDescription, - ) -> StratisResult> { + ) -> StratisResult> { if token_slot != OptionalTokenSlotInput::Legacy { return Err(StratisError::Msg("Specifying the token slot for binding is not supported in V1 pools; please migrate to V2 pools to use this feature".to_string())); } let changed = self.backstore.bind_keyring(key_description)?; if changed { - Ok(CreateAction::Created(Key)) + Ok(CreateAction::Created((Key, LUKS2_TOKEN_ID))) } else { Ok(CreateAction::Identity) } diff --git a/src/engine/strat_engine/pool/v2.rs b/src/engine/strat_engine/pool/v2.rs index c0677a20c8..847ba98b49 100644 --- a/src/engine/strat_engine/pool/v2.rs +++ b/src/engine/strat_engine/pool/v2.rs @@ -649,12 +649,11 @@ impl Pool for StratPool { token_slot: OptionalTokenSlotInput, pin: &str, clevis_info: &Value, - ) -> StratisResult> { + ) -> StratisResult> { let changed = self.backstore.bind_clevis(token_slot, pin, clevis_info)?; - if changed { - Ok(CreateAction::Created(Clevis)) - } else { - Ok(CreateAction::Identity) + match changed { + Some(t) => Ok(CreateAction::Created((Clevis, t))), + None => Ok(CreateAction::Identity), } } @@ -663,12 +662,11 @@ impl Pool for StratPool { &mut self, token_slot: OptionalTokenSlotInput, key_description: &KeyDescription, - ) -> StratisResult> { + ) -> StratisResult> { let changed = self.backstore.bind_keyring(token_slot, key_description)?; - if changed { - Ok(CreateAction::Created(Key)) - } else { - Ok(CreateAction::Identity) + match changed { + Some(t) => Ok(CreateAction::Created((Key, t))), + None => Ok(CreateAction::Identity), } } diff --git a/src/engine/types/actions.rs b/src/engine/types/actions.rs index dbd45cfcf2..5737d5949d 100644 --- a/src/engine/types/actions.rs +++ b/src/engine/types/actions.rs @@ -75,13 +75,13 @@ impl Display for CreateAction { } } -impl Display for CreateAction { +impl Display for CreateAction<(Clevis, u32)> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - CreateAction::Created(Clevis) => { + CreateAction::Created((_, t)) => { write!( f, - "Pool successfully bound to an unlocking mechanism using clevis" + "Pool successfully bound an unlocking mechanism to token slot {t} using clevis" ) } CreateAction::Identity => { @@ -94,13 +94,13 @@ impl Display for CreateAction { } } -impl Display for CreateAction { +impl Display for CreateAction<(Key, u32)> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - CreateAction::Created(Key) => { + CreateAction::Created((_, t)) => { write!( f, - "Pool successfully bound to a passphrase in the kernel keyring" + "Pool successfully bound to a passphrase to token slot {t} in the kernel keyring" ) } CreateAction::Identity => { diff --git a/src/engine/types/keys.rs b/src/engine/types/keys.rs index 4421e2bbe9..b25402eb30 100644 --- a/src/engine/types/keys.rs +++ b/src/engine/types/keys.rs @@ -390,6 +390,23 @@ impl EncryptionInfo { pub fn is_empty(&self) -> bool { self.encryption_infos.is_empty() } + + /// Returns the values that are in &self but not in &other. + pub fn diff(&self, other: &Self) -> Option { + self.encryption_infos + .keys() + .cloned() + .collect::>() + .difference( + &other + .encryption_infos + .keys() + .cloned() + .collect::>(), + ) + .next() + .cloned() + } } impl IntoIterator for EncryptionInfo {