summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authornick <nick@chromium.org>2014-11-13 20:11:49 -0800
committerCommit bot <commit-bot@chromium.org>2014-11-14 04:13:06 +0000
commit4c8dfd482ad484f2402a5d9bea921870f3ea5d89 (patch)
tree539588cc49bc86d99d61391c3ac468c41eb55bda
parentf0ba8deca9141fc42e0a9746395f6843541bcde0 (diff)
downloadchromium_src-4c8dfd482ad484f2402a5d9bea921870f3ea5d89.zip
chromium_src-4c8dfd482ad484f2402a5d9bea921870f3ea5d89.tar.gz
chromium_src-4c8dfd482ad484f2402a5d9bea921870f3ea5d89.tar.bz2
IPC: a way for security exploit browsertests to simulate the appearance of a malicious IPC.
Use this to add a SecurityExploitBrowserTest for http://crbug.com/429922 Fix SecurityExploitBrowserTests on Android. Re-enable SecurityExploitBrowserTests on Android, except for two issues (1) the new test, which is actually disabled because of http://crbug.com/432737, discovered while developing this CL and (2) SetWebUIProperty, which is disabled because of http://crbug.com/433068, also discovered while developing this CL. Moral of the story being: never try. BUG=429922,432737,338023,433068 TEST=content_browsertests Review URL: https://codereview.chromium.org/712713002 Cr-Commit-Position: refs/heads/master@{#304170}
-rw-r--r--build/android/pylib/gtest/filter/content_browsertests_disabled9
-rw-r--r--content/browser/security_exploit_browsertest.cc127
-rw-r--r--ipc/ipc.gyp2
-rw-r--r--ipc/ipc_channel_proxy.h4
-rw-r--r--ipc/ipc_security_test_util.cc25
-rw-r--r--ipc/ipc_security_test_util.h40
6 files changed, 190 insertions, 17 deletions
diff --git a/build/android/pylib/gtest/filter/content_browsertests_disabled b/build/android/pylib/gtest/filter/content_browsertests_disabled
index 748f680..0314f5f 100644
--- a/build/android/pylib/gtest/filter/content_browsertests_disabled
+++ b/build/android/pylib/gtest/filter/content_browsertests_disabled
@@ -23,11 +23,6 @@ GpuPixelBrowserTest.*
FileSystemBrowserTestWithLowQuota.QuotaTest
ChildProcessSecurityPolicyInProcessBrowserTest.NoLeak
-# Needs to start the test server before
-# BrowserTestBase::SetUpCommandLine is called, but on Android
-# the test server needs to be started in SetUpOnMainThread().
-SecurityExploitBrowserTest.SetWebUIProperty
-
# Plugins are not supported.
BrowserPluginThreadedCompositorPixelTest.*
BrowserPluginHostTest.*
@@ -108,10 +103,6 @@ TouchActionBrowserTest.*
# http://crbug.com/338408
TracingControllerTest.EnableCaptureAndDisableMonitoring
-# http://crbug.com/338023
-SecurityExploitBrowserTest.AttemptDuplicateRenderViewHost
-SecurityExploitBrowserTest.AttemptDuplicateRenderWidgetHost
-
# http://crbug.com/338411
FrameTreeBrowserTest.NavigateWithLeftoverFrames
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 2a1be07..68430ad 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -4,14 +4,18 @@
#include "base/command_line.h"
#include "base/containers/hash_tables.h"
+#include "base/strings/utf_string_conversions.h"
#include "content/browser/dom_storage/dom_storage_context_wrapper.h"
#include "content/browser/dom_storage/session_storage_namespace_impl.h"
#include "content/browser/frame_host/navigator.h"
#include "content/browser/renderer_host/render_view_host_factory.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
+#include "content/common/frame_messages.h"
#include "content/common/view_messages.h"
#include "content/public/browser/browser_context.h"
+#include "content/public/browser/interstitial_page.h"
+#include "content/public/browser/interstitial_page_delegate.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/content_switches.h"
#include "content/public/test/browser_test_utils.h"
@@ -19,6 +23,11 @@
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "content/shell/browser/shell.h"
+#include "ipc/ipc_security_test_util.h"
+#include "net/dns/mock_host_resolver.h"
+#include "net/test/embedded_test_server/embedded_test_server.h"
+
+using IPC::IpcSecurityTestUtil;
namespace content {
@@ -35,10 +44,11 @@ namespace {
// the attempt is the return value.
RenderViewHostImpl* PrepareToDuplicateHosts(Shell* shell,
int* target_routing_id) {
- GURL foo("http://foo.com/files/simple_page.html");
+ GURL foo("http://foo.com/simple_page.html");
// Start off with initial navigation, so we get the first process allocated.
NavigateToURL(shell, foo);
+ EXPECT_EQ(base::ASCIIToUTF16("OK"), shell->web_contents()->GetTitle());
// Open another window, so we generate some more routing ids.
ShellAddedObserver shell2_observer;
@@ -55,7 +65,7 @@ RenderViewHostImpl* PrepareToDuplicateHosts(Shell* shell,
shell->web_contents()->GetRenderViewHost()->GetRoutingID());
// Now, simulate a link click coming from the renderer.
- GURL extension_url("https://bar.com/files/simple_page.html");
+ GURL extension_url("https://bar.com/simple_page.html");
WebContentsImpl* wc = static_cast<WebContentsImpl*>(shell->web_contents());
wc->GetFrameTree()->root()->navigator()->RequestOpenURL(
wc->GetFrameTree()->root()->current_frame_host(), extension_url,
@@ -84,25 +94,37 @@ RenderViewHostImpl* PrepareToDuplicateHosts(Shell* shell,
class SecurityExploitBrowserTest : public ContentBrowserTest {
public:
SecurityExploitBrowserTest() {}
+
void SetUpCommandLine(CommandLine* command_line) override {
- ASSERT_TRUE(test_server()->Start());
+ ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
// Add a host resolver rule to map all outgoing requests to the test server.
// This allows us to use "real" hostnames in URLs, which we can use to
// create arbitrary SiteInstances.
command_line->AppendSwitchASCII(
switches::kHostResolverRules,
- "MAP * " + test_server()->host_port_pair().ToString() +
+ "MAP * " +
+ net::HostPortPair::FromURL(embedded_test_server()->base_url())
+ .ToString() +
",EXCLUDE localhost");
}
};
+// Fails because the process kill on Android does not succeed.
+// http://crbug.com/433068
+#if defined(OS_ANDROID)
+#define MAYBE_SetWebUIProperty DISABLED_SetWebUIProperty
+#else
+#define MAYBE_SetWebUIProperty SetWebUIProperty
+#endif
+
// Ensure that we kill the renderer process if we try to give it WebUI
// properties and it doesn't have enabled WebUI bindings.
-IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, SetWebUIProperty) {
- GURL foo("http://foo.com/files/simple_page.html");
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest, MAYBE_SetWebUIProperty) {
+ GURL foo("http://foo.com/simple_page.html");
NavigateToURL(shell(), foo);
+ EXPECT_EQ(base::ASCIIToUTF16("OK"), shell()->web_contents()->GetTitle());
EXPECT_EQ(0,
shell()->web_contents()->GetRenderViewHost()->GetEnabledBindings());
@@ -166,4 +188,97 @@ IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
// If the above operation doesn't crash, the test has succeeded!
}
+class SecurityExploitTestInterstitialPage : public InterstitialPageDelegate {
+ public:
+ explicit SecurityExploitTestInterstitialPage(WebContents* contents) {
+ InterstitialPage* interstitial = InterstitialPage::Create(
+ contents, true, contents->GetLastCommittedURL(), this);
+ interstitial->Show();
+ }
+
+ // InterstitialPageDelegate implementation.
+ void CommandReceived(const std::string& command) override {
+ last_command_ = command;
+ }
+
+ std::string GetHTMLContents() override {
+ return "<html><head><script>"
+ "window.domAutomationController.setAutomationId(1);"
+ "window.domAutomationController.send(\"okay\");"
+ "</script></head>"
+ "<body>this page is an interstitial</body></html>";
+ }
+
+ std::string last_command() { return last_command_; }
+
+ private:
+ std::string last_command_;
+ DISALLOW_COPY_AND_ASSIGN(SecurityExploitTestInterstitialPage);
+};
+
+// Fails due to InterstitialPage's reliance on PostNonNestableTask
+// http://crbug.com/432737
+#if defined(OS_ANDROID)
+#define MAYBE_InterstitialCommandFromUnderlyingContent \
+ DISABLED_InterstitialCommandFromUnderlyingContent
+#else
+#define MAYBE_InterstitialCommandFromUnderlyingContent \
+ InterstitialCommandFromUnderlyingContent
+#endif
+
+// The interstitial should not be controllable by the underlying content.
+IN_PROC_BROWSER_TEST_F(SecurityExploitBrowserTest,
+ MAYBE_InterstitialCommandFromUnderlyingContent) {
+ // Start off with initial navigation, to allocate the process.
+ GURL foo("http://foo.com/simple_page.html");
+ NavigateToURL(shell(), foo);
+ EXPECT_EQ(base::ASCIIToUTF16("OK"), shell()->web_contents()->GetTitle());
+
+ DOMMessageQueue message_queue;
+
+ // Install and show an interstitial page.
+ SecurityExploitTestInterstitialPage* interstitial =
+ new SecurityExploitTestInterstitialPage(shell()->web_contents());
+
+ ASSERT_EQ("", interstitial->last_command());
+ content::WaitForInterstitialAttach(shell()->web_contents());
+
+ InterstitialPage* interstitial_page =
+ shell()->web_contents()->GetInterstitialPage();
+ ASSERT_TRUE(interstitial_page != NULL);
+ ASSERT_TRUE(shell()->web_contents()->ShowingInterstitialPage());
+ ASSERT_TRUE(interstitial_page->GetDelegateForTesting() == interstitial);
+
+ // The interstitial page ought to be able to send a message.
+ std::string message;
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ ASSERT_EQ("\"okay\"", message);
+ ASSERT_EQ("\"okay\"", interstitial->last_command());
+
+ // Send an automation message from the underlying content and wait for it to
+ // be dispatched on this thread. This message should not be received by the
+ // interstitial.
+ content::RenderFrameHost* compromised_renderer =
+ shell()->web_contents()->GetMainFrame();
+ FrameHostMsg_DomOperationResponse evil(compromised_renderer->GetRoutingID(),
+ "evil", MSG_ROUTING_NONE);
+ IpcSecurityTestUtil::PwnMessageReceived(
+ compromised_renderer->GetProcess()->GetChannel(), evil);
+
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ ASSERT_EQ("evil", message)
+ << "Automation message should be received by WebContents.";
+ ASSERT_EQ("\"okay\"", interstitial->last_command())
+ << "Interstitial should not be affected.";
+
+ // Send a second message from the interstitial page, and make sure that the
+ // "evil" message doesn't arrive in the intervening period.
+ ASSERT_TRUE(content::ExecuteScript(
+ interstitial_page->GetRenderViewHostForTesting(),
+ "window.domAutomationController.send(\"okay2\");"));
+ ASSERT_TRUE(message_queue.WaitForMessage(&message));
+ ASSERT_EQ("\"okay2\"", message);
+ ASSERT_EQ("\"okay2\"", interstitial->last_command());
+}
+
} // namespace content
diff --git a/ipc/ipc.gyp b/ipc/ipc.gyp
index 28c112b..cd63b54 100644
--- a/ipc/ipc.gyp
+++ b/ipc/ipc.gyp
@@ -132,6 +132,8 @@
'ipc_multiprocess_test.h',
'ipc_perftest_support.cc',
'ipc_perftest_support.h',
+ 'ipc_security_test_util.cc',
+ 'ipc_security_test_util.h',
'ipc_test_base.cc',
'ipc_test_base.h',
'ipc_test_channel_listener.cc',
diff --git a/ipc/ipc_channel_proxy.h b/ipc/ipc_channel_proxy.h
index dda5fa5..9ef8bf3 100644
--- a/ipc/ipc_channel_proxy.h
+++ b/ipc/ipc_channel_proxy.h
@@ -177,7 +177,7 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe {
private:
friend class ChannelProxy;
- friend class SendCallbackHelper;
+ friend class IpcSecurityTestUtil;
// Create the Channel
void CreateChannel(scoped_ptr<ChannelFactory> factory);
@@ -225,7 +225,7 @@ class IPC_EXPORT ChannelProxy : public Sender, public base::NonThreadSafe {
Context* context() { return context_.get(); }
private:
- friend class SendCallbackHelper;
+ friend class IpcSecurityTestUtil;
// By maintaining this indirection (ref-counted) to our internal state, we
// can safely be destroyed while the background thread continues to do stuff
diff --git a/ipc/ipc_security_test_util.cc b/ipc/ipc_security_test_util.cc
new file mode 100644
index 0000000..4ae5a06
--- /dev/null
+++ b/ipc/ipc_security_test_util.cc
@@ -0,0 +1,25 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "ipc/ipc_security_test_util.h"
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/run_loop.h"
+#include "ipc/ipc_channel_proxy.h"
+
+namespace IPC {
+
+void IpcSecurityTestUtil::PwnMessageReceived(ChannelProxy* channel,
+ const IPC::Message& message) {
+ base::RunLoop run_loop;
+ base::Closure inject_message = base::Bind(
+ base::IgnoreResult(&IPC::ChannelProxy::Context::OnMessageReceived),
+ channel->context(), message);
+ channel->context()->ipc_task_runner()->PostTaskAndReply(
+ FROM_HERE, inject_message, run_loop.QuitClosure());
+ run_loop.Run();
+}
+
+} // namespace IPC
diff --git a/ipc/ipc_security_test_util.h b/ipc/ipc_security_test_util.h
new file mode 100644
index 0000000..1ec2555
--- /dev/null
+++ b/ipc/ipc_security_test_util.h
@@ -0,0 +1,40 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef IPC_IPC_SECURITY_TEST_UTIL_H_
+#define IPC_IPC_SECURITY_TEST_UTIL_H_
+
+#include "base/basictypes.h"
+
+namespace IPC {
+
+class ChannelProxy;
+class Message;
+
+class IpcSecurityTestUtil {
+ public:
+ // Enables testing of security exploit scenarios where a compromised child
+ // process can send a malicious message of an arbitrary type.
+ //
+ // This function will post the message to the IPC channel's thread, where it
+ // is offered to the channel's listeners. Afterwards, a reply task is posted
+ // back to the current thread. This function blocks until the reply task is
+ // received. For messages forwarded back to the current thread, we won't
+ // return until after the message has been handled here.
+ //
+ // Use this only for testing security bugs in a browsertest; other uses are
+ // likely perilous. Unit tests should be using IPC::TestSink which has an
+ // OnMessageReceived method you can call directly. Non-security browsertests
+ // should just exercise the child process's normal codepaths to send messages.
+ static void PwnMessageReceived(ChannelProxy* channel, const Message& message);
+
+ private:
+ IpcSecurityTestUtil(); // Not instantiable.
+
+ DISALLOW_COPY_AND_ASSIGN(IpcSecurityTestUtil);
+};
+
+} // namespace IPC
+
+#endif // IPC_IPC_SECURITY_TEST_UTIL_H_