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