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