OSDN Git Service

Two phases to set the password for disk encryption
authorPaul Crowley <paulcrowley@google.com>
Fri, 22 Apr 2016 20:35:35 +0000 (13:35 -0700)
committerPaul Crowley <paulcrowley@google.com>
Fri, 6 May 2016 18:10:23 +0000 (11:10 -0700)
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

core/java/android/os/storage/IMountService.java
services/core/java/com/android/server/LockSettingsService.java
services/core/java/com/android/server/LockSettingsStorage.java
services/core/java/com/android/server/MountService.java

index 3915b02..b9bcd1c 100644 (file)
@@ -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;
index 434464c..c2a1c50 100644 (file)
@@ -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
index 9ab6300..ab91a73 100644 (file)
@@ -74,7 +74,7 @@ class LockSettingsStorage {
 
     private SparseArray<Integer> mStoredCredentialType;
 
-    class CredentialHash {
+    static class CredentialHash {
         static final int TYPE_NONE = -1;
         static final int TYPE_PATTERN = 1;
         static final int TYPE_PASSWORD = 2;
index 25ce485..c89b6ea 100644 (file)
@@ -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 addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) {
+        enforcePermission(android.Manifest.permission.STORAGE_INTERNAL);
+        waitForReady();
+
+        try {
+            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 changeUserKey(int userId, int serialNumber,
-            byte[] token, byte[] oldSecret, byte[] newSecret) {
+    public void fixateNewestUserKeyAuth(int userId) {
         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", "fixate_newest_user_key_auth", userId);
         } catch (NativeDaemonConnectorException e) {
             throw e.rethrowAsParcelableException();
         }