1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.cts.deviceandprofileowner; 17 18 import static android.app.admin.DevicePolicyManager.KEYGUARD_DISABLE_FINGERPRINT; 19 import static android.app.admin.DevicePolicyManager.PASSWORD_COMPLEXITY_HIGH; 20 import static android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_COMPLEX; 21 import static android.app.admin.SecurityLog.LEVEL_ERROR; 22 import static android.app.admin.SecurityLog.LEVEL_INFO; 23 import static android.app.admin.SecurityLog.LEVEL_WARNING; 24 import static android.app.admin.SecurityLog.TAG_ADB_SHELL_CMD; 25 import static android.app.admin.SecurityLog.TAG_ADB_SHELL_INTERACTIVE; 26 import static android.app.admin.SecurityLog.TAG_APP_PROCESS_START; 27 import static android.app.admin.SecurityLog.TAG_CAMERA_POLICY_SET; 28 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_INSTALLED; 29 import static android.app.admin.SecurityLog.TAG_CERT_AUTHORITY_REMOVED; 30 import static android.app.admin.SecurityLog.TAG_CERT_VALIDATION_FAILURE; 31 import static android.app.admin.SecurityLog.TAG_CRYPTO_SELF_TEST_COMPLETED; 32 import static android.app.admin.SecurityLog.TAG_KEYGUARD_DISABLED_FEATURES_SET; 33 import static android.app.admin.SecurityLog.TAG_KEYGUARD_DISMISSED; 34 import static android.app.admin.SecurityLog.TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT; 35 import static android.app.admin.SecurityLog.TAG_KEYGUARD_SECURED; 36 import static android.app.admin.SecurityLog.TAG_KEY_DESTRUCTION; 37 import static android.app.admin.SecurityLog.TAG_KEY_GENERATED; 38 import static android.app.admin.SecurityLog.TAG_KEY_IMPORT; 39 import static android.app.admin.SecurityLog.TAG_KEY_INTEGRITY_VIOLATION; 40 import static android.app.admin.SecurityLog.TAG_LOGGING_STARTED; 41 import static android.app.admin.SecurityLog.TAG_LOGGING_STOPPED; 42 import static android.app.admin.SecurityLog.TAG_LOG_BUFFER_SIZE_CRITICAL; 43 import static android.app.admin.SecurityLog.TAG_MAX_PASSWORD_ATTEMPTS_SET; 44 import static android.app.admin.SecurityLog.TAG_MAX_SCREEN_LOCK_TIMEOUT_SET; 45 import static android.app.admin.SecurityLog.TAG_MEDIA_MOUNT; 46 import static android.app.admin.SecurityLog.TAG_MEDIA_UNMOUNT; 47 import static android.app.admin.SecurityLog.TAG_OS_SHUTDOWN; 48 import static android.app.admin.SecurityLog.TAG_OS_STARTUP; 49 import static android.app.admin.SecurityLog.TAG_PASSWORD_COMPLEXITY_REQUIRED; 50 import static android.app.admin.SecurityLog.TAG_PASSWORD_COMPLEXITY_SET; 51 import static android.app.admin.SecurityLog.TAG_PASSWORD_EXPIRATION_SET; 52 import static android.app.admin.SecurityLog.TAG_PASSWORD_HISTORY_LENGTH_SET; 53 import static android.app.admin.SecurityLog.TAG_REMOTE_LOCK; 54 import static android.app.admin.SecurityLog.TAG_SYNC_RECV_FILE; 55 import static android.app.admin.SecurityLog.TAG_SYNC_SEND_FILE; 56 import static android.app.admin.SecurityLog.TAG_USER_RESTRICTION_ADDED; 57 import static android.app.admin.SecurityLog.TAG_USER_RESTRICTION_REMOVED; 58 import static android.app.admin.SecurityLog.TAG_WIPE_FAILURE; 59 60 import static com.android.cts.devicepolicy.TestCertificates.TEST_CA; 61 import static com.android.cts.devicepolicy.TestCertificates.TEST_CA_SUBJECT; 62 63 import static com.google.common.collect.ImmutableList.of; 64 import static com.google.common.truth.Truth.assertThat; 65 66 import android.app.admin.DevicePolicyManager; 67 import android.app.admin.SecurityLog; 68 import android.app.admin.SecurityLog.SecurityEvent; 69 import android.content.Context; 70 import android.content.SharedPreferences; 71 import android.os.Parcel; 72 import android.os.Process; 73 import android.os.UserHandle; 74 import android.os.UserManager; 75 import android.security.keystore.KeyGenParameterSpec; 76 import android.security.keystore.KeyProperties; 77 import android.security.keystore.KeyProtection; 78 import android.support.test.uiautomator.UiDevice; 79 import android.text.TextUtils; 80 import android.util.DebugUtils; 81 import android.util.Log; 82 83 import androidx.test.InstrumentationRegistry; 84 85 import com.google.common.collect.ImmutableMap; 86 import com.google.common.collect.ImmutableSet; 87 88 import junit.framework.AssertionFailedError; 89 90 import java.security.KeyPair; 91 import java.security.KeyPairGenerator; 92 import java.security.KeyStore; 93 import java.util.Arrays; 94 import java.util.List; 95 import java.util.Map; 96 import java.util.Set; 97 import java.util.concurrent.TimeUnit; 98 import java.util.function.Predicate; 99 import java.util.stream.Collectors; 100 101 import javax.crypto.spec.SecretKeySpec; 102 103 public class SecurityLoggingTest extends BaseDeviceAdminTest { 104 private static final String TAG = "SecurityLoggingTest"; 105 private static final String ARG_BATCH_NUMBER = "batchNumber"; 106 private static final String PREF_KEY_PREFIX = "batch-last-id-"; 107 private static final String PREF_NAME = "batchIds"; 108 // system/core/liblog/event.logtags: 1006 liblog (dropped|1) 109 private static final int TAG_LIBLOG_DROPPED = 1006; 110 private static final String DELEGATE_APP_PKG = "com.android.cts.delegate"; 111 private static final String DELEGATION_SECURITY_LOGGING = "delegation-security-logging"; 112 private static final boolean VERBOSE = false; 113 114 // For brevity. 115 private static final Class<String> S = String.class; 116 private static final Class<Long> L = Long.class; 117 private static final Class<Integer> I = Integer.class; 118 119 private static final Map<Integer, List<Class>> PAYLOAD_TYPES_MAP = 120 new ImmutableMap.Builder<Integer, List<Class>>() 121 .put(TAG_ADB_SHELL_INTERACTIVE, of()) 122 .put(TAG_ADB_SHELL_CMD, of(S)) 123 .put(TAG_SYNC_RECV_FILE, of(S)) 124 .put(TAG_SYNC_SEND_FILE, of(S)) 125 .put(TAG_APP_PROCESS_START, of(S, L, I, I, S, S)) 126 .put(TAG_KEYGUARD_DISMISSED, of()) 127 .put(TAG_KEYGUARD_DISMISS_AUTH_ATTEMPT, of(I, I)) 128 .put(TAG_KEYGUARD_SECURED, of()) 129 .put(TAG_OS_STARTUP, of(S, S)) 130 .put(TAG_OS_SHUTDOWN, of()) 131 .put(TAG_LOGGING_STARTED, of()) 132 .put(TAG_LOGGING_STOPPED, of()) 133 .put(TAG_MEDIA_MOUNT, of(S, S)) 134 .put(TAG_MEDIA_UNMOUNT, of(S, S)) 135 .put(TAG_LOG_BUFFER_SIZE_CRITICAL, of()) 136 .put(TAG_PASSWORD_EXPIRATION_SET, of(S, I, I, L)) 137 .put(TAG_PASSWORD_COMPLEXITY_SET, of(S, I, I, I, I, I, I, I, I, I, I)) 138 .put(TAG_PASSWORD_HISTORY_LENGTH_SET, of(S, I, I, I)) 139 .put(TAG_MAX_SCREEN_LOCK_TIMEOUT_SET, of(S, I, I, L)) 140 .put(TAG_MAX_PASSWORD_ATTEMPTS_SET, of(S, I, I, I)) 141 .put(TAG_KEYGUARD_DISABLED_FEATURES_SET, of(S, I, I, I)) 142 .put(TAG_REMOTE_LOCK, of(S, I, I)) 143 .put(TAG_WIPE_FAILURE, of()) 144 .put(TAG_KEY_GENERATED, of(I, S, I)) 145 .put(TAG_KEY_IMPORT, of(I, S, I)) 146 .put(TAG_KEY_DESTRUCTION, of(I, S, I)) 147 .put(TAG_CERT_AUTHORITY_INSTALLED, of(I, S, I)) 148 .put(TAG_CERT_AUTHORITY_REMOVED, of(I, S, I)) 149 .put(TAG_USER_RESTRICTION_ADDED, of(S, I, S)) 150 .put(TAG_USER_RESTRICTION_REMOVED, of(S, I, S)) 151 .put(TAG_CRYPTO_SELF_TEST_COMPLETED, of(I)) 152 .put(TAG_KEY_INTEGRITY_VIOLATION, of(S, I)) 153 .put(TAG_CERT_VALIDATION_FAILURE, of(S)) 154 .put(TAG_CAMERA_POLICY_SET, of(S, I, I, I)) 155 .put(TAG_PASSWORD_COMPLEXITY_REQUIRED, of(S, I, I, I)) 156 .build(); 157 158 private static final String GENERATED_KEY_ALIAS = "generated_key_alias"; 159 private static final String IMPORTED_KEY_ALIAS = "imported_key_alias"; 160 161 // Indices of various fields in event payload. 162 private static final int SUCCESS_INDEX = 0; 163 private static final int ALIAS_INDEX = 1; 164 private static final int UID_INDEX = 2; 165 private static final int USERID_INDEX = 2; 166 private static final int SUBJECT_INDEX = 1; 167 private static final int ADMIN_PKG_INDEX = 0; 168 private static final int ADMIN_USER_INDEX = 1; 169 private static final int TARGET_USER_INDEX = 2; 170 private static final int PWD_LEN_INDEX = 3; 171 private static final int PWD_QUALITY_INDEX = 4; 172 private static final int LETTERS_INDEX = 5; 173 private static final int NON_LETTERS_INDEX = 6; 174 private static final int NUMERIC_INDEX = 7; 175 private static final int UPPERCASE_INDEX = 8; 176 private static final int LOWERCASE_INDEX = 9; 177 private static final int SYMBOLS_INDEX = 10; 178 private static final int PWD_EXPIRATION_INDEX = 3; 179 private static final int PWD_HIST_LEN_INDEX = 3; 180 private static final int USER_RESTRICTION_INDEX = 2; 181 private static final int MAX_PWD_ATTEMPTS_INDEX = 3; 182 private static final int KEYGUARD_FEATURES_INDEX = 3; 183 private static final int MAX_SCREEN_TIMEOUT_INDEX = 3; 184 private static final int CAMERA_DISABLED_INDEX = 3; 185 186 // Value that indicates success in events that have corresponding field in their payload. 187 private static final int SUCCESS_VALUE = 1; 188 189 private static final int TEST_PWD_LENGTH = 10; 190 // Min number of various character types to use. 191 private static final int TEST_PWD_CHARS = 2; 192 193 private static final long TEST_PWD_EXPIRATION_TIMEOUT = TimeUnit.DAYS.toMillis(356); 194 private static final int TEST_PWD_HISTORY_LENGTH = 3; 195 private static final int TEST_PWD_MAX_ATTEMPTS = 5; 196 private static final long TEST_MAX_TIME_TO_LOCK = TimeUnit.HOURS.toMillis(1); 197 198 /** 199 * Test: retrieving security logs can only be done if there's one user on the device or all 200 * secondary users / profiles are affiliated. 201 */ testRetrievingSecurityLogsThrowsSecurityException()202 public void testRetrievingSecurityLogsThrowsSecurityException() { 203 try { 204 mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT); 205 fail("did not throw expected SecurityException"); 206 } catch (SecurityException expected) { 207 } 208 } 209 210 /** 211 * Test: retrieving previous security logs can only be done if there's one user on the device or 212 * all secondary users / profiles are affiliated. 213 */ testRetrievingPreviousSecurityLogsThrowsSecurityException()214 public void testRetrievingPreviousSecurityLogsThrowsSecurityException() { 215 try { 216 mDevicePolicyManager.retrievePreRebootSecurityLogs(ADMIN_RECEIVER_COMPONENT); 217 fail("did not throw expected SecurityException"); 218 } catch (SecurityException expected) { 219 } 220 } 221 222 /** 223 * Test: retrieves security logs and verifies that all events generated as a result of host 224 * side actions and by {@link #testGenerateLogs()} are there. 225 */ testVerifyGeneratedLogs()226 public void testVerifyGeneratedLogs() throws Exception { 227 forceSecurityLogs(); 228 229 final List<SecurityEvent> events = getEvents(); 230 231 verifyAutomaticEventsPresent(events); 232 verifyKeystoreEventsPresent(events); 233 verifyKeyChainEventsPresent(events); 234 verifyAdminEventsPresent(events); 235 verifyAdbShellCommand(events); // Event generated from host side logic 236 if (mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) { 237 verifyEventsRedacted(events); 238 } 239 } 240 forceSecurityLogs()241 private void forceSecurityLogs() throws Exception { 242 UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) 243 .executeShellCommand("dpm force-security-logs"); 244 } 245 verifyAutomaticEventsPresent(List<SecurityEvent> events)246 private void verifyAutomaticEventsPresent(List<SecurityEvent> events) { 247 verifyOsStartupEventPresent(events); 248 verifyLoggingStartedEventPresent(events); 249 verifyCryptoSelfTestEventPresent(events); 250 } 251 verifyKeyChainEventsPresent(List<SecurityEvent> events)252 private void verifyKeyChainEventsPresent(List<SecurityEvent> events) { 253 verifyCertInstalledEventPresent(events); 254 verifyCertUninstalledEventPresent(events); 255 } 256 verifyKeystoreEventsPresent(List<SecurityEvent> events)257 private void verifyKeystoreEventsPresent(List<SecurityEvent> events) { 258 verifyKeyGeneratedEventPresent(events, GENERATED_KEY_ALIAS); 259 verifyKeyDeletedEventPresent(events, GENERATED_KEY_ALIAS); 260 verifyKeyImportedEventPresent(events, IMPORTED_KEY_ALIAS); 261 verifyKeyDeletedEventPresent(events, IMPORTED_KEY_ALIAS); 262 } 263 verifyAdminEventsPresent(List<SecurityEvent> events)264 private void verifyAdminEventsPresent(List<SecurityEvent> events) { 265 if (mHasSecureLockScreen) { 266 verifyPasswordComplexityEventsPresent(events); 267 verifyNewStylePasswordComplexityEventPresent(events); 268 } 269 verifyLockingPolicyEventsPresent(events); 270 verifyUserRestrictionEventsPresent(events); 271 verifyCameraPolicyEvents(events); 272 } verifyAdbShellCommand(List<SecurityEvent> events)273 private void verifyAdbShellCommand(List<SecurityEvent> events) { 274 // Won't be able to locate the command on org-owned devices, as it will be redacted. 275 if (!mDevicePolicyManager.isOrganizationOwnedDeviceWithManagedProfile()) { 276 findEvent("adb command", events, 277 e -> e.getTag() == TAG_ADB_SHELL_CMD && 278 e.getData().equals("whoami")); 279 280 } 281 } 282 verifyEventsRedacted(List<SecurityEvent> events)283 private void verifyEventsRedacted(List<SecurityEvent> events) { 284 final int userId = Process.myUserHandle().getIdentifier(); 285 for (SecurityEvent event : events) { 286 switch (event.getTag()) { 287 case TAG_ADB_SHELL_CMD: 288 assertTrue(TextUtils.isEmpty((String) event.getData())); 289 break; 290 case TAG_APP_PROCESS_START: 291 case TAG_KEY_GENERATED: 292 case TAG_KEY_IMPORT: 293 case TAG_KEY_DESTRUCTION: 294 assertEquals(userId, UserHandle.getUserId(getInt(event, UID_INDEX))); 295 break; 296 case TAG_CERT_AUTHORITY_INSTALLED: 297 case TAG_CERT_AUTHORITY_REMOVED: 298 assertEquals(userId, getInt(event, USERID_INDEX)); 299 break; 300 case TAG_KEY_INTEGRITY_VIOLATION: 301 assertEquals(userId, UserHandle.getUserId(getInt(event, 1))); 302 break; 303 } 304 } 305 } 306 307 /** 308 * Generates events for positive test cases. 309 */ testGenerateLogs()310 public void testGenerateLogs() throws Exception { 311 generateKeystoreEvents(); 312 generateKeyChainEvents(); 313 generateAdminEvents(); 314 } 315 generateKeyChainEvents()316 private void generateKeyChainEvents() { 317 installCaCert(); 318 uninstallCaCert(); 319 } 320 generateKeystoreEvents()321 private void generateKeystoreEvents() throws Exception { 322 generateKey(GENERATED_KEY_ALIAS); 323 deleteKey(GENERATED_KEY_ALIAS); 324 importKey(IMPORTED_KEY_ALIAS); 325 deleteKey(IMPORTED_KEY_ALIAS); 326 } 327 generateAdminEvents()328 private void generateAdminEvents() { 329 if (mHasSecureLockScreen) { 330 generatePasswordComplexityEvents(); 331 generateNewStylePasswordComplexityEvents(); 332 } 333 generateLockingPolicyEvents(); 334 generateUserRestrictionEvents(); 335 generateCameraPolicyEvents(); 336 } 337 338 /** 339 * Fetches and checks the events. 340 */ getEvents()341 private List<SecurityEvent> getEvents() throws Exception { 342 List<SecurityEvent> events = null; 343 // Retry once after seeping for 1 second, in case "dpm force-security-logs" hasn't taken 344 // effect just yet. 345 for (int i = 0; i < 2 && events == null; i++) { 346 events = mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT); 347 Log.v(TAG, "getEvents(), batch #" + i + ": " + (events == null ? -1 : events.size()) 348 + " events"); 349 if (events == null) sleep(1000); 350 } 351 352 Log.d(TAG, "getEvents(): received " + events.size() + " events"); 353 if (VERBOSE) dumpSecurityLogs(events); 354 355 try { 356 verifySecurityLogs(events); 357 } catch (AssertionFailedError e) { 358 dumpSecurityLogs(events); 359 throw e; 360 } 361 362 return events; 363 } 364 365 /** 366 * Test: check that there are no gaps between ids in two consecutive batches. Shared preference 367 * is used to store these numbers between test invocations. 368 */ testVerifyLogIds()369 public void testVerifyLogIds() throws Exception { 370 forceSecurityLogs(); 371 final String param = InstrumentationRegistry.getArguments().getString(ARG_BATCH_NUMBER); 372 final int batchId = param == null ? 0 : Integer.parseInt(param); 373 final List<SecurityEvent> events = getEvents(); 374 final SharedPreferences prefs = 375 mContext.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); 376 377 final long firstId = events.get(0).getId(); 378 if (batchId == 0) { 379 assertEquals("Event id wasn't reset.", 0L, firstId); 380 } else { 381 final String prevBatchLastIdKey = PREF_KEY_PREFIX + (batchId - 1); 382 assertTrue("Last event id from previous batch not found in shared prefs", 383 prefs.contains(prevBatchLastIdKey)); 384 final long prevBatchLastId = prefs.getLong(prevBatchLastIdKey, 0); 385 assertEquals("Event ids aren't consecutive between batches", 386 firstId, prevBatchLastId + 1); 387 } 388 389 final String currBatchLastIdKey = PREF_KEY_PREFIX + batchId; 390 final long lastId = events.get(events.size() - 1).getId(); 391 prefs.edit().putLong(currBatchLastIdKey, lastId).commit(); 392 } 393 verifySecurityLogs(List<SecurityEvent> events)394 private void verifySecurityLogs(List<SecurityEvent> events) { 395 assertTrue("Unable to get events", events != null && events.size() > 0); 396 397 // We don't know much about the events, so just call public API methods. 398 for (int i = 0; i < events.size(); i++) { 399 final SecurityEvent event = events.get(i); 400 401 // Skip liblog dropped event. 402 if (event.getTag() == TAG_LIBLOG_DROPPED) { 403 continue; 404 } 405 406 verifyPayloadTypes(event); 407 408 // Test id for monotonically increasing. 409 if (i > 0) { 410 assertEquals("Event IDs are not monotonically increasing within the batch", 411 events.get(i - 1).getId() + 1, event.getId()); 412 } 413 414 // Test parcelling: flatten to a parcel. 415 Parcel p = Parcel.obtain(); 416 event.writeToParcel(p, 0); 417 p.setDataPosition(0); 418 419 // Restore from parcel and check contents. 420 final SecurityEvent restored = SecurityEvent.CREATOR.createFromParcel(p); 421 p.recycle(); 422 423 final int level = event.getLogLevel(); 424 assertTrue(level == LEVEL_INFO || level == LEVEL_WARNING || level == LEVEL_ERROR); 425 426 // For some events data is encapsulated into Object array. 427 if (event.getData() instanceof Object[]) { 428 assertTrue("Parcelling changed the array returned by getData", 429 Arrays.equals((Object[]) event.getData(), (Object[]) restored.getData())); 430 } else { 431 assertEquals("Parcelling changed the result of getData", 432 event.getData(), restored.getData()); 433 } 434 assertEquals("Parcelling changed the result of getId", 435 event.getId(), restored.getId()); 436 assertEquals("Parcelling changed the result of getTag", 437 event.getTag(), restored.getTag()); 438 assertEquals("Parcelling changed the result of getTimeNanos", 439 event.getTimeNanos(), restored.getTimeNanos()); 440 assertEquals("Parcelling changed the result of describeContents", 441 event.describeContents(), restored.describeContents()); 442 } 443 } 444 verifyPayloadTypes(SecurityEvent event)445 private void verifyPayloadTypes(SecurityEvent event) { 446 final List<Class> payloadTypes = PAYLOAD_TYPES_MAP.get(event.getTag()); 447 assertNotNull("event type unknown: " + event.getTag(), payloadTypes); 448 449 if (payloadTypes.size() == 0) { 450 // No payload. 451 assertNull("non-null payload", event.getData()); 452 } else if (payloadTypes.size() == 1) { 453 // Singleton payload. 454 assertTrue(payloadTypes.get(0).isInstance(event.getData())); 455 } else { 456 // Payload is incapsulated into Object[] 457 assertTrue(event.getData() instanceof Object[]); 458 final Object[] dataArray = (Object[]) event.getData(); 459 assertEquals(payloadTypes.size(), dataArray.length); 460 for (int i = 0; i < payloadTypes.size(); i++) { 461 assertTrue(payloadTypes.get(i).isInstance(dataArray[i])); 462 } 463 } 464 } 465 verifyOsStartupEventPresent(List<SecurityEvent> events)466 private void verifyOsStartupEventPresent(List<SecurityEvent> events) { 467 final SecurityEvent event = findEvent("os startup", events, TAG_OS_STARTUP); 468 // Verified boot state, empty if running on emulator 469 assertOneOf(ImmutableSet.of("", "green", "yellow", "orange"), getString(event, 0)); 470 // dm-verity mode, empty if it is disabled 471 assertOneOf(ImmutableSet.of("", "enforcing", "eio", "disabled"), getString(event, 1)); 472 } 473 assertOneOf(Set<String> allowed, String s)474 private void assertOneOf(Set<String> allowed, String s) { 475 assertTrue(String.format("\"%s\" is not one of [%s]", s, String.join(", ", allowed)), 476 allowed.contains(s)); 477 } 478 verifyCryptoSelfTestEventPresent(List<SecurityEvent> events)479 private void verifyCryptoSelfTestEventPresent(List<SecurityEvent> events) { 480 final SecurityEvent event = findEvent("crypto self test complete", 481 events, TAG_CRYPTO_SELF_TEST_COMPLETED); 482 // Success code. 483 assertEquals(1, getInt(event)); 484 } 485 verifyLoggingStartedEventPresent(List<SecurityEvent> events)486 private void verifyLoggingStartedEventPresent(List<SecurityEvent> events) { 487 findEvent("logging started", events, TAG_LOGGING_STARTED); 488 } 489 findEvent(String description, List<SecurityEvent> events, int tag)490 private SecurityEvent findEvent(String description, List<SecurityEvent> events, int tag) { 491 return findEvent(description, events, e -> e.getTag() == tag); 492 } 493 findEvent(String description, List<SecurityEvent> events, Predicate<SecurityEvent> predicate)494 private SecurityEvent findEvent(String description, List<SecurityEvent> events, 495 Predicate<SecurityEvent> predicate) { 496 final List<SecurityEvent> matches = 497 events.stream().filter(predicate).collect(Collectors.toList()); 498 assertEquals("Invalid number of matching events: " + description, 1, matches.size()); 499 return matches.get(0); 500 } 501 getDatum(SecurityEvent event, int index)502 private static Object getDatum(SecurityEvent event, int index) { 503 final Object[] dataArray = (Object[]) event.getData(); 504 return dataArray[index]; 505 } 506 getString(SecurityEvent event, int index)507 private static String getString(SecurityEvent event, int index) { 508 return (String) getDatum(event, index); 509 } 510 getInt(SecurityEvent event)511 private static int getInt(SecurityEvent event) { 512 return (Integer) event.getData(); 513 } 514 getInt(SecurityEvent event, int index)515 private static int getInt(SecurityEvent event, int index) { 516 return (Integer) getDatum(event, index); 517 } 518 getLong(SecurityEvent event, int index)519 private static long getLong(SecurityEvent event, int index) { 520 return (Long) getDatum(event, index); 521 } 522 523 /** 524 * Test: Test enabling security logging. This test should be executed after installing a device 525 * owner so that we check that logging is not enabled by default. This test has a side effect: 526 * security logging is enabled after its execution. 527 */ testEnablingSecurityLogging()528 public void testEnablingSecurityLogging() { 529 assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT)); 530 mDevicePolicyManager.setSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT, true); 531 assertTrue(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT)); 532 } 533 534 /** 535 * Test: Test disabling security logging. This test has a side effect: security logging is 536 * disabled after its execution. 537 */ testDisablingSecurityLogging()538 public void testDisablingSecurityLogging() { 539 mDevicePolicyManager.setSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT, false); 540 assertFalse(mDevicePolicyManager.isSecurityLoggingEnabled(ADMIN_RECEIVER_COMPONENT)); 541 542 // Verify that logs are actually not available. 543 assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT)); 544 } 545 546 /** 547 * Test: retrieving security logs should be rate limited - subsequent attempts should return 548 * null. 549 */ testSecurityLoggingRetrievalRateLimited()550 public void testSecurityLoggingRetrievalRateLimited() { 551 final List<SecurityEvent> logs = mDevicePolicyManager.retrieveSecurityLogs( 552 ADMIN_RECEIVER_COMPONENT); 553 // if logs is null it means that that attempt was rate limited => test PASS 554 if (logs != null) { 555 assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT)); 556 assertNull(mDevicePolicyManager.retrieveSecurityLogs(ADMIN_RECEIVER_COMPONENT)); 557 } 558 } 559 testSetDelegateScope_delegationSecurityLogging()560 public void testSetDelegateScope_delegationSecurityLogging() { 561 setDelegatedScopes(DELEGATE_APP_PKG, Arrays.asList(DELEGATION_SECURITY_LOGGING)); 562 563 assertThat(mDevicePolicyManager.getDelegatedScopes( 564 ADMIN_RECEIVER_COMPONENT, DELEGATE_APP_PKG)).contains(DELEGATION_SECURITY_LOGGING); 565 } 566 testSetDelegateScope_noDelegation()567 public void testSetDelegateScope_noDelegation() { 568 setDelegatedScopes(DELEGATE_APP_PKG, Arrays.asList()); 569 570 assertThat(mDevicePolicyManager.getDelegatedScopes( 571 ADMIN_RECEIVER_COMPONENT, DELEGATE_APP_PKG)) 572 .doesNotContain(DELEGATION_SECURITY_LOGGING); 573 } 574 generateKey(String keyAlias)575 private void generateKey(String keyAlias) throws Exception { 576 final KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", "AndroidKeyStore"); 577 generator.initialize( 578 new KeyGenParameterSpec.Builder(keyAlias, KeyProperties.PURPOSE_SIGN).build()); 579 final KeyPair keyPair = generator.generateKeyPair(); 580 assertNotNull(keyPair); 581 } 582 verifyKeyGeneratedEventPresent(List<SecurityEvent> events, String alias)583 private void verifyKeyGeneratedEventPresent(List<SecurityEvent> events, String alias) { 584 findEvent("key generated", events, 585 e -> e.getTag() == TAG_KEY_GENERATED 586 && getInt(e, SUCCESS_INDEX) == SUCCESS_VALUE 587 && getString(e, ALIAS_INDEX).contains(alias) 588 && getInt(e, UID_INDEX) == Process.myUid()); 589 } 590 importKey(String alias)591 private void importKey(String alias) throws Exception{ 592 final KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); 593 ks.load(null); 594 ks.setEntry(alias, new KeyStore.SecretKeyEntry(new SecretKeySpec(new byte[32], "AES")), 595 new KeyProtection.Builder(KeyProperties.PURPOSE_ENCRYPT).build()); 596 } 597 verifyKeyImportedEventPresent(List<SecurityEvent> events, String alias)598 private void verifyKeyImportedEventPresent(List<SecurityEvent> events, String alias) { 599 findEvent("key imported", events, 600 e -> e.getTag() == TAG_KEY_IMPORT 601 && getInt(e, SUCCESS_INDEX) == SUCCESS_VALUE 602 && getString(e, ALIAS_INDEX).contains(alias) 603 && getInt(e, UID_INDEX) == Process.myUid()); 604 } 605 deleteKey(String keyAlias)606 private void deleteKey(String keyAlias) throws Exception { 607 final KeyStore ks = KeyStore.getInstance("AndroidKeyStore"); 608 ks.load(null); 609 ks.deleteEntry(keyAlias); 610 } 611 verifyKeyDeletedEventPresent(List<SecurityEvent> events, String alias)612 private void verifyKeyDeletedEventPresent(List<SecurityEvent> events, String alias) { 613 findEvent("key deleted", events, 614 e -> e.getTag() == TAG_KEY_DESTRUCTION 615 && getInt(e, SUCCESS_INDEX) == SUCCESS_VALUE 616 && getString(e, ALIAS_INDEX).contains(alias) 617 && getInt(e, UID_INDEX) == Process.myUid()); 618 } 619 installCaCert()620 private void installCaCert() { 621 assertTrue( 622 mDevicePolicyManager.installCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes())); 623 } 624 verifyCertInstalledEventPresent(List<SecurityEvent> events)625 private void verifyCertInstalledEventPresent(List<SecurityEvent> events) { 626 findEvent("cert authority installed", events, 627 e -> e.getTag() == TAG_CERT_AUTHORITY_INSTALLED 628 && getInt(e, SUCCESS_INDEX) == SUCCESS_VALUE 629 && getString(e, SUBJECT_INDEX).equals(TEST_CA_SUBJECT)); 630 } 631 uninstallCaCert()632 private void uninstallCaCert() { 633 mDevicePolicyManager.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes()); 634 } 635 verifyCertUninstalledEventPresent(List<SecurityEvent> events)636 private void verifyCertUninstalledEventPresent(List<SecurityEvent> events) { 637 findEvent("cert authority removed", events, 638 e -> e.getTag() == TAG_CERT_AUTHORITY_REMOVED 639 && getInt(e, SUCCESS_INDEX) == SUCCESS_VALUE 640 && getString(e, SUBJECT_INDEX).equals(TEST_CA_SUBJECT)); 641 } 642 generatePasswordComplexityEvents()643 private void generatePasswordComplexityEvents() { 644 DevicePolicyManager dpm = getDpmToGenerateEvents(); 645 646 dpm.setPasswordQuality(ADMIN_RECEIVER_COMPONENT, PASSWORD_QUALITY_COMPLEX); 647 dpm.setPasswordMinimumLength(ADMIN_RECEIVER_COMPONENT, TEST_PWD_LENGTH); 648 dpm.setPasswordMinimumLetters(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS); 649 dpm.setPasswordMinimumNonLetter(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS); 650 dpm.setPasswordMinimumUpperCase(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS); 651 dpm.setPasswordMinimumLowerCase(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS); 652 dpm.setPasswordMinimumNumeric(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS); 653 dpm.setPasswordMinimumSymbols(ADMIN_RECEIVER_COMPONENT, TEST_PWD_CHARS); 654 } 655 generateNewStylePasswordComplexityEvents()656 private void generateNewStylePasswordComplexityEvents() { 657 DevicePolicyManager dpm = getDpmToGenerateEvents(); 658 659 dpm.setRequiredPasswordComplexity(PASSWORD_COMPLEXITY_HIGH); 660 } 661 verifyPasswordComplexityEventsPresent(List<SecurityEvent> events)662 private void verifyPasswordComplexityEventsPresent(List<SecurityEvent> events) { 663 final int userId = Process.myUserHandle().getIdentifier(); 664 // This reflects default values for password complexity event payload fields. 665 final Object[] expectedPayload = new Object[] { 666 ADMIN_RECEIVER_COMPONENT.getPackageName(), // admin package 667 userId, // admin user 668 userId, // target user 669 0, // default password length 670 0, // default password quality 671 1, // default min letters 672 0, // default min non-letters 673 1, // default min numeric 674 0, // default min uppercase 675 0, // default min lowercase 676 1, // default min symbols 677 }; 678 679 // The order should be consistent with the order in generatePasswordComplexityEvents(), so 680 // that the expected values change in the same sequence as when setting password policies. 681 expectedPayload[PWD_QUALITY_INDEX] = PASSWORD_QUALITY_COMPLEX; 682 findPasswordComplexityEvent("set pwd quality", events, expectedPayload); 683 expectedPayload[PWD_LEN_INDEX] = TEST_PWD_LENGTH; 684 findPasswordComplexityEvent("set pwd length", events, expectedPayload); 685 expectedPayload[LETTERS_INDEX] = TEST_PWD_CHARS; 686 findPasswordComplexityEvent("set pwd min letters", events, expectedPayload); 687 expectedPayload[NON_LETTERS_INDEX] = TEST_PWD_CHARS; 688 findPasswordComplexityEvent("set pwd min non-letters", events, expectedPayload); 689 expectedPayload[UPPERCASE_INDEX] = TEST_PWD_CHARS; 690 findPasswordComplexityEvent("set pwd min uppercase", events, expectedPayload); 691 expectedPayload[LOWERCASE_INDEX] = TEST_PWD_CHARS; 692 findPasswordComplexityEvent("set pwd min lowercase", events, expectedPayload); 693 expectedPayload[NUMERIC_INDEX] = TEST_PWD_CHARS; 694 findPasswordComplexityEvent("set pwd min numeric", events, expectedPayload); 695 expectedPayload[SYMBOLS_INDEX] = TEST_PWD_CHARS; 696 findPasswordComplexityEvent("set pwd min symbols", events, expectedPayload); 697 } 698 verifyNewStylePasswordComplexityEventPresent(List<SecurityEvent> events)699 private void verifyNewStylePasswordComplexityEventPresent(List<SecurityEvent> events) { 700 final int userId = Process.myUserHandle().getIdentifier(); 701 // This reflects default values for password complexity event payload fields. 702 final Object[] expectedPayload = new Object[] { 703 ADMIN_RECEIVER_COMPONENT.getPackageName(), // admin package 704 userId, // admin user 705 userId, // target user 706 PASSWORD_COMPLEXITY_HIGH // password complexity 707 }; 708 709 findNewStylePasswordComplexityEvent("require password complexity", events, expectedPayload); 710 } 711 generateLockingPolicyEvents()712 private void generateLockingPolicyEvents() { 713 DevicePolicyManager dpm = mContext.getSystemService(DevicePolicyManager.class); 714 715 if (mHasSecureLockScreen) { 716 dpm.setPasswordExpirationTimeout(ADMIN_RECEIVER_COMPONENT, TEST_PWD_EXPIRATION_TIMEOUT); 717 dpm.setPasswordHistoryLength(ADMIN_RECEIVER_COMPONENT, TEST_PWD_HISTORY_LENGTH); 718 dpm.setMaximumFailedPasswordsForWipe(ADMIN_RECEIVER_COMPONENT, TEST_PWD_MAX_ATTEMPTS); 719 } 720 dpm.setKeyguardDisabledFeatures(ADMIN_RECEIVER_COMPONENT, 721 KEYGUARD_DISABLE_FINGERPRINT); 722 dpm.setMaximumTimeToLock(ADMIN_RECEIVER_COMPONENT, TEST_MAX_TIME_TO_LOCK); 723 dpm.lockNow(); 724 } 725 verifyLockingPolicyEventsPresent(List<SecurityEvent> events)726 private void verifyLockingPolicyEventsPresent(List<SecurityEvent> events) { 727 final int userId = Process.myUserHandle().getIdentifier(); 728 final String packageName = ADMIN_RECEIVER_COMPONENT.getPackageName(); 729 if (mHasSecureLockScreen) { 730 findEvent("set password expiration", events, 731 e -> e.getTag() == TAG_PASSWORD_EXPIRATION_SET && 732 getString(e, ADMIN_PKG_INDEX).equals(packageName) && 733 getInt(e, ADMIN_USER_INDEX) == userId && 734 getInt(e, TARGET_USER_INDEX) == userId && 735 getLong(e, PWD_EXPIRATION_INDEX) == TEST_PWD_EXPIRATION_TIMEOUT); 736 737 findEvent("set password history length", events, 738 e -> e.getTag() == TAG_PASSWORD_HISTORY_LENGTH_SET && 739 getString(e, ADMIN_PKG_INDEX).equals(packageName) && 740 getInt(e, ADMIN_USER_INDEX) == userId && 741 getInt(e, TARGET_USER_INDEX) == userId && 742 getInt(e, PWD_HIST_LEN_INDEX) == TEST_PWD_HISTORY_LENGTH); 743 744 findEvent("set password attempts", events, 745 e -> e.getTag() == TAG_MAX_PASSWORD_ATTEMPTS_SET && 746 getString(e, ADMIN_PKG_INDEX).equals(packageName) && 747 getInt(e, ADMIN_USER_INDEX) == userId && 748 getInt(e, TARGET_USER_INDEX) == userId && 749 getInt(e, MAX_PWD_ATTEMPTS_INDEX) == TEST_PWD_MAX_ATTEMPTS); 750 } 751 752 findEvent("set keyguard disabled features", events, 753 e -> e.getTag() == TAG_KEYGUARD_DISABLED_FEATURES_SET && 754 getString(e, ADMIN_PKG_INDEX).equals(packageName) && 755 getInt(e, ADMIN_USER_INDEX) == userId && 756 getInt(e, TARGET_USER_INDEX) == userId && 757 getInt(e, KEYGUARD_FEATURES_INDEX) == KEYGUARD_DISABLE_FINGERPRINT); 758 759 findEvent("set screen lock timeout", events, 760 e -> e.getTag() == TAG_MAX_SCREEN_LOCK_TIMEOUT_SET && 761 getString(e, ADMIN_PKG_INDEX).equals(packageName) && 762 getInt(e, ADMIN_USER_INDEX) == userId && 763 getInt(e, TARGET_USER_INDEX) == userId && 764 getLong(e, MAX_SCREEN_TIMEOUT_INDEX) == TEST_MAX_TIME_TO_LOCK); 765 766 findEvent("set screen lock timeout", events, 767 e -> e.getTag() == TAG_REMOTE_LOCK && 768 getString(e, ADMIN_PKG_INDEX).equals(packageName) && 769 getInt(e, ADMIN_USER_INDEX) == userId); 770 } 771 findPasswordComplexityEvent( String description, List<SecurityEvent> events, Object[] expectedPayload)772 private void findPasswordComplexityEvent( 773 String description, List<SecurityEvent> events, Object[] expectedPayload) { 774 findEvent(description, events, 775 byTagAndPayload(TAG_PASSWORD_COMPLEXITY_SET, expectedPayload)); 776 } 777 findNewStylePasswordComplexityEvent( String description, List<SecurityEvent> events, Object[] expectedPayload)778 private void findNewStylePasswordComplexityEvent( 779 String description, List<SecurityEvent> events, Object[] expectedPayload) { 780 findEvent(description, events, 781 byTagAndPayload(TAG_PASSWORD_COMPLEXITY_REQUIRED, expectedPayload)); 782 } 783 byTagAndPayload(int expectedTag, Object[] expectedPayload)784 private Predicate<SecurityEvent> byTagAndPayload(int expectedTag, Object[] expectedPayload) { 785 return (event) -> { 786 boolean tagMatch = event.getTag() == expectedTag; 787 if (!tagMatch) return false; 788 789 Object[] payload = (Object[]) event.getData(); 790 boolean payloadMatch = Arrays.equals(payload, expectedPayload); 791 792 if (!payloadMatch) { 793 Log.w(TAG, "Found event (id=" + event.getId() + ") with tag " 794 + eventLogtoString(event.getTag()) + ", but invalid payload: " 795 + "expected=" + Arrays.toString(expectedPayload) 796 + ", actual=" + Arrays.toString(payload)); 797 } else if (VERBOSE) { 798 Log.v(TAG, "Found event (id=" + event.getId() + ") with tag " 799 + eventLogtoString(event.getTag()) + ", and expected payload (" 800 + Arrays.toString(payload) + ")"); 801 } 802 return payloadMatch; 803 }; 804 } 805 generateUserRestrictionEvents()806 private void generateUserRestrictionEvents() { 807 DevicePolicyManager dpm = getDpmToGenerateEvents(); 808 809 dpm.addUserRestriction(ADMIN_RECEIVER_COMPONENT, UserManager.DISALLOW_PRINTING); 810 dpm.clearUserRestriction(ADMIN_RECEIVER_COMPONENT, UserManager.DISALLOW_PRINTING); 811 } 812 verifyUserRestrictionEventsPresent(List<SecurityEvent> events)813 private void verifyUserRestrictionEventsPresent(List<SecurityEvent> events) { 814 findUserRestrictionEvent("set user restriction", events, TAG_USER_RESTRICTION_ADDED); 815 findUserRestrictionEvent("clear user restriction", events, TAG_USER_RESTRICTION_REMOVED); 816 } 817 findUserRestrictionEvent(String description, List<SecurityEvent> events, int tag)818 private void findUserRestrictionEvent(String description, List<SecurityEvent> events, int tag) { 819 final int userId = Process.myUserHandle().getIdentifier(); 820 findEvent(description, events, 821 e -> e.getTag() == tag && 822 getString(e, ADMIN_PKG_INDEX).equals( 823 ADMIN_RECEIVER_COMPONENT.getPackageName()) && 824 getInt(e, ADMIN_USER_INDEX) == userId && 825 UserManager.DISALLOW_PRINTING.equals(getString(e, USER_RESTRICTION_INDEX))); 826 } 827 generateCameraPolicyEvents()828 private void generateCameraPolicyEvents() { 829 DevicePolicyManager dpm = getDpmToGenerateEvents(); 830 831 dpm.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, true); 832 dpm.setCameraDisabled(ADMIN_RECEIVER_COMPONENT, false); 833 } 834 verifyCameraPolicyEvents(List<SecurityEvent> events)835 private void verifyCameraPolicyEvents(List<SecurityEvent> events) { 836 final int userId = Process.myUserHandle().getIdentifier(); 837 838 findEvent("set camera disabled", events, 839 e -> e.getTag() == TAG_CAMERA_POLICY_SET && 840 getString(e, ADMIN_PKG_INDEX).equals( 841 ADMIN_RECEIVER_COMPONENT.getPackageName()) && 842 getInt(e, ADMIN_USER_INDEX) == userId && 843 getInt(e, TARGET_USER_INDEX) == userId && 844 getInt(e, CAMERA_DISABLED_INDEX) == 1); 845 846 findEvent("set camera enabled", events, 847 e -> e.getTag() == TAG_CAMERA_POLICY_SET && 848 getString(e, ADMIN_PKG_INDEX).equals( 849 ADMIN_RECEIVER_COMPONENT.getPackageName()) && 850 getInt(e, ADMIN_USER_INDEX) == userId && 851 getInt(e, TARGET_USER_INDEX) == userId && 852 getInt(e, CAMERA_DISABLED_INDEX) == 0); 853 } 854 getDpmToGenerateEvents()855 private DevicePolicyManager getDpmToGenerateEvents() { 856 // It must use the dpm for the current user, as mDevicePolicyManager tunnels the calls to 857 // the device owner user on headless system user, which would cause a mismatch in the events 858 return mContext.getSystemService(DevicePolicyManager.class); 859 } 860 eventLogtoString(int log)861 private static String eventLogtoString(int log) { 862 return DebugUtils.constantToString(SecurityLog.class, "TAG_", log); 863 } 864 toString(SecurityEvent event)865 private static String toString(SecurityEvent event) { 866 return "Event[id=" + event.getId() + ",tag=" + eventLogtoString(event.getTag()) + "]"; 867 } 868 dumpSecurityLogs(List<SecurityEvent> events)869 private void dumpSecurityLogs(List<SecurityEvent> events) { 870 Log.d(TAG, "Security events dump (" + events.size() + " events):"); 871 events.forEach((event) -> Log.d(TAG, toString(event))); 872 } 873 } 874