120 lines
4.5 KiB
Rust
120 lines
4.5 KiB
Rust
// Copyright 2021, The Android Open Source Project
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
use android_hardware_security_dice::aidl::android::hardware::security::dice::ResponseCode::ResponseCode;
|
|
use anyhow::Result;
|
|
use binder::{ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode};
|
|
use std::ffi::CString;
|
|
|
|
/// This is the error type for DICE HAL implementations. It wraps
|
|
/// `android::hardware::security::dice::ResponseCode` generated
|
|
/// from AIDL in the `Rc` variant and Binder and BinderTransaction errors in the respective
|
|
/// variants.
|
|
#[allow(dead_code)] // Binder error forwarding will be needed when proxy nodes are implemented.
|
|
#[derive(Debug, thiserror::Error, Eq, PartialEq, Clone)]
|
|
pub enum Error {
|
|
/// Wraps a dice `ResponseCode` as defined by the Keystore AIDL interface specification.
|
|
#[error("Error::Rc({0:?})")]
|
|
Rc(ResponseCode),
|
|
/// Wraps a Binder exception code other than a service specific exception.
|
|
#[error("Binder exception code {0:?}, {1:?}")]
|
|
Binder(ExceptionCode, i32),
|
|
/// Wraps a Binder status code.
|
|
#[error("Binder transaction error {0:?}")]
|
|
BinderTransaction(StatusCode),
|
|
}
|
|
|
|
/// This function should be used by dice service calls to translate error conditions
|
|
/// into service specific exceptions.
|
|
///
|
|
/// All error conditions get logged by this function.
|
|
///
|
|
/// All `Error::Rc(x)` variants get mapped onto a service specific error code of x.
|
|
/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
|
|
///
|
|
/// All non `Error` error conditions and the Error::Binder variant get mapped onto
|
|
/// ResponseCode::SYSTEM_ERROR`.
|
|
///
|
|
/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
|
|
/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
|
|
/// typically returns Ok(value).
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// fn do_something() -> anyhow::Result<Vec<u8>> {
|
|
/// Err(anyhow!(Error::Rc(ResponseCode::NOT_IMPLEMENTED)))
|
|
/// }
|
|
///
|
|
/// map_or_log_err(do_something(), Ok)
|
|
/// ```
|
|
pub fn map_or_log_err<T, U, F>(result: Result<U>, handle_ok: F) -> BinderResult<T>
|
|
where
|
|
F: FnOnce(U) -> BinderResult<T>,
|
|
{
|
|
map_err_with(
|
|
result,
|
|
|e| {
|
|
log::error!("{:?}", e);
|
|
e
|
|
},
|
|
handle_ok,
|
|
)
|
|
}
|
|
|
|
/// This function behaves similar to map_or_log_error, but it does not log the errors, instead
|
|
/// it calls map_err on the error before mapping it to a binder result allowing callers to
|
|
/// log or transform the error before mapping it.
|
|
fn 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>,
|
|
{
|
|
result.map_or_else(
|
|
|e| {
|
|
let e = map_err(e);
|
|
let msg = match CString::new(format!("{:?}", e)) {
|
|
Ok(msg) => Some(msg),
|
|
Err(_) => {
|
|
log::warn!(
|
|
"Cannot convert error message to CStr. It contained a nul byte.
|
|
Omitting message from service specific error."
|
|
);
|
|
None
|
|
}
|
|
};
|
|
let rc = get_error_code(&e);
|
|
Err(BinderStatus::new_service_specific_error(rc, msg.as_deref()))
|
|
},
|
|
handle_ok,
|
|
)
|
|
}
|
|
|
|
/// Extracts the error code from an `anyhow::Error` mapping any error that does not have a
|
|
/// root cause of `Error::Rc` onto `ResponseCode::SYSTEM_ERROR` and to `e` with `Error::Rc(e)`
|
|
/// otherwise.
|
|
fn get_error_code(e: &anyhow::Error) -> i32 {
|
|
let root_cause = e.root_cause();
|
|
match root_cause.downcast_ref::<Error>() {
|
|
Some(Error::Rc(rcode)) => rcode.0,
|
|
// If an Error::Binder reaches this stage we report a system error.
|
|
// The exception code and possible service specific error will be
|
|
// printed in the error log above.
|
|
Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
|
|
ResponseCode::SYSTEM_ERROR.0
|
|
}
|
|
None => ResponseCode::SYSTEM_ERROR.0,
|
|
}
|
|
}
|