• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // 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::SYSTEM_ERROR` 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 
33 pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
34 pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
35 use android_system_keystore2::binder::{
36     ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
37 };
38 use keystore2_selinux as selinux;
39 use std::cmp::PartialEq;
40 use std::ffi::CString;
41 
42 /// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
43 /// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
44 #[derive(Debug, thiserror::Error, PartialEq)]
45 pub enum Error {
46     /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
47     #[error("Error::Rc({0:?})")]
48     Rc(ResponseCode),
49     /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
50     #[error("Error::Km({0:?})")]
51     Km(ErrorCode),
52     /// Wraps a Binder exception code other than a service specific exception.
53     #[error("Binder exception code {0:?}, {1:?}")]
54     Binder(ExceptionCode, i32),
55     /// Wraps a Binder status code.
56     #[error("Binder transaction error {0:?}")]
57     BinderTransaction(StatusCode),
58     /// Wraps a Remote Provisioning ErrorCode as defined by the IRemotelyProvisionedComponent
59     /// AIDL interface spec.
60     #[error("Error::Rp({0:?})")]
61     Rp(ErrorCode),
62 }
63 
64 impl Error {
65     /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
sys() -> Self66     pub fn sys() -> Self {
67         Error::Rc(ResponseCode::SYSTEM_ERROR)
68     }
69 
70     /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED)`
perm() -> Self71     pub fn perm() -> Self {
72         Error::Rc(ResponseCode::PERMISSION_DENIED)
73     }
74 
75     /// Short hand for `Error::Rc(ResponseCode::OUT_OF_KEYS)`
out_of_keys() -> Self76     pub fn out_of_keys() -> Self {
77         Error::Rc(ResponseCode::OUT_OF_KEYS)
78     }
79 }
80 
81 /// Helper function to map the binder status we get from calls into KeyMint
82 /// to a Keystore Error. We don't create an anyhow error here to make
83 /// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
84 /// when diagnosing authentication requirements, update requirements, and running
85 /// out of operation slots.
map_km_error<T>(r: BinderResult<T>) -> Result<T, Error>86 pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
87     r.map_err(|s| {
88         match s.exception_code() {
89             ExceptionCode::SERVICE_SPECIFIC => {
90                 let se = s.service_specific_error();
91                 if se < 0 {
92                     // Negative service specific errors are KM error codes.
93                     Error::Km(ErrorCode(s.service_specific_error()))
94                 } else {
95                     // Non negative error codes cannot be KM error codes.
96                     // So we create an `Error::Binder` variant to preserve
97                     // the service specific error code for logging.
98                     // `map_or_log_err` will map this on a system error,
99                     // but not before logging the details to logcat.
100                     Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
101                 }
102             }
103             // We create `Error::Binder` to preserve the exception code
104             // for logging.
105             // `map_or_log_err` will map this on a system error.
106             e_code => Error::Binder(e_code, 0),
107         }
108     })
109 }
110 
111 /// Helper function to map the binder status we get from calls into a RemotelyProvisionedComponent
112 /// to a Keystore Error. We don't create an anyhow error here to make
113 /// it easier to evaluate service specific errors.
map_rem_prov_error<T>(r: BinderResult<T>) -> Result<T, Error>114 pub fn map_rem_prov_error<T>(r: BinderResult<T>) -> Result<T, Error> {
115     r.map_err(|s| match s.exception_code() {
116         ExceptionCode::SERVICE_SPECIFIC => Error::Rp(ErrorCode(s.service_specific_error())),
117         e_code => Error::Binder(e_code, 0),
118     })
119 }
120 
121 /// This function is similar to map_km_error only that we don't expect
122 /// any KeyMint error codes, we simply preserve the exception code and optional
123 /// service specific exception.
map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error>124 pub fn map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error> {
125     r.map_err(|s| match s.exception_code() {
126         ExceptionCode::SERVICE_SPECIFIC => {
127             let se = s.service_specific_error();
128             Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
129         }
130         ExceptionCode::TRANSACTION_FAILED => {
131             let e = s.transaction_error();
132             Error::BinderTransaction(e)
133         }
134         e_code => Error::Binder(e_code, 0),
135     })
136 }
137 
138 /// This function maps a status code onto a Keystore Error.
map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error>139 pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
140     r.map_err(Error::BinderTransaction)
141 }
142 
143 /// This function should be used by Keystore service calls to translate error conditions
144 /// into service specific exceptions.
145 ///
146 /// All error conditions get logged by this function, except for KEY_NOT_FOUND error.
147 ///
148 /// All `Error::Rc(x)` and `Error::Km(x)` variants get mapped onto a service specific error
149 /// code of x. This is possible because KeyMint `ErrorCode` errors are always negative and
150 /// `ResponseCode` codes are always positive.
151 /// `selinux::Error::PermissionDenied` is mapped on `ResponseCode::PERMISSION_DENIED`.
152 ///
153 /// All non `Error` error conditions and the Error::Binder variant get mapped onto
154 /// ResponseCode::SYSTEM_ERROR`.
155 ///
156 /// `handle_ok` will be called if `result` is `Ok(value)` where `value` will be passed
157 /// as argument to `handle_ok`. `handle_ok` must generate a `BinderResult<T>`, but it
158 /// typically returns Ok(value).
159 ///
160 /// # Examples
161 ///
162 /// ```
163 /// fn loadKey() -> anyhow::Result<Vec<u8>> {
164 ///     if (good_but_auth_required) {
165 ///         Ok(vec!['k', 'e', 'y'])
166 ///     } else {
167 ///         Err(anyhow!(Error::Rc(ResponseCode::KEY_NOT_FOUND)))
168 ///     }
169 /// }
170 ///
171 /// map_or_log_err(loadKey(), Ok)
172 /// ```
map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T> where F: FnOnce(U) -> BinderResult<T>,173 pub fn map_or_log_err<T, U, F>(result: anyhow::Result<U>, handle_ok: F) -> BinderResult<T>
174 where
175     F: FnOnce(U) -> BinderResult<T>,
176 {
177     map_err_with(
178         result,
179         |e| {
180             // Make the key not found errors silent.
181             if !matches!(
182                 e.root_cause().downcast_ref::<Error>(),
183                 Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
184             ) {
185                 log::error!("{:?}", e);
186             }
187             e
188         },
189         handle_ok,
190     )
191 }
192 
193 /// This function turns an anyhow error into an optional CString.
194 /// This is especially useful to add a message string to a service specific error.
195 /// If the formatted string was not convertible because it contained a nul byte,
196 /// None is returned and a warning is logged.
anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString>197 pub fn anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString> {
198     match CString::new(format!("{:?}", e)) {
199         Ok(msg) => Some(msg),
200         Err(_) => {
201             log::warn!("Cannot convert error message to CStr. It contained a nul byte.");
202             None
203         }
204     }
205 }
206 
207 /// This function behaves similar to map_or_log_error, but it does not log the errors, instead
208 /// it calls map_err on the error before mapping it to a binder result allowing callers to
209 /// log or transform the error before mapping it.
map_err_with<T, U, F1, F2>( result: anyhow::Result<U>, map_err: F1, handle_ok: F2, ) -> BinderResult<T> where F1: FnOnce(anyhow::Error) -> anyhow::Error, F2: FnOnce(U) -> BinderResult<T>,210 pub fn map_err_with<T, U, F1, F2>(
211     result: anyhow::Result<U>,
212     map_err: F1,
213     handle_ok: F2,
214 ) -> BinderResult<T>
215 where
216     F1: FnOnce(anyhow::Error) -> anyhow::Error,
217     F2: FnOnce(U) -> BinderResult<T>,
218 {
219     result.map_or_else(
220         |e| {
221             let e = map_err(e);
222             let rc = get_error_code(&e);
223             Err(BinderStatus::new_service_specific_error(
224                 rc,
225                 anyhow_error_to_cstring(&e).as_deref(),
226             ))
227         },
228         handle_ok,
229     )
230 }
231 
232 /// Returns the error code given a reference to the error
get_error_code(e: &anyhow::Error) -> i32233 pub fn get_error_code(e: &anyhow::Error) -> i32 {
234     let root_cause = e.root_cause();
235     match root_cause.downcast_ref::<Error>() {
236         Some(Error::Rc(rcode)) => rcode.0,
237         Some(Error::Km(ec)) => ec.0,
238         Some(Error::Rp(_)) => ResponseCode::SYSTEM_ERROR.0,
239         // If an Error::Binder reaches this stage we report a system error.
240         // The exception code and possible service specific error will be
241         // printed in the error log above.
242         Some(Error::Binder(_, _)) | Some(Error::BinderTransaction(_)) => {
243             ResponseCode::SYSTEM_ERROR.0
244         }
245         None => match root_cause.downcast_ref::<selinux::Error>() {
246             Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
247             _ => ResponseCode::SYSTEM_ERROR.0,
248         },
249     }
250 }
251 
252 #[cfg(test)]
253 pub mod tests {
254 
255     use super::*;
256     use android_system_keystore2::binder::{
257         ExceptionCode, Result as BinderResult, Status as BinderStatus,
258     };
259     use anyhow::{anyhow, Context};
260 
nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()>261     fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
262         Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
263     }
264 
nested_rc(rc: ResponseCode) -> anyhow::Result<()>265     fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
266         nested_nested_rc(rc).context("nested rc")
267     }
268 
nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()>269     fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
270         Err(anyhow!(Error::Km(ec))).context("nested nested ec")
271     }
272 
nested_ec(ec: ErrorCode) -> anyhow::Result<()>273     fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
274         nested_nested_ec(ec).context("nested ec")
275     }
276 
nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode>277     fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
278         Ok(rc)
279     }
280 
nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode>281     fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
282         nested_nested_ok(rc).context("nested ok")
283     }
284 
nested_nested_selinux_perm() -> anyhow::Result<()>285     fn nested_nested_selinux_perm() -> anyhow::Result<()> {
286         Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
287     }
288 
nested_selinux_perm() -> anyhow::Result<()>289     fn nested_selinux_perm() -> anyhow::Result<()> {
290         nested_nested_selinux_perm().context("nested selinux permission denied")
291     }
292 
293     #[derive(Debug, thiserror::Error)]
294     enum TestError {
295         #[error("TestError::Fail")]
296         Fail = 0,
297     }
298 
nested_nested_other_error() -> anyhow::Result<()>299     fn nested_nested_other_error() -> anyhow::Result<()> {
300         Err(anyhow!(TestError::Fail)).context("nested nested other error")
301     }
302 
nested_other_error() -> anyhow::Result<()>303     fn nested_other_error() -> anyhow::Result<()> {
304         nested_nested_other_error().context("nested other error")
305     }
306 
binder_sse_error(sse: i32) -> BinderResult<()>307     fn binder_sse_error(sse: i32) -> BinderResult<()> {
308         Err(BinderStatus::new_service_specific_error(sse, None))
309     }
310 
binder_exception(ex: ExceptionCode) -> BinderResult<()>311     fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
312         Err(BinderStatus::new_exception(ex, None))
313     }
314 
315     #[test]
keystore_error_test() -> anyhow::Result<(), String>316     fn keystore_error_test() -> anyhow::Result<(), String> {
317         android_logger::init_once(
318             android_logger::Config::default()
319                 .with_tag("keystore_error_tests")
320                 .with_min_level(log::Level::Debug),
321         );
322         // All Error::Rc(x) get mapped on a service specific error
323         // code of x.
324         for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
325             assert_eq!(
326                 Result::<(), i32>::Err(rc),
327                 map_or_log_err(nested_rc(ResponseCode(rc)), |_| Err(BinderStatus::ok()))
328                     .map_err(|s| s.service_specific_error())
329             );
330         }
331 
332         // All Keystore Error::Km(x) get mapped on a service
333         // specific error of x.
334         for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
335             assert_eq!(
336                 Result::<(), i32>::Err(ec),
337                 map_or_log_err(nested_ec(ErrorCode(ec)), |_| Err(BinderStatus::ok()))
338                     .map_err(|s| s.service_specific_error())
339             );
340         }
341 
342         // All Keymint errors x received through a Binder Result get mapped on
343         // a service specific error of x.
344         for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
345             assert_eq!(
346                 Result::<(), i32>::Err(ec),
347                 map_or_log_err(
348                     map_km_error(binder_sse_error(ec))
349                         .with_context(|| format!("Km error code: {}.", ec)),
350                     |_| Err(BinderStatus::ok())
351                 )
352                 .map_err(|s| s.service_specific_error())
353             );
354         }
355 
356         // map_km_error creates an Error::Binder variant storing
357         // ExceptionCode::SERVICE_SPECIFIC and the given
358         // service specific error.
359         let sse = map_km_error(binder_sse_error(1));
360         assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
361         // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
362         assert_eq!(
363             Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
364             map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
365                 BinderStatus::ok()
366             ))
367             .map_err(|s| ResponseCode(s.service_specific_error()))
368         );
369 
370         // map_km_error creates a Error::Binder variant storing the given exception code.
371         let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
372         assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
373         // map_or_log_err then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
374         assert_eq!(
375             Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
376             map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
377                 BinderStatus::ok()
378             ))
379             .map_err(|s| ResponseCode(s.service_specific_error()))
380         );
381 
382         // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
383         assert_eq!(
384             Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
385             map_or_log_err(nested_selinux_perm(), |_| Err(BinderStatus::ok()))
386                 .map_err(|s| ResponseCode(s.service_specific_error()))
387         );
388 
389         // All other errors get mapped on System Error.
390         assert_eq!(
391             Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
392             map_or_log_err(nested_other_error(), |_| Err(BinderStatus::ok()))
393                 .map_err(|s| ResponseCode(s.service_specific_error()))
394         );
395 
396         // Result::Ok variants get passed to the ok handler.
397         assert_eq!(Ok(ResponseCode::LOCKED), map_or_log_err(nested_ok(ResponseCode::LOCKED), Ok));
398         assert_eq!(
399             Ok(ResponseCode::SYSTEM_ERROR),
400             map_or_log_err(nested_ok(ResponseCode::SYSTEM_ERROR), Ok)
401         );
402 
403         Ok(())
404     }
405 
406     //Helper function to test whether error cases are handled as expected.
check_result_contains_error_string<T>( result: anyhow::Result<T>, expected_error_string: &str, )407     pub fn check_result_contains_error_string<T>(
408         result: anyhow::Result<T>,
409         expected_error_string: &str,
410     ) {
411         let error_str = format!(
412             "{:#?}",
413             result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
414         );
415         assert!(
416             error_str.contains(expected_error_string),
417             "The string \"{}\" should contain \"{}\"",
418             error_str,
419             expected_error_string
420         );
421     }
422 } // mod tests
423