Verify WebView package info before loading WebView

To ensure that the package we are using to load WebView is a valid
provider we need to verify this before loading WebView - not only when
choosing what package to use.

Bug: 27900925
Change-Id: If57ca32a7a3fa08735a3b8ea9ed268c1bb1b8ede
This commit is contained in:
Gustav Sennton
2016-04-14 09:58:36 +01:00
parent e767e1bc17
commit cd8f2737cc

View File

@@ -18,13 +18,14 @@ package android.webkit;
import android.annotation.SystemApi;
import android.app.ActivityManagerInternal;
import android.app.ActivityManagerNative;
import android.app.AppGlobals;
import android.app.Application;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.res.XmlResourceParser;
import android.content.pm.Signature;
import android.os.Build;
import android.os.Process;
import android.os.RemoteException;
@@ -32,27 +33,21 @@ import android.os.ServiceManager;
import android.os.StrictMode;
import android.os.SystemProperties;
import android.os.Trace;
import android.provider.Settings;
import android.provider.Settings.Secure;
import android.text.TextUtils;
import android.util.AndroidRuntimeException;
import android.util.ArraySet;
import android.util.Log;
import com.android.internal.util.XmlUtils;
import com.android.server.LocalServices;
import dalvik.system.VMRuntime;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.xmlpull.v1.XmlPullParserException;
/**
* Top level factory, used creating all the main WebView implementation classes.
*
@@ -192,52 +187,126 @@ public final class WebViewFactory {
}
}
/**
* Returns true if the signatures match, false otherwise
*/
private static boolean signaturesEquals(Signature[] s1, Signature[] s2) {
if (s1 == null) {
return s2 == null;
}
if (s2 == null) return false;
ArraySet<Signature> set1 = new ArraySet<>();
for(Signature signature : s1) {
set1.add(signature);
}
ArraySet<Signature> set2 = new ArraySet<>();
for(Signature signature : s2) {
set2.add(signature);
}
return set1.equals(set2);
}
// Throws MissingWebViewPackageException on failure
private static void verifyPackageInfo(PackageInfo chosen, PackageInfo toUse) {
if (!chosen.packageName.equals(toUse.packageName)) {
throw new MissingWebViewPackageException("Failed to verify WebView provider, "
+ "packageName mismatch, expected: "
+ chosen.packageName + " actual: " + toUse.packageName);
}
if (chosen.versionCode > toUse.versionCode) {
throw new MissingWebViewPackageException("Failed to verify WebView provider, "
+ "version code mismatch, expected: " + chosen.versionCode
+ " actual: " + toUse.versionCode);
}
if (getWebViewLibrary(toUse.applicationInfo) == null) {
throw new MissingWebViewPackageException("Tried to load an invalid WebView provider: "
+ toUse.packageName);
}
if (!signaturesEquals(chosen.signatures, toUse.signatures)) {
throw new MissingWebViewPackageException("Failed to verify WebView provider, "
+ "signature mismatch");
}
}
private static Context getWebViewContextAndSetProvider() {
Application initialApplication = AppGlobals.getInitialApplication();
try {
WebViewProviderResponse response = null;
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"WebViewUpdateService.waitForAndGetProvider()");
try {
response = getUpdateService().waitForAndGetProvider();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
if (response.status != LIBLOAD_SUCCESS) {
throw new MissingWebViewPackageException("Failed to load WebView provider: "
+ getWebViewPreparationErrorReason(response.status));
}
// Register to be killed before fetching package info - so that we will be
// killed if the package info goes out-of-date.
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "ActivityManager.addPackageDependency()");
try {
ActivityManagerNative.getDefault().addPackageDependency(
response.packageInfo.packageName);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
// Fetch package info and verify it against the chosen package
PackageInfo newPackageInfo = null;
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getPackageInfo()");
try {
newPackageInfo = initialApplication.getPackageManager().getPackageInfo(
response.packageInfo.packageName,
PackageManager.GET_SHARED_LIBRARY_FILES
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING
// Make sure that we fetch the current provider even if its not
// installed for the current user
| PackageManager.MATCH_UNINSTALLED_PACKAGES
// Fetch signatures for verification
| PackageManager.GET_SIGNATURES
// Get meta-data for meta data flag verification
| PackageManager.GET_META_DATA);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
// Validate the newly fetched package info, throws MissingWebViewPackageException on
// failure
verifyPackageInfo(response.packageInfo, newPackageInfo);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
try {
// Construct an app context to load the Java code into the current app.
Context webViewContext = initialApplication.createApplicationContext(
newPackageInfo.applicationInfo,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
sPackageInfo = response.packageInfo;
return webViewContext;
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (RemoteException | PackageManager.NameNotFoundException e) {
throw new MissingWebViewPackageException("Failed to load WebView provider: " + e);
}
}
private static Class<WebViewFactoryProvider> getProviderClass() {
Context webViewContext = null;
Application initialApplication = AppGlobals.getInitialApplication();
try {
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"WebViewFactory.waitForProviderAndSetPackageInfo()");
"WebViewFactory.getWebViewContextAndSetProvider()");
try {
// First fetch the package info so we can log the webview package version.
int res = waitForProviderAndSetPackageInfo();
if (res != LIBLOAD_SUCCESS) {
throw new MissingWebViewPackageException(
"Failed to load WebView provider, error: "
+ getWebViewPreparationErrorReason(res));
}
webViewContext = getWebViewContextAndSetProvider();
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
Log.i(LOGTAG, "Loading " + sPackageInfo.packageName + " version " +
sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
Application initialApplication = AppGlobals.getInitialApplication();
Context webViewContext = null;
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "PackageManager.getApplicationInfo()");
try {
// Construct a package context to load the Java code into the current app.
// This is done as early as possible since by constructing a package context we
// register the WebView package as a dependency for the current application so that
// when the WebView package is updated this application will be killed.
ApplicationInfo applicationInfo =
initialApplication.getPackageManager().getApplicationInfo(
sPackageInfo.packageName, PackageManager.GET_SHARED_LIBRARY_FILES
| PackageManager.MATCH_DEBUG_TRIAGED_MISSING
// make sure that we fetch the current provider even if its not installed
// for the current user
| PackageManager.MATCH_UNINSTALLED_PACKAGES);
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW,
"initialApplication.createApplicationContext");
try {
webViewContext = initialApplication.createApplicationContext(applicationInfo,
Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
} catch (PackageManager.NameNotFoundException e) {
throw new MissingWebViewPackageException(e);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_WEBVIEW);
}
sPackageInfo.versionName + " (code " + sPackageInfo.versionCode + ")");
Trace.traceBegin(Trace.TRACE_TAG_WEBVIEW, "WebViewFactory.getChromiumProviderClass()");
try {