From a99b8b5e3fe456b74b9f86e12bebebb5e418f58e Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Thu, 11 Jun 2015 13:27:34 -0700 Subject: [PATCH] Do not require USE_FINGERPRINT for getAuthenticatorId. This removes the requirement to hold the USE_FINGERPRINT permission to successfully invoke FingerprintManager.getAuthenticatorId(). This is needed because Android Keystore classes which run inside app processes occasionally need to access this authenticator ID. The access however is not necessarily triggered by the developer using APIs to do with fingerprints. Thus, if an app does not hold the USE_FINGERPRINT permission and uses Android Keystore API, it may unexpectedly encounter a SecurityException. It's OK to provide access to authenticator ID without requiring USE_FINGERPRINT permission because there are other ways to access this ID without holding that permission, such as though hidden KeyStore API. Once Android Keystore code is restructured to no longer require access to authenticator ID, this CL can be reverted. Bug: 21030147 Change-Id: I9af29830abce34c46e29e5c1682cc3ab88c95c00 --- keystore/java/android/security/KeyStore.java | 18 +++++------------- .../security/keystore/KeymasterUtils.java | 11 ++++------- .../fingerprint/FingerprintService.java | 19 ++++++++++++++++--- 3 files changed, 25 insertions(+), 23 deletions(-) diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index 35fcda6033ff6..6a08368321d88 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -108,15 +108,10 @@ public class KeyStore { } public static Context getApplicationContext() { - ActivityThread activityThread = ActivityThread.currentActivityThread(); - if (activityThread == null) { - throw new IllegalStateException( - "Failed to obtain application Context: no ActivityThread"); - } - Application application = activityThread.getApplication(); + Application application = ActivityThread.currentApplication(); if (application == null) { throw new IllegalStateException( - "Failed to obtain application Context: no Application"); + "Failed to obtain application Context from ActivityThread"); } return application; } @@ -698,16 +693,13 @@ public class KeyStore { } private long getFingerprintOnlySid() { - FingerprintManager fingerprintManager = - mContext.getSystemService(FingerprintManager.class); + FingerprintManager fingerprintManager = mContext.getSystemService(FingerprintManager.class); if (fingerprintManager == null) { return 0; } - if (!fingerprintManager.isHardwareDetected()) { - return 0; - } - + // TODO: Restore USE_FINGERPRINT permission check in + // FingerprintManager.getAuthenticatorId once the ID is no longer needed here. return fingerprintManager.getAuthenticatorId(); } diff --git a/keystore/java/android/security/keystore/KeymasterUtils.java b/keystore/java/android/security/keystore/KeymasterUtils.java index 0639d49ba22af..4b37d905edc55 100644 --- a/keystore/java/android/security/keystore/KeymasterUtils.java +++ b/keystore/java/android/security/keystore/KeymasterUtils.java @@ -101,13 +101,10 @@ public abstract class KeymasterUtils { // fingerprint-only auth. FingerprintManager fingerprintManager = KeyStore.getApplicationContext().getSystemService(FingerprintManager.class); - if ((fingerprintManager == null) || (!fingerprintManager.isHardwareDetected())) { - throw new IllegalStateException( - "This device does not support keys which require authentication for every" - + " use -- this requires fingerprint authentication which is not" - + " available on this device"); - } - long fingerprintOnlySid = fingerprintManager.getAuthenticatorId(); + // TODO: Restore USE_FINGERPRINT permission check in + // FingerprintManager.getAuthenticatorId once the ID is no longer needed here. + long fingerprintOnlySid = + (fingerprintManager != null) ? fingerprintManager.getAuthenticatorId() : 0; if (fingerprintOnlySid == 0) { throw new IllegalStateException( "At least one fingerprint must be enrolled to create keys requiring user" diff --git a/services/core/java/com/android/server/fingerprint/FingerprintService.java b/services/core/java/com/android/server/fingerprint/FingerprintService.java index b0d576550cab3..7f0be5766d1a7 100644 --- a/services/core/java/com/android/server/fingerprint/FingerprintService.java +++ b/services/core/java/com/android/server/fingerprint/FingerprintService.java @@ -706,9 +706,22 @@ public class FingerprintService extends SystemService implements IBinder.DeathRe @Override // Binder call public long getAuthenticatorId(String opPackageName) { - if (!canUseFingerprint(opPackageName)) { - return 0; - } + // In this method, we're not checking whether the caller is permitted to use fingerprint + // API because current authenticator ID is leaked (in a more contrived way) via Android + // Keystore (android.security.keystore package): the user of that API can create a key + // which requires fingerprint authentication for its use, and then query the key's + // characteristics (hidden API) which returns, among other things, fingerprint + // authenticator ID which was active at key creation time. + // + // Reason: The part of Android Keystore which runs inside an app's process invokes this + // method in certain cases. Those cases are not always where the developer demonstrates + // explicit intent to use fingerprint functionality. Thus, to avoiding throwing an + // unexpected SecurityException this method does not check whether its caller is + // permitted to use fingerprint API. + // + // The permission check should be restored once Android Keystore no longer invokes this + // method from inside app processes. + return FingerprintService.this.getAuthenticatorId(); } }