diff options
| author | 2022-01-18 17:44:20 -0800 | |
|---|---|---|
| committer | 2022-01-21 01:36:43 +0000 | |
| commit | 14ce8bc12010a9ef3a96001dad57a88d951abf7e (patch) | |
| tree | a1d1bb2a78d442ad310ad7f893a237c1010e832e | |
| parent | 1acb7b93ddbfbca909a3f7ec7aee72df10a94b51 (diff) | |
Framework support for AndroidKeyStore migration
- Add a new boolean attribute `inheritKeyStoreKeys` to allow apps to
indicate whether they want keys to be transferred to the updated app
- Call the appropriate KeyStore method to migrate keys from the old
namespace to the new one
- Clear keys owned by the previous app ID if it is removed
Test: atest SharedUserMigrationTest#testKeyMigration
Test: atest AndroidPackageTest
Bug: 179284822
Change-Id: I321b85b88c150f17709a2270c0cbaf368ca035cc
14 files changed, 97 insertions, 48 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 735de5421f16..32a5e62c1c16 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -824,6 +824,7 @@ package android { field public static final int indicatorRight = 16843022; // 0x101010e field public static final int indicatorStart = 16843729; // 0x10103d1 field public static final int inflatedId = 16842995; // 0x10100f3 + field public static final int inheritKeyStoreKeys; field public static final int inheritShowWhenLocked = 16844188; // 0x101059c field public static final int initOrder = 16842778; // 0x101001a field public static final int initialKeyguardLayout = 16843714; // 0x10103c2 diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index a5954338910d..3a2fb6e70ba8 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -401,6 +401,15 @@ and before. --> <attr name="sharedUserMaxSdkVersion" format="integer" /> + <!-- Whether the application should inherit all AndroidKeyStore keys of its shared user + group in the case of leaving its shared user ID in an upgrade. If set to false, all + AndroidKeyStore keys will remain in the shared user group, and the application will no + longer have access to those keys after the upgrade. If set to true, all AndroidKeyStore + keys owned by the shared user group will be transferred to the upgraded application; + other applications in the shared user group will no longer have access to those keys + after the migration. The default value is false if not explicitly set. --> + <attr name="inheritKeyStoreKeys" format="boolean" /> + <!-- Internal version code. This is the number used to determine whether one version is more recent than another: it has no other meaning than that higher numbers are more recent. You could use this number to @@ -1677,6 +1686,7 @@ <attr name="sharedUserId" /> <attr name="sharedUserLabel" /> <attr name="sharedUserMaxSdkVersion" /> + <attr name="inheritKeyStoreKeys" /> <attr name="installLocation" /> <attr name="isolatedSplits" /> <attr name="isFeatureSplit" /> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index e83f4a66883c..19547b85505c 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -3256,6 +3256,7 @@ <public name="gameSessionService" /> <public name="localeConfig" /> <public name="showBackground" /> + <public name="inheritKeyStoreKeys" /> </staging-public-group> <staging-public-group type="id" first-id="0x01de0000"> diff --git a/keystore/java/android/security/KeyStore.java b/keystore/java/android/security/KeyStore.java index a9543443d3f4..8811a7fec932 100644 --- a/keystore/java/android/security/KeyStore.java +++ b/keystore/java/android/security/KeyStore.java @@ -20,7 +20,6 @@ import android.compat.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.UserHandle; import android.security.maintenance.UserState; -import android.system.keystore2.Domain; /** * @hide This should not be made public in its present form because it @@ -120,15 +119,6 @@ public class KeyStore { } /** - * Forwards the request to clear a UID to Keystore 2.0. - * @hide - */ - public boolean clearUid(int uid) { - return AndroidKeyStoreMaintenance.clearNamespace(Domain.APP, uid) == 0; - } - - - /** * Add an authentication record to the keystore authorization table. * * @param authToken The packed bytes of a hw_auth_token_t to be provided to keymaster. diff --git a/services/core/java/com/android/server/pm/AppDataHelper.java b/services/core/java/com/android/server/pm/AppDataHelper.java index a66af3cf7603..f7d0950892d3 100644 --- a/services/core/java/com/android/server/pm/AppDataHelper.java +++ b/services/core/java/com/android/server/pm/AppDataHelper.java @@ -24,7 +24,6 @@ import static com.android.server.pm.PackageManagerServiceUtils.logCriticalInfo; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.pm.PackageManager; -import com.android.server.pm.pkg.SELinuxUtil; import android.content.pm.UserInfo; import android.os.CreateAppDataArgs; import android.os.Environment; @@ -35,6 +34,9 @@ import android.os.UserHandle; import android.os.storage.StorageManager; import android.os.storage.StorageManagerInternal; import android.os.storage.VolumeInfo; +import android.security.AndroidKeyStoreMaintenance; +import android.system.keystore2.Domain; +import android.system.keystore2.KeyDescriptor; import android.text.TextUtils; import android.util.Log; import android.util.Slog; @@ -46,6 +48,7 @@ import com.android.server.SystemServerInitThreadPool; import com.android.server.pm.dex.ArtManagerService; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; +import com.android.server.pm.pkg.SELinuxUtil; import dalvik.system.VMRuntime; @@ -156,8 +159,7 @@ final class AppDataHelper { * <ul> * <li>If previousAppId < 0, app data will be migrated to the new app ID * <li>If previousAppId == 0, no migration will happen and data will be wiped and recreated - * <li>If previousAppId > 0, it will migrate all data owned by previousAppId - * to the new app ID + * <li>If previousAppId > 0, app data owned by previousAppId will be migrated to the new app ID * </ul> */ private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch, @@ -549,6 +551,22 @@ final class AppDataHelper { return prepareAppDataFuture; } + public void migrateKeyStoreData(int previousAppId, int appId) { + for (int userId : mPm.resolveUserIds(UserHandle.USER_ALL)) { + int srcUid = UserHandle.getUid(userId, previousAppId); + int destUid = UserHandle.getUid(userId, appId); + final KeyDescriptor[] keys = AndroidKeyStoreMaintenance.listEntries(Domain.APP, srcUid); + if (keys == null) continue; + for (final KeyDescriptor key : keys) { + KeyDescriptor dest = new KeyDescriptor(); + dest.domain = Domain.APP; + dest.nspace = destUid; + dest.alias = key.alias; + AndroidKeyStoreMaintenance.migrateKeyNamespace(key, dest); + } + } + } + void clearAppDataLIF(AndroidPackage pkg, int userId, int flags) { if (pkg == null) { return; @@ -633,4 +651,18 @@ final class AppDataHelper { pkg.getProperties().get(PackageManager.PROPERTY_NO_APP_DATA_STORAGE); return noAppDataProp == null || !noAppDataProp.getBoolean(); } + + /** + * Remove entries from the keystore daemon. Will only remove if the {@code appId} is valid. + */ + public void clearKeystoreData(int userId, int appId) { + if (appId < 0) { + return; + } + + for (int realUserId : mPm.resolveUserIds(userId)) { + AndroidKeyStoreMaintenance.clearNamespace( + Domain.APP, UserHandle.getUid(realUserId, appId)); + } + } } diff --git a/services/core/java/com/android/server/pm/DeletePackageHelper.java b/services/core/java/com/android/server/pm/DeletePackageHelper.java index 9a80a4e558c4..48689a8da335 100644 --- a/services/core/java/com/android/server/pm/DeletePackageHelper.java +++ b/services/core/java/com/android/server/pm/DeletePackageHelper.java @@ -485,8 +485,7 @@ final class DeletePackageHelper { mAppDataHelper.destroyAppDataLIF(pkg, nextUserId, FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); } - PackageManagerService.removeKeystoreDataIfNeeded(mUserManagerInternal, nextUserId, - ps.getAppId()); + mAppDataHelper.clearKeystoreData(nextUserId, ps.getAppId()); preferredActivityHelper.clearPackagePreferredActivities(ps.getPackageName(), nextUserId); mPm.mDomainVerificationManager.clearPackageForUser(ps.getPackageName(), nextUserId); diff --git a/services/core/java/com/android/server/pm/InstallPackageHelper.java b/services/core/java/com/android/server/pm/InstallPackageHelper.java index c9478dd30f40..4e17a98af384 100644 --- a/services/core/java/com/android/server/pm/InstallPackageHelper.java +++ b/services/core/java/com/android/server/pm/InstallPackageHelper.java @@ -2246,6 +2246,17 @@ final class InstallPackageHelper { if (reconciledPkg.mScanResult.needsNewAppId()) { // Only set previousAppId if the app is migrating out of shared UID previousAppId = reconciledPkg.mScanResult.mPreviousAppId; + + if (pkg.shouldInheritKeyStoreKeys()) { + // Migrate keystore data + mAppDataHelper.migrateKeyStoreData( + previousAppId, reconciledPkg.mPkgSetting.getAppId()); + } + + if (reconciledPkg.mInstallResult.mRemovedInfo.mRemovedAppId == previousAppId) { + // If the previous app ID is removed, clear the keys + mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, previousAppId); + } } mAppDataHelper.prepareAppDataPostCommitLIF(pkg, previousAppId); if (reconciledPkg.mPrepareResult.mClearCodeCache) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 13f91e0dca62..88b707d30940 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -167,7 +167,6 @@ import android.permission.PermissionManager; import android.provider.DeviceConfig; import android.provider.Settings.Global; import android.provider.Settings.Secure; -import android.security.KeyStore; import android.text.TextUtils; import android.text.format.DateUtils; import android.util.ArrayMap; @@ -5445,7 +5444,7 @@ public class PackageManagerService extends IPackageManager.Stub FLAG_STORAGE_DE | FLAG_STORAGE_CE | FLAG_STORAGE_EXTERNAL); final int appId = UserHandle.getAppId(pkg.getUid()); - removeKeystoreDataIfNeeded(mInjector.getUserManagerInternal(), userId, appId); + mAppDataHelper.clearKeystoreData(userId, appId); UserManagerInternal umInternal = mInjector.getUserManagerInternal(); StorageManagerInternal smInternal = mInjector.getLocalService(StorageManagerInternal.class); @@ -5518,30 +5517,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - /** - * Remove entries from the keystore daemon. Will only remove it if the - * {@code appId} is valid. - */ - static void removeKeystoreDataIfNeeded(UserManagerInternal um, @UserIdInt int userId, - @AppIdInt int appId) { - if (appId < 0) { - return; - } - - final KeyStore keyStore = KeyStore.getInstance(); - if (keyStore != null) { - if (userId == UserHandle.USER_ALL) { - for (final int individual : um.getUserIds()) { - keyStore.clearUid(UserHandle.getUid(individual, appId)); - } - } else { - keyStore.clearUid(UserHandle.getUid(userId, appId)); - } - } else { - Slog.w(TAG, "Could not contact keystore to clear entries for app id " + appId); - } - } - @Override public void deleteApplicationCacheFiles(final String packageName, final IPackageDataObserver observer) { diff --git a/services/core/java/com/android/server/pm/RemovePackageHelper.java b/services/core/java/com/android/server/pm/RemovePackageHelper.java index b0e03403b653..7e898cbe86b0 100644 --- a/services/core/java/com/android/server/pm/RemovePackageHelper.java +++ b/services/core/java/com/android/server/pm/RemovePackageHelper.java @@ -29,7 +29,6 @@ import static com.android.server.pm.PackageManagerService.TAG; import android.annotation.NonNull; import android.content.pm.PackageManager; -import com.android.server.pm.pkg.component.ParsedInstrumentation; import android.os.UserHandle; import android.os.incremental.IncrementalManager; import android.util.Log; @@ -43,6 +42,7 @@ import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.PackageImpl; import com.android.server.pm.permission.PermissionManagerServiceInternal; import com.android.server.pm.pkg.PackageStateInternal; +import com.android.server.pm.pkg.component.ParsedInstrumentation; import java.io.File; import java.util.Collections; @@ -330,8 +330,7 @@ final class RemovePackageHelper { if (removedAppId != -1) { // A user ID was deleted here. Go through all users and remove it // from KeyStore. - mPm.removeKeystoreDataIfNeeded( - mUserManagerInternal, UserHandle.USER_ALL, removedAppId); + mAppDataHelper.clearKeystoreData(UserHandle.USER_ALL, removedAppId); } } } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java index 18a6435d17c8..52d9b7a3abc1 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackage.java @@ -26,6 +26,10 @@ import android.content.pm.FeatureGroupInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager.Property; import android.content.pm.SigningDetails; +import android.os.Bundle; +import android.util.SparseArray; +import android.util.SparseIntArray; + import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedApexSystemService; import com.android.server.pm.pkg.component.ParsedAttribution; @@ -37,9 +41,6 @@ import com.android.server.pm.pkg.component.ParsedProcess; import com.android.server.pm.pkg.component.ParsedProvider; import com.android.server.pm.pkg.component.ParsedService; import com.android.server.pm.pkg.component.ParsedUsesPermission; -import android.os.Bundle; -import android.util.SparseArray; -import android.util.SparseIntArray; import java.security.PublicKey; import java.util.Map; @@ -286,6 +287,9 @@ public interface ParsingPackage extends ParsingPackageRead { ParsingPackage setInstallLocation(int installLocation); + /** @see R#styleable.AndroidManifest_inheritKeyStoreKeys */ + ParsingPackage setInheritKeyStoreKeys(boolean inheritKeyStoreKeys); + ParsingPackage setLabelRes(int labelRes); ParsingPackage setLargestWidthLimitDp(int largestWidthLimitDp); diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java index c4de862bccd9..1f21938fc706 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageImpl.java @@ -492,6 +492,10 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, ENABLED, DISALLOW_PROFILING, REQUEST_FOREGROUND_SERVICE_EXEMPTION, + ATTRIBUTIONS_ARE_USER_VISIBLE, + RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED, + SDK_LIBRARY, + INHERIT_KEYSTORE_KEYS, }) public @interface Values {} private static final long EXTERNAL_STORAGE = 1L; @@ -544,6 +548,7 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, private static final long ATTRIBUTIONS_ARE_USER_VISIBLE = 1L << 47; private static final long RESET_ENABLED_SETTINGS_ON_APP_DATA_CLEARED = 1L << 48; private static final long SDK_LIBRARY = 1L << 49; + private static final long INHERIT_KEYSTORE_KEYS = 1L << 50; } private ParsingPackageImpl setBoolean(@Booleans.Values long flag, boolean value) { @@ -2371,6 +2376,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, } @Override + public boolean shouldInheritKeyStoreKeys() { + return getBoolean(Booleans.INHERIT_KEYSTORE_KEYS); + } + + @Override public ParsingPackageImpl setBaseRevisionCode(int value) { baseRevisionCode = value; return this; @@ -2514,6 +2524,11 @@ public class ParsingPackageImpl implements ParsingPackage, ParsingPackageHidden, } @Override + public ParsingPackageImpl setInheritKeyStoreKeys(boolean value) { + return setBoolean(Booleans.INHERIT_KEYSTORE_KEYS, value); + } + + @Override public ParsingPackageImpl setLabelRes(int value) { labelRes = value; return this; diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java index 149711287f32..4b659a14418f 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageRead.java @@ -350,4 +350,9 @@ public interface ParsingPackageRead extends PkgWithoutStateAppInfo, PkgWithoutSt * @see R.styleable#AndroidManifestApplication_localeConfig */ int getLocaleConfigRes(); + + /** + * @see R.styleable#AndroidManifest_inheritKeyStoreKeys + */ + boolean shouldInheritKeyStoreKeys(); } diff --git a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java index 1ce01f633791..bf7c55f0f59e 100644 --- a/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java +++ b/services/core/java/com/android/server/pm/pkg/parsing/ParsingPackageUtils.java @@ -868,7 +868,9 @@ public class ParsingPackageUtils { .setTargetSandboxVersion(anInteger(PARSE_DEFAULT_TARGET_SANDBOX, R.styleable.AndroidManifest_targetSandboxVersion, sa)) /* Set the global "on SD card" flag */ - .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0); + .setExternalStorage((flags & PARSE_EXTERNAL_STORAGE) != 0) + .setInheritKeyStoreKeys(bool(false, + R.styleable.AndroidManifest_inheritKeyStoreKeys, sa)); boolean foundApp = false; final int depth = parser.getDepth(); diff --git a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt index a0f3bbf928ab..cc663d955612 100644 --- a/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt +++ b/services/tests/PackageManagerServiceTests/unit/src/com/android/server/pm/test/parsing/parcelling/AndroidPackageTest.kt @@ -503,6 +503,11 @@ class AndroidPackageTest : ParcelableComponentTest(AndroidPackage::class, Packag PackageManager.Property::getString ) } + ), + getSetByValue( + AndroidPackage::shouldInheritKeyStoreKeys, + ParsingPackage::setInheritKeyStoreKeys, + true ) ) |