Commit 90b1a0d7 authored by Chris Mumford's avatar Chris Mumford Committed by Commit Bot

Allow file/filesystem schemes to redirect to same scheme.

Relax redirect safety checks, which exist in the network
service but were not present in URLRequestJob, to allow
the loading of a file/filesystem scheme to redirect to
the file/filesystem scheme.

One example of a redirect is where the following URL:

  file:///path/to/directory

during reload is redirected to:

  file:///path/to/directory/

This change also fixes redirects of Windows file links
(with network service) as it redirects back to the client
before following the redirect. This avoids an infinite
recursion bug triggered by circular symbolic links.

Bug: 884277,887039
Change-Id: I13923fc29397b1d3aa6679d861e9edc3af1c816b
Reviewed-on: https://chromium-review.googlesource.com/1234335Reviewed-by: default avatarJohn Abd-El-Malek <jam@chromium.org>
Commit-Queue: Chris Mumford <cmumford@chromium.org>
Cr-Commit-Position: refs/heads/master@{#594450}
parent 6fb30d31
This diff is collapsed.
...@@ -447,14 +447,6 @@ class FileSystemFileURLLoader : public FileSystemEntryURLLoader { ...@@ -447,14 +447,6 @@ class FileSystemFileURLLoader : public FileSystemEntryURLLoader {
original_request_.url.ReplaceComponents(replacements); original_request_.url.ReplaceComponents(replacements);
head_.encoded_data_length = 0; head_.encoded_data_length = 0;
client_->OnReceiveRedirect(redirect_info, head_); client_->OnReceiveRedirect(redirect_info, head_);
// Restart the request with a directory loader.
network::ResourceRequest new_request = original_request_;
new_request.url = redirect_info.new_url;
FileSystemDirectoryURLLoader::CreateAndStart(
new_request, binding_.Unbind(), client_.PassInterface(),
std::move(params_), io_task_runner_);
MaybeDeleteSelf();
return; return;
} }
......
...@@ -935,7 +935,7 @@ void NavigationRequest::OnResponseStarted( ...@@ -935,7 +935,7 @@ void NavigationRequest::OnResponseStarted(
bool is_stream, bool is_stream,
PreviewsState previews_state, PreviewsState previews_state,
base::Optional<SubresourceLoaderParams> subresource_loader_params) { base::Optional<SubresourceLoaderParams> subresource_loader_params) {
DCHECK(state_ == STARTED); DCHECK_EQ(state_, STARTED);
DCHECK(response); DCHECK(response);
TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this, TRACE_EVENT_ASYNC_STEP_INTO0("navigation", "NavigationRequest", this,
"OnResponseStarted"); "OnResponseStarted");
......
...@@ -322,10 +322,12 @@ bool IsURLHandledByDefaultLoader(const GURL& url) { ...@@ -322,10 +322,12 @@ bool IsURLHandledByDefaultLoader(const GURL& url) {
return IsURLHandledByNetworkService(url) || url.SchemeIs(url::kDataScheme); return IsURLHandledByNetworkService(url) || url.SchemeIs(url::kDataScheme);
} }
// Determines whether it is safe to redirect to |url|. // Determines whether it is safe to redirect from |from_url| to |to_url|.
bool IsRedirectSafe(const GURL& url, ResourceContext* resource_context) { bool IsRedirectSafe(const GURL& from_url,
return IsSafeRedirectTarget(url) && const GURL& to_url,
GetContentClient()->browser()->IsSafeRedirectTarget(url, ResourceContext* resource_context) {
return IsSafeRedirectTarget(from_url, to_url) &&
GetContentClient()->browser()->IsSafeRedirectTarget(to_url,
resource_context); resource_context);
} }
...@@ -1186,7 +1188,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController ...@@ -1186,7 +1188,7 @@ class NavigationURLLoaderImpl::URLLoaderRequestController
const network::ResourceResponseHead& head) override { const network::ResourceResponseHead& head) override {
if (base::FeatureList::IsEnabled(network::features::kNetworkService) && if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
!bypass_redirect_checks_ && !bypass_redirect_checks_ &&
!IsRedirectSafe(redirect_info.new_url, resource_context_)) { !IsRedirectSafe(url_, redirect_info.new_url, resource_context_)) {
OnComplete(network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT)); OnComplete(network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT));
return; return;
} }
......
...@@ -111,14 +111,23 @@ bool IsRendererDebugURL(const GURL& url) { ...@@ -111,14 +111,23 @@ bool IsRendererDebugURL(const GURL& url) {
return false; return false;
} }
bool IsSafeRedirectTarget(const GURL& url) { bool IsSafeRedirectTarget(const GURL& from_url, const GURL& to_url) {
static base::NoDestructor<std::set<std::string>> kUnsafeSchemes( static base::NoDestructor<std::set<std::string>> kUnsafeSchemes(
std::set<std::string>({ std::set<std::string>({
url::kAboutScheme, url::kDataScheme, url::kFileScheme, url::kAboutScheme, url::kDataScheme, url::kFileScheme,
url::kFileSystemScheme, url::kFileSystemScheme,
})); }));
return !HasWebUIScheme(url) && if (HasWebUIScheme(to_url))
kUnsafeSchemes->find(url.scheme()) == kUnsafeSchemes->end(); return false;
if (kUnsafeSchemes->find(to_url.scheme()) == kUnsafeSchemes->end())
return true;
if (from_url.is_empty())
return false;
if (from_url.SchemeIsFile() && to_url.SchemeIsFile())
return true;
if (from_url.SchemeIsFileSystem() && to_url.SchemeIsFileSystem())
return true;
return false;
} }
} // namespace content } // namespace content
...@@ -39,8 +39,9 @@ CONTENT_EXPORT bool IsRendererDebugURL(const GURL& url); ...@@ -39,8 +39,9 @@ CONTENT_EXPORT bool IsRendererDebugURL(const GURL& url);
// is disabled. // is disabled.
bool CONTENT_EXPORT IsURLHandledByNetworkService(const GURL& url); bool CONTENT_EXPORT IsURLHandledByNetworkService(const GURL& url);
// Determines whether it is safe to redirect to |url|. // Determines whether it is safe to redirect from |from_url| to |to_url|.
CONTENT_EXPORT bool IsSafeRedirectTarget(const GURL& url); CONTENT_EXPORT bool IsSafeRedirectTarget(const GURL& from_url,
const GURL& to_url);
} // namespace content } // namespace content
......
...@@ -22,4 +22,23 @@ TEST(UrlUtilsTest, IsURLHandledByNetworkStack) { ...@@ -22,4 +22,23 @@ TEST(UrlUtilsTest, IsURLHandledByNetworkStack) {
EXPECT_FALSE(IsURLHandledByNetworkStack(GURL())); EXPECT_FALSE(IsURLHandledByNetworkStack(GURL()));
} }
TEST(UrlUtilsTest, IsSafeRedirectTarget) {
EXPECT_FALSE(IsSafeRedirectTarget(GURL(), GURL("chrome://foo/bar.html")));
EXPECT_TRUE(IsSafeRedirectTarget(GURL(), GURL("http://foo/bar.html")));
EXPECT_FALSE(IsSafeRedirectTarget(GURL(), GURL("file:///foo/bar/")));
EXPECT_TRUE(
IsSafeRedirectTarget(GURL("file:///foo/bar"), GURL("file:///foo/bar/")));
EXPECT_TRUE(IsSafeRedirectTarget(GURL("filesystem://foo/bar"),
GURL("filesystem://foo/bar/")));
EXPECT_TRUE(IsSafeRedirectTarget(GURL(), GURL("unknown://foo/bar/")));
EXPECT_FALSE(IsSafeRedirectTarget(GURL("http://foo/bar.html"),
GURL("file:///foo/bar/")));
EXPECT_TRUE(IsSafeRedirectTarget(GURL("file:///foo/bar/"),
GURL("http://foo/bar.html")));
// TODO(cmumford): Capturing current behavior, but should probably prevent
// redirect to invalid URL.
EXPECT_TRUE(IsSafeRedirectTarget(GURL(), GURL()));
}
} // namespace content } // namespace content
...@@ -760,7 +760,7 @@ int ResourceDispatcher::StartAsync( ...@@ -760,7 +760,7 @@ int ResourceDispatcher::StartAsync(
pending_requests_[request_id]->url_loader_client = pending_requests_[request_id]->url_loader_client =
std::make_unique<URLLoaderClientImpl>( std::make_unique<URLLoaderClientImpl>(
request_id, this, loading_task_runner, request_id, this, loading_task_runner,
true /* bypass_redirect_checks */); true /* bypass_redirect_checks */, request->url);
if (request->resource_type == RESOURCE_TYPE_SHARED_WORKER) { if (request->resource_type == RESOURCE_TYPE_SHARED_WORKER) {
// For shared workers, immediately post a task for continuing loading // For shared workers, immediately post a task for continuing loading
...@@ -782,9 +782,9 @@ int ResourceDispatcher::StartAsync( ...@@ -782,9 +782,9 @@ int ResourceDispatcher::StartAsync(
return request_id; return request_id;
} }
std::unique_ptr<URLLoaderClientImpl> client( std::unique_ptr<URLLoaderClientImpl> client(new URLLoaderClientImpl(
new URLLoaderClientImpl(request_id, this, loading_task_runner, request_id, this, loading_task_runner,
url_loader_factory->BypassRedirectChecks())); url_loader_factory->BypassRedirectChecks(), request->url));
if (pass_response_pipe_to_peer) if (pass_response_pipe_to_peer)
client->SetPassResponsePipeToDispatcher(true); client->SetPassResponsePipeToDispatcher(true);
......
...@@ -19,10 +19,10 @@ ...@@ -19,10 +19,10 @@
namespace content { namespace content {
namespace { namespace {
// Determines whether it is safe to redirect to |url|. // Determines whether it is safe to redirect from |from_url| to |to_url|.
bool IsRedirectSafe(const GURL& url) { bool IsRedirectSafe(const GURL& from_url, const GURL& to_url) {
return IsSafeRedirectTarget(url) && return IsSafeRedirectTarget(from_url, to_url) &&
GetContentClient()->renderer()->IsSafeRedirectTarget(url); GetContentClient()->renderer()->IsSafeRedirectTarget(to_url);
} }
} // namespace } // namespace
...@@ -127,11 +127,13 @@ URLLoaderClientImpl::URLLoaderClientImpl( ...@@ -127,11 +127,13 @@ URLLoaderClientImpl::URLLoaderClientImpl(
int request_id, int request_id,
ResourceDispatcher* resource_dispatcher, ResourceDispatcher* resource_dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner, scoped_refptr<base::SingleThreadTaskRunner> task_runner,
bool bypass_redirect_checks) bool bypass_redirect_checks,
const GURL& request_url)
: request_id_(request_id), : request_id_(request_id),
resource_dispatcher_(resource_dispatcher), resource_dispatcher_(resource_dispatcher),
task_runner_(std::move(task_runner)), task_runner_(std::move(task_runner)),
bypass_redirect_checks_(bypass_redirect_checks), bypass_redirect_checks_(bypass_redirect_checks),
last_loaded_url_(request_url),
url_loader_client_binding_(this), url_loader_client_binding_(this),
weak_factory_(this) {} weak_factory_(this) {}
...@@ -247,11 +249,13 @@ void URLLoaderClientImpl::OnReceiveRedirect( ...@@ -247,11 +249,13 @@ void URLLoaderClientImpl::OnReceiveRedirect(
DCHECK(!has_received_response_); DCHECK(!has_received_response_);
DCHECK(!body_consumer_); DCHECK(!body_consumer_);
if (base::FeatureList::IsEnabled(network::features::kNetworkService) && if (base::FeatureList::IsEnabled(network::features::kNetworkService) &&
!bypass_redirect_checks_ && !IsRedirectSafe(redirect_info.new_url)) { !bypass_redirect_checks_ &&
!IsRedirectSafe(last_loaded_url_, redirect_info.new_url)) {
OnComplete(network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT)); OnComplete(network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT));
return; return;
} }
last_loaded_url_ = redirect_info.new_url;
if (NeedsStoringMessage()) { if (NeedsStoringMessage()) {
StoreAndDispatch(std::make_unique<DeferredOnReceiveRedirect>( StoreAndDispatch(std::make_unique<DeferredOnReceiveRedirect>(
redirect_info, response_head, task_runner_)); redirect_info, response_head, task_runner_));
......
...@@ -38,7 +38,8 @@ class CONTENT_EXPORT URLLoaderClientImpl final ...@@ -38,7 +38,8 @@ class CONTENT_EXPORT URLLoaderClientImpl final
URLLoaderClientImpl(int request_id, URLLoaderClientImpl(int request_id,
ResourceDispatcher* resource_dispatcher, ResourceDispatcher* resource_dispatcher,
scoped_refptr<base::SingleThreadTaskRunner> task_runner, scoped_refptr<base::SingleThreadTaskRunner> task_runner,
bool bypass_redirect_checks); bool bypass_redirect_checks,
const GURL& request_url);
~URLLoaderClientImpl() override; ~URLLoaderClientImpl() override;
// Sets |is_deferred_|. From now, the received messages are not dispatched // Sets |is_deferred_|. From now, the received messages are not dispatched
...@@ -105,6 +106,7 @@ class CONTENT_EXPORT URLLoaderClientImpl final ...@@ -105,6 +106,7 @@ class CONTENT_EXPORT URLLoaderClientImpl final
ResourceDispatcher* const resource_dispatcher_; ResourceDispatcher* const resource_dispatcher_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
bool bypass_redirect_checks_ = false; bool bypass_redirect_checks_ = false;
GURL last_loaded_url_;
network::mojom::URLLoaderPtr url_loader_; network::mojom::URLLoaderPtr url_loader_;
mojo::Binding<network::mojom::URLLoaderClient> url_loader_client_binding_; mojo::Binding<network::mojom::URLLoaderClient> url_loader_client_binding_;
......
...@@ -176,7 +176,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnReceiveRedirect( ...@@ -176,7 +176,7 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnReceiveRedirect(
const net::RedirectInfo& redirect_info, const net::RedirectInfo& redirect_info,
const network::ResourceResponseHead& head) { const network::ResourceResponseHead& head) {
if (redirect_url_ != redirect_info.new_url && if (redirect_url_ != redirect_info.new_url &&
!IsRedirectSafe(redirect_info.new_url)) { !IsRedirectSafe(request_.url, redirect_info.new_url)) {
OnRequestError( OnRequestError(
network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT)); network::URLLoaderCompletionStatus(net::ERR_UNSAFE_REDIRECT));
return; return;
...@@ -541,18 +541,19 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnRequestError( ...@@ -541,18 +541,19 @@ void WebRequestProxyingURLLoaderFactory::InProgressRequest::OnRequestError(
factory_->RemoveRequest(network_service_request_id_, request_id_); factory_->RemoveRequest(network_service_request_id_, request_id_);
} }
// Determines whether it is safe to redirect to |url|. // Determines whether it is safe to redirect from |from_url| to |to_url|.
bool WebRequestProxyingURLLoaderFactory::InProgressRequest::IsRedirectSafe( bool WebRequestProxyingURLLoaderFactory::InProgressRequest::IsRedirectSafe(
const GURL& url) { const GURL& from_url,
if (url.SchemeIs(extensions::kExtensionScheme)) { const GURL& to_url) {
if (to_url.SchemeIs(extensions::kExtensionScheme)) {
const Extension* extension = const Extension* extension =
factory_->info_map_->extensions().GetByID(url.host()); factory_->info_map_->extensions().GetByID(to_url.host());
if (!extension) if (!extension)
return false; return false;
return WebAccessibleResourcesInfo::IsResourceWebAccessible(extension, return WebAccessibleResourcesInfo::IsResourceWebAccessible(extension,
url.path()); to_url.path());
} }
return content::IsSafeRedirectTarget(url); return content::IsSafeRedirectTarget(from_url, to_url);
} }
WebRequestProxyingURLLoaderFactory::WebRequestProxyingURLLoaderFactory( WebRequestProxyingURLLoaderFactory::WebRequestProxyingURLLoaderFactory(
......
...@@ -108,7 +108,7 @@ class WebRequestProxyingURLLoaderFactory ...@@ -108,7 +108,7 @@ class WebRequestProxyingURLLoaderFactory
void HandleResponseOrRedirectHeaders( void HandleResponseOrRedirectHeaders(
const net::CompletionCallback& continuation); const net::CompletionCallback& continuation);
void OnRequestError(const network::URLLoaderCompletionStatus& status); void OnRequestError(const network::URLLoaderCompletionStatus& status);
bool IsRedirectSafe(const GURL& url); bool IsRedirectSafe(const GURL& from_url, const GURL& to_url);
WebRequestProxyingURLLoaderFactory* const factory_; WebRequestProxyingURLLoaderFactory* const factory_;
network::ResourceRequest request_; network::ResourceRequest request_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment