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