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