diff options
author | Janis Danisevskis <jdanis@google.com> | 2020-08-07 12:39:56 -0700 |
---|---|---|
committer | Janis Danisevskis <jdanis@google.com> | 2020-09-02 14:13:22 -0700 |
commit | 1b3a6e25da9a070338e40132aad5931aadffc99c (patch) | |
tree | 2ebb2c1827618ce59f1bd1448accc35e50a81b74 | |
parent | a2716509a1470f19fba4fc973c854caea291ffe5 (diff) | |
download | security-1b3a6e25da9a070338e40132aad5931aadffc99c.tar.gz |
Keystore 2.0 permissions.rs: Use generated aidl types.
Modify permissions.rs to use generated aidl types instead of
placeholders.
Test: keystore2_test
Change-Id: Ia6bcc9cff714a5839f0d2391c30c287e4e4cbadf
-rw-r--r-- | keystore2/Android.bp | 2 | ||||
-rw-r--r-- | keystore2/src/permission.rs | 324 |
2 files changed, 192 insertions, 134 deletions
diff --git a/keystore2/Android.bp b/keystore2/Android.bp index f1173e00..7016d066 100644 --- a/keystore2/Android.bp +++ b/keystore2/Android.bp @@ -18,6 +18,7 @@ rust_library { srcs: ["src/lib.rs"], rustlibs: [ + "libandroid_security_keystore2", "libanyhow", "libandroid_hardware_keymint", "libkeystore_aidl_generated", @@ -39,6 +40,7 @@ rust_test { auto_gen_config: true, rustlibs: [ "libandroid_logger", + "libandroid_security_keystore2", "libanyhow", "libandroid_hardware_keymint", "libkeystore_aidl_generated", diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs index 0db56dda..e5939c85 100644 --- a/keystore2/src/permission.rs +++ b/keystore2/src/permission.rs @@ -18,7 +18,9 @@ //! It also provides KeystorePerm and KeyPerm as convenience wrappers for the SELinux permission //! defined by keystore2 and keystore2_key respectively. -use keystore_aidl_generated as aidl; +use android_security_keystore2::aidl::android::security::keystore2::KeyPermission; + +use android_security_keystore2::aidl::android::security::keystore2::KeyDescriptor::KeyDescriptor; use std::cmp::PartialEq; use std::convert::From; @@ -49,17 +51,25 @@ fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Conte KEYSTORE2_KEY_LABEL_BACKEND.lookup(&namespace.to_string()) } -/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements -/// * `From<i32> for `MyPerm`, where each unknown numeric value is mapped to the given default, -/// here `None` -/// * `Into<MyPermission> for `MyPerm` -/// * `MyPerm::foo()` and `MyPerm::bar()` which construct MyPerm instances representing -/// `MyPermission::Foo` and `MyPermission::Bar` respectively. -/// * `MyPerm.to_selinux(&self)`, which returns the selinux string representation of the +/// ## Background +/// +/// AIDL enums are represented as constants of the form: +/// ``` +/// mod EnumName { +/// pub type EnumName = i32; +/// pub const Variant1: EnumName = <value1>; +/// pub const Variant2: EnumName = <value2>; +/// ... +/// } +///``` +/// This macro wraps the enum in a new type, e.g., `MyPerm` and maps each variant to an SELinux +/// permission while providing the following interface: +/// * From<EnumName> and Into<EnumName> are implemented. Where the implementation of From maps +/// any variant not specified to the default. +/// * Every variant has a constructor with a name corresponding to its lower case SELinux string +/// representation. +/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the /// represented permission. -/// * Tests in the given test namespace for each permision that check that the numeric -/// representations of MyPermission and MyPerm match. (TODO replace with static assert if -/// they become available.) /// /// ## Special behavior /// If the keyword `use` appears as an selinux name `use_` is used as identifier for the @@ -68,85 +78,78 @@ fn lookup_keystore2_key_context(namespace: i64) -> anyhow::Result<selinux::Conte /// /// ## Example /// ``` -/// #[i32] -/// enum MyPermission { -/// None = 0, -/// Foo = 1, -/// Bar = 2, -/// } /// /// implement_permission!( /// /// MyPerm documentation. /// #[derive(Clone, Copy, Debug, PartialEq)] -/// MyPermission as MyPerm with default (None = 0, none) -/// and test namespace my_perm_tests { -/// Foo = 1, selinux name: foo; -/// Bar = 2, selinux name: bar; +/// MyPerm from EnumName with default (None, none) {} +/// Variant1, selinux name: variant1; +/// Variant2, selinux name: variant1; /// } /// ); /// ``` -macro_rules! implement_permission { +macro_rules! implement_permission_aidl { // This rule provides the public interface of the macro. And starts the preprocessing // recursion (see below). - ($(#[$m:meta])* $t:ty as $name:ident with default ($($def:tt)*) - and test namespace $tn:ident { $($element:tt)* }) + ($(#[$m:meta])* $name:ident from $aidl_name:ident with default ($($def:tt)*) + { $($element:tt)* }) => { - implement_permission!(@replace_use $($m)*, $t, $name, $tn, ($($def)*), [] , $($element)*); + implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), [], + $($element)*); }; - // The following three rules recurse through the elements of the form - // `<enum variant> = <integer_literal>, selinux name: <selinux_name>;` + // `<enum variant>, selinux name: <selinux_name>;` // preprocessing the input. // The first rule terminates the recursion and passes the processed arguments to the final // rule that spills out the implementation. - (@replace_use $($m:meta)*, $t:ty, $name:ident, $tn:ident, ($($def:tt)*), [$($out:tt)*], ) => { - implement_permission!(@end $($m)*, $t, $name, $tn, ($($def)*) { $($out)* } ); + (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], ) => { + implement_permission_aidl!(@end $($m)*, $name, $aidl_name, ($($def)*) { $($out)* } ); }; // The second rule is triggered if the selinux name of an element is literally `use`. - // It produces the tuple `<enum variant> = <integer_literal>, use_, use;` + // It produces the tuple `<enum variant>, use_, use;` // and appends it to the out list. - (@replace_use $($m:meta)*, $t:ty, $name:ident, $tn:ident, ($($def:tt)*), [$($out:tt)*], - $e_name:ident = $e_val:expr, selinux name: use; $($element:tt)*) + (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], + $e_name:ident, selinux name: use; $($element:tt)*) => { - implement_permission!(@replace_use $($m)*, $t, $name, $tn, ($($def)*), - [$($out)* $e_name = $e_val, use_, use;], $($element)*); + implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), + [$($out)* $e_name, use_, use;], $($element)*); }; // The third rule is the default rule which replaces every input tuple with - // `<enum variant> = <integer_literal>, <selinux_name>, <selinux_name>;` + // `<enum variant>, <selinux_name>, <selinux_name>;` // and appends the result to the out list. - (@replace_use $($m:meta)*, $t:ty, $name:ident, $tn:ident, ($($def:tt)*), [$($out:tt)*], - $e_name:ident = $e_val:expr, selinux name: $e_str:ident; $($element:tt)*) + (@replace_use $($m:meta)*, $name:ident, $aidl_name:ident, ($($def:tt)*), [$($out:tt)*], + $e_name:ident, selinux name: $e_str:ident; $($element:tt)*) => { - implement_permission!(@replace_use $($m)*, $t, $name, $tn, ($($def)*), - [$($out)* $e_name = $e_val, $e_str, $e_str;], $($element)*); + implement_permission_aidl!(@replace_use $($m)*, $name, $aidl_name, ($($def)*), + [$($out)* $e_name, $e_str, $e_str;], $($element)*); }; - (@end $($m:meta)*, $t:ty, $name:ident, $tn:ident, - ($def_name:ident = $def:expr, $def_selinux_name:ident) { - $($element_name:ident = $element_val:expr, $element_identifier:ident, + (@end $($m:meta)*, $name:ident, $aidl_name:ident, + ($def_name:ident, $def_selinux_name:ident) { + $($element_name:ident, $element_identifier:ident, $selinux_name:ident;)* }) => { $(#[$m])* - pub struct $name($t); + pub struct $name(pub $aidl_name::$aidl_name); - impl From<i32> for $name { - fn from (p: i32) -> Self { + impl From<$aidl_name::$aidl_name> for $name { + fn from (p: $aidl_name::$aidl_name) -> Self { match p { - $def => Self(<$t>::$def_name), - $($element_val => Self(<$t>::$element_name),)* - _ => Self(<$t>::$def_name), + $aidl_name::$def_name => Self($aidl_name::$def_name), + $($aidl_name::$element_name => Self($aidl_name::$element_name),)* + _ => Self($aidl_name::$def_name), } } } - impl Into<$t> for $name { - fn into(self) -> $t { + impl Into<$aidl_name::$aidl_name> for $name { + fn into(self) -> $aidl_name::$aidl_name { self.0 } } @@ -156,39 +159,23 @@ macro_rules! implement_permission { /// `selinux::check_access`. pub fn to_selinux(&self) -> &'static str { match self { - Self(<$t>::$def_name) => stringify!($def_selinux_name), - $(Self(<$t>::$element_name) => stringify!($selinux_name),)* + Self($aidl_name::$def_name) => stringify!($def_selinux_name), + $(Self($aidl_name::$element_name) => stringify!($selinux_name),)* + _ => stringify!($def_selinux_name), } } /// Creates an instance representing a permission with the same name. - pub const fn $def_selinux_name() -> Self { Self(<$t>::$def_name) } + pub const fn $def_selinux_name() -> Self { Self($aidl_name::$def_name) } $( /// Creates an instance representing a permission with the same name. - pub const fn $element_identifier() -> Self { Self(<$t>::$element_name) } - )* - } - #[cfg(test)] - mod $tn { - use super::*; - - #[test] - fn $def_selinux_name() { - assert_eq!($name::$def_selinux_name(), (<$t>::$def_name as i32).into()); - } - $( - #[test] - fn $element_identifier() { - assert_eq!($name::$element_identifier(), (<$t>::$element_name as i32).into()); - } + pub const fn $element_identifier() -> Self { Self($aidl_name::$element_name) } )* } }; - - } -implement_permission!( +implement_permission_aidl!( /// KeyPerm provides a convenient abstraction from the SELinux class `keystore2_key`. /// At the same time it maps `KeyPermissions` from the Keystore 2.0 AIDL Grant interface to /// the SELinux permissions. With the implement_permission macro, we conveniently @@ -204,56 +191,113 @@ implement_permission!( /// KeyPerm::get_info().to_selinux()); /// ``` #[derive(Clone, Copy, Debug, Eq, PartialEq)] - aidl::KeyPermission as KeyPerm with default (None = 0, none) - and test namespace key_perm_tests { - Delete = 1, selinux name: delete; - GenUniqueId = 2, selinux name: gen_unique_id; - GetInfo = 4, selinux name: get_info; - Grant = 8, selinux name: grant; - List = 0x10, selinux name: list; - ManageBlob = 0x20, selinux name: manage_blob; - Rebind = 0x40, selinux name: rebind; - ReqForcedOp = 0x80, selinux name: req_forced_op; - Update = 0x100, selinux name: update; - Use = 0x200, selinux name: use; - UseDevId = 0x400, selinux name: use_dev_id; + KeyPerm from KeyPermission with default (None, none) { + Delete, selinux name: delete; + GenUniqueId, selinux name: gen_unique_id; + GetInfo, selinux name: get_info; + Grant, selinux name: grant; + List, selinux name: list; + ManageBlob, selinux name: manage_blob; + Rebind, selinux name: rebind; + ReqForcedOp, selinux name: req_forced_op; + Update, selinux name: update; + Use, selinux name: use; + UseDevId, selinux name: use_dev_id; } ); -/// KeystorePermission defines values for the SELinux `keystore2` security class. -/// Countrary to `KeyPermission`, this enum is not generated by AIDL and need not be -/// wrapped by newtype pattern. But we conveniently use the implement_permission macro -/// to provide the same feature that we did for `KeyPermission` to this set of permissions. -#[repr(i32)] -#[derive(Debug, Clone, Copy, Eq, PartialEq)] -pub enum KeystorePermission { - /// `None` is not a permission that can ever be granted. It is not known to the SEPolicy. - None = 0, - /// Checked when a new auth token is installed. - AddAuth = 1, - /// Checked when an app is uninstalled or wiped. - ClearNs = 2, - /// Checked when the locked state of Keystore 2.0 is queried. - GetState = 4, - /// Checked when Keystore 2.0 gets locked. - Lock = 8, - /// Checked when Keystore 2.0 shall be reset. - Reset = 0x10, - /// Checked when Keystore 2.0 shall be unlocked. - Unlock = 0x20, +/// This macro implements an enum with values mapped to SELinux permission names. +/// The below example wraps the enum MyPermission in the tuple struct `MyPerm` and implements +/// * From<i32> and Into<i32> are implemented. Where the implementation of From maps +/// any variant not specified to the default. +/// * Every variant has a constructor with a name corresponding to its lower case SELinux string +/// representation. +/// * `MyPerm.to_selinux(&self)` returns the SELinux string representation of the +/// represented permission. +/// +/// ## Example +/// ``` +/// implement_permission!( +/// /// MyPerm documentation. +/// #[derive(Clone, Copy, Debug, Eq, PartialEq)] +/// MyPerm with default (None = 0, none) { +/// Foo = 1, selinux name: foo; +/// Bar = 2, selinux name: bar; +/// } +/// ); +/// ``` +macro_rules! implement_permission { + // This rule provides the public interface of the macro. And starts the preprocessing + // recursion (see below). + ($(#[$m:meta])* $name:ident with default + ($def_name:ident = $def_val:expr, $def_selinux_name:ident) + { + $($(#[$element_meta:meta])* + $element_name:ident = $element_val:expr, selinux name: $selinux_name:ident;)* + }) + => { + $(#[$m])* + pub enum $name { + /// The default variant of an enum. + $def_name = $def_val, + $( + $(#[$element_meta])* + $element_name = $element_val, + )* + } + + impl From<i32> for $name { + fn from (p: i32) -> Self { + match p { + $def_val => Self::$def_name, + $($element_val => Self::$element_name,)* + _ => Self::$def_name, + } + } + } + + impl Into<i32> for $name { + fn into(self) -> i32 { + self as i32 + } + } + + impl $name { + /// Returns a string representation of the permission as required by + /// `selinux::check_access`. + pub fn to_selinux(&self) -> &'static str { + match self { + Self::$def_name => stringify!($def_selinux_name), + $(Self::$element_name => stringify!($selinux_name),)* + } + } + + /// Creates an instance representing a permission with the same name. + pub const fn $def_selinux_name() -> Self { Self::$def_name } + $( + /// Creates an instance representing a permission with the same name. + pub const fn $selinux_name() -> Self { Self::$element_name } + )* + } + }; } implement_permission!( /// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`. /// Using the implement_permission macro we get the same features as `KeyPerm`. #[derive(Clone, Copy, Debug, PartialEq)] - KeystorePermission as KeystorePerm with default (None = 0, none) - and test namespace keystore_perm_tests { + KeystorePerm with default (None = 0, none) { + /// Checked when a new auth token is installed. AddAuth = 1, selinux name: add_auth; + /// Checked when an app is uninstalled or wiped. ClearNs = 2, selinux name: clear_ns; + /// Checked when Keystore 2.0 gets locked. GetState = 4, selinux name: get_state; + /// Checked when Keystore 2.0 gets locked. Lock = 8, selinux name: lock; + /// Checked when Keystore 2.0 shall be reset. Reset = 0x10, selinux name: reset; + /// Checked when Keystore 2.0 shall be unlocked. Unlock = 0x20, selinux name: unlock; } ); @@ -281,8 +325,8 @@ implement_permission!( /// assert_eq(Some(KeyPerm::use_()), i.next()); /// assert_eq(None, i.next()); /// ``` -#[derive(Copy, Clone)] -pub struct KeyPermSet(i32); +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd)] +pub struct KeyPermSet(pub i32); mod perm { use super::*; @@ -322,9 +366,22 @@ impl From<KeyPerm> for KeyPermSet { } } +/// allow conversion from the AIDL wire type i32 to a permission set. +impl From<i32> for KeyPermSet { + fn from(p: i32) -> Self { + Self(p) + } +} + +impl From<KeyPermSet> for i32 { + fn from(p: KeyPermSet) -> i32 { + p.0 + } +} + impl KeyPermSet { /// Returns true iff this permission set has all of the permissions that are in `other`. - fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool { + pub fn includes<T: Into<KeyPermSet>>(&self, other: T) -> bool { let o: KeyPermSet = other.into(); (self.0 & o.0) == o.0 } @@ -379,9 +436,9 @@ pub fn check_keystore_permission( pub fn check_grant_permission( caller_ctx: &selinux::Context, access_vec: KeyPermSet, - key: &aidl::KeyDescriptor, + key: &KeyDescriptor, ) -> anyhow::Result<()> { - use aidl::Domain; + use android_security_keystore2::aidl::android::security::keystore2::Domain; let target_context = match key.domain { Domain::App => getcon().context("check_grant_permission: getcon failed.")?, @@ -429,10 +486,10 @@ pub fn check_grant_permission( pub fn check_key_permission( caller_ctx: &selinux::Context, perm: KeyPerm, - key: &aidl::KeyDescriptor, + key: &KeyDescriptor, access_vector: &Option<KeyPermSet>, ) -> anyhow::Result<()> { - use aidl::Domain; + use android_security_keystore2::aidl::android::security::keystore2::Domain; let target_context = match key.domain { // apps get the default keystore context @@ -477,6 +534,10 @@ pub fn check_key_permission( tctx } + _ => { + return Err(KsError::sys()) + .context(format!("Unknown domain value: \"{}\".", key.domain)) + } }; selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux()) @@ -488,7 +549,6 @@ mod tests { use anyhow::anyhow; use anyhow::Result; use keystore2_selinux::*; - use keystore_aidl_generated as aidl; const ALL_PERMS: KeyPermSet = key_perm_set![ KeyPerm::manage_blob(), @@ -591,9 +651,8 @@ mod tests { fn check_grant_permission_app() -> Result<()> { let system_server_ctx = Context::new("u:r:system_server:s0")?; let shell_ctx = Context::new("u:r:shell:s0")?; - use aidl::Domain; - let key = - aidl::KeyDescriptor { domain: Domain::App, namespace_: 0, alias: None, blob: None }; + use android_security_keystore2::aidl::android::security::keystore2::Domain; + let key = KeyDescriptor { domain: Domain::App, namespace_: 0, alias: None, blob: None }; assert!(check_grant_permission(&system_server_ctx, NOT_GRANT_PERMS, &key).is_ok()); // attempts to grant the grant permission must always fail even when privileged. @@ -609,9 +668,9 @@ mod tests { #[test] fn check_grant_permission_selinux() -> Result<()> { - use aidl::Domain; + use android_security_keystore2::aidl::android::security::keystore2::Domain; let (sctx, namespace, is_su) = check_context()?; - let key = aidl::KeyDescriptor { + let key = KeyDescriptor { domain: Domain::SELinux, namespace_: namespace as i64, alias: None, @@ -630,9 +689,8 @@ mod tests { #[test] fn check_key_permission_domain_grant() -> Result<()> { - use aidl::Domain; - let key = - aidl::KeyDescriptor { domain: Domain::Grant, namespace_: 0, alias: None, blob: None }; + use android_security_keystore2::aidl::android::security::keystore2::Domain; + let key = KeyDescriptor { domain: Domain::Grant, namespace_: 0, alias: None, blob: None }; assert_perm_failed!(check_key_permission( &selinux::Context::new("ignored").unwrap(), @@ -654,10 +712,9 @@ mod tests { let system_server_ctx = Context::new("u:r:system_server:s0")?; let shell_ctx = Context::new("u:r:shell:s0")?; let gmscore_app = Context::new("u:r:gmscore_app:s0")?; - use aidl::Domain; + use android_security_keystore2::aidl::android::security::keystore2::Domain; - let key = - aidl::KeyDescriptor { domain: Domain::App, namespace_: 0, alias: None, blob: None }; + let key = KeyDescriptor { domain: Domain::App, namespace_: 0, alias: None, blob: None }; assert!(check_key_permission(&system_server_ctx, KeyPerm::use_(), &key, &None).is_ok()); assert!(check_key_permission(&system_server_ctx, KeyPerm::delete(), &key, &None).is_ok()); @@ -698,9 +755,9 @@ mod tests { #[test] fn check_key_permission_domain_selinux() -> Result<()> { - use aidl::Domain; + use android_security_keystore2::aidl::android::security::keystore2::Domain; let (sctx, namespace, is_su) = check_context()?; - let key = aidl::KeyDescriptor { + let key = KeyDescriptor { domain: Domain::SELinux, namespace_: namespace as i64, alias: None, @@ -737,9 +794,9 @@ mod tests { #[test] fn check_key_permission_domain_blob() -> Result<()> { - use aidl::Domain; + use android_security_keystore2::aidl::android::security::keystore2::Domain; let (sctx, namespace, is_su) = check_context()?; - let key = aidl::KeyDescriptor { + let key = KeyDescriptor { domain: Domain::Blob, namespace_: namespace as i64, alias: None, @@ -756,9 +813,8 @@ mod tests { #[test] fn check_key_permission_domain_key_id() -> Result<()> { - use aidl::Domain; - let key = - aidl::KeyDescriptor { domain: Domain::KeyId, namespace_: 0, alias: None, blob: None }; + use android_security_keystore2::aidl::android::security::keystore2::Domain; + let key = KeyDescriptor { domain: Domain::KeyId, namespace_: 0, alias: None, blob: None }; assert_eq!( Some(&KsError::sys()), |