xref: /aosp_15_r20/system/security/keystore2/selinux/src/lib.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
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 libselinux `getpidcon`. It initializes the `Context::Raw` variant of the
251 /// returned `Context`.
252 ///
253 /// ## Return
254 ///  * Ok(Context::Raw()) if successful.
255 ///  * Err(Error::sys()) if getpidcon succeeded but returned a NULL pointer.
256 ///  * Err(io::Error::last_os_error()) if getpidcon failed.
getpidcon(pid: selinux::pid_t) -> Result<Context>257 pub fn getpidcon(pid: selinux::pid_t) -> Result<Context> {
258     init_logger_once();
259     let _lock = LIB_SELINUX_LOCK.lock().unwrap();
260 
261     let mut con: *mut c_char = ptr::null_mut();
262     match unsafe { selinux::getpidcon(pid, &mut con) } {
263         0 => {
264             if !con.is_null() {
265                 Ok(Context::Raw(con))
266             } else {
267                 Err(anyhow!(Error::sys(format!(
268                     "getpidcon returned a NULL context for pid {}",
269                     pid
270                 ))))
271             }
272         }
273         _ => Err(anyhow!(io::Error::last_os_error()))
274             .context(format!("getpidcon failed for pid {}", pid)),
275     }
276 }
277 
278 /// Safe wrapper around selinux_check_access.
279 ///
280 /// ## Return
281 ///  * Ok(()) iff the requested access was granted.
282 ///  * Err(anyhow!(Error::perm()))) if the permission was denied.
283 ///  * Err(anyhow!(ioError::last_os_error())) if any other error occurred while performing
284 ///            the access check.
check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()>285 pub fn check_access(source: &CStr, target: &CStr, tclass: &str, perm: &str) -> Result<()> {
286     init_logger_once();
287 
288     let c_tclass = CString::new(tclass).with_context(|| {
289         format!("check_access: Failed to convert tclass \"{}\" to CString.", tclass)
290     })?;
291     let c_perm = CString::new(perm).with_context(|| {
292         format!("check_access: Failed to convert perm \"{}\" to CString.", perm)
293     })?;
294 
295     match unsafe {
296         let _lock = LIB_SELINUX_LOCK.lock().unwrap();
297 
298         selinux::selinux_check_access(
299             source.as_ptr(),
300             target.as_ptr(),
301             c_tclass.as_ptr(),
302             c_perm.as_ptr(),
303             ptr::null_mut(),
304         )
305     } {
306         0 => Ok(()),
307         _ => {
308             let e = io::Error::last_os_error();
309             match e.kind() {
310                 io::ErrorKind::PermissionDenied => Err(anyhow!(Error::perm())),
311                 _ => Err(anyhow!(e)),
312             }
313             .with_context(|| {
314                 format!(
315                     concat!(
316                         "check_access: Failed with sctx: {:?} tctx: {:?}",
317                         " with target class: \"{}\" perm: \"{}\""
318                     ),
319                     source, target, tclass, perm
320                 )
321             })
322         }
323     }
324 }
325 
326 /// Safe wrapper around setcon.
setcon(target: &CStr) -> std::io::Result<()>327 pub fn setcon(target: &CStr) -> std::io::Result<()> {
328     // SAFETY: `setcon` takes a const char* and only performs read accesses on it
329     // using strdup and strcmp. `setcon` does not retain a pointer to `target`
330     // and `target` outlives the call to `setcon`.
331     if unsafe { selinux::setcon(target.as_ptr()) } != 0 {
332         Err(std::io::Error::last_os_error())
333     } else {
334         Ok(())
335     }
336 }
337 
338 /// Represents an SEPolicy permission belonging to a specific class.
339 pub trait ClassPermission {
340     /// The permission string of the given instance as specified in the class vector.
name(&self) -> &'static str341     fn name(&self) -> &'static str;
342     /// The class of the permission.
class_name(&self) -> &'static str343     fn class_name(&self) -> &'static str;
344 }
345 
346 /// This macro implements an enum with values mapped to SELinux permission names.
347 /// The example below implements `enum MyPermission with public visibility:
348 ///  * From<i32> and Into<i32> are implemented. Where the implementation of From maps
349 ///    any variant not specified to the default `None` with value `0`.
350 ///  * `MyPermission` implements ClassPermission.
351 ///  * An implicit default values `MyPermission::None` is created with a numeric representation
352 ///    of `0` and a string representation of `"none"`.
353 ///  * Specifying a value is optional. If the value is omitted it is set to the value of the
354 ///    previous variant left shifted by 1.
355 ///
356 /// ## Example
357 /// ```
358 /// implement_class!(
359 ///     /// MyPermission documentation.
360 ///     #[derive(Clone, Copy, Debug, Eq, PartialEq)]
361 ///     #[selinux(class_name = my_class)]
362 ///     pub enum MyPermission {
363 ///         #[selinux(name = foo)]
364 ///         Foo = 1,
365 ///         #[selinux(name = bar)]
366 ///         Bar = 2,
367 ///         #[selinux(name = snafu)]
368 ///         Snafu, // Implicit value: MyPermission::Bar << 1 -> 4
369 ///     }
370 ///     assert_eq!(MyPermission::Foo.name(), &"foo");
371 ///     assert_eq!(MyPermission::Foo.class_name(), &"my_class");
372 ///     assert_eq!(MyPermission::Snafu as i32, 4);
373 /// );
374 /// ```
375 #[macro_export]
376 macro_rules! implement_class {
377     // First rule: Public interface.
378     (
379         $(#[$($enum_meta:tt)+])*
380         $enum_vis:vis enum $enum_name:ident $body:tt
381     ) => {
382         implement_class! {
383             @extract_class
384             []
385             [$(#[$($enum_meta)+])*]
386             $enum_vis enum $enum_name $body
387         }
388     };
389 
390     // The next two rules extract the #[selinux(class_name = <name>)] meta field from
391     // the types meta list.
392     // This first rule finds the field and terminates the recursion through the meta fields.
393     (
394         @extract_class
395         [$(#[$mout:meta])*]
396         [
397             #[selinux(class_name = $class_name:ident)]
398             $(#[$($mtail:tt)+])*
399         ]
400         $enum_vis:vis enum $enum_name:ident {
401             $(
402                 $(#[$($emeta:tt)+])*
403                 $vname:ident$( = $vval:expr)?
404             ),* $(,)?
405         }
406     ) => {
407         implement_class!{
408             @extract_perm_name
409             $class_name
410             $(#[$mout])*
411             $(#[$($mtail)+])*
412             $enum_vis enum $enum_name {
413                 1;
414                 []
415                 [$(
416                     [] [$(#[$($emeta)+])*]
417                     $vname$( = $vval)?,
418                 )*]
419             }
420         }
421     };
422 
423     // The second rule iterates through the type global meta fields.
424     (
425         @extract_class
426         [$(#[$mout:meta])*]
427         [
428             #[$front:meta]
429             $(#[$($mtail:tt)+])*
430         ]
431         $enum_vis:vis enum $enum_name:ident $body:tt
432     ) => {
433         implement_class!{
434             @extract_class
435             [
436                 $(#[$mout])*
437                 #[$front]
438             ]
439             [$(#[$($mtail)+])*]
440             $enum_vis enum $enum_name $body
441         }
442     };
443 
444     // The next four rules implement two nested recursions. The outer iterates through
445     // the enum variants and the inner iterates through the meta fields of each variant.
446     // The first two rules find the #[selinux(name = <name>)] stanza, terminate the inner
447     // recursion and descend a level in the outer recursion.
448     // The first rule matches variants with explicit initializer $vval. And updates the next
449     // value to ($vval << 1).
450     (
451         @extract_perm_name
452         $class_name:ident
453         $(#[$enum_meta:meta])*
454         $enum_vis:vis enum $enum_name:ident {
455             $next_val:expr;
456             [$($out:tt)*]
457             [
458                 [$(#[$mout:meta])*]
459                 [
460                     #[selinux(name = $selinux_name:ident)]
461                     $(#[$($mtail:tt)+])*
462                 ]
463                 $vname:ident = $vval:expr,
464                 $($tail:tt)*
465             ]
466         }
467     ) => {
468         implement_class!{
469             @extract_perm_name
470             $class_name
471             $(#[$enum_meta])*
472             $enum_vis enum $enum_name {
473                 ($vval << 1);
474                 [
475                     $($out)*
476                     $(#[$mout])*
477                     $(#[$($mtail)+])*
478                     $selinux_name $vname = $vval,
479                 ]
480                 [$($tail)*]
481             }
482         }
483     };
484 
485     // The second rule differs form the previous in that there is no explicit initializer.
486     // Instead $next_val is used as initializer and the next value is set to (&next_val << 1).
487     (
488         @extract_perm_name
489         $class_name:ident
490         $(#[$enum_meta:meta])*
491         $enum_vis:vis enum $enum_name:ident {
492             $next_val:expr;
493             [$($out:tt)*]
494             [
495                 [$(#[$mout:meta])*]
496                 [
497                     #[selinux(name = $selinux_name:ident)]
498                     $(#[$($mtail:tt)+])*
499                 ]
500                 $vname:ident,
501                 $($tail:tt)*
502             ]
503         }
504     ) => {
505         implement_class!{
506             @extract_perm_name
507             $class_name
508             $(#[$enum_meta])*
509             $enum_vis enum $enum_name {
510                 ($next_val << 1);
511                 [
512                     $($out)*
513                     $(#[$mout])*
514                     $(#[$($mtail)+])*
515                     $selinux_name $vname = $next_val,
516                 ]
517                 [$($tail)*]
518             }
519         }
520     };
521 
522     // The third rule descends a step in the inner recursion.
523     (
524         @extract_perm_name
525         $class_name:ident
526         $(#[$enum_meta:meta])*
527         $enum_vis:vis enum $enum_name:ident {
528             $next_val:expr;
529             [$($out:tt)*]
530             [
531                 [$(#[$mout:meta])*]
532                 [
533                     #[$front:meta]
534                     $(#[$($mtail:tt)+])*
535                 ]
536                 $vname:ident$( = $vval:expr)?,
537                 $($tail:tt)*
538             ]
539         }
540     ) => {
541         implement_class!{
542             @extract_perm_name
543             $class_name
544             $(#[$enum_meta])*
545             $enum_vis enum $enum_name {
546                 $next_val;
547                 [$($out)*]
548                 [
549                     [
550                         $(#[$mout])*
551                         #[$front]
552                     ]
553                     [$(#[$($mtail)+])*]
554                     $vname$( = $vval)?,
555                     $($tail)*
556                 ]
557             }
558         }
559     };
560 
561     // The fourth rule terminates the outer recursion and transitions to the
562     // implementation phase @spill.
563     (
564         @extract_perm_name
565         $class_name:ident
566         $(#[$enum_meta:meta])*
567         $enum_vis:vis enum $enum_name:ident {
568             $next_val:expr;
569             [$($out:tt)*]
570             []
571         }
572     ) => {
573         implement_class!{
574             @spill
575             $class_name
576             $(#[$enum_meta])*
577             $enum_vis enum $enum_name {
578                 $($out)*
579             }
580         }
581     };
582 
583     (
584         @spill
585         $class_name:ident
586         $(#[$enum_meta:meta])*
587         $enum_vis:vis enum $enum_name:ident {
588             $(
589                 $(#[$emeta:meta])*
590                 $selinux_name:ident $vname:ident = $vval:expr,
591             )*
592         }
593     ) => {
594         $(#[$enum_meta])*
595         $enum_vis enum $enum_name {
596             /// The default variant of the enum.
597             None = 0,
598             $(
599                 $(#[$emeta])*
600                 $vname = $vval,
601             )*
602         }
603 
604         impl From<i32> for $enum_name {
605             #[allow(non_upper_case_globals)]
606             fn from (p: i32) -> Self {
607                 // Creating constants forces the compiler to evaluate the value expressions
608                 // so that they can be used in the match statement below.
609                 $(const $vname: i32 = $vval;)*
610                 match p {
611                     0 => Self::None,
612                     $($vname => Self::$vname,)*
613                     _ => Self::None,
614                 }
615             }
616         }
617 
618         impl From<$enum_name> for i32 {
619             fn from(p: $enum_name) -> i32 {
620                 p as i32
621             }
622         }
623 
624         impl ClassPermission for $enum_name {
625             fn name(&self) -> &'static str {
626                 match self {
627                     Self::None => &"none",
628                     $(Self::$vname => stringify!($selinux_name),)*
629                 }
630             }
631             fn class_name(&self) -> &'static str {
632                 stringify!($class_name)
633             }
634         }
635     };
636 }
637 
638 /// Calls `check_access` on the given class permission.
check_permission<T: ClassPermission>(source: &CStr, target: &CStr, perm: T) -> Result<()>639 pub fn check_permission<T: ClassPermission>(source: &CStr, target: &CStr, perm: T) -> Result<()> {
640     check_access(source, target, perm.class_name(), perm.name())
641 }
642 
643 #[cfg(test)]
644 mod tests {
645     use super::*;
646     use anyhow::Result;
647 
648     /// The su_key namespace as defined in su.te and keystore_key_contexts of the
649     /// SePolicy (system/sepolicy).
650     static SU_KEY_NAMESPACE: &str = "0";
651     /// The shell_key namespace as defined in shell.te and keystore_key_contexts of the
652     /// SePolicy (system/sepolicy).
653     static SHELL_KEY_NAMESPACE: &str = "1";
654 
check_context() -> Result<(Context, &'static str, bool)>655     fn check_context() -> Result<(Context, &'static str, bool)> {
656         let context = getcon()?;
657         match context.to_str().unwrap() {
658             "u:r:su:s0" => Ok((context, SU_KEY_NAMESPACE, true)),
659             "u:r:shell:s0" => Ok((context, SHELL_KEY_NAMESPACE, false)),
660             c => Err(anyhow!(format!(
661                 "This test must be run as \"su\" or \"shell\". Current context: \"{}\"",
662                 c
663             ))),
664         }
665     }
666 
667     #[test]
test_getcon() -> Result<()>668     fn test_getcon() -> Result<()> {
669         check_context()?;
670         Ok(())
671     }
672 
673     #[test]
test_label_lookup() -> Result<()>674     fn test_label_lookup() -> Result<()> {
675         let (_context, namespace, is_su) = check_context()?;
676         let backend = crate::KeystoreKeyBackend::new()?;
677         let context = backend.lookup(namespace)?;
678         if is_su {
679             assert_eq!(context.to_str(), Ok("u:object_r:su_key:s0"));
680         } else {
681             assert_eq!(context.to_str(), Ok("u:object_r:shell_key:s0"));
682         }
683         Ok(())
684     }
685 
686     #[test]
context_from_string() -> Result<()>687     fn context_from_string() -> Result<()> {
688         let tctx = Context::new("u:object_r:keystore:s0").unwrap();
689         let sctx = Context::new("u:r:system_server:s0").unwrap();
690         check_access(&sctx, &tctx, "keystore2_key", "use")?;
691         Ok(())
692     }
693 
694     mod perm {
695         use super::super::*;
696         use super::*;
697         use anyhow::Result;
698 
699         /// check_key_perm(perm, privileged, priv_domain)
700         /// `perm` is a permission of the keystore2_key class and `privileged` is a boolean
701         /// indicating whether the permission is considered privileged.
702         /// Privileged permissions are expected to be denied to `shell` users but granted
703         /// to the given priv_domain.
704         macro_rules! check_key_perm {
705             // "use" is a keyword and cannot be used as an identifier, but we must keep
706             // the permission string intact. So we map the identifier name on use_ while using
707             // the permission string "use". In all other cases we can simply use the stringified
708             // identifier as permission string.
709             (use, $privileged:expr) => {
710                 check_key_perm!(use_, $privileged, "use");
711             };
712             ($perm:ident, $privileged:expr) => {
713                 check_key_perm!($perm, $privileged, stringify!($perm));
714             };
715             ($perm:ident, $privileged:expr, $p_str:expr) => {
716                 #[test]
717                 fn $perm() -> Result<()> {
718                     android_logger::init_once(
719                         android_logger::Config::default()
720                             .with_tag("keystore_selinux_tests")
721                             .with_max_level(log::LevelFilter::Debug),
722                     );
723                     let scontext = Context::new("u:r:shell:s0")?;
724                     let backend = KeystoreKeyBackend::new()?;
725                     let tcontext = backend.lookup(SHELL_KEY_NAMESPACE)?;
726 
727                     if $privileged {
728                         assert_eq!(
729                             Some(&Error::perm()),
730                             check_access(
731                                 &scontext,
732                                 &tcontext,
733                                 "keystore2_key",
734                                 $p_str
735                             )
736                             .err()
737                             .unwrap()
738                             .root_cause()
739                             .downcast_ref::<Error>()
740                         );
741                     } else {
742                         assert!(check_access(
743                             &scontext,
744                             &tcontext,
745                             "keystore2_key",
746                             $p_str
747                         )
748                         .is_ok());
749                     }
750                     Ok(())
751                 }
752             };
753         }
754 
755         check_key_perm!(manage_blob, true);
756         check_key_perm!(delete, false);
757         check_key_perm!(use_dev_id, true);
758         check_key_perm!(req_forced_op, true);
759         check_key_perm!(gen_unique_id, true);
760         check_key_perm!(grant, true);
761         check_key_perm!(get_info, false);
762         check_key_perm!(rebind, false);
763         check_key_perm!(update, false);
764         check_key_perm!(use, false);
765 
766         macro_rules! check_keystore_perm {
767             ($perm:ident) => {
768                 #[test]
769                 fn $perm() -> Result<()> {
770                     let ks_context = Context::new("u:object_r:keystore:s0")?;
771                     let priv_context = Context::new("u:r:system_server:s0")?;
772                     let unpriv_context = Context::new("u:r:shell:s0")?;
773                     assert!(check_access(
774                         &priv_context,
775                         &ks_context,
776                         "keystore2",
777                         stringify!($perm)
778                     )
779                     .is_ok());
780                     assert_eq!(
781                         Some(&Error::perm()),
782                         check_access(&unpriv_context, &ks_context, "keystore2", stringify!($perm))
783                             .err()
784                             .unwrap()
785                             .root_cause()
786                             .downcast_ref::<Error>()
787                     );
788                     Ok(())
789                 }
790             };
791         }
792 
793         check_keystore_perm!(add_auth);
794         check_keystore_perm!(clear_ns);
795         check_keystore_perm!(lock);
796         check_keystore_perm!(reset);
797         check_keystore_perm!(unlock);
798     }
799 
800     #[test]
test_getpidcon()801     fn test_getpidcon() {
802         // Check that `getpidcon` of our pid is equal to what `getcon` returns.
803         // And by using `unwrap` we make sure that both also have to return successfully
804         // fully to pass the test.
805         assert_eq!(getpidcon(std::process::id() as i32).unwrap(), getcon().unwrap());
806     }
807 }
808