diff options
| author | Huahui Wu <hwu@google.com> | 2010-12-08 15:24:55 -0800 |
|---|---|---|
| committer | Huahui Wu <hwu@google.com> | 2010-12-15 09:38:54 -0800 |
| commit | ad053cebc82cbdd7534fcdef095fe79396da3100 (patch) | |
| tree | 40cdde11666462c9f20427b149b9e7636eb9a357 /core/java | |
| parent | 3be80f2e6cf763fdfeb058d5a4ac8257cdc91135 (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.java | 50 | ||||
| -rw-r--r-- | core/java/android/webkit/Network.java | 4 | ||||
| -rw-r--r-- | core/java/android/webkit/SslCertLookupTable.java | 52 | ||||
| -rw-r--r-- | core/java/android/webkit/SslErrorHandler.java | 244 | ||||
| -rw-r--r-- | core/java/android/webkit/SslErrorHandlerImpl.java | 272 |
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); + } + } +} |
