diff options
author | Andres Morales <anmorales@google.com> | 2015-03-31 09:19:50 -0700 |
---|---|---|
committer | Andres Morales <anmorales@google.com> | 2015-04-13 18:38:45 -0700 |
commit | 8fa5665f0e757cec0063fb4cf1354f1596f93a91 (patch) | |
tree | 01fe5264f4151e15432fc8047fdde712c8434974 /services | |
parent | 832017963d1d74ba41348deed5eb747b45b441fb (diff) | |
download | frameworks_base-8fa5665f0e757cec0063fb4cf1354f1596f93a91.zip frameworks_base-8fa5665f0e757cec0063fb4cf1354f1596f93a91.tar.gz frameworks_base-8fa5665f0e757cec0063fb4cf1354f1596f93a91.tar.bz2 |
Wire up GateKeeper to LockSettingsService
Adds:
- Communication to GKService
- password upgrade flow
- enroll takes previous credential
Change-Id: I0161b64642be3d0e34ff4a9e6e3ca8569f2d7c0a
Diffstat (limited to 'services')
4 files changed, 259 insertions, 51 deletions
diff --git a/services/core/java/com/android/server/LockSettingsService.java b/services/core/java/com/android/server/LockSettingsService.java index 2e6d1c1..48f4120 100644 --- a/services/core/java/com/android/server/LockSettingsService.java +++ b/services/core/java/com/android/server/LockSettingsService.java @@ -44,12 +44,14 @@ import android.provider.Settings; import android.provider.Settings.Secure; import android.provider.Settings.SettingNotFoundException; import android.security.KeyStore; +import android.service.gatekeeper.IGateKeeperService; import android.text.TextUtils; import android.util.Slog; import com.android.internal.util.ArrayUtils; import com.android.internal.widget.ILockSettings; import com.android.internal.widget.LockPatternUtils; +import com.android.server.LockSettingsStorage.CredentialHash; import java.util.Arrays; import java.util.List; @@ -72,6 +74,7 @@ public class LockSettingsService extends ILockSettings.Stub { private LockPatternUtils mLockPatternUtils; private boolean mFirstCallToVold; + private IGateKeeperService mGateKeeperService; public LockSettingsService(Context context) { mContext = context; @@ -131,6 +134,7 @@ public class LockSettingsService extends ILockSettings.Stub { public void systemReady() { migrateOldData(); + getGateKeeperService(); mStorage.prefetchUser(UserHandle.USER_OWNER); } @@ -277,7 +281,6 @@ public class LockSettingsService extends ILockSettings.Stub { @Override public boolean getBoolean(String key, boolean defaultValue, int userId) throws RemoteException { checkReadPermission(key, userId); - String value = getStringUnchecked(key, null, userId); return TextUtils.isEmpty(value) ? defaultValue : (value.equals("1") || value.equals("true")); @@ -345,40 +348,133 @@ public class LockSettingsService extends ILockSettings.Stub { } } + + private byte[] getCurrentHandle(int userId) { + CredentialHash credential; + byte[] currentHandle; + + int currentHandleType = mStorage.getStoredCredentialType(userId); + switch (currentHandleType) { + case CredentialHash.TYPE_PATTERN: + credential = mStorage.readPatternHash(userId); + currentHandle = credential != null + ? credential.hash + : null; + break; + case CredentialHash.TYPE_PASSWORD: + credential = mStorage.readPasswordHash(userId); + currentHandle = credential != null + ? credential.hash + : null; + break; + case CredentialHash.TYPE_NONE: + default: + currentHandle = null; + break; + } + + // sanity check + if (currentHandleType != CredentialHash.TYPE_NONE && currentHandle == null) { + Slog.e(TAG, "Stored handle type [" + currentHandleType + "] but no handle available"); + } + + return currentHandle; + } + + @Override - public void setLockPattern(String pattern, int userId) throws RemoteException { - checkWritePermission(userId); + public void setLockPattern(String pattern, String savedCredential, int userId) + throws RemoteException { + byte[] currentHandle = getCurrentHandle(userId); - maybeUpdateKeystore(pattern, userId); + if (currentHandle == null) { + if (savedCredential != null) { + Slog.w(TAG, "Saved credential provided, but none stored"); + } + savedCredential = null; + } - final byte[] hash = LockPatternUtils.patternToHash( - LockPatternUtils.stringToPattern(pattern)); - mStorage.writePatternHash(hash, userId); + byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, pattern, userId); + if (enrolledHandle != null) { + mStorage.writePatternHash(enrolledHandle, userId); + } else { + Slog.e(TAG, "Failed to enroll pattern"); + } } + @Override - public void setLockPassword(String password, int userId) throws RemoteException { - checkWritePermission(userId); + public void setLockPassword(String password, String savedCredential, int userId) + throws RemoteException { - maybeUpdateKeystore(password, userId); + byte[] currentHandle = getCurrentHandle(userId); + + if (currentHandle == null) { + if (savedCredential != null) { + Slog.w(TAG, "Saved credential provided, but none stored"); + } + savedCredential = null; + } + + byte[] enrolledHandle = enrollCredential(currentHandle, savedCredential, password, userId); + if (enrolledHandle != null) { + mStorage.writePasswordHash(enrolledHandle, userId); + } else { + Slog.e(TAG, "Failed to enroll password"); + } + } + + private byte[] enrollCredential(byte[] enrolledHandle, + String enrolledCredential, String toEnroll, int userId) + throws RemoteException { + checkWritePermission(userId); + byte[] enrolledCredentialBytes = enrolledCredential == null + ? null + : enrolledCredential.getBytes(); + byte[] toEnrollBytes = toEnroll == null + ? null + : toEnroll.getBytes(); + byte[] hash = getGateKeeperService().enroll(userId, enrolledHandle, enrolledCredentialBytes, + toEnrollBytes); + + if (hash != null) { + maybeUpdateKeystore(toEnroll, userId); + } - mStorage.writePasswordHash(mLockPatternUtils.passwordToHash(password, userId), userId); + return hash; } @Override public boolean checkPattern(String pattern, int userId) throws RemoteException { checkPasswordReadPermission(userId); - byte[] hash = LockPatternUtils.patternToHash(LockPatternUtils.stringToPattern(pattern)); - byte[] storedHash = mStorage.readPatternHash(userId); + + CredentialHash storedHash = mStorage.readPatternHash(userId); if (storedHash == null) { return true; } - boolean matched = Arrays.equals(hash, storedHash); + if (storedHash.version == CredentialHash.VERSION_LEGACY) { + // Try the old backend and upgrade if a match is found + byte[] hash = LockPatternUtils.patternToHash( + LockPatternUtils.stringToPattern(pattern)); + boolean matched = Arrays.equals(hash, storedHash.hash); + if (matched && !TextUtils.isEmpty(pattern)) { + maybeUpdateKeystore(pattern, userId); + // migrate pattern to GateKeeper + setLockPattern(pattern, null, userId); + } + + return matched; + } + + boolean matched = getGateKeeperService() + .verify(userId, storedHash.hash, pattern.getBytes()); if (matched && !TextUtils.isEmpty(pattern)) { maybeUpdateKeystore(pattern, userId); + return true; } + return matched; } @@ -386,17 +482,31 @@ public class LockSettingsService extends ILockSettings.Stub { public boolean checkPassword(String password, int userId) throws RemoteException { checkPasswordReadPermission(userId); - byte[] hash = mLockPatternUtils.passwordToHash(password, userId); - byte[] storedHash = mStorage.readPasswordHash(userId); + CredentialHash storedHash = mStorage.readPasswordHash(userId); if (storedHash == null) { return true; } - boolean matched = Arrays.equals(hash, storedHash); - if (matched && !TextUtils.isEmpty(password)) { + if (storedHash.version == CredentialHash.VERSION_LEGACY) { + byte[] hash = mLockPatternUtils.passwordToHash(password, userId); + boolean matched = Arrays.equals(hash, storedHash.hash); + if (matched && !TextUtils.isEmpty(password)) { + maybeUpdateKeystore(password, userId); + // migrate password to GateKeeper + setLockPassword(password, null, userId); + } + return matched; + } + + + + boolean matched = getGateKeeperService() + .verify(userId, storedHash.hash, password.getBytes()); + if (!TextUtils.isEmpty(password) && matched) { maybeUpdateKeystore(password, userId); } + return matched; } @@ -497,4 +607,21 @@ public class LockSettingsService extends ILockSettings.Stub { } return null; } + + private synchronized IGateKeeperService getGateKeeperService() { + if (mGateKeeperService != null) { + return mGateKeeperService; + } + + final IBinder service = + ServiceManager.getService("android.service.gatekeeper.IGateKeeperService"); + if (service != null) { + mGateKeeperService = IGateKeeperService.Stub.asInterface(service); + return mGateKeeperService; + } + + Slog.e(TAG, "Unable to acquire GateKeeperService"); + return null; + } + } diff --git a/services/core/java/com/android/server/LockSettingsStorage.java b/services/core/java/com/android/server/LockSettingsStorage.java index d81daa9..f202c36 100644 --- a/services/core/java/com/android/server/LockSettingsStorage.java +++ b/services/core/java/com/android/server/LockSettingsStorage.java @@ -56,8 +56,10 @@ class LockSettingsStorage { }; private static final String SYSTEM_DIRECTORY = "/system/"; - private static final String LOCK_PATTERN_FILE = "gesture.key"; - private static final String LOCK_PASSWORD_FILE = "password.key"; + private static final String LOCK_PATTERN_FILE = "gatekeeper.gesture.key"; + private static final String LEGACY_LOCK_PATTERN_FILE = "gesture.key"; + private static final String LOCK_PASSWORD_FILE = "gatekeeper.password.key"; + private static final String LEGACY_LOCK_PASSWORD_FILE = "password.key"; private static final Object DEFAULT = new Object(); @@ -66,6 +68,25 @@ class LockSettingsStorage { private final Cache mCache = new Cache(); private final Object mFileWriteLock = new Object(); + private int mStoredCredentialType; + + class CredentialHash { + static final int TYPE_NONE = -1; + static final int TYPE_PATTERN = 1; + static final int TYPE_PASSWORD = 2; + + static final int VERSION_LEGACY = 0; + static final int VERSION_GATEKEEPER = 1; + + CredentialHash(byte[] hash, int version) { + this.hash = hash; + this.version = version; + } + + byte[] hash; + int version; + } + public LockSettingsStorage(Context context, Callback callback) { mContext = context; mOpenHelper = new DatabaseHelper(context, callback); @@ -148,28 +169,72 @@ class LockSettingsStorage { readPatternHash(userId); } - public byte[] readPasswordHash(int userId) { - final byte[] stored = readFile(getLockPasswordFilename(userId)); + public int getStoredCredentialType(int userId) { + if (mStoredCredentialType != 0) { + return mStoredCredentialType; + } + + CredentialHash pattern = readPatternHash(userId); + if (pattern == null) { + if (readPasswordHash(userId) != null) { + mStoredCredentialType = CredentialHash.TYPE_PASSWORD; + } else { + mStoredCredentialType = CredentialHash.TYPE_NONE; + } + } else { + CredentialHash password = readPasswordHash(userId); + if (password != null) { + // Both will never be GateKeeper + if (password.version == CredentialHash.VERSION_GATEKEEPER) { + mStoredCredentialType = CredentialHash.TYPE_PASSWORD; + } else { + mStoredCredentialType = CredentialHash.TYPE_PATTERN; + } + } else { + mStoredCredentialType = CredentialHash.TYPE_PATTERN; + } + } + + return mStoredCredentialType; + } + + + public CredentialHash readPasswordHash(int userId) { + byte[] stored = readFile(getLockPasswordFilename(userId)); if (stored != null && stored.length > 0) { - return stored; + return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER); } + + stored = readFile(getLegacyLockPasswordFilename(userId)); + if (stored != null && stored.length > 0) { + return new CredentialHash(stored, CredentialHash.VERSION_LEGACY); + } + return null; } - public byte[] readPatternHash(int userId) { - final byte[] stored = readFile(getLockPatternFilename(userId)); + public CredentialHash readPatternHash(int userId) { + byte[] stored = readFile(getLockPatternFilename(userId)); if (stored != null && stored.length > 0) { - return stored; + return new CredentialHash(stored, CredentialHash.VERSION_GATEKEEPER); } + + stored = readFile(getLegacyLockPatternFilename(userId)); + if (stored != null && stored.length > 0) { + return new CredentialHash(stored, CredentialHash.VERSION_LEGACY); + } + return null; } public boolean hasPassword(int userId) { - return hasFile(getLockPasswordFilename(userId)); + return hasFile(getLockPasswordFilename(userId)) || + hasFile(getLegacyLockPasswordFilename(userId)); } public boolean hasPattern(int userId) { - return hasFile(getLockPatternFilename(userId)); + return hasFile(getLockPatternFilename(userId)) || + hasFile(getLegacyLockPatternFilename(userId)); } private boolean hasFile(String name) { @@ -237,6 +302,9 @@ class LockSettingsStorage { } public void writePatternHash(byte[] hash, int userId) { + mStoredCredentialType = hash == null + ? CredentialHash.TYPE_NONE + : CredentialHash.TYPE_PATTERN; writeFile(getLockPatternFilename(userId), hash); clearPasswordHash(userId); } @@ -246,6 +314,9 @@ class LockSettingsStorage { } public void writePasswordHash(byte[] hash, int userId) { + mStoredCredentialType = hash == null + ? CredentialHash.TYPE_NONE + : CredentialHash.TYPE_PASSWORD; writeFile(getLockPasswordFilename(userId), hash); clearPatternHash(userId); } @@ -264,6 +335,16 @@ class LockSettingsStorage { return getLockCredentialFilePathForUser(userId, LOCK_PASSWORD_FILE); } + @VisibleForTesting + String getLegacyLockPatternFilename(int userId) { + return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PATTERN_FILE); + } + + @VisibleForTesting + String getLegacyLockPasswordFilename(int userId) { + return getLockCredentialFilePathForUser(userId, LEGACY_LOCK_PASSWORD_FILE); + } + private String getLockCredentialFilePathForUser(int userId, String basename) { userId = getUserParentOrSelfId(userId); String dataSystemDirectory = diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java index 3677132..2ff80c6 100644 --- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java +++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java @@ -2799,7 +2799,7 @@ public class DevicePolicyManagerService extends IDevicePolicyManager.Stub { try { LockPatternUtils utils = new LockPatternUtils(mContext); if (!TextUtils.isEmpty(password)) { - utils.saveLockPassword(password, quality, userHandle); + utils.saveLockPassword(password, null, quality, userHandle); } else { utils.clearLock(userHandle); } diff --git a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java index bf0e75d..dae8447 100644 --- a/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java +++ b/services/tests/servicestests/src/com/android/server/LockSettingsStorageTests.java @@ -219,31 +219,31 @@ public class LockSettingsStorageTests extends AndroidTestCase { public void testPassword_Write() { mStorage.writePasswordHash("thepassword".getBytes(), 0); - assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0)); + assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash); mStorage.clearCache(); - assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0)); + assertArrayEquals("thepassword".getBytes(), mStorage.readPasswordHash(0).hash); } public void testPassword_WriteProfileWritesParent() { mStorage.writePasswordHash("parentpasswordd".getBytes(), 1); mStorage.writePasswordHash("profilepassword".getBytes(), 2); - assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1)); - assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2)); + assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash); + assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash); mStorage.clearCache(); - assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1)); - assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2)); + assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(1).hash); + assertArrayEquals("profilepassword".getBytes(), mStorage.readPasswordHash(2).hash); } public void testPassword_WriteParentWritesProfile() { mStorage.writePasswordHash("profilepassword".getBytes(), 2); mStorage.writePasswordHash("parentpasswordd".getBytes(), 1); - assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1)); - assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2)); + assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash); + assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash); mStorage.clearCache(); - assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1)); - assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2)); + assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(1).hash); + assertArrayEquals("parentpasswordd".getBytes(), mStorage.readPasswordHash(2).hash); } public void testPattern_Default() { @@ -253,31 +253,31 @@ public class LockSettingsStorageTests extends AndroidTestCase { public void testPattern_Write() { mStorage.writePatternHash("thepattern".getBytes(), 0); - assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0)); + assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash); mStorage.clearCache(); - assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0)); + assertArrayEquals("thepattern".getBytes(), mStorage.readPatternHash(0).hash); } public void testPattern_WriteProfileWritesParent() { mStorage.writePatternHash("parentpatternn".getBytes(), 1); mStorage.writePatternHash("profilepattern".getBytes(), 2); - assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1)); - assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2)); + assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash); + assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash); mStorage.clearCache(); - assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1)); - assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2)); + assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(1).hash); + assertArrayEquals("profilepattern".getBytes(), mStorage.readPatternHash(2).hash); } public void testPattern_WriteParentWritesProfile() { mStorage.writePatternHash("profilepattern".getBytes(), 2); mStorage.writePatternHash("parentpatternn".getBytes(), 1); - assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1)); - assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2)); + assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash); + assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash); mStorage.clearCache(); - assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1)); - assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2)); + assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(1).hash); + assertArrayEquals("parentpatternn".getBytes(), mStorage.readPatternHash(2).hash); } public void testPrefetch() { @@ -289,8 +289,8 @@ public class LockSettingsStorageTests extends AndroidTestCase { mStorage.prefetchUser(0); assertEquals("toBeFetched", mStorage.readKeyValue("key", "default", 0)); - assertArrayEquals("pattern".getBytes(), mStorage.readPatternHash(0)); - assertArrayEquals("password".getBytes(), mStorage.readPasswordHash(0)); + assertArrayEquals("pattern".getBytes(), mStorage.readPatternHash(0).hash); + assertArrayEquals("password".getBytes(), mStorage.readPasswordHash(0).hash); } public void testFileLocation_Owner() { |