// 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`, but it /// typically returns Ok(value). /// /// # Examples /// /// ``` /// fn do_something() -> anyhow::Result> { /// Err(anyhow!(Error::Rc(ResponseCode::NOT_IMPLEMENTED))) /// } /// /// map_or_log_err(do_something(), Ok) /// ``` pub fn map_or_log_err(result: Result, handle_ok: F) -> BinderResult where F: FnOnce(U) -> BinderResult, { 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(result: Result, map_err: F1, handle_ok: F2) -> BinderResult where F1: FnOnce(anyhow::Error) -> anyhow::Error, F2: FnOnce(U) -> BinderResult, { 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::() { 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, } }