From 17e5dce5112fece2d2b9cd070c2f96bf65108e40 Mon Sep 17 00:00:00 2001 From: Paul Crowley Date: Fri, 22 Apr 2016 13:35:35 -0700 Subject: [PATCH] Two phases to set the password for disk encryption In one phase, we make the new password work, and in the second we make it the only one which works ("fixation"). This means that we can set the password in Gatekeeper between these two phases, and a crash doesn't break things. Unlocking a user automatically fixates the presented credential. Bug: 28154455 Change-Id: I18812f9ce753486ce4e33b4fe2cca392b006b39c --- .../android/os/storage/IMountService.java | 47 ++++++++++++++----- .../android/server/LockSettingsService.java | 45 ++++++++++++++---- .../android/server/LockSettingsStorage.java | 2 +- .../java/com/android/server/MountService.java | 29 ++++++++++-- 4 files changed, 96 insertions(+), 27 deletions(-) diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java index 3915b02574931..b9bcd1c190a4a 100644 --- a/core/java/android/os/storage/IMountService.java +++ b/core/java/android/os/storage/IMountService.java @@ -1233,8 +1233,8 @@ public interface IMountService extends IInterface { } @Override - public void changeUserKey(int userId, int serialNumber, - byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException { + public void addUserKeyAuth(int userId, int serialNumber, + byte[] token, byte[] secret) throws RemoteException { Parcel _data = Parcel.obtain(); Parcel _reply = Parcel.obtain(); try { @@ -1242,9 +1242,23 @@ public interface IMountService extends IInterface { _data.writeInt(userId); _data.writeInt(serialNumber); _data.writeByteArray(token); - _data.writeByteArray(oldSecret); - _data.writeByteArray(newSecret); - mRemote.transact(Stub.TRANSACTION_changeUserKey, _data, _reply, 0); + _data.writeByteArray(secret); + mRemote.transact(Stub.TRANSACTION_addUserKeyAuth, _data, _reply, 0); + _reply.readException(); + } finally { + _reply.recycle(); + _data.recycle(); + } + } + + @Override + public void fixateNewestUserKeyAuth(int userId) throws RemoteException { + Parcel _data = Parcel.obtain(); + Parcel _reply = Parcel.obtain(); + try { + _data.writeInterfaceToken(DESCRIPTOR); + _data.writeInt(userId); + mRemote.transact(Stub.TRANSACTION_fixateNewestUserKeyAuth, _data, _reply, 0); _reply.readException(); } finally { _reply.recycle(); @@ -1489,7 +1503,9 @@ public interface IMountService extends IInterface { static final int TRANSACTION_mountAppFuse = IBinder.FIRST_CALL_TRANSACTION + 69; - static final int TRANSACTION_changeUserKey = IBinder.FIRST_CALL_TRANSACTION + 70; + static final int TRANSACTION_addUserKeyAuth = IBinder.FIRST_CALL_TRANSACTION + 70; + + static final int TRANSACTION_fixateNewestUserKeyAuth = IBinder.FIRST_CALL_TRANSACTION + 71; /** * Cast an IBinder object into an IMountService interface, generating a @@ -2069,14 +2085,20 @@ public interface IMountService extends IInterface { reply.writeNoException(); return true; } - case TRANSACTION_changeUserKey: { + case TRANSACTION_addUserKeyAuth: { data.enforceInterface(DESCRIPTOR); int userId = data.readInt(); int serialNumber = data.readInt(); byte[] token = data.createByteArray(); - byte[] oldSecret = data.createByteArray(); - byte[] newSecret = data.createByteArray(); - changeUserKey(userId, serialNumber, token, oldSecret, newSecret); + byte[] secret = data.createByteArray(); + addUserKeyAuth(userId, serialNumber, token, secret); + reply.writeNoException(); + return true; + } + case TRANSACTION_fixateNewestUserKeyAuth: { + data.enforceInterface(DESCRIPTOR); + int userId = data.readInt(); + fixateNewestUserKeyAuth(userId); reply.writeNoException(); return true; } @@ -2452,8 +2474,9 @@ public interface IMountService extends IInterface { public void createUserKey(int userId, int serialNumber, boolean ephemeral) throws RemoteException; public void destroyUserKey(int userId) throws RemoteException; - public void changeUserKey(int userId, int serialNumber, - byte[] token, byte[] oldSecret, byte[] newSecret) throws RemoteException; + public void addUserKeyAuth(int userId, int serialNumber, + byte[] token, byte[] secret) throws RemoteException; + public void fixateNewestUserKeyAuth(int userId) throws RemoteException; public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) throws RemoteException; diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 434464c90a09d..c2a1c50a8d2b7 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -789,10 +789,11 @@ public class LockSettingsService extends ILockSettings.Stub { if (isSecure) { tieManagedProfileLockIfNecessary(managedUserId, null); } else { + clearUserKeyProtection(managedUserId); getGateKeeperService().clearSecureUserId(managedUserId); mStorage.writePatternHash(null, managedUserId); setKeystorePassword(null, managedUserId); - clearUserKeyProtection(managedUserId); + fixateNewestUserKeyAuth(managedUserId); mStorage.removeChildProfileLock(managedUserId); removeKeystoreProfileKey(managedUserId); } @@ -827,10 +828,11 @@ public class LockSettingsService extends ILockSettings.Stub { byte[] currentHandle = getCurrentHandle(userId); if (pattern == null) { + clearUserKeyProtection(userId); getGateKeeperService().clearSecureUserId(userId); mStorage.writePatternHash(null, userId); setKeystorePassword(null, userId); - clearUserKeyProtection(userId); + fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); return; } @@ -860,8 +862,12 @@ public class LockSettingsService extends ILockSettings.Stub { byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId); if (enrolledHandle != null) { + CredentialHash willStore + = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER); + setUserKeyProtection(userId, pattern, + doVerifyPattern(pattern, willStore, true, 0, userId)); mStorage.writePatternHash(enrolledHandle, userId); - setUserKeyProtection(userId, pattern, verifyPattern(pattern, 0, userId)); + fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); } else { throw new RemoteException("Failed to enroll pattern"); @@ -884,10 +890,11 @@ public class LockSettingsService extends ILockSettings.Stub { throws RemoteException { byte[] currentHandle = getCurrentHandle(userId); if (password == null) { + clearUserKeyProtection(userId); getGateKeeperService().clearSecureUserId(userId); mStorage.writePasswordHash(null, userId); setKeystorePassword(null, userId); - clearUserKeyProtection(userId); + fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); return; } @@ -915,8 +922,12 @@ public class LockSettingsService extends ILockSettings.Stub { byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId); if (enrolledHandle != null) { + CredentialHash willStore + = new CredentialHash(enrolledHandle, CredentialHash.VERSION_GATEKEEPER); + setUserKeyProtection(userId, password, + doVerifyPassword(password, willStore, true, 0, userId)); mStorage.writePasswordHash(enrolledHandle, userId); - setUserKeyProtection(userId, password, verifyPassword(password, 0, userId)); + fixateNewestUserKeyAuth(userId); onUserLockChanged(userId); } else { throw new RemoteException("Failed to enroll password"); @@ -1021,11 +1032,11 @@ public class LockSettingsService extends ILockSettings.Stub { if (token == null) { throw new RemoteException("Empty payload verifying a credential we just set"); } - changeUserKey(userId, token, secretFromCredential(credential)); + addUserKeyAuth(userId, token, secretFromCredential(credential)); } private void clearUserKeyProtection(int userId) throws RemoteException { - changeUserKey(userId, null, null); + addUserKeyAuth(userId, null, null); } private static byte[] secretFromCredential(String credential) throws RemoteException { @@ -1044,18 +1055,23 @@ public class LockSettingsService extends ILockSettings.Stub { } } - private void changeUserKey(int userId, byte[] token, byte[] secret) + private void addUserKeyAuth(int userId, byte[] token, byte[] secret) throws RemoteException { final UserInfo userInfo = UserManager.get(mContext).getUserInfo(userId); final IMountService mountService = getMountService(); final long callingId = Binder.clearCallingIdentity(); try { - mountService.changeUserKey(userId, userInfo.serialNumber, token, null, secret); + mountService.addUserKeyAuth(userId, userInfo.serialNumber, token, secret); } finally { Binder.restoreCallingIdentity(callingId); } } + private void fixateNewestUserKeyAuth(int userId) + throws RemoteException { + getMountService().fixateNewestUserKeyAuth(userId); + } + @Override public VerifyCredentialResponse checkPattern(String pattern, int userId) throws RemoteException { return doVerifyPattern(pattern, false, 0, userId); @@ -1071,6 +1087,11 @@ public class LockSettingsService extends ILockSettings.Stub { long challenge, int userId) throws RemoteException { checkPasswordReadPermission(userId); CredentialHash storedHash = mStorage.readPatternHash(userId); + return doVerifyPattern(pattern, storedHash, hasChallenge, challenge, userId); + } + + private VerifyCredentialResponse doVerifyPattern(String pattern, CredentialHash storedHash, + boolean hasChallenge, long challenge, int userId) throws RemoteException { boolean shouldReEnrollBaseZero = storedHash != null && storedHash.isBaseZeroPattern; String patternToVerify; @@ -1108,7 +1129,6 @@ public class LockSettingsService extends ILockSettings.Stub { } return response; - } @Override @@ -1158,6 +1178,11 @@ public class LockSettingsService extends ILockSettings.Stub { long challenge, int userId) throws RemoteException { checkPasswordReadPermission(userId); CredentialHash storedHash = mStorage.readPasswordHash(userId); + return doVerifyPassword(password, storedHash, hasChallenge, challenge, userId); + } + + private VerifyCredentialResponse doVerifyPassword(String password, CredentialHash storedHash, + boolean hasChallenge, long challenge, int userId) throws RemoteException { return verifyCredential(userId, storedHash, password, hasChallenge, challenge, new CredentialUtil() { @Override diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java index 9ab63003c8dc4..ab91a732f3ccf 100644 --- a/services/core/java/com/android/server/LockSettingsStorage.java +++ b/services/core/java/com/android/server/LockSettingsStorage.java @@ -74,7 +74,7 @@ class LockSettingsStorage { private SparseArray mStoredCredentialType; - class CredentialHash { + static class CredentialHash { static final int TYPE_NONE = -1; static final int TYPE_PATTERN = 1; static final int TYPE_PASSWORD = 2; diff --git a/services/core/java/com/android/server/MountService.java b/services/core/java/com/android/server/MountService.java index 25ce4856be1fe..c89b6ea4f18d7 100644 --- a/services/core/java/com/android/server/MountService.java +++ b/services/core/java/com/android/server/MountService.java @@ -2816,15 +2816,36 @@ class MountService extends IMountService.Stub } } + /* + * Add this token/secret pair to the set of ways we can recover a disk encryption key. + * Changing the token/secret for a disk encryption key is done in two phases: first, adding + * a new token/secret pair with this call, then delting all other pairs with + * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as + * Gatekeeper, to be updated between the two calls. + */ @Override - public void changeUserKey(int userId, int serialNumber, - byte[] token, byte[] oldSecret, byte[] newSecret) { + public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); waitForReady(); try { - mCryptConnector.execute("cryptfs", "change_user_key", userId, serialNumber, - encodeBytes(token), encodeBytes(oldSecret), encodeBytes(newSecret)); + mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber, + encodeBytes(token), encodeBytes(secret)); + } catch (NativeDaemonConnectorException e) { + throw e.rethrowAsParcelableException(); + } + } + + /* + * Delete all disk encryption token/secret pairs except the most recently added one + */ + @Override + public void fixateNewestUserKeyAuth(int userId) { + enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); + waitForReady(); + + try { + mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId); } catch (NativeDaemonConnectorException e) { throw e.rethrowAsParcelableException(); }