diff options
Diffstat (limited to 'services')
5 files changed, 139 insertions, 4 deletions
diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java index 62156a89392e..6ffcb811ae1d 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowManager.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowManager.java @@ -70,6 +70,7 @@ class RebootEscrowManager { public static final String REBOOT_ESCROW_ARMED_KEY = "reboot_escrow_armed_count"; static final String REBOOT_ESCROW_KEY_ARMED_TIMESTAMP = "reboot_escrow_key_stored_timestamp"; + static final String REBOOT_ESCROW_KEY_PROVIDER = "reboot_escrow_key_provider"; /** * The verified boot 2.0 vbmeta digest of the current slot, the property value is always @@ -113,6 +114,7 @@ class RebootEscrowManager { ERROR_LOAD_ESCROW_KEY, ERROR_RETRY_COUNT_EXHAUSTED, ERROR_UNLOCK_ALL_USERS, + ERROR_PROVIDER_MISMATCH, }) @Retention(RetentionPolicy.SOURCE) @interface RebootEscrowErrorCode { @@ -124,6 +126,7 @@ class RebootEscrowManager { static final int ERROR_LOAD_ESCROW_KEY = 3; static final int ERROR_RETRY_COUNT_EXHAUSTED = 4; static final int ERROR_UNLOCK_ALL_USERS = 5; + static final int ERROR_PROVIDER_MISMATCH = 6; private @RebootEscrowErrorCode int mLoadEscrowDataErrorCode = ERROR_NONE; @@ -360,7 +363,15 @@ class RebootEscrowManager { if (escrowKey == null) { if (mLoadEscrowDataErrorCode == ERROR_NONE) { - mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY; + // Specifically check if the RoR provider has changed after reboot. + int providerType = mInjector.serverBasedResumeOnReboot() + ? RebootEscrowProviderInterface.TYPE_SERVER_BASED + : RebootEscrowProviderInterface.TYPE_HAL; + if (providerType != mStorage.getInt(REBOOT_ESCROW_KEY_PROVIDER, -1, USER_SYSTEM)) { + mLoadEscrowDataErrorCode = ERROR_PROVIDER_MISMATCH; + } else { + mLoadEscrowDataErrorCode = ERROR_LOAD_ESCROW_KEY; + } } onGetRebootEscrowKeyFailed(users, attemptNumber + 1); return; @@ -387,6 +398,7 @@ class RebootEscrowManager { mStorage.removeKey(REBOOT_ESCROW_KEY_ARMED_TIMESTAMP, USER_SYSTEM); mStorage.removeKey(REBOOT_ESCROW_KEY_VBMETA_DIGEST, USER_SYSTEM); mStorage.removeKey(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, USER_SYSTEM); + mStorage.removeKey(REBOOT_ESCROW_KEY_PROVIDER, USER_SYSTEM); } private int getVbmetaDigestStatusOnRestoreComplete() { @@ -435,6 +447,7 @@ class RebootEscrowManager { if (!success && mLoadEscrowDataErrorCode == ERROR_NONE) { mLoadEscrowDataErrorCode = ERROR_UNKNOWN; } + // TODO(179105110) report the duration since boot complete. mInjector.reportMetric(success, mLoadEscrowDataErrorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, -1); @@ -586,6 +599,9 @@ class RebootEscrowManager { return false; } + int actualProviderType = rebootEscrowProvider.getType(); + // TODO(b/183140900) Fail the reboot if provider type mismatches. + RebootEscrowKey escrowKey; synchronized (mKeyGenerationLock) { escrowKey = mPendingRebootEscrowKey; @@ -612,6 +628,7 @@ class RebootEscrowManager { USER_SYSTEM); mStorage.setString(REBOOT_ESCROW_KEY_OTHER_VBMETA_DIGEST, mInjector.getVbmetaDigest(true), USER_SYSTEM); + mStorage.setInt(REBOOT_ESCROW_KEY_PROVIDER, actualProviderType, USER_SYSTEM); mEventLog.addEntry(RebootEscrowEvent.SET_ARMED_STATUS); } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java index 4b00772088f2..e8f6f4abd030 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderHalImpl.java @@ -60,6 +60,11 @@ class RebootEscrowProviderHalImpl implements RebootEscrowProviderInterface { } @Override + public int getType() { + return TYPE_HAL; + } + + @Override public boolean hasRebootEscrowSupport() { return mInjector.getRebootEscrow() != null; } diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java index af6faad3c76e..e106d817c533 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderInterface.java @@ -16,7 +16,11 @@ package com.android.server.locksettings; +import android.annotation.IntDef; + import java.io.IOException; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import javax.crypto.SecretKey; @@ -28,6 +32,21 @@ import javax.crypto.SecretKey; * @hide */ public interface RebootEscrowProviderInterface { + @IntDef(prefix = {"TYPE_"}, value = { + TYPE_HAL, + TYPE_SERVER_BASED, + }) + @Retention(RetentionPolicy.SOURCE) + @interface RebootEscrowProviderType { + } + int TYPE_HAL = 0; + int TYPE_SERVER_BASED = 1; + + /** + * Returns the reboot escrow provider type. + */ + @RebootEscrowProviderType int getType(); + /** * Returns true if the secure store/discard of reboot escrow key is supported. */ diff --git a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java index 697bf08a232e..28669875f1cd 100644 --- a/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java +++ b/services/core/java/com/android/server/locksettings/RebootEscrowProviderServerBasedImpl.java @@ -95,6 +95,11 @@ class RebootEscrowProviderServerBasedImpl implements RebootEscrowProviderInterfa } @Override + public int getType() { + return TYPE_SERVER_BASED; + } + + @Override public boolean hasRebootEscrowSupport() { return mInjector.getServiceConnection() != null; } diff --git a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java index 502b5e4c6633..8c08226201a8 100644 --- a/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java +++ b/services/tests/servicestests/src/com/android/server/locksettings/RebootEscrowManagerTests.java @@ -113,6 +113,8 @@ public class RebootEscrowManagerTests { long getCurrentTimeMillis(); + boolean forceServerBased(); + void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete); } @@ -177,6 +179,9 @@ public class RebootEscrowManagerTests { @Override public boolean serverBasedResumeOnReboot() { + if (mInjected.forceServerBased()) { + return true; + } return mServerBased; } @@ -221,6 +226,7 @@ public class RebootEscrowManagerTests { public void reportMetric(boolean success, int errorCode, int serviceType, int attemptCount, int escrowDurationInSeconds, int vbmetaDigestStatus, int durationSinceBootComplete) { + mInjected.reportMetric(success, errorCode, serviceType, attemptCount, escrowDurationInSeconds, vbmetaDigestStatus, durationSinceBootComplete); } @@ -497,6 +503,84 @@ public class RebootEscrowManagerTests { } @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedRemoteException_Failure() + throws Exception { + setServerBasedRebootEscrowProvider(); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertTrue(mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, + eq(1) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(RemoteException.class); + mService.loadRebootEscrowDataIfAvailable(null); + verify(mServiceConnection).unwrap(any(), anyLong()); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); + } + + @Test + public void loadRebootEscrowDataIfAvailable_ServerBasedIoError_RetryFailure() throws Exception { + setServerBasedRebootEscrowProvider(); + + when(mInjected.getBootCount()).thenReturn(0); + RebootEscrowListener mockListener = mock(RebootEscrowListener.class); + mService.setRebootEscrowListener(mockListener); + mService.prepareRebootEscrow(); + + clearInvocations(mServiceConnection); + mService.callToRebootEscrowIfNeeded(PRIMARY_USER_ID, FAKE_SP_VERSION, FAKE_AUTH_TOKEN); + verify(mockListener).onPreparedForReboot(eq(true)); + verify(mServiceConnection, never()).wrapBlob(any(), anyLong(), anyLong()); + + // Use x -> x for both wrap & unwrap functions. + when(mServiceConnection.wrapBlob(any(), anyLong(), anyLong())) + .thenAnswer(invocation -> invocation.getArgument(0)); + assertTrue(mService.armRebootEscrowIfNeeded()); + verify(mServiceConnection).wrapBlob(any(), anyLong(), anyLong()); + assertTrue(mStorage.hasRebootEscrowServerBlob()); + + // pretend reboot happens here + when(mInjected.getBootCount()).thenReturn(1); + ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), + metricsErrorCodeCaptor.capture(), eq(2) /* Server based */, + eq(2) /* attempt count */, anyInt(), eq(0) /* vbmeta status */, anyInt()); + when(mServiceConnection.unwrap(any(), anyLong())).thenThrow(IOException.class); + + HandlerThread thread = new HandlerThread("RebootEscrowManagerTest"); + thread.start(); + mService.loadRebootEscrowDataIfAvailable(new Handler(thread.getLooper())); + // Sleep 5s for the retry to complete + Thread.sleep(5 * 1000); + assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_RETRY_COUNT_EXHAUSTED), + metricsErrorCodeCaptor.getValue()); + } + + @Test public void loadRebootEscrowDataIfAvailable_ServerBased_RetrySuccess() throws Exception { setServerBasedRebootEscrowProvider(); @@ -659,12 +743,17 @@ public class RebootEscrowManagerTests { when(mInjected.getBootCount()).thenReturn(1); ArgumentCaptor<Boolean> metricsSuccessCaptor = ArgumentCaptor.forClass(Boolean.class); + ArgumentCaptor<Integer> metricsErrorCodeCaptor = ArgumentCaptor.forClass(Integer.class); + // Return a null escrow key doNothing().when(mInjected).reportMetric(metricsSuccessCaptor.capture(), - anyInt() /* error code */, eq(1) /* HAL based */, eq(1) /* attempt count */, - anyInt(), anyInt(), anyInt()); - when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> new byte[32]); + metricsErrorCodeCaptor.capture(), eq(1) /* HAL based */, + eq(1) /* attempt count */, anyInt(), anyInt(), anyInt()); + + when(mRebootEscrow.retrieveKey()).thenAnswer(invocation -> null); mService.loadRebootEscrowDataIfAvailable(null); verify(mRebootEscrow).retrieveKey(); assertFalse(metricsSuccessCaptor.getValue()); + assertEquals(Integer.valueOf(RebootEscrowManager.ERROR_LOAD_ESCROW_KEY), + metricsErrorCodeCaptor.getValue()); } } |