From 86ea370e8f2d754d4172471fc17da168ec485d1e Mon Sep 17 00:00:00 2001 From: Tim Bruijnzeels Date: Tue, 20 Aug 2019 14:01:48 +0200 Subject: [PATCH] Support (great-) grandchildren under multiple lineages. Closes: #25 --- client/src/client.rs | 42 ++- client/src/options.rs | 209 ++++++++++++- client/src/report.rs | 4 +- commons/src/api/admin.rs | 2 +- daemon/src/ca/certauth.rs | 64 ++-- daemon/src/ca/commands.rs | 9 +- daemon/src/ca/server.rs | 310 +++++++++++--------- daemon/src/endpoints.rs | 74 ++++- daemon/src/http/server.rs | 13 +- daemon/src/krillserver.rs | 33 ++- daemon/src/mq.rs | 17 +- daemon/src/scheduler.rs | 1 - daemon/src/test.rs | 32 +- daemon/tests/ca_keyroll_under_rfc6492_ta.rs | 2 +- daemon/tests/ca_under_embedded_ta.rs | 6 +- daemon/tests/ca_under_rfc6492_ta.rs | 6 +- daemon/tests/grand_children.rs | 93 ++++++ 17 files changed, 675 insertions(+), 242 deletions(-) create mode 100644 daemon/tests/grand_children.rs diff --git a/client/src/client.rs b/client/src/client.rs index fe5585f6..afd49f3d 100644 --- a/client/src/client.rs +++ b/client/src/client.rs @@ -1,6 +1,6 @@ -use rpki::uri; use std::io; +use rpki::uri; use serde::de::DeserializeOwned; use krill_commons::api::admin::{ @@ -81,7 +81,7 @@ impl KrillClient { let uri = self.resolve_uri("api/v1/trustanchor/children"); let info: ParentCaContact = httpclient::post_json_with_response(&uri, req, Some(&self.token))?; - Ok(ApiResponse::ParentCaInfo(info)) + Ok(ApiResponse::ParentCaContact(info)) } TrustAnchorCommand::UpdateChild(child, req) => { let uri = format!("api/v1/trustanchor/children/{}", child); @@ -94,10 +94,9 @@ impl KrillClient { fn certauth(&self, command: CaCommand) -> Result { match command { - CaCommand::AddParent(handle, parent) => { - let uri = format!("api/v1/cas/{}/parents", handle); - let uri = self.resolve_uri(&uri); - httpclient::post_json(&uri, parent, Some(&self.token))?; + CaCommand::Init(init) => { + let uri = self.resolve_uri("api/v1/cas"); + httpclient::post_json(&uri, init, Some(&self.token))?; Ok(ApiResponse::Empty) } CaCommand::ChildRequest(handle) => { @@ -108,16 +107,28 @@ impl KrillClient { let req = rfc8183::ChildRequest::validate(xml.as_bytes())?; Ok(ApiResponse::Rfc8183ChildRequest(req)) } - CaCommand::Init(init) => { - let uri = self.resolve_uri("api/v1/cas"); - httpclient::post_json(&uri, init, Some(&self.token))?; + + CaCommand::AddParent(handle, parent) => { + let uri = format!("api/v1/cas/{}/parents", handle); + let uri = self.resolve_uri(&uri); + httpclient::post_json(&uri, parent, Some(&self.token))?; Ok(ApiResponse::Empty) } - CaCommand::List => { - let uri = self.resolve_uri("api/v1/cas"); - let cas = self.get_json(&uri)?; - Ok(ApiResponse::CertAuths(cas)) + + CaCommand::AddChild(handle, req) => { + let uri = format!("api/v1/cas/{}/children", handle); + let uri = self.resolve_uri(&uri); + let info: ParentCaContact = + httpclient::post_json_with_response(&uri, req, Some(&self.token))?; + Ok(ApiResponse::ParentCaContact(info)) } + CaCommand::UpdateChild(handle, child, req) => { + let uri = format!("api/v1/cas/{}/children/{}", handle, child); + let uri = self.resolve_uri(&uri); + httpclient::post_json(&uri, req, Some(&self.token))?; + Ok(ApiResponse::Empty) + } + CaCommand::KeyRollInit(handle) => { let uri = format!("api/v1/cas/{}/keys/roll_init", handle); let uri = self.resolve_uri(&uri); @@ -137,6 +148,11 @@ impl KrillClient { Ok(ApiResponse::CertAuthInfo(ca_info)) } + CaCommand::List => { + let uri = self.resolve_uri("api/v1/cas"); + let cas = self.get_json(&uri)?; + Ok(ApiResponse::CertAuths(cas)) + } } } diff --git a/client/src/options.rs b/client/src/options.rs index 626216b4..4b81e7a9 100644 --- a/client/src/options.rs +++ b/client/src/options.rs @@ -285,8 +285,119 @@ impl Options { ) - ) + + .subcommand(SubCommand::with_name("children") + .about("Manage children of this CA") + .arg(Arg::with_name("handle") + .short("h") + .long("handle") + .value_name("ca handle") + .help("The handle (name) for the CA") + .required(true) + ) + + .subcommand(SubCommand::with_name("add") + .about("Add a child to the embedded CA") + + .arg(Arg::with_name("asn") + .short("a") + .long("asn") + .value_name("AS resources") + .help("The delegated AS resources: e.g. AS1, AS3-4") + .required(false) + ) + .arg(Arg::with_name("ipv4") + .short("4") + .long("ipv4") + .value_name("IPv4 resources") + .help("The delegated IPv4 resources: e.g. 192.168.0.0/16") + .required(false) + ) + .arg(Arg::with_name("ipv6") + .short("6") + .long("ipv6") + .value_name("IPv6 resources") + .help("The delegated IPv6 resources: e.g. 2001:db8::/32") + .required(false) + ) + + .subcommand(SubCommand::with_name("embedded") + .about("Add an embedded child") + .arg(Arg::with_name("handle") + .short("h") + .long("handle") + .value_name("child-handle") + .help("The handle of the child") + .required(true) + ) + ) + + .subcommand(SubCommand::with_name("rfc6492") + .about("Add an RFC 6492 child") + .arg(Arg::with_name("handle") + .short("h") + .long("handle") + .value_name("child-handle") + .help("Override the handle in the XML") + .required(false) + ) + .arg(Arg::with_name("xml") + .short("x") + .long("xml") + .value_name("FILE") + .help("RFC 8183 Child Request XML") + .required(true) + ) + ) + ) + .subcommand(SubCommand::with_name("update") + .about("Update details for a child") + .arg(Arg::with_name("handle") + .short("h") + .long("handle") + .value_name("child-handle") + .help("Override the handle in the XML") + .required(false) + ) + .arg(Arg::with_name("xml") + .short("x") + .long("xml") + .value_name("FILE") + .help("Update child certificate from RFC 8183 Child Request XML") + .required(false) + ) + .arg(Arg::with_name("asn") + .short("a") + .long("asn") + .value_name("AS resources") + .help("Update the delegated AS resources: e.g. AS1, AS3-4") + .required(false) + ) + .arg(Arg::with_name("ipv4") + .short("4") + .long("ipv4") + .value_name("IPv4 resources") + .help("Update the delegated IPv4 resources: e.g. 192.168.0.0/16") + .required(false) + ) + .arg(Arg::with_name("ipv6") + .short("6") + .long("ipv6") + .value_name("IPv6 resources") + .help("Update the delegated IPv6 resources: e.g. 2001:db8::/32") + .required(false) + ) + .arg(Arg::with_name("force") + .short("f") + .long("force") + .takes_value(false) + .help("Force resource shrink now.") + .required(false) + ) + ) + ) + ) .subcommand(SubCommand::with_name("publishers") @@ -486,6 +597,79 @@ impl Options { let handle = Handle::from(m.value_of("handle").unwrap()); command = Command::CertAuth(CaCommand::Show(handle)); } + + if let Some(m) = m.subcommand_matches("children") { + let ca = Handle::from(m.value_of("handle").unwrap()); + + if let Some(m) = m.subcommand_matches("add") { + let asn = m.value_of("asn").unwrap_or(""); + let ipv4 = m.value_of("ipv4").unwrap_or(""); + let ipv6 = m.value_of("ipv6").unwrap_or(""); + + if let Some(m) = m.subcommand_matches("embedded") { + let handle = Handle::from(m.value_of("handle").unwrap()); + let res = ResourceSet::from_strs(asn, ipv4, ipv6).unwrap(); + let auth = ChildAuthRequest::Embedded; + + let req = AddChildRequest::new(handle, res, auth); + command = Command::TrustAnchor(TrustAnchorCommand::AddChild(req)) + } + + if let Some(m) = m.subcommand_matches("rfc6492") { + let xml_path = m.value_of("xml").unwrap(); + let xml = PathBuf::from(xml_path); + let bytes = file::read(&xml)?; + let cr = rfc8183::ChildRequest::validate(bytes.as_ref())?; + + let handle = { + if let Some(handle) = m.value_of("handle") { + Handle::from(handle) + } else { + cr.child_handle().clone() + } + }; + + let res = ResourceSet::from_strs(asn, ipv4, ipv6)?; + + let auth = ChildAuthRequest::Rfc8183(cr); + + let req = AddChildRequest::new(handle, res, auth); + command = Command::CertAuth(CaCommand::AddChild(ca, req)) + } + } else if let Some(m) = m.subcommand_matches("update") { + let handle = Handle::from(m.value_of("handle").unwrap()); + let cert = match m.value_of("xml") { + Some(xml_path) => { + let xml = PathBuf::from(xml_path); + let bytes = file::read(&xml)?; + let cr = rfc8183::ChildRequest::validate(bytes.as_ref())?; + let (_, _, cert) = cr.unwrap(); + Some(cert) + } + None => None, + }; + + let asn = m.value_of("asn").unwrap_or(""); + let ipv4 = m.value_of("ipv4").unwrap_or(""); + let ipv6 = m.value_of("ipv6").unwrap_or(""); + let resources = ResourceSet::from_strs(asn, ipv4, ipv6)?; + + let resources = if resources.is_empty() { + None + } else { + Some(resources) + }; + + let req = if m.is_present("force") { + UpdateChildRequest::force(cert, resources) + } else { + UpdateChildRequest::graceful(cert, resources) + }; + + command = Command::CertAuth(CaCommand::UpdateChild(ca, handle, req)) + } + } + if let Some(m) = m.subcommand_matches("update") { let handle = Handle::from(m.value_of("handle").unwrap()); @@ -598,13 +782,30 @@ pub enum TrustAnchorCommand { #[derive(Clone, Debug, Eq, PartialEq)] #[allow(clippy::large_enum_variant)] pub enum CaCommand { - AddParent(Handle, AddParentRequest), - ChildRequest(Handle), + // Initialise a CA Init(CertAuthInit), + + // Get the RFC8183 child request + ChildRequest(Handle), + + // Add a parent to this CA + AddParent(Handle, AddParentRequest), + + // Add a child to this CA + AddChild(Handle, AddChildRequest), + UpdateChild(Handle, Handle, UpdateChildRequest), + + // Initialise a manual key-roll now KeyRollInit(Handle), + + // Activate all new keys now (finish keyroll, provided new key was certified) KeyRollActivate(Handle), - List, + + // Show details for this CA Show(Handle), + + // List all CAs + List, } #[derive(Clone, Debug, Eq, PartialEq)] diff --git a/client/src/report.rs b/client/src/report.rs index 41377be3..ddc56958 100644 --- a/client/src/report.rs +++ b/client/src/report.rs @@ -19,7 +19,7 @@ pub enum ApiResponse { CertAuthInfo(CertAuthInfo), CertAuths(CertAuthList), - ParentCaInfo(ParentCaContact), + ParentCaContact(ParentCaContact), PublisherDetails(PublisherDetails), PublisherList(PublisherList), @@ -48,7 +48,7 @@ impl ApiResponse { ApiResponse::TrustAnchorInfo(ta) => Ok(Some(ta.report(fmt)?)), ApiResponse::CertAuths(list) => Ok(Some(list.report(fmt)?)), ApiResponse::CertAuthInfo(info) => Ok(Some(info.report(fmt)?)), - ApiResponse::ParentCaInfo(info) => Ok(Some(info.report(fmt)?)), + ApiResponse::ParentCaContact(contact) => Ok(Some(contact.report(fmt)?)), ApiResponse::PublisherList(list) => Ok(Some(list.report(fmt)?)), ApiResponse::PublisherDetails(details) => Ok(Some(details.report(fmt)?)), ApiResponse::Rfc8181ClientList(list) => Ok(Some(list.report(fmt)?)), diff --git a/commons/src/api/admin.rs b/commons/src/api/admin.rs index cbd414ae..cff7d6b7 100644 --- a/commons/src/api/admin.rs +++ b/commons/src/api/admin.rs @@ -327,7 +327,7 @@ impl AddParentRequest { #[derive(Clone, Debug, Deserialize, Display, Eq, PartialEq, Serialize)] #[allow(clippy::large_enum_variant)] pub enum ParentCaContact { - #[display(fmt = "In this context this CA is a self-signed TA")] + #[display(fmt = "This CA is a TA with TAL:\n{}", _0)] Ta(TrustAnchorLocator), #[display(fmt = "Embedded parent")] diff --git a/daemon/src/ca/certauth.rs b/daemon/src/ca/certauth.rs index 307cd2b7..0e8c8f03 100644 --- a/daemon/src/ca/certauth.rs +++ b/daemon/src/ca/certauth.rs @@ -251,7 +251,7 @@ impl Aggregate for CertAuth { // Key rolls CmdDet::KeyRollInitiate(duration, signer) => self.keyroll_initiate(duration, signer), CmdDet::KeyRollActivate(duration, signer) => self.keyroll_activate(duration, signer), - CmdDet::KeyRollFinish(parent, response) => self.keyroll_finish(&parent, response), + CmdDet::KeyRollFinish(rcn, response) => self.keyroll_finish(rcn, response), // Republish CmdDet::Republish(signer) => self.republish(signer), @@ -351,13 +351,13 @@ impl CertAuth { /// List entitlements (section 3.3.2 of RFC6492). Return an error if /// the child is not authorized -- or unknown etc. - /// - /// Only supported in TAs until issue #25 is implemented. pub fn list(&self, child_handle: &Handle) -> Result { - // TODO: Support arbitrary resource classes. See issue #25. let mut classes = vec![]; - if let Some(class) = self.entitlement_class(child_handle, &ResourceClassName::default()) { - classes.push(class); + + for rcn in self.resources.keys() { + if let Some(class) = self.entitlement_class(child_handle, rcn) { + classes.push(class); + } } Ok(Entitlements::new(classes)) @@ -498,8 +498,6 @@ impl CertAuth { /// = the csr is invalid, /// = the limit exceeds the child allocation, /// = the signer throws up.. - /// - /// This CA is not a TA (until #25) fn certify_child( &self, child: Handle, @@ -1018,12 +1016,15 @@ impl CertAuth { /// Get all the current open certificate requests for a parent. /// Returns an empty list if the parent is not found. - pub fn cert_requests(&self, parent_handle: &ParentHandle) -> Vec { - let mut res = vec![]; + pub fn cert_requests( + &self, + parent_handle: &ParentHandle, + ) -> HashMap> { + let mut res = HashMap::new(); - for rc in self.resources.values() { + for (name, rc) in self.resources.iter() { if rc.parent_handle() == parent_handle { - res.append(&mut rc.cert_requests()) + res.insert(name.clone(), rc.cert_requests()); } } @@ -1049,14 +1050,19 @@ impl CertAuth { } /// Returns the open revocation requests for the given parent. - pub fn revoke_requests(&self, parent: &ParentHandle) -> Vec<&RevocationRequest> { - let mut res = vec![]; - for rc in self.resources.values() { + pub fn revoke_requests( + &self, + parent: &ParentHandle, + ) -> HashMap> { + let mut res = HashMap::new(); + for (name, rc) in self.resources.iter() { + let mut revokes = vec![]; if let Some(req) = rc.revoke_request() { if rc.parent_handle() == parent { - res.push(req) + revokes.push(req.clone()) } } + res.insert(name.clone(), revokes); } res } @@ -1089,10 +1095,13 @@ impl CertAuth { .iter() .map(|c| c.class_name()) .collect(); - for (name, class) in current_resource_classes - .iter() - .filter(|(name, _class)| !entitled_classes.contains(name)) - { + + for (name, class) in current_resource_classes.iter().filter(|(_name, class)| { + // Find the classes for this parent, not included + // in the entitlements now received. + class.parent_handle() == &parent_handle + && !entitled_classes.contains(&class.parent_rc_name()) + }) { let signer = signer.read().unwrap(); let delta = class.withdraw(&self.base_repo); @@ -1261,19 +1270,18 @@ impl CertAuth { fn keyroll_finish( &self, - parent_h: &ParentHandle, - response: RevocationResponse, + rcn: ResourceClassName, + _response: RevocationResponse, ) -> ca::Result> { if self.is_ta() { return Ok(vec![]); } - let (parent_rc_name, _key_id) = response.unpack(); + let my_rc = self + .resources + .get(&rcn) + .ok_or_else(|| Error::unknown_resource_class(&rcn))?; - let (my_name, my_rc) = self - .find_parent_rc(parent_h, &parent_rc_name) - .ok_or_else(|| Error::unknown_resource_class(&parent_rc_name))?; - - let finish_details = my_rc.keyroll_finish(my_name.clone(), &self.base_repo)?; + let finish_details = my_rc.keyroll_finish(rcn, &self.base_repo)?; Ok(vec![StoredEvent::new( self.handle(), diff --git a/daemon/src/ca/commands.rs b/daemon/src/ca/commands.rs index 89690af9..e98078c7 100644 --- a/daemon/src/ca/commands.rs +++ b/daemon/src/ca/commands.rs @@ -67,7 +67,7 @@ pub enum CmdDet { // Finish the keyroll after the parent confirmed that a key for a parent and resource // class has been revoked. I.e. remove the old key, and withdraw the crl and mft for it. - KeyRollFinish(ParentHandle, RevocationResponse), + KeyRollFinish(ResourceClassName, RevocationResponse), // ------------------------------------------------------------ // Publishing @@ -81,8 +81,7 @@ impl eventsourcing::CommandDetails for CmdDet { impl CmdDet { /// Adds a child to this CA. Will return an error in case you try - /// to give the child resources not held by the CA. And until issue - /// #25 is implemented, returns an error when the CA is not a TA. + /// to give the child resources not held by the CA. pub fn child_add( handle: &Handle, child_handle: Handle, @@ -190,10 +189,10 @@ impl CmdDet { pub fn key_roll_finish( handle: &Handle, - parent: ParentHandle, + rcn: ResourceClassName, res: RevocationResponse, ) -> Cmd { - eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollFinish(parent, res)) + eventsourcing::SentCommand::new(handle, None, CmdDet::KeyRollFinish(rcn, res)) } pub fn publish(handle: &Handle, signer: Arc>) -> Cmd { diff --git a/daemon/src/ca/server.rs b/daemon/src/ca/server.rs index 41b9350e..ab99dd0f 100644 --- a/daemon/src/ca/server.rs +++ b/daemon/src/ca/server.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::ops::Deref; use std::path::PathBuf; use std::sync::{Arc, RwLock}; @@ -110,41 +111,41 @@ impl CaServer { Ok(()) } - /// Adds a child under the embedded TA - pub fn ta_add_child( + /// Adds a child under an embedded CA + pub fn ca_add_child( &self, + parent: &ParentHandle, req: AddChildRequest, service_uri: &uri::Https, ) -> ServerResult { - let (handle, resources, auth) = req.unwrap(); + let (child_handle, child_res, child_auth) = req.unwrap(); - debug!("Adding child {} to TA", &handle); + info!("Adding child {} to CA {}", &child_handle, &parent); - let ta = self.get_trust_anchor()?; - let ta_handle = ca::ta_handle(); + let ca = self.get_ca(parent)?; - let id_cert = match &auth { + let id_cert = match &child_auth { ChildAuthRequest::Embedded => None, ChildAuthRequest::Rfc8183(req) => Some(req.id_cert().clone()), }; - let add_child = CmdDet::child_add(&ta_handle, handle.clone(), id_cert, resources); + let add_child = CmdDet::child_add(&parent, child_handle.clone(), id_cert, child_res); - let events = ta.process_command(add_child)?; - let ta = self.ca_store.update(&ta_handle, ta, events)?; + let events = ca.process_command(add_child)?; + let ca = self.ca_store.update(&parent, ca, events)?; - match auth { + match child_auth { ChildAuthRequest::Embedded => Ok(ParentCaContact::Embedded), ChildAuthRequest::Rfc8183(req) => { - let service_uri = format!("{}rfc6492/{}", service_uri.to_string(), ta.handle()); + let service_uri = format!("{}rfc6492/{}", service_uri.to_string(), ca.handle()); let service_uri = uri::Https::from_string(service_uri).unwrap(); let service_uri = rfc8183::ServiceUri::Https(service_uri); let response = rfc8183::ParentResponse::new( req.tag().cloned(), - ta.id_cert().clone(), - ta.handle().clone(), - handle, + ca.id_cert().clone(), + ca.handle().clone(), + child_handle, service_uri, ); Ok(ParentCaContact::for_rfc6492(response)) @@ -154,11 +155,15 @@ impl CaServer { /// Show details for a child under the TA. Returns Ok(None) if the TA is present, /// but the child is not known. - pub fn ta_show_child(&self, child: &ChildHandle) -> ServerResult, S> { - debug!("Finding details for {} under TA", child); + pub fn ca_show_child( + &self, + parent: &ParentHandle, + child: &ChildHandle, + ) -> ServerResult, S> { + debug!("Finding details for CA: {} under parent: {}", child, parent); - let ta = self.get_trust_anchor()?; - let child_opt = match ta.get_child(child) { + let ca = self.get_ca(parent)?; + let child_opt = match ca.get_child(child) { Err(_) => None, Ok(child_details) => Some(child_details.clone().into()), }; @@ -166,30 +171,34 @@ impl CaServer { Ok(child_opt) } - pub fn ta_update_child( + /// Update a child under this CA. + pub fn ca_update_child( &self, + parent: &ParentHandle, child: ChildHandle, req: UpdateChildRequest, ) -> ServerResult<(), S> { - debug!("Updating details for {} under TA", child); - let mut ta = self.get_trust_anchor()?; - let ta_handle = ca::ta_handle(); + debug!( + "Updating details for CA: {} under parent: {}", + child, parent + ); + let mut ca = self.get_ca(parent)?; let force = req.is_force(); - let events = ta.process_command(CmdDet::child_update(&ta_handle, child.clone(), req))?; + let events = ca.process_command(CmdDet::child_update(parent, child.clone(), req))?; if !events.is_empty() { - ta = self.ca_store.update(&ta_handle, ta, events)?; + ca = self.ca_store.update(parent, ca, events)?; if force { - let events = ta.process_command(CmdDet::child_shrink( - &ta_handle, + let events = ca.process_command(CmdDet::child_shrink( + parent, child, Duration::seconds(0), self.signer.clone(), ))?; if !events.is_empty() { - self.ca_store.update(&ta_handle, ta, events)?; + self.ca_store.update(parent, ca, events)?; } } } @@ -250,22 +259,15 @@ impl CaServer { msg: rfc6492::Message, ) -> ServerResult { debug!("RFC6492 Response wrapping for {}", handle); - let ca = self.ca_store.get_latest(handle)?; - let res = ca + self.get_ca(handle)? .sign_rfc6492_response(msg, self.signer.read().unwrap().deref()) - .map_err(ServerError::::CertAuth); - debug!("RFC6492 Response wrapped for {}", handle); - res + .map_err(ServerError::::CertAuth) } /// List the entitlements for a child: 3.3.2 of RFC6492 pub fn list(&self, parent: &Handle, child: &Handle) -> ServerResult { - if parent != &ca::ta_handle() { - unimplemented!("https://github.com/NLnetLabs/krill/issues/25"); - } else { - let ta = self.get_trust_anchor()?; - Ok(ta.list(child)?) - } + let ca = self.get_ca(parent)?; + Ok(ca.list(child)?) } /// Issue a Certificate in response to a Certificate Issuance request @@ -277,34 +279,25 @@ impl CaServer { child: &ChildHandle, issue_req: IssuanceRequest, ) -> ServerResult { - if parent != &ca::ta_handle() { - unimplemented!("https://github.com/NLnetLabs/krill/issues/25"); - } else { - let ta = self.get_trust_anchor()?; + let ca = self.get_ca(parent)?; - let class_name = issue_req.class_name(); - let pub_key = issue_req.csr().public_key(); + let class_name = issue_req.class_name(); + let pub_key = issue_req.csr().public_key(); - if class_name != &ResourceClassName::default() { - unimplemented!("Issue for multiple classes from CAs, issue #25") - } + let cmd = CmdDet::child_certify( + parent, + child.clone(), + issue_req.clone(), + self.signer.clone(), + ); - let cmd = CmdDet::child_certify( - parent, - child.clone(), - issue_req.clone(), - self.signer.clone(), - ); + let events = ca.process_command(cmd)?; + let ca = self.ca_store.update(parent, ca, events)?; - let events = ta.process_command(cmd)?; - let ta = self.ca_store.update(parent, ta, events)?; + // The updated CA will now include the newly issued certificate. + let response = ca.issuance_response(child, &class_name, &pub_key)?; - // New entitlements will include this resource class, and - // the newly issued certificate. - let response = ta.issuance_response(child, &class_name, &pub_key)?; - - Ok(response) - } + Ok(response) } /// See: https://tools.ietf.org/html/rfc6492#section3.5.1-2 @@ -480,10 +473,12 @@ impl CaServer { let revoke_responses = self.send_revoke_requests(handle, parent, requests)?; - for response in revoke_responses.into_iter() { - let cmd = CmdDet::key_roll_finish(handle, parent.clone(), response); - let events = child.process_command(cmd)?; - child = self.ca_store.update(handle, child, events)?; + for (rcn, revoke_responses) in revoke_responses.into_iter() { + for response in revoke_responses.into_iter() { + let cmd = CmdDet::key_roll_finish(handle, rcn.clone(), response); + let events = child.process_command(cmd)?; + child = self.ca_store.update(handle, child, events)?; + } } Ok(()) @@ -493,77 +488,82 @@ impl CaServer { &self, handle: &Handle, parent: &ParentHandle, - requests: Vec<&RevocationRequest>, - ) -> ServerResult, S> { + revoke_requests: HashMap>, + ) -> ServerResult>, S> { let child = self.ca_store.get_latest(handle)?; match child.parent(parent)? { ParentCaContact::Ta(_) => { Err(ca::Error::NotAllowedForTa).map_err(ServerError::CertAuth) } ParentCaContact::Embedded => { - self.send_revoke_requests_embedded(requests, handle, parent) + self.send_revoke_requests_embedded(revoke_requests, handle, parent) } ParentCaContact::Rfc6492(parent_res) => { - self.send_revoke_requests_rfc6492(requests, child.id_key(), parent_res) + self.send_revoke_requests_rfc6492(revoke_requests, child.id_key(), parent_res) } } } fn send_revoke_requests_embedded( &self, - revoke_requests: Vec<&RevocationRequest>, + revoke_requests: HashMap>, handle: &Handle, parent_h: &ParentHandle, - ) -> ServerResult, S> { + ) -> ServerResult>, S> { let mut parent = self.ca_store.get_latest(parent_h)?; - let mut revocation_responses = vec![]; + let mut revoke_map = HashMap::new(); - for req in revoke_requests.into_iter() { - let cmd = CmdDet::child_revoke_key( - parent_h, - handle.clone(), - req.clone(), - self.signer.clone(), - ); + for (rcn, revoke_requests) in revoke_requests.into_iter() { + let mut revocations = vec![]; + for req in revoke_requests.into_iter() { + revocations.push((&req).into()); - let events = parent.process_command(cmd)?; - parent = self.ca_store.update(parent_h, parent, events)?; + let cmd = + CmdDet::child_revoke_key(parent_h, handle.clone(), req, self.signer.clone()); - revocation_responses.push(req.into()); + let events = parent.process_command(cmd)?; + parent = self.ca_store.update(parent_h, parent, events)?; + } + revoke_map.insert(rcn, revocations); } - Ok(revocation_responses) + Ok(revoke_map) } fn send_revoke_requests_rfc6492( &self, - revoke_requests: Vec<&RevocationRequest>, + revoke_requests: HashMap>, signing_key: &KeyId, parent_res: &rfc8183::ParentResponse, - ) -> ServerResult, S> { - let mut res = vec![]; + ) -> ServerResult>, S> { + let mut revoke_map = HashMap::new(); - for req in revoke_requests.into_iter() { - let sender = parent_res.child_handle().to_string(); - let recipient = parent_res.parent_handle().to_string(); - let revoke = rfc6492::Message::revoke(sender, recipient, req.clone()); + for (rcn, revoke_requests) in revoke_requests.into_iter() { + let mut revocations = vec![]; + for req in revoke_requests.into_iter() { + let sender = parent_res.child_handle().to_string(); + let recipient = parent_res.parent_handle().to_string(); + let revoke = rfc6492::Message::revoke(sender, recipient, req.clone()); - match self.send_rfc6492_and_validate_response( - signing_key, - parent_res, - revoke.into_bytes(), - ) { - Err(e) => error!("Could not send/validate revoke: {}", e), - Ok(response) => match response { - rfc6492::Res::Revoke(revoke_response) => res.push(revoke_response), - rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), - rfc6492::Res::List(_) => error!("List response to revoke request??"), - rfc6492::Res::Issue(_) => error!("Issue response to revoke request??"), - }, + match self.send_rfc6492_and_validate_response( + signing_key, + parent_res, + revoke.into_bytes(), + ) { + Err(e) => error!("Could not send/validate revoke: {}", e), + Ok(response) => match response { + rfc6492::Res::Revoke(revoke_response) => revocations.push(revoke_response), + rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), + rfc6492::Res::List(_) => error!("List response to revoke request??"), + rfc6492::Res::Issue(_) => error!("Issue response to revoke request??"), + }, + } } + + revoke_map.insert(rcn, revocations); } - Ok(res) + Ok(revoke_map) } fn send_cert_requests_handle_responses( @@ -586,14 +586,20 @@ impl CaServer { } }?; - for (class_name, issued) in issued_certs.into_iter() { - let received = RcvdCert::from(issued); + for (class_name, issued_certs) in issued_certs.into_iter() { + for issued in issued_certs.into_iter() { + let received = RcvdCert::from(issued); - let upd_rcvd_cmd = - CmdDet::upd_received_cert(handle, class_name, received, self.signer.clone()); + let upd_rcvd_cmd = CmdDet::upd_received_cert( + handle, + class_name.clone(), + received, + self.signer.clone(), + ); - let evts = child.process_command(upd_rcvd_cmd)?; - child = self.ca_store.update(handle, child, evts)?; + let evts = child.process_command(upd_rcvd_cmd)?; + child = self.ca_store.update(handle, child, evts)?; + } } Ok(()) @@ -601,64 +607,76 @@ impl CaServer { fn send_cert_requests_embedded( &self, - requests: Vec, + requests: HashMap>, handle: &Handle, parent_h: &ParentHandle, - ) -> ServerResult, S> { + ) -> ServerResult>, S> { let mut parent = self.ca_store.get_latest(parent_h)?; - let mut issued_certs: Vec<(ResourceClassName, IssuedCert)> = vec![]; + let mut issued_map = HashMap::new(); - for req in requests.into_iter() { - let class_name = req.class_name().clone(); - let pub_key = req.csr().public_key().clone(); + for (rcn, requests) in requests.into_iter() { + let mut issued_certs = vec![]; + for req in requests.into_iter() { + let pub_key = req.csr().public_key().clone(); + let parent_class = req.class_name().clone(); - let cmd = CmdDet::child_certify(parent_h, handle.clone(), req, self.signer.clone()); + let cmd = CmdDet::child_certify(parent_h, handle.clone(), req, self.signer.clone()); - let events = parent.process_command(cmd)?; - parent = self.ca_store.update(parent_h, parent, events)?; + let events = parent.process_command(cmd)?; + parent = self.ca_store.update(parent_h, parent, events)?; - let response = parent.issuance_response(handle, &class_name, &pub_key)?; + let response = parent.issuance_response(handle, &parent_class, &pub_key)?; - let (_, _, _, issued) = response.unwrap(); + let (_, _, _, issued) = response.unwrap(); - issued_certs.push((class_name, issued)); + issued_certs.push(issued); + } + + issued_map.insert(rcn, issued_certs); } - Ok(issued_certs) + + Ok(issued_map) } fn send_cert_requests_rfc6492( &self, - requests: Vec, + requests: HashMap>, signing_key: &KeyId, parent_res: &rfc8183::ParentResponse, - ) -> ServerResult, S> { - let mut res = vec![]; + ) -> ServerResult>, S> { + let mut issued_map = HashMap::new(); - for req in requests.into_iter() { - let sender = parent_res.child_handle().to_string(); - let recipient = parent_res.parent_handle().to_string(); - let issue = rfc6492::Message::issue(sender, recipient, req); + for (rcn, requests) in requests.into_iter() { + let mut issued_certs = vec![]; - match self.send_rfc6492_and_validate_response( - signing_key, - parent_res, - issue.into_bytes(), - ) { - Err(e) => error!("Could not send/validate csr: {}", e), - Ok(response) => match response { - rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), - rfc6492::Res::Issue(issue_response) => { - let (class_name, _, _, issued) = issue_response.unwrap(); - res.push((class_name, issued)); - } - rfc6492::Res::List(_) => error!("List reply to issue request??"), - rfc6492::Res::Revoke(_) => error!("Revoke reply to issue request??"), - }, + for req in requests.into_iter() { + let sender = parent_res.child_handle().to_string(); + let recipient = parent_res.parent_handle().to_string(); + let issue = rfc6492::Message::issue(sender, recipient, req); + + match self.send_rfc6492_and_validate_response( + signing_key, + parent_res, + issue.into_bytes(), + ) { + Err(e) => error!("Could not send/validate csr: {}", e), + Ok(response) => match response { + rfc6492::Res::NotPerformed(e) => error!("We got an error response: {}", e), + rfc6492::Res::Issue(issue_response) => { + let (_, _, _, issued) = issue_response.unwrap(); + issued_certs.push(issued); + } + rfc6492::Res::List(_) => error!("List reply to issue request??"), + rfc6492::Res::Revoke(_) => error!("Revoke reply to issue request??"), + }, + } } + + issued_map.insert(rcn, issued_certs); } - Ok(res) + Ok(issued_map) } /// Updates the CA if entitlements are different from what the CA diff --git a/daemon/src/endpoints.rs b/daemon/src/endpoints.rs index 23301d5c..32446601 100644 --- a/daemon/src/endpoints.rs +++ b/daemon/src/endpoints.rs @@ -18,8 +18,7 @@ use krill_pubd::publishers::PublisherError; use krill_pubd::repo::RrdpServerError; use crate::auth::Auth; -use crate::ca; -use crate::ca::ParentHandle; +use crate::ca::{self, ta_handle, ParentHandle}; use crate::http::server::AppServer; use crate::krillserver; @@ -278,19 +277,38 @@ pub fn ta_cer(server: web::Data) -> HttpResponse { } } +// TODO: Deprecate! pub fn ta_add_child( server: web::Data, req: Json, auth: Auth, ) -> HttpResponse { if_api_allowed(&server, &auth, || { - match server.read().ta_add_child(req.into_inner()) { + match server.read().ca_add_child(&ta_handle(), req.into_inner()) { Ok(info) => render_json(info), Err(e) => server_error(&Error::ServerError(e)), } }) } +pub fn ca_add_child( + server: web::Data, + parent: Path, + req: Json, + auth: Auth, +) -> HttpResponse { + if_api_allowed(&server, &auth, || { + match server + .read() + .ca_add_child(&parent.into_inner(), req.into_inner()) + { + Ok(info) => render_json(info), + Err(e) => server_error(&Error::ServerError(e)), + } + }) +} + +// TODO: Deprecate pub fn ta_update_child( server: web::Data, child: Path, @@ -298,21 +316,59 @@ pub fn ta_update_child( auth: Auth, ) -> HttpResponse { if_api_allowed(&server, &auth, || { - render_empty_res( - server - .read() - .ta_update_child(child.into_inner(), req.into_inner()), - ) + render_empty_res(server.read().ca_update_child( + &ta_handle(), + child.into_inner(), + req.into_inner(), + )) }) } +pub fn ca_update_child( + server: web::Data, + parent: Path, + child: Path, + req: Json, + auth: Auth, +) -> HttpResponse { + if_api_allowed(&server, &auth, || { + render_empty_res(server.read().ca_update_child( + &parent.into_inner(), + child.into_inner(), + req.into_inner(), + )) + }) +} + +// TODO: Deprecate pub fn ta_show_child( server: web::Data, child: Path, auth: Auth, ) -> HttpResponse { if_api_allowed(&server, &auth, || { - match server.read().ta_show_child(&child.into_inner()) { + match server + .read() + .ca_show_child(&ta_handle(), &child.into_inner()) + { + Ok(Some(child)) => render_json(child), + Ok(None) => api_not_found(), + Err(e) => server_error(&Error::ServerError(e)), + } + }) +} + +pub fn ca_show_child( + server: web::Data, + parent: Path, + child: Path, + auth: Auth, +) -> HttpResponse { + if_api_allowed(&server, &auth, || { + match server + .read() + .ca_show_child(&parent.into_inner(), &child.into_inner()) + { Ok(Some(child)) => render_json(child), Ok(None) => api_not_found(), Err(e) => server_error(&Error::ServerError(e)), diff --git a/daemon/src/http/server.rs b/daemon/src/http/server.rs index 1f6305bb..8851c2b1 100644 --- a/daemon/src/http/server.rs +++ b/daemon/src/http/server.rs @@ -82,12 +82,15 @@ pub fn start(config: &Config) -> Result<(), Error> { .route("/trustanchor/children/{handle}", post().to(ta_update_child)) .route("/cas", post().to(ca_init)) .route("/cas", get().to(cas)) - .route("/cas/{handle}", get().to(ca_info)) - .route("/cas/{handle}/child_request", get().to(ca_child_req)) - .route("/cas/{handle}/parents", post().to(ca_add_parent)) - .route("/cas/{handle}/keys/roll_init", post().to(ca_keyroll_init)) + .route("/cas/{ca}", get().to(ca_info)) + .route("/cas/{ca}/child_request", get().to(ca_child_req)) + .route("/cas/{ca}/parents", post().to(ca_add_parent)) + .route("/cas/{ca}/children", post().to(ca_add_child)) + .route("/cas/{ca}/children/{child}", get().to(ca_show_child)) + .route("/cas/{ca}/children/{child}", post().to(ca_update_child)) + .route("/cas/{ca}/keys/roll_init", post().to(ca_keyroll_init)) .route( - "/cas/{handle}/keys/roll_activate", + "/cas/{ca}/keys/roll_activate", post().to(ca_keyroll_activate), ) .route("/republish", post().to(republish_all)), diff --git a/daemon/src/krillserver.rs b/daemon/src/krillserver.rs index 2d7ccdb2..f9100f15 100644 --- a/daemon/src/krillserver.rs +++ b/daemon/src/krillserver.rs @@ -27,8 +27,7 @@ use krill_pubd::publishers::Publisher; use krill_pubd::PubServer; use crate::auth::{Auth, Authorizer}; -use crate::ca::ChildHandle; -use crate::ca::{self, ta_handle}; +use crate::ca::{self, ta_handle, ChildHandle, ParentHandle}; use crate::config::Config; use crate::mq::EventQueueListener; use crate::scheduler::Scheduler; @@ -310,21 +309,35 @@ impl KrillServer { Ok(()) } - /// Adds a child to the TA and returns the ParentCaInfo that the child - /// will to contact this TA for resource requests. - pub fn ta_add_child(&self, req: AddChildRequest) -> KrillRes { - let contact = self.caserver.ta_add_child(req, &self.service_uri)?; + /// Adds a child to a CA and returns the ParentCaInfo that the child + /// will need to contact this CA for resource requests. + pub fn ca_add_child( + &self, + parent: &ParentHandle, + req: AddChildRequest, + ) -> KrillRes { + let contact = self.caserver.ca_add_child(parent, req, &self.service_uri)?; Ok(contact) } - pub fn ta_update_child(&self, child: ChildHandle, req: UpdateChildRequest) -> EmptyRes { - self.caserver.ta_update_child(child, req)?; + /// Update IdCert or resources of a child. + pub fn ca_update_child( + &self, + parent: &ParentHandle, + child: ChildHandle, + req: UpdateChildRequest, + ) -> EmptyRes { + self.caserver.ca_update_child(parent, child, req)?; Ok(()) } /// Show details for a child under the TA. - pub fn ta_show_child(&self, child: &ChildHandle) -> KrillRes> { - let child = self.caserver.ta_show_child(child)?; + pub fn ca_show_child( + &self, + parent: &ParentHandle, + child: &ChildHandle, + ) -> KrillRes> { + let child = self.caserver.ca_show_child(parent, child)?; Ok(child) } diff --git a/daemon/src/mq.rs b/daemon/src/mq.rs index b2fd7b3e..c90b8e01 100644 --- a/daemon/src/mq.rs +++ b/daemon/src/mq.rs @@ -3,11 +3,12 @@ //! signed material, or asking a newly added parent for resource //! entitlements. -use std::collections::VecDeque; +use std::collections::{HashMap, VecDeque}; use std::fmt; use std::sync::RwLock; use krill_commons::api::admin::Handle; +use krill_commons::api::ca::ResourceClassName; use krill_commons::api::publication::PublishDelta; use krill_commons::api::RevocationRequest; use krill_commons::eventsourcing; @@ -24,7 +25,11 @@ pub enum QueueEvent { Delta(Handle, PublishDelta), ParentAdded(Handle, ParentHandle), RequestsPending(Handle), - ResourceClassRemoved(Handle, ParentHandle, Vec), + ResourceClassRemoved( + Handle, + ParentHandle, + HashMap>, + ), } #[derive(Debug)] @@ -72,12 +77,16 @@ impl eventsourcing::EventListener> for EventQueueListener let evt = QueueEvent::Delta(handle.clone(), publish_delta); self.push_back(evt); } - EvtDet::ResourceClassRemoved(_class_name, delta, parent, revocations) => { + EvtDet::ResourceClassRemoved(class_name, delta, parent, revocations) => { self.push_back(QueueEvent::Delta(handle.clone(), delta.clone().into())); + + let mut revocations_map = HashMap::new(); + revocations_map.insert(class_name.clone(), revocations.clone()); + self.push_back(QueueEvent::ResourceClassRemoved( handle.clone(), parent.clone(), - revocations.clone(), + revocations_map, )) } EvtDet::KeyRollFinished(_class_name, delta) => { diff --git a/daemon/src/scheduler.rs b/daemon/src/scheduler.rs index f9de5490..c7a6870e 100644 --- a/daemon/src/scheduler.rs +++ b/daemon/src/scheduler.rs @@ -64,7 +64,6 @@ fn make_event_sh( publish(&handle, delta, &pubserver); } QueueEvent::ResourceClassRemoved(handle, parent, revocations) => { - let revocations = revocations.iter().collect(); if caserver .send_revoke_requests(&handle, &parent, revocations) .is_err() diff --git a/daemon/src/test.rs b/daemon/src/test.rs index 4842c834..b7a8ec5f 100644 --- a/daemon/src/test.rs +++ b/daemon/src/test.rs @@ -15,7 +15,7 @@ use krill_commons::api::ca::{CertAuthInfo, ResourceClassKeysInfo, ResourceClassN use krill_commons::remote::rfc8183; use krill_commons::util::test; -use crate::ca::ta_handle; +use crate::ca::{ta_handle, ChildHandle, ParentHandle}; use crate::config::Config; use crate::http::server; @@ -101,7 +101,7 @@ pub fn add_child_to_ta_embedded(handle: &Handle, resources: ResourceSet) -> Pare let res = krill_admin(Command::TrustAnchor(TrustAnchorCommand::AddChild(req))); match res { - ApiResponse::ParentCaInfo(info) => info, + ApiResponse::ParentCaContact(info) => info, _ => panic!("Expected ParentCaInfo response"), } } @@ -116,7 +116,23 @@ pub fn add_child_to_ta_rfc6492( let res = krill_admin(Command::TrustAnchor(TrustAnchorCommand::AddChild(req))); match res { - ApiResponse::ParentCaInfo(info) => info, + ApiResponse::ParentCaContact(info) => info, + _ => panic!("Expected ParentCaInfo response"), + } +} + +pub fn add_child_rfc6492( + parent: &ParentHandle, + child: &ChildHandle, + req: rfc8183::ChildRequest, + resources: ResourceSet, +) -> ParentCaContact { + let auth = ChildAuthRequest::Rfc8183(req); + let req = AddChildRequest::new(child.clone(), resources, auth); + let res = krill_admin(Command::CertAuth(CaCommand::AddChild(parent.clone(), req))); + + match res { + ApiResponse::ParentCaContact(info) => info, _ => panic!("Expected ParentCaInfo response"), } } @@ -180,7 +196,7 @@ where panic!(error_msg); } -pub fn wait_for_resources_on_current_key(handle: &Handle, resources: &ResourceSet) { +pub fn wait_for_current_resources(handle: &Handle, resources: &ResourceSet) { wait_for( 30, "cms child did not get its resource certificate", @@ -249,17 +265,19 @@ pub fn ta_issued_resources(child: &Handle) -> ResourceSet { pub fn ca_current_resources(handle: &Handle) -> ResourceSet { let ca = ca_details(handle); - if let Some(rc) = ca.resources().get(&ResourceClassName::default()) { + let mut res = ResourceSet::default(); + + for rc in ca.resources().values() { match rc.keys() { ResourceClassKeysInfo::Active(current) | ResourceClassKeysInfo::RollPending(_, current) | ResourceClassKeysInfo::RollNew(_, current) | ResourceClassKeysInfo::RollOld(current, _) => { - return current.incoming_cert().resources().clone() + res = res.union(current.incoming_cert().resources()); } _ => {} } } - ResourceSet::default() + res } diff --git a/daemon/tests/ca_keyroll_under_rfc6492_ta.rs b/daemon/tests/ca_keyroll_under_rfc6492_ta.rs index 4c493851..ab40dcf7 100644 --- a/daemon/tests/ca_keyroll_under_rfc6492_ta.rs +++ b/daemon/tests/ca_keyroll_under_rfc6492_ta.rs @@ -28,7 +28,7 @@ fn ca_keyroll_under_rfc6492_ta() { }; add_parent_to_ca(&child, parent); - wait_for_resources_on_current_key(&child, &child_resources); + wait_for_current_resources(&child, &child_resources); wait_for_ta_to_have_number_of_issued_certs(1); ca_roll_init(&child); diff --git a/daemon/tests/ca_under_embedded_ta.rs b/daemon/tests/ca_under_embedded_ta.rs index 5b985a3f..f53161f9 100644 --- a/daemon/tests/ca_under_embedded_ta.rs +++ b/daemon/tests/ca_under_embedded_ta.rs @@ -28,7 +28,7 @@ fn ca_under_embedded_ta() { // When the parent is added, a child CA will immediately request a certificate. add_parent_to_ca(&child, parent); - wait_for_resources_on_current_key(&child, &child_resources); + wait_for_current_resources(&child, &child_resources); wait_for_ta_to_have_number_of_issued_certs(1); // When the parent adds resources to a CA, it will allocate them only when the child @@ -36,7 +36,7 @@ fn ca_under_embedded_ta() { let new_child_resources = ResourceSet::from_strs("AS65000", "10.0.0.0/16", "").unwrap(); force_update_child(&child, &new_child_resources); assert_eq!(ta_issued_resources(&child), child_resources); - wait_for_resources_on_current_key(&child, &new_child_resources); + wait_for_current_resources(&child, &new_child_resources); wait_for_ta_to_have_number_of_issued_certs(1); assert_eq!(ta_issued_resources(&child), new_child_resources); @@ -45,7 +45,7 @@ fn ca_under_embedded_ta() { let child_resources = ResourceSet::from_strs("", "10.0.0.0/24", "").unwrap(); force_update_child(&child, &child_resources); assert_eq!(ta_issued_resources(&child), child_resources); - wait_for_resources_on_current_key(&child, &child_resources); + wait_for_current_resources(&child, &child_resources); // When all resources are removed, the child still gets a chance to clean up if force // is not used.. The child will request that its certificate is revoked, and remove diff --git a/daemon/tests/ca_under_rfc6492_ta.rs b/daemon/tests/ca_under_rfc6492_ta.rs index dd81e8e0..4b33dc23 100644 --- a/daemon/tests/ca_under_rfc6492_ta.rs +++ b/daemon/tests/ca_under_rfc6492_ta.rs @@ -29,7 +29,7 @@ fn ca_under_rfc6492_ta() { // When the parent is added, a child CA will immediately request a certificate. add_parent_to_ca(&child, parent); - wait_for_resources_on_current_key(&child, &child_resources); + wait_for_current_resources(&child, &child_resources); wait_for_ta_to_have_number_of_issued_certs(1); // When the parent adds resources to a CA, it will allocate them only when the child @@ -37,7 +37,7 @@ fn ca_under_rfc6492_ta() { let new_child_resources = ResourceSet::from_strs("AS65000", "10.0.0.0/16", "").unwrap(); force_update_child(&child, &new_child_resources); assert_eq!(ta_issued_resources(&child), child_resources); - wait_for_resources_on_current_key(&child, &new_child_resources); + wait_for_current_resources(&child, &new_child_resources); wait_for_ta_to_have_number_of_issued_certs(1); assert_eq!(ta_issued_resources(&child), new_child_resources); @@ -46,7 +46,7 @@ fn ca_under_rfc6492_ta() { let child_resources = ResourceSet::from_strs("", "10.0.0.0/24", "").unwrap(); force_update_child(&child, &child_resources); assert_eq!(ta_issued_resources(&child), child_resources); - wait_for_resources_on_current_key(&child, &child_resources); + wait_for_current_resources(&child, &child_resources); // When all resources are removed, the child still gets a chance to clean up if force // is not used.. The child will request that its certificate is revoked, and remove diff --git a/daemon/tests/grand_children.rs b/daemon/tests/grand_children.rs new file mode 100644 index 00000000..3a8609b6 --- /dev/null +++ b/daemon/tests/grand_children.rs @@ -0,0 +1,93 @@ +extern crate krill_client; +extern crate krill_commons; +extern crate krill_daemon; +extern crate krill_pubc; + +use krill_commons::api::admin::{AddParentRequest, Handle, Token}; +use krill_commons::api::ca::ResourceSet; +use krill_daemon::ca::ta_handle; +use krill_daemon::test::*; + +#[test] +fn grand_children() { + test_with_krill_server(|_d| { + // Test that we can delegate from normal CAs to child CAs, and that these child CAs + // can have multiple parents. + // + // TA + // / \ + // CA1 CA2 + // \ / + // CA3 (two resource classes) + // | | + // CA4 (two resource classes) + // + + // -------------------- TA ----------------------------------------------- + + let ta_handle = ta_handle(); + init_ta(); + + // -------------------- CA1 ----------------------------------------------- + let ca1 = Handle::from("CA1"); + let ca1_res = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); + + init_child(&ca1, &Token::from("CA1")); + let req = child_request(&ca1); + let parent = { + let contact = add_child_to_ta_rfc6492(&ca1, req, ca1_res.clone()); + AddParentRequest::new(ta_handle.clone(), contact) + }; + add_parent_to_ca(&ca1, parent); + wait_for_current_resources(&ca1, &ca1_res); + + // -------------------- CA2 ----------------------------------------------- + let ca2 = Handle::from("CA2"); + let ca2_res = ResourceSet::from_strs("", "10.1.0.0/16", "").unwrap(); + + init_child(&ca2, &Token::from("CA2")); + let req = child_request(&ca2); + let parent = { + let contact = add_child_to_ta_rfc6492(&ca2, req, ca2_res.clone()); + AddParentRequest::new(ta_handle.clone(), contact) + }; + add_parent_to_ca(&ca2, parent); + wait_for_current_resources(&ca2, &ca2_res); + + // -------------------- CA3 ----------------------------------------------- + let ca3 = Handle::from("CA3"); + let ca_3_res_under_ca_1 = ResourceSet::from_strs("", "10.0.0.0/16", "").unwrap(); + + init_child(&ca3, &Token::from("CA3")); + let req = child_request(&ca3); + let parent = { + let contact = add_child_rfc6492(&ca1, &ca3, req, ca_3_res_under_ca_1.clone()); + AddParentRequest::new(ca1.clone(), contact) + }; + add_parent_to_ca(&ca3, parent); + wait_for_current_resources(&ca3, &ca_3_res_under_ca_1); + + let ca_3_res_under_ca_2 = ResourceSet::from_strs("", "10.1.0.0/24", "").unwrap(); + let ca_3_res = ca_3_res_under_ca_1.union(&ca_3_res_under_ca_2); + let req = child_request(&ca3); + let parent = { + let contact = add_child_rfc6492(&ca2, &ca3, req, ca_3_res_under_ca_2.clone()); + AddParentRequest::new(ca2.clone(), contact) + }; + add_parent_to_ca(&ca3, parent); + wait_for_current_resources(&ca3, &ca_3_res); + + // -------------------- CA4 ----------------------------------------------- + let ca4 = Handle::from("CA4"); + let ca_4_res_under_ca_3 = ResourceSet::from_strs("", "10.0.0.0-10.1.0.255", "").unwrap(); + + init_child(&ca4, &Token::from("CA4")); + let req = child_request(&ca4); + let parent = { + let contact = add_child_rfc6492(&ca3, &ca4, req, ca_4_res_under_ca_3.clone()); + AddParentRequest::new(ca3.clone(), contact) + }; + add_parent_to_ca(&ca4, parent); + wait_for_current_resources(&ca4, &ca_4_res_under_ca_3); + }); +}