Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 19db3fab authored by Shawn Willden's avatar Shawn Willden
Browse files

Update LockSettingsService to support hardened FRP

This change does three things:

1.  Broadcasts a new protected intent to notify system apps such as
    GMSCore of LSKF changes so they can react by updating FRP data.
2.  Deactivates FRP (if active) when LSKF authentication is performed
    using the FRP credential.
3.  Modifies LockSettingsService to use the new PDB API to query for
    FRP status, rather than relying on the SECURE_FRP_MODE property,
    which can more easily be manipulated by attackers.

Ignore-AOSP-First: Depends on internal changes that can't go into AOSP yet.
Bug: 290312729
Test: atest com.android.server.locksettings
Change-Id: If15ab387e86b5cdcf271085d414ce39ff3a04a83
parent cee75dc2
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -3666,6 +3666,7 @@ package android.content {
    field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
    field public static final String ACTION_INSTANT_APP_RESOLVER_SETTINGS = "android.intent.action.INSTANT_APP_RESOLVER_SETTINGS";
    field @Deprecated public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
    field @Deprecated public static final String ACTION_INTENT_FILTER_NEEDS_VERIFICATION = "android.intent.action.INTENT_FILTER_NEEDS_VERIFICATION";
    field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
    field public static final String ACTION_LOAD_DATA = "android.intent.action.LOAD_DATA";
    field @FlaggedApi("android.security.frp_enforcement") public static final String ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED = "android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED";
    field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
    field @RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) public static final String ACTION_MANAGE_APP_PERMISSION = "android.intent.action.MANAGE_APP_PERMISSION";
    field @Deprecated public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
    field @Deprecated public static final String ACTION_MANAGE_APP_PERMISSIONS = "android.intent.action.MANAGE_APP_PERMISSIONS";
    field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
    field @RequiresPermission(android.Manifest.permission.MANAGE_ROLE_HOLDERS) public static final String ACTION_MANAGE_DEFAULT_APP = "android.intent.action.MANAGE_DEFAULT_APP";
+21 −0
Original line number Original line Diff line number Diff line
@@ -19,6 +19,7 @@ package android.content;
import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.app.sdksandbox.SdkSandboxManager.ACTION_START_SANDBOXED_ACTIVITY;
import static android.content.ContentProvider.maybeAddUserId;
import static android.content.ContentProvider.maybeAddUserId;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.os.Flags.FLAG_ALLOW_PRIVATE_PROFILE;
import static android.security.Flags.FLAG_FRP_ENFORCEMENT;
import static android.service.chooser.Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA;
import static android.service.chooser.Flags.FLAG_ENABLE_SHARESHEET_METADATA_EXTRA;
import android.Manifest;
import android.Manifest;
@@ -3901,6 +3902,26 @@ public class Intent implements Parcelable, Cloneable {
    public static final String ACTION_IDLE_MAINTENANCE_END =
    public static final String ACTION_IDLE_MAINTENANCE_END =
            "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
            "android.intent.action.ACTION_IDLE_MAINTENANCE_END";
    /**
     * Broadcast Action: A broadcast sent to the main user when the main user changes their
     * Lock Screen Knowledge Factor, either because they changed the current value, or because
     * they added or removed it.
     *
     * <p class="note">At present, this intent is only broadcast to listeners with the
     * CONFIGURE_FACTORY_RESET_PROTECTION signature|privileged permiession.</p>
     *
     * <p class="note">This is a protected intent that can only be sent by the system.</p>
     *
     * @hide
     */
    @FlaggedApi(FLAG_FRP_ENFORCEMENT)
    @SystemApi
    @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
    @BroadcastBehavior(protectedBroadcast = true)
    public static final String
            ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED =
            "android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED";
    /**
    /**
     * Broadcast Action: a remote intent is to be broadcasted.
     * Broadcast Action: a remote intent is to be broadcasted.
     *
     *
+1 −0
Original line number Original line Diff line number Diff line
@@ -836,6 +836,7 @@
    <protected-broadcast android:name="android.intent.action.PROFILE_AVAILABLE" />
    <protected-broadcast android:name="android.intent.action.PROFILE_AVAILABLE" />
    <protected-broadcast android:name="android.intent.action.PROFILE_UNAVAILABLE" />
    <protected-broadcast android:name="android.intent.action.PROFILE_UNAVAILABLE" />
    <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />
    <protected-broadcast android:name="android.app.action.CONSOLIDATED_NOTIFICATION_POLICY_CHANGED" />
    <protected-broadcast android:name="android.intent.action.MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED" />


    <!-- ====================================================================== -->
    <!-- ====================================================================== -->
    <!--                          RUNTIME PERMISSIONS                           -->
    <!--                          RUNTIME PERMISSIONS                           -->
+30 −3
Original line number Original line Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.locksettings;


import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.security.Flags.reportPrimaryAuthAttempts;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE;
import static android.Manifest.permission.CONFIGURE_FACTORY_RESET_PROTECTION;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.MANAGE_BIOMETRIC;
import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
import static android.Manifest.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS;
import static android.Manifest.permission.SET_INITIAL_LOCK;
import static android.Manifest.permission.SET_INITIAL_LOCK;
@@ -27,6 +28,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRY
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_MESSAGE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
import static android.app.admin.DevicePolicyResources.Strings.Core.PROFILE_ENCRYPTED_TITLE;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Context.KEYGUARD_SERVICE;
import static android.content.Intent.ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static android.os.UserHandle.USER_SYSTEM;
@@ -1201,8 +1203,9 @@ public class LockSettingsService extends ILockSettings.Stub {


        final boolean inSetupWizard = Settings.Secure.getIntForUser(cr,
        final boolean inSetupWizard = Settings.Secure.getIntForUser(cr,
                Settings.Secure.USER_SETUP_COMPLETE, 0, mainUserId) == 0;
                Settings.Secure.USER_SETUP_COMPLETE, 0, mainUserId) == 0;
        final boolean secureFrp = Settings.Global.getInt(cr,
        final boolean secureFrp = android.security.Flags.frpEnforcement()
                Settings.Global.SECURE_FRP_MODE, 0) == 1;
                ? mStorage.isFactoryResetProtectionActive()
                : (Settings.Global.getInt(cr, Settings.Global.SECURE_FRP_MODE, 0) == 1);


        if (inSetupWizard && secureFrp) {
        if (inSetupWizard && secureFrp) {
            throw new SecurityException("Cannot change credential in SUW while factory reset"
            throw new SecurityException("Cannot change credential in SUW while factory reset"
@@ -2332,8 +2335,13 @@ public class LockSettingsService extends ILockSettings.Stub {


        synchronized (mSpManager) {
        synchronized (mSpManager) {
            if (isSpecialUserId(userId)) {
            if (isSpecialUserId(userId)) {
                return mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
                response = mSpManager.verifySpecialUserCredential(userId, getGateKeeperService(),
                        credential, progressCallback);
                        credential, progressCallback);
                if (android.security.Flags.frpEnforcement() && response.isMatched()
                        && userId == USER_FRP) {
                    mStorage.deactivateFactoryResetProtectionWithoutSecret();
                }
                return response;
            }
            }


            long protectorId = getCurrentLskfBasedProtectorId(userId);
            long protectorId = getCurrentLskfBasedProtectorId(userId);
@@ -3054,6 +3062,7 @@ public class LockSettingsService extends ILockSettings.Stub {
        setCurrentLskfBasedProtectorId(newProtectorId, userId);
        setCurrentLskfBasedProtectorId(newProtectorId, userId);
        LockPatternUtils.invalidateCredentialTypeCache();
        LockPatternUtils.invalidateCredentialTypeCache();
        synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
        synchronizeUnifiedChallengeForProfiles(userId, profilePasswords);
        sendMainUserCredentialChangedNotificationIfNeeded(userId);


        setUserPasswordMetrics(credential, userId);
        setUserPasswordMetrics(credential, userId);
        mUnifiedProfilePasswordCache.removePassword(userId);
        mUnifiedProfilePasswordCache.removePassword(userId);
@@ -3071,6 +3080,24 @@ public class LockSettingsService extends ILockSettings.Stub {
        return newProtectorId;
        return newProtectorId;
    }
    }


    private void sendMainUserCredentialChangedNotificationIfNeeded(int userId) {
        if (!android.security.Flags.frpEnforcement()) {
            return;
        }

        if (userId != mInjector.getUserManagerInternal().getMainUserId()) {
            return;
        }

        sendBroadcast(new Intent(ACTION_MAIN_USER_LOCKSCREEN_KNOWLEDGE_FACTOR_CHANGED),
                UserHandle.of(userId), CONFIGURE_FACTORY_RESET_PROTECTION);
    }

    @VisibleForTesting
    void sendBroadcast(Intent intent, UserHandle userHandle, String permission) {
        mContext.sendBroadcastAsUser(intent, userHandle, permission, /* options */ null);
    }

    private void removeBiometricsForUser(int userId) {
    private void removeBiometricsForUser(int userId) {
        removeAllFingerprintForUser(userId);
        removeAllFingerprintForUser(userId);
        removeAllFaceForUser(userId);
        removeAllFaceForUser(userId);
+30 −0
Original line number Original line Diff line number Diff line
@@ -34,6 +34,7 @@ import android.os.Environment;
import android.os.UserHandle;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.UserManager;
import android.provider.Settings;
import android.provider.Settings;
import android.service.persistentdata.PersistentDataBlockManager;
import android.text.TextUtils;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArrayMap;
import android.util.AtomicFile;
import android.util.AtomicFile;
@@ -587,6 +588,10 @@ class LockSettingsStorage {
        return mPersistentDataBlockManagerInternal;
        return mPersistentDataBlockManagerInternal;
    }
    }


    /**
     * Writes main user credential handle to the persistent data block, to enable factory reset
     * protection to be deactivated with the credential.
     */
    public void writePersistentDataBlock(int persistentType, int userId, int qualityForUi,
    public void writePersistentDataBlock(int persistentType, int userId, int qualityForUi,
            byte[] payload) {
            byte[] payload) {
        PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager();
        PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager();
@@ -610,6 +615,31 @@ class LockSettingsStorage {
        }
        }
    }
    }


    public void deactivateFactoryResetProtectionWithoutSecret() {
        PersistentDataBlockManagerInternal persistentDataBlock = getPersistentDataBlockManager();
        if (persistentDataBlock != null) {
            persistentDataBlock.deactivateFactoryResetProtectionWithoutSecret();
        } else {
            Slog.wtf(TAG, "Failed to get PersistentDataBlockManagerInternal");
        }
    }

    public boolean isFactoryResetProtectionActive() {
        PersistentDataBlockManager persistentDataBlockManager =
                mContext.getSystemService(PersistentDataBlockManager.class);
        if (persistentDataBlockManager != null) {
            return persistentDataBlockManager.isFactoryResetProtectionActive();
        } else {
            Slog.wtf(TAG, "Failed to get PersistentDataBlockManager");
            // This should never happen, but in the event it does, let's not block the user.  This
            // may be the wrong call, since if an attacker can find a way to prevent us from
            // getting the PersistentDataBlockManager they can defeat FRP, but if they can block
            // access to PersistentDataBlockManager they must have compromised the system and we've
            // probably already lost this battle.
            return false;
        }
    }

    /**
    /**
     * Provides a concrete data structure to represent the minimal information from
     * Provides a concrete data structure to represent the minimal information from
     * a user's LSKF-based SP protector that is needed to verify the user's LSKF,
     * a user's LSKF-based SP protector that is needed to verify the user's LSKF,
Loading