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