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:
@@ -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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user