diff --git a/server/lib/src/constants/entries.rs b/server/lib/src/constants/entries.rs index dece34daf..b9383baa6 100644 --- a/server/lib/src/constants/entries.rs +++ b/server/lib/src/constants/entries.rs @@ -585,7 +585,7 @@ lazy_static! { "description", Value::new_utf8s("System (local) info and metadata object.") ), - ("version", Value::Uint32(14)) + ("version", Value::Uint32(15)) ); } diff --git a/server/lib/src/constants/schema.rs b/server/lib/src/constants/schema.rs index ea1583a74..3ebea51cb 100644 --- a/server/lib/src/constants/schema.rs +++ b/server/lib/src/constants/schema.rs @@ -549,8 +549,8 @@ pub static ref SCHEMA_CLASS_PERSON: SchemaClass = SchemaClass { description: "Object representation of a person".to_string(), sync_allowed: true, - systemmay: attrstring_vec!(["mail", "legalname", "id_verification_eckey"]), - systemmust: attrstring_vec!(["displayname", "name"]), + systemmay: attrstring_vec!(["mail", "legalname"]), + systemmust: attrstring_vec!(["displayname", "name", "id_verification_eckey"]), ..Default::default() }; diff --git a/server/lib/src/plugins/eckeygen.rs b/server/lib/src/plugins/eckeygen.rs index 6e9266721..693643ce8 100644 --- a/server/lib/src/plugins/eckeygen.rs +++ b/server/lib/src/plugins/eckeygen.rs @@ -1,54 +1,39 @@ use openssl::ec::{EcGroup, EcKey}; use openssl::nid::Nid; -use sketching::{admin_error, security_info}; -use uuid::Uuid; + +use crate::prelude::*; use super::Plugin; -use crate::event::{CreateEvent, ModifyEvent}; -use crate::modify::{ModifyList, ModifyValid}; -use crate::prelude::{BatchModifyEvent, EntryInvalidCommitted, Modify}; -use crate::prelude::{Entry, EntryInvalid, EntryInvalidNew, OperationError}; -use crate::server::QueryServerWriteTransaction; -use crate::value::PartialValue; -use sketching::tagged_event; -use sketching::EventTag; lazy_static! { // it contains all the partialvalues used to match against an Entry's class, // we need ALL partialvalues to match in order to target the entry - static ref CLASSES_TO_UPDATE: [PartialValue; 3] = [PartialValue::new_iutf8("account"), PartialValue::new_iutf8("person"), PartialValue::new_iutf8("object")]; - static ref DEFAULT_KEY_GROUP: EcGroup = { let nid = Nid::X9_62_PRIME256V1; // NIST P-256 curve #[allow(clippy::unwrap_used)] EcGroup::from_curve_name(nid).unwrap() }; } + pub struct EcdhKeyGen {} impl EcdhKeyGen { - fn is_entry_to_update(entry: &mut Entry) -> bool { - CLASSES_TO_UPDATE - .iter() - .all(|pv| entry.attribute_equality("class", pv)) - } // we optionally provide a target_cand to update only the entry with the given uuid fn generate_key( cands: &mut [Entry], - target_cand: Option, ) -> Result<(), OperationError> { for cand in cands.iter_mut() { - if Self::is_entry_to_update(cand) { - if let (Some(target_cand), Some(current_uuid)) = (target_cand, cand.get_uuid()) { - if target_cand != current_uuid { - continue; - } - } + if cand.attribute_equality("class", &PVCLASS_PERSON) + && !cand.attribute_pres("id_verification_eckey") + { + debug!("Generating idv_eckey for {}", cand.get_display_id()); + let new_private_key = EcKey::generate(&DEFAULT_KEY_GROUP).map_err(|e| { - admin_error!(err = ?e, "Unable to generate identification ECDH private key"); + error!(err = ?e, "Unable to generate id verification ECDH private key"); OperationError::CryptographyError })?; - cand.add_ava_if_not_exist( + + cand.add_ava( "id_verification_eckey", crate::value::Value::EcKeyPrivate(new_private_key), ) @@ -56,49 +41,6 @@ impl EcdhKeyGen { } Ok(()) } - - fn handle_modify( - cands: &mut [EntryInvalidCommitted], - me: &ModifyEvent, - ) -> Result<(), OperationError> { - if Self::should_regenerate_ecdh_key(&me.modlist)? { - security_info!("regenerating personal ecdh secret"); - Self::generate_key(cands, None)?; - }; - - Ok(()) - } - - fn handle_batch_modify( - cands: &mut [EntryInvalidCommitted], - me: &BatchModifyEvent, - ) -> Result<(), OperationError> { - for (uuid, modlist) in me.modset.iter() { - if Self::should_regenerate_ecdh_key(modlist)? { - security_info!("regenerating personal ecdh secret"); - Self::generate_key(cands, Some(*uuid))?; - }; - } - Ok(()) - } - - fn should_regenerate_ecdh_key( - modlist: &ModifyList, - ) -> Result { - let modify_present_attempted = modlist.iter().any(|m| match m { - Modify::Present(a, _) => a == "id_verification_eckey", - _ => false, - }); - if modify_present_attempted { - Err(OperationError::SystemProtectedAttribute) - } else { - let should_regenerate_ecdh_key = modlist.iter().any(|m| match m { - Modify::Purged(a) | Modify::Removed(a, _) => a == "id_verification_eckey", - _ => false, - }); - Ok(should_regenerate_ecdh_key) - } - } } impl Plugin for EcdhKeyGen { @@ -106,30 +48,33 @@ impl Plugin for EcdhKeyGen { "plugin_ecdhkey_gen" } + #[instrument(level = "debug", name = "ecdhkeygen::pre_create_transform", skip_all)] fn pre_create_transform( _qs: &mut QueryServerWriteTransaction, cand: &mut Vec, _ce: &CreateEvent, ) -> Result<(), OperationError> { - Self::generate_key(cand, None) + Self::generate_key(cand) } + #[instrument(level = "debug", name = "ecdhkeygen::pre_modify", skip_all)] fn pre_modify( _qs: &mut crate::server::QueryServerWriteTransaction, _pre_cand: &[std::sync::Arc], cand: &mut Vec, - me: &crate::event::ModifyEvent, + _me: &crate::event::ModifyEvent, ) -> Result<(), kanidm_proto::v1::OperationError> { - Self::handle_modify(cand, me) + Self::generate_key(cand) } + #[instrument(level = "debug", name = "ecdhkeygen::pre_batch_modify", skip_all)] fn pre_batch_modify( _qs: &mut crate::server::QueryServerWriteTransaction, _pre_cand: &[std::sync::Arc], cand: &mut Vec, - me: &crate::server::batch_modify::BatchModifyEvent, + _me: &crate::server::batch_modify::BatchModifyEvent, ) -> Result<(), kanidm_proto::v1::OperationError> { - Self::handle_batch_modify(cand, me) + Self::generate_key(cand) } } @@ -175,6 +120,9 @@ mod tests { ); } + /* + // Invalid, can't be set due to no impl from clone_value + #[test] fn test_modify_present_ecdkey() { let ea = entry_init!( @@ -200,6 +148,7 @@ mod tests { |_| {} ); } + */ #[test] fn test_modify_purge_eckey() { diff --git a/server/lib/src/plugins/protected.rs b/server/lib/src/plugins/protected.rs index 248de652a..496c3f7e6 100644 --- a/server/lib/src/plugins/protected.rs +++ b/server/lib/src/plugins/protected.rs @@ -28,6 +28,7 @@ lazy_static! { m.insert("domain_ldap_basedn"); m.insert("fernet_private_key_str"); m.insert("es256_private_key_der"); + m.insert("id_verification_eckey"); m.insert("badlist_password"); m.insert("domain_display_name"); m diff --git a/server/lib/src/repl/tests.rs b/server/lib/src/repl/tests.rs index 43da9eb51..3e34bc1f6 100644 --- a/server/lib/src/repl/tests.rs +++ b/server/lib/src/repl/tests.rs @@ -1500,6 +1500,7 @@ async fn test_repl_increment_schema_conflict(server_a: &QueryServer, server_b: & let modlist = ModifyList::new_list(vec![ Modify::Removed("class".into(), PVCLASS_PERSON.clone()), Modify::Present("class".into(), CLASS_GROUP.clone()), + Modify::Purged("id_verification_eckey".into()), Modify::Purged("displayname".into()), ]); assert!(server_b_txn.internal_modify_uuid(t_uuid, &modlist).is_ok()); diff --git a/server/lib/src/server/migrations.rs b/server/lib/src/server/migrations.rs index 88cf54299..b513d0033 100644 --- a/server/lib/src/server/migrations.rs +++ b/server/lib/src/server/migrations.rs @@ -107,6 +107,10 @@ impl QueryServer { if system_info_version < 14 { write_txn.migrate_13_to_14()?; } + + if system_info_version < 15 { + write_txn.migrate_14_to_15()?; + } } write_txn.reload()?; @@ -421,6 +425,17 @@ impl<'a> QueryServerWriteTransaction<'a> { // Complete } + #[instrument(level = "debug", skip_all)] + pub fn migrate_14_to_15(&mut self) -> Result<(), OperationError> { + admin_warn!("starting 14 to 15 migration."); + let filter = filter!(f_eq("class", PVCLASS_PERSON.clone())); + // Delete the non-existing attr for idv private key which triggers + // it to regen. + let modlist = ModifyList::new_purge("id_verification_eckey"); + self.internal_modify(&filter, &modlist) + // Complete + } + #[instrument(level = "debug", skip_all)] pub fn initialise_schema_core(&mut self) -> Result<(), OperationError> { admin_debug!("initialise_schema_core -> start ...");