• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021, 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 use android_security_dice::aidl::android::security::dice::ResponseCode::ResponseCode;
16 use anyhow::Result;
17 use binder::{ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode};
18 use keystore2_selinux as selinux;
19 use std::ffi::CString;
20 
21 /// This is the main Diced error type. It wraps the Diced `ResponseCode` generated
22 /// from AIDL in the `Rc` variant and Binder and BinderTransaction errors in the respective
23 /// variants.
24 #[allow(dead_code)] // Binder error forwarding will be needed when proxy nodes are implemented.
25 #[derive(Debug, thiserror::Error, Eq, PartialEq, Clone)]
26 pub enum Error {
27     /// Wraps a dice `ResponseCode` as defined by the android.security.dice AIDL interface
28     /// specification.
29     #[error("Error::Rc({0:?})")]
30     Rc(ResponseCode),
31     /// Wraps a Binder exception code other than a service specific exception.
32     #[error("Binder exception code {0:?}, {1:?}")]
33     Binder(ExceptionCode, i32),
34     /// Wraps a Binder status code.
35     #[error("Binder transaction error {0:?}")]
36     BinderTransaction(StatusCode),
37 }
38 
39 /// This function should be used by dice service calls to translate error conditions
40 /// into service specific exceptions.
41 ///
42 /// All error conditions get logged by this function.
43 ///
44 /// All `Error::Rc(x)` variants get mapped onto a service specific error code of x.
45 /// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
46 ///
47 /// All non `Error` error conditions and the Error::Binder variant get mapped onto
48 /// ResponseCode::SYSTEM_ERROR`.
49 ///
50 /// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
51 /// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
52 /// typically returns Ok(value).
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// fn do_something() -> anyhow::Result<Vec<u8>> {
58 ///     Err(anyhow!(Error::Rc(ResponseCode::NOT_IMPLEMENTED)))
59 /// }
60 ///
61 /// map_or_log_err(do_something(), Ok)
62 /// ```
map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T> where F: FnOnce(U) -> BinderResult<T>,63 pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
64 where
65     F: FnOnce(U) -> BinderResult<T>,
66 {
67     map_err_with(
68         result,
69         |e| {
70             log::error!("{:?}", e);
71             e
72         },
73         handle_ok,
74     )
75 }
76 
77 /// This function behaves similar to map_or_log_error, but it does not log the errors, instead
78 /// it calls map_err on the error before mapping it to a binder result allowing callers to
79 /// log or transform the error before mapping it.
map_err_with<T, U, F1, F2>(result: Result<U>, map_err: F1, handle_ok: F2) -> BinderResult<T> where F1: FnOnce(anyhow::Error) -> anyhow::Error, F2: FnOnce(U) -> BinderResult<T>,80 fn map_err_with<T, U, F1, F2>(result: Result<U>, map_err: F1, handle_ok: F2) -> BinderResult<T>
81 where
82     F1: FnOnce(anyhow::Error) -> anyhow::Error,
83     F2: FnOnce(U) -> BinderResult<T>,
84 {
85     result.map_or_else(
86         |e| {
87             let e = map_err(e);
88             let msg = match CString::new(format!("{:?}", e)) {
89                 Ok(msg) => Some(msg),
90                 Err(_) => {
91                     log::warn!(
92                         "Cannot convert error message to CStr. It contained a nul byte.
93                          Omitting message from service specific error."
94                     );
95                     None
96                 }
97             };
98             let rc = get_error_code(&e);
99             Err(BinderStatus::new_service_specific_error(rc, msg.as_deref()))
100         },
101         handle_ok,
102     )
103 }
104 
105 /// Extracts the error code from an `anyhow::Error` mapping any error that does not have a
106 /// root cause of `Error::Rc` onto `ResponseCode::SYSTEM_ERROR` and to `e` with `Error::Rc(e)`
107 /// otherwise.
get_error_code(e: &anyhow::Error) -> i32108 fn get_error_code(e: &anyhow::Error) -> i32 {
109     let root_cause = e.root_cause();
110     match root_cause.downcast_ref::<Error>() {
111         Some(Error::Rc(rcode)) => rcode.0,
112         // If an Error::Binder reaches this stage we report a system error.
113         // The exception code and possible service specific error will be
114         // printed in the error log above.
115         Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
116             ResponseCode::SYSTEM_ERROR.0
117         }
118         None => match root_cause.downcast_ref::<selinux::Error>() {
119             Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
120             _ => ResponseCode::SYSTEM_ERROR.0,
121         },
122     }
123 }
124