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,