xref: /aosp_15_r20/system/security/keystore2/src/error.rs (revision e1997b9af69e3155ead6e072d106a0077849ffba)
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 postprocessor_client::Error as PostProcessorError;
38 use rkpd_client::Error as RkpdError;
39 use std::cmp::PartialEq;
40 use std::ffi::CString;
41 
42 #[cfg(test)]
43 pub mod tests;
44 
45 /// This is the main Keystore error type. It wraps the Keystore `ResponseCode` generated
46 /// from AIDL in the `Rc` variant and Keymint `ErrorCode` in the Km variant.
47 #[derive(Debug, thiserror::Error, PartialEq, Eq)]
48 pub enum Error {
49     /// Wraps a Keystore `ResponseCode` as defined by the Keystore AIDL interface specification.
50     #[error("Error::Rc({0:?})")]
51     Rc(ResponseCode),
52     /// Wraps a Keymint `ErrorCode` as defined by the Keymint AIDL interface specification.
53     #[error("Error::Km({0:?})")]
54     Km(ErrorCode),
55     /// Wraps a Binder exception code other than a service specific exception.
56     #[error("Binder exception code {0:?}, {1:?}")]
57     Binder(ExceptionCode, i32),
58     /// Wraps a Binder status code.
59     #[error("Binder transaction error {0:?}")]
60     BinderTransaction(StatusCode),
61 }
62 
63 impl Error {
64     /// Short hand for `Error::Rc(ResponseCode::SYSTEM_ERROR)`
sys() -> Self65     pub fn sys() -> Self {
66         Error::Rc(ResponseCode::SYSTEM_ERROR)
67     }
68 
69     /// Short hand for `Error::Rc(ResponseCode::PERMISSION_DENIED)`
perm() -> Self70     pub fn perm() -> Self {
71         Error::Rc(ResponseCode::PERMISSION_DENIED)
72     }
73 }
74 
75 impl From<RkpdError> for Error {
from(e: RkpdError) -> Self76     fn from(e: RkpdError) -> Self {
77         match e {
78             RkpdError::RequestCancelled | RkpdError::GetRegistrationFailed => {
79                 Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
80             }
81             RkpdError::GetKeyFailed(e) => {
82                 let response_code = match e {
83                     GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
84                     GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
85                     GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
86                         ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
87                     }
88                     GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
89                         ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
90                     }
91                     _ => {
92                         log::error!("Unexpected get key error from rkpd: {e:?}");
93                         ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
94                     }
95                 };
96                 Error::Rc(response_code)
97             }
98             RkpdError::RetryableTimeout => Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
99             RkpdError::StoreUpgradedKeyFailed | RkpdError::Timeout => {
100                 Error::Rc(ResponseCode::SYSTEM_ERROR)
101             }
102             RkpdError::BinderTransaction(s) => Error::BinderTransaction(s),
103         }
104     }
105 }
106 
107 impl From<PostProcessorError> for Error {
from(e: PostProcessorError) -> Self108     fn from(e: PostProcessorError) -> Self {
109         match e {
110             PostProcessorError(s) => Error::BinderTransaction(s),
111         }
112     }
113 }
114 
115 /// 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) -> Error116 pub fn wrapped_rkpd_error_to_ks_error(e: &anyhow::Error) -> Error {
117     match e.downcast_ref::<RkpdError>() {
118         Some(e) => Error::from(*e),
119         None => {
120             log::error!("Failed to downcast the anyhow::Error to rkpd_client::Error: {e:?}");
121             Error::Rc(ResponseCode::SYSTEM_ERROR)
122         }
123     }
124 }
125 
126 /// Helper function to map the binder status we get from calls into KeyMint
127 /// to a Keystore Error. We don't create an anyhow error here to make
128 /// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
129 /// when diagnosing authentication requirements, update requirements, and running
130 /// out of operation slots.
map_km_error<T>(r: BinderResult<T>) -> Result<T, Error>131 pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
132     r.map_err(|s| {
133         match s.exception_code() {
134             ExceptionCode::SERVICE_SPECIFIC => {
135                 let se = s.service_specific_error();
136                 if se < 0 {
137                     // Negative service specific errors are KM error codes.
138                     Error::Km(ErrorCode(s.service_specific_error()))
139                 } else {
140                     // Non negative error codes cannot be KM error codes.
141                     // So we create an `Error::Binder` variant to preserve
142                     // the service specific error code for logging.
143                     Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
144                 }
145             }
146             // We create `Error::Binder` to preserve the exception code
147             // for logging.
148             e_code => Error::Binder(e_code, 0),
149         }
150     })
151 }
152 
153 /// This function is similar to map_km_error only that we don't expect
154 /// any KeyMint error codes, we simply preserve the exception code and optional
155 /// service specific exception.
map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error>156 pub fn map_binder_status<T>(r: BinderResult<T>) -> Result<T, Error> {
157     r.map_err(|s| match s.exception_code() {
158         ExceptionCode::SERVICE_SPECIFIC => {
159             let se = s.service_specific_error();
160             Error::Binder(ExceptionCode::SERVICE_SPECIFIC, se)
161         }
162         ExceptionCode::TRANSACTION_FAILED => {
163             let e = s.transaction_error();
164             Error::BinderTransaction(e)
165         }
166         e_code => Error::Binder(e_code, 0),
167     })
168 }
169 
170 /// This function maps a status code onto a Keystore Error.
map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error>171 pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
172     r.map_err(Error::BinderTransaction)
173 }
174 
175 /// Convert an [`anyhow::Error`] to a [`binder::Status`], logging the value
176 /// along the way (except if it is `KEY_NOT_FOUND`).
into_logged_binder(e: anyhow::Error) -> BinderStatus177 pub fn into_logged_binder(e: anyhow::Error) -> BinderStatus {
178     // Log everything except key not found.
179     if !matches!(
180         e.root_cause().downcast_ref::<Error>(),
181         Some(Error::Rc(ResponseCode::KEY_NOT_FOUND))
182     ) {
183         log::error!("{:?}", e);
184     }
185     into_binder(e)
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 /// Convert an [`anyhow::Error`] to a [`binder::Status`].
into_binder(e: anyhow::Error) -> binder::Status203 pub fn into_binder(e: anyhow::Error) -> binder::Status {
204     let rc = anyhow_error_to_serialized_error(&e);
205     BinderStatus::new_service_specific_error(rc.0, anyhow_error_to_cstring(&e).as_deref())
206 }
207 
208 /// This type is used to send error codes on the wire.
209 ///
210 /// Errors are squashed into one number space using following rules:
211 /// - All Keystore and Keymint errors codes are identity mapped. It's possible because by
212 ///   convention Keystore `ResponseCode` errors are positive, and Keymint `ErrorCode` errors are
213 ///   negative.
214 /// - `selinux::Error::PermissionDenied` is mapped to `ResponseCode::PERMISSION_DENIED`.
215 /// - All other error conditions, e.g. Binder errors, are mapped to `ResponseCode::SYSTEM_ERROR`.
216 ///
217 /// The type should be used to forward all error codes to clients of Keystore AIDL interface and to
218 /// metrics events.
219 #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
220 pub struct SerializedError(pub i32);
221 
222 /// Returns a SerializedError given a reference to Error.
error_to_serialized_error(e: &Error) -> SerializedError223 pub fn error_to_serialized_error(e: &Error) -> SerializedError {
224     match e {
225         Error::Rc(rcode) => SerializedError(rcode.0),
226         Error::Km(ec) => SerializedError(ec.0),
227         // Binder errors are reported as system error.
228         Error::Binder(_, _) | Error::BinderTransaction(_) => {
229             SerializedError(ResponseCode::SYSTEM_ERROR.0)
230         }
231     }
232 }
233 
234 /// Returns a SerializedError given a reference to anyhow::Error.
anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError235 pub fn anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError {
236     let root_cause = e.root_cause();
237     match root_cause.downcast_ref::<Error>() {
238         Some(e) => error_to_serialized_error(e),
239         None => match root_cause.downcast_ref::<selinux::Error>() {
240             Some(selinux::Error::PermissionDenied) => {
241                 SerializedError(ResponseCode::PERMISSION_DENIED.0)
242             }
243             _ => SerializedError(ResponseCode::SYSTEM_ERROR.0),
244         },
245     }
246 }
247