|  | // Copyright 2019 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 "content/browser/renderer_host/ipc_utils.h" | 
|  |  | 
|  | #include <utility> | 
|  |  | 
|  | #include "base/optional.h" | 
|  | #include "content/browser/bad_message.h" | 
|  | #include "content/browser/blob_storage/chrome_blob_storage_context.h" | 
|  | #include "content/browser/child_process_security_policy_impl.h" | 
|  | #include "content/common/frame.mojom.h" | 
|  | #include "content/common/frame_messages.h" | 
|  | #include "content/common/navigation_params_utils.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/browser_thread.h" | 
|  | #include "content/public/browser/render_process_host.h" | 
|  | #include "content/public/browser/site_instance.h" | 
|  | #include "content/public/common/child_process_host.h" | 
|  | #include "content/public/common/url_constants.h" | 
|  | #include "mojo/public/cpp/system/message_pipe.h" | 
|  |  | 
|  | namespace content { | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Validates that |received_token| is non-null iff associated with a blob: URL. | 
|  | bool VerifyBlobToken( | 
|  | int process_id, | 
|  | const mojo::PendingRemote<blink::mojom::BlobURLToken>& received_token, | 
|  | const GURL& received_url) { | 
|  | DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id); | 
|  |  | 
|  | if (received_token.is_valid()) { | 
|  | if (!received_url.SchemeIsBlob()) { | 
|  | bad_message::ReceivedBadMessage( | 
|  | process_id, bad_message::BLOB_URL_TOKEN_FOR_NON_BLOB_URL); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyInitiatorOrigin(int process_id, | 
|  | const url::Origin& initiator_origin) { | 
|  | // TODO(acolwell, nasko): https://crbug.com/1029092: Ensure the precursor of | 
|  | // opaque origins matches the origin lock.  One known problematic case are | 
|  | // reloads initiated from error pages - see the following | 
|  | // RenderFrameHostManagerTest tests: | 
|  | // 1. ErrorPageNavigationReload: | 
|  | //    - renderer origin lock = chrome-error://chromewebdata/ | 
|  | //    - precursor of initiator origin = http://127.0.0.1:.../ | 
|  | // 2. ErrorPageNavigationReload_InSubframe_BlockedByClient | 
|  | //    - renderer origin lock = http://b.com:.../ | 
|  | //    - precursor of initiator origin = http://c.com:.../ | 
|  | if (initiator_origin.opaque()) | 
|  | return true; | 
|  |  | 
|  | auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); | 
|  | if (!policy->CanAccessDataForOrigin(process_id, initiator_origin)) { | 
|  | bad_message::ReceivedBadMessage(process_id, | 
|  | bad_message::INVALID_INITIATOR_ORIGIN); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool VerifyDownloadUrlParams(SiteInstance* site_instance, | 
|  | const blink::mojom::DownloadURLParams& params) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | DCHECK(site_instance); | 
|  | RenderProcessHost* process = site_instance->GetProcess(); | 
|  | int process_id = process->GetID(); | 
|  |  | 
|  | // Verifies |params.blob_url_token| is appropriately set. | 
|  | if (!VerifyBlobToken(process_id, params.blob_url_token, params.url)) | 
|  | return false; | 
|  |  | 
|  | // Verify |params.initiator_origin|. | 
|  | if (params.initiator_origin && | 
|  | !VerifyInitiatorOrigin(process_id, *params.initiator_origin)) | 
|  | return false; | 
|  |  | 
|  | // If |params.url| is not set, this must be a large data URL being passed | 
|  | // through |params.data_url_blob|. | 
|  | if (!params.url.is_valid() && !params.data_url_blob.is_valid()) | 
|  | return false; | 
|  |  | 
|  | // Verification succeeded. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyOpenURLParams(SiteInstance* site_instance, | 
|  | const blink::mojom::OpenURLParamsPtr& params, | 
|  | GURL* out_validated_url, | 
|  | scoped_refptr<network::SharedURLLoaderFactory>* | 
|  | out_blob_url_loader_factory) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | DCHECK(site_instance); | 
|  | DCHECK(out_validated_url); | 
|  | DCHECK(out_blob_url_loader_factory); | 
|  | RenderProcessHost* process = site_instance->GetProcess(); | 
|  | int process_id = process->GetID(); | 
|  |  | 
|  | // Verify |params.url| and populate |out_validated_url|. | 
|  | *out_validated_url = params->url; | 
|  | process->FilterURL(false, out_validated_url); | 
|  |  | 
|  | // Verify |params.blob_url_token| and populate |out_blob_url_loader_factory|. | 
|  | if (!VerifyBlobToken(process_id, params->blob_url_token, params->url)) | 
|  | return false; | 
|  |  | 
|  | if (params->blob_url_token.is_valid()) { | 
|  | *out_blob_url_loader_factory = | 
|  | ChromeBlobStorageContext::URLLoaderFactoryForToken( | 
|  | BrowserContext::GetStoragePartition( | 
|  | site_instance->GetBrowserContext(), site_instance), | 
|  | std::move(params->blob_url_token)); | 
|  | } | 
|  |  | 
|  | // Verify |params.post_body|. | 
|  | auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); | 
|  | if (!policy->CanReadRequestBody(site_instance, params->post_body)) { | 
|  | bad_message::ReceivedBadMessage(process, | 
|  | bad_message::ILLEGAL_UPLOAD_PARAMS); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Verify |params.initiator_origin|. | 
|  | if (!VerifyInitiatorOrigin(process_id, params->initiator_origin)) | 
|  | return false; | 
|  |  | 
|  | // Verification succeeded. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool VerifyBeginNavigationCommonParams( | 
|  | SiteInstance* site_instance, | 
|  | mojom::CommonNavigationParams* common_params) { | 
|  | DCHECK_CURRENTLY_ON(BrowserThread::UI); | 
|  | DCHECK(site_instance); | 
|  | DCHECK(common_params); | 
|  | RenderProcessHost* process = site_instance->GetProcess(); | 
|  | int process_id = process->GetID(); | 
|  |  | 
|  | // Verify (and possibly rewrite) |url|. | 
|  | process->FilterURL(false, &common_params->url); | 
|  | if (common_params->url.SchemeIs(kChromeErrorScheme)) { | 
|  | mojo::ReportBadMessage("Renderer cannot request error page URLs directly"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Verify |post_data|. | 
|  | auto* policy = ChildProcessSecurityPolicyImpl::GetInstance(); | 
|  | if (!policy->CanReadRequestBody(site_instance, common_params->post_data)) { | 
|  | bad_message::ReceivedBadMessage(process, | 
|  | bad_message::ILLEGAL_UPLOAD_PARAMS); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Verify |transition| is webby. | 
|  | if (!PageTransitionIsWebTriggerable(common_params->transition)) { | 
|  | bad_message::ReceivedBadMessage( | 
|  | process, bad_message::RFHI_BEGIN_NAVIGATION_NON_WEBBY_TRANSITION); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Verify |initiator_origin|. | 
|  | if (!common_params->initiator_origin.has_value()) { | 
|  | bad_message::ReceivedBadMessage( | 
|  | process, bad_message::RFHI_BEGIN_NAVIGATION_MISSING_INITIATOR_ORIGIN); | 
|  | return false; | 
|  | } | 
|  | if (!VerifyInitiatorOrigin(process_id, | 
|  | common_params->initiator_origin.value())) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Verify |base_url_for_data_url|. | 
|  | if (!common_params->base_url_for_data_url.is_empty()) { | 
|  | // Kills the process. http://crbug.com/726142 | 
|  | bad_message::ReceivedBadMessage( | 
|  | process, bad_message::RFH_BASE_URL_FOR_DATA_URL_SPECIFIED); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Asynchronous (browser-controlled, but) renderer-initiated navigations can | 
|  | // not be same-document. Allowing this incorrectly could have us try to | 
|  | // navigate an existing document to a different site. | 
|  | if (NavigationTypeUtils::IsSameDocument(common_params->navigation_type)) | 
|  | return false; | 
|  |  | 
|  | // Verification succeeded. | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace content |