blob: f4da749265686669f07ae6c42a932df6114c30d2 [file] [log] [blame]
Janis Danisevskis7d77a762020-07-20 13:03:31 -07001// Copyright 2020, 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//! Keystore error provides convenience methods and types for Keystore error handling.
16//! Clients of Keystore expect one of two error codes, i.e., a Keystore ResponseCode as
17//! defined by the Keystore AIDL interface, or a Keymint ErrorCode as defined by
18//! the Keymint HAL specification.
19//! This crate provides `Error` which can wrap both. It is to be used
20//! internally by Keystore to diagnose error conditions that need to be reported to
21//! the client. To report the error condition to the client the Keystore AIDL
22//! interface defines a wire type `Result` which is distinctly different from Rust's
23//! `enum Result<T,E>`.
24//!
25//! This crate provides the convenience method `map_or_log_err` to convert `anyhow::Error`
26//! into this wire type. In addition to handling the conversion of `Error`
27//! to the `Result` wire type it handles any other error by mapping it to
28//! `ResponseCode::SystemError` and logs any error condition.
29//!
30//! Keystore functions should use `anyhow::Result` to return error conditions, and
31//! context should be added every time an error is forwarded.
32
33use std::cmp::PartialEq;
Janis Danisevskis7d77a762020-07-20 13:03:31 -070034
Janis Danisevskise24f3472020-08-12 17:58:49 -070035pub use android_hardware_keymint::aidl::android::hardware::keymint::ErrorCode as Ec;
36pub use android_security_keystore2::aidl::android::security::keystore2::ResponseCode as Rc;
37
38use android_hardware_keymint::aidl::android::hardware::keymint::ErrorCode::ErrorCode;
39use android_security_keystore2::aidl::android::security::keystore2::ResponseCode::ResponseCode;
Janis Danisevskis7d77a762020-07-20 13:03:31 -070040
Janis Danisevskisce995432020-07-21 12:22:34 -070041use keystore2_selinux as selinux;
42
Janis Danisevskise24f3472020-08-12 17:58:49 -070043use android_security_keystore2::binder::{Result as BinderResult, Status as BinderStatus};
Janis Danisevskis7d77a762020-07-20 13:03:31 -070044
45/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
46/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
47#[derive(Debug, thiserror::Error, PartialEq)]
48pub enum Error {
49 /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
50 #[error("Error::Rc({0:?})")]
Janis Danisevskise24f3472020-08-12 17:58:49 -070051 Rc(ResponseCode),
Janis Danisevskis7d77a762020-07-20 13:03:31 -070052 /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
53 #[error("Error::Km({0:?})")]
Janis Danisevskise24f3472020-08-12 17:58:49 -070054 Km(ErrorCode),
Janis Danisevskis7d77a762020-07-20 13:03:31 -070055}
56
57impl Error {
58 /// Short hand for `Error::Rc(ResponseCode::SystemError)`
59 pub fn sys() -> Self {
Janis Danisevskise24f3472020-08-12 17:58:49 -070060 Error::Rc(Rc::SystemError)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070061 }
62
63 /// Short hand for `Error::Rc(ResponseCode::PermissionDenied`
64 pub fn perm() -> Self {
Janis Danisevskise24f3472020-08-12 17:58:49 -070065 Error::Rc(Rc::PermissionDenied)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070066 }
67}
68
69/// This function should be used by Keystore service calls to translate error conditions
70/// into `android.security.keystore2.Result` which is imported here as `aidl::Result`
71/// and newtyped as AidlResult.
72/// All error conditions get logged by this function.
73/// All `Error::Rc(x)` variants get mapped onto `aidl::Result{x, 0}`.
74/// All `Error::Km(x)` variants get mapped onto
75/// `aidl::Result{aidl::ResponseCode::KeymintErrorCode, x}`.
Janis Danisevskisce995432020-07-21 12:22:34 -070076/// `selinux::Error::perm()` is mapped on `aidl::Result{aidl::ResponseCode::PermissionDenied, 0}`.
Janis Danisevskis7d77a762020-07-20 13:03:31 -070077///
78/// All non `Error` error conditions get mapped onto
79/// `aidl::Result{aidl::ResponseCode::SystemError}`.
80///
81/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
82/// as argument to `handle_ok`. `handle_ok` must generate an `AidlResult`, typically
83/// `AidlResult::ok()`, but other response codes may be used, e.g.,
84/// `aidl::ResponseCode::OpAuthNeeded` which does not required logging.
85///
86/// # Examples
87///
88/// ```
89/// fn loadKey() -> anyhow::Result<aidl::ResponseCode> {
90/// if (good_but_auth_required) {
91/// Ok(aidl::ResponseCode::OpAuthRequired)
92/// } else {
93/// Err(anyhow!(Error::Rc(aidl::ResponseCode::KeyNotFound)))
94/// }
95/// }
96///
97/// aidl_result_ = map_or_log_err(loadKey(), |r| { some_side_effect(); AidlResult::rc(r) });
98/// ```
Janis Danisevskise24f3472020-08-12 17:58:49 -070099pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
100where
101 F: FnOnce(U) -> BinderResult<T>,
102{
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700103 result.map_or_else(
104 |e| {
105 log::error!("{:?}", e);
Janis Danisevskise24f3472020-08-12 17:58:49 -0700106 let root_cause = e.root_cause();
107 let rc = match root_cause.downcast_ref::<Error>() {
108 Some(Error::Rc(rcode)) => *rcode,
109 Some(Error::Km(ec)) => *ec,
110 None => match root_cause.downcast_ref::<selinux::Error>() {
111 Some(selinux::Error::PermissionDenied) => Rc::PermissionDenied,
112 _ => Rc::SystemError,
113 },
114 };
115 Err(BinderStatus::new_service_specific_error(rc, None))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700116 },
117 handle_ok,
118 )
119}
120
121#[cfg(test)]
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000122pub mod tests {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700123
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700124 use super::*;
Janis Danisevskise24f3472020-08-12 17:58:49 -0700125 use anyhow::{anyhow, Context};
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700126
Janis Danisevskise24f3472020-08-12 17:58:49 -0700127 fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700128 Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
129 }
130
Janis Danisevskise24f3472020-08-12 17:58:49 -0700131 fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700132 nested_nested_rc(rc).context("nested rc")
133 }
134
135 fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
136 Err(anyhow!(Error::Km(ec))).context("nested nested ec")
137 }
138
139 fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
140 nested_nested_ec(ec).context("nested ec")
141 }
142
Janis Danisevskise24f3472020-08-12 17:58:49 -0700143 fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700144 Ok(rc)
145 }
146
Janis Danisevskise24f3472020-08-12 17:58:49 -0700147 fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700148 nested_nested_ok(rc).context("nested ok")
149 }
150
Janis Danisevskisce995432020-07-21 12:22:34 -0700151 fn nested_nested_selinux_perm() -> anyhow::Result<()> {
152 Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
153 }
154
155 fn nested_selinux_perm() -> anyhow::Result<()> {
156 nested_nested_selinux_perm().context("nested selinux permission denied")
157 }
158
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700159 #[derive(Debug, thiserror::Error)]
160 enum TestError {
161 #[error("TestError::Fail")]
162 Fail = 0,
163 }
164
165 fn nested_nested_other_error() -> anyhow::Result<()> {
166 Err(anyhow!(TestError::Fail)).context("nested nested other error")
167 }
168
169 fn nested_other_error() -> anyhow::Result<()> {
170 nested_nested_other_error().context("nested other error")
171 }
172
173 #[test]
174 fn keystore_error_test() -> anyhow::Result<(), String> {
175 android_logger::init_once(
176 android_logger::Config::default()
177 .with_tag("keystore_error_tests")
178 .with_min_level(log::Level::Debug),
179 );
Janis Danisevskise24f3472020-08-12 17:58:49 -0700180 // All Error::Rc(x) get mapped on a service specific error
181 // code of x.
182 for rc in Rc::Ok..Rc::BackendBusy {
183 assert_eq!(
184 Result::<(), i32>::Err(rc),
185 map_or_log_err(nested_rc(rc), |_| Err(BinderStatus::ok()))
186 .map_err(|s| s.service_specific_error())
187 );
188 }
189
190 // All KeystoreKerror::Km(x) get mapped on a service
191 // specific error of x.
192 for ec in Ec::UNKNOWN_ERROR..Ec::ROOT_OF_TRUST_ALREADY_SET {
193 assert_eq!(
194 Result::<(), i32>::Err(ec),
195 map_or_log_err(nested_ec(ec), |_| Err(BinderStatus::ok()))
196 .map_err(|s| s.service_specific_error())
197 );
198 }
199
200 // selinux::Error::Perm() needs to be mapped to Rc::PermissionDenied
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700201 assert_eq!(
Janis Danisevskise24f3472020-08-12 17:58:49 -0700202 Result::<(), i32>::Err(Rc::PermissionDenied),
203 map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
204 .map_err(|s| s.service_specific_error())
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700205 );
206
Janis Danisevskise24f3472020-08-12 17:58:49 -0700207 // All other errors get mapped on System Error.
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700208 assert_eq!(
Janis Danisevskise24f3472020-08-12 17:58:49 -0700209 Result::<(), i32>::Err(Rc::SystemError),
210 map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
211 .map_err(|s| s.service_specific_error())
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700212 );
213
214 // Result::Ok variants get passed to the ok handler.
Janis Danisevskise24f3472020-08-12 17:58:49 -0700215 assert_eq!(Ok(Rc::OpAuthNeeded), map_or_log_err(nested_ok(Rc::OpAuthNeeded), Ok));
216 assert_eq!(Ok(Rc::Ok), map_or_log_err(nested_ok(Rc::Ok), Ok));
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700217
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700218 Ok(())
219 }
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000220
221 //Helper function to test whether error cases are handled as expected.
Janis Danisevskise24f3472020-08-12 17:58:49 -0700222 pub fn check_result_contains_error_string<T>(
223 result: anyhow::Result<T>,
224 expected_error_string: &str,
225 ) {
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000226 let error_str = format!(
227 "{:#?}",
228 result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
229 );
230 assert!(
231 error_str.contains(expected_error_string),
232 "The string \"{}\" should contain \"{}\"",
233 error_str,
234 expected_error_string
235 );
236 }
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700237} // mod tests