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
This commit is contained in:
Paul Crowley
2016-04-22 13:35:35 -07:00
parent f8892359b0
commit 17e5dce511
4 changed files with 96 additions and 27 deletions

View 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;

View 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

View 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;

View 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 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();
}