diff options
author | dcashman <dcashman@google.com> | 2014-06-09 09:27:54 -0700 |
---|---|---|
committer | dcashman <dcashman@google.com> | 2014-07-12 12:09:40 -0700 |
commit | 405912bce074e9e59a246e2357a108e50dffabf8 (patch) | |
tree | 23e28254e4dd33b2cf6181e8136b3644f6ba3179 | |
parent | 325503caef80ebce0da3e4d5fa2ac3f917dd1b1d (diff) | |
download | frameworks_base-405912bce074e9e59a246e2357a108e50dffabf8.zip frameworks_base-405912bce074e9e59a246e2357a108e50dffabf8.tar.gz frameworks_base-405912bce074e9e59a246e2357a108e50dffabf8.tar.bz2 |
Initial KeySet API.
Bug: 6967056
Change-Id: I47a01bd5dc25591cc70f58f38920ad0a021094ae
-rw-r--r-- | api/current.txt | 7 | ||||
-rw-r--r-- | core/java/android/app/ApplicationPackageManager.java | 59 | ||||
-rw-r--r-- | core/java/android/content/pm/IPackageManager.aidl | 5 | ||||
-rw-r--r-- | core/java/android/content/pm/KeySet.java | 27 | ||||
-rw-r--r-- | core/java/android/content/pm/PackageManager.java | 27 | ||||
-rw-r--r-- | core/tests/coretests/AndroidManifest.xml | 7 | ||||
-rw-r--r-- | core/tests/coretests/apks/keyset/Android.mk | 17 | ||||
-rw-r--r-- | core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml | 20 | ||||
-rw-r--r-- | core/tests/coretests/src/android/content/pm/PackageManagerTests.java | 170 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/KeySetHandle.java | 22 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/KeySetManagerService.java | 111 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 79 | ||||
-rw-r--r-- | test-runner/src/android/test/mock/MockPackageManager.java | 21 |
13 files changed, 521 insertions, 51 deletions
diff --git a/api/current.txt b/api/current.txt index f588524..0b658af 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8344,6 +8344,9 @@ package android.content.pm { field public java.lang.String targetPackage; } + public class KeySet { + } + public class LabeledIntent extends android.content.Intent { ctor public LabeledIntent(android.content.Intent, java.lang.String, int, int); ctor public LabeledIntent(android.content.Intent, java.lang.String, java.lang.CharSequence, int); @@ -8481,6 +8484,7 @@ package android.content.pm { method public abstract java.util.List<android.content.pm.PackageInfo> getInstalledPackages(int); method public abstract java.lang.String getInstallerPackageName(java.lang.String); method public abstract android.content.pm.InstrumentationInfo getInstrumentationInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.KeySet getKeySetByAlias(java.lang.String, java.lang.String); method public abstract android.content.Intent getLaunchIntentForPackage(java.lang.String); method public abstract android.content.Intent getLeanbackLaunchIntentForPackage(java.lang.String); method public abstract java.lang.String getNameForUid(int); @@ -8499,12 +8503,15 @@ package android.content.pm { method public abstract android.content.res.Resources getResourcesForApplication(android.content.pm.ApplicationInfo) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.res.Resources getResourcesForApplication(java.lang.String) throws android.content.pm.PackageManager.NameNotFoundException; method public abstract android.content.pm.ServiceInfo getServiceInfo(android.content.ComponentName, int) throws android.content.pm.PackageManager.NameNotFoundException; + method public abstract android.content.pm.KeySet getSigningKeySet(java.lang.String); method public abstract android.content.pm.FeatureInfo[] getSystemAvailableFeatures(); method public abstract java.lang.String[] getSystemSharedLibraryNames(); method public abstract java.lang.CharSequence getText(java.lang.String, int, android.content.pm.ApplicationInfo); method public abstract android.content.res.XmlResourceParser getXml(java.lang.String, int, android.content.pm.ApplicationInfo); method public abstract boolean hasSystemFeature(java.lang.String); method public abstract boolean isSafeMode(); + method public abstract boolean isSignedBy(java.lang.String, android.content.pm.KeySet); + method public abstract boolean isSignedByExactly(java.lang.String, android.content.pm.KeySet); method public abstract java.util.List<android.content.pm.ResolveInfo> queryBroadcastReceivers(android.content.Intent, int); method public abstract java.util.List<android.content.pm.ProviderInfo> queryContentProviders(java.lang.String, int, int); method public abstract java.util.List<android.content.pm.InstrumentationInfo> queryInstrumentation(java.lang.String, int); diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java index 2935b8e..4730559 100644 --- a/core/java/android/app/ApplicationPackageManager.java +++ b/core/java/android/app/ApplicationPackageManager.java @@ -33,6 +33,7 @@ import android.content.pm.IPackageManager; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; @@ -52,6 +53,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; +import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; @@ -59,6 +61,7 @@ import android.os.UserManager; import android.util.ArrayMap; import android.util.Log; import android.view.Display; +import com.android.internal.util.Preconditions; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -1447,6 +1450,62 @@ final class ApplicationPackageManager extends PackageManager { return false; } + @Override + public KeySet getKeySetByAlias(String packageName, String alias) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(alias); + IBinder keySetToken; + try { + keySetToken = mPM.getKeySetByAlias(packageName, alias); + } catch (RemoteException e) { + return null; + } + if (keySetToken == null) { + return null; + } + return new KeySet(keySetToken); + } + + @Override + public KeySet getSigningKeySet(String packageName) { + Preconditions.checkNotNull(packageName); + IBinder keySetToken; + try { + keySetToken = mPM.getSigningKeySet(packageName); + } catch (RemoteException e) { + return null; + } + if (keySetToken == null) { + return null; + } + return new KeySet(keySetToken); + } + + + @Override + public boolean isSignedBy(String packageName, KeySet ks) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(ks); + IBinder keySetToken = ks.getToken(); + try { + return mPM.isPackageSignedByKeySet(packageName, keySetToken); + } catch (RemoteException e) { + return false; + } + } + + @Override + public boolean isSignedByExactly(String packageName, KeySet ks) { + Preconditions.checkNotNull(packageName); + Preconditions.checkNotNull(ks); + IBinder keySetToken = ks.getToken(); + try { + return mPM.isPackageSignedByKeySetExactly(packageName, keySetToken); + } catch (RemoteException e) { + return false; + } + } + /** * @hide */ diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 58d3526..3a98f5d 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -429,4 +429,9 @@ interface IPackageManager { boolean setBlockUninstallForUser(String packageName, boolean blockUninstall, int userId); boolean getBlockUninstallForUser(String packageName, int userId); + + IBinder getKeySetByAlias(String packageName, String alias); + IBinder getSigningKeySet(String packageName); + boolean isPackageSignedByKeySet(String packageName, IBinder ks); + boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks); } diff --git a/core/java/android/content/pm/KeySet.java b/core/java/android/content/pm/KeySet.java index 0ef09a4..fcdaa18 100644 --- a/core/java/android/content/pm/KeySet.java +++ b/core/java/android/content/pm/KeySet.java @@ -16,19 +16,36 @@ package android.content.pm; -import android.os.Binder; +import android.os.IBinder; -/** @hide */ +/** + * Represents a {@code KeySet} that has been declared in the AndroidManifest.xml + * file for the application. A {@code KeySet} can be used explicitly to + * represent a trust relationship with other applications on the device. + */ public class KeySet { - private Binder token; + private IBinder token; /** @hide */ - public KeySet(Binder token) { + public KeySet(IBinder token) { + if (token == null) { + throw new NullPointerException("null value for KeySet IBinder token"); + } this.token = token; } - Binder getToken() { + /** @hide */ + public IBinder getToken() { return token; } + + @Override + public boolean equals(Object o) { + if (o instanceof KeySet) { + KeySet ks = (KeySet) o; + return token == ks.token; + } + return false; + } }
\ No newline at end of file diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 7e783eb..ec701b6 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -3594,6 +3594,33 @@ public abstract class PackageManager { public abstract boolean isSafeMode(); /** + * Return the {@link KeySet} associated with the String alias for this + * application. + * + * @param Alias The alias for a given {@link KeySet} as defined in the + * application's AndroidManifest.xml. + */ + public abstract KeySet getKeySetByAlias(String packageName, String alias); + + /** Return the signing {@link KeySet} for this application. */ + public abstract KeySet getSigningKeySet(String packageName); + + /** + * Return whether the package denoted by packageName has been signed by all + * of the keys specified by the {@link KeySet} ks. This will return true if + * the package has been signed by additional keys (a superset) as well. + * Compare to {@link #isSignedByExactly(String packageName, KeySet ks)}. + */ + public abstract boolean isSignedBy(String packageName, KeySet ks); + + /** + * Return whether the package denoted by packageName has been signed by all + * of, and only, the keys specified by the {@link KeySet} ks. Compare to + * {@link #isSignedBy(String packageName, KeySet ks)}. + */ + public abstract boolean isSignedByExactly(String packageName, KeySet ks); + + /** * Attempts to move package resources from internal to external media or vice versa. * Since this may take a little while, the result will * be posted back to the given observer. This call may fail if the calling context diff --git a/core/tests/coretests/AndroidManifest.xml b/core/tests/coretests/AndroidManifest.xml index a2cc40c..b524177 100644 --- a/core/tests/coretests/AndroidManifest.xml +++ b/core/tests/coretests/AndroidManifest.xml @@ -1257,4 +1257,11 @@ <instrumentation android:name="android.test.InstrumentationTestRunner" android:targetPackage="com.android.frameworks.coretests" android:label="Frameworks Core Tests" /> + <key-sets> + <key-set android:name="A" > + <public-key android:name="keyA" + android:value="MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAJoN1Nsgqf0V4C/bbN8wo8O2X/S5D76+5Mb9mlIsHkUTUTbHCNk+LxHIUYLm89YbP9zImrV0bUHLUAZUyoMUCiMCAwEAAQ=="/> + </key-set> + <upgrade-key-set android:name="A"/> + </key-sets> </manifest> diff --git a/core/tests/coretests/apks/keyset/Android.mk b/core/tests/coretests/apks/keyset/Android.mk index e44ac6c..306dc90 100644 --- a/core/tests/coretests/apks/keyset/Android.mk +++ b/core/tests/coretests/apks/keyset/Android.mk @@ -88,4 +88,21 @@ LOCAL_PACKAGE_NAME := keyset_sau_ub LOCAL_CERTIFICATE := $(LOCAL_PATH)/../../certs/keyset_A LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_B LOCAL_MANIFEST_FILE := uB/AndroidManifest.xml +include $(FrameworkCoreTests_BUILD_PACKAGE) + +#apks signed by platform only +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_PACKAGE_NAME := keyset_splat_api +LOCAL_CERTIFICATE := platform +LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml +include $(FrameworkCoreTests_BUILD_PACKAGE) + +#apks signed by platform and keyset_A +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-subdir-java-files) +LOCAL_PACKAGE_NAME := keyset_splata_api +LOCAL_CERTIFICATE := platform +LOCAL_ADDITIONAL_CERTIFICATES := $(LOCAL_PATH)/../../certs/keyset_A +LOCAL_MANIFEST_FILE := api_test/AndroidManifest.xml include $(FrameworkCoreTests_BUILD_PACKAGE)
\ No newline at end of file diff --git a/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml new file mode 100644 index 0000000..4c7e968 --- /dev/null +++ b/core/tests/coretests/apks/keyset/api_test/AndroidManifest.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2010 The Android Open Source Project + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.android.frameworks.coretests.keysets_api"> + <application android:hasCode="false"> + </application> +</manifest> diff --git a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java index 0244425..3a80309 100644 --- a/core/tests/coretests/src/android/content/pm/PackageManagerTests.java +++ b/core/tests/coretests/src/android/content/pm/PackageManagerTests.java @@ -26,6 +26,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.KeySet; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; @@ -33,6 +34,7 @@ import android.content.pm.PackageParser.PackageParserException; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.net.Uri; +import android.os.Binder; import android.os.Bundle; import android.os.Environment; import android.os.FileUtils; @@ -3328,6 +3330,174 @@ public class PackageManagerTests extends AndroidTestCase { } /** + * The following tests are related to testing KeySets-based API + */ + + /* + * testGetSigningKeySetNull - ensure getSigningKeySet() returns null on null + * input and when calling a package other than that which made the call. + */ + public void testGetSigningKeySet() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet ks; + try { + ks = pm.getSigningKeySet(null); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getSigningKeySet("keysets.test.bogus.package"); + assertTrue(false); // should have thrown + } catch (IllegalArgumentException e) { + } + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + try { + ks = pm.getSigningKeySet(otherPkgName); + assertTrue(false); // should have thrown + } catch (SecurityException e) { + } + cleanUpInstall(otherPkgName); + ks = pm.getSigningKeySet(mContext.getPackageName()); + assertNotNull(ks); + } + + /* + * testGetKeySetByAlias - same as getSigningKeySet, but for keysets defined + * by this package. + */ + public void testGetKeySetByAlias() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet ks; + try { + ks = pm.getKeySetByAlias(null, null); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getKeySetByAlias(null, "keysetBogus"); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getKeySetByAlias("keysets.test.bogus.package", null); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + ks = pm.getKeySetByAlias("keysets.test.bogus.package", "A"); + assertTrue(false); // should have thrown + } catch(IllegalArgumentException e) { + } + try { + ks = pm.getKeySetByAlias(mPkgName, "keysetBogus"); + assertTrue(false); // should have thrown + } catch(IllegalArgumentException e) { + } + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + try { + ks = pm.getKeySetByAlias(otherPkgName, "A"); + assertTrue(false); // should have thrown + } catch (SecurityException e) { + } + cleanUpInstall(otherPkgName); + ks = pm.getKeySetByAlias(mPkgName, "A"); + assertNotNull(ks); + } + + public void testIsSignedBy() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet mSigningKS = pm.getSigningKeySet(mPkgName); + KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A"); + + try { + assertFalse(pm.isSignedBy(null, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(null, mSigningKS)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(mPkgName, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy("keysets.test.bogus.package", mDefinedKS)); + } catch(IllegalArgumentException e) { + } + assertFalse(pm.isSignedBy(mPkgName, mDefinedKS)); + assertFalse(pm.isSignedBy(mPkgName, new KeySet(new Binder()))); + assertTrue(pm.isSignedBy(mPkgName, mSigningKS)); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertFalse(pm.isSignedBy(otherPkgName, mDefinedKS)); + assertTrue(pm.isSignedBy(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertTrue(pm.isSignedBy(otherPkgName, mDefinedKS)); + assertTrue(pm.isSignedBy(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + } + + public void testIsSignedByExactly() throws Exception { + PackageManager pm = getPm(); + String mPkgName = mContext.getPackageName(); + String otherPkgName = "com.android.frameworks.coretests.keysets_api"; + KeySet mSigningKS = pm.getSigningKeySet(mPkgName); + KeySet mDefinedKS = pm.getKeySetByAlias(mPkgName, "A"); + try { + assertFalse(pm.isSignedBy(null, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(null, mSigningKS)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedBy(mPkgName, null)); + assertTrue(false); // should have thrown + } catch (NullPointerException e) { + } + try { + assertFalse(pm.isSignedByExactly("keysets.test.bogus.package", mDefinedKS)); + } catch(IllegalArgumentException e) { + } + assertFalse(pm.isSignedByExactly(mPkgName, mDefinedKS)); + assertFalse(pm.isSignedByExactly(mPkgName, new KeySet(new Binder()))); + assertTrue(pm.isSignedByExactly(mPkgName, mSigningKS)); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splat_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS)); + assertTrue(pm.isSignedByExactly(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + + installFromRawResource("keysetApi.apk", R.raw.keyset_splata_api, + 0, false, false, -1, PackageInfo.INSTALL_LOCATION_UNSPECIFIED); + assertFalse(pm.isSignedByExactly(otherPkgName, mDefinedKS)); + assertFalse(pm.isSignedByExactly(otherPkgName, mSigningKS)); + cleanUpInstall(otherPkgName); + } + + + + /** * The following tests are related to testing the checkSignatures api. */ private void checkSignatures(int apk1, int apk2, int expMatchResult) throws Exception { diff --git a/services/core/java/com/android/server/pm/KeySetHandle.java b/services/core/java/com/android/server/pm/KeySetHandle.java new file mode 100644 index 0000000..640feb3 --- /dev/null +++ b/services/core/java/com/android/server/pm/KeySetHandle.java @@ -0,0 +1,22 @@ +/* + * Copyright 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.os.Binder; + +public class KeySetHandle extends Binder { +}
\ No newline at end of file diff --git a/services/core/java/com/android/server/pm/KeySetManagerService.java b/services/core/java/com/android/server/pm/KeySetManagerService.java index c19951f..37bedf3 100644 --- a/services/core/java/com/android/server/pm/KeySetManagerService.java +++ b/services/core/java/com/android/server/pm/KeySetManagerService.java @@ -16,7 +16,6 @@ package com.android.server.pm; -import android.content.pm.KeySet; import android.content.pm.PackageParser; import android.os.Binder; import android.util.ArraySet; @@ -52,7 +51,7 @@ public class KeySetManagerService { /** Sentinel value returned when public key is not found. */ protected static final long PUBLIC_KEY_NOT_FOUND = -1; - private final LongSparseArray<KeySet> mKeySets; + private final LongSparseArray<KeySetHandle> mKeySets; private final LongSparseArray<PublicKey> mPublicKeys; @@ -65,7 +64,7 @@ public class KeySetManagerService { private static long lastIssuedKeyId = 0; public KeySetManagerService(Map<String, PackageSetting> packages) { - mKeySets = new LongSparseArray<KeySet>(); + mKeySets = new LongSparseArray<KeySetHandle>(); mPublicKeys = new LongSparseArray<PublicKey>(); mKeySetMapping = new LongSparseArray<ArraySet<Long>>(); mPackages = packages; @@ -82,7 +81,7 @@ public class KeySetManagerService { * * Note that this can return true for multiple KeySets. */ - public boolean packageIsSignedByLPr(String packageName, KeySet ks) { + public boolean packageIsSignedByLPr(String packageName, KeySetHandle ks) { PackageSetting pkg = mPackages.get(packageName); if (pkg == null) { throw new NullPointerException("Invalid package name"); @@ -91,16 +90,42 @@ public class KeySetManagerService { throw new NullPointerException("Package has no KeySet data"); } long id = getIdByKeySetLPr(ks); + if (id == KEYSET_NOT_FOUND) { + return false; + } return pkg.keySetData.packageIsSignedBy(id); } /** + * Determine if a package is signed by the given KeySet. + * + * Returns false if the package was not signed by all the + * keys in the KeySet, or if the package was signed by keys + * not in the KeySet. + * + * Note that this can return only for one KeySet. + */ + public boolean packageIsSignedByExactlyLPr(String packageName, KeySetHandle ks) { + PackageSetting pkg = mPackages.get(packageName); + if (pkg == null) { + throw new NullPointerException("Invalid package name"); + } + if (pkg.keySetData == null + || pkg.keySetData.getProperSigningKeySet() + == PackageKeySetData.KEYSET_UNASSIGNED) { + throw new NullPointerException("Package has no KeySet data"); + } + long id = getIdByKeySetLPr(ks); + return pkg.keySetData.getProperSigningKeySet() == id; + } + + /** * This informs the system that the given package has defined a KeySet * in its manifest that a) contains the given keys and b) is named * alias by that package. */ public void addDefinedKeySetToPackageLPw(String packageName, - Set<PublicKey> keys, String alias) { + ArraySet<PublicKey> keys, String alias) { if ((packageName == null) || (keys == null) || (alias == null)) { Slog.w(TAG, "Got null argument for a defined keyset, ignoring!"); return; @@ -110,7 +135,7 @@ public class KeySetManagerService { throw new NullPointerException("Unknown package"); } // Add to KeySets, then to package - KeySet ks = addKeySetLPw(keys); + KeySetHandle ks = addKeySetLPw(keys); long id = getIdByKeySetLPr(ks); pkg.keySetData.addDefinedKeySet(id, alias); } @@ -137,19 +162,18 @@ public class KeySetManagerService { * was signed by the provided KeySet. */ public void addSigningKeySetToPackageLPw(String packageName, - Set<PublicKey> signingKeys) { + ArraySet<PublicKey> signingKeys) { if ((packageName == null) || (signingKeys == null)) { Slog.w(TAG, "Got null argument for a signing keyset, ignoring!"); return; } // add the signing KeySet - KeySet ks = addKeySetLPw(signingKeys); + KeySetHandle ks = addKeySetLPw(signingKeys); long id = getIdByKeySetLPr(ks); - Set<Long> publicKeyIds = mKeySetMapping.get(id); + ArraySet<Long> publicKeyIds = mKeySetMapping.get(id); if (publicKeyIds == null) { throw new NullPointerException("Got invalid KeySet id"); } - // attach it to the package PackageSetting pkg = mPackages.get(packageName); if (pkg == null) { @@ -160,7 +184,7 @@ public class KeySetManagerService { // KeySet id to the package's signing KeySets for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) { long keySetID = mKeySets.keyAt(keySetIndex); - Set<Long> definedKeys = mKeySetMapping.get(keySetID); + ArraySet<Long> definedKeys = mKeySetMapping.get(keySetID); if (publicKeyIds.containsAll(definedKeys)) { pkg.keySetData.addSigningKeySet(keySetID); } @@ -171,9 +195,9 @@ public class KeySetManagerService { * Fetches the stable identifier associated with the given KeySet. Returns * {@link #KEYSET_NOT_FOUND} if the KeySet... wasn't found. */ - private long getIdByKeySetLPr(KeySet ks) { + private long getIdByKeySetLPr(KeySetHandle ks) { for (int keySetIndex = 0; keySetIndex < mKeySets.size(); keySetIndex++) { - KeySet value = mKeySets.valueAt(keySetIndex); + KeySetHandle value = mKeySets.valueAt(keySetIndex); if (ks.equals(value)) { return mKeySets.keyAt(keySetIndex); } @@ -187,25 +211,24 @@ public class KeySetManagerService { * Returns {@link #KEYSET_NOT_FOUND} if the identifier doesn't * identify a {@link KeySet}. */ - public KeySet getKeySetByIdLPr(long id) { + public KeySetHandle getKeySetByIdLPr(long id) { return mKeySets.get(id); } /** - * Fetches the {@link KeySet} that a given package refers to by the provided alias. - * - * @throws IllegalArgumentException if the package has no keyset data. - * @throws NullPointerException if the package is unknown. + * Fetches the {@link KeySetHandle} that a given package refers to by the + * provided alias. Returns null if the package is unknown or does not have a + * KeySet corresponding to that alias. */ - public KeySet getKeySetByAliasAndPackageNameLPr(String packageName, String alias) { + public KeySetHandle getKeySetByAliasAndPackageNameLPr(String packageName, String alias) { PackageSetting p = mPackages.get(packageName); - if (p == null) { - throw new NullPointerException("Unknown package"); + if (p == null || p.keySetData == null) { + return null; } - if (p.keySetData == null) { - throw new IllegalArgumentException("Package has no keySet data"); + Long keySetId = p.keySetData.getAliases().get(alias); + if (keySetId == null) { + throw new IllegalArgumentException("Unknown KeySet alias: " + alias); } - long keySetId = p.keySetData.getAliases().get(alias); return mKeySets.get(keySetId); } @@ -214,7 +237,7 @@ public class KeySetManagerService { * KeySet id. * * Returns {@code null} if the identifier doesn't - * identify a {@link KeySet}. + * identify a {@link KeySetHandle}. */ public ArraySet<PublicKey> getPublicKeysFromKeySetLPr(long id) { if(mKeySetMapping.get(id) == null) { @@ -228,36 +251,32 @@ public class KeySetManagerService { } /** - * Fetches all the known {@link KeySet KeySets} that signed the given + * Fetches the proper {@link KeySetHandle KeySet} that signed the given * package. * * @throws IllegalArgumentException if the package has no keyset data. * @throws NullPointerException if the package is unknown. */ - public Set<KeySet> getSigningKeySetsByPackageNameLPr(String packageName) { - Set<KeySet> signingKeySets = new ArraySet<KeySet>(); + public KeySetHandle getSigningKeySetByPackageNameLPr(String packageName) { PackageSetting p = mPackages.get(packageName); - if (p == null) { - throw new NullPointerException("Unknown package"); - } - if (p.keySetData == null || p.keySetData.getSigningKeySets() == null) { - throw new IllegalArgumentException("Package has no keySet data"); - } - for (long l : p.keySetData.getSigningKeySets()) { - signingKeySets.add(mKeySets.get(l)); + if (p == null + || p.keySetData == null + || p.keySetData.getProperSigningKeySet() + == PackageKeySetData.KEYSET_UNASSIGNED) { + return null; } - return signingKeySets; + return mKeySets.get(p.keySetData.getProperSigningKeySet()); } /** - * Fetches all the known {@link KeySet KeySets} that may upgrade the given + * Fetches all the known {@link KeySetHandle KeySets} that may upgrade the given * package. * * @throws IllegalArgumentException if the package has no keyset data. * @throws NullPointerException if the package is unknown. */ - public ArraySet<KeySet> getUpgradeKeySetsByPackageNameLPr(String packageName) { - ArraySet<KeySet> upgradeKeySets = new ArraySet<KeySet>(); + public ArraySet<KeySetHandle> getUpgradeKeySetsByPackageNameLPr(String packageName) { + ArraySet<KeySetHandle> upgradeKeySets = new ArraySet<KeySetHandle>(); PackageSetting p = mPackages.get(packageName); if (p == null) { throw new NullPointerException("Unknown package"); @@ -287,7 +306,7 @@ public class KeySetManagerService { * * Throws if the provided set is {@code null}. */ - private KeySet addKeySetLPw(Set<PublicKey> keys) { + private KeySetHandle addKeySetLPw(ArraySet<PublicKey> keys) { if (keys == null) { throw new NullPointerException("Provided keys cannot be null"); } @@ -305,7 +324,7 @@ public class KeySetManagerService { } // create the KeySet object - KeySet ks = new KeySet(new Binder()); + KeySetHandle ks = new KeySetHandle(); // get the first unoccupied slot in mKeySets long id = getFreeKeySetIDLPw(); // add the KeySet object to it @@ -318,7 +337,7 @@ public class KeySetManagerService { if (p.keySetData != null) { long pProperSigning = p.keySetData.getProperSigningKeySet(); if (pProperSigning != PackageKeySetData.KEYSET_UNASSIGNED) { - Set<Long> pSigningKeys = mKeySetMapping.get(pProperSigning); + ArraySet<Long> pSigningKeys = mKeySetMapping.get(pProperSigning); if (pSigningKeys.containsAll(addedKeyIds)) { p.keySetData.addSigningKeySet(id); } @@ -353,7 +372,7 @@ public class KeySetManagerService { */ private long getIdFromKeyIdsLPr(Set<Long> publicKeyIds) { for (int keyMapIndex = 0; keyMapIndex < mKeySetMapping.size(); keyMapIndex++) { - Set<Long> value = mKeySetMapping.valueAt(keyMapIndex); + ArraySet<Long> value = mKeySetMapping.valueAt(keyMapIndex); if (value.equals(publicKeyIds)) { return mKeySetMapping.keyAt(keyMapIndex); } @@ -582,7 +601,7 @@ public class KeySetManagerService { serializer.startTag(null, "keysets"); for (int keySetIndex = 0; keySetIndex < mKeySetMapping.size(); keySetIndex++) { long id = mKeySetMapping.keyAt(keySetIndex); - Set<Long> keys = mKeySetMapping.valueAt(keySetIndex); + ArraySet<Long> keys = mKeySetMapping.valueAt(keySetIndex); serializer.startTag(null, "keyset"); serializer.attribute(null, "identifier", Long.toString(id)); for (long keyId : keys) { @@ -662,7 +681,7 @@ public class KeySetManagerService { final String tagName = parser.getName(); if (tagName.equals("keyset")) { currentKeySetId = readIdentifierLPw(parser); - mKeySets.put(currentKeySetId, new KeySet(new Binder())); + mKeySets.put(currentKeySetId, new KeySetHandle()); mKeySetMapping.put(currentKeySetId, new ArraySet<Long>()); } else if (tagName.equals("key-id")) { long id = readIdentifierLPw(parser); diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 9edadb5..e2009c7 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -13233,4 +13233,83 @@ public class PackageManagerService extends IPackageManager.Stub { } return mUserNeedsBadging.valueAt(index); } + + @Override + public KeySetHandle getKeySetByAlias(String packageName, String alias) { + if (packageName == null || alias == null) { + return null; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (pkg.applicationInfo.uid != Binder.getCallingUid() + && Process.SYSTEM_UID != Binder.getCallingUid()) { + throw new SecurityException("May not access KeySets defined by" + + " aliases in other applications."); + } + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.getKeySetByAliasAndPackageNameLPr(packageName, alias); + } + } + + @Override + public KeySetHandle getSigningKeySet(String packageName) { + if (packageName == null) { + return null; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (pkg.applicationInfo.uid != Binder.getCallingUid() + && Process.SYSTEM_UID != Binder.getCallingUid()) { + throw new SecurityException("May not access signing KeySet of other apps."); + } + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.getSigningKeySetByPackageNameLPr(packageName); + } + } + + @Override + public boolean isPackageSignedByKeySet(String packageName, IBinder ks) { + if (packageName == null || ks == null) { + return false; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (ks instanceof KeySetHandle) { + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.packageIsSignedByLPr(packageName, (KeySetHandle) ks); + } + return false; + } + } + + @Override + public boolean isPackageSignedByKeySetExactly(String packageName, IBinder ks) { + if (packageName == null || ks == null) { + return false; + } + synchronized(mPackages) { + final PackageParser.Package pkg = mPackages.get(packageName); + if (pkg == null) { + Slog.w(TAG, "KeySet requested for unknown package:" + packageName); + throw new IllegalArgumentException("Unknown package: " + packageName); + } + if (ks instanceof KeySetHandle) { + KeySetManagerService ksms = mSettings.mKeySetManagerService; + return ksms.packageIsSignedByExactlyLPr(packageName, (KeySetHandle) ks); + } + return false; + } + } } diff --git a/test-runner/src/android/test/mock/MockPackageManager.java b/test-runner/src/android/test/mock/MockPackageManager.java index a14714a..648c418 100644 --- a/test-runner/src/android/test/mock/MockPackageManager.java +++ b/test-runner/src/android/test/mock/MockPackageManager.java @@ -31,6 +31,7 @@ import android.content.pm.IPackageInstallObserver; import android.content.pm.IPackageMoveObserver; import android.content.pm.IPackageStatsObserver; import android.content.pm.InstrumentationInfo; +import android.content.pm.KeySet; import android.content.pm.ManifestDigest; import android.content.pm.PackageInfo; import android.content.pm.PackageInstaller; @@ -616,6 +617,26 @@ public class MockPackageManager extends PackageManager { throw new UnsupportedOperationException(); } + @Override + public KeySet getKeySetByAlias(String packageName, String alias) { + throw new UnsupportedOperationException(); + } + + @Override + public KeySet getSigningKeySet(String packageName) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSignedBy(String packageName, KeySet ks) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isSignedByExactly(String packageName, KeySet ks) { + throw new UnsupportedOperationException(); + } + /** * @hide */ |