Merge "Restrict updates of system packages" into nyc-dev

am: 4e4fca43b0

* commit '4e4fca43b0fb1320ce0265640d33b700cd886da8':
  Restrict updates of system packages

Change-Id: Ifbfdbcc9056fddbd41ea6d690441cb3805f7c3ff
This commit is contained in:
Todd Kennedy
2016-05-02 14:51:59 +00:00
committed by android-build-merger
4 changed files with 74 additions and 2 deletions

View File

@@ -159,6 +159,7 @@ public class PackageParser {
private static final String TAG_SUPPORTS_INPUT = "supports-input";
private static final String TAG_EAT_COMMENT = "eat-comment";
private static final String TAG_PACKAGE = "package";
private static final String TAG_RESTRICT_UPDATE = "restrict-update";
// These are the tags supported by child packages
private static final Set<String> CHILD_PACKAGE_TAGS = new ArraySet<>();
@@ -1639,9 +1640,9 @@ public class PackageParser {
/**
* This is the common parsing routing for handling parent and child
* packages in a base APK. The difference between parent and child
* parsing is that some targs are not supported by child packages as
* parsing is that some tags are not supported by child packages as
* well as some manifest attributes are ignored. The implementation
* assumes the calling code already handled the manifest tag if needed
* assumes the calling code has already handled the manifest tag if needed
* (this applies to the parent only).
*
* @param pkg The package which to populate
@@ -2089,6 +2090,29 @@ public class PackageParser {
// If parsing a child failed the error is already set
return null;
}
} else if (tagName.equals(TAG_RESTRICT_UPDATE)) {
if ((flags & PARSE_IS_SYSTEM_DIR) != 0) {
sa = res.obtainAttributes(parser,
com.android.internal.R.styleable.AndroidManifestRestrictUpdate);
final String hash = sa.getNonConfigurationString(
com.android.internal.R.styleable.AndroidManifestRestrictUpdate_hash, 0);
sa.recycle();
pkg.restrictUpdateHash = null;
if (hash != null) {
final int hashLength = hash.length();
final byte[] hashBytes = new byte[hashLength / 2];
for (int i = 0; i < hashLength; i += 2){
hashBytes[i/2] = (byte) ((Character.digit(hash.charAt(i), 16) << 4)
+ Character.digit(hash.charAt(i + 1), 16));
}
pkg.restrictUpdateHash = hashBytes;
}
}
XmlUtils.skipCurrentTag(parser);
} else if (RIGID_PARSER) {
outError[0] = "Bad element under <manifest>: "
+ parser.getName();
@@ -4822,6 +4846,8 @@ public class PackageParser {
*/
public boolean use32bitAbi;
public byte[] restrictUpdateHash;
public Package(String packageName) {
this.packageName = packageName;
applicationInfo.packageName = packageName;

View File

@@ -2291,4 +2291,14 @@
<attr name="minimalHeight" format="dimension" />
</declare-styleable>
<!-- <code>restrict-update</code> tag restricts system apps from being updated unless the
SHA-512 hash equals the specified value.
@hide -->
<declare-styleable name="AndroidManifestRestrictUpdate" parent="AndroidManifest">
<!-- The SHA-512 hash of the only APK that can be used to update a package.
<p>NOTE: This is only applicable to system packages.
@hide -->
<attr name="hash" format="string" />
</declare-styleable>
</resources>

View File

@@ -2715,6 +2715,7 @@
<public type="attr" name="contentInsetEndWithActions" />
<public type="attr" name="numberPickerStyle" />
<public type="attr" name="enableVrMode" />
<public type="attr" name="hash" />
<public type="style" name="Theme.Material.Light.DialogWhenLarge.DarkActionBar" />
<public type="style" name="Widget.Material.SeekBar.Discrete" />

View File

@@ -266,6 +266,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
@@ -274,6 +275,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
@@ -13838,6 +13840,13 @@ public class PackageManagerService extends IPackageManager.Stub {
return false;
}
private static void updateDigest(MessageDigest digest, File file) throws IOException {
try (DigestInputStream digestStream =
new DigestInputStream(new FileInputStream(file), digest)) {
while (digestStream.read() != -1) {} // nothing to do; just plow through the file
}
}
private void replacePackageLIF(PackageParser.Package pkg, final int policyFlags, int scanFlags,
UserHandle user, String installerPackageName, PackageInstalledInfo res) {
final boolean isEphemeral = (policyFlags & PackageParser.PARSE_IS_EPHEMERAL) != 0;
@@ -13892,6 +13901,32 @@ public class PackageManagerService extends IPackageManager.Stub {
}
}
// don't allow a system upgrade unless the upgrade hash matches
if (oldPackage.restrictUpdateHash != null && oldPackage.isSystemApp()) {
byte[] digestBytes = null;
try {
final MessageDigest digest = MessageDigest.getInstance("SHA-512");
updateDigest(digest, new File(pkg.baseCodePath));
if (!ArrayUtils.isEmpty(pkg.splitCodePaths)) {
for (String path : pkg.splitCodePaths) {
updateDigest(digest, new File(path));
}
}
digestBytes = digest.digest();
} catch (NoSuchAlgorithmException | IOException e) {
res.setError(INSTALL_FAILED_INVALID_APK,
"Could not compute hash: " + pkgName);
return;
}
if (!Arrays.equals(oldPackage.restrictUpdateHash, digestBytes)) {
res.setError(INSTALL_FAILED_INVALID_APK,
"New package fails restrict-update check: " + pkgName);
return;
}
// retain upgrade restriction
pkg.restrictUpdateHash = oldPackage.restrictUpdateHash;
}
// Check for shared user id changes
String invalidPackageName =
getParentOrChildPackageChangedSharedUser(oldPackage, pkg);