• 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 //!
17 //! Here are some important types and helper functions:
18 //!
19 //! `Error` type encapsulate Keystore, Keymint, and Binder errors. It is used internally by
20 //! Keystore to diagnose error conditions that need to be reported to the client.
21 //!
22 //! `SerializedError` is used send error codes on the wire.
23 //!
24 //! `into_[logged_]binder` is a convenience method used to convert `anyhow::Error` into
25 //! `SerializedError` wire type.
26 //!
27 //! Keystore functions should use `anyhow::Result` to return error conditions, and context should
28 //! be added every time an error is forwarded.
29 
30 pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
31 use android_security_rkp_aidl::aidl::android::security::rkp::IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode;
32 pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
33 use android_system_keystore2::binder::{
34     ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
35 };
36 use keystore2_selinux as selinux;
37 use rkpd_client::Error as RkpdError;
38 use std::cmp::PartialEq;
39 use std::ffi::CString;
40 
41 /// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
42 /// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
43 #[derive(Debug, thiserror::Error, PartialEq, Eq)]
44 pub enum Error {
45     /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
46     #[error("Error::Rc({0:?})")]
47     Rc(ResponseCode),
48     /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
49     #[error("Error::Km({0:?})")]
50     Km(ErrorCode),
51     /// Wraps a Binder exception code other than a service specific exception.
52     #[error("Binder exception code {0:?}, {1:?}")]
53     Binder(ExceptionCode, i32),
54     /// Wraps a Binder status code.
55     #[error("Binder transaction error {0:?}")]
56     BinderTransaction(StatusCode),
57 }
58 
59 impl Error {
60     /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
sys() -> Self61     pub fn sys() -> Self {
62         Error::Rc(ResponseCode::SYSTEM_ERROR)
63     }
64 
65     /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED)`
perm() -> Self66     pub fn perm() -> Self {
67         Error::Rc(ResponseCode::PERMISSION_DENIED)
68     }
69 }
70 
71 impl From<RkpdError> for Error {
from(e: RkpdError) -> Self72     fn from(e: RkpdError) -> Self {
73         match e {
74             RkpdError::RequestCancelled | RkpdError::GetRegistrationFailed => {
75                 Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
76             }
77             RkpdError::GetKeyFailed(e) => {
78                 let response_code = match e {
79                     GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
80                     GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
81                     GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
82                         ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
83                     }
84                     GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
85                         ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
86                     }
87                     _ => {
88                         log::error!("Unexpected get key error from rkpd: {e:?}");
89                         ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
90                     }
91                 };
92                 Error::Rc(response_code)
93             }
94             RkpdError::RetryableTimeout => Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
95             RkpdError::StoreUpgradedKeyFailed | RkpdError::Timeout => {
96                 Error::Rc(ResponseCode::SYSTEM_ERROR)
97             }
98             RkpdError::BinderTransaction(s) => Error::BinderTransaction(s),
99         }
100     }
101 }
102 
103 /// Maps an `rkpd_client::Error` that is wrapped with an `anyhow::Error` to a keystore2 `Error`.
wrapped_rkpd_error_to_ks_error(e: &anyhow::Error) -> Error104 pub fn wrapped_rkpd_error_to_ks_error(e: &anyhow::Error) -> Error {
105     match e.downcast_ref::<RkpdError>() {
106         Some(e) => Error::from(*e),
107         None => {
108             log::error!("Failed to downcast the anyhow::Error to rkpd_client::Error: {e:?}");
109             Error::Rc(ResponseCode::SYSTEM_ERROR)
110         }
111     }
112 }
113 
114 /// Helper function to map the binder status we get from calls into KeyMint
115 /// to a Keystore Error. We don't create an anyhow error here to make
116 /// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
117 /// when diagnosing authentication requirements, update requirements, and running
118 /// out of operation slots.
map_km_error<T>(r: BinderResult<T>) -> Result<T, Error>119 pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
120     r.map_err(|s| {
121         match s.exception_code() {
122             ExceptionCode::SERVICE_SPECIFIC => {
123                 let se = s.service_specific_error();
124                 if se < 0 {
125                     // Negative service specific errors are KM error codes.
126                     Error::Km(ErrorCode(s.service_specific_error()))
127                 } else {
128                     // Non negative error codes cannot be KM error codes.
129                     // So we create an `Error::Binder` variant to preserve
130                     // the service specific error code for logging.
131                     Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
132                 }
133             }
134             // We create `Error::Binder` to preserve the exception code
135             // for logging.
136             e_code => Error::Binder(e_code, 0),
137         }
138     })
139 }
140 
141 /// This function is similar to map_km_error only that we don't expect
142 /// any KeyMint error codes, we simply preserve the exception code and optional
143 /// service specific exception.
map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error>144 pub fn map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error> {
145     r.map_err(|s| match s.exception_code() {
146         ExceptionCode::SERVICE_SPECIFIC => {
147             let se = s.service_specific_error();
148             Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
149         }
150         ExceptionCode::TRANSACTION_FAILED => {
151             let e = s.transaction_error();
152             Error::BinderTransaction(e)
153         }
154         e_code => Error::Binder(e_code, 0),
155     })
156 }
157 
158 /// This function maps a status code onto a Keystore Error.
map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error>159 pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
160     r.map_err(Error::BinderTransaction)
161 }
162 
163 /// Convert an [`anyhow::Error`] to a [`binder::Status`], logging the value
164 /// along the way (except if it is `KEY_NOT_FOUND`).
into_logged_binder(e: anyhow::Error) -> BinderStatus165 pub fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
166     // Log everything except key not found.
167     if !matches!(
168         e.root_cause().downcast_ref::<Error>(),
169         Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
170     ) {
171         log::error!("{:?}", e);
172     }
173     into_binder(e)
174 }
175 
176 /// This function turns an anyhow error into an optional CString.
177 /// This is especially useful to add a message string to a service specific error.
178 /// If the formatted string was not convertible because it contained a nul byte,
179 /// None is returned and a warning is logged.
anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString>180 pub fn anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString> {
181     match CString::new(format!("{:?}", e)) {
182         Ok(msg) => Some(msg),
183         Err(_) => {
184             log::warn!("Cannot convert error message to CStr. It contained a nul byte.");
185             None
186         }
187     }
188 }
189 
190 /// Convert an [`anyhow::Error`] to a [`binder::Status`].
into_binder(e: anyhow::Error) -> binder::Status191 pub fn into_binder(e: anyhow::Error) -> binder::Status {
192     let rc = anyhow_error_to_serialized_error(&e);
193     BinderStatus::new_service_specific_error(rc.0, anyhow_error_to_cstring(&e).as_deref())
194 }
195 
196 /// This type is used to send error codes on the wire.
197 ///
198 /// Errors are squashed into one number space using following rules:
199 /// - All Keystore and Keymint errors codes are identity mapped. It's possible because by
200 ///   convention Keystore `ResponseCode` errors are positive, and Keymint `ErrorCode` errors are
201 ///   negative.
202 /// - `selinux::Error::PermissionDenied` is mapped to `ResponseCode::PERMISSION_DENIED`.
203 /// - All other error conditions, e.g. Binder errors, are mapped to `ResponseCode::SYSTEM_ERROR`.
204 ///
205 /// The type should be used to forward all error codes to clients of Keystore AIDL interface and to
206 /// metrics events.
207 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
208 pub struct SerializedError(pub i32);
209 
210 /// Returns a SerializedError given a reference to Error.
error_to_serialized_error(e: &Error) -> SerializedError211 pub fn error_to_serialized_error(e: &Error) -> SerializedError {
212     match e {
213         Error::Rc(rcode) => SerializedError(rcode.0),
214         Error::Km(ec) => SerializedError(ec.0),
215         // Binder errors are reported as system error.
216         Error::Binder(_, _) | Error::BinderTransaction(_) => {
217             SerializedError(ResponseCode::SYSTEM_ERROR.0)
218         }
219     }
220 }
221 
222 /// Returns a SerializedError given a reference to anyhow::Error.
anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError223 pub fn anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError {
224     let root_cause = e.root_cause();
225     match root_cause.downcast_ref::<Error>() {
226         Some(e) => error_to_serialized_error(e),
227         None => match root_cause.downcast_ref::<selinux::Error>() {
228             Some(selinux::Error::PermissionDenied) => {
229                 SerializedError(ResponseCode::PERMISSION_DENIED.0)
230             }
231             _ => SerializedError(ResponseCode::SYSTEM_ERROR.0),
232         },
233     }
234 }
235 
236 #[cfg(test)]
237 pub mod tests {
238 
239     use super::*;
240     use android_system_keystore2::binder::{
241         ExceptionCode, Result as BinderResult, Status as BinderStatus,
242     };
243     use anyhow::{anyhow, Context};
244 
nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()>245     fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
246         Err(anyhow!(Error::Rc(rc))).context("nested nested rc")
247     }
248 
nested_rc(rc: ResponseCode) -> anyhow::Result<()>249     fn nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
250         nested_nested_rc(rc).context("nested rc")
251     }
252 
nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()>253     fn nested_nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
254         Err(anyhow!(Error::Km(ec))).context("nested nested ec")
255     }
256 
nested_ec(ec: ErrorCode) -> anyhow::Result<()>257     fn nested_ec(ec: ErrorCode) -> anyhow::Result<()> {
258         nested_nested_ec(ec).context("nested ec")
259     }
260 
nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode>261     fn nested_nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
262         Ok(rc)
263     }
264 
nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode>265     fn nested_ok(rc: ResponseCode) -> anyhow::Result<ResponseCode> {
266         nested_nested_ok(rc).context("nested ok")
267     }
268 
nested_nested_selinux_perm() -> anyhow::Result<()>269     fn nested_nested_selinux_perm() -> anyhow::Result<()> {
270         Err(anyhow!(selinux::Error::perm())).context("nested nexted selinux permission denied")
271     }
272 
nested_selinux_perm() -> anyhow::Result<()>273     fn nested_selinux_perm() -> anyhow::Result<()> {
274         nested_nested_selinux_perm().context("nested selinux permission denied")
275     }
276 
277     #[derive(Debug, thiserror::Error)]
278     enum TestError {
279         #[error("TestError::Fail")]
280         Fail = 0,
281     }
282 
nested_nested_other_error() -> anyhow::Result<()>283     fn nested_nested_other_error() -> anyhow::Result<()> {
284         Err(anyhow!(TestError::Fail)).context("nested nested other error")
285     }
286 
nested_other_error() -> anyhow::Result<()>287     fn nested_other_error() -> anyhow::Result<()> {
288         nested_nested_other_error().context("nested other error")
289     }
290 
binder_sse_error(sse: i32) -> BinderResult<()>291     fn binder_sse_error(sse: i32) -> BinderResult<()> {
292         Err(BinderStatus::new_service_specific_error(sse, None))
293     }
294 
binder_exception(ex: ExceptionCode) -> BinderResult<()>295     fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
296         Err(BinderStatus::new_exception(ex, None))
297     }
298 
299     #[test]
keystore_error_test() -> anyhow::Result<(), String>300     fn keystore_error_test() -> anyhow::Result<(), String> {
301         android_logger::init_once(
302             android_logger::Config::default()
303                 .with_tag("keystore_error_tests")
304                 .with_max_level(log::LevelFilter::Debug),
305         );
306         // All Error::Rc(x) get mapped on a service specific error
307         // code of x.
308         for rc in ResponseCode::LOCKED.0..ResponseCode::BACKEND_BUSY.0 {
309             assert_eq!(
310                 Result::<(), i32>::Err(rc),
311                 nested_rc(ResponseCode(rc))
312                     .map_err(into_logged_binder)
313                     .map_err(|s| s.service_specific_error())
314             );
315         }
316 
317         // All Keystore Error::Km(x) get mapped on a service
318         // specific error of x.
319         for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
320             assert_eq!(
321                 Result::<(), i32>::Err(ec),
322                 nested_ec(ErrorCode(ec))
323                     .map_err(into_logged_binder)
324                     .map_err(|s| s.service_specific_error())
325             );
326         }
327 
328         // All Keymint errors x received through a Binder Result get mapped on
329         // a service specific error of x.
330         for ec in ErrorCode::UNKNOWN_ERROR.0..ErrorCode::ROOT_OF_TRUST_ALREADY_SET.0 {
331             assert_eq!(
332                 Result::<(), i32>::Err(ec),
333                 map_km_error(binder_sse_error(ec))
334                     .with_context(|| format!("Km error code: {}.", ec))
335                     .map_err(into_logged_binder)
336                     .map_err(|s| s.service_specific_error())
337             );
338         }
339 
340         // map_km_error creates an Error::Binder variant storing
341         // ExceptionCode::SERVICE_SPECIFIC and the given
342         // service specific error.
343         let sse = map_km_error(binder_sse_error(1));
344         assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
345         // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
346         assert_eq!(
347             Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
348             sse.context("Non negative service specific error.")
349                 .map_err(into_logged_binder)
350                 .map_err(|s| ResponseCode(s.service_specific_error()))
351         );
352 
353         // map_km_error creates a Error::Binder variant storing the given exception code.
354         let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
355         assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
356         // into_binder then maps it on a service specific error of ResponseCode::SYSTEM_ERROR.
357         assert_eq!(
358             Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
359             binder_exception
360                 .context("Binder Exception.")
361                 .map_err(into_logged_binder)
362                 .map_err(|s| ResponseCode(s.service_specific_error()))
363         );
364 
365         // selinux::Error::Perm() needs to be mapped to ResponseCode::PERMISSION_DENIED
366         assert_eq!(
367             Result::<(), ResponseCode>::Err(ResponseCode::PERMISSION_DENIED),
368             nested_selinux_perm()
369                 .map_err(into_logged_binder)
370                 .map_err(|s| ResponseCode(s.service_specific_error()))
371         );
372 
373         // All other errors get mapped on System Error.
374         assert_eq!(
375             Result::<(), ResponseCode>::Err(ResponseCode::SYSTEM_ERROR),
376             nested_other_error()
377                 .map_err(into_logged_binder)
378                 .map_err(|s| ResponseCode(s.service_specific_error()))
379         );
380 
381         // Result::Ok variants get passed to the ok handler.
382         assert_eq!(
383             Ok(ResponseCode::LOCKED),
384             nested_ok(ResponseCode::LOCKED).map_err(into_logged_binder)
385         );
386         assert_eq!(
387             Ok(ResponseCode::SYSTEM_ERROR),
388             nested_ok(ResponseCode::SYSTEM_ERROR).map_err(into_logged_binder)
389         );
390 
391         Ok(())
392     }
393 
394     //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, )395     pub fn check_result_contains_error_string<T>(
396         result: anyhow::Result<T>,
397         expected_error_string: &str,
398     ) {
399         let error_str = format!(
400             "{:#?}",
401             result.err().unwrap_or_else(|| panic!("Expected the error: {}", expected_error_string))
402         );
403         assert!(
404             error_str.contains(expected_error_string),
405             "The string \"{}\" should contain \"{}\"",
406             error_str,
407             expected_error_string
408         );
409     }
410 
411     #[test]
rkpd_error_is_in_sync_with_response_code()412     fn rkpd_error_is_in_sync_with_response_code() {
413         let error_mapping = [
414             (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
415             (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
416             (
417                 RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN),
418                 ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
419             ),
420             (
421                 RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT),
422                 ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
423             ),
424             (
425                 RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY),
426                 ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
427             ),
428             (
429                 RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH),
430                 ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
431             ),
432             (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR),
433             (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
434             (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR),
435         ];
436         for (rkpd_error, expected_response_code) in error_mapping {
437             let e: Error = rkpd_error.into();
438             assert_eq!(e, Error::Rc(expected_response_code));
439         }
440     }
441 } // mod tests
442