blob: ae414324d96777ad8fc4ae0b5d6583a02b50cadd [file] [log] [blame]
Hasini Gunasinghe3410f792020-09-14 17:55:21 +00001// 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
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000015//! This is the Keystore 2.0 Enforcements module.
16// TODO: more description to follow.
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000017use crate::auth_token_handler::AuthTokenHandler;
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +000018use crate::background_task_handler::Message;
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000019use crate::database::AuthTokenEntry;
20use crate::error::Error as KeystoreError;
Hasini Gunasinghe557b1032020-11-10 01:35:30 +000021use crate::globals::DB;
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000022use crate::key_parameter::{KeyParameter, KeyParameterValue};
23use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Hasini Gunasinghe5112c702020-11-09 22:13:25 +000024 Algorithm::Algorithm, ErrorCode::ErrorCode as Ec, HardwareAuthToken::HardwareAuthToken,
25 HardwareAuthenticatorType::HardwareAuthenticatorType, KeyPurpose::KeyPurpose,
Hasini Gunasinghe557b1032020-11-10 01:35:30 +000026 SecurityLevel::SecurityLevel, Tag::Tag, Timestamp::Timestamp,
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +000027 VerificationToken::VerificationToken,
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000028};
29use android_system_keystore2::aidl::android::system::keystore2::OperationChallenge::OperationChallenge;
30use anyhow::{Context, Result};
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000031use std::collections::{HashMap, HashSet};
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +000032use std::sync::mpsc::{channel, Sender};
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000033use std::sync::Mutex;
Hasini Gunasinghe5112c702020-11-09 22:13:25 +000034use std::time::SystemTime;
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000035
36/// Enforcements data structure
37pub struct Enforcements {
38 // This hash set contains the user ids for whom the device is currently unlocked. If a user id
39 // is not in the set, it implies that the device is locked for the user.
40 device_unlocked_set: Mutex<HashSet<i32>>,
41 // This maps the operation challenge to an optional auth token, to maintain op-auth tokens
42 // in-memory, until they are picked up and given to the operation by authorise_update_finish().
43 op_auth_map: Mutex<HashMap<i64, Option<HardwareAuthToken>>>,
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +000044 // sender end of the channel via which the enforcement module communicates with the
45 // background task handler (bth). This is of type Mutex in an Option because it is initialized
46 // after the global enforcement object is created.
47 sender_to_bth: Mutex<Option<Sender<Message>>>,
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000048}
49
50impl Enforcements {
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +000051 /// Creates an enforcement object with the two data structures it holds and the sender as None.
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000052 pub fn new() -> Self {
53 Enforcements {
54 device_unlocked_set: Mutex::new(HashSet::new()),
55 op_auth_map: Mutex::new(HashMap::new()),
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +000056 sender_to_bth: Mutex::new(None),
Hasini Gunasinghe3410f792020-09-14 17:55:21 +000057 }
58 }
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000059
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +000060 /// Initialize the sender_to_bth field, using the given sender end of a channel.
61 pub fn set_sender_to_bth(&self, sender: Sender<Message>) {
62 // It is ok to unwrap here because there is no chance of poisoning this mutex.
63 let mut sender_guard = self.sender_to_bth.lock().unwrap();
64 *sender_guard = Some(sender);
65 }
66
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000067 /// Checks if update or finish calls are authorized. If the operation is based on per-op key,
68 /// try to receive the auth token from the op_auth_map. We assume that by the time update/finish
69 /// is called, the auth token has been delivered to keystore. Therefore, we do not wait for it
70 /// and if the auth token is not found in the map, an error is returned.
Hasini Gunasinghe888dd352020-11-17 23:08:39 +000071 /// This method is called only during the first call to update or if finish is called right
72 /// after create operation, because the operation caches the authorization decisions and tokens
73 /// from previous calls to enforcement module.
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000074 pub fn authorize_update_or_finish(
75 &self,
76 key_params: &[KeyParameter],
Hasini Gunasinghe888dd352020-11-17 23:08:39 +000077 op_challenge: Option<&OperationChallenge>,
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +000078 ) -> Result<AuthTokenHandler> {
79 let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
80 let mut user_secure_ids = Vec::<i64>::new();
81 let mut is_timeout_key = false;
82
83 for key_param in key_params.iter() {
84 match key_param.key_parameter_value() {
85 KeyParameterValue::NoAuthRequired => {
86 // unlike in authorize_create, we do not check if both NoAuthRequired and user
87 // secure id are present, because that is already checked in authorize_create.
88 return Ok(AuthTokenHandler::NoAuthRequired);
89 }
90 KeyParameterValue::AuthTimeout(_) => {
91 is_timeout_key = true;
92 }
93 KeyParameterValue::HardwareAuthenticatorType(a) => {
94 user_auth_type = Some(*a);
95 }
96 KeyParameterValue::UserSecureID(u) => {
97 user_secure_ids.push(*u);
98 }
99 _ => {}
100 }
101 }
102
103 // If either of auth_type or secure_id is present and the other is not present,
104 // authorize_create would have already returned error.
105 // At this point, if UserSecureID is present and AuthTimeout is not present in
106 // key parameters, per-op auth is required.
107 // Obtain and validate the auth token.
108 if !is_timeout_key && !user_secure_ids.is_empty() {
109 let challenge =
110 op_challenge.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
111 "In authorize_update_or_finish: Auth required, but operation challenge is not
112 present.",
113 )?;
114 let auth_type =
115 user_auth_type.ok_or(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
116 "In authorize_update_or_finish: Auth required, but authenticator type is not
117 present.",
118 )?;
119 // It is ok to unwrap here, because there is no way this lock can get poisoned and
120 // and there is no way to recover if it is poisoned.
121 let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
122 let auth_entry = op_auth_map_guard.remove(&(challenge.challenge));
123
124 match auth_entry {
125 Some(Some(auth_token)) => {
126 if AuthTokenEntry::satisfies_auth(&auth_token, &user_secure_ids, auth_type) {
127 return Ok(AuthTokenHandler::Token(auth_token, None));
128 } else {
129 return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
130 .context("In authorize_update_or_finish: Auth token does not match.");
131 }
132 }
133 _ => {
134 // there was no auth token
135 return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
136 "In authorize_update_or_finish: Auth required, but an auth token
137 is not found for the given operation challenge, in the op_auth_map.",
138 );
139 }
140 }
141 }
142
143 // If we don't find HardwareAuthenticatorType and UserSecureID, we assume that
144 // authentication is not required, because in legacy keys, authentication related
145 // key parameters may not present.
146 // TODO: METRICS: count how many times (if any) this code path is executed, in order
147 // to identify if any such keys are in use
148 Ok(AuthTokenHandler::NoAuthRequired)
149 }
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000150
151 /// Checks if a create call is authorized, given key parameters and operation parameters.
152 /// With regard to auth tokens, the following steps are taken:
153 /// If the key is time-bound, find a matching auth token from the database.
154 /// If the above step is successful, and if the security level is STRONGBOX, return a
155 /// VerificationRequired variant of the AuthTokenHandler with the found auth token to signal
156 /// the operation that it may need to obtain a verification token from TEE KeyMint.
157 /// If the security level is not STRONGBOX, return a Token variant of the AuthTokenHandler with
158 /// the found auth token to signal the operation that no more authorization required.
159 /// If the key is per-op, return an OpAuthRequired variant of the AuthTokenHandler to signal
160 /// create_operation() that it needs to add the operation challenge to the op_auth_map, once it
161 /// is received from the keymint, and that operation needs to be authorized before update/finish
162 /// is called.
163 pub fn authorize_create(
164 &self,
165 purpose: KeyPurpose,
166 key_params: &[KeyParameter],
167 op_params: &[KeyParameter],
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000168 security_level: SecurityLevel,
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000169 ) -> Result<AuthTokenHandler> {
170 match purpose {
171 // Allow SIGN, DECRYPT for both symmetric and asymmetric keys.
172 KeyPurpose::SIGN | KeyPurpose::DECRYPT => {}
173 // Rule out WRAP_KEY purpose
174 KeyPurpose::WRAP_KEY => {
175 return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
176 .context("In authorize_create: WRAP_KEY purpose is not allowed here.");
177 }
178 KeyPurpose::VERIFY | KeyPurpose::ENCRYPT => {
179 // We do not support ENCRYPT and VERIFY (the remaining two options of purpose) for
180 // asymmetric keys.
181 for kp in key_params.iter() {
182 match *kp.key_parameter_value() {
183 KeyParameterValue::Algorithm(Algorithm::RSA)
184 | KeyParameterValue::Algorithm(Algorithm::EC) => {
185 return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE)).context(
186 "In authorize_create: public operations on asymmetric keys are not
187 supported.",
188 );
189 }
190 _ => {}
191 }
192 }
193 }
194 _ => {
195 return Err(KeystoreError::Km(Ec::UNSUPPORTED_PURPOSE))
196 .context("In authorize_create: specified purpose is not supported.");
197 }
198 }
199 // The following variables are to record information from key parameters to be used in
200 // enforcements, when two or more such pieces of information are required for enforcements.
201 // There is only one additional variable than what legacy keystore has, but this helps
202 // reduce the number of for loops on key parameters from 3 to 1, compared to legacy keystore
203 let mut key_purpose_authorized: bool = false;
204 let mut is_time_out_key: bool = false;
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000205 let mut user_auth_type: Option<HardwareAuthenticatorType> = None;
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000206 let mut no_auth_required: bool = false;
207 let mut caller_nonce_allowed = false;
208 let mut user_id: i32 = -1;
209 let mut user_secure_ids = Vec::<i64>::new();
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000210 let mut key_time_out: Option<i64> = None;
211 let mut allow_while_on_body = false;
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000212
213 // iterate through key parameters, recording information we need for authorization
214 // enforcements later, or enforcing authorizations in place, where applicable
215 for key_param in key_params.iter() {
216 match key_param.key_parameter_value() {
217 KeyParameterValue::NoAuthRequired => {
218 no_auth_required = true;
219 }
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000220 KeyParameterValue::AuthTimeout(t) => {
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000221 is_time_out_key = true;
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000222 key_time_out = Some(*t as i64);
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000223 }
224 KeyParameterValue::HardwareAuthenticatorType(a) => {
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000225 user_auth_type = Some(*a);
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000226 }
227 KeyParameterValue::KeyPurpose(p) => {
228 // Note: if there can be multiple KeyPurpose key parameters (TODO: confirm this),
229 // following check has the effect of key_params.contains(purpose)
230 // Also, authorizing purpose can not be completed here, if there can be multiple
231 // key parameters for KeyPurpose
232 if !key_purpose_authorized && *p == purpose {
233 key_purpose_authorized = true;
234 }
235 }
236 KeyParameterValue::CallerNonce => {
237 caller_nonce_allowed = true;
238 }
239 KeyParameterValue::ActiveDateTime(a) => {
240 if !Enforcements::is_given_time_passed(*a, true) {
241 return Err(KeystoreError::Km(Ec::KEY_NOT_YET_VALID))
242 .context("In authorize_create: key is not yet active.");
243 }
244 }
245 KeyParameterValue::OriginationExpireDateTime(o) => {
246 if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
247 && Enforcements::is_given_time_passed(*o, false)
248 {
249 return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
250 .context("In authorize_create: key is expired.");
251 }
252 }
253 KeyParameterValue::UsageExpireDateTime(u) => {
254 if (purpose == KeyPurpose::DECRYPT || purpose == KeyPurpose::VERIFY)
255 && Enforcements::is_given_time_passed(*u, false)
256 {
257 return Err(KeystoreError::Km(Ec::KEY_EXPIRED))
258 .context("In authorize_create: key is expired.");
259 }
260 }
261 KeyParameterValue::UserSecureID(s) => {
262 user_secure_ids.push(*s);
263 }
264 KeyParameterValue::UserID(u) => {
265 user_id = *u;
266 }
267 KeyParameterValue::UnlockedDeviceRequired => {
268 // check the device locked status. If locked, operations on the key are not
269 // allowed.
270 if self.is_device_locked(user_id) {
271 return Err(KeystoreError::Km(Ec::DEVICE_LOCKED))
272 .context("In authorize_create: device is locked.");
273 }
274 }
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000275 KeyParameterValue::AllowWhileOnBody => {
276 allow_while_on_body = true;
277 }
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000278 // NOTE: as per offline discussion, sanitizing key parameters and rejecting
279 // create operation if any non-allowed tags are present, is not done in
280 // authorize_create (unlike in legacy keystore where AuthorizeBegin is rejected if
281 // a subset of non-allowed tags are present). Because santizing key parameters
282 // should have been done during generate/import key, by KeyMint.
283 _ => { /*Do nothing on all the other key parameters, as in legacy keystore*/ }
284 }
285 }
286
287 // authorize the purpose
288 if !key_purpose_authorized {
289 return Err(KeystoreError::Km(Ec::INCOMPATIBLE_PURPOSE))
290 .context("In authorize_create: the purpose is not authorized.");
291 }
292
293 // if both NO_AUTH_REQUIRED and USER_SECURE_ID tags are present, return error
294 if !user_secure_ids.is_empty() && no_auth_required {
295 return Err(KeystoreError::Km(Ec::INVALID_KEY_BLOB)).context(
296 "In authorize_create: key has both NO_AUTH_REQUIRED
297 and USER_SECURE_ID tags.",
298 );
299 }
300
301 // if either of auth_type or secure_id is present and the other is not present, return error
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000302 if (user_auth_type.is_some() && user_secure_ids.is_empty())
303 || (user_auth_type.is_none() && !user_secure_ids.is_empty())
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000304 {
305 return Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED)).context(
306 "In authorize_create: Auth required, but either auth type or secure ids
307 are not present.",
308 );
309 }
310 // validate caller nonce for origination purposes
311 if (purpose == KeyPurpose::ENCRYPT || purpose == KeyPurpose::SIGN)
312 && !caller_nonce_allowed
313 && op_params.iter().any(|kp| kp.get_tag() == Tag::NONCE)
314 {
315 return Err(KeystoreError::Km(Ec::CALLER_NONCE_PROHIBITED)).context(
316 "In authorize_create, NONCE is present,
317 although CALLER_NONCE is not present",
318 );
319 }
320
321 if !user_secure_ids.is_empty() {
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000322 // key requiring authentication per operation
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000323 if !is_time_out_key {
324 return Ok(AuthTokenHandler::OpAuthRequired);
325 } else {
Hasini Gunasinghef70cf8e2020-11-11 01:02:41 +0000326 // key requiring time-out based authentication
327 let auth_token = DB
328 .with::<_, Result<HardwareAuthToken>>(|db| {
329 let mut db = db.borrow_mut();
330 match (user_auth_type, key_time_out) {
331 (Some(auth_type), Some(key_time_out)) => {
332 let matching_entry = db
333 .find_timed_auth_token_entry(
334 &user_secure_ids,
335 auth_type,
336 key_time_out,
337 allow_while_on_body,
338 )
339 .context("Failed to find timed auth token.")?;
340 Ok(matching_entry.get_auth_token())
341 }
342 (_, _) => Err(KeystoreError::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
343 .context("Authenticator type and/or key time out is not given."),
344 }
345 })
346 .context("In authorize_create.")?;
347
348 if security_level == SecurityLevel::STRONGBOX {
349 return Ok(AuthTokenHandler::VerificationRequired(auth_token));
350 } else {
351 return Ok(AuthTokenHandler::Token(auth_token, None));
352 }
Hasini Gunasinghe5112c702020-11-09 22:13:25 +0000353 }
354 }
355
356 // If we reach here, all authorization enforcements have passed and no auth token required.
357 Ok(AuthTokenHandler::NoAuthRequired)
358 }
359
360 /// Checks if the time now since epoch is greater than (or equal, if is_given_time_inclusive is
361 /// set) the given time (in milliseconds)
362 fn is_given_time_passed(given_time: i64, is_given_time_inclusive: bool) -> bool {
363 let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH);
364
365 let time_since_epoch = match duration_since_epoch {
366 Ok(duration) => duration.as_millis(),
367 Err(_) => return false,
368 };
369
370 if is_given_time_inclusive {
371 time_since_epoch >= (given_time as u128)
372 } else {
373 time_since_epoch > (given_time as u128)
374 }
375 }
376
377 /// Check if the device is locked for the given user. If there's no entry yet for the user,
378 /// we assume that the device is locked
379 fn is_device_locked(&self, user_id: i32) -> bool {
380 // unwrap here because there's no way this mutex guard can be poisoned and
381 // because there's no way to recover, even if it is poisoned.
382 let set = self.device_unlocked_set.lock().unwrap();
383 !set.contains(&user_id)
384 }
Hasini Gunasinghe557b1032020-11-10 01:35:30 +0000385
386 /// Sets the device locked status for the user. This method is called externally.
387 pub fn set_device_locked(&self, user_id: i32, device_locked_status: bool) {
388 // unwrap here because there's no way this mutex guard can be poisoned and
389 // because there's no way to recover, even if it is poisoned.
390 let mut set = self.device_unlocked_set.lock().unwrap();
391 if device_locked_status {
392 set.remove(&user_id);
393 } else {
394 set.insert(user_id);
395 }
396 }
397
398 /// Add this auth token to the database.
399 /// Then check if there is an entry in the op_auth_map, indexed by the challenge of this
400 /// auth token (which could have been inserted during create_operation of an operation on a
401 /// per-op-auth key). If so, add a copy of this auth token to the map indexed by the
402 /// challenge.
403 pub fn add_auth_token(&self, auth_token: HardwareAuthToken) -> Result<()> {
404 //it is ok to unwrap here, because there is no way this lock can get poisoned and
405 //and there is no way to recover if it is poisoned.
406 let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
407
408 if op_auth_map_guard.contains_key(&auth_token.challenge) {
409 let auth_token_copy = HardwareAuthToken {
410 challenge: auth_token.challenge,
411 userId: auth_token.userId,
412 authenticatorId: auth_token.authenticatorId,
413 authenticatorType: HardwareAuthenticatorType(auth_token.authenticatorType.0),
414 timestamp: Timestamp { milliSeconds: auth_token.timestamp.milliSeconds },
415 mac: auth_token.mac.clone(),
416 };
417 op_auth_map_guard.insert(auth_token.challenge, Some(auth_token_copy));
418 }
419
420 DB.with(|db| db.borrow_mut().insert_auth_token(&auth_token))
421 .context("In add_auth_token.")?;
422 Ok(())
423 }
Hasini Gunasinghe888dd352020-11-17 23:08:39 +0000424
425 /// This allows adding an entry to the op_auth_map, indexed by the operation challenge.
426 /// This is to be called by create_operation, once it has received the operation challenge
427 /// from keymint for an operation whose authorization decision is OpAuthRequired, as signalled
428 /// by the AuthTokenHandler.
429 pub fn insert_to_op_auth_map(&self, op_challenge: i64) {
430 let mut op_auth_map_guard = self.op_auth_map.lock().unwrap();
431 op_auth_map_guard.insert(op_challenge, None);
432 }
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +0000433
434 /// Requests a verification token from the background task handler which will retrieve it from
435 /// Timestamp Service or TEE KeyMint.
436 /// Once the create_operation receives an operation challenge from KeyMint, if it has
437 /// previously received a VerificationRequired variant of AuthTokenHandler during
438 /// authorize_create_operation, it calls this method to obtain a VerificationToken.
439 pub fn request_verification_token(
440 &self,
441 auth_token: HardwareAuthToken,
442 op_challenge: OperationChallenge,
443 ) -> Result<AuthTokenHandler> {
444 // create a channel for this particular operation
445 let (op_sender, op_receiver) = channel::<(HardwareAuthToken, VerificationToken)>();
446 // it is ok to unwrap here because there is no way this mutex gets poisoned.
447 let sender_guard = self.sender_to_bth.lock().unwrap();
448 if let Some(sender) = &*sender_guard {
449 let sender_cloned = sender.clone();
450 drop(sender_guard);
451 sender_cloned
452 .send(Message::Inputs((auth_token, op_challenge, op_sender)))
453 .map_err(|_| KeystoreError::sys())
454 .context(
455 "In request_verification_token. Sending a request for a verification token
456 failed.",
457 )?;
458 }
459 Ok(AuthTokenHandler::Channel(op_receiver))
460 }
Hasini Gunasinghe3410f792020-09-14 17:55:21 +0000461}
462
463impl Default for Enforcements {
464 fn default() -> Self {
465 Self::new()
466 }
467}
468
Hasini Gunasinghef04d07a2020-11-25 22:41:35 +0000469impl Drop for Enforcements {
470 fn drop(&mut self) {
471 let sender_guard = self.sender_to_bth.lock().unwrap();
472 if let Some(sender) = &*sender_guard {
473 let sender_cloned = sender.clone();
474 drop(sender_guard);
475 // TODO: Verify how best to handle the error in this case.
476 sender_cloned.send(Message::Shutdown).unwrap_or_else(|e| {
477 panic!(
478 "Failed to send shutdown message to background task handler because of {:?}.",
479 e
480 );
481 });
482 }
483 }
484}
485
Hasini Gunasinghe52333ba2020-11-06 01:24:16 +0000486// TODO: Add tests to enforcement module (b/175578618).