20230817 idv migration (#1992)

* Must attr
* Post merge cleanup of idv
This commit is contained in:
Firstyear 2023-08-18 20:29:00 +10:00 committed by GitHub
parent 17741c4929
commit f6001504a9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 43 additions and 77 deletions

View file

@ -585,7 +585,7 @@ lazy_static! {
"description",
Value::new_utf8s("System (local) info and metadata object.")
),
("version", Value::Uint32(14))
("version", Value::Uint32(15))
);
}

View file

@ -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()
};

View file

@ -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<VALUE, STATE>(entry: &mut Entry<VALUE, STATE>) -> 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<STATE: Clone>(
cands: &mut [Entry<EntryInvalid, STATE>],
target_cand: Option<Uuid>,
) -> 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<ModifyValid>,
) -> Result<bool, OperationError> {
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<EntryInvalidNew>,
_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<crate::prelude::EntrySealedCommitted>],
cand: &mut Vec<crate::prelude::EntryInvalidCommitted>,
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<crate::prelude::EntrySealedCommitted>],
cand: &mut Vec<crate::prelude::EntryInvalidCommitted>,
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() {

View file

@ -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

View file

@ -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());

View file

@ -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 ...");