blob: 49d72bbb9b118aa0c9a987d81110a0ac8143cf10 [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
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070028//! `ResponseCode::SYSTEM_ERROR` and logs any error condition.
Janis Danisevskis7d77a762020-07-20 13:03:31 -070029//!
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 Danisevskisc5b210b2020-09-11 13:27:37 -070035pub use android_hardware_keymint::aidl::android::hardware::keymint::ErrorCode::ErrorCode;
36pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
Janis Danisevskis7d77a762020-07-20 13:03:31 -070037
Janis Danisevskisce995432020-07-21 12:22:34 -070038use keystore2_selinux as selinux;
39
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070040use android_system_keystore2::binder::{
Janis Danisevskis017d2092020-09-02 10:15:52 -070041 ExceptionCode, Result as BinderResult, Status as BinderStatus,
42};
Janis Danisevskis7d77a762020-07-20 13:03:31 -070043
44/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
45/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
46#[derive(Debug, thiserror::Error, PartialEq)]
47pub enum Error {
48 /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
49 #[error("Error::Rc({0:?})")]
Janis Danisevskise24f3472020-08-12 17:58:49 -070050 Rc(ResponseCode),
Janis Danisevskis7d77a762020-07-20 13:03:31 -070051 /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
52 #[error("Error::Km({0:?})")]
Janis Danisevskise24f3472020-08-12 17:58:49 -070053 Km(ErrorCode),
Janis Danisevskis017d2092020-09-02 10:15:52 -070054 /// Wraps a Binder exception code other than a service specific exception.
55 #[error("Binder exception code {0:?}, {1:?}")]
56 Binder(ExceptionCode, i32),
Janis Danisevskis7d77a762020-07-20 13:03:31 -070057}
58
59impl Error {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070060 /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
Janis Danisevskis7d77a762020-07-20 13:03:31 -070061 pub fn sys() -> Self {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070062 Error::Rc(ResponseCode::SYSTEM_ERROR)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070063 }
64
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070065 /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED`
Janis Danisevskis7d77a762020-07-20 13:03:31 -070066 pub fn perm() -> Self {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070067 Error::Rc(ResponseCode::PERMISSION_DENIED)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070068 }
69}
70
Janis Danisevskis017d2092020-09-02 10:15:52 -070071/// Helper function to map the binder status we get from calls into KeyMint
72/// to a Keystore Error. We don't create an anyhow error here to make
73/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
74/// when diagnosing authentication requirements, update requirements, and running
75/// out of operation slots.
76pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
77 r.map_err(|s| {
78 match s.exception_code() {
79 ExceptionCode::SERVICE_SPECIFIC => {
80 let se = s.service_specific_error();
81 if se < 0 {
82 // Negative service specific errors are KM error codes.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070083 Error::Km(ErrorCode(s.service_specific_error()))
Janis Danisevskis017d2092020-09-02 10:15:52 -070084 } else {
85 // Non negative error codes cannot be KM error codes.
86 // So we create an `Error::Binder` variant to preserve
87 // the service specific error code for logging.
88 // `map_or_log_err` will map this on a system error,
89 // but not before logging the details to logcat.
90 Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
91 }
92 }
93 // We create `Error::Binder` to preserve the exception code
94 // for logging.
95 // `map_or_log_err` will map this on a system error.
96 e_code => Error::Binder(e_code, 0),
97 }
98 })
99}
100
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700101/// This function should be used by Keystore service calls to translate error conditions
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800102/// into service specific exceptions.
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700103///
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800104/// All error conditions get logged by this function.
105///
106/// All `Error::Rc(x)` and `Error::Km(x)` variants get mapped onto a service specific error
107/// code of x. This is possible because KeyMint `ErrorCode` errors are always negative and
108/// `ResponseCode` codes are always positive.
109/// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
110///
111/// All non `Error` error conditions and the Error::Binder variant get mapped onto
112/// ResponseCode::SYSTEM_ERROR`.
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700113///
114/// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800115/// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
116/// typically returns Ok(value).
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700117///
118/// # Examples
119///
120/// ```
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800121/// fn loadKey() -> anyhow::Result<Vec<u8>> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700122/// if (good_but_auth_required) {
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800123/// Ok(vec!['k', 'e', 'y'])
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700124/// } else {
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800125/// Err(anyhow!(Error::Rc(ResponseCode::KEY_NOT_FOUND)))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700126/// }
127/// }
128///
Janis Danisevskis8ea5f552020-11-20 11:22:59 -0800129/// map_or_log_err(loadKey(), Ok)
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700130/// ```
Janis Danisevskise24f3472020-08-12 17:58:49 -0700131pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
132where
133 F: FnOnce(U) -> BinderResult<T>,
134{
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700135 result.map_or_else(
136 |e| {
137 log::error!("{:?}", e);
Janis Danisevskise24f3472020-08-12 17:58:49 -0700138 let root_cause = e.root_cause();
139 let rc = match root_cause.downcast_ref::<Error>() {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700140 Some(Error::Rc(rcode)) => rcode.0,
141 Some(Error::Km(ec)) => ec.0,
Janis Danisevskis017d2092020-09-02 10:15:52 -0700142 // If an Error::Binder reaches this stage we report a system error.
143 // The exception code and possible service specific error will be
144 // printed in the error log above.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700145 Some(Error::Binder(_, _)) => ResponseCode::SYSTEM_ERROR.0,
Janis Danisevskise24f3472020-08-12 17:58:49 -0700146 None => match root_cause.downcast_ref::<selinux::Error>() {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700147 Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
148 _ => ResponseCode::SYSTEM_ERROR.0,
Janis Danisevskise24f3472020-08-12 17:58:49 -0700149 },
150 };
151 Err(BinderStatus::new_service_specific_error(rc, None))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700152 },
153 handle_ok,
154 )
155}
156
157#[cfg(test)]
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000158pub mod tests {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700159
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700160 use super::*;
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700161 use android_system_keystore2::binder::{
Janis Danisevskis017d2092020-09-02 10:15:52 -0700162 ExceptionCode, Result as BinderResult, Status as BinderStatus,
163 };
Janis Danisevskise24f3472020-08-12 17:58:49 -0700164 use anyhow::{anyhow, Context};
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700165
Janis Danisevskise24f3472020-08-12 17:58:49 -0700166 fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700167 Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
168 }
169
Janis Danisevskise24f3472020-08-12 17:58:49 -0700170 fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700171 nested_nested_rc(rc).context("nested rc")
172 }
173
174 fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
175 Err(anyhow!(Error::Km(ec))).context("nested nested ec")
176 }
177
178 fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
179 nested_nested_ec(ec).context("nested ec")
180 }
181
Janis Danisevskise24f3472020-08-12 17:58:49 -0700182 fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700183 Ok(rc)
184 }
185
Janis Danisevskise24f3472020-08-12 17:58:49 -0700186 fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700187 nested_nested_ok(rc).context("nested ok")
188 }
189
Janis Danisevskisce995432020-07-21 12:22:34 -0700190 fn nested_nested_selinux_perm() -> anyhow::Result<()> {
191 Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
192 }
193
194 fn nested_selinux_perm() -> anyhow::Result<()> {
195 nested_nested_selinux_perm().context("nested selinux permission denied")
196 }
197
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700198 #[derive(Debug, thiserror::Error)]
199 enum TestError {
200 #[error("TestError::Fail")]
201 Fail = 0,
202 }
203
204 fn nested_nested_other_error() -> anyhow::Result<()> {
205 Err(anyhow!(TestError::Fail)).context("nested nested other error")
206 }
207
208 fn nested_other_error() -> anyhow::Result<()> {
209 nested_nested_other_error().context("nested other error")
210 }
211
Janis Danisevskis017d2092020-09-02 10:15:52 -0700212 fn binder_sse_error(sse: i32) -> BinderResult<()> {
213 Err(BinderStatus::new_service_specific_error(sse, None))
214 }
215
216 fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
217 Err(BinderStatus::new_exception(ex, None))
218 }
219
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700220 #[test]
221 fn keystore_error_test() -> anyhow::Result<(), String> {
222 android_logger::init_once(
223 android_logger::Config::default()
224 .with_tag("keystore_error_tests")
225 .with_min_level(log::Level::Debug),
226 );
Janis Danisevskise24f3472020-08-12 17:58:49 -0700227 // All Error::Rc(x) get mapped on a service specific error
228 // code of x.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700229 for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
Janis Danisevskise24f3472020-08-12 17:58:49 -0700230 assert_eq!(
231 Result::<(), i32>::Err(rc),
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700232 map_or_log_err(nested_rc(ResponseCode(rc)), |_| Err(BinderStatus::ok()))
Janis Danisevskise24f3472020-08-12 17:58:49 -0700233 .map_err(|s| s.service_specific_error())
234 );
235 }
236
Janis Danisevskis017d2092020-09-02 10:15:52 -0700237 // All Keystore Error::Km(x) get mapped on a service
Janis Danisevskise24f3472020-08-12 17:58:49 -0700238 // specific error of x.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700239 for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
Janis Danisevskise24f3472020-08-12 17:58:49 -0700240 assert_eq!(
241 Result::<(), i32>::Err(ec),
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700242 map_or_log_err(nested_ec(ErrorCode(ec)), |_| Err(BinderStatus::ok()))
Janis Danisevskise24f3472020-08-12 17:58:49 -0700243 .map_err(|s| s.service_specific_error())
244 );
245 }
246
Janis Danisevskis017d2092020-09-02 10:15:52 -0700247 // All Keymint errors x received through a Binder Result get mapped on
248 // a service specific error of x.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700249 for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
Janis Danisevskis017d2092020-09-02 10:15:52 -0700250 assert_eq!(
251 Result::<(), i32>::Err(ec),
252 map_or_log_err(
253 map_km_error(binder_sse_error(ec))
254 .with_context(|| format!("Km error code: {}.", ec)),
255 |_| Err(BinderStatus::ok())
256 )
257 .map_err(|s| s.service_specific_error())
258 );
259 }
260
261 // map_km_error creates an Error::Binder variant storing
262 // ExceptionCode::SERVICE_SPECIFIC and the given
263 // service specific error.
264 let sse = map_km_error(binder_sse_error(1));
265 assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700266 // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
Janis Danisevskis017d2092020-09-02 10:15:52 -0700267 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700268 Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
Janis Danisevskis017d2092020-09-02 10:15:52 -0700269 map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
270 BinderStatus::ok()
271 ))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700272 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis017d2092020-09-02 10:15:52 -0700273 );
274
275 // map_km_error creates a Error::Binder variant storing the given exception code.
276 let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
277 assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700278 // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
Janis Danisevskis017d2092020-09-02 10:15:52 -0700279 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700280 Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
Janis Danisevskis017d2092020-09-02 10:15:52 -0700281 map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
282 BinderStatus::ok()
283 ))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700284 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis017d2092020-09-02 10:15:52 -0700285 );
286
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700287 // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700288 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700289 Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
Janis Danisevskise24f3472020-08-12 17:58:49 -0700290 map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700291 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700292 );
293
Janis Danisevskise24f3472020-08-12 17:58:49 -0700294 // All other errors get mapped on System Error.
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700295 assert_eq!(
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700296 Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
Janis Danisevskise24f3472020-08-12 17:58:49 -0700297 map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700298 .map_err(|s| ResponseCode(s.service_specific_error()))
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700299 );
300
301 // Result::Ok variants get passed to the ok handler.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700302 assert_eq!(Ok(ResponseCode::LOCKED), map_or_log_err(nested_ok(ResponseCode::LOCKED), Ok));
303 assert_eq!(
304 Ok(ResponseCode::SYSTEM_ERROR),
305 map_or_log_err(nested_ok(ResponseCode::SYSTEM_ERROR), Ok)
306 );
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700307
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700308 Ok(())
309 }
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000310
311 //Helper function to test whether error cases are handled as expected.
Janis Danisevskise24f3472020-08-12 17:58:49 -0700312 pub fn check_result_contains_error_string<T>(
313 result: anyhow::Result<T>,
314 expected_error_string: &str,
315 ) {
Hasini Gunasingheaf993662020-07-24 18:40:20 +0000316 let error_str = format!(
317 "{:#?}",
318 result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
319 );
320 assert!(
321 error_str.contains(expected_error_string),
322 "The string \"{}\" should contain \"{}\"",
323 error_str,
324 expected_error_string
325 );
326 }
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700327} // mod tests