diff options
4 files changed, 206 insertions, 8 deletions
diff --git a/core/java/com/android/server/SystemConfig.java b/core/java/com/android/server/SystemConfig.java index 9b583be547c3..00b01051adae 100644 --- a/core/java/com/android/server/SystemConfig.java +++ b/core/java/com/android/server/SystemConfig.java @@ -339,6 +339,8 @@ public class SystemConfig { // A map from package name of vendor APEXes that can be updated to an installer package name // allowed to install updates for it. private final ArrayMap<String, String> mAllowedVendorApexes = new ArrayMap<>(); + // A set of package names that are allowed to use <install-constraints> manifest tag. + private final Set<String> mInstallConstraintsAllowlist = new ArraySet<>(); private String mModulesInstallerPackageName; @@ -535,6 +537,10 @@ public class SystemConfig { return mAllowedVendorApexes; } + public Set<String> getInstallConstraintsAllowlist() { + return mInstallConstraintsAllowlist; + } + public String getModulesInstallerPackageName() { return mModulesInstallerPackageName; } @@ -1455,6 +1461,20 @@ public class SystemConfig { } XmlUtils.skipCurrentTag(parser); } break; + case "install-constraints-allowed": { + if (allowAppConfigs) { + String packageName = parser.getAttributeValue(null, "package"); + if (packageName == null) { + Slog.w(TAG, "<" + name + "> without package in " + permFile + + " at " + parser.getPositionDescription()); + } else { + mInstallConstraintsAllowlist.add(packageName); + } + } else { + logNotAllowedInPartition(name, permFile, parser); + } + XmlUtils.skipCurrentTag(parser); + } break; default: { Slog.w(TAG, "Tag " + name + " is unknown in " + permFile + " at " + parser.getPositionDescription()); diff --git a/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java new file mode 100644 index 000000000000..14976848fe61 --- /dev/null +++ b/services/core/java/com/android/server/pm/pkg/component/InstallConstraintsTagParser.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2022 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.pkg.component; + + +import android.content.pm.parsing.result.ParseInput; +import android.content.pm.parsing.result.ParseResult; +import android.content.res.Resources; +import android.content.res.TypedArray; +import android.content.res.XmlResourceParser; +import android.os.Build; +import android.util.ArraySet; + +import com.android.internal.R; +import com.android.server.SystemConfig; +import com.android.server.pm.pkg.parsing.ParsingPackage; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; + +import java.io.IOException; +import java.util.Set; + +/** + * Utility methods for handling the tag {@code <install-constraints/>} + * + * @hide + */ +public class InstallConstraintsTagParser { + + private static final String TAG_FINGERPRINT_PREFIX = "fingerprint-prefix"; + + /** + * @hide + */ + public static ParseResult<ParsingPackage> parseInstallConstraints( + ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + Set<String> allowlist = SystemConfig.getInstance().getInstallConstraintsAllowlist(); + if (!allowlist.contains(pkg.getPackageName())) { + return input.skip("install-constraints cannot be used by this package"); + } + + ParseResult<Set<String>> prefixes = parseFingerprintPrefixes(input, res, parser); + if (prefixes.isSuccess()) { + if (validateFingerprintPrefixes(prefixes.getResult())) { + return input.success(pkg); + } else { + return input.skip( + "Install of this package is restricted on this device; device fingerprint" + + " does not start with one of the allowed prefixes"); + } + } + return input.skip(prefixes.getErrorMessage()); + } + + private static ParseResult<Set<String>> parseFingerprintPrefixes( + ParseInput input, Resources res, XmlResourceParser parser) + throws XmlPullParserException, IOException { + Set<String> prefixes = new ArraySet<>(); + int type; + while (true) { + // move to the tag that contains the next prefix + type = parser.next(); + if (type == XmlPullParser.END_TAG) { + if (prefixes.size() == 0) { + return input.error("install-constraints must contain at least one constraint"); + } + return input.success(prefixes); + } else if (type == XmlPullParser.START_TAG) { + if (parser.getName().equals(TAG_FINGERPRINT_PREFIX)) { + ParseResult<String> parsedPrefix = + readFingerprintPrefixValue(input, res, parser); + if (parsedPrefix.isSuccess()) { + prefixes.add(parsedPrefix.getResult()); + } else { + return input.error(parsedPrefix.getErrorMessage()); + } + } else { + return input.error("Unexpected tag: " + parser.getName()); + } + + // consume the end tag of this attribute + type = parser.next(); + if (type != XmlPullParser.END_TAG) { + return input.error("Expected end tag; instead got " + type); + } + } + } + } + + private static ParseResult<String> readFingerprintPrefixValue(ParseInput input, Resources res, + XmlResourceParser parser) { + TypedArray sa = res.obtainAttributes(parser, + R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix); + try { + String value = sa.getString( + R.styleable.AndroidManifestInstallConstraintsFingerprintPrefix_value); + if (value == null) { + return input.error("Failed to specify prefix value"); + } + return input.success(value); + } finally { + sa.recycle(); + } + } + + private static boolean validateFingerprintPrefixes(Set<String> prefixes) { + String fingerprint = Build.FINGERPRINT; + for (String prefix : prefixes) { + if (fingerprint.startsWith(prefix)) { + return true; + } + } + return false; + } +} 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 d8945eddc86b..7ce7f7ebf6cc 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 @@ -95,6 +95,7 @@ import com.android.server.pm.SharedUidMigration; import com.android.server.pm.permission.CompatibilityPermissionInfo; import com.android.server.pm.pkg.component.ComponentMutateUtils; import com.android.server.pm.pkg.component.ComponentParseUtils; +import com.android.server.pm.pkg.component.InstallConstraintsTagParser; import com.android.server.pm.pkg.component.ParsedActivity; import com.android.server.pm.pkg.component.ParsedActivityUtils; import com.android.server.pm.pkg.component.ParsedApexSystemService; @@ -1735,14 +1736,7 @@ public class ParsingPackageUtils { private static ParseResult<ParsingPackage> parseInstallConstraints( ParseInput input, ParsingPackage pkg, Resources res, XmlResourceParser parser) throws IOException, XmlPullParserException { - final int depth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG - || parser.getDepth() > depth)) { - // TODO(b/232475788): call input.skip if constraints check fails - } - return input.success(pkg); + return InstallConstraintsTagParser.parseInstallConstraints(input, pkg, res, parser); } private static ParseResult<ParsingPackage> parseQueries(ParseInput input, ParsingPackage pkg, diff --git a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java index e9171c0c3514..92c7871e611d 100644 --- a/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java +++ b/services/tests/servicestests/src/com/android/server/systemconfig/SystemConfigTest.java @@ -336,6 +336,59 @@ public class SystemConfigTest { assertThat(mSysConfig.getAllowedVendorApexes()).isEmpty(); } + /** + * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. + */ + @Test + public void readPermissions_installConstraints_successful() throws IOException { + final String contents = + "<config>\n" + + " <install-constraints-allowed package=\"com.android.apex1\" />\n" + + "</config>"; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "install-constraints-allowlist.xml", contents); + + readPermissions(folder, /* Grant all permission flags */ ~0); + + assertThat(mSysConfig.getInstallConstraintsAllowlist()) + .containsExactly("com.android.apex1"); + } + + /** + * Tests that readPermissions works correctly for the tag: {@code install-constraints-allowed}. + */ + @Test + public void readPermissions_installConstraints_noPackage() throws IOException { + final String contents = + "<config>\n" + + " <install-constraints-allowed/>\n" + + "</config>"; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "install-constraints-allowlist.xml", contents); + + readPermissions(folder, /* Grant all permission flags */ ~0); + + assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); + } + + /** + * Tests that readPermissions works correctly for the tag {@code install-constraints-allowed} + * without {@link SystemConfig#ALLOW_VENDOR_APEX}. + */ + @Test + public void readPermissions_installConstraints_noAppConfigs() throws IOException { + final String contents = + "<config>\n" + + " <install-constraints-allowed package=\"com.android.apex1\" />\n" + + "</config>"; + final File folder = createTempSubfolder("folder"); + createTempFile(folder, "install-constraints-allowlist.xml", contents); + + readPermissions(folder, /* Grant all but ALLOW_APP_CONFIGS flag */ ~0x08); + + assertThat(mSysConfig.getInstallConstraintsAllowlist()).isEmpty(); + } + @Test public void readApexPrivAppPermissions_addAllPermissions() throws Exception { |