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