diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-10-13 11:50:13 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2016-10-13 12:01:23 +0000 |
commit | 45d4f70ca6ad97e4b860fb97de44e7329cf02633 (patch) | |
tree | 3dd32935d81d1458ba42a4b621e09dace19e9cce | |
parent | 7a27704b9b66b68d74f5fb42af921325bc758f8f (diff) |
[Backport] Merges six security fixes to M54, related to blobs.
Merge patch created pair programming style with creis@ and nick@.
Several manual fixups were required to get the tests passing on M54.
BUG=644966,646278,652784
TEST=Manual testing included:
- Verifying exploit steps w/ chrome w/ --isolate-extensions
- content_browsertests and content_unittests
- The following browser_tests subsets, both w/ and w/o --isolate-extensions:
*ProcessManager*
*Grants*
*Exploit*
*TouchFocuses*
NOPRESUBMIT=true
NOTRY=true
TBR=nick@chromium.org
The following six fixes are included in this diff:
1. https://codereview.chromium.org/2322673005:
> Fix process transfers for blob urls of sites requiring dedicated processes
>
> RenderFrameHostManager::IsRendererTransferNeededForNavigation had a bug
> where it passed an effective url, instead of an effective SITE url, to
> a function that was expecting the latter.
>
> Add a test that exercises this case. Add a CHECK to content shell browser
> client to verify that we're actually getting site urls all the time.
>
> Committed: https://crrev.com/db193a1b105de523fd0bb089c9769a71ed287d9e
> Cr-Commit-Position: refs/heads/master@{#417752}
2. https://codereview.chromium.org/2331063002:
> Fix IsolateIcelandFrameTreeBrowserTest.ProcessSwitchForIsolatedBlob so
> that it's not flaky under --site-per-process.
>
> Committed: https://crrev.com/07fd7e19e0095aeb30bd2c99109d083bb67732cb
> Cr-Commit-Position: refs/heads/master@{#417987}
3. https://codereview.chromium.org/2365433002:
> (re-land) Disallow navigations to blob URLs with non-canonical origins.
>
> Re-landing this with a fix for xhr-to-blob-in-isolated-world.html
>
> Review-Url: https://codereview.chromium.org/2365433002
> Cr-Commit-Position: refs/heads/master@{#420436}
4. https://codereview.chromium.org/2332263002
[partial merge, just for the helper function it added, used by later CLs]
> Updated suborigin serialization to latest spec proposal
>
> This modifiest the serialization format of suborigins so they are now
> represented in the form https-so://suboriginname.host.name (or,
> alternatively, with the scheme http-so). This change removes collisions
> with potentially valid URLs that were being deserialized as suborigins.
>
> Additionally, this adds suborigins back as an experimental web platform
> feature rather than a testing feature.
>
> Review-Url: https://codereview.chromium.org/2332263002
> Cr-Commit-Position: refs/heads/master@{#420828}
> CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_site_isolation
5. https://codereview.chromium.org/2364633004:
> Update ChildProcessSecurityPolicy so that the chrome-extension:// scheme
> is considered "web safe" to be requestable from any process, but only
> "web safe" to commit in extension processes.
>
> In ChildProcessSecurityPolicy::CanRequestURL and CanCommitURL, when
> seeing blob and filesystem urls, make a security decision based
> on the inner origin rather than the scheme.
>
> When the extensions ProcessManager (via ExtensionWebContentsObserver)
> notices a RenderFrame being created in an extension SiteInstance,
> grant that process permission to commit chrome-extension:// URLs.
>
> In BlobDispatcherHost, only allow creation of blob URLs from processes
> that would be able to commit them.
>
> Add a security exploit browsertest that verifies the above mechanisms
> working together.
>
> Committed: https://crrev.com/a411fd062bc68fc2b5fc3aca7e4cbb8e4a3e074e
> Committed: https://crrev.com/2a8ba8c4c186e5ea0a2ed938cc5d41441af64228
> Cr-Original-Commit-Position: refs/heads/master@{#421964}
> Cr-Commit-Position: refs/heads/master@{#422474}
6. https://codereview.chromium.org/2396533003:
> Allow <webview> to access URLs in the origin of the app embedding it.
>
> With r422474 creation of blob: URLs with origin of a chrome-extension://
> was locked down. However, the case of a <webview> loading an
> accessible_resource from its embedder and creating a blob: is disallowed.
> This CL adds permission for <webview> to create such URLs in the origin
> of its embedder.
>
> This CL is based on work by nick@chromium.org.
>
> Committed: https://crrev.com/5edda59b0b1cb8fff058b47567ac32e58be5168a
> Cr-Commit-Position: refs/heads/master@{#422976}
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_site_isolation
Change-Id: I89461b125fff72e19a05a5e676eba520e2a7938b
Review-Url: https://codereview.chromium.org/2399853003
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
14 files changed, 567 insertions, 52 deletions
diff --git a/chromium/content/browser/bad_message.h b/chromium/content/browser/bad_message.h index 1a8c80abb4e..f6568280466 100644 --- a/chromium/content/browser/bad_message.h +++ b/chromium/content/browser/bad_message.h @@ -127,6 +127,7 @@ enum BadMessageReason { DWNLD_INVALID_SAVABLE_RESOURCE_LINKS_RESPONSE = 103, DWNLD_INVALID_SERIALIZE_AS_MHTML_RESPONSE = 104, BDH_DEVICE_NOT_ALLOWED_FOR_ORIGIN = 105, + BDH_DISALLOWED_ORIGIN = 139, // Please add new elements here. The naming convention is abbreviated class // name (e.g. RenderFrameHost becomes RFH) plus a unique description of the diff --git a/chromium/content/browser/blob_storage/blob_url_browsertest.cc b/chromium/content/browser/blob_storage/blob_url_browsertest.cc new file mode 100644 index 00000000000..f25ee5b258f --- /dev/null +++ b/chromium/content/browser/blob_storage/blob_url_browsertest.cc @@ -0,0 +1,188 @@ +// Copyright 2016 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 "base/macros.h" +#include "base/strings/pattern.h" +#include "build/build_config.h" +#include "content/browser/web_contents/web_contents_impl.h" +#include "content/public/test/browser_test_utils.h" +#include "content/public/test/content_browser_test.h" +#include "content/public/test/content_browser_test_utils.h" +#include "content/public/test/test_utils.h" +#include "content/shell/browser/shell.h" +#include "content/test/content_browser_test_utils_internal.h" +#include "net/dns/mock_host_resolver.h" +#include "net/test/embedded_test_server/embedded_test_server.h" +#include "url/gurl.h" +#include "url/origin.h" + +namespace content { + +// Tests of the blob: URL scheme. +class BlobUrlBrowserTest : public ContentBrowserTest { + public: + BlobUrlBrowserTest() {} + + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + SetupCrossSiteRedirector(embedded_test_server()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(BlobUrlBrowserTest); +}; + +IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, LinkToUniqueOriginBlob) { + // Use a data URL to obtain a test page in a unique origin. The page + // contains a link to a "blob:null/SOME-GUID-STRING" URL. + NavigateToURL( + shell(), + GURL("data:text/html,<body><script>" + "var link = document.body.appendChild(document.createElement('a'));" + "link.innerText = 'Click Me!';" + "link.href = URL.createObjectURL(new Blob(['potato']));" + "link.target = '_blank';" + "link.id = 'click_me';" + "</script></body>")); + + // Click the link. + ShellAddedObserver new_shell_observer; + EXPECT_TRUE( + ExecuteScript(shell(), "document.getElementById('click_me').click()")); + + // The link should create a new tab. + Shell* new_shell = new_shell_observer.GetShell(); + WebContents* new_contents = new_shell->web_contents(); + WaitForLoadStop(new_contents); + + EXPECT_TRUE( + base::MatchPattern(new_contents->GetVisibleURL().spec(), "blob:null/*")); + std::string page_content; + EXPECT_TRUE(ExecuteScriptAndExtractString( + new_contents, + "domAutomationController.send(" + " document.origin + ' ' + document.body.innerText);", + &page_content)); + EXPECT_EQ("null potato", page_content); +} + +IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, LinkToSameOriginBlob) { + // Using an http page, click a link that opens a popup to a same-origin blob. + GURL url = embedded_test_server()->GetURL("chromium.org", "/title1.html"); + url::Origin origin(url); + NavigateToURL(shell(), url); + + ShellAddedObserver new_shell_observer; + EXPECT_TRUE(ExecuteScript( + shell(), + "var link = document.body.appendChild(document.createElement('a'));" + "link.innerText = 'Click Me!';" + "link.href = URL.createObjectURL(new Blob(['potato']));" + "link.target = '_blank';" + "link.click()")); + + // The link should create a new tab. + Shell* new_shell = new_shell_observer.GetShell(); + WebContents* new_contents = new_shell->web_contents(); + WaitForLoadStop(new_contents); + + EXPECT_TRUE(base::MatchPattern(new_contents->GetVisibleURL().spec(), + "blob:" + origin.Serialize() + "/*")); + std::string page_content; + EXPECT_TRUE(ExecuteScriptAndExtractString( + new_contents, + "domAutomationController.send(" + " document.origin + ' ' + document.body.innerText);", + &page_content)); + EXPECT_EQ(origin.Serialize() + " potato", page_content); +} + +// Regression test for https://crbug.com/646278 +IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, LinkToSameOriginBlobWithAuthority) { + // Using an http page, click a link that opens a popup to a same-origin blob + // that has a spoofy authority section applied. This should be blocked. + GURL url = embedded_test_server()->GetURL("chromium.org", "/title1.html"); + url::Origin origin(url); + NavigateToURL(shell(), url); + + ShellAddedObserver new_shell_observer; + EXPECT_TRUE(ExecuteScript( + shell(), + "var link = document.body.appendChild(document.createElement('a'));" + "link.innerText = 'Click Me!';" + "link.href = 'blob:http://spoof.com@' + " + " URL.createObjectURL(new Blob(['potato'])).split('://')[1];" + "link.target = '_blank';" + "link.click()")); + + // The link should create a new tab. + Shell* new_shell = new_shell_observer.GetShell(); + WebContents* new_contents = new_shell->web_contents(); + WaitForLoadStop(new_contents); + + // The spoofy URL should not be shown to the user. + EXPECT_FALSE( + base::MatchPattern(new_contents->GetVisibleURL().spec(), "*spoof*")); + // The currently implemented behavior is that the URL gets rewritten to + // about:blank. + EXPECT_EQ("about:blank", new_contents->GetVisibleURL().spec()); + std::string page_content; + EXPECT_TRUE(ExecuteScriptAndExtractString( + new_contents, + "domAutomationController.send(" + " document.origin + ' ' + document.body.innerText);", + &page_content)); + EXPECT_EQ(origin.Serialize() + " ", page_content); // no potato +} + +// Regression test for https://crbug.com/646278 +IN_PROC_BROWSER_TEST_F(BlobUrlBrowserTest, ReplaceStateToAddAuthorityToBlob) { + // history.replaceState from a validly loaded blob URL shouldn't allow adding + // an authority to the inner URL, which would be spoofy. + GURL url = embedded_test_server()->GetURL("chromium.org", "/title1.html"); + url::Origin origin(url); + NavigateToURL(shell(), url); + + ShellAddedObserver new_shell_observer; + EXPECT_TRUE(ExecuteScript( + shell(), + "var spoof_fn = function () {\n" + " host_port = document.origin.split('://')[1];\n" + " spoof_url = 'blob:http://spoof.com@' + host_port + '/abcd';\n" + " window.history.replaceState({}, '', spoof_url);\n" + "};\n" + "args = ['<body>potato<scr', 'ipt>(', spoof_fn, ')();</scri', 'pt>'];\n" + "b = new Blob(args, {type: 'text/html'});" + "window.open(URL.createObjectURL(b));")); + + Shell* new_shell = new_shell_observer.GetShell(); + WebContents* new_contents = new_shell->web_contents(); + WaitForLoadStop(new_contents); + + // The spoofy URL should not be shown to the user. + EXPECT_FALSE( + base::MatchPattern(new_contents->GetVisibleURL().spec(), "*spoof*")); + + // The currently implemented behavior is that the URL gets rewritten to + // about:blank. The content of the page stays the same. + EXPECT_EQ("about:blank", new_contents->GetVisibleURL().spec()); + std::string page_content; + EXPECT_TRUE(ExecuteScriptAndExtractString( + new_contents, + "domAutomationController.send(" + " document.origin + ' ' + document.body.innerText);", + &page_content)); + EXPECT_EQ(origin.Serialize() + " potato", page_content); + + // TODO(nick): Currently, window.location still reflects the spoof URL. + // This seems unfortunate -- can we fix it? + std::string window_location; + EXPECT_TRUE(ExecuteScriptAndExtractString( + new_contents, "domAutomationController.send(window.location.href);", + &window_location)); + EXPECT_TRUE(base::MatchPattern(window_location, "*spoof*")); +} + +} // namespace content diff --git a/chromium/content/browser/child_process_security_policy_impl.cc b/chromium/content/browser/child_process_security_policy_impl.cc index 639401fc731..9a0c26d5bd4 100644 --- a/chromium/content/browser/child_process_security_policy_impl.cc +++ b/chromium/content/browser/child_process_security_policy_impl.cc @@ -67,6 +67,31 @@ enum ChildProcessSecurityGrants { DELETE_FILE_GRANT = DELETE_FILE_PERMISSION, }; +// https://crbug.com/646278 Valid blob URLs should contain canonically +// serialized origins. +bool IsMalformedBlobUrl(const GURL& url) { + if (!url.SchemeIsBlob()) + return false; + + // If the part after blob: survives a roundtrip through url::Origin, then + // it's a normal blob URL. + std::string canonical_origin = url::Origin(url).Serialize(); + canonical_origin.append(1, '/'); + if (base::StartsWith(url.GetContent(), canonical_origin, + base::CompareCase::INSENSITIVE_ASCII)) + return false; + + // blob:blobinternal:// is used by blink for stream URLs. This doesn't survive + // url::Origin canonicalization -- blobinternal is a fake scheme -- but allow + // it anyway. TODO(nick): Added speculatively, might be unnecessary. + if (base::StartsWith(url.GetContent(), "blobinternal://", + base::CompareCase::INSENSITIVE_ASCII)) + return false; + + // This is a malformed blob URL. + return true; +} + } // namespace // The SecurityState class is used to maintain per-child process security state @@ -177,6 +202,8 @@ class ChildProcessSecurityPolicyImpl::SecurityState { // Determine whether permission has been granted to commit |url|. bool CanCommitURL(const GURL& url) { + DCHECK(!url.SchemeIsBlob() && !url.SchemeIsFileSystem()) + << "inner_url extraction should be done already."; // Having permission to a scheme implies permission to all of its URLs. SchemeMap::const_iterator scheme_judgment( scheme_policy_.find(url.scheme())); @@ -301,6 +328,10 @@ ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { RegisterWebSafeScheme(url::kFtpScheme); RegisterWebSafeScheme(url::kDataScheme); RegisterWebSafeScheme("feed"); + + // TODO(nick): https://crbug.com/651534 blob: and filesystem: schemes embed + // other origins, so we should not treat them as web safe. Remove callers of + // IsWebSafeScheme(), and then eliminate the next two lines. RegisterWebSafeScheme(url::kBlobScheme); RegisterWebSafeScheme(url::kFileSystemScheme); @@ -311,11 +342,6 @@ ChildProcessSecurityPolicyImpl::ChildProcessSecurityPolicyImpl() { } ChildProcessSecurityPolicyImpl::~ChildProcessSecurityPolicyImpl() { - web_safe_schemes_.clear(); - pseudo_schemes_.clear(); - STLDeleteContainerPairSecondPointers(security_state_.begin(), - security_state_.end()); - security_state_.clear(); } // static @@ -353,25 +379,43 @@ void ChildProcessSecurityPolicyImpl::Remove(int child_id) { void ChildProcessSecurityPolicyImpl::RegisterWebSafeScheme( const std::string& scheme) { base::AutoLock lock(lock_); - DCHECK_EQ(0U, web_safe_schemes_.count(scheme)) << "Add schemes at most once."; + DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) + << "Add schemes at most once."; DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) << "Web-safe implies not pseudo."; - web_safe_schemes_.insert(scheme); + schemes_okay_to_request_in_any_process_.insert(scheme); + schemes_okay_to_commit_in_any_process_.insert(scheme); +} + +void ChildProcessSecurityPolicyImpl::RegisterWebSafeIsolatedScheme( + const std::string& scheme, + bool always_allow_in_origin_headers) { + base::AutoLock lock(lock_); + DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) + << "Add schemes at most once."; + DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) + << "Web-safe implies not pseudo."; + + schemes_okay_to_request_in_any_process_.insert(scheme); + if (always_allow_in_origin_headers) + schemes_okay_to_appear_as_origin_headers_.insert(scheme); } bool ChildProcessSecurityPolicyImpl::IsWebSafeScheme( const std::string& scheme) { base::AutoLock lock(lock_); - return ContainsKey(web_safe_schemes_, scheme); + return ContainsKey(schemes_okay_to_request_in_any_process_, scheme); } void ChildProcessSecurityPolicyImpl::RegisterPseudoScheme( const std::string& scheme) { base::AutoLock lock(lock_); DCHECK_EQ(0U, pseudo_schemes_.count(scheme)) << "Add schemes at most once."; - DCHECK_EQ(0U, web_safe_schemes_.count(scheme)) + DCHECK_EQ(0U, schemes_okay_to_request_in_any_process_.count(scheme)) + << "Pseudo implies not web-safe."; + DCHECK_EQ(0U, schemes_okay_to_commit_in_any_process_.count(scheme)) << "Pseudo implies not web-safe."; pseudo_schemes_.insert(scheme); @@ -407,6 +451,10 @@ void ChildProcessSecurityPolicyImpl::GrantRequestURL( return; // Can't grant the capability to request pseudo schemes. } + if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { + return; // Don't grant blanket access to blob: or filesystem: schemes. + } + { base::AutoLock lock(lock_); SecurityStateMap::iterator state = security_state_.find(child_id); @@ -608,6 +656,20 @@ bool ChildProcessSecurityPolicyImpl::CanRequestURL( return false; } + // Blob and filesystem URLs require special treatment, since they embed an + // inner origin. + if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { + if (IsMalformedBlobUrl(url)) + return false; + + url::Origin origin(url); + return origin.unique() || IsWebSafeScheme(origin.scheme()) || + CanCommitURL(child_id, GURL(origin.Serialize())); + } + + if (IsWebSafeScheme(url.scheme())) + return true; + // If the process can commit the URL, it can request it. if (CanCommitURL(child_id, url)) return true; @@ -626,16 +688,33 @@ bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id, if (IsPseudoScheme(url.scheme())) return base::LowerCaseEqualsASCII(url.spec(), url::kAboutBlankURL); - // TODO(creis): Tighten this for Site Isolation, so that a URL from a site - // that is isolated can only be committed in a process dedicated to that site. - // CanRequestURL should still allow all web-safe schemes. See - // https://crbug.com/515309. - if (IsWebSafeScheme(url.scheme())) - return true; // The scheme has been white-listed for every child process. + // Blob and filesystem URLs require special treatment; validate the inner + // origin they embed. + if (url.SchemeIsBlob() || url.SchemeIsFileSystem()) { + if (IsMalformedBlobUrl(url)) + return false; + + url::Origin origin(url); + return origin.unique() || CanCommitURL(child_id, GURL(origin.Serialize())); + } { base::AutoLock lock(lock_); + // Most schemes can commit in any process. Note that we check + // schemes_okay_to_commit_in_any_process_ here, which is stricter than + // IsWebSafeScheme(). + // + // TODO(creis, nick): https://crbug.com/515309: in generalized Site + // Isolation and/or --site-per-process, there will be no such thing as a + // scheme that is okay to commit in any process. Instead, an URL from a site + // that is isolated may only be committed in a process dedicated to that + // site, so CanCommitURL will need to rely on explicit, per-process grants. + // Note how today, even with extension isolation, the line below does not + // enforce that http pages cannot commit in an extension process. + if (ContainsKey(schemes_okay_to_commit_in_any_process_, url.scheme())) + return true; + SecurityStateMap::iterator state = security_state_.find(child_id); if (state == security_state_.end()) return false; @@ -646,6 +725,28 @@ bool ChildProcessSecurityPolicyImpl::CanCommitURL(int child_id, } } +bool ChildProcessSecurityPolicyImpl::CanSetAsOriginHeader(int child_id, + const GURL& url) { + if (!url.is_valid()) + return false; // Can't set invalid URLs as origin headers. + + // If this process can commit |url|, it can use |url| as an origin for + // outbound requests. + if (CanCommitURL(child_id, url)) + return true; + + // Allow schemes which may come from scripts executing in isolated worlds; + // XHRs issued by such scripts reflect the script origin rather than the + // document origin. + { + base::AutoLock lock(lock_); + if (ContainsKey(schemes_okay_to_appear_as_origin_headers_, + url.scheme())) + return true; + } + return false; +} + bool ChildProcessSecurityPolicyImpl::CanReadFile(int child_id, const base::FilePath& file) { return HasPermissionsForFile(child_id, file, READ_FILE_GRANT); diff --git a/chromium/content/browser/child_process_security_policy_impl.h b/chromium/content/browser/child_process_security_policy_impl.h index 46d2d7a6e96..b0aebc259cd 100644 --- a/chromium/content/browser/child_process_security_policy_impl.h +++ b/chromium/content/browser/child_process_security_policy_impl.h @@ -42,6 +42,9 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl // ChildProcessSecurityPolicy implementation. void RegisterWebSafeScheme(const std::string& scheme) override; + void RegisterWebSafeIsolatedScheme( + const std::string& scheme, + bool always_allow_in_origin_headers) override; bool IsWebSafeScheme(const std::string& scheme) override; void GrantReadFile(int child_id, const base::FilePath& file) override; void GrantCreateReadWriteFile(int child_id, @@ -63,6 +66,8 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl const std::string& filesystem_id) override; void GrantOrigin(int child_id, const url::Origin& origin) override; void GrantScheme(int child_id, const std::string& scheme) override; + bool CanRequestURL(int child_id, const GURL& url) override; + bool CanCommitURL(int child_id, const GURL& url) override; bool CanReadFile(int child_id, const base::FilePath& file) override; bool CanCreateReadWriteFile(int child_id, const base::FilePath& file) override; @@ -123,16 +128,9 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl // Revoke read raw cookies permission. void RevokeReadRawCookies(int child_id); - // Before servicing a child process's request for a URL, the browser should - // call this method to determine whether the process has the capability to - // request the URL. - bool CanRequestURL(int child_id, const GURL& url); - - // Whether the process is allowed to commit a document from the given URL. - // This is more restrictive than CanRequestURL, since CanRequestURL allows - // requests that might lead to cross-process navigations or external protocol - // handlers. - bool CanCommitURL(int child_id, const GURL& url); + // Whether the given origin is valid for an origin header. Valid origin + // headers are commitable URLs. + bool CanSetAsOriginHeader(int child_id, const GURL& url); // Explicit permissions checks for FileSystemURL specified files. bool CanReadFileSystemFile(int child_id, const storage::FileSystemURL& url); @@ -227,9 +225,11 @@ class CONTENT_EXPORT ChildProcessSecurityPolicyImpl // class. You must not block while holding this lock. base::Lock lock_; - // These schemes are white-listed for all child processes. This set is - // protected by |lock_|. - SchemeSet web_safe_schemes_; + // These schemes are white-listed for all child processes in various contexts. + // These sets are protected by |lock_|. + SchemeSet schemes_okay_to_commit_in_any_process_; + SchemeSet schemes_okay_to_request_in_any_process_; + SchemeSet schemes_okay_to_appear_as_origin_headers_; // These schemes do not actually represent retrievable URLs. For example, // the the URLs in the "about" scheme are aliases to other URLs. This set is diff --git a/chromium/content/browser/child_process_security_policy_unittest.cc b/chromium/content/browser/child_process_security_policy_unittest.cc index 23604683ad3..2ff4ed1dfbb 100644 --- a/chromium/content/browser/child_process_security_policy_unittest.cc +++ b/chromium/content/browser/child_process_security_policy_unittest.cc @@ -174,8 +174,17 @@ TEST_F(ChildProcessSecurityPolicyTest, StandardSchemesTest) { GURL("view-source:http://www.google.com/"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("view-source:http://www.google.com/"))); + EXPECT_TRUE( + p->CanSetAsOriginHeader(kRendererID, GURL("http://www.google.com/"))); + EXPECT_TRUE( + p->CanSetAsOriginHeader(kRendererID, GURL("https://www.paypal.com/"))); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("ftp://ftp.gnu.org/"))); + EXPECT_TRUE( + p->CanSetAsOriginHeader(kRendererID, GURL("data:text/html,<b>Hi</b>"))); + EXPECT_TRUE(p->CanSetAsOriginHeader( + kRendererID, GURL("filesystem:http://localhost/temporary/a.gif"))); - // Dangerous to request or commit. + // Dangerous to request, commit, or set as origin header. EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, @@ -184,6 +193,63 @@ TEST_F(ChildProcessSecurityPolicyTest, StandardSchemesTest) { GURL("file:///etc/passwd"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("chrome://foo/bar"))); + EXPECT_FALSE( + p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("chrome://foo/bar"))); + EXPECT_FALSE(p->CanSetAsOriginHeader( + kRendererID, GURL("view-source:http://www.google.com/"))); + + p->Remove(kRendererID); +} + +TEST_F(ChildProcessSecurityPolicyTest, BlobSchemeTest) { + ChildProcessSecurityPolicyImpl* p = + ChildProcessSecurityPolicyImpl::GetInstance(); + + p->Add(kRendererID); + + EXPECT_TRUE( + p->CanRequestURL(kRendererID, GURL("blob:http://localhost/some-guid"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("blob:null/some-guid"))); + EXPECT_TRUE( + p->CanRequestURL(kRendererID, GURL("blob:http://localhost/some-guid"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("blob:NulL/some-guid"))); + EXPECT_TRUE( + p->CanRequestURL(kRendererID, GURL("blob:NulL/some-guid#fragment"))); + EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("blob:NulL/some-guid?query"))); + EXPECT_TRUE( + p->CanRequestURL(kRendererID, GURL("blob:blobinternal://some-guid"))); + EXPECT_FALSE(p->CanRequestURL( + kRendererID, GURL("blob:http://username@localhost/some-guid"))); + EXPECT_FALSE(p->CanRequestURL( + kRendererID, GURL("blob:http://username @localhost/some-guid"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("blob:blob:some-guid"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("blob:some-guid"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, + GURL("blob:filesystem:http://localhost/path"))); + EXPECT_FALSE(p->CanRequestURL(kRendererID, + GURL("filesystem:blob:http://localhost/guid"))); + + EXPECT_TRUE( + p->CanCommitURL(kRendererID, GURL("blob:http://localhost/some-guid"))); + EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("blob:null/some-guid"))); + EXPECT_TRUE( + p->CanCommitURL(kRendererID, GURL("blob:http://localhost/some-guid"))); + EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("blob:NulL/some-guid"))); + EXPECT_TRUE( + p->CanCommitURL(kRendererID, GURL("blob:NulL/some-guid#fragment"))); + EXPECT_TRUE( + p->CanCommitURL(kRendererID, GURL("blob:blobinternal://some-guid"))); + EXPECT_FALSE(p->CanCommitURL( + kRendererID, GURL("blob:http://username@localhost/some-guid"))); + EXPECT_FALSE(p->CanCommitURL( + kRendererID, GURL("blob:http://username @localhost/some-guid"))); + EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("blob:blob:some-guid"))); + EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("blob:some-guid"))); + EXPECT_FALSE(p->CanCommitURL(kRendererID, + GURL("blob:filesystem:http://localhost/path"))); + EXPECT_FALSE(p->CanCommitURL(kRendererID, + GURL("filesystem:blob:http://localhost/guid"))); p->Remove(kRendererID); } @@ -202,6 +268,10 @@ TEST_F(ChildProcessSecurityPolicyTest, AboutTest) { EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("about:BlAnK"))); EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("aBouT:BlAnK"))); EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("aBouT:blank"))); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("about:blank"))); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("about:BlAnK"))); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("aBouT:BlAnK"))); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("aBouT:blank"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:memory"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash"))); @@ -211,6 +281,9 @@ TEST_F(ChildProcessSecurityPolicyTest, AboutTest) { EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:crash"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:cache"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:hang"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:crash"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:cache"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:hang"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("aBoUt:memory"))); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:CrASh"))); @@ -218,17 +291,21 @@ TEST_F(ChildProcessSecurityPolicyTest, AboutTest) { EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("aBoUt:memory"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:CrASh"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("abOuT:cAChe"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:CrASh"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("abOuT:cAChe"))); // Requests for about: pages should be denied. p->GrantRequestURL(kRendererID, GURL("about:crash")); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("about:crash"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("about:crash"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("about:crash"))); // These requests for chrome:// pages should be granted. GURL chrome_url("chrome://foo"); p->GrantRequestURL(kRendererID, chrome_url); EXPECT_TRUE(p->CanRequestURL(kRendererID, chrome_url)); EXPECT_TRUE(p->CanCommitURL(kRendererID, chrome_url)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, chrome_url)); p->Remove(kRendererID); } @@ -241,9 +318,13 @@ TEST_F(ChildProcessSecurityPolicyTest, JavaScriptTest) { EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("javascript:alert('xss')"))); + EXPECT_FALSE( + p->CanSetAsOriginHeader(kRendererID, GURL("javascript:alert('xss')"))); p->GrantRequestURL(kRendererID, GURL("javascript:alert('xss')")); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("javascript:alert('xss')"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("javascript:alert('xss')"))); + EXPECT_FALSE( + p->CanSetAsOriginHeader(kRendererID, GURL("javascript:alert('xss')"))); p->Remove(kRendererID); } @@ -258,16 +339,19 @@ TEST_F(ChildProcessSecurityPolicyTest, RegisterWebSafeSchemeTest) { // requested but not committed. EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("asdf:rockers"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("asdf:rockers"))); // Once we register "asdf", we default to deny. RegisterTestScheme("asdf"); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("asdf:rockers"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("asdf:rockers"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, GURL("asdf:rockers"))); // We can allow new schemes by adding them to the whitelist. p->RegisterWebSafeScheme("asdf"); EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("asdf:rockers"))); EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("asdf:rockers"))); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("asdf:rockers"))); // Cleanup. p->Remove(kRendererID); @@ -281,15 +365,20 @@ TEST_F(ChildProcessSecurityPolicyTest, CanServiceCommandsTest) { EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("file:///etc/passwd"))); + EXPECT_FALSE( + p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd"))); p->GrantRequestURL(kRendererID, GURL("file:///etc/passwd")); EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); EXPECT_TRUE(p->CanCommitURL(kRendererID, GURL("file:///etc/passwd"))); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd"))); // We should forget our state if we repeat a renderer id. p->Remove(kRendererID); p->Add(kRendererID); EXPECT_FALSE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); EXPECT_FALSE(p->CanCommitURL(kRendererID, GURL("file:///etc/passwd"))); + EXPECT_FALSE( + p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd"))); p->Remove(kRendererID); } @@ -319,6 +408,16 @@ TEST_F(ChildProcessSecurityPolicyTest, ViewSource) { kRendererID, GURL("view-source:view-source:http://www.google.com/"))); + // View source URLs should not be setable as origin headers + EXPECT_FALSE(p->CanSetAsOriginHeader( + kRendererID, GURL("view-source:http://www.google.com/"))); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, + GURL("view-source:file:///etc/passwd"))); + EXPECT_FALSE( + p->CanSetAsOriginHeader(kRendererID, GURL("file:///etc/passwd"))); + EXPECT_FALSE(p->CanSetAsOriginHeader( + kRendererID, GURL("view-source:view-source:http://www.google.com/"))); + p->GrantRequestURL(kRendererID, GURL("view-source:file:///etc/passwd")); // View source needs to be able to request the embedded scheme. EXPECT_TRUE(p->CanRequestURL(kRendererID, GURL("file:///etc/passwd"))); @@ -343,18 +442,24 @@ TEST_F(ChildProcessSecurityPolicyTest, SpecificFile) { EXPECT_FALSE(p->CanRequestURL(kRendererID, sensitive_url)); EXPECT_FALSE(p->CanCommitURL(kRendererID, icon_url)); EXPECT_FALSE(p->CanCommitURL(kRendererID, sensitive_url)); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, icon_url)); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, sensitive_url)); p->GrantRequestSpecificFileURL(kRendererID, icon_url); EXPECT_TRUE(p->CanRequestURL(kRendererID, icon_url)); EXPECT_FALSE(p->CanRequestURL(kRendererID, sensitive_url)); EXPECT_TRUE(p->CanCommitURL(kRendererID, icon_url)); EXPECT_FALSE(p->CanCommitURL(kRendererID, sensitive_url)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, icon_url)); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, sensitive_url)); p->GrantRequestURL(kRendererID, icon_url); EXPECT_TRUE(p->CanRequestURL(kRendererID, icon_url)); EXPECT_TRUE(p->CanRequestURL(kRendererID, sensitive_url)); EXPECT_TRUE(p->CanCommitURL(kRendererID, icon_url)); EXPECT_TRUE(p->CanCommitURL(kRendererID, sensitive_url)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, icon_url)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, sensitive_url)); p->Remove(kRendererID); } @@ -715,6 +820,9 @@ TEST_F(ChildProcessSecurityPolicyTest, OriginGranting) { EXPECT_FALSE(p->CanCommitURL(kRendererID, url_foo1)); EXPECT_FALSE(p->CanCommitURL(kRendererID, url_foo2)); EXPECT_FALSE(p->CanCommitURL(kRendererID, url_bar)); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_foo1)); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_foo2)); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_bar)); p->GrantOrigin(kRendererID, url::Origin(url_foo1)); @@ -724,6 +832,9 @@ TEST_F(ChildProcessSecurityPolicyTest, OriginGranting) { EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo1)); EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo2)); EXPECT_FALSE(p->CanCommitURL(kRendererID, url_bar)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo1)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo2)); + EXPECT_FALSE(p->CanSetAsOriginHeader(kRendererID, url_bar)); p->GrantScheme(kRendererID, kChromeUIScheme); @@ -733,6 +844,9 @@ TEST_F(ChildProcessSecurityPolicyTest, OriginGranting) { EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo1)); EXPECT_TRUE(p->CanCommitURL(kRendererID, url_foo2)); EXPECT_TRUE(p->CanCommitURL(kRendererID, url_bar)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo1)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_foo2)); + EXPECT_TRUE(p->CanSetAsOriginHeader(kRendererID, url_bar)); p->Remove(kRendererID); } diff --git a/chromium/content/browser/frame_host/frame_tree_browsertest.cc b/chromium/content/browser/frame_host/frame_tree_browsertest.cc index 1ffdd0a21d2..5964f73395f 100644 --- a/chromium/content/browser/frame_host/frame_tree_browsertest.cc +++ b/chromium/content/browser/frame_host/frame_tree_browsertest.cc @@ -10,6 +10,7 @@ #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/browser/notification_service.h" #include "content/public/browser/notification_types.h" +#include "content/public/common/content_switches.h" #include "content/public/common/url_constants.h" #include "content/public/test/browser_test_utils.h" #include "content/public/test/content_browser_test.h" @@ -17,6 +18,7 @@ #include "content/public/test/test_navigation_observer.h" #include "content/public/test/test_utils.h" #include "content/shell/browser/shell.h" +#include "content/shell/common/shell_switches.h" #include "content/test/content_browser_test_utils_internal.h" #include "content/test/test_frame_navigation_observer.h" #include "net/dns/mock_host_resolver.h" @@ -31,6 +33,17 @@ namespace content { +namespace { + +std::string GetOriginFromRenderer(FrameTreeNode* node) { + std::string origin; + EXPECT_TRUE(ExecuteScriptAndExtractString( + node, "window.domAutomationController.send(document.origin);", &origin)); + return origin; +} + +} // namespace + class FrameTreeBrowserTest : public ContentBrowserTest { public: FrameTreeBrowserTest() {} @@ -635,4 +648,67 @@ IN_PROC_BROWSER_TEST_F(CrossProcessFrameTreeBrowserTest, EXPECT_EQ(root->child_at(1)->current_origin().Serialize(), "null"); } +// FrameTreeBrowserTest variant where we isolate http://*.is, Iceland's top +// level domain. This is an analogue to --isolate-extensions that we use inside +// of content_browsertests, where extensions don't exist. Iceland, like an +// extension process, is a special place with magical powers; we want to protect +// it from outsiders. +class IsolateIcelandFrameTreeBrowserTest : public ContentBrowserTest { + public: + IsolateIcelandFrameTreeBrowserTest() {} + + void SetUpCommandLine(base::CommandLine* command_line) override { + command_line->AppendSwitchASCII(switches::kIsolateSitesForTesting, "*.is"); + } + + void SetUpOnMainThread() override { + host_resolver()->AddRule("*", "127.0.0.1"); + ASSERT_TRUE(embedded_test_server()->Start()); + SetupCrossSiteRedirector(embedded_test_server()); + } + + private: + DISALLOW_COPY_AND_ASSIGN(IsolateIcelandFrameTreeBrowserTest); +}; + +// Regression test for https://crbug.com/644966 +IN_PROC_BROWSER_TEST_F(IsolateIcelandFrameTreeBrowserTest, + ProcessSwitchForIsolatedBlob) { + // blink suppresses navigations to blob URLs of origins different from the + // frame initiating the navigation. We disable those checks for this test, to + // test what happens in a compromise scenario. + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kDisableWebSecurity); + + // Set up an iframe. + WebContents* contents = shell()->web_contents(); + FrameTreeNode* root = + static_cast<WebContentsImpl*>(contents)->GetFrameTree()->root(); + GURL main_url(embedded_test_server()->GetURL( + "a.com", "/cross_site_iframe_factory.html?a(a)")); + EXPECT_TRUE(NavigateToURL(shell(), main_url)); + + // The navigation targets an invalid blob url; that's intentional to trigger + // an error response. The response should commit in a process dedicated to + // http://b.is. + std::string result; + EXPECT_TRUE(ExecuteScriptAndExtractString( + root, + "var iframe_element = document.getElementsByTagName('iframe')[0];" + "iframe_element.onload = () => {" + " domAutomationController.send('done');" + "};" + "iframe_element.src = 'blob:http://b.is:2932/';", + &result)); + WaitForLoadStop(contents); + + // Make sure we did a process transfer back to "b.is". + EXPECT_EQ( + " Site A ------------ proxies for B\n" + " +--Site B ------- proxies for A\n" + "Where A = http://a.com/\n" + " B = http://b.is/", + FrameTreeVisualizer().DepictFrameTree(root)); +} + } // namespace content diff --git a/chromium/content/browser/frame_host/render_frame_host_manager.cc b/chromium/content/browser/frame_host/render_frame_host_manager.cc index 093dd4a5fa8..4c2f4b604e2 100644 --- a/chromium/content/browser/frame_host/render_frame_host_manager.cc +++ b/chromium/content/browser/frame_host/render_frame_host_manager.cc @@ -1419,8 +1419,6 @@ bool RenderFrameHostManager::IsRendererTransferNeededForNavigation( } BrowserContext* context = rfh->GetSiteInstance()->GetBrowserContext(); - GURL effective_url = SiteInstanceImpl::GetEffectiveURL(context, dest_url); - // TODO(nasko, nick): These following --site-per-process checks are // overly simplistic. Update them to match all the cases // considered by DetermineSiteInstanceForURL. @@ -1434,7 +1432,7 @@ bool RenderFrameHostManager::IsRendererTransferNeededForNavigation( // then a transfer is needed. return rfh->GetSiteInstance()->RequiresDedicatedProcess() || SiteInstanceImpl::DoesSiteRequireDedicatedProcess(context, - effective_url); + dest_url); } SiteInstance* RenderFrameHostManager::ConvertToSiteInstance( diff --git a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc index d6010572ca2..7c74682b056 100644 --- a/chromium/content/browser/loader/resource_dispatcher_host_impl.cc +++ b/chromium/content/browser/loader/resource_dispatcher_host_impl.cc @@ -307,7 +307,7 @@ bool ShouldServiceRequest(int process_type, origin_string != "null"; if (has_origin) { GURL origin(origin_string); - if (!policy->CanCommitURL(child_id, origin) || + if (!policy->CanSetAsOriginHeader(child_id, origin) || GetContentClient()->browser()->IsIllegalOrigin(resource_context, child_id, origin)) { VLOG(1) << "Killed renderer for illegal origin: " << origin_string; diff --git a/chromium/content/browser/site_instance_impl.cc b/chromium/content/browser/site_instance_impl.cc index 9f88bafc19e..2ae47bf7cb9 100644 --- a/chromium/content/browser/site_instance_impl.cc +++ b/chromium/content/browser/site_instance_impl.cc @@ -361,15 +361,18 @@ GURL SiteInstanceImpl::GetEffectiveURL(BrowserContext* browser_context, // static bool SiteInstanceImpl::DoesSiteRequireDedicatedProcess( BrowserContext* browser_context, - const GURL& effective_url) { + const GURL& url) { // If --site-per-process is enabled, site isolation is enabled everywhere. if (SiteIsolationPolicy::UseDedicatedProcessesForAllSites()) return true; - // Let the content embedder enable site isolation for specific URLs. + // Let the content embedder enable site isolation for specific URLs. Use the + // canonical site url for this check, so that schemes with nested origins + // (blob and filesystem) work properly. + GURL site_url = GetSiteForURL(browser_context, url); if (GetContentClient()->IsSupplementarySiteIsolationModeEnabled() && GetContentClient()->browser()->DoesSiteRequireDedicatedProcess( - browser_context, effective_url)) { + browser_context, site_url)) { return true; } diff --git a/chromium/content/browser/site_instance_impl.h b/chromium/content/browser/site_instance_impl.h index b54de7f5886..35f07d52733 100644 --- a/chromium/content/browser/site_instance_impl.h +++ b/chromium/content/browser/site_instance_impl.h @@ -104,14 +104,12 @@ class CONTENT_EXPORT SiteInstanceImpl : public SiteInstance, static GURL GetEffectiveURL(BrowserContext* browser_context, const GURL& url); - // Returns true if pages loaded from |effective_url| ought to be handled only - // by a renderer process isolated from other sites. If --site-per-process is - // on the command line, this is true for all sites. In other site isolation - // modes, only a subset of sites will require dedicated processes. - // - // |effective_url| must be an effective URL. + // Returns true if pages loaded from |url| ought to be handled only by a + // renderer process isolated from other sites. If --site-per-process is on the + // command line, this is true for all sites. In other site isolation modes, + // only a subset of sites will require dedicated processes. static bool DoesSiteRequireDedicatedProcess(BrowserContext* browser_context, - const GURL& effective_url); + const GURL& url); protected: friend class BrowsingInstance; diff --git a/chromium/content/content_tests.gypi b/chromium/content/content_tests.gypi index 2792d423b8e..b83997a0821 100644 --- a/chromium/content/content_tests.gypi +++ b/chromium/content/content_tests.gypi @@ -204,6 +204,7 @@ 'browser/battery_status/battery_monitor_impl_browsertest.cc', 'browser/battery_status/battery_monitor_integration_browsertest.cc', 'browser/bluetooth/bluetooth_allowed_devices_map_unittest.cc', + 'browser/blob_storage/blob_url_browsertest.cc', 'browser/bookmarklet_browsertest.cc', 'browser/browser_side_navigation_browsertest.cc', 'browser/child_process_launcher_browsertest.cc', diff --git a/chromium/content/public/browser/child_process_security_policy.h b/chromium/content/public/browser/child_process_security_policy.h index 9a2becf0c82..f2dea9ed31d 100644 --- a/chromium/content/public/browser/child_process_security_policy.h +++ b/chromium/content/public/browser/child_process_security_policy.h @@ -34,11 +34,35 @@ class ChildProcessSecurityPolicy { static CONTENT_EXPORT ChildProcessSecurityPolicy* GetInstance(); // Web-safe schemes can be requested by any child process. Once a web-safe - // scheme has been registered, any child process can request URLs with - // that scheme. There is no mechanism for revoking web-safe schemes. + // scheme has been registered, any child process can request URLs whose + // origins use that scheme. There is no mechanism for revoking web-safe + // schemes. + // + // Only call this function if URLs of this scheme are okay to host in + // any ordinary renderer process. + // + // Registering 'your-scheme' as web-safe also causes 'blob:your-scheme://' + // and 'filesystem:your-scheme://' URLs to be considered web-safe. virtual void RegisterWebSafeScheme(const std::string& scheme) = 0; + // More restrictive variant of RegisterWebSafeScheme; URLs with this scheme + // may be requested by any child process, but navigations to this scheme may + // only commit in child processes that have been explicitly granted + // permission to do so. + // + // |always_allow_in_origin_headers| controls whether this scheme is allowed to + // appear as the Origin HTTP header in outbound requests, even if the + // originating process does not have permission to commit this scheme. This + // may be necessary if the scheme is used in conjunction with blink's + // IsolatedWorldSecurityOrigin mechanism, as for extension content scripts. + virtual void RegisterWebSafeIsolatedScheme( + const std::string& scheme, + bool always_allow_in_origin_headers) = 0; + // Returns true iff |scheme| has been registered as a web-safe scheme. + // TODO(nick): https://crbug.com/651534 This function does not have enough + // information to render an appropriate judgment for blob and filesystem URLs; + // change it to accept an URL instead. virtual bool IsWebSafeScheme(const std::string& scheme) = 0; // This permission grants only read access to a file. @@ -58,6 +82,17 @@ class ChildProcessSecurityPolicy { // This permission grants delete permission for |dir|. virtual void GrantDeleteFrom(int child_id, const base::FilePath& dir) = 0; + // Determine whether the process has the capability to request the URL. + // Before servicing a child process's request for a URL, the content layer + // calls this method to determine whether it is safe. + virtual bool CanRequestURL(int child_id, const GURL& url) = 0; + + // Whether the process is allowed to commit a document from the given URL. + // This is more restrictive than CanRequestURL, since CanRequestURL allows + // requests that might lead to cross-process navigations or external protocol + // handlers. + virtual bool CanCommitURL(int child_id, const GURL& url) = 0; + // These methods verify whether or not the child process has been granted // permissions perform these functions on |file|. diff --git a/chromium/content/public/browser/content_browser_client.cc b/chromium/content/public/browser/content_browser_client.cc index 3f6d970c9f3..e248038da22 100644 --- a/chromium/content/public/browser/content_browser_client.cc +++ b/chromium/content/public/browser/content_browser_client.cc @@ -44,7 +44,7 @@ bool ContentBrowserClient::ShouldUseProcessPerSite( bool ContentBrowserClient::DoesSiteRequireDedicatedProcess( BrowserContext* browser_context, - const GURL& effective_url) { + const GURL& effective_site_url) { return false; } diff --git a/chromium/content/public/browser/content_browser_client.h b/chromium/content/public/browser/content_browser_client.h index aa5ac0ebc88..38b67f620c7 100644 --- a/chromium/content/public/browser/content_browser_client.h +++ b/chromium/content/public/browser/content_browser_client.h @@ -189,17 +189,17 @@ class CONTENT_EXPORT ContentBrowserClient { virtual bool ShouldUseProcessPerSite(BrowserContext* browser_context, const GURL& effective_url); - // Returns true if site isolation should be enabled for |effective_url|. This - // call allows the embedder to supplement the site isolation policy enforced - // by the content layer. + // Returns true if site isolation should be enabled for |effective_site_url|. + // This call allows the embedder to supplement the site isolation policy + // enforced by the content layer. // // Will only be called if both of the following happen: // 1. The embedder asked to be consulted, by returning true from // ContentClient::IsSupplementarySiteIsolationModeEnabled(). - // 2. The content layer didn't decide to isolate |effective_url| according - // to its internal policy (e.g. because of --site-per-process). + // 2. The content layer didn't decide to isolate |effective_site_url| + // according to its internal policy (e.g. because of --site-per-process). virtual bool DoesSiteRequireDedicatedProcess(BrowserContext* browser_context, - const GURL& effective_url); + const GURL& effective_site_url); // Returns true unless the effective URL is part of a site that cannot live in // a process restricted to just that site. This is only called if site |