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