Keystore 2.0: Refactor permissions. 3/5

* Add trait ClassPermission and fn check_permission. This binds
  together permission names and their class name.
* Rename implement_permission! to implement_class!.
* Add #[selinux(class_name = <name>)] stanza to the syntax of
  implement_class!.

Test: keystore2_test for regressions.
Bug: 203555519

This reverts commit b8fd77fba016c4c908d371d546a5d86aff4a78d7.

Change-Id: I6863269ea4af5a6d0b36cf17e0238c81bc713d48
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index aeb0e15..1d9ac12 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -333,14 +333,21 @@
     }
 }
 
+/// Represents an SEPolicy permission belonging to a specific class.
+pub trait ClassPermission {
+    /// The permission string of the given instance as specified in the class vector.
+    fn name(&self) -> &'static str;
+    /// The class of the permission.
+    fn class_name(&self) -> &'static str;
+}
+
 /// 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
+/// The example below implements `enum MyPermission with public visibility:
 ///  * From<i32> and Into<i32> are implemented. Where the implementation of From maps
-///    any variant not specified to the default.
+///    any variant not specified to the default `None` with value `0`.
 ///  * Every variant has a constructor with a name corresponding to its lower case SELinux string
 ///    representation.
-///  * `MyPermission::to_selinux(&self)` returns the SELinux string representation of the
-///    corresponding permission.
+///  * `MyPermission` implements ClassPermission.
 ///  * An implicit default values `MyPermission::None` is created with a numeric representation
 ///    of `0` and a string representation of `"none"`.
 ///  * Specifying a value is optional. If the value is omitted it is set to the value of the
@@ -348,35 +355,62 @@
 ///
 /// ## Example
 /// ```
-/// implement_permission!(
+/// implement_class!(
 ///     /// MyPermission documentation.
 ///     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+///     #[selinux(class_name = my_class)]
 ///     pub enum MyPermission {
 ///         #[selinux(name = foo)]
 ///         Foo = 1,
 ///         #[selinux(name = bar)]
 ///         Bar = 2,
 ///         #[selinux(name = snafu)]
-///         Snafu, // Implicit value: MyPermission::Bar << 1 = 4
+///         Snafu, // Implicit value: MyPermission::Bar << 1 -> 4
 ///     }
+///     assert_eq!(MyPermission::Foo.name(), &"foo");
+///     assert_eq!(MyPermission::Foo.class_name(), &"my_class");
+///     assert_eq!(MyPermission::Snafu as i32, 4);
 /// );
 /// ```
 #[macro_export]
-macro_rules! implement_permission {
+macro_rules! implement_class {
+    // First rule: Public interface.
     (
-        $(#[$enum_meta:meta])*
+        $(#[$($enum_meta:tt)+])*
+        $enum_vis:vis enum $enum_name:ident $body:tt
+    ) => {
+        implement_class! {
+            @extract_class
+            []
+            [$(#[$($enum_meta)+])*]
+            $enum_vis enum $enum_name $body
+        }
+    };
+
+    // The next two rules extract the #[selinux(class_name = <name>)] meta field from
+    // the types meta list.
+    // This first rule finds the field and terminates the recursion through the meta fields.
+    (
+        @extract_class
+        [$(#[$mout:meta])*]
+        [
+            #[selinux(class_name = $class_name:ident)]
+            $(#[$($mtail:tt)+])*
+        ]
         $enum_vis:vis enum $enum_name:ident {
             $(
                 $(#[$($emeta:tt)+])*
-                $vname:ident$( = $vval:tt)?
+                $vname:ident$( = $vval:expr)?
             ),* $(,)?
         }
     ) => {
-        implement_permission!{
-            @extract_attr
-            $(#[$enum_meta])*
+        implement_class!{
+            @extract_perm_name
+            $class_name
+            $(#[$mout])*
+            $(#[$($mtail)+])*
             $enum_vis enum $enum_name {
-                1
+                1;
                 []
                 [$(
                     [] [$(#[$($emeta)+])*]
@@ -386,11 +420,39 @@
         }
     };
 
+    // The second rule iterates through the type global meta fields.
     (
-        @extract_attr
+        @extract_class
+        [$(#[$mout:meta])*]
+        [
+            #[$front:meta]
+            $(#[$($mtail:tt)+])*
+        ]
+        $enum_vis:vis enum $enum_name:ident $body:tt
+    ) => {
+        implement_class!{
+            @extract_class
+            [
+                $(#[$mout])*
+                #[$front]
+            ]
+            [$(#[$($mtail)+])*]
+            $enum_vis enum $enum_name $body
+        }
+    };
+
+    // The next four rules implement two nested recursions. The outer iterates through
+    // the enum variants and the inner iterates through the meta fields of each variant.
+    // The first two rules find the #[selinux(name = <name>)] stanza, terminate the inner
+    // recursion and descend a level in the outer recursion.
+    // The first rule matches variants with explicit initializer $vval. And updates the next
+    // value to ($vval << 1).
+    (
+        @extract_perm_name
+        $class_name:ident
         $(#[$enum_meta:meta])*
         $enum_vis:vis enum $enum_name:ident {
-            $next_val:tt
+            $next_val:expr;
             [$($out:tt)*]
             [
                 [$(#[$mout:meta])*]
@@ -398,16 +460,17 @@
                     #[selinux(name = $selinux_name:ident)]
                     $(#[$($mtail:tt)+])*
                 ]
-                $vname:ident = $vval:tt,
+                $vname:ident = $vval:expr,
                 $($tail:tt)*
             ]
         }
     ) => {
-        implement_permission!{
-            @extract_attr
+        implement_class!{
+            @extract_perm_name
+            $class_name
             $(#[$enum_meta])*
             $enum_vis enum $enum_name {
-                ($vval << 1)
+                ($vval << 1);
                 [
                     $($out)*
                     $(#[$mout])*
@@ -419,11 +482,14 @@
         }
     };
 
+    // The second rule differs form the previous in that there is no explicit initializer.
+    // Instead $next_val is used as initializer and the next value is set to (&next_val << 1).
     (
-        @extract_attr
+        @extract_perm_name
+        $class_name:ident
         $(#[$enum_meta:meta])*
         $enum_vis:vis enum $enum_name:ident {
-            $next_val:tt
+            $next_val:expr;
             [$($out:tt)*]
             [
                 [$(#[$mout:meta])*]
@@ -436,11 +502,12 @@
             ]
         }
     ) => {
-        implement_permission!{
-            @extract_attr
+        implement_class!{
+            @extract_perm_name
+            $class_name
             $(#[$enum_meta])*
             $enum_vis enum $enum_name {
-                ($next_val << 1)
+                ($next_val << 1);
                 [
                     $($out)*
                     $(#[$mout])*
@@ -452,12 +519,13 @@
         }
     };
 
-
+    // The third rule descends a step in the inner recursion.
     (
-        @extract_attr
+        @extract_perm_name
+        $class_name:ident
         $(#[$enum_meta:meta])*
         $enum_vis:vis enum $enum_name:ident {
-            $next_val:tt
+            $next_val:expr;
             [$($out:tt)*]
             [
                 [$(#[$mout:meta])*]
@@ -465,16 +533,17 @@
                     #[$front:meta]
                     $(#[$($mtail:tt)+])*
                 ]
-                $vname:ident$( = $vval:tt)?,
+                $vname:ident$( = $vval:expr)?,
                 $($tail:tt)*
             ]
         }
     ) => {
-        implement_permission!{
-            @extract_attr
+        implement_class!{
+            @extract_perm_name
+            $class_name
             $(#[$enum_meta])*
             $enum_vis enum $enum_name {
-                $next_val
+                $next_val;
                 [$($out)*]
                 [
                     [
@@ -489,17 +558,21 @@
         }
     };
 
+    // The fourth rule terminates the outer recursion and transitions to the
+    // implementation phase @spill.
     (
-        @extract_attr
+        @extract_perm_name
+        $class_name:ident
         $(#[$enum_meta:meta])*
         $enum_vis:vis enum $enum_name:ident {
-            $next_val:tt
+            $next_val:expr;
             [$($out:tt)*]
             []
         }
     ) => {
-        implement_permission!{
+        implement_class!{
             @spill
+            $class_name
             $(#[$enum_meta])*
             $enum_vis enum $enum_name {
                 $($out)*
@@ -509,17 +582,18 @@
 
     (
         @spill
+        $class_name:ident
         $(#[$enum_meta:meta])*
         $enum_vis:vis enum $enum_name:ident {
             $(
                 $(#[$emeta:meta])*
-                $selinux_name:ident $vname:ident = $vval:tt,
+                $selinux_name:ident $vname:ident = $vval:expr,
             )*
         }
     ) => {
         $(#[$enum_meta])*
         $enum_vis enum $enum_name {
-            /// The default variant of an enum.
+            /// The default variant of the enum.
             None = 0,
             $(
                 $(#[$emeta])*
@@ -547,17 +621,19 @@
             }
         }
 
-        impl $enum_name {
-
-            /// Returns a string representation of the permission as required by
-            /// `selinux::check_access`.
-            pub fn to_selinux(self) -> &'static str {
+        impl ClassPermission for $enum_name {
+            fn name(&self) -> &'static str {
                 match self {
                     Self::None => &"none",
                     $(Self::$vname => stringify!($selinux_name),)*
                 }
             }
+            fn class_name(&self) -> &'static str {
+                stringify!($class_name)
+            }
+        }
 
+        impl $enum_name {
             /// Creates an instance representing a permission with the same name.
             pub const fn none() -> Self { Self::None }
             $(
@@ -568,6 +644,11 @@
     };
 }
 
+/// Calls `check_access` on the given class permission.
+pub fn check_permission<T: ClassPermission>(source: &CStr, target: &CStr, perm: T) -> Result<()> {
+    check_access(source, target, perm.class_name(), perm.name())
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index fad5636..89cbd5d 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -25,7 +25,7 @@
 use anyhow::Context as AnyhowContext;
 use keystore2_selinux as selinux;
 use lazy_static::lazy_static;
-use selinux::{implement_permission, Backend};
+use selinux::{implement_class, Backend, ClassPermission};
 use std::cmp::PartialEq;
 use std::convert::From;
 use std::ffi::CStr;
@@ -64,12 +64,12 @@
 ///    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
+///  * `MyPerm.name()(&self)` returns the SELinux string representation of the
 ///    represented permission.
 ///
 /// ## Special behavior
 /// If the keyword `use` appears as an selinux name `use_` is used as identifier for the
-/// constructor function (e.g. `MePerm::use_()`) but the string returned by `to_selinux` will
+/// constructor function (e.g. `MePerm::use_()`) but the string returned by `name()` will
 /// still be `"use"`.
 ///
 /// ## Example
@@ -149,17 +149,20 @@
             }
         }
 
-        impl $name {
-            /// Returns a string representation of the permission as required by
-            /// `selinux::check_access`.
-            pub fn to_selinux(self) -> &'static str {
+        impl ClassPermission for $name {
+            fn name(&self) -> &'static str {
                 match self {
                     Self($aidl_name::$def_name) => stringify!($def_selinux_name),
                     $(Self($aidl_name::$element_name) => stringify!($selinux_name),)*
                     _ => stringify!($def_selinux_name),
                 }
             }
+            fn class_name(&self) -> &'static str {
+                "keystore2_key"
+            }
+        }
 
+        impl $name {
             /// Creates an instance representing a permission with the same name.
             pub const fn $def_selinux_name() -> Self { Self($aidl_name::$def_name) }
             $(
@@ -182,8 +185,7 @@
     /// In this access check `KeyPerm::get_info().to_selinux()` would return the SELinux representation
     /// "info".
     /// ```
-    /// selinux::check_access(source_context, target_context, "keystore2_key",
-    ///                       KeyPerm::get_info().to_selinux());
+    /// selinux::check_permission(source_context, target_context, KeyPerm::get_info());
     /// ```
     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
     KeyPerm from KeyPermission with default (NONE, none) {
@@ -201,9 +203,10 @@
     }
 );
 
-implement_permission!(
+implement_class!(
     /// KeystorePerm provides a convenient abstraction from the SELinux class `keystore2`.
     /// Using the implement_permission macro we get the same features as `KeyPerm`.
+    #[selinux(class_name = keystore2)]
     #[derive(Clone, Copy, Debug, PartialEq)]
     pub enum KeystorePerm {
         /// Checked when a new auth token is installed.
@@ -363,14 +366,14 @@
     }
 }
 
-/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` may access
+/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` may access
 /// the given permision `perm` of the `keystore2` security class.
 pub fn check_keystore_permission(caller_ctx: &CStr, perm: KeystorePerm) -> anyhow::Result<()> {
     let target_context = getcon().context("check_keystore_permission: getcon failed.")?;
-    selinux::check_access(caller_ctx, &target_context, "keystore2", perm.to_selinux())
+    selinux::check_permission(caller_ctx, &target_context, perm)
 }
 
-/// Uses `selinux::check_access` to check if the given caller context `caller_cxt` has
+/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt` has
 /// all the permissions indicated in `access_vec` for the target domain indicated by the key
 /// descriptor `key` in the security class `keystore2_key`.
 ///
@@ -395,7 +398,7 @@
         _ => return Err(KsError::sys()).context(format!("Cannot grant {:?}.", key.domain)),
     };
 
-    selinux::check_access(caller_ctx, &target_context, "keystore2_key", "grant")
+    selinux::check_permission(caller_ctx, &target_context, KeyPerm::grant())
         .context("Grant permission is required when granting.")?;
 
     if access_vec.includes(KeyPerm::grant()) {
@@ -403,19 +406,16 @@
     }
 
     for p in access_vec.into_iter() {
-        selinux::check_access(caller_ctx, &target_context, "keystore2_key", p.to_selinux())
-            .context(format!(
-                concat!(
-                    "check_grant_permission: check_access failed. ",
-                    "The caller may have tried to grant a permission that they don't possess. {:?}"
-                ),
-                p
-            ))?
+        selinux::check_permission(caller_ctx, &target_context, p).context(format!(
+            "check_grant_permission: check_permission failed. \
+            The caller may have tried to grant a permission that they don't possess. {:?}",
+            p
+        ))?
     }
     Ok(())
 }
 
-/// Uses `selinux::check_access` to check if the given caller context `caller_cxt`
+/// Uses `selinux::check_permission` to check if the given caller context `caller_cxt`
 /// has the permissions indicated by `perm` for the target domain indicated by the key
 /// descriptor `key` in the security class `keystore2_key`.
 ///
@@ -425,7 +425,7 @@
 ///                      backend, and the result is used as target context.
 ///  * `Domain::BLOB` Same as SELinux but the "manage_blob" permission is always checked additionally
 ///                   to the one supplied in `perm`.
-///  * `Domain::GRANT` Does not use selinux::check_access. Instead the `access_vector`
+///  * `Domain::GRANT` Does not use selinux::check_permission. Instead the `access_vector`
 ///                    parameter is queried for permission, which must be supplied in this case.
 ///
 /// ## Return values.
@@ -469,7 +469,7 @@
             match access_vector {
                 Some(_) => {
                     return Err(selinux::Error::perm())
-                        .context(format!("\"{}\" not granted", perm.to_selinux()));
+                        .context(format!("\"{}\" not granted", perm.name()));
                 }
                 None => {
                     // If DOMAIN_GRANT was selected an access vector must be supplied.
@@ -490,12 +490,7 @@
                 .context("Domain::BLOB: Failed to lookup namespace.")?;
             // If DOMAIN_KEY_BLOB was specified, we check for the "manage_blob"
             // permission in addition to the requested permission.
-            selinux::check_access(
-                caller_ctx,
-                &tctx,
-                "keystore2_key",
-                KeyPerm::manage_blob().to_selinux(),
-            )?;
+            selinux::check_permission(caller_ctx, &tctx, KeyPerm::manage_blob())?;
 
             tctx
         }
@@ -505,7 +500,7 @@
         }
     };
 
-    selinux::check_access(caller_ctx, &target_context, "keystore2_key", perm.to_selinux())
+    selinux::check_permission(caller_ctx, &target_context, perm)
 }
 
 #[cfg(test)]
@@ -881,16 +876,16 @@
             KeyPerm::use_() // Test if the macro accepts missing comma at the end of the list.
         ];
         let mut i = v.into_iter();
-        assert_eq!(i.next().unwrap().to_selinux(), "delete");
-        assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
-        assert_eq!(i.next().unwrap().to_selinux(), "get_info");
-        assert_eq!(i.next().unwrap().to_selinux(), "grant");
-        assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
-        assert_eq!(i.next().unwrap().to_selinux(), "rebind");
-        assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
-        assert_eq!(i.next().unwrap().to_selinux(), "update");
-        assert_eq!(i.next().unwrap().to_selinux(), "use");
-        assert_eq!(i.next().unwrap().to_selinux(), "use_dev_id");
+        assert_eq!(i.next().unwrap().name(), "delete");
+        assert_eq!(i.next().unwrap().name(), "gen_unique_id");
+        assert_eq!(i.next().unwrap().name(), "get_info");
+        assert_eq!(i.next().unwrap().name(), "grant");
+        assert_eq!(i.next().unwrap().name(), "manage_blob");
+        assert_eq!(i.next().unwrap().name(), "rebind");
+        assert_eq!(i.next().unwrap().name(), "req_forced_op");
+        assert_eq!(i.next().unwrap().name(), "update");
+        assert_eq!(i.next().unwrap().name(), "use");
+        assert_eq!(i.next().unwrap().name(), "use_dev_id");
         assert_eq!(None, i.next());
     }
     #[test]
@@ -903,11 +898,11 @@
             KeyPerm::use_(), // Test if macro accepts the comma at the end of the list.
         ];
         let mut i = v.into_iter();
-        assert_eq!(i.next().unwrap().to_selinux(), "gen_unique_id");
-        assert_eq!(i.next().unwrap().to_selinux(), "manage_blob");
-        assert_eq!(i.next().unwrap().to_selinux(), "req_forced_op");
-        assert_eq!(i.next().unwrap().to_selinux(), "update");
-        assert_eq!(i.next().unwrap().to_selinux(), "use");
+        assert_eq!(i.next().unwrap().name(), "gen_unique_id");
+        assert_eq!(i.next().unwrap().name(), "manage_blob");
+        assert_eq!(i.next().unwrap().name(), "req_forced_op");
+        assert_eq!(i.next().unwrap().name(), "update");
+        assert_eq!(i.next().unwrap().name(), "use");
         assert_eq!(None, i.next());
     }
     #[test]