• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020, The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! This crate provides some safe wrappers around the libselinux API. It is currently limited
16 //! to the API surface that Keystore 2.0 requires to perform permission checks against
17 //! the SEPolicy. Notably, it provides wrappers for:
18 //!  * getcon
19 //!  * selinux_check_access
20 //!  * selabel_lookup for the keystore2_key backend.
21 //!
22 //! And it provides an owning wrapper around context strings `Context`.
23 
24 // TODO(b/290018030): Remove this and add proper safety comments.
25 #![allow(clippy::undocumented_unsafe_blocks)]
26 
27 use anyhow::Context as AnyhowContext;
28 use anyhow::{anyhow, Result};
29 pub use selinux::pid_t;
30 use selinux::SELABEL_CTX_ANDROID_KEYSTORE2_KEY;
31 use selinux::SELINUX_CB_LOG;
32 use selinux_bindgen as selinux;
33 use std::ffi::{CStr, CString};
34 use std::fmt;
35 use std::io;
36 use std::marker::{Send, Sync};
37 pub use std::ops::Deref;
38 use std::os::raw::c_char;
39 use std::ptr;
40 use std::sync;
41 
42 static SELINUX_LOG_INIT: sync::Once = sync::Once::new();
43 
44 /// `selinux_check_access` is only thread safe if avc_init was called with lock callbacks.
45 /// However, avc_init is deprecated and not exported by androids version of libselinux.
46 /// `selinux_set_callbacks` does not allow setting lock callbacks. So the only option
47 /// that remains right now is to put a big lock around calls into libselinux.
48 /// TODO b/188079221 It should suffice to protect `selinux_check_access` but until we are
49 /// certain of that, we leave the extra locks in place
50 static LIB_SELINUX_LOCK: sync::Mutex<()> = sync::Mutex::new(());
51 
redirect_selinux_logs_to_logcat()52 fn redirect_selinux_logs_to_logcat() {
53     // `selinux_set_callback` assigns the static lifetime function pointer
54     // `selinux_log_callback` to a static lifetime variable.
55     let cb = selinux::selinux_callback { func_log: Some(selinux::selinux_log_callback) };
56     unsafe {
57         selinux::selinux_set_callback(SELINUX_CB_LOG as i32, cb);
58     }
59 }
60 
61 // This function must be called before any entry point into lib selinux.
62 // Or leave a comment reasoning why calling this macro is not necessary
63 // for a given entry point.
init_logger_once()64 fn init_logger_once() {
65     SELINUX_LOG_INIT.call_once(redirect_selinux_logs_to_logcat)
66 }
67 
68 /// Selinux Error code.
69 #[derive(thiserror::Error, Debug, PartialEq, Eq)]
70 pub enum Error {
71     /// Indicates that an access check yielded no access.
72     #[error("Permission Denied")]
73     PermissionDenied,
74     /// Indicates an unexpected system error. Nested string provides some details.
75     #[error("Selinux SystemError: {0}")]
76     SystemError(String),
77 }
78 
79 impl Error {
80     /// Constructs a `PermissionDenied` error.
perm() -> Self81     pub fn perm() -> Self {
82         Error::PermissionDenied
83     }
sys<T: Into<String>>(s: T) -> Self84     fn sys<T: Into<String>>(s: T) -> Self {
85         Error::SystemError(s.into())
86     }
87 }
88 
89 /// Context represents an SELinux context string. It can take ownership of a raw
90 /// s-string as allocated by `getcon` or `selabel_lookup`. In this case it uses
91 /// `freecon` to free the resources when dropped. In its second variant it stores
92 /// an `std::ffi::CString` that can be initialized from a Rust string slice.
93 #[derive(Debug)]
94 pub enum Context {
95     /// Wraps a raw context c-string as returned by libselinux.
96     Raw(*mut ::std::os::raw::c_char),
97     /// Stores a context string as `std::ffi::CString`.
98     CString(CString),
99 }
100 
101 impl PartialEq for Context {
eq(&self, other: &Self) -> bool102     fn eq(&self, other: &Self) -> bool {
103         // We dereference both and thereby delegate the comparison
104         // to `CStr`'s implementation of `PartialEq`.
105         **self == **other
106     }
107 }
108 
109 impl Eq for Context {}
110 
111 impl fmt::Display for Context {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result112     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113         write!(f, "{}", (**self).to_str().unwrap_or("Invalid context"))
114     }
115 }
116 
117 impl Drop for Context {
drop(&mut self)118     fn drop(&mut self) {
119         if let Self::Raw(p) = self {
120             // No need to initialize the logger here, because
121             // `freecon` cannot run unless `Backend::lookup` or `getcon`
122             // has run.
123             unsafe { selinux::freecon(*p) };
124         }
125     }
126 }
127 
128 impl Deref for Context {
129     type Target = CStr;
130 
deref(&self) -> &Self::Target131     fn deref(&self) -> &Self::Target {
132         match self {
133             Self::Raw(p) => unsafe { CStr::from_ptr(*p) },
134             Self::CString(cstr) => cstr,
135         }
136     }
137 }
138 
139 impl Context {
140     /// Initializes the `Context::CString` variant from a Rust string slice.
new(con: &str) -> Result<Self>141     pub fn new(con: &str) -> Result<Self> {
142         Ok(Self::CString(
143             CString::new(con)
144                 .with_context(|| format!("Failed to create Context with \"{}\"", con))?,
145         ))
146     }
147 }
148 
149 /// The backend trait provides a uniform interface to all libselinux context backends.
150 /// Currently, we only implement the KeystoreKeyBackend though.
151 pub trait Backend {
152     /// Implementers use libselinux `selabel_lookup` to lookup the context for the given `key`.
lookup(&self, key: &str) -> Result<Context>153     fn lookup(&self, key: &str) -> Result<Context>;
154 }
155 
156 /// Keystore key backend takes onwnership of the SELinux context handle returned by
157 /// `selinux_android_keystore2_key_context_handle` and uses `selabel_close` to free
158 /// the handle when dropped.
159 /// It implements `Backend` to provide keystore_key label lookup functionality.
160 pub struct KeystoreKeyBackend {
161     handle: *mut selinux::selabel_handle,
162 }
163 
164 // SAFETY: KeystoreKeyBackend is Sync because selabel_lookup is thread safe.
165 unsafe impl Sync for KeystoreKeyBackend {}
166 // SAFETY: KeystoreKeyBackend is Send because selabel_lookup is thread safe.
167 unsafe impl Send for KeystoreKeyBackend {}
168 
169 impl KeystoreKeyBackend {
170     const BACKEND_TYPE: i32 = SELABEL_CTX_ANDROID_KEYSTORE2_KEY as i32;
171 
172     /// Creates a new instance representing an SELinux context handle as returned by
173     /// `selinux_android_keystore2_key_context_handle`.
new() -> Result<Self>174     pub fn new() -> Result<Self> {
175         init_logger_once();
176         let _lock = LIB_SELINUX_LOCK.lock().unwrap();
177 
178         let handle = unsafe { selinux::selinux_android_keystore2_key_context_handle() };
179         if handle.is_null() {
180             return Err(anyhow!(Error::sys("Failed to open KeystoreKeyBackend")));
181         }
182         Ok(KeystoreKeyBackend { handle })
183     }
184 }
185 
186 impl Drop for KeystoreKeyBackend {
drop(&mut self)187     fn drop(&mut self) {
188         // No need to initialize the logger here because it cannot be called unless
189         // KeystoreKeyBackend::new has run.
190         unsafe { selinux::selabel_close(self.handle) };
191     }
192 }
193 
194 // Because KeystoreKeyBackend is Sync and Send, member function must never call
195 // non thread safe libselinux functions. As of this writing no non thread safe
196 // functions exist that could be called on a label backend handle.
197 impl Backend for KeystoreKeyBackend {
lookup(&self, key: &str) -> Result<Context>198     fn lookup(&self, key: &str) -> Result<Context> {
199         let mut con: *mut c_char = ptr::null_mut();
200         let c_key = CString::new(key).with_context(|| {
201             format!("selabel_lookup: Failed to convert key \"{}\" to CString.", key)
202         })?;
203         match unsafe {
204             // No need to initialize the logger here because it cannot run unless
205             // KeystoreKeyBackend::new has run.
206             let _lock = LIB_SELINUX_LOCK.lock().unwrap();
207 
208             selinux::selabel_lookup(self.handle, &mut con, c_key.as_ptr(), Self::BACKEND_TYPE)
209         } {
210             0 => {
211                 if !con.is_null() {
212                     Ok(Context::Raw(con))
213                 } else {
214                     Err(anyhow!(Error::sys(format!(
215                         "selabel_lookup returned a NULL context for key \"{}\"",
216                         key
217                     ))))
218                 }
219             }
220             _ => Err(anyhow!(io::Error::last_os_error()))
221                 .with_context(|| format!("selabel_lookup failed for key \"{}\"", key)),
222         }
223     }
224 }
225 
226 /// Safe wrapper around libselinux `getcon`. It initializes the `Context::Raw` variant of the
227 /// returned `Context`.
228 ///
229 /// ## Return
230 ///  * Ok(Context::Raw()) if successful.
231 ///  * Err(Error::sys()) if getcon succeeded but returned a NULL pointer.
232 ///  * Err(io::Error::last_os_error()) if getcon failed.
getcon() -> Result<Context>233 pub fn getcon() -> Result<Context> {
234     init_logger_once();
235     let _lock = LIB_SELINUX_LOCK.lock().unwrap();
236 
237     let mut con: *mut c_char = ptr::null_mut();
238     match unsafe { selinux::getcon(&mut con) } {
239         0 => {
240             if !con.is_null() {
241                 Ok(Context::Raw(con))
242             } else {
243                 Err(anyhow!(Error::sys("getcon returned a NULL context")))
244             }
245         }
246         _ => Err(anyhow!(io::Error::last_os_error())).context("getcon failed"),
247     }
248 }
249 
250 /// Safe wrapper around selinux_check_access.
251 ///
252 /// ## Return
253 ///  * Ok(()) iff the requested access was granted.
254 ///  * Err(anyhow!(Error::perm()))) if the permission was denied.
255 ///  * Err(anyhow!(ioError::last_os_error())) if any other error occurred while performing
256 ///            the access check.
check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()>257 pub fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
258     init_logger_once();
259 
260     let c_tclass = CString::new(tclass).with_context(|| {
261         format!("check_access: Failed to convert tclass \"{}\" to CString.", tclass)
262     })?;
263     let c_perm = CString::new(perm).with_context(|| {
264         format!("check_access: Failed to convert perm \"{}\" to CString.", perm)
265     })?;
266 
267     match unsafe {
268         let _lock = LIB_SELINUX_LOCK.lock().unwrap();
269 
270         selinux::selinux_check_access(
271             source.as_ptr(),
272             target.as_ptr(),
273             c_tclass.as_ptr(),
274             c_perm.as_ptr(),
275             ptr::null_mut(),
276         )
277     } {
278         0 => Ok(()),
279         _ => {
280             let e = io::Error::last_os_error();
281             match e.kind() {
282                 io::ErrorKind::PermissionDenied => Err(anyhow!(Error::perm())),
283                 _ => Err(anyhow!(e)),
284             }
285             .with_context(|| {
286                 format!(
287                     concat!(
288                         "check_access: Failed with sctx: {:?} tctx: {:?}",
289                         " with target class: \"{}\" perm: \"{}\""
290                     ),
291                     source, target, tclass, perm
292                 )
293             })
294         }
295     }
296 }
297 
298 /// Safe wrapper around setcon.
setcon(target: &CStr) -> std::io::Result<()>299 pub fn setcon(target: &CStr) -> std::io::Result<()> {
300     // SAFETY: `setcon` takes a const char* and only performs read accesses on it
301     // using strdup and strcmp. `setcon` does not retain a pointer to `target`
302     // and `target` outlives the call to `setcon`.
303     if unsafe { selinux::setcon(target.as_ptr()) } != 0 {
304         Err(std::io::Error::last_os_error())
305     } else {
306         Ok(())
307     }
308 }
309 
310 /// Represents an SEPolicy permission belonging to a specific class.
311 pub trait ClassPermission {
312     /// The permission string of the given instance as specified in the class vector.
name(&self) -> &'static str313     fn name(&self) -> &'static str;
314     /// The class of the permission.
class_name(&self) -> &'static str315     fn class_name(&self) -> &'static str;
316 }
317 
318 /// This macro implements an enum with values mapped to SELinux permission names.
319 /// The example below implements `enum MyPermission with public visibility:
320 ///  * From<i32> and Into<i32> are implemented. Where the implementation of From maps
321 ///    any variant not specified to the default `None` with value `0`.
322 ///  * `MyPermission` implements ClassPermission.
323 ///  * An implicit default values `MyPermission::None` is created with a numeric representation
324 ///    of `0` and a string representation of `"none"`.
325 ///  * Specifying a value is optional. If the value is omitted it is set to the value of the
326 ///    previous variant left shifted by 1.
327 ///
328 /// ## Example
329 /// ```
330 /// implement_class!(
331 ///     /// MyPermission documentation.
332 ///     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
333 ///     #[selinux(class_name = my_class)]
334 ///     pub enum MyPermission {
335 ///         #[selinux(name = foo)]
336 ///         Foo = 1,
337 ///         #[selinux(name = bar)]
338 ///         Bar = 2,
339 ///         #[selinux(name = snafu)]
340 ///         Snafu, // Implicit value: MyPermission::Bar << 1 -> 4
341 ///     }
342 ///     assert_eq!(MyPermission::Foo.name(), &"foo");
343 ///     assert_eq!(MyPermission::Foo.class_name(), &"my_class");
344 ///     assert_eq!(MyPermission::Snafu as i32, 4);
345 /// );
346 /// ```
347 #[macro_export]
348 macro_rules! implement_class {
349     // First rule: Public interface.
350     (
351         $(#[$($enum_meta:tt)+])*
352         $enum_vis:vis enum $enum_name:ident $body:tt
353     ) => {
354         implement_class! {
355             @extract_class
356             []
357             [$(#[$($enum_meta)+])*]
358             $enum_vis enum $enum_name $body
359         }
360     };
361 
362     // The next two rules extract the #[selinux(class_name = <name>)] meta field from
363     // the types meta list.
364     // This first rule finds the field and terminates the recursion through the meta fields.
365     (
366         @extract_class
367         [$(#[$mout:meta])*]
368         [
369             #[selinux(class_name = $class_name:ident)]
370             $(#[$($mtail:tt)+])*
371         ]
372         $enum_vis:vis enum $enum_name:ident {
373             $(
374                 $(#[$($emeta:tt)+])*
375                 $vname:ident$( = $vval:expr)?
376             ),* $(,)?
377         }
378     ) => {
379         implement_class!{
380             @extract_perm_name
381             $class_name
382             $(#[$mout])*
383             $(#[$($mtail)+])*
384             $enum_vis enum $enum_name {
385                 1;
386                 []
387                 [$(
388                     [] [$(#[$($emeta)+])*]
389                     $vname$( = $vval)?,
390                 )*]
391             }
392         }
393     };
394 
395     // The second rule iterates through the type global meta fields.
396     (
397         @extract_class
398         [$(#[$mout:meta])*]
399         [
400             #[$front:meta]
401             $(#[$($mtail:tt)+])*
402         ]
403         $enum_vis:vis enum $enum_name:ident $body:tt
404     ) => {
405         implement_class!{
406             @extract_class
407             [
408                 $(#[$mout])*
409                 #[$front]
410             ]
411             [$(#[$($mtail)+])*]
412             $enum_vis enum $enum_name $body
413         }
414     };
415 
416     // The next four rules implement two nested recursions. The outer iterates through
417     // the enum variants and the inner iterates through the meta fields of each variant.
418     // The first two rules find the #[selinux(name = <name>)] stanza, terminate the inner
419     // recursion and descend a level in the outer recursion.
420     // The first rule matches variants with explicit initializer $vval. And updates the next
421     // value to ($vval << 1).
422     (
423         @extract_perm_name
424         $class_name:ident
425         $(#[$enum_meta:meta])*
426         $enum_vis:vis enum $enum_name:ident {
427             $next_val:expr;
428             [$($out:tt)*]
429             [
430                 [$(#[$mout:meta])*]
431                 [
432                     #[selinux(name = $selinux_name:ident)]
433                     $(#[$($mtail:tt)+])*
434                 ]
435                 $vname:ident = $vval:expr,
436                 $($tail:tt)*
437             ]
438         }
439     ) => {
440         implement_class!{
441             @extract_perm_name
442             $class_name
443             $(#[$enum_meta])*
444             $enum_vis enum $enum_name {
445                 ($vval << 1);
446                 [
447                     $($out)*
448                     $(#[$mout])*
449                     $(#[$($mtail)+])*
450                     $selinux_name $vname = $vval,
451                 ]
452                 [$($tail)*]
453             }
454         }
455     };
456 
457     // The second rule differs form the previous in that there is no explicit initializer.
458     // Instead $next_val is used as initializer and the next value is set to (&next_val << 1).
459     (
460         @extract_perm_name
461         $class_name:ident
462         $(#[$enum_meta:meta])*
463         $enum_vis:vis enum $enum_name:ident {
464             $next_val:expr;
465             [$($out:tt)*]
466             [
467                 [$(#[$mout:meta])*]
468                 [
469                     #[selinux(name = $selinux_name:ident)]
470                     $(#[$($mtail:tt)+])*
471                 ]
472                 $vname:ident,
473                 $($tail:tt)*
474             ]
475         }
476     ) => {
477         implement_class!{
478             @extract_perm_name
479             $class_name
480             $(#[$enum_meta])*
481             $enum_vis enum $enum_name {
482                 ($next_val << 1);
483                 [
484                     $($out)*
485                     $(#[$mout])*
486                     $(#[$($mtail)+])*
487                     $selinux_name $vname = $next_val,
488                 ]
489                 [$($tail)*]
490             }
491         }
492     };
493 
494     // The third rule descends a step in the inner recursion.
495     (
496         @extract_perm_name
497         $class_name:ident
498         $(#[$enum_meta:meta])*
499         $enum_vis:vis enum $enum_name:ident {
500             $next_val:expr;
501             [$($out:tt)*]
502             [
503                 [$(#[$mout:meta])*]
504                 [
505                     #[$front:meta]
506                     $(#[$($mtail:tt)+])*
507                 ]
508                 $vname:ident$( = $vval:expr)?,
509                 $($tail:tt)*
510             ]
511         }
512     ) => {
513         implement_class!{
514             @extract_perm_name
515             $class_name
516             $(#[$enum_meta])*
517             $enum_vis enum $enum_name {
518                 $next_val;
519                 [$($out)*]
520                 [
521                     [
522                         $(#[$mout])*
523                         #[$front]
524                     ]
525                     [$(#[$($mtail)+])*]
526                     $vname$( = $vval)?,
527                     $($tail)*
528                 ]
529             }
530         }
531     };
532 
533     // The fourth rule terminates the outer recursion and transitions to the
534     // implementation phase @spill.
535     (
536         @extract_perm_name
537         $class_name:ident
538         $(#[$enum_meta:meta])*
539         $enum_vis:vis enum $enum_name:ident {
540             $next_val:expr;
541             [$($out:tt)*]
542             []
543         }
544     ) => {
545         implement_class!{
546             @spill
547             $class_name
548             $(#[$enum_meta])*
549             $enum_vis enum $enum_name {
550                 $($out)*
551             }
552         }
553     };
554 
555     (
556         @spill
557         $class_name:ident
558         $(#[$enum_meta:meta])*
559         $enum_vis:vis enum $enum_name:ident {
560             $(
561                 $(#[$emeta:meta])*
562                 $selinux_name:ident $vname:ident = $vval:expr,
563             )*
564         }
565     ) => {
566         $(#[$enum_meta])*
567         $enum_vis enum $enum_name {
568             /// The default variant of the enum.
569             None = 0,
570             $(
571                 $(#[$emeta])*
572                 $vname = $vval,
573             )*
574         }
575 
576         impl From<i32> for $enum_name {
577             #[allow(non_upper_case_globals)]
578             fn from (p: i32) -> Self {
579                 // Creating constants forces the compiler to evaluate the value expressions
580                 // so that they can be used in the match statement below.
581                 $(const $vname: i32 = $vval;)*
582                 match p {
583                     0 => Self::None,
584                     $($vname => Self::$vname,)*
585                     _ => Self::None,
586                 }
587             }
588         }
589 
590         impl From<$enum_name> for i32 {
591             fn from(p: $enum_name) -> i32 {
592                 p as i32
593             }
594         }
595 
596         impl ClassPermission for $enum_name {
597             fn name(&self) -> &'static str {
598                 match self {
599                     Self::None => &"none",
600                     $(Self::$vname => stringify!($selinux_name),)*
601                 }
602             }
603             fn class_name(&self) -> &'static str {
604                 stringify!($class_name)
605             }
606         }
607     };
608 }
609 
610 /// Calls `check_access` on the given class permission.
check_permission<T: ClassPermission>(source: &CStr, target: &CStr, perm: T) -> Result<()>611 pub fn check_permission<T: ClassPermission>(source: &CStr, target: &CStr, perm: T) -> Result<()> {
612     check_access(source, target, perm.class_name(), perm.name())
613 }
614 
615 #[cfg(test)]
616 mod tests {
617     use super::*;
618     use anyhow::Result;
619 
620     /// The su_key namespace as defined in su.te and keystore_key_contexts of the
621     /// SePolicy (system/sepolicy).
622     static SU_KEY_NAMESPACE: &str = "0";
623     /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
624     /// SePolicy (system/sepolicy).
625     static SHELL_KEY_NAMESPACE: &str = "1";
626 
check_context() -> Result<(Context, &'static str, bool)>627     fn check_context() -> Result<(Context, &'static str, bool)> {
628         let context = getcon()?;
629         match context.to_str().unwrap() {
630             "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
631             "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
632             c => Err(anyhow!(format!(
633                 "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
634                 c
635             ))),
636         }
637     }
638 
639     #[test]
test_getcon() -> Result<()>640     fn test_getcon() -> Result<()> {
641         check_context()?;
642         Ok(())
643     }
644 
645     #[test]
test_label_lookup() -> Result<()>646     fn test_label_lookup() -> Result<()> {
647         let (_context, namespace, is_su) = check_context()?;
648         let backend = crate::KeystoreKeyBackend::new()?;
649         let context = backend.lookup(namespace)?;
650         if is_su {
651             assert_eq!(context.to_str(), Ok("u:object_r:su_key:s0"));
652         } else {
653             assert_eq!(context.to_str(), Ok("u:object_r:shell_key:s0"));
654         }
655         Ok(())
656     }
657 
658     #[test]
context_from_string() -> Result<()>659     fn context_from_string() -> Result<()> {
660         let tctx = Context::new("u:object_r:keystore:s0").unwrap();
661         let sctx = Context::new("u:r:system_server:s0").unwrap();
662         check_access(&sctx, &tctx, "keystore2_key", "use")?;
663         Ok(())
664     }
665 
666     mod perm {
667         use super::super::*;
668         use super::*;
669         use anyhow::Result;
670 
671         /// check_key_perm(perm, privileged, priv_domain)
672         /// `perm` is a permission of the keystore2_key class and `privileged` is a boolean
673         /// indicating whether the permission is considered privileged.
674         /// Privileged permissions are expected to be denied to `shell` users but granted
675         /// to the given priv_domain.
676         macro_rules! check_key_perm {
677             // "use" is a keyword and cannot be used as an identifier, but we must keep
678             // the permission string intact. So we map the identifier name on use_ while using
679             // the permission string "use". In all other cases we can simply use the stringified
680             // identifier as permission string.
681             (use, $privileged:expr) => {
682                 check_key_perm!(use_, $privileged, "use");
683             };
684             ($perm:ident, $privileged:expr) => {
685                 check_key_perm!($perm, $privileged, stringify!($perm));
686             };
687             ($perm:ident, $privileged:expr, $p_str:expr) => {
688                 #[test]
689                 fn $perm() -> Result<()> {
690                     android_logger::init_once(
691                         android_logger::Config::default()
692                             .with_tag("keystore_selinux_tests")
693                             .with_max_level(log::LevelFilter::Debug),
694                     );
695                     let scontext = Context::new("u:r:shell:s0")?;
696                     let backend = KeystoreKeyBackend::new()?;
697                     let tcontext = backend.lookup(SHELL_KEY_NAMESPACE)?;
698 
699                     if $privileged {
700                         assert_eq!(
701                             Some(&Error::perm()),
702                             check_access(
703                                 &scontext,
704                                 &tcontext,
705                                 "keystore2_key",
706                                 $p_str
707                             )
708                             .err()
709                             .unwrap()
710                             .root_cause()
711                             .downcast_ref::<Error>()
712                         );
713                     } else {
714                         assert!(check_access(
715                             &scontext,
716                             &tcontext,
717                             "keystore2_key",
718                             $p_str
719                         )
720                         .is_ok());
721                     }
722                     Ok(())
723                 }
724             };
725         }
726 
727         check_key_perm!(manage_blob, true);
728         check_key_perm!(delete, false);
729         check_key_perm!(use_dev_id, true);
730         check_key_perm!(req_forced_op, true);
731         check_key_perm!(gen_unique_id, true);
732         check_key_perm!(grant, true);
733         check_key_perm!(get_info, false);
734         check_key_perm!(rebind, false);
735         check_key_perm!(update, false);
736         check_key_perm!(use, false);
737 
738         macro_rules! check_keystore_perm {
739             ($perm:ident) => {
740                 #[test]
741                 fn $perm() -> Result<()> {
742                     let ks_context = Context::new("u:object_r:keystore:s0")?;
743                     let priv_context = Context::new("u:r:system_server:s0")?;
744                     let unpriv_context = Context::new("u:r:shell:s0")?;
745                     assert!(check_access(
746                         &priv_context,
747                         &ks_context,
748                         "keystore2",
749                         stringify!($perm)
750                     )
751                     .is_ok());
752                     assert_eq!(
753                         Some(&Error::perm()),
754                         check_access(&unpriv_context, &ks_context, "keystore2", stringify!($perm))
755                             .err()
756                             .unwrap()
757                             .root_cause()
758                             .downcast_ref::<Error>()
759                     );
760                     Ok(())
761                 }
762             };
763         }
764 
765         check_keystore_perm!(add_auth);
766         check_keystore_perm!(clear_ns);
767         check_keystore_perm!(lock);
768         check_keystore_perm!(reset);
769         check_keystore_perm!(unlock);
770     }
771 }
772