diff options
Diffstat (limited to 'core')
12 files changed, 331 insertions, 12 deletions
diff --git a/core/java/android/content/pm/PackageInfoLite.java b/core/java/android/content/pm/PackageInfoLite.java index da97fde..9625944 100644 --- a/core/java/android/content/pm/PackageInfoLite.java +++ b/core/java/android/content/pm/PackageInfoLite.java @@ -41,6 +41,8 @@ public class PackageInfoLite implements Parcelable { public int recommendedInstallLocation; public int installLocation; + public VerifierInfo[] verifiers; + public PackageInfoLite() { } @@ -58,6 +60,13 @@ public class PackageInfoLite implements Parcelable { dest.writeString(packageName); dest.writeInt(recommendedInstallLocation); dest.writeInt(installLocation); + + if (verifiers == null || verifiers.length == 0) { + dest.writeInt(0); + } else { + dest.writeInt(verifiers.length); + dest.writeTypedArray(verifiers, parcelableFlags); + } } public static final Parcelable.Creator<PackageInfoLite> CREATOR @@ -75,5 +84,13 @@ public class PackageInfoLite implements Parcelable { packageName = source.readString(); recommendedInstallLocation = source.readInt(); installLocation = source.readInt(); + + final int verifiersLength = source.readInt(); + if (verifiersLength == 0) { + verifiers = new VerifierInfo[0]; + } else { + verifiers = new VerifierInfo[verifiersLength]; + source.readTypedArray(verifiers, VerifierInfo.CREATOR); + } } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index ef7e233..d45a71a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -725,6 +725,16 @@ public abstract class PackageManager { public static final int MOVE_EXTERNAL_MEDIA = 0x00000002; /** + * Usable by the required verifier as the {@code verificationCode} argument + * for {@link PackageManager#verifyPendingInstall} to indicate that it will + * allow the installation to proceed without any of the optional verifiers + * needing to vote. + * + * @hide + */ + public static final int VERIFICATION_ALLOW_WITHOUT_SUFFICIENT = 2; + + /** * Used as the {@code verificationCode} argument for * {@link PackageManager#verifyPendingInstall} to indicate that the calling * package verifier allows the installation to proceed. diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index e7b844c..c30675b 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -28,7 +28,9 @@ import android.os.Build; import android.os.Bundle; import android.os.PatternMatcher; import android.util.AttributeSet; +import android.util.Base64; import android.util.DisplayMetrics; +import android.util.Log; import android.util.Slog; import android.util.TypedValue; import com.android.internal.util.XmlUtils; @@ -40,11 +42,18 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.lang.ref.WeakReference; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; import java.security.cert.Certificate; import java.security.cert.CertificateEncodingException; +import java.security.spec.EncodedKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; +import java.util.List; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -150,12 +159,14 @@ public class PackageParser { * @hide */ public static class PackageLite { - public String packageName; - public int installLocation; - public String mScanPath; - public PackageLite(String packageName, int installLocation) { + public final String packageName; + public final int installLocation; + public final VerifierInfo[] verifiers; + + public PackageLite(String packageName, int installLocation, List<VerifierInfo> verifiers) { this.packageName = packageName; this.installLocation = installLocation; + this.verifiers = verifiers.toArray(new VerifierInfo[verifiers.size()]); } } @@ -619,8 +630,9 @@ public class PackageParser { * @return PackageLite object with package information or null on failure. */ public static PackageLite parsePackageLite(String packageFilePath, int flags) { - XmlResourceParser parser = null; AssetManager assmgr = null; + final XmlResourceParser parser; + final Resources res; try { assmgr = new AssetManager(); assmgr.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, @@ -631,6 +643,9 @@ public class PackageParser { return null; } + final DisplayMetrics metrics = new DisplayMetrics(); + metrics.setToDefaults(); + res = new Resources(assmgr, metrics, null); parser = assmgr.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME); } catch (Exception e) { if (assmgr != null) assmgr.close(); @@ -638,11 +653,12 @@ public class PackageParser { + packageFilePath, e); return null; } - AttributeSet attrs = parser; - String errors[] = new String[1]; + + final AttributeSet attrs = parser; + final String errors[] = new String[1]; PackageLite packageLite = null; try { - packageLite = parsePackageLite(parser, attrs, flags, errors); + packageLite = parsePackageLite(res, parser, attrs, flags, errors); } catch (IOException e) { Slog.w(TAG, packageFilePath, e); } catch (XmlPullParserException e) { @@ -719,9 +735,9 @@ public class PackageParser { return pkgName.intern(); } - private static PackageLite parsePackageLite(XmlPullParser parser, - AttributeSet attrs, int flags, String[] outError) - throws IOException, XmlPullParserException { + private static PackageLite parsePackageLite(Resources res, XmlPullParser parser, + AttributeSet attrs, int flags, String[] outError) throws IOException, + XmlPullParserException { int type; while ((type = parser.next()) != XmlPullParser.START_TAG @@ -759,7 +775,26 @@ public class PackageParser { break; } } - return new PackageLite(pkgName.intern(), installLocation); + + // Only search the tree when the tag is directly below <manifest> + final int searchDepth = parser.getDepth() + 1; + + final List<VerifierInfo> verifiers = new ArrayList<VerifierInfo>(); + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG || parser.getDepth() >= searchDepth)) { + if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { + continue; + } + + if (parser.getDepth() == searchDepth && "package-verifier".equals(parser.getName())) { + final VerifierInfo verifier = parseVerifier(res, parser, attrs, flags, outError); + if (verifier != null) { + verifiers.add(verifier); + } + } + } + + return new PackageLite(pkgName.intern(), installLocation, verifiers); } /** @@ -2691,6 +2726,63 @@ public class PackageParser { return data; } + private static VerifierInfo parseVerifier(Resources res, XmlPullParser parser, + AttributeSet attrs, int flags, String[] outError) throws XmlPullParserException, + IOException { + final TypedArray sa = res.obtainAttributes(attrs, + com.android.internal.R.styleable.AndroidManifestPackageVerifier); + + final String packageName = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestPackageVerifier_name); + + final String encodedPublicKey = sa.getNonResourceString( + com.android.internal.R.styleable.AndroidManifestPackageVerifier_publicKey); + + sa.recycle(); + + if (packageName == null || packageName.length() == 0) { + Slog.i(TAG, "verifier package name was null; skipping"); + return null; + } else if (encodedPublicKey == null) { + Slog.i(TAG, "verifier " + packageName + " public key was null; skipping"); + } + + EncodedKeySpec keySpec; + try { + final byte[] encoded = Base64.decode(encodedPublicKey, Base64.DEFAULT); + keySpec = new X509EncodedKeySpec(encoded); + } catch (IllegalArgumentException e) { + Slog.i(TAG, "Could not parse verifier " + packageName + " public key; invalid Base64"); + return null; + } + + /* First try the key as an RSA key. */ + try { + final KeyFactory keyFactory = KeyFactory.getInstance("RSA"); + final PublicKey publicKey = keyFactory.generatePublic(keySpec); + return new VerifierInfo(packageName, publicKey); + } catch (NoSuchAlgorithmException e) { + Log.wtf(TAG, "Could not parse public key because RSA isn't included in build"); + return null; + } catch (InvalidKeySpecException e) { + // Not a RSA public key. + } + + /* Now try it as a DSA key. */ + try { + final KeyFactory keyFactory = KeyFactory.getInstance("DSA"); + final PublicKey publicKey = keyFactory.generatePublic(keySpec); + return new VerifierInfo(packageName, publicKey); + } catch (NoSuchAlgorithmException e) { + Log.wtf(TAG, "Could not parse public key because DSA isn't included in build"); + return null; + } catch (InvalidKeySpecException e) { + // Not a DSA public key. + } + + return null; + } + private static final String ANDROID_RESOURCES = "http://schemas.android.com/apk/res/android"; diff --git a/core/java/android/content/pm/Signature.java b/core/java/android/content/pm/Signature.java index c6aefb8..9c9340d 100644 --- a/core/java/android/content/pm/Signature.java +++ b/core/java/android/content/pm/Signature.java @@ -19,7 +19,12 @@ package android.content.pm; import android.os.Parcel; import android.os.Parcelable; +import java.io.ByteArrayInputStream; import java.lang.ref.SoftReference; +import java.security.PublicKey; +import java.security.cert.Certificate; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; import java.util.Arrays; /** @@ -135,6 +140,20 @@ public class Signature implements Parcelable { return bytes; } + /** + * Returns the public key for this signature. + * + * @throws CertificateException when Signature isn't a valid X.509 + * certificate; shouldn't happen. + * @hide + */ + public PublicKey getPublicKey() throws CertificateException { + final CertificateFactory certFactory = CertificateFactory.getInstance("X.509"); + final ByteArrayInputStream bais = new ByteArrayInputStream(mSignature); + final Certificate cert = certFactory.generateCertificate(bais); + return cert.getPublicKey(); + } + @Override public boolean equals(Object obj) { try { diff --git a/core/java/android/content/pm/VerifierInfo.aidl b/core/java/android/content/pm/VerifierInfo.aidl new file mode 100644 index 0000000..7702d38 --- /dev/null +++ b/core/java/android/content/pm/VerifierInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright 2011, 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 android.content.pm; + +parcelable VerifierInfo; diff --git a/core/java/android/content/pm/VerifierInfo.java b/core/java/android/content/pm/VerifierInfo.java new file mode 100644 index 0000000..0a2b283 --- /dev/null +++ b/core/java/android/content/pm/VerifierInfo.java @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2011 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 android.content.pm; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.security.PublicKey; + +/** + * Contains information about a package verifier as used by + * {@code PackageManagerService} during package verification. + * + * @hide + */ +public class VerifierInfo implements Parcelable { + /** Package name of the verifier. */ + public final String packageName; + + /** Signatures used to sign the package verifier's package. */ + public final PublicKey publicKey; + + /** + * Creates an object that represents a verifier info object. + * + * @param packageName the package name in Java-style. Must not be {@code + * null} or empty. + * @param publicKey the public key for the signer encoded in Base64. Must + * not be {@code null} or empty. + * @throws IllegalArgumentException if either argument is null or empty. + */ + public VerifierInfo(String packageName, PublicKey publicKey) { + if (packageName == null || packageName.length() == 0) { + throw new IllegalArgumentException("packageName must not be null or empty"); + } else if (publicKey == null) { + throw new IllegalArgumentException("publicKey must not be null"); + } + + this.packageName = packageName; + this.publicKey = publicKey; + } + + private VerifierInfo(Parcel source) { + packageName = source.readString(); + publicKey = (PublicKey) source.readSerializable(); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(packageName); + dest.writeSerializable(publicKey); + } + + public static final Parcelable.Creator<VerifierInfo> CREATOR + = new Parcelable.Creator<VerifierInfo>() { + public VerifierInfo createFromParcel(Parcel source) { + return new VerifierInfo(source); + } + + public VerifierInfo[] newArray(int size) { + return new VerifierInfo[size]; + } + }; +}
\ No newline at end of file diff --git a/core/tests/coretests/apks/install_verifier_bad/Android.mk b/core/tests/coretests/apks/install_verifier_bad/Android.mk new file mode 100644 index 0000000..b50cfd0 --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_bad/Android.mk @@ -0,0 +1,11 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := FrameworkCoreTests_install_verifier_bad + +include $(BUILD_PACKAGE) + diff --git a/core/tests/coretests/apks/install_verifier_bad/AndroidManifest.xml b/core/tests/coretests/apks/install_verifier_bad/AndroidManifest.xml new file mode 100644 index 0000000..0170cdd --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_bad/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?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.install_verifier_bad"> + + <package-verifier android:name="com.android.frameworks.coretests.nonexistent" android:publicKey="Zm9vYmFy" /> + + <application android:hasCode="false"> + </application> +</manifest> diff --git a/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml b/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml new file mode 100644 index 0000000..3b8b3b1 --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_bad/res/values/strings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Just need this dummy file to have something to build. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="dummy">dummy</string> +</resources> diff --git a/core/tests/coretests/apks/install_verifier_good/Android.mk b/core/tests/coretests/apks/install_verifier_good/Android.mk new file mode 100644 index 0000000..a48a80e --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_good/Android.mk @@ -0,0 +1,10 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := tests + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_PACKAGE_NAME := FrameworkCoreTests_install_verifier_good + +include $(BUILD_PACKAGE) diff --git a/core/tests/coretests/apks/install_verifier_good/AndroidManifest.xml b/core/tests/coretests/apks/install_verifier_good/AndroidManifest.xml new file mode 100644 index 0000000..90135a5 --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_good/AndroidManifest.xml @@ -0,0 +1,23 @@ +<?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.install_verifier_bad"> + + <package-verifier android:name="com.android.frameworks.coretests" android:publicKey="MIIBIDANBgkqhkiG9w0BAQEFAAOCAQ0AMIIBCAKCAQEAnHgFkqwNXTgc3qpl7MimAG42SAxtcgexIBG+UIY6q+K1XQCa33FG1vIgIoDHzU172yYkO4qAbCazSxN1I6SSaCJJBNwBST58Cs8aBch09psDe2AwnZB00kKA4WutKoc0NhlR6vcqSC0JsgSxh14SrJjBqnc9aAC56v3lbVi+2OjaFvmjYAmcN6g0pt/tt7a0SgSeB6Jp/M8sVJbyzzbWTfkKO42PNKO6q0z1M3GrJ3GbO6WHVK0MU/wU4dtF1R4jT7vpPJuk7fnOVCYTUOxTVge/aaL/SqB9tffqIA0JpsG0niFAL4ntEZCJOqtakYDxUugvhaRXU89fwZBxxe7IJwIBAw==" /> + + <application android:hasCode="false"> + </application> +</manifest> diff --git a/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml b/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml new file mode 100644 index 0000000..3b8b3b1 --- /dev/null +++ b/core/tests/coretests/apks/install_verifier_good/res/values/strings.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="utf-8"?> + +<!-- Just need this dummy file to have something to build. --> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <string name="dummy">dummy</string> +</resources> |