1 // Copyright 2022, 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 use nix::unistd::{getuid, Gid, Uid};
16 use rustutils::users::AID_USER_OFFSET;
17 use std::thread;
18 use std::thread::JoinHandle;
19 
20 use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
21     Digest::Digest, ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, SecurityLevel::SecurityLevel,
22 };
23 use android_system_keystore2::aidl::android::system::keystore2::{
24     CreateOperationResponse::CreateOperationResponse, Domain::Domain,
25     IKeystoreOperation::IKeystoreOperation, ResponseCode::ResponseCode,
26 };
27 
28 use keystore2_test_utils::{
29     authorizations, get_keystore_service, key_generations, key_generations::Error, run_as,
30 };
31 
32 use crate::keystore2_client_test_utils::{
33     create_signing_operation, execute_op_run_as_child, perform_sample_sign_operation,
34     BarrierReached, ForcedOp, TestOutcome,
35 };
36 
37 /// Create `max_ops` number child processes with the given context and perform an operation under each
38 /// child process.
39 ///
40 /// # Safety
41 ///
42 /// Must be called from a process with no other threads.
create_operations( target_ctx: &'static str, forced_op: ForcedOp, max_ops: i32, ) -> Vec<run_as::ChildHandle<TestOutcome, BarrierReached>>43 pub unsafe fn create_operations(
44     target_ctx: &'static str,
45     forced_op: ForcedOp,
46     max_ops: i32,
47 ) -> Vec<run_as::ChildHandle<TestOutcome, BarrierReached>> {
48     let alias = format!("ks_op_test_key_{}", getuid());
49     let base_gid = 99 * AID_USER_OFFSET + 10001;
50     let base_uid = 99 * AID_USER_OFFSET + 10001;
51     (0..max_ops)
52         // SAFETY: The caller guarantees that there are no other threads.
53         .map(|i| unsafe {
54             execute_op_run_as_child(
55                 target_ctx,
56                 Domain::APP,
57                 key_generations::SELINUX_SHELL_NAMESPACE,
58                 Some(alias.to_string()),
59                 Uid::from_raw(base_uid + (i as u32)),
60                 Gid::from_raw(base_gid + (i as u32)),
61                 forced_op,
62             )
63         })
64         .collect()
65 }
66 
67 /// Executes an operation in a thread. Expect an `OPERATION_BUSY` error in case of operation
68 /// failure. Returns True if `OPERATION_BUSY` error is encountered otherwise returns false.
perform_op_busy_in_thread(op: binder::Strong<dyn IKeystoreOperation>) -> JoinHandle<bool>69 fn perform_op_busy_in_thread(op: binder::Strong<dyn IKeystoreOperation>) -> JoinHandle<bool> {
70     thread::spawn(move || {
71         for _n in 1..1000 {
72             match key_generations::map_ks_error(op.update(b"my message")) {
73                 Ok(_) => continue,
74                 Err(e) => {
75                     assert_eq!(Error::Rc(ResponseCode::OPERATION_BUSY), e);
76                     return true;
77                 }
78             }
79         }
80         let sig = op.finish(None, None).unwrap();
81         assert!(sig.is_some());
82         false
83     })
84 }
85 
86 /// This test verifies that backend service throws BACKEND_BUSY error when all
87 /// operations slots are full. This test creates operations in child processes and
88 /// collects the status of operations performed in each child proc and determines
89 /// whether any child proc exited with error status.
90 #[test]
keystore2_backend_busy_test()91 fn keystore2_backend_busy_test() {
92     const MAX_OPS: i32 = 100;
93     static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
94 
95     // SAFETY: The test is run in a separate process with no other threads.
96     let mut child_handles = unsafe { create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS) };
97 
98     // Wait until all child procs notifies us to continue,
99     // so that there are definitely enough operations outstanding to trigger a BACKEND_BUSY.
100     for ch in child_handles.iter_mut() {
101         ch.recv();
102     }
103     // Notify each child to resume and finish.
104     for ch in child_handles.iter_mut() {
105         ch.send(&BarrierReached {});
106     }
107 
108     // Collect the result and validate whether backend busy has occurred.
109     let mut busy_count = 0;
110     for ch in child_handles.into_iter() {
111         if ch.get_result() == TestOutcome::BackendBusy {
112             busy_count += 1;
113         }
114     }
115     assert!(busy_count > 0)
116 }
117 
118 /// This test confirms that forced operation is having high pruning power.
119 /// 1. Initially create regular operations such that there are enough operations outstanding
120 ///    to trigger BACKEND_BUSY.
121 /// 2. Then, create a forced operation. System should be able to prune one of the regular
122 ///    operations and create a slot for forced operation successfully.
123 #[test]
keystore2_forced_op_after_backendbusy_test()124 fn keystore2_forced_op_after_backendbusy_test() {
125     const MAX_OPS: i32 = 100;
126     static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
127 
128     // Create regular operations.
129     // SAFETY: The test is run in a separate process with no other threads.
130     let mut child_handles = unsafe { create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS) };
131 
132     // Wait until all child procs notifies us to continue, so that there are enough
133     // operations outstanding to trigger a BACKEND_BUSY.
134     for ch in child_handles.iter_mut() {
135         ch.recv();
136     }
137 
138     // Create a forced operation.
139     let auid = 99 * AID_USER_OFFSET + 10604;
140     let agid = 99 * AID_USER_OFFSET + 10604;
141     // SAFETY: The test is run in a separate process with no other threads.
142     unsafe {
143         run_as::run_as(
144             key_generations::TARGET_VOLD_CTX,
145             Uid::from_raw(auid),
146             Gid::from_raw(agid),
147             move || {
148                 let alias = format!("ks_prune_forced_op_key_{}", getuid());
149 
150                 // To make room for this forced op, system should be able to prune one of the
151                 // above created regular operations and create a slot for this forced operation
152                 // successfully.
153                 create_signing_operation(
154                     ForcedOp(true),
155                     KeyPurpose::SIGN,
156                     Digest::SHA_2_256,
157                     Domain::SELINUX,
158                     100,
159                     Some(alias),
160                 )
161                 .expect("Client failed to create forced operation after BACKEND_BUSY state.");
162             },
163         );
164     };
165 
166     // Notify each child to resume and finish.
167     for ch in child_handles.iter_mut() {
168         ch.send(&BarrierReached {});
169     }
170 
171     // Collect the results of above created regular operations.
172     let mut pruned_count = 0;
173     let mut busy_count = 0;
174     let mut _other_err = 0;
175     for ch in child_handles.into_iter() {
176         match ch.get_result() {
177             TestOutcome::BackendBusy => {
178                 busy_count += 1;
179             }
180             TestOutcome::InvalidHandle => {
181                 pruned_count += 1;
182             }
183             _ => {
184                 _other_err += 1;
185             }
186         }
187     }
188     // Verify that there should be at least one backend busy has occurred while creating
189     // above regular operations.
190     assert!(busy_count > 0);
191 
192     // Verify that there should be at least one pruned operation which should have failed while
193     // performing operation.
194     assert!(pruned_count > 0);
195 }
196 
197 /// This test confirms that forced operations can't be pruned.
198 ///  1. Creates an initial forced operation and tries to complete the operation after BACKEND_BUSY
199 ///     error is triggered.
200 ///  2. Create MAX_OPS number of forced operations so that definitely enough number of operations
201 ///     outstanding to trigger a BACKEND_BUSY.
202 ///  3. Try to use initially created forced operation (in step #1) and able to perform the
203 ///     operation successfully. This confirms that none of the later forced operations evicted the
204 ///     initial forced operation.
205 #[test]
keystore2_max_forced_ops_test()206 fn keystore2_max_forced_ops_test() {
207     const MAX_OPS: i32 = 100;
208     let auid = 99 * AID_USER_OFFSET + 10205;
209     let agid = 99 * AID_USER_OFFSET + 10205;
210 
211     // Create initial forced operation in a child process
212     // and wait for the parent to notify to perform operation.
213     let alias = format!("ks_forced_op_key_{}", getuid());
214     // SAFETY: The test is run in a separate process with no other threads.
215     let mut first_op_handle = unsafe {
216         execute_op_run_as_child(
217             key_generations::TARGET_SU_CTX,
218             Domain::SELINUX,
219             key_generations::SELINUX_SHELL_NAMESPACE,
220             Some(alias),
221             Uid::from_raw(auid),
222             Gid::from_raw(agid),
223             ForcedOp(true),
224         )
225     };
226 
227     // Wait until above child proc notifies us to continue, so that there is definitely a forced
228     // operation outstanding to perform a operation.
229     first_op_handle.recv();
230 
231     // Create MAX_OPS number of forced operations.
232     let mut child_handles =
233     // SAFETY: The test is run in a separate process with no other threads.
234         unsafe { create_operations(key_generations::TARGET_SU_CTX, ForcedOp(true), MAX_OPS) };
235 
236     // Wait until all child procs notifies us to continue, so that  there are enough operations
237     // outstanding to trigger a BACKEND_BUSY.
238     for ch in child_handles.iter_mut() {
239         ch.recv();
240     }
241 
242     // Notify initial created forced operation to continue performing the operations.
243     first_op_handle.send(&BarrierReached {});
244 
245     // Collect initially created forced operation result and is expected to complete operation
246     // successfully.
247     let first_op_result = first_op_handle.get_result();
248     assert_eq!(first_op_result, TestOutcome::Ok);
249 
250     // Notify each child to resume and finish.
251     for ch in child_handles.iter_mut() {
252         ch.send(&BarrierReached {});
253     }
254 
255     // Collect the result and validate whether backend busy has occurred with MAX_OPS number
256     // of forced operations.
257     let busy_count = child_handles
258         .into_iter()
259         .map(|ch| ch.get_result())
260         .filter(|r| *r == TestOutcome::BackendBusy)
261         .count();
262     assert!(busy_count > 0);
263 }
264 
265 /// This test will verify the use case with the same owner(UID) requesting `n` number of operations.
266 /// This test confirms that when all operation slots are full and a new operation is requested,
267 /// an operation which is least recently used and lived longest will be pruned to make a room
268 /// for a new operation. Pruning strategy should prevent the operations of the other owners(UID)
269 /// from being pruned.
270 ///
271 /// 1. Create an operation in a child process with `untrusted_app` context and wait for parent
272 ///    notification to complete the operation.
273 /// 2. Let parent process create `n` number of operations such that there are enough operations
274 ///    outstanding to trigger cannibalizing their own sibling operations.
275 /// 3. Sequentially try to use above created `n` number of operations and also add a new operation,
276 ///    so that it should trigger cannibalizing one of their own sibling operations.
277 ///    3.1 While trying to use these pruned operations an `INVALID_OPERATION_HANDLE` error is
278 ///        expected as they are already pruned.
279 /// 4. Notify the child process to resume and complete the operation. It is expected to complete the
280 ///    operation successfully.
281 /// 5. Try to use the latest operation of parent. It is expected to complete the operation
282 ///    successfully.
283 #[test]
keystore2_ops_prune_test()284 fn keystore2_ops_prune_test() {
285     const MAX_OPS: usize = 40; // This should be at least 32 with sec_level TEE.
286 
287     static TARGET_CTX: &str = "u:r:untrusted_app:s0";
288     const USER_ID: u32 = 99;
289     const APPLICATION_ID: u32 = 10601;
290 
291     let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
292     let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
293 
294     // Create an operation in an untrusted_app context. Wait until the parent notifies to continue.
295     // Once the parent notifies, this operation is expected to be completed successfully.
296     let alias = format!("ks_reg_op_key_{}", getuid());
297     // SAFETY: The test is run in a separate process with no other threads.
298     let mut child_handle = unsafe {
299         execute_op_run_as_child(
300             TARGET_CTX,
301             Domain::APP,
302             -1,
303             Some(alias),
304             Uid::from_raw(uid),
305             Gid::from_raw(gid),
306             ForcedOp(false),
307         )
308     };
309 
310     // Wait until child process notifies us to continue, so that an operation from child process is
311     // outstanding to complete the operation.
312     child_handle.recv();
313 
314     // Generate a key to use in below operations.
315     let keystore2 = get_keystore_service();
316     let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
317     let alias = format!("ks_prune_op_test_key_{}", getuid());
318     let key_metadata = key_generations::generate_ec_p256_signing_key(
319         &sec_level,
320         Domain::SELINUX,
321         key_generations::SELINUX_SHELL_NAMESPACE,
322         Some(alias),
323         None,
324     )
325     .unwrap();
326 
327     // Create multiple operations in this process to trigger cannibalizing sibling operations.
328     let mut ops: Vec<binder::Result<CreateOperationResponse>> = (0..MAX_OPS)
329         .map(|_| {
330             sec_level.createOperation(
331                 &key_metadata.key,
332                 &authorizations::AuthSetBuilder::new()
333                     .purpose(KeyPurpose::SIGN)
334                     .digest(Digest::SHA_2_256),
335                 false,
336             )
337         })
338         .collect();
339 
340     // Sequentially try to use operation handles created above and also add a new operation.
341     for vec_index in 0..MAX_OPS {
342         match &ops[vec_index] {
343             Ok(CreateOperationResponse { iOperation: Some(op), .. }) => {
344                 // Older operation handle is pruned, if we try to use that an error is expected.
345                 assert_eq!(
346                     Err(Error::Km(ErrorCode::INVALID_OPERATION_HANDLE)),
347                     key_generations::map_ks_error(op.update(b"my message"))
348                 );
349             }
350             _ => panic!("Operation should have created successfully."),
351         }
352 
353         // Create a new operation, it should trigger to cannibalize one of their own sibling
354         // operations.
355         ops.push(
356             sec_level.createOperation(
357                 &key_metadata.key,
358                 &authorizations::AuthSetBuilder::new()
359                     .purpose(KeyPurpose::SIGN)
360                     .digest(Digest::SHA_2_256),
361                 false,
362             ),
363         );
364     }
365 
366     // Notify child process to continue the operation.
367     child_handle.send(&BarrierReached {});
368     assert!((child_handle.get_result() == TestOutcome::Ok), "Failed to perform an operation");
369 
370     // Try to use the latest operation created by parent, should be able to use it successfully.
371     match ops.last() {
372         Some(Ok(CreateOperationResponse { iOperation: Some(op), .. })) => {
373             assert_eq!(Ok(()), key_generations::map_ks_error(perform_sample_sign_operation(op)));
374         }
375         _ => panic!("Operation should have created successfully."),
376     }
377 }
378 
379 /// Try to create forced operations with various contexts -
380 ///   - untrusted_app
381 ///   - system_server
382 ///   - priv_app
383 /// `PERMISSION_DENIED` error response is expected.
384 #[test]
keystore2_forced_op_perm_denied_test()385 fn keystore2_forced_op_perm_denied_test() {
386     static TARGET_CTXS: &[&str] =
387         &["u:r:untrusted_app:s0", "u:r:system_server:s0", "u:r:priv_app:s0"];
388     const USER_ID: u32 = 99;
389     const APPLICATION_ID: u32 = 10601;
390 
391     let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
392     let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
393 
394     for context in TARGET_CTXS.iter() {
395         // SAFETY: The test is run in a separate process with no other threads.
396         unsafe {
397             run_as::run_as(context, Uid::from_raw(uid), Gid::from_raw(gid), move || {
398                 let alias = format!("ks_app_forced_op_test_key_{}", getuid());
399                 let result = key_generations::map_ks_error(create_signing_operation(
400                     ForcedOp(true),
401                     KeyPurpose::SIGN,
402                     Digest::SHA_2_256,
403                     Domain::APP,
404                     -1,
405                     Some(alias),
406                 ));
407                 assert!(result.is_err());
408                 assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
409             });
410         }
411     }
412 }
413 
414 /// Try to create a forced operation with `vold` context.
415 /// Should be able to create forced operation with `vold` context successfully.
416 #[test]
keystore2_forced_op_success_test()417 fn keystore2_forced_op_success_test() {
418     static TARGET_CTX: &str = "u:r:vold:s0";
419     const USER_ID: u32 = 99;
420     const APPLICATION_ID: u32 = 10601;
421 
422     let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
423     let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
424 
425     // SAFETY: The test is run in a separate process with no other threads.
426     unsafe {
427         run_as::run_as(TARGET_CTX, Uid::from_raw(uid), Gid::from_raw(gid), move || {
428             let alias = format!("ks_vold_forced_op_key_{}", getuid());
429             create_signing_operation(
430                 ForcedOp(true),
431                 KeyPurpose::SIGN,
432                 Digest::SHA_2_256,
433                 Domain::SELINUX,
434                 key_generations::SELINUX_VOLD_NAMESPACE,
435                 Some(alias),
436             )
437             .expect("Client with vold context failed to create forced operation.");
438         });
439     }
440 }
441 
442 /// Create an operation and try to use this operation handle in multiple threads to perform
443 /// operations. Test should fail to perform an operation with an error response `OPERATION_BUSY`
444 /// when multiple threads try to access the operation handle at same time.
445 #[test]
keystore2_op_fails_operation_busy()446 fn keystore2_op_fails_operation_busy() {
447     let op_response = create_signing_operation(
448         ForcedOp(false),
449         KeyPurpose::SIGN,
450         Digest::SHA_2_256,
451         Domain::APP,
452         -1,
453         Some("op_busy_alias_test_key".to_string()),
454     )
455     .unwrap();
456 
457     let op: binder::Strong<dyn IKeystoreOperation> = op_response.iOperation.unwrap();
458 
459     let th_handle_1 = perform_op_busy_in_thread(op.clone());
460     let th_handle_2 = perform_op_busy_in_thread(op);
461 
462     let result1 = th_handle_1.join().unwrap();
463     let result2 = th_handle_2.join().unwrap();
464 
465     assert!(result1 || result2);
466 }
467