From 59a3631267eed4cfef091a28ab84c9776e3a6789 Mon Sep 17 00:00:00 2001 From: Hugues de Valon Date: Fri, 15 Jan 2021 11:25:35 +0000 Subject: [PATCH] Add ListClients and DeleteClient operations Also adds multitenancy tests for those operations and for the admin feature. Splits the ApplicationName structure into Application and ApplicationName to only deal with key names in the Key Info Managers and not the admin status which is not relevent there. Signed-off-by: Hugues de Valon --- Cargo.lock | 3 +- Cargo.toml | 2 +- e2e_tests/provider_cfg/all/config.toml | 1 + e2e_tests/src/lib.rs | 17 ++- e2e_tests/src/stress.rs | 4 +- e2e_tests/tests/all_providers/multitenancy.rs | 33 ++++++ e2e_tests/tests/all_providers/normal.rs | 46 ++++++++ .../direct_authenticator/mod.rs | 20 ++-- .../jwt_svid_authenticator/mod.rs | 6 +- src/authenticators/mod.rs | 50 +++++--- .../mod.rs | 35 ++++-- src/back/backend_handler.rs | 109 ++++++++++-------- src/back/dispatcher.rs | 10 +- src/front/front_end.rs | 14 +-- src/key_info_managers/mod.rs | 21 ++++ src/key_info_managers/on_disk_manager/mod.rs | 2 +- src/providers/core/mod.rs | 42 ++++++- src/providers/cryptoauthlib/mod.rs | 9 +- src/providers/mbed_crypto/mod.rs | 16 ++- src/providers/mod.rs | 21 +++- src/providers/pkcs11/mod.rs | 16 ++- src/providers/tpm/mod.rs | 16 ++- 22 files changed, 377 insertions(+), 116 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 80eca3e7..c2b2eedb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -989,8 +989,7 @@ dependencies = [ [[package]] name = "parsec-interface" version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2315911063bdd449304da2c4d36ab1816a199c3de13c0efa0e30856305bf49c5" +source = "git+https://github.com/parallaxsecond/parsec-interface-rs?rev=c8a59544fac04df347f51d19323f4a0b5bb9580d#c8a59544fac04df347f51d19323f4a0b5bb9580d" dependencies = [ "bincode", "derivative", diff --git a/Cargo.toml b/Cargo.toml index db6ae467..5008a014 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,7 +18,7 @@ name = "parsec" path = "src/bin/main.rs" [dependencies] -parsec-interface = "0.22.0" +parsec-interface = { git = "https://github.com/parallaxsecond/parsec-interface-rs", rev = "c8a59544fac04df347f51d19323f4a0b5bb9580d" } rand = { version = "0.7.3", features = ["small_rng"], optional = true } base64 = "0.12.3" uuid = "0.8.1" diff --git a/e2e_tests/provider_cfg/all/config.toml b/e2e_tests/provider_cfg/all/config.toml index 40ccd267..b6537788 100644 --- a/e2e_tests/provider_cfg/all/config.toml +++ b/e2e_tests/provider_cfg/all/config.toml @@ -14,6 +14,7 @@ socket_path = "/tmp/parsec.sock" [authenticator] auth_type = "Direct" +admins = [ { name = "list_clients test" }, { name = "1000" }, { name = "client1" }, { name = "spiffe://example.org/parsec-client-1" } ] #workload_endpoint="unix:///tmp/agent.sock" [[key_manager]] diff --git a/e2e_tests/src/lib.rs b/e2e_tests/src/lib.rs index 4ef03d5d..4237565b 100644 --- a/e2e_tests/src/lib.rs +++ b/e2e_tests/src/lib.rs @@ -53,7 +53,10 @@ impl TestClient { /// Creates a TestClient instance. pub fn new() -> TestClient { // As this method is called in test, it will be called more than once per application. - if let Err(_) = env_logger::try_init() {}; + #[allow(unused_must_use)] + { + env_logger::try_init(); + } let mut basic_client = BasicClient::new_naked(); @@ -892,6 +895,18 @@ impl TestClient { self.basic_client.list_keys().map_err(convert_error) } + /// Lists the clients. + pub fn list_clients(&mut self) -> Result> { + self.basic_client.list_clients().map_err(convert_error) + } + + /// Delete a client. + pub fn delete_client(&mut self, client: String) -> Result<()> { + self.basic_client + .delete_client(client) + .map_err(convert_error) + } + /// Executes a ping operation. pub fn ping(&mut self) -> Result<(u8, u8)> { self.basic_client.ping().map_err(convert_error) diff --git a/e2e_tests/src/stress.rs b/e2e_tests/src/stress.rs index 63c42081..de96fa22 100644 --- a/e2e_tests/src/stress.rs +++ b/e2e_tests/src/stress.rs @@ -330,7 +330,7 @@ impl ServiceChecker { .expect("Verification failed"); client - .destroy_key(sign_key_name.clone()) + .destroy_key(sign_key_name) .expect("Failed to destroy key"); } @@ -352,7 +352,7 @@ impl ServiceChecker { assert_eq!(plaintext, vec![0xa5; 16]); client - .destroy_key(encr_key_name.clone()) + .destroy_key(encr_key_name) .expect("Failed to destroy key"); } } diff --git a/e2e_tests/tests/all_providers/multitenancy.rs b/e2e_tests/tests/all_providers/multitenancy.rs index 625dabb0..5a0c2037 100644 --- a/e2e_tests/tests/all_providers/multitenancy.rs +++ b/e2e_tests/tests/all_providers/multitenancy.rs @@ -9,6 +9,8 @@ use parsec_client::core::interface::requests::{ProviderID, ResponseStatus}; // 3. client1_after is executed as parsec-client-1 // // They are executed against all possible authenticators in Parsec. +// +// client1 will be configured as an admin. #[test] fn client1_before() { @@ -23,11 +25,15 @@ fn client1_before() { client.set_provider(*provider); client.generate_rsa_sign_key(key.clone()).unwrap(); } + + let clients = client.list_clients().unwrap(); + assert_eq!(clients.len(), 1); } #[test] fn client2() { let mut client = TestClient::new(); + client.do_not_destroy_keys(); client.set_default_auth(Some("client2".to_string())); let key = String::from("multitenant"); @@ -49,11 +55,24 @@ fn client2() { client.generate_rsa_sign_key(key.clone()).unwrap(); client.destroy_key(key.clone()).unwrap(); } + + assert_eq!( + client.list_clients().unwrap_err(), + ResponseStatus::AdminOperation + ); + assert_eq!( + client.delete_client("toto".to_string()).unwrap_err(), + ResponseStatus::AdminOperation + ); + client + .generate_rsa_sign_key("client2-key".to_string()) + .unwrap(); } #[test] fn client1_after() { let mut client = TestClient::new(); + client.do_not_destroy_keys(); client.set_default_auth(Some("client1".to_string())); // Verify all keys are still there and can be used @@ -66,4 +85,18 @@ fn client1_after() { client.set_provider(*provider); client.destroy_key(key.clone()).unwrap(); } + + client + .generate_rsa_sign_key("client1-key".to_string()) + .unwrap(); + let mut clients = client.list_clients().unwrap(); + assert_eq!(clients.len(), 2); + client.delete_client(clients.remove(0)).unwrap(); + let mut clients = client.list_clients().unwrap(); + assert_eq!(clients.len(), 1); + client.delete_client(clients.remove(0)).unwrap(); + let clients = client.list_clients().unwrap(); + assert_eq!(clients.len(), 0); + let keys = client.list_keys().unwrap(); + assert_eq!(keys.len(), 0); } diff --git a/e2e_tests/tests/all_providers/normal.rs b/e2e_tests/tests/all_providers/normal.rs index 4d7207b2..4186a066 100644 --- a/e2e_tests/tests/all_providers/normal.rs +++ b/e2e_tests/tests/all_providers/normal.rs @@ -167,3 +167,49 @@ fn invalid_provider_list_keys() { .expect("Failed to read Response"); assert_eq!(resp.header.status, ResponseStatus::PsaErrorNotSupported); } + +#[test] +fn invalid_provider_list_clients() { + let mut client = RawRequestClient {}; + let mut req_hdr = RawHeader::new(); + + // Always targeting the Mbed Crypto provider + req_hdr.provider = 0x1; + req_hdr.opcode = Opcode::ListClients as u32; + + let resp = client + .send_raw_request(req_hdr, Vec::new()) + .expect("Failed to read Response"); + assert_eq!(resp.header.status, ResponseStatus::PsaErrorNotSupported); +} + +#[test] +fn list_and_delete_clients() { + let mut client = TestClient::new(); + client.do_not_destroy_keys(); + client.set_default_auth(Some("list_clients test".to_string())); + + let clients = client.list_clients().expect("list_clients failed"); + assert!(!clients.contains(&"list_clients test".to_string())); + + let key1 = String::from("list_clients1"); + let key2 = String::from("list_keys2"); + let key3 = String::from("list_keys3"); + + client.set_provider(ProviderID::MbedCrypto); + client.generate_rsa_sign_key(key1.clone()).unwrap(); + client.set_provider(ProviderID::Pkcs11); + client.generate_rsa_sign_key(key2.clone()).unwrap(); + client.set_provider(ProviderID::Tpm); + client.generate_rsa_sign_key(key3.clone()).unwrap(); + + let clients = client.list_clients().expect("list_clients failed"); + assert!(clients.contains(&"list_clients test".to_string())); + client + .delete_client("list_clients test".to_string()) + .unwrap(); + + let keys = client.list_keys().expect("list_keys failed"); + + assert!(keys.is_empty()); +} diff --git a/src/authenticators/direct_authenticator/mod.rs b/src/authenticators/direct_authenticator/mod.rs index 124a7fd1..00eef9ee 100644 --- a/src/authenticators/direct_authenticator/mod.rs +++ b/src/authenticators/direct_authenticator/mod.rs @@ -8,7 +8,7 @@ //! This authenticator does not offer any security value and should only be used in environments //! where all the clients and the service are mutually trustworthy. -use super::{Admin, AdminList, ApplicationName, Authenticate}; +use super::{Admin, AdminList, Application, Authenticate}; use crate::front::listener::ConnectionMetadata; use log::error; use parsec_interface::operations::list_authenticators; @@ -51,7 +51,7 @@ impl Authenticate for DirectAuthenticator { &self, auth: &RequestAuth, _: Option, - ) -> Result { + ) -> Result { if auth.buffer.expose_secret().is_empty() { error!("The direct authenticator does not expect empty authentication values."); Err(ResponseStatus::AuthenticationError) @@ -60,7 +60,7 @@ impl Authenticate for DirectAuthenticator { Ok(str) => { let app_name = String::from(str); let is_admin = self.admins.is_admin(&app_name); - Ok(ApplicationName::new(app_name, is_admin)) + Ok(Application::new(app_name, is_admin)) } Err(_) => { error!("Error parsing the authentication value as a UTF-8 string."); @@ -75,6 +75,7 @@ impl Authenticate for DirectAuthenticator { mod test { use super::super::{Admin, Authenticate}; use super::DirectAuthenticator; + use crate::authenticators::ApplicationName; use parsec_interface::requests::request::RequestAuth; use parsec_interface::requests::ResponseStatus; @@ -88,12 +89,12 @@ mod test { let req_auth = RequestAuth::new(app_name.clone().into_bytes()); let conn_metadata = None; - let auth_name = authenticator + let app = authenticator .authenticate(&req_auth, conn_metadata) .expect("Failed to authenticate"); - assert_eq!(auth_name.get_name(), app_name); - assert_eq!(auth_name.is_admin, false); + assert_eq!(app.get_name(), &ApplicationName::from_name(app_name)); + assert_eq!(app.is_admin, false); } #[test] @@ -140,7 +141,7 @@ mod test { .authenticate(&req_auth, conn_metadata) .expect("Failed to authenticate"); - assert_eq!(auth_name.get_name(), app_name); + assert_eq!(auth_name.get_name(), &ApplicationName::from_name(app_name)); assert_eq!(auth_name.is_admin, false); let req_auth = RequestAuth::new(admin_name.clone().into_bytes()); @@ -148,7 +149,10 @@ mod test { .authenticate(&req_auth, conn_metadata) .expect("Failed to authenticate"); - assert_eq!(auth_name.get_name(), admin_name); + assert_eq!( + auth_name.get_name(), + &ApplicationName::from_name(admin_name) + ); assert_eq!(auth_name.is_admin, true); } } diff --git a/src/authenticators/jwt_svid_authenticator/mod.rs b/src/authenticators/jwt_svid_authenticator/mod.rs index e065c35d..641a80a8 100644 --- a/src/authenticators/jwt_svid_authenticator/mod.rs +++ b/src/authenticators/jwt_svid_authenticator/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 //! JWT SVID authenticator -use super::{Admin, AdminList, ApplicationName, Authenticate}; +use super::{Admin, AdminList, Application, Authenticate}; use crate::front::listener::ConnectionMetadata; use log::error; use parsec_interface::operations::list_authenticators; @@ -48,7 +48,7 @@ impl Authenticate for JwtSvidAuthenticator { &self, auth: &RequestAuth, _: Option, - ) -> Result { + ) -> Result { let svid = Jwt::new( str::from_utf8(auth.buffer.expose_secret()) .map_err(|e| { @@ -68,6 +68,6 @@ impl Authenticate for JwtSvidAuthenticator { })?; let app_name = validate_response.spiffe_id().to_string(); let is_admin = self.admins.is_admin(&app_name); - Ok(ApplicationName::new(app_name, is_admin)) + Ok(Application::new(app_name, is_admin)) } } diff --git a/src/authenticators/mod.rs b/src/authenticators/mod.rs index 5c91f803..4d185566 100644 --- a/src/authenticators/mod.rs +++ b/src/authenticators/mod.rs @@ -27,6 +27,20 @@ use zeroize::Zeroize; #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct ApplicationName { name: String, +} + +impl Deref for ApplicationName { + type Target = String; + + fn deref(&self) -> &Self::Target { + &self.name + } +} + +/// Wrapper for a Parsec application +#[derive(Debug, Clone)] +pub struct Application { + name: ApplicationName, is_admin: bool, } @@ -40,7 +54,7 @@ pub trait Authenticate { /// operation. fn describe(&self) -> Result; - /// Authenticates a `RequestAuth` payload and returns the `ApplicationName` if successful. A + /// Authenticates a `RequestAuth` payload and returns the `Application` if successful. A /// optional `ConnectionMetadata` object is passed in too, since it is sometimes possible to /// perform authentication based on the connection's metadata (i.e. as is the case for UNIX /// domain sockets with Unix peer credentials). @@ -52,32 +66,40 @@ pub trait Authenticate { &self, auth: &RequestAuth, meta: Option, - ) -> Result; + ) -> Result; } impl ApplicationName { - /// Create a new ApplicationName - fn new(name: String, is_admin: bool) -> ApplicationName { - ApplicationName { name, is_admin } - } - /// Create ApplicationName from name string only pub fn from_name(name: String) -> ApplicationName { - ApplicationName { - name, - is_admin: false, - } + ApplicationName { name } } +} - /// Get a reference to the inner string - pub fn get_name(&self) -> &str { - &self.name +impl Application { + /// Create a new Application structure + pub fn new(name: String, is_admin: bool) -> Application { + Application { + name: ApplicationName::from_name(name), + is_admin, + } } /// Check whether the application is an admin pub fn is_admin(&self) -> bool { self.is_admin } + + /// Get a reference to the inner ApplicationName string + pub fn get_name(&self) -> &ApplicationName { + &self.name + } +} + +impl From for ApplicationName { + fn from(auth: Application) -> Self { + auth.name + } } impl std::fmt::Display for ApplicationName { diff --git a/src/authenticators/unix_peer_credentials_authenticator/mod.rs b/src/authenticators/unix_peer_credentials_authenticator/mod.rs index 8081b5c0..7dcd47a5 100644 --- a/src/authenticators/unix_peer_credentials_authenticator/mod.rs +++ b/src/authenticators/unix_peer_credentials_authenticator/mod.rs @@ -9,7 +9,7 @@ //! //! Currently, the stringified UID is used as the application name. -use super::{Admin, AdminList, ApplicationName, Authenticate}; +use super::{Admin, AdminList, Application, Authenticate}; use crate::front::listener::ConnectionMetadata; use log::error; use parsec_interface::operations::list_authenticators; @@ -53,7 +53,7 @@ impl Authenticate for UnixPeerCredentialsAuthenticator { &self, auth: &RequestAuth, meta: Option, - ) -> Result { + ) -> Result { // Parse authentication request. let expected_uid_bytes = auth.buffer.expose_secret(); @@ -88,7 +88,7 @@ impl Authenticate for UnixPeerCredentialsAuthenticator { if uid == expected_uid { let app_name = uid.to_string(); let is_admin = self.admins.is_admin(&app_name); - Ok(ApplicationName::new(app_name, is_admin)) + Ok(Application::new(app_name, is_admin)) } else { error!("Declared UID in authentication request does not match the process's UID."); Err(ResponseStatus::AuthenticationError) @@ -100,6 +100,7 @@ impl Authenticate for UnixPeerCredentialsAuthenticator { mod test { use super::super::{Admin, Authenticate}; use super::UnixPeerCredentialsAuthenticator; + use crate::authenticators::ApplicationName; use crate::front::domain_socket::peer_credentials; use crate::front::listener::ConnectionMetadata; use parsec_interface::requests::request::RequestAuth; @@ -136,7 +137,10 @@ mod test { .authenticate(&req_auth, conn_metadata) .expect("Failed to authenticate"); - assert_eq!(auth_name.get_name(), get_current_uid().to_string()); + assert_eq!( + auth_name.get_name(), + &ApplicationName::from_name(get_current_uid().to_string()) + ); assert_eq!(auth_name.is_admin, false); } @@ -165,8 +169,10 @@ mod test { pid: cred_a.pid, }); - let auth_result = authenticator.authenticate(&req_auth, conn_metadata); - assert_eq!(auth_result, Err(ResponseStatus::AuthenticationError)); + let auth_result = authenticator + .authenticate(&req_auth, conn_metadata) + .unwrap_err(); + assert_eq!(auth_result, ResponseStatus::AuthenticationError); } #[test] @@ -192,8 +198,10 @@ mod test { pid: cred_a.pid, }); - let auth_result = authenticator.authenticate(&req_auth, conn_metadata); - assert_eq!(auth_result, Err(ResponseStatus::AuthenticationError)); + let auth_result = authenticator + .authenticate(&req_auth, conn_metadata) + .unwrap_err(); + assert_eq!(auth_result, ResponseStatus::AuthenticationError); } #[test] @@ -204,8 +212,10 @@ mod test { let req_auth = RequestAuth::new("secret".into()); let conn_metadata = None; - let auth_result = authenticator.authenticate(&req_auth, conn_metadata); - assert_eq!(auth_result, Err(ResponseStatus::AuthenticationError)); + let auth_result = authenticator + .authenticate(&req_auth, conn_metadata) + .unwrap_err(); + assert_eq!(auth_result, ResponseStatus::AuthenticationError); } #[test] @@ -236,7 +246,10 @@ mod test { .authenticate(&req_auth, conn_metadata) .expect("Failed to authenticate"); - assert_eq!(auth_name.get_name(), get_current_uid().to_string()); + assert_eq!( + auth_name.get_name(), + &ApplicationName::from_name(get_current_uid().to_string()) + ); assert_eq!(auth_name.is_admin, true); } diff --git a/src/back/backend_handler.rs b/src/back/backend_handler.rs index faed93d6..7ca8aedc 100644 --- a/src/back/backend_handler.rs +++ b/src/back/backend_handler.rs @@ -5,10 +5,10 @@ //! The backend handler embodies the last processing step from external request //! to internal function call - parsing of the request body and conversion to a //! native operation which is then passed to the provider. -use crate::authenticators::ApplicationName; +use crate::authenticators::Application; use crate::providers::Provide; use derivative::Derivative; -use log::{error, trace}; +use log::{error, trace, warn}; use parsec_interface::operations::Convert; use parsec_interface::operations::{NativeOperation, NativeResult}; use parsec_interface::requests::{ @@ -82,7 +82,7 @@ impl BackEndHandler { /// /// If any of the steps fails, a response containing an appropriate status code is /// returned. - pub fn execute_request(&self, request: Request, app_name: Option) -> Response { + pub fn execute_request(&self, request: Request, app: Option) -> Response { trace!("execute_request ingress"); let opcode = request.header.opcode; let header = request.header; @@ -96,6 +96,20 @@ impl BackEndHandler { }; } + if opcode.is_admin() { + let app = + unwrap_or_else_return!((&app).as_ref().ok_or(ResponseStatus::NotAuthenticated)); + + if !app.is_admin() { + warn!( + "Application name \"{}\" tried to perform an admin operation ({:?}).", + app.get_name(), + opcode + ); + return Response::from_request_header(header, ResponseStatus::AdminOperation); + } + } + match unwrap_or_else_return!(self.converter.body_to_operation(request.body, opcode)) { NativeOperation::ListProviders(op_list_providers) => { let result = @@ -114,96 +128,87 @@ impl BackEndHandler { self.result_to_response(NativeResult::Ping(result), header) } NativeOperation::PsaGenerateKey(op_generate_key) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self .provider - .psa_generate_key(app_name, op_generate_key)); + .psa_generate_key(app.into(), op_generate_key)); trace!("psa_generate_key egress"); self.result_to_response(NativeResult::PsaGenerateKey(result), header) } NativeOperation::PsaImportKey(op_import_key) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = - unwrap_or_else_return!(self.provider.psa_import_key(app_name, op_import_key)); + unwrap_or_else_return!(self.provider.psa_import_key(app.into(), op_import_key)); trace!("psa_import_key egress"); self.result_to_response(NativeResult::PsaImportKey(result), header) } NativeOperation::PsaExportPublicKey(op_export_public_key) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self .provider - .psa_export_public_key(app_name, op_export_public_key)); + .psa_export_public_key(app.into(), op_export_public_key)); trace!("psa_export_public_key egress"); self.result_to_response(NativeResult::PsaExportPublicKey(result), header) } NativeOperation::PsaExportKey(op_export_key) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = - unwrap_or_else_return!(self.provider.psa_export_key(app_name, op_export_key)); + unwrap_or_else_return!(self.provider.psa_export_key(app.into(), op_export_key)); trace!("psa_export_public_key egress"); self.result_to_response(NativeResult::PsaExportKey(result), header) } NativeOperation::PsaDestroyKey(op_destroy_key) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); - let result = - unwrap_or_else_return!(self.provider.psa_destroy_key(app_name, op_destroy_key)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); + let result = unwrap_or_else_return!(self + .provider + .psa_destroy_key(app.into(), op_destroy_key)); trace!("psa_destroy_key egress"); self.result_to_response(NativeResult::PsaDestroyKey(result), header) } NativeOperation::PsaSignHash(op_sign_hash) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = - unwrap_or_else_return!(self.provider.psa_sign_hash(app_name, op_sign_hash)); + unwrap_or_else_return!(self.provider.psa_sign_hash(app.into(), op_sign_hash)); trace!("psa_sign_hash egress"); self.result_to_response(NativeResult::PsaSignHash(result), header) } NativeOperation::PsaVerifyHash(op_verify_hash) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); - let result = - unwrap_or_else_return!(self.provider.psa_verify_hash(app_name, op_verify_hash)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); + let result = unwrap_or_else_return!(self + .provider + .psa_verify_hash(app.into(), op_verify_hash)); trace!("psa_verify_hash egress"); self.result_to_response(NativeResult::PsaVerifyHash(result), header) } NativeOperation::PsaAsymmetricEncrypt(op_asymmetric_encrypt) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self .provider - .psa_asymmetric_encrypt(app_name, op_asymmetric_encrypt)); + .psa_asymmetric_encrypt(app.into(), op_asymmetric_encrypt)); trace!("psa_asymmetric_encrypt_egress"); self.result_to_response(NativeResult::PsaAsymmetricEncrypt(result), header) } NativeOperation::PsaAsymmetricDecrypt(op_asymmetric_decrypt) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self .provider - .psa_asymmetric_decrypt(app_name, op_asymmetric_decrypt)); + .psa_asymmetric_decrypt(app.into(), op_asymmetric_decrypt)); trace!("psa_asymmetric_decrypt_egress"); self.result_to_response(NativeResult::PsaAsymmetricDecrypt(result), header) } NativeOperation::PsaAeadEncrypt(op_aead_encrypt) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self .provider - .psa_aead_encrypt(app_name, op_aead_encrypt)); + .psa_aead_encrypt(app.into(), op_aead_encrypt)); trace!("psa_aead_encrypt_egress"); self.result_to_response(NativeResult::PsaAeadEncrypt(result), header) } NativeOperation::PsaAeadDecrypt(op_aead_decrypt) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self .provider - .psa_aead_decrypt(app_name, op_aead_decrypt)); + .psa_aead_decrypt(app.into(), op_aead_decrypt)); trace!("psa_aead_decrypt_egress"); self.result_to_response(NativeResult::PsaAeadDecrypt(result), header) } @@ -215,35 +220,39 @@ impl BackEndHandler { self.result_to_response(NativeResult::ListAuthenticators(result), header) } NativeOperation::ListKeys(op_list_keys) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = - unwrap_or_else_return!(self.provider.list_keys(app_name, op_list_keys)); + unwrap_or_else_return!(self.provider.list_keys(app.into(), op_list_keys)); trace!("list_keys egress"); self.result_to_response(NativeResult::ListKeys(result), header) } + NativeOperation::ListClients(op_list_clients) => { + let result = unwrap_or_else_return!(self.provider.list_clients(op_list_clients)); + trace!("list_clients egress"); + self.result_to_response(NativeResult::ListClients(result), header) + } + NativeOperation::DeleteClient(op_delete_client) => { + let result = unwrap_or_else_return!(self.provider.delete_client(op_delete_client)); + trace!("delete_client egress"); + self.result_to_response(NativeResult::DeleteClient(result), header) + } NativeOperation::PsaHashCompute(op_hash_compute) => { - let _app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self.provider.psa_hash_compute(op_hash_compute)); - trace!("psa_aead_decrypt_egress"); + trace!("psa_hash_compute_egress"); self.result_to_response(NativeResult::PsaHashCompute(result), header) } NativeOperation::PsaHashCompare(op_hash_compare) => { - let _app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self.provider.psa_hash_compare(op_hash_compare)); - trace!("psa_aead_decrypt_egress"); + trace!("psa_hash_compare_egress"); self.result_to_response(NativeResult::PsaHashCompare(result), header) } NativeOperation::PsaRawKeyAgreement(op_raw_key_agreement) => { - let app_name = - unwrap_or_else_return!(app_name.ok_or(ResponseStatus::NotAuthenticated)); + let app = unwrap_or_else_return!(app.ok_or(ResponseStatus::NotAuthenticated)); let result = unwrap_or_else_return!(self .provider - .psa_raw_key_agreement(app_name, op_raw_key_agreement)); + .psa_raw_key_agreement(app.into(), op_raw_key_agreement)); trace!("psa_raw_key_agreement_egress"); self.result_to_response(NativeResult::PsaRawKeyAgreement(result), header) } diff --git a/src/back/dispatcher.rs b/src/back/dispatcher.rs index a672094d..2b6b18e7 100644 --- a/src/back/dispatcher.rs +++ b/src/back/dispatcher.rs @@ -5,7 +5,7 @@ //! The dispatcher's role is to direct requests to the provider they specify, if //! said provider is available on the system, thus acting as a multiplexer. use super::backend_handler::BackEndHandler; -use crate::authenticators::ApplicationName; +use crate::authenticators::Application; use log::trace; use parsec_interface::requests::request::Request; use parsec_interface::requests::ProviderID; @@ -32,18 +32,14 @@ impl Dispatcher { /// Returns either the response coming from the backend handler, or a response /// containing a status code consistent with the error encountered during /// processing. - pub fn dispatch_request( - &self, - request: Request, - app_name: Option, - ) -> Response { + pub fn dispatch_request(&self, request: Request, app: Option) -> Response { trace!("dispatch_request ingress"); if let Some(backend) = self.backends.get(&request.header.provider) { if let Err(status) = backend.is_capable(&request) { Response::from_request_header(request.header, status) } else { { - let response = backend.execute_request(request, app_name); + let response = backend.execute_request(request, app); trace!("execute_request egress"); response } diff --git a/src/front/front_end.rs b/src/front/front_end.rs index 65eb0689..7679f5b7 100644 --- a/src/front/front_end.rs +++ b/src/front/front_end.rs @@ -58,7 +58,7 @@ impl FrontEndHandler { }; // Check if the request was sent without authentication - let (app_name, err_response) = if AuthType::NoAuth == request.header.auth_type { + let (app, err_response) = if AuthType::NoAuth == request.header.auth_type { (None, None) // Otherwise find an authenticator that is capable to authenticate the request } else if let Some(authenticator) = self.authenticators.get(&request.header.auth_type) { @@ -66,7 +66,7 @@ impl FrontEndHandler { match authenticator.authenticate(&request.auth, connection.metadata) { // Send the request to the dispatcher // Get a response back - Ok(app_name) => (Some(app_name), None), + Ok(app) => (Some(app), None), Err(status) => ( None, Some(Response::from_request_header(request.header, status)), @@ -86,16 +86,16 @@ impl FrontEndHandler { err_response } else { if crate::utils::GlobalConfig::log_error_details() { - if let Some(app_name_string) = app_name.clone() { + if let Some(app) = &app.as_ref() { info!( "New request received from application name \"{}\"", - app_name_string + app.get_name() ) } else { info!("New request received without authentication") } }; - let response = self.dispatcher.dispatch_request(request, app_name.clone()); + let response = self.dispatcher.dispatch_request(request, app.clone()); trace!("dispatch_request egress"); response }; @@ -105,10 +105,10 @@ impl FrontEndHandler { match response.write_to_stream(&mut connection.stream) { Ok(_) => { if crate::utils::GlobalConfig::log_error_details() { - if let Some(app_name_string) = app_name { + if let Some(app) = app { info!( "Response for application name \"{}\" sent back", - app_name_string + app.get_name() ); } else { info!("Response sent back from request without authentication"); diff --git a/src/key_info_managers/mod.rs b/src/key_info_managers/mod.rs index c25360c5..5e098531 100644 --- a/src/key_info_managers/mod.rs +++ b/src/key_info_managers/mod.rs @@ -182,3 +182,24 @@ pub fn list_keys( Ok(keys) } + +/// Returns a Vec of ApplicationName of clients having keys in the provider. +/// +/// # Errors +/// +/// Returns an error as a String if there was a problem accessing the Key Info Manager. +pub fn list_clients( + manager: &dyn ManageKeyInfo, + provider_id: ProviderID, +) -> Result, String> { + let key_triples = manager.get_all(provider_id)?; + let mut clients = Vec::new(); + + for key_triple in key_triples { + if !clients.contains(key_triple.app_name()) { + let _ = clients.push(key_triple.app_name().clone()); + } + } + + Ok(clients) +} diff --git a/src/key_info_managers/on_disk_manager/mod.rs b/src/key_info_managers/on_disk_manager/mod.rs index 9dd9591c..8a332a9d 100644 --- a/src/key_info_managers/on_disk_manager/mod.rs +++ b/src/key_info_managers/on_disk_manager/mod.rs @@ -43,7 +43,7 @@ pub struct OnDiskKeyInfoManager { /// being a number from 0 and 255. fn key_triple_to_base64_filenames(key_triple: &KeyTriple) -> (String, String, String) { ( - base64::encode_config(key_triple.app_name.get_name().as_bytes(), base64::URL_SAFE), + base64::encode_config(key_triple.app_name.as_bytes(), base64::URL_SAFE), (key_triple.provider_id as u8).to_string(), base64::encode_config(key_triple.key_name.as_bytes(), base64::URL_SAFE), ) diff --git a/src/providers/core/mod.rs b/src/providers/core/mod.rs index f4901843..b5e8eb46 100644 --- a/src/providers/core/mod.rs +++ b/src/providers/core/mod.rs @@ -10,7 +10,8 @@ use crate::authenticators::ApplicationName; use derivative::Derivative; use log::trace; use parsec_interface::operations::{ - list_authenticators, list_keys, list_opcodes, list_providers, ping, + delete_client, list_authenticators, list_clients, list_keys, list_opcodes, list_providers, + ping, psa_destroy_key, }; use parsec_interface::operations::{ list_authenticators::AuthenticatorInfo, list_keys::KeyInfo, list_providers::ProviderInfo, @@ -93,6 +94,45 @@ impl Provide for Provider { Ok(list_keys::Result { keys }) } + fn list_clients(&self, _op: list_clients::Operation) -> Result { + trace!("list_clients ingress"); + + let mut clients: Vec = Vec::new(); + for provider in &self.prov_list { + let mut result = provider.list_clients(_op)?; + clients.append(&mut result.clients); + } + clients.sort(); + clients.dedup(); + + Ok(list_clients::Result { clients }) + } + + fn delete_client(&self, op: delete_client::Operation) -> Result { + trace!("delete_client ingress"); + + let client = op.client; + + for provider in &self.prov_list { + // Currently Parsec only stores keys, we delete all of them. + let keys = provider + .list_keys( + ApplicationName::from_name(client.clone()), + list_keys::Operation {}, + )? + .keys; + for key in keys { + let key_name = key.name; + let _ = provider.psa_destroy_key( + ApplicationName::from_name(client.clone()), + psa_destroy_key::Operation { key_name }, + )?; + } + } + + Ok(delete_client::Result {}) + } + fn ping(&self, _op: ping::Operation) -> Result { trace!("ping ingress"); let result = ping::Result { diff --git a/src/providers/cryptoauthlib/mod.rs b/src/providers/cryptoauthlib/mod.rs index 59e96eaf..b07d9ba1 100644 --- a/src/providers/cryptoauthlib/mod.rs +++ b/src/providers/cryptoauthlib/mod.rs @@ -8,9 +8,9 @@ use crate::authenticators::ApplicationName; use crate::key_info_managers::ManageKeyInfo; use derivative::Derivative; use log::trace; -use parsec_interface::operations::list_keys; use parsec_interface::operations::list_keys::KeyInfo; use parsec_interface::operations::list_providers::ProviderInfo; +use parsec_interface::operations::{list_clients, list_keys}; use parsec_interface::requests::{Opcode, ProviderID, ResponseStatus, Result}; use std::collections::HashSet; use std::io::{Error, ErrorKind}; @@ -58,6 +58,13 @@ impl Provide for Provider { Ok(list_keys::Result { keys }) } + + fn list_clients(&self, _op: list_clients::Operation) -> Result { + trace!("list_clients ingress"); + let clients: Vec = Vec::new(); + + Ok(list_clients::Result { clients }) + } } /// CryptoAuthentication Library Provider builder diff --git a/src/providers/mbed_crypto/mod.rs b/src/providers/mbed_crypto/mod.rs index 5c171f2d..1a06d273 100644 --- a/src/providers/mbed_crypto/mod.rs +++ b/src/providers/mbed_crypto/mod.rs @@ -8,7 +8,7 @@ use crate::authenticators::ApplicationName; use crate::key_info_managers::{self, KeyTriple, ManageKeyInfo}; use derivative::Derivative; use log::{error, trace}; -use parsec_interface::operations::{list_keys, list_providers::ProviderInfo}; +use parsec_interface::operations::{list_clients, list_keys, list_providers::ProviderInfo}; use parsec_interface::operations::{ psa_aead_decrypt, psa_aead_encrypt, psa_asymmetric_decrypt, psa_asymmetric_encrypt, psa_destroy_key, psa_export_key, psa_export_public_key, psa_generate_key, psa_generate_random, @@ -181,6 +181,20 @@ impl Provide for Provider { }) } + fn list_clients(&self, _op: list_clients::Operation) -> Result { + let store_handle = self.key_info_store.read().expect("Key store lock poisoned"); + Ok(list_clients::Result { + clients: key_info_managers::list_clients(store_handle.deref(), ProviderID::MbedCrypto) + .map_err(|e| { + format_error!("Error occurred when fetching key information", e); + ResponseStatus::KeyInfoManagerError + })? + .into_iter() + .map(|app_name| app_name.to_string()) + .collect(), + }) + } + fn psa_generate_key( &self, app_name: ApplicationName, diff --git a/src/providers/mod.rs b/src/providers/mod.rs index 1e6c314a..e3358bba 100644 --- a/src/providers/mod.rs +++ b/src/providers/mod.rs @@ -104,10 +104,11 @@ impl ProviderConfig { use crate::authenticators::ApplicationName; use parsec_interface::operations::{ - list_authenticators, list_keys, list_opcodes, list_providers, ping, psa_aead_decrypt, - psa_aead_encrypt, psa_asymmetric_decrypt, psa_asymmetric_encrypt, psa_destroy_key, - psa_export_key, psa_export_public_key, psa_generate_key, psa_generate_random, psa_hash_compare, - psa_hash_compute, psa_import_key, psa_raw_key_agreement, psa_sign_hash, psa_verify_hash, + delete_client, list_authenticators, list_clients, list_keys, list_opcodes, list_providers, + ping, psa_aead_decrypt, psa_aead_encrypt, psa_asymmetric_decrypt, psa_asymmetric_encrypt, + psa_destroy_key, psa_export_key, psa_export_public_key, psa_generate_key, psa_generate_random, + psa_hash_compare, psa_hash_compute, psa_import_key, psa_raw_key_agreement, psa_sign_hash, + psa_verify_hash, }; use parsec_interface::requests::{ResponseStatus, Result}; @@ -155,6 +156,18 @@ pub trait Provide { Err(ResponseStatus::PsaErrorNotSupported) } + /// Lists all clients currently having data in the service. + fn list_clients(&self, _op: list_clients::Operation) -> Result { + trace!("list_clients ingress"); + Err(ResponseStatus::PsaErrorNotSupported) + } + + /// Delete all data a client has in the service.. + fn delete_client(&self, _op: delete_client::Operation) -> Result { + trace!("delete_client ingress"); + Err(ResponseStatus::PsaErrorNotSupported) + } + /// Execute a Ping operation to get the wire protocol version major and minor information. /// /// # Errors diff --git a/src/providers/pkcs11/mod.rs b/src/providers/pkcs11/mod.rs index 942f43bc..54de87b5 100644 --- a/src/providers/pkcs11/mod.rs +++ b/src/providers/pkcs11/mod.rs @@ -9,7 +9,7 @@ use crate::authenticators::ApplicationName; use crate::key_info_managers::{self, KeyInfo, KeyTriple, ManageKeyInfo}; use derivative::Derivative; use log::{error, info, trace, warn}; -use parsec_interface::operations::{list_keys, list_providers::ProviderInfo}; +use parsec_interface::operations::{list_clients, list_keys, list_providers::ProviderInfo}; use parsec_interface::operations::{ psa_asymmetric_decrypt, psa_asymmetric_encrypt, psa_destroy_key, psa_export_public_key, psa_generate_key, psa_import_key, psa_sign_hash, psa_verify_hash, @@ -225,6 +225,20 @@ impl Provide for Provider { }) } + fn list_clients(&self, _op: list_clients::Operation) -> Result { + let store_handle = self.key_info_store.read().expect("Key store lock poisoned"); + Ok(list_clients::Result { + clients: key_info_managers::list_clients(store_handle.deref(), ProviderID::Pkcs11) + .map_err(|e| { + format_error!("Error occurred when fetching key information", e); + ResponseStatus::KeyInfoManagerError + })? + .into_iter() + .map(|app_name| app_name.to_string()) + .collect(), + }) + } + fn psa_generate_key( &self, app_name: ApplicationName, diff --git a/src/providers/tpm/mod.rs b/src/providers/tpm/mod.rs index 47352c81..8334586c 100644 --- a/src/providers/tpm/mod.rs +++ b/src/providers/tpm/mod.rs @@ -9,7 +9,7 @@ use crate::authenticators::ApplicationName; use crate::key_info_managers::{self, ManageKeyInfo}; use derivative::Derivative; use log::{info, trace}; -use parsec_interface::operations::{list_keys, list_providers::ProviderInfo}; +use parsec_interface::operations::{list_clients, list_keys, list_providers::ProviderInfo}; use parsec_interface::operations::{ psa_asymmetric_decrypt, psa_asymmetric_encrypt, psa_destroy_key, psa_export_public_key, psa_generate_key, psa_import_key, psa_sign_hash, psa_verify_hash, @@ -108,6 +108,20 @@ impl Provide for Provider { }) } + fn list_clients(&self, _op: list_clients::Operation) -> Result { + let store_handle = self.key_info_store.read().expect("Key store lock poisoned"); + Ok(list_clients::Result { + clients: key_info_managers::list_clients(store_handle.deref(), ProviderID::Tpm) + .map_err(|e| { + format_error!("Error occurred when fetching key information", e); + ResponseStatus::KeyInfoManagerError + })? + .into_iter() + .map(|app_name| app_name.to_string()) + .collect(), + }) + } + fn psa_generate_key( &self, app_name: ApplicationName,