blob: 5e80266ea97fda2d7b6ec7e6e3c310e67ee3c091 [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.
Janis Danisevskis7d77a762020-07-20 13:03:31 -070016//!
Tri Vocd6fc7a2023-08-31 11:46:32 -040017//! Here are some important types and helper functions:
Janis Danisevskis7d77a762020-07-20 13:03:31 -070018//!
Tri Vocd6fc7a2023-08-31 11:46:32 -040019//! `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//!
David Drysdaledb7ddde2024-06-07 16:22:49 +010024//! `into_[logged_]binder` is a convenience method used to convert `anyhow::Error` into
25//! `SerializedError` wire type.
Tri Vocd6fc7a2023-08-31 11:46:32 -040026//!
27//! Keystore functions should use `anyhow::Result` to return error conditions, and context should
28//! be added every time an error is forwarded.
Janis Danisevskis7d77a762020-07-20 13:03:31 -070029
Shawn Willden708744a2020-12-11 13:05:27 +000030pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
Alice Wang849cfe42023-11-10 12:43:36 +000031use android_security_rkp_aidl::aidl::android::security::rkp::IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode;
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070032pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070033use android_system_keystore2::binder::{
Janis Danisevskisba998992020-12-29 16:08:40 -080034 ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
Janis Danisevskis017d2092020-09-02 10:15:52 -070035};
Janis Danisevskis2ee014b2021-05-05 14:29:08 -070036use keystore2_selinux as selinux;
Alice Wang01c16b62023-11-07 14:27:49 +000037use rkpd_client::Error as RkpdError;
Janis Danisevskis2ee014b2021-05-05 14:29:08 -070038use std::cmp::PartialEq;
Janis Danisevskisea03cff2021-12-16 08:10:17 -080039use std::ffi::CString;
Janis Danisevskis7d77a762020-07-20 13:03:31 -070040
David Drysdale2566fb32024-07-09 14:46:37 +010041#[cfg(test)]
42pub mod tests;
43
Janis Danisevskis7d77a762020-07-20 13:03:31 -070044/// 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.
Chris Wailes263de9f2022-08-11 15:00:51 -070046#[derive(Debug, thiserror::Error, PartialEq, Eq)]
Janis Danisevskis7d77a762020-07-20 13:03:31 -070047pub 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 Danisevskisba998992020-12-29 16:08:40 -080057 /// Wraps a Binder status code.
58 #[error("Binder transaction error {0:?}")]
59 BinderTransaction(StatusCode),
Janis Danisevskis7d77a762020-07-20 13:03:31 -070060}
61
62impl Error {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070063 /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
Janis Danisevskis7d77a762020-07-20 13:03:31 -070064 pub fn sys() -> Self {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070065 Error::Rc(ResponseCode::SYSTEM_ERROR)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070066 }
67
Seth Moore7ee79f92021-12-07 11:42:49 -080068 /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED)`
Janis Danisevskis7d77a762020-07-20 13:03:31 -070069 pub fn perm() -> Self {
Janis Danisevskisc5b210b2020-09-11 13:27:37 -070070 Error::Rc(ResponseCode::PERMISSION_DENIED)
Janis Danisevskis7d77a762020-07-20 13:03:31 -070071 }
72}
73
Alice Wang849cfe42023-11-10 12:43:36 +000074impl From<RkpdError> for Error {
75 fn from(e: RkpdError) -> Self {
76 match e {
77 RkpdError::RequestCancelled | RkpdError::GetRegistrationFailed => {
78 Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
79 }
80 RkpdError::GetKeyFailed(e) => {
81 let response_code = match e {
82 GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
83 GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
84 GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
85 ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
86 }
87 GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
88 ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
89 }
90 _ => {
91 log::error!("Unexpected get key error from rkpd: {e:?}");
92 ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
93 }
94 };
95 Error::Rc(response_code)
96 }
97 RkpdError::RetryableTimeout => Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
98 RkpdError::StoreUpgradedKeyFailed | RkpdError::Timeout => {
99 Error::Rc(ResponseCode::SYSTEM_ERROR)
100 }
101 RkpdError::BinderTransaction(s) => Error::BinderTransaction(s),
102 }
103 }
104}
105
106/// Maps an `rkpd_client::Error` that is wrapped with an `anyhow::Error` to a keystore2 `Error`.
107pub fn wrapped_rkpd_error_to_ks_error(e: &anyhow::Error) -> Error {
108 match e.downcast_ref::<RkpdError>() {
109 Some(e) => Error::from(*e),
110 None => {
111 log::error!("Failed to downcast the anyhow::Error to rkpd_client::Error: {e:?}");
112 Error::Rc(ResponseCode::SYSTEM_ERROR)
113 }
114 }
115}
116
Janis Danisevskis017d2092020-09-02 10:15:52 -0700117/// Helper function to map the binder status we get from calls into KeyMint
118/// to a Keystore Error. We don't create an anyhow error here to make
119/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
120/// when diagnosing authentication requirements, update requirements, and running
121/// out of operation slots.
122pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
123 r.map_err(|s| {
124 match s.exception_code() {
125 ExceptionCode::SERVICE_SPECIFIC => {
126 let se = s.service_specific_error();
127 if se < 0 {
128 // Negative service specific errors are KM error codes.
Janis Danisevskisc5b210b2020-09-11 13:27:37 -0700129 Error::Km(ErrorCode(s.service_specific_error()))
Janis Danisevskis017d2092020-09-02 10:15:52 -0700130 } else {
131 // Non negative error codes cannot be KM error codes.
132 // So we create an `Error::Binder` variant to preserve
133 // the service specific error code for logging.
Janis Danisevskis017d2092020-09-02 10:15:52 -0700134 Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
135 }
136 }
137 // We create `Error::Binder` to preserve the exception code
138 // for logging.
Janis Danisevskis017d2092020-09-02 10:15:52 -0700139 e_code => Error::Binder(e_code, 0),
140 }
141 })
142}
143
Janis Danisevskisba998992020-12-29 16:08:40 -0800144/// This function is similar to map_km_error only that we don't expect
145/// any KeyMint error codes, we simply preserve the exception code and optional
146/// service specific exception.
147pub fn map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error> {
148 r.map_err(|s| match s.exception_code() {
149 ExceptionCode::SERVICE_SPECIFIC => {
150 let se = s.service_specific_error();
151 Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
152 }
153 ExceptionCode::TRANSACTION_FAILED => {
154 let e = s.transaction_error();
155 Error::BinderTransaction(e)
156 }
157 e_code => Error::Binder(e_code, 0),
158 })
159}
160
161/// This function maps a status code onto a Keystore Error.
162pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
163 r.map_err(Error::BinderTransaction)
164}
165
David Drysdaledb7ddde2024-06-07 16:22:49 +0100166/// Convert an [`anyhow::Error`] to a [`binder::Status`], logging the value
167/// along the way (except if it is `KEY_NOT_FOUND`).
168pub fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
169 // Log everything except key not found.
170 if !matches!(
171 e.root_cause().downcast_ref::<Error>(),
172 Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
173 ) {
174 log::error!("{:?}", e);
175 }
176 into_binder(e)
Janis Danisevskis778245c2021-03-04 15:40:23 -0800177}
178
Janis Danisevskisea03cff2021-12-16 08:10:17 -0800179/// This function turns an anyhow error into an optional CString.
180/// This is especially useful to add a message string to a service specific error.
181/// If the formatted string was not convertible because it contained a nul byte,
182/// None is returned and a warning is logged.
183pub fn anyhow_error_to_cstring(e: &anyhow::Error) -> Option<CString> {
184 match CString::new(format!("{:?}", e)) {
185 Ok(msg) => Some(msg),
186 Err(_) => {
187 log::warn!("Cannot convert error message to CStr. It contained a nul byte.");
188 None
189 }
190 }
191}
192
David Drysdaledb7ddde2024-06-07 16:22:49 +0100193/// Convert an [`anyhow::Error`] to a [`binder::Status`].
194pub fn into_binder(e: anyhow::Error) -> binder::Status {
195 let rc = anyhow_error_to_serialized_error(&e);
196 BinderStatus::new_service_specific_error(rc.0, anyhow_error_to_cstring(&e).as_deref())
Janis Danisevskis7d77a762020-07-20 13:03:31 -0700197}
198
Tri Vocd6fc7a2023-08-31 11:46:32 -0400199/// This type is used to send error codes on the wire.
200///
201/// Errors are squashed into one number space using following rules:
202/// - All Keystore and Keymint errors codes are identity mapped. It's possible because by
203/// convention Keystore `ResponseCode` errors are positive, and Keymint `ErrorCode` errors are
204/// negative.
205/// - `selinux::Error::PermissionDenied` is mapped to `ResponseCode::PERMISSION_DENIED`.
206/// - All other error conditions, e.g. Binder errors, are mapped to `ResponseCode::SYSTEM_ERROR`.
207///
208/// The type should be used to forward all error codes to clients of Keystore AIDL interface and to
209/// metrics events.
210#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
211pub struct SerializedError(pub i32);
212
213/// Returns a SerializedError given a reference to Error.
214pub fn error_to_serialized_error(e: &Error) -> SerializedError {
215 match e {
216 Error::Rc(rcode) => SerializedError(rcode.0),
217 Error::Km(ec) => SerializedError(ec.0),
218 // Binder errors are reported as system error.
219 Error::Binder(_, _) | Error::BinderTransaction(_) => {
220 SerializedError(ResponseCode::SYSTEM_ERROR.0)
221 }
222 }
223}
224
225/// Returns a SerializedError given a reference to anyhow::Error.
226pub fn anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError {
Hasini Gunasingheb7142972021-02-20 03:11:27 +0000227 let root_cause = e.root_cause();
228 match root_cause.downcast_ref::<Error>() {
Tri Vocd6fc7a2023-08-31 11:46:32 -0400229 Some(e) => error_to_serialized_error(e),
Hasini Gunasingheb7142972021-02-20 03:11:27 +0000230 None => match root_cause.downcast_ref::<selinux::Error>() {
Tri Vocd6fc7a2023-08-31 11:46:32 -0400231 Some(selinux::Error::PermissionDenied) => {
232 SerializedError(ResponseCode::PERMISSION_DENIED.0)
233 }
234 _ => SerializedError(ResponseCode::SYSTEM_ERROR.0),
Hasini Gunasingheb7142972021-02-20 03:11:27 +0000235 },
236 }
237}