summaryrefslogtreecommitdiff
path: root/core/java
diff options
context:
space:
mode:
authorHuahui Wu <hwu@google.com>2010-12-08 15:24:55 -0800
committerHuahui Wu <hwu@google.com>2010-12-15 09:38:54 -0800
commitad053cebc82cbdd7534fcdef095fe79396da3100 (patch)
tree40cdde11666462c9f20427b149b9e7636eb9a357 /core/java
parent3be80f2e6cf763fdfeb058d5a4ac8257cdc91135 (diff)
b/2864818 Prompt the SSL error dialog to user and proceed or cancel
the request. C++ side cl: https://android-git.corp.google.com/g/#change,84529 Change-Id: I1f4c69c6ddb92324a1ec3c193018e8d703454f56
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/webkit/BrowserFrame.java50
-rw-r--r--core/java/android/webkit/Network.java4
-rw-r--r--core/java/android/webkit/SslCertLookupTable.java52
-rw-r--r--core/java/android/webkit/SslErrorHandler.java244
-rw-r--r--core/java/android/webkit/SslErrorHandlerImpl.java272
5 files changed, 383 insertions, 239 deletions
diff --git a/core/java/android/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java
index 2c38c93ede6f..97bbd5aba7db 100644
--- a/core/java/android/webkit/BrowserFrame.java
+++ b/core/java/android/webkit/BrowserFrame.java
@@ -28,6 +28,8 @@ import android.net.Uri;
import android.net.WebAddress;
import android.net.http.ErrorStrings;
import android.net.http.SslCertificate;
+import android.net.http.SslError;
+import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.OpenableColumns;
@@ -44,11 +46,15 @@ import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.URLEncoder;
+import java.security.cert.X509Certificate;
import java.util.ArrayList;
+import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
+import org.apache.harmony.security.provider.cert.X509CertImpl;
+
class BrowserFrame extends Handler {
private static final String LOGTAG = "webkit";
@@ -1102,6 +1108,47 @@ class BrowserFrame extends Handler {
}
/**
+ * Called by JNI when the native HTTP(S) stack gets a invalid cert chain.
+ *
+ * We delegate the request to CallbackProxy, and route its response to
+ * {@link #nativeSslCertErrorProceed(int)} or
+ * {@link #nativeSslCertErrorCancel(int, int)}.
+ */
+ private void reportSslCertError(final int handle, final int cert_error, byte cert_der[]) {
+ final SslError ssl_error;
+ try {
+ X509Certificate cert = new X509CertImpl(cert_der);
+ ssl_error = new SslError(cert_error, cert);
+ } catch (IOException e) {
+ // Can't get the cert, not much to do.
+ Log.e(LOGTAG, "Can't get the certificate from WebKit, cancling");
+ nativeSslCertErrorCancel(handle, cert_error);
+ return;
+ }
+
+ SslErrorHandler handler = new SslErrorHandler() {
+
+ @Override
+ public void proceed() {
+ SslCertLookupTable.getInstance().Allow(ssl_error);
+ nativeSslCertErrorProceed(handle);
+ }
+
+ @Override
+ public void cancel() {
+ SslCertLookupTable.getInstance().Deny(ssl_error);
+ nativeSslCertErrorCancel(handle, cert_error);
+ }
+ };
+
+ if (SslCertLookupTable.getInstance().IsAllowed(ssl_error)) {
+ nativeSslCertErrorProceed(handle);
+ } else {
+ mCallbackProxy.onReceivedSslError(handler, ssl_error);
+ }
+ }
+
+ /**
* Called by JNI when the native HTTP stack needs to download a file.
*
* We delegate the request to CallbackProxy, which owns the current app's
@@ -1246,4 +1293,7 @@ class BrowserFrame extends Handler {
private native void nativeAuthenticationProceed(int handle, String username, String password);
private native void nativeAuthenticationCancel(int handle);
+
+ private native void nativeSslCertErrorProceed(int handle);
+ private native void nativeSslCertErrorCancel(int handle, int cert_error);
}
diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java
index 59bec24b5f0d..30bbb04b72d3 100644
--- a/core/java/android/webkit/Network.java
+++ b/core/java/android/webkit/Network.java
@@ -73,7 +73,7 @@ class Network {
* SSL error handler: takes care of synchronization of multiple async
* loaders with SSL-related problems.
*/
- private SslErrorHandler mSslErrorHandler;
+ private SslErrorHandlerImpl mSslErrorHandler;
/**
* HTTP authentication handler: takes care of synchronization of HTTP
@@ -157,7 +157,7 @@ class Network {
getName().equals(WebViewCore.THREAD_NAME));
}
mContext = context;
- mSslErrorHandler = new SslErrorHandler();
+ mSslErrorHandler = new SslErrorHandlerImpl();
mHttpAuthHandler = new HttpAuthHandlerImpl(this);
mRequestQueue = new RequestQueue(context);
diff --git a/core/java/android/webkit/SslCertLookupTable.java b/core/java/android/webkit/SslCertLookupTable.java
new file mode 100644
index 000000000000..abf612ef866b
--- /dev/null
+++ b/core/java/android/webkit/SslCertLookupTable.java
@@ -0,0 +1,52 @@
+/*
+ * 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.
+ */
+
+package android.webkit;
+
+import android.os.Bundle;
+import android.net.http.SslError;
+
+/*
+ * A simple class to store the wrong certificates that user is aware but
+ * chose to proceed.
+ */
+class SslCertLookupTable {
+ private static SslCertLookupTable sTable;
+ private final Bundle table;
+
+ public static synchronized SslCertLookupTable getInstance() {
+ if (sTable == null) {
+ sTable = new SslCertLookupTable();
+ }
+ return sTable;
+ }
+
+ private SslCertLookupTable() {
+ table = new Bundle();
+ }
+
+ public void Allow(SslError ssl_error) {
+ table.putBoolean(ssl_error.toString(), true);
+ }
+
+ public void Deny(SslError ssl_error) {
+ table.putBoolean(ssl_error.toString(), false);
+ }
+
+ public boolean IsAllowed(SslError ssl_error) {
+ return table.getBoolean(ssl_error.toString());
+ }
+}
diff --git a/core/java/android/webkit/SslErrorHandler.java b/core/java/android/webkit/SslErrorHandler.java
index 1b0afaf576a7..426145ae7ca5 100644
--- a/core/java/android/webkit/SslErrorHandler.java
+++ b/core/java/android/webkit/SslErrorHandler.java
@@ -16,258 +16,28 @@
package android.webkit;
-import junit.framework.Assert;
-
-import android.net.http.SslError;
-import android.os.Bundle;
import android.os.Handler;
-import android.os.Message;
-import android.util.Log;
-
-import java.util.LinkedList;
-import java.util.ListIterator;
/**
- * SslErrorHandler: class responsible for handling SSL errors. This class is
- * passed as a parameter to BrowserCallback.displaySslErrorDialog and is meant
- * to receive the user's response.
+ * SslErrorHandler: class responsible for handling SSL errors.
+ * This class is passed as a parameter to BrowserCallback.displaySslErrorDialog
+ * and is meant to receive the user's response.
*/
public class SslErrorHandler extends Handler {
- /* One problem here is that there may potentially be multiple SSL errors
- * coming from mutiple loaders. Therefore, we keep a queue of loaders
- * that have SSL-related problems and process errors one by one in the
- * order they were received.
- */
-
- private static final String LOGTAG = "network";
-
- /**
- * Queue of loaders that experience SSL-related problems.
- */
- private LinkedList<LoadListener> mLoaderQueue;
-
- /**
- * SSL error preference table.
- */
- private Bundle mSslPrefTable;
-
- // These are only used in the client facing SslErrorHandler.
- private final SslErrorHandler mOriginHandler;
- private final LoadListener mLoadListener;
-
- // Message id for handling the response
- private static final int HANDLE_RESPONSE = 100;
-
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case HANDLE_RESPONSE:
- LoadListener loader = (LoadListener) msg.obj;
- synchronized (SslErrorHandler.this) {
- handleSslErrorResponse(loader, loader.sslError(),
- msg.arg1 == 1);
- mLoaderQueue.remove(loader);
- fastProcessQueuedSslErrors();
- }
- break;
- }
- }
-
- /**
- * Creates a new error handler with an empty loader queue.
- */
- /* package */ SslErrorHandler() {
- mLoaderQueue = new LinkedList<LoadListener>();
- mSslPrefTable = new Bundle();
-
- // These are used by client facing SslErrorHandlers.
- mOriginHandler = null;
- mLoadListener = null;
- }
-
- /**
- * Create a new error handler that will be passed to the client.
- */
- private SslErrorHandler(SslErrorHandler origin, LoadListener listener) {
- mOriginHandler = origin;
- mLoadListener = listener;
- }
-
- /**
- * Saves this handler's state into a map.
- * @return True iff succeeds.
- */
- /* package */ synchronized boolean saveState(Bundle outState) {
- boolean success = (outState != null);
- if (success) {
- // TODO?
- outState.putBundle("ssl-error-handler", mSslPrefTable);
- }
-
- return success;
- }
-
- /**
- * Restores this handler's state from a map.
- * @return True iff succeeds.
- */
- /* package */ synchronized boolean restoreState(Bundle inState) {
- boolean success = (inState != null);
- if (success) {
- success = inState.containsKey("ssl-error-handler");
- if (success) {
- mSslPrefTable = inState.getBundle("ssl-error-handler");
- }
- }
-
- return success;
- }
-
- /**
- * Clears SSL error preference table.
- */
- /* package */ synchronized void clear() {
- mSslPrefTable.clear();
- }
-
- /**
- * Handles SSL error(s) on the way up to the user.
- */
- /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
- if (DebugFlags.SSL_ERROR_HANDLER) {
- Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
- "url=" + loader.url());
- }
-
- if (!loader.cancelled()) {
- mLoaderQueue.offer(loader);
- if (loader == mLoaderQueue.peek()) {
- fastProcessQueuedSslErrors();
- }
- }
- }
/**
- * Check the preference table for a ssl error that has already been shown
- * to the user.
+ * Package-private constructor needed for API compatibility.
*/
- /* package */ synchronized boolean checkSslPrefTable(LoadListener loader,
- SslError error) {
- final String host = loader.host();
- final int primary = error.getPrimaryError();
-
- if (DebugFlags.SSL_ERROR_HANDLER) {
- Assert.assertTrue(host != null && primary != 0);
- }
-
- if (mSslPrefTable.containsKey(host)) {
- if (primary <= mSslPrefTable.getInt(host)) {
- handleSslErrorResponse(loader, error, true);
- return true;
- }
- }
- return false;
- }
-
- /**
- * Processes queued SSL-error confirmation requests in
- * a tight loop while there is no need to ask the user.
- */
- /* package */void fastProcessQueuedSslErrors() {
- while (processNextLoader());
- }
-
- /**
- * Processes the next loader in the queue.
- * @return True iff should proceed to processing the
- * following loader in the queue
- */
- private synchronized boolean processNextLoader() {
- LoadListener loader = mLoaderQueue.peek();
- if (loader != null) {
- // if this loader has been cancelled
- if (loader.cancelled()) {
- // go to the following loader in the queue. Make sure this
- // loader has been removed from the queue.
- mLoaderQueue.remove(loader);
- return true;
- }
-
- SslError error = loader.sslError();
-
- if (DebugFlags.SSL_ERROR_HANDLER) {
- Assert.assertNotNull(error);
- }
-
- // checkSslPrefTable will handle the ssl error response if the
- // answer is available. It does not remove the loader from the
- // queue.
- if (checkSslPrefTable(loader, error)) {
- mLoaderQueue.remove(loader);
- return true;
- }
-
- // if we do not have information on record, ask
- // the user (display a dialog)
- CallbackProxy proxy = loader.getFrame().getCallbackProxy();
- proxy.onReceivedSslError(new SslErrorHandler(this, loader), error);
- }
-
- // the queue must be empty, stop
- return false;
- }
+ SslErrorHandler() {}
/**
* Proceed with the SSL certificate.
*/
- public void proceed() {
- mOriginHandler.sendMessage(
- mOriginHandler.obtainMessage(
- HANDLE_RESPONSE, 1, 0, mLoadListener));
- }
+ public void proceed() {}
/**
* Cancel this request and all pending requests for the WebView that had
* the error.
*/
- public void cancel() {
- mOriginHandler.sendMessage(
- mOriginHandler.obtainMessage(
- HANDLE_RESPONSE, 0, 0, mLoadListener));
- }
-
- /**
- * Handles SSL error(s) on the way down from the user.
- */
- /* package */ synchronized void handleSslErrorResponse(LoadListener loader,
- SslError error, boolean proceed) {
- if (DebugFlags.SSL_ERROR_HANDLER) {
- Assert.assertNotNull(loader);
- Assert.assertNotNull(error);
- }
-
- if (DebugFlags.SSL_ERROR_HANDLER) {
- Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
- + " proceed: " + proceed
- + " url:" + loader.url());
- }
-
- if (!loader.cancelled()) {
- if (proceed) {
- // update the user's SSL error preference table
- int primary = error.getPrimaryError();
- String host = loader.host();
-
- if (DebugFlags.SSL_ERROR_HANDLER) {
- Assert.assertTrue(host != null && primary != 0);
- }
- boolean hasKey = mSslPrefTable.containsKey(host);
- if (!hasKey ||
- primary > mSslPrefTable.getInt(host)) {
- mSslPrefTable.putInt(host, primary);
- }
- }
- loader.handleSslErrorResponse(proceed);
- }
- }
+ public void cancel() {}
}
diff --git a/core/java/android/webkit/SslErrorHandlerImpl.java b/core/java/android/webkit/SslErrorHandlerImpl.java
new file mode 100644
index 000000000000..e029e372c141
--- /dev/null
+++ b/core/java/android/webkit/SslErrorHandlerImpl.java
@@ -0,0 +1,272 @@
+/*
+ * 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.
+ */
+
+package android.webkit;
+
+import junit.framework.Assert;
+
+import android.net.http.SslError;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.util.Log;
+
+import java.util.LinkedList;
+import java.util.ListIterator;
+
+/**
+ * SslErrorHandler's implementation for Android Java HTTP stack.
+ * This class is not needed if the Chromium HTTP stack is used.
+ */
+class SslErrorHandlerImpl extends SslErrorHandler {
+ /* One problem here is that there may potentially be multiple SSL errors
+ * coming from multiple loaders. Therefore, we keep a queue of loaders
+ * that have SSL-related problems and process errors one by one in the
+ * order they were received.
+ */
+
+ private static final String LOGTAG = "network";
+
+ /**
+ * Queue of loaders that experience SSL-related problems.
+ */
+ private LinkedList<LoadListener> mLoaderQueue;
+
+ /**
+ * SSL error preference table.
+ */
+ private Bundle mSslPrefTable;
+
+ // These are only used in the client facing SslErrorHandler.
+ private final SslErrorHandler mOriginHandler;
+ private final LoadListener mLoadListener;
+
+ // Message id for handling the response
+ private static final int HANDLE_RESPONSE = 100;
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case HANDLE_RESPONSE:
+ LoadListener loader = (LoadListener) msg.obj;
+ synchronized (SslErrorHandlerImpl.this) {
+ handleSslErrorResponse(loader, loader.sslError(),
+ msg.arg1 == 1);
+ mLoaderQueue.remove(loader);
+ fastProcessQueuedSslErrors();
+ }
+ break;
+ }
+ }
+
+ /**
+ * Creates a new error handler with an empty loader queue.
+ */
+ /* package */ SslErrorHandlerImpl() {
+ mLoaderQueue = new LinkedList<LoadListener>();
+ mSslPrefTable = new Bundle();
+
+ // These are used by client facing SslErrorHandlers.
+ mOriginHandler = null;
+ mLoadListener = null;
+ }
+
+ /**
+ * Create a new error handler that will be passed to the client.
+ */
+ private SslErrorHandlerImpl(SslErrorHandler origin, LoadListener listener) {
+ mOriginHandler = origin;
+ mLoadListener = listener;
+ }
+
+ /**
+ * Saves this handler's state into a map.
+ * @return True iff succeeds.
+ */
+ /* package */ synchronized boolean saveState(Bundle outState) {
+ boolean success = (outState != null);
+ if (success) {
+ // TODO?
+ outState.putBundle("ssl-error-handler", mSslPrefTable);
+ }
+
+ return success;
+ }
+
+ /**
+ * Restores this handler's state from a map.
+ * @return True iff succeeds.
+ */
+ /* package */ synchronized boolean restoreState(Bundle inState) {
+ boolean success = (inState != null);
+ if (success) {
+ success = inState.containsKey("ssl-error-handler");
+ if (success) {
+ mSslPrefTable = inState.getBundle("ssl-error-handler");
+ }
+ }
+
+ return success;
+ }
+
+ /**
+ * Clears SSL error preference table.
+ */
+ /* package */ synchronized void clear() {
+ mSslPrefTable.clear();
+ }
+
+ /**
+ * Handles SSL error(s) on the way up to the user.
+ */
+ /* package */ synchronized void handleSslErrorRequest(LoadListener loader) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
+ Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " +
+ "url=" + loader.url());
+ }
+
+ if (!loader.cancelled()) {
+ mLoaderQueue.offer(loader);
+ if (loader == mLoaderQueue.peek()) {
+ fastProcessQueuedSslErrors();
+ }
+ }
+ }
+
+ /**
+ * Check the preference table for a ssl error that has already been shown
+ * to the user.
+ */
+ /* package */ synchronized boolean checkSslPrefTable(LoadListener loader,
+ SslError error) {
+ final String host = loader.host();
+ final int primary = error.getPrimaryError();
+
+ if (DebugFlags.SSL_ERROR_HANDLER) {
+ Assert.assertTrue(host != null && primary != 0);
+ }
+
+ if (mSslPrefTable.containsKey(host)) {
+ if (primary <= mSslPrefTable.getInt(host)) {
+ handleSslErrorResponse(loader, error, true);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Processes queued SSL-error confirmation requests in
+ * a tight loop while there is no need to ask the user.
+ */
+ /* package */void fastProcessQueuedSslErrors() {
+ while (processNextLoader());
+ }
+
+ /**
+ * Processes the next loader in the queue.
+ * @return True iff should proceed to processing the
+ * following loader in the queue
+ */
+ private synchronized boolean processNextLoader() {
+ LoadListener loader = mLoaderQueue.peek();
+ if (loader != null) {
+ // if this loader has been cancelled
+ if (loader.cancelled()) {
+ // go to the following loader in the queue. Make sure this
+ // loader has been removed from the queue.
+ mLoaderQueue.remove(loader);
+ return true;
+ }
+
+ SslError error = loader.sslError();
+
+ if (DebugFlags.SSL_ERROR_HANDLER) {
+ Assert.assertNotNull(error);
+ }
+
+ // checkSslPrefTable will handle the ssl error response if the
+ // answer is available. It does not remove the loader from the
+ // queue.
+ if (checkSslPrefTable(loader, error)) {
+ mLoaderQueue.remove(loader);
+ return true;
+ }
+
+ // if we do not have information on record, ask
+ // the user (display a dialog)
+ CallbackProxy proxy = loader.getFrame().getCallbackProxy();
+ proxy.onReceivedSslError(new SslErrorHandlerImpl(this, loader), error);
+ }
+
+ // the queue must be empty, stop
+ return false;
+ }
+
+ /**
+ * Proceed with the SSL certificate.
+ */
+ public void proceed() {
+ mOriginHandler.sendMessage(
+ mOriginHandler.obtainMessage(
+ HANDLE_RESPONSE, 1, 0, mLoadListener));
+ }
+
+ /**
+ * Cancel this request and all pending requests for the WebView that had
+ * the error.
+ */
+ public void cancel() {
+ mOriginHandler.sendMessage(
+ mOriginHandler.obtainMessage(
+ HANDLE_RESPONSE, 0, 0, mLoadListener));
+ }
+
+ /**
+ * Handles SSL error(s) on the way down from the user.
+ */
+ /* package */ synchronized void handleSslErrorResponse(LoadListener loader,
+ SslError error, boolean proceed) {
+ if (DebugFlags.SSL_ERROR_HANDLER) {
+ Assert.assertNotNull(loader);
+ Assert.assertNotNull(error);
+ }
+
+ if (DebugFlags.SSL_ERROR_HANDLER) {
+ Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():"
+ + " proceed: " + proceed
+ + " url:" + loader.url());
+ }
+
+ if (!loader.cancelled()) {
+ if (proceed) {
+ // update the user's SSL error preference table
+ int primary = error.getPrimaryError();
+ String host = loader.host();
+
+ if (DebugFlags.SSL_ERROR_HANDLER) {
+ Assert.assertTrue(host != null && primary != 0);
+ }
+ boolean hasKey = mSslPrefTable.containsKey(host);
+ if (!hasKey ||
+ primary > mSslPrefTable.getInt(host)) {
+ mSslPrefTable.putInt(host, primary);
+ }
+ }
+ loader.handleSslErrorResponse(proceed);
+ }
+ }
+}