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 //! This crate implements a safe wrapper around the ConfirmationUI HIDL spec, which
16 //! is the backend for Android Protected Confirmation (APC).
17 //!
18 //! It provides a safe wrapper around a C++ implementation of ConfirmationUI
19 //! client.
20 
21 use keystore2_apc_compat_bindgen::{
22     abortUserConfirmation, closeUserConfirmationService, promptUserConfirmation,
23     tryGetUserConfirmationService, ApcCompatCallback, ApcCompatServiceHandle,
24 };
25 pub use keystore2_apc_compat_bindgen::{
26     ApcCompatUiOptions, APC_COMPAT_ERROR_ABORTED, APC_COMPAT_ERROR_CANCELLED,
27     APC_COMPAT_ERROR_IGNORED, APC_COMPAT_ERROR_OK, APC_COMPAT_ERROR_OPERATION_PENDING,
28     APC_COMPAT_ERROR_SYSTEM_ERROR, INVALID_SERVICE_HANDLE,
29 };
30 use std::{ffi::CString, slice};
31 
32 /// Safe wrapper around the ConfirmationUI HIDL spec.
33 ///
34 /// # Example
35 /// ```
36 /// struct Cb();
37 /// impl ApcHalCallback for Cb {
38 ///     fn result(
39 ///         &self,
40 ///         rc: u32,
41 ///         message: Option<&[u8]>,
42 ///         token: Option<&[u8]>,
43 ///     ) {
44 ///         println!("Callback called with rc: {}, message: {}, token: {}", rc, message, token);
45 ///     }
46 /// };
47 ///
48 /// fn prompt() -> Result<(), u32> {
49 ///     let hal = ApcHal::try_get_service()?;
50 ///     hal.prompt_user_confirmation(Box::new(Cb()), "Do you agree?", b"extra data", "en", 0)?;
51 /// }
52 ///
53 /// ```
54 pub struct ApcHal(ApcCompatServiceHandle);
55 
56 // SAFETY: This is a wrapper around `ApcCompatSession`, which can be used from any thread.
57 unsafe impl Send for ApcHal {}
58 // SAFETY: `ApcCompatSession` can be called simultaneously from different threads because AIDL and
59 // HIDL are thread-safe.
60 unsafe impl Sync for ApcHal {}
61 
62 impl Drop for ApcHal {
drop(&mut self)63     fn drop(&mut self) {
64         // # Safety:
65         // This ends the life cycle of the contained `ApcCompatServiceHandle` owned by this
66         // `ApcHal` object.
67         //
68         // `ApcHal` objects are only created if a valid handle was acquired so self.0 is
69         // always valid when dropped.
70         unsafe {
71             closeUserConfirmationService(self.0);
72         }
73     }
74 }
75 
76 type Callback = dyn FnOnce(u32, Option<&[u8]>, Option<&[u8]>);
77 
confirmation_result_callback( handle: *mut ::std::os::raw::c_void, rc: u32, tbs_message: *const u8, tbs_message_size: usize, confirmation_token: *const u8, confirmation_token_size: usize, )78 extern "C" fn confirmation_result_callback(
79     handle: *mut ::std::os::raw::c_void,
80     rc: u32,
81     tbs_message: *const u8,
82     tbs_message_size: usize,
83     confirmation_token: *const u8,
84     confirmation_token_size: usize,
85 ) {
86     // # Safety:
87     // The C/C++ implementation must pass to us the handle that was created
88     // and assigned to the `ApcCompatCallback::data` field in
89     // `ApcHal::prompt_user_confirmation` below. Also we consume the handle,
90     // by letting `hal_cb` go out of scope with this function call. So
91     // the C/C++ implementation must assure that each `ApcCompatCallback` is only used once.
92     let hal_cb: Box<Box<Callback>> = unsafe { Box::from_raw(handle as *mut Box<Callback>) };
93     let tbs_message = match (tbs_message.is_null(), tbs_message_size) {
94         (true, _) | (_, 0) => None,
95         (false, s) => Some(
96             // # Safety:
97             // If the pointer and size is not nullptr and not 0 respectively, the C/C++
98             // implementation must pass a valid pointer to an allocation of at least size bytes,
99             // and the pointer must be valid until this function returns.
100             unsafe { slice::from_raw_parts(tbs_message, s) },
101         ),
102     };
103     let confirmation_token = match (confirmation_token.is_null(), confirmation_token_size) {
104         (true, _) | (_, 0) => None,
105         (false, s) => Some(
106             // # Safety:
107             // If the pointer and size is not nullptr and not 0 respectively, the C/C++
108             // implementation must pass a valid pointer to an allocation of at least size bytes,
109             // and the pointer must be valid until this function returns.
110             unsafe { slice::from_raw_parts(confirmation_token, s) },
111         ),
112     };
113     hal_cb(rc, tbs_message, confirmation_token)
114 }
115 
116 impl ApcHal {
117     /// Attempts to connect to the APC (confirmationui) backend. On success, it returns an
118     /// initialized `ApcHal` object.
try_get_service() -> Option<Self>119     pub fn try_get_service() -> Option<Self> {
120         // # Safety:
121         // `tryGetUserConfirmationService` returns a valid handle or INVALID_SERVICE_HANDLE.
122         // On success, `ApcHal` takes ownership of this handle and frees it with
123         // `closeUserConfirmationService` when dropped.
124         let handle = unsafe { tryGetUserConfirmationService() };
125         match handle {
126             // SAFETY: This is just a constant.
127             h if h == unsafe { INVALID_SERVICE_HANDLE } => None,
128             h => Some(Self(h)),
129         }
130     }
131 
132     /// Attempts to start a confirmation prompt. The given callback is consumed, and it is
133     /// guaranteed to be called eventually IFF this function returns `APC_COMPAT_ERROR_OK`.
134     ///
135     /// The callback has the following arguments:
136     /// rc: u32 - The reason for the termination which takes one of the values.
137     ///       * `APC_COMPAT_ERROR_OK` - The user confirmed the prompted message.
138     ///       * `APC_COMPAT_ERROR_CANCELLED` - The user rejected the prompted message.
139     ///       * `APC_COMPAT_ERROR_ABORTED` - The prompt was aborted either because the client
140     ///          aborted. the session or an asynchronous system event occurred that ended the
141     ///          prompt prematurely.
142     ///       * `APC_COMPAT_ERROR_SYSTEMERROR` - An unspecified system error occurred. Logs may
143     ///          have more information.
144     ///
145     /// data_confirmed: Option<&[u8]> and
146     /// confirmation_token: Option<&[u8]> hold the confirmed message and the confirmation token
147     /// respectively. They must be `Some()` if `rc == APC_COMPAT_ERROR_OK` and `None` otherwise.
148     ///
149     /// `cb` does not get called if this function returns an error.
150     /// (Thus the allow(unused_must_use))
151     #[allow(unused_must_use)]
prompt_user_confirmation<F>( &self, prompt_text: &str, extra_data: &[u8], locale: &str, ui_opts: ApcCompatUiOptions, cb: F, ) -> Result<(), u32> where F: FnOnce(u32, Option<&[u8]>, Option<&[u8]>) + 'static,152     pub fn prompt_user_confirmation<F>(
153         &self,
154         prompt_text: &str,
155         extra_data: &[u8],
156         locale: &str,
157         ui_opts: ApcCompatUiOptions,
158         cb: F,
159     ) -> Result<(), u32>
160     where
161         F: FnOnce(u32, Option<&[u8]>, Option<&[u8]>) + 'static,
162     {
163         let cb_data_ptr = Box::into_raw(Box::new(Box::new(cb) as Box<Callback>));
164         let cb = ApcCompatCallback {
165             data: cb_data_ptr as *mut std::ffi::c_void,
166             result: Some(confirmation_result_callback),
167         };
168         let prompt_text = CString::new(prompt_text).unwrap();
169         let locale = CString::new(locale).unwrap();
170         // # Safety:
171         // The `ApcCompatCallback` object (`cb`) is passed to the callee by value, and with it
172         // ownership of the `data` field pointer. The data pointer is guaranteed to be valid
173         // until the C/C++ implementation calls the callback. Calling the callback consumes
174         // the data pointer. The C/C++ implementation must not access it after calling the
175         // callback and it must not call the callback a second time.
176         //
177         // The C/C++ must make no assumptions about the life time of the other parameters after
178         // the function returns.
179         let rc = unsafe {
180             promptUserConfirmation(
181                 self.0,
182                 cb,
183                 prompt_text.as_ptr(),
184                 extra_data.as_ptr(),
185                 extra_data.len(),
186                 locale.as_ptr(),
187                 ui_opts,
188             )
189         };
190         match rc {
191             APC_COMPAT_ERROR_OK => Ok(()),
192             rc => {
193                 // # Safety:
194                 // If promptUserConfirmation does not succeed, it must not take ownership of the
195                 // callback, so we must destroy it.
196                 unsafe { Box::from_raw(cb_data_ptr) };
197                 Err(rc)
198             }
199         }
200     }
201 
202     /// Aborts a running confirmation session, or no-op if none is running.
abort(&self)203     pub fn abort(&self) {
204         // # Safety:
205         // It is always safe to call `abortUserConfirmation`, because spurious calls are ignored.
206         // The handle argument must be valid, but this is an invariant of `ApcHal`.
207         unsafe { abortUserConfirmation(self.0) }
208     }
209 }
210