Keystore2: Handle errors from binder service calls.
This is required for handling Keymint errors received by the Keystore service. Test: keystore2_test Change-Id: I7cf1b0d53db465a738c60594d929944379179836
This commit is contained in:
parent
935e6c6d1b
commit
017d20905d
1 changed files with 92 additions and 2 deletions
|
@ -40,7 +40,9 @@ use android_security_keystore2::aidl::android::security::keystore2::ResponseCode
|
|||
|
||||
use keystore2_selinux as selinux;
|
||||
|
||||
use android_security_keystore2::binder::{Result as BinderResult, Status as BinderStatus};
|
||||
use android_security_keystore2::binder::{
|
||||
ExceptionCode, Result as BinderResult, Status as BinderStatus,
|
||||
};
|
||||
|
||||
/// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
|
||||
/// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
|
||||
|
@ -52,6 +54,9 @@ pub enum Error {
|
|||
/// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
|
||||
#[error("Error::Km({0:?})")]
|
||||
Km(ErrorCode),
|
||||
/// Wraps a Binder exception code other than a service specific exception.
|
||||
#[error("Binder exception code {0:?}, {1:?}")]
|
||||
Binder(ExceptionCode, i32),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
|
@ -66,6 +71,36 @@ impl Error {
|
|||
}
|
||||
}
|
||||
|
||||
/// Helper function to map the binder status we get from calls into KeyMint
|
||||
/// to a Keystore Error. We don't create an anyhow error here to make
|
||||
/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
|
||||
/// when diagnosing authentication requirements, update requirements, and running
|
||||
/// out of operation slots.
|
||||
pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
|
||||
r.map_err(|s| {
|
||||
match s.exception_code() {
|
||||
ExceptionCode::SERVICE_SPECIFIC => {
|
||||
let se = s.service_specific_error();
|
||||
if se < 0 {
|
||||
// Negative service specific errors are KM error codes.
|
||||
Error::Km(s.service_specific_error())
|
||||
} else {
|
||||
// Non negative error codes cannot be KM error codes.
|
||||
// So we create an `Error::Binder` variant to preserve
|
||||
// the service specific error code for logging.
|
||||
// `map_or_log_err` will map this on a system error,
|
||||
// but not before logging the details to logcat.
|
||||
Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
|
||||
}
|
||||
}
|
||||
// We create `Error::Binder` to preserve the exception code
|
||||
// for logging.
|
||||
// `map_or_log_err` will map this on a system error.
|
||||
e_code => Error::Binder(e_code, 0),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// This function should be used by Keystore service calls to translate error conditions
|
||||
/// into `android.security.keystore2.Result` which is imported here as `aidl::Result`
|
||||
/// and newtyped as AidlResult.
|
||||
|
@ -107,6 +142,10 @@ where
|
|||
let rc = match root_cause.downcast_ref::<Error>() {
|
||||
Some(Error::Rc(rcode)) => *rcode,
|
||||
Some(Error::Km(ec)) => *ec,
|
||||
// If an Error::Binder reaches this stage we report a system error.
|
||||
// The exception code and possible service specific error will be
|
||||
// printed in the error log above.
|
||||
Some(Error::Binder(_, _)) => Rc::SystemError,
|
||||
None => match root_cause.downcast_ref::<selinux::Error>() {
|
||||
Some(selinux::Error::PermissionDenied) => Rc::PermissionDenied,
|
||||
_ => Rc::SystemError,
|
||||
|
@ -122,6 +161,9 @@ where
|
|||
pub mod tests {
|
||||
|
||||
use super::*;
|
||||
use android_security_keystore2::binder::{
|
||||
ExceptionCode, Result as BinderResult, Status as BinderStatus,
|
||||
};
|
||||
use anyhow::{anyhow, Context};
|
||||
|
||||
fn nested_nested_rc(rc: ResponseCode) -> anyhow::Result<()> {
|
||||
|
@ -170,6 +212,14 @@ pub mod tests {
|
|||
nested_nested_other_error().context("nested other error")
|
||||
}
|
||||
|
||||
fn binder_sse_error(sse: i32) -> BinderResult<()> {
|
||||
Err(BinderStatus::new_service_specific_error(sse, None))
|
||||
}
|
||||
|
||||
fn binder_exception(ex: ExceptionCode) -> BinderResult<()> {
|
||||
Err(BinderStatus::new_exception(ex, None))
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn keystore_error_test() -> anyhow::Result<(), String> {
|
||||
android_logger::init_once(
|
||||
|
@ -187,7 +237,7 @@ pub mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
// All KeystoreKerror::Km(x) get mapped on a service
|
||||
// All Keystore Error::Km(x) get mapped on a service
|
||||
// specific error of x.
|
||||
for ec in Ec::UNKNOWN_ERROR..Ec::ROOT_OF_TRUST_ALREADY_SET {
|
||||
assert_eq!(
|
||||
|
@ -197,6 +247,46 @@ pub mod tests {
|
|||
);
|
||||
}
|
||||
|
||||
// All Keymint errors x received through a Binder Result get mapped on
|
||||
// a service specific error of x.
|
||||
for ec in Ec::UNKNOWN_ERROR..Ec::ROOT_OF_TRUST_ALREADY_SET {
|
||||
assert_eq!(
|
||||
Result::<(), i32>::Err(ec),
|
||||
map_or_log_err(
|
||||
map_km_error(binder_sse_error(ec))
|
||||
.with_context(|| format!("Km error code: {}.", ec)),
|
||||
|_| Err(BinderStatus::ok())
|
||||
)
|
||||
.map_err(|s| s.service_specific_error())
|
||||
);
|
||||
}
|
||||
|
||||
// map_km_error creates an Error::Binder variant storing
|
||||
// ExceptionCode::SERVICE_SPECIFIC and the given
|
||||
// service specific error.
|
||||
let sse = map_km_error(binder_sse_error(1));
|
||||
assert_eq!(Err(Error::Binder(ExceptionCode::SERVICE_SPECIFIC, 1)), sse);
|
||||
// map_or_log_err then maps it on a service specific error of Rc::SystemError.
|
||||
assert_eq!(
|
||||
Result::<(), i32>::Err(Rc::SystemError),
|
||||
map_or_log_err(sse.context("Non negative service specific error."), |_| Err(
|
||||
BinderStatus::ok()
|
||||
))
|
||||
.map_err(|s| s.service_specific_error())
|
||||
);
|
||||
|
||||
// map_km_error creates a Error::Binder variant storing the given exception code.
|
||||
let binder_exception = map_km_error(binder_exception(ExceptionCode::TRANSACTION_FAILED));
|
||||
assert_eq!(Err(Error::Binder(ExceptionCode::TRANSACTION_FAILED, 0)), binder_exception);
|
||||
// map_or_log_err then maps it on a service specific error of Rc::SystemError.
|
||||
assert_eq!(
|
||||
Result::<(), i32>::Err(Rc::SystemError),
|
||||
map_or_log_err(binder_exception.context("Binder Exception."), |_| Err(
|
||||
BinderStatus::ok()
|
||||
))
|
||||
.map_err(|s| s.service_specific_error())
|
||||
);
|
||||
|
||||
// selinux::Error::Perm() needs to be mapped to Rc::PermissionDenied
|
||||
assert_eq!(
|
||||
Result::<(), i32>::Err(Rc::PermissionDenied),
|
||||
|
|
Loading…
Reference in a new issue