Move verification of OpenURL_Params and DownloadURL_Params to util func.

This CL deduplicates verification code that was present in
- RenderFrameProxyHost::OnOpenURL
- RenderFrameHostImpl::OnOpenURL
- RenderFrameMessageFilter::OnDownloadUrl

The duplicated code is moved into new util functions.  The new functions
will be tweaked in the future to add more verification (e.g. to verify
|initiator_origin|).

Bug: 919144
Change-Id: I50515c42d7f5003dd1f782ba59349599dca0f88d
Reviewed-on: https://chromium-review.googlesource.com/c/1395886
Reviewed-by: Nasko Oskov <nasko@chromium.org>
Commit-Queue: Ɓukasz Anforowicz <lukasza@chromium.org>
Cr-Commit-Position: refs/heads/master@{#626207}
diff --git a/content/browser/BUILD.gn b/content/browser/BUILD.gn
index 2ececf6..c4d780c 100644
--- a/content/browser/BUILD.gn
+++ b/content/browser/BUILD.gn
@@ -868,6 +868,8 @@
     "frame_host/interstitial_page_impl.h",
     "frame_host/interstitial_page_navigator_impl.cc",
     "frame_host/interstitial_page_navigator_impl.h",
+    "frame_host/ipc_utils.cc",
+    "frame_host/ipc_utils.h",
     "frame_host/keep_alive_handle_factory.cc",
     "frame_host/keep_alive_handle_factory.h",
     "frame_host/mixed_content_navigation_throttle.cc",
diff --git a/content/browser/bad_message.h b/content/browser/bad_message.h
index 5dcf68ec..03b16dde 100644
--- a/content/browser/bad_message.h
+++ b/content/browser/bad_message.h
@@ -195,9 +195,9 @@
   RWH_INVALID_FRAME_TOKEN = 167,
   RWH_BAD_FRAME_SINK_REQUEST = 168,
   RWH_SURFACE_INVARIANTS_VIOLATION = 169,
-  RFH_ILLEGAL_UPLOAD_PARAMS = 170,
+  ILLEGAL_UPLOAD_PARAMS = 170,
   RFH_BASE_URL_FOR_DATA_URL_SPECIFIED = 171,
-  RFPH_ILLEGAL_UPLOAD_PARAMS = 172,
+  OBSOLETE_RFPH_ILLEGAL_UPLOAD_PARAMS = 172,
   OBSOLETE_SWDH_PROVIDER_CREATED_ILLEGAL_TYPE = 173,
   OBSOLETE_SWDH_PROVIDER_CREATED_ILLEGAL_TYPE_NOT_WINDOW = 174,
   OBSOLETE_SWDH_PROVIDER_CREATED_ILLEGAL_TYPE_SERVICE_WORKER = 175,
@@ -226,10 +226,10 @@
   SYNC_COMPOSITOR_NO_FUTURE_FRAME = 198,
   SYNC_COMPOSITOR_NO_BEGIN_FRAME = 199,
   WEBUI_BAD_HOST_ACCESS = 200,
-  RFMF_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 201,
+  OBSOLETE_RFMF_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 201,
   PERMISSION_SERVICE_BAD_PERMISSION_DESCRIPTOR = 202,
-  RFH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 203,
-  RFPH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 204,
+  BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 203,
+  OBSOLETE_RFPH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL = 204,
   RFH_ERROR_PROCESS_NON_ERROR_COMMIT = 205,
   RFH_ERROR_PROCESS_NON_UNIQUE_ORIGIN_COMMIT = 206,
   RFH_CANNOT_RENDER_FALLBACK_CONTENT = 207,
diff --git a/content/browser/cross_site_transfer_browsertest.cc b/content/browser/cross_site_transfer_browsertest.cc
index 2bb9bfd..62bcf49 100644
--- a/content/browser/cross_site_transfer_browsertest.cc
+++ b/content/browser/cross_site_transfer_browsertest.cc
@@ -418,7 +418,7 @@
       "setTimeout(\n"
       "  function() { document.getElementById('file-form').submit(); },\n"
       "  0);"));
-  EXPECT_EQ(bad_message::RFPH_ILLEGAL_UPLOAD_PARAMS, kill_waiter.Wait());
+  EXPECT_EQ(bad_message::ILLEGAL_UPLOAD_PARAMS, kill_waiter.Wait());
 
   // The target frame should still be at the original location - the malicious
   // navigation should have been stopped.
diff --git a/content/browser/frame_host/ipc_utils.cc b/content/browser/frame_host/ipc_utils.cc
new file mode 100644
index 0000000..1530444
--- /dev/null
+++ b/content/browser/frame_host/ipc_utils.cc
@@ -0,0 +1,114 @@
+// 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/frame_host/ipc_utils.h"
+
+#include <utility>
+
+#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/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 "mojo/public/cpp/system/message_pipe.h"
+
+namespace content {
+
+namespace {
+
+bool VerifyBlobToken(
+    int process_id,
+    mojo::MessagePipeHandle received_token,
+    const GURL& received_url,
+    blink::mojom::BlobURLTokenPtrInfo* out_blob_url_token_info) {
+  DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id);
+  DCHECK(out_blob_url_token_info);
+
+  mojo::ScopedMessagePipeHandle blob_url_token_handle(
+      std::move(received_token));
+  blink::mojom::BlobURLTokenPtrInfo blob_url_token_info(
+      std::move(blob_url_token_handle), blink::mojom::BlobURLToken::Version_);
+  if (blob_url_token_info) {
+    if (!received_url.SchemeIsBlob()) {
+      bad_message::ReceivedBadMessage(
+          process_id, bad_message::BLOB_URL_TOKEN_FOR_NON_BLOB_URL);
+      return false;
+    }
+  }
+
+  *out_blob_url_token_info = std::move(blob_url_token_info);
+  return true;
+}
+
+}  // namespace
+
+bool VerifyDownloadUrlParams(
+    int process_id,
+    const FrameHostMsg_DownloadUrl_Params& params,
+    blink::mojom::BlobURLTokenPtrInfo* out_blob_url_token_info) {
+  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO) ||
+         BrowserThread::CurrentlyOn(BrowserThread::UI));
+  DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id);
+  DCHECK(out_blob_url_token_info);
+
+  // Verify |params.blob_url_token| and populate |out_blob_url_token_info|.
+  if (!VerifyBlobToken(process_id, params.blob_url_token, params.url,
+                       out_blob_url_token_info)) {
+    return false;
+  }
+
+  // TODO(lukasza): Verify |params.initiator_origin|.
+
+  return true;
+}
+
+bool VerifyOpenURLParams(SiteInstance* site_instance,
+                         const FrameHostMsg_OpenURL_Params& 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|.
+  blink::mojom::BlobURLTokenPtrInfo blob_url_token_info;
+  if (!VerifyBlobToken(process_id, params.blob_url_token, params.url,
+                       &blob_url_token_info)) {
+    return false;
+  }
+  if (blob_url_token_info) {
+    blink::mojom::BlobURLTokenPtr blob_url_token(
+        std::move(blob_url_token_info));
+    *out_blob_url_loader_factory =
+        ChromeBlobStorageContext::URLLoaderFactoryForToken(
+            process->GetBrowserContext(), std::move(blob_url_token));
+  }
+
+  // Verify |params.resource_request_body|.
+  auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
+  if (!policy->CanReadRequestBody(site_instance,
+                                  params.resource_request_body)) {
+    bad_message::ReceivedBadMessage(process,
+                                    bad_message::ILLEGAL_UPLOAD_PARAMS);
+    return false;
+  }
+
+  // TODO(lukasza): Verify |params.initiator_origin|.
+
+  return true;
+}
+
+}  // namespace content
diff --git a/content/browser/frame_host/ipc_utils.h b/content/browser/frame_host/ipc_utils.h
new file mode 100644
index 0000000..5263264
--- /dev/null
+++ b/content/browser/frame_host/ipc_utils.h
@@ -0,0 +1,53 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_FRAME_HOST_IPC_UTILS_H_
+#define CONTENT_BROWSER_FRAME_HOST_IPC_UTILS_H_
+
+#include "base/memory/ref_counted.h"
+#include "content/common/frame.mojom.h"
+#include "services/network/public/cpp/shared_url_loader_factory.h"
+#include "url/gurl.h"
+
+struct FrameHostMsg_DownloadUrl_Params;
+struct FrameHostMsg_OpenURL_Params;
+
+namespace content {
+
+class SiteInstance;
+
+// Verifies that |params| are valid and can be accessed by |process_id|.
+//
+// Returns true if the |params| are valid.  As a side-effect of the verification
+// |out_blob_url_token_info| will be populated.
+//
+// Terminates the renderer with the given |process_id| and returns false if the
+// |params| are invalid.
+//
+// This function may be called on either the IO thread or the UI thread
+// (but not on other threads).
+bool VerifyDownloadUrlParams(
+    int process_id,
+    const FrameHostMsg_DownloadUrl_Params& params,
+    blink::mojom::BlobURLTokenPtrInfo* out_blob_url_token_info);
+
+// Verifies that |params| are valid and can be accessed by the renderer process
+// associated with |site_instance|.
+//
+// Returns true if the |params| are valid.  As a side-effect of the verification
+// |out_validated_url| and |out_blob_url_loader_factory| will be populated.
+//
+// Terminates the renderer the process associated with |site_instance| and
+// returns false if the |params| are invalid.
+//
+// This function has to be called on the UI thread.
+bool VerifyOpenURLParams(SiteInstance* site_instance,
+                         const FrameHostMsg_OpenURL_Params& params,
+                         GURL* out_validated_url,
+                         scoped_refptr<network::SharedURLLoaderFactory>*
+                             out_blob_url_loader_factory);
+
+}  // namespace content
+
+#endif  // CONTENT_BROWSER_FRAME_HOST_IPC_UTILS_H_
diff --git a/content/browser/frame_host/render_frame_host_impl.cc b/content/browser/frame_host/render_frame_host_impl.cc
index 7c27281..0a92476 100644
--- a/content/browser/frame_host/render_frame_host_impl.cc
+++ b/content/browser/frame_host/render_frame_host_impl.cc
@@ -47,6 +47,7 @@
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
 #include "content/browser/frame_host/input/input_injector_impl.h"
+#include "content/browser/frame_host/ipc_utils.h"
 #include "content/browser/frame_host/keep_alive_handle_factory.h"
 #include "content/browser/frame_host/navigation_entry_impl.h"
 #include "content/browser/frame_host/navigation_handle_impl.h"
@@ -2061,29 +2062,11 @@
 }
 
 void RenderFrameHostImpl::OnOpenURL(const FrameHostMsg_OpenURL_Params& params) {
-  GURL validated_url(params.url);
-  GetProcess()->FilterURL(false, &validated_url);
-
-  mojo::ScopedMessagePipeHandle blob_url_token_handle(params.blob_url_token);
-  blink::mojom::BlobURLTokenPtr blob_url_token(
-      blink::mojom::BlobURLTokenPtrInfo(std::move(blob_url_token_handle),
-                                        blink::mojom::BlobURLToken::Version_));
+  // Verify and unpack IPC payload.
+  GURL validated_url;
   scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
-  if (blob_url_token) {
-    if (!params.url.SchemeIsBlob()) {
-      bad_message::ReceivedBadMessage(
-          GetProcess(), bad_message::RFH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL);
-      return;
-    }
-    blob_url_loader_factory =
-        ChromeBlobStorageContext::URLLoaderFactoryForToken(
-            GetSiteInstance()->GetBrowserContext(), std::move(blob_url_token));
-  }
-
-  if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadRequestBody(
-          GetSiteInstance(), params.resource_request_body)) {
-    bad_message::ReceivedBadMessage(GetProcess(),
-                                    bad_message::RFH_ILLEGAL_UPLOAD_PARAMS);
+  if (!VerifyOpenURLParams(GetSiteInstance(), params, &validated_url,
+                           &blob_url_loader_factory)) {
     return;
   }
 
@@ -3910,7 +3893,7 @@
   if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadRequestBody(
           GetSiteInstance(), validated_params.post_data)) {
     bad_message::ReceivedBadMessage(GetProcess(),
-                                    bad_message::RFH_ILLEGAL_UPLOAD_PARAMS);
+                                    bad_message::ILLEGAL_UPLOAD_PARAMS);
     return;
   }
 
diff --git a/content/browser/frame_host/render_frame_message_filter.cc b/content/browser/frame_host/render_frame_message_filter.cc
index 15cd7e5a..e17622d6 100644
--- a/content/browser/frame_host/render_frame_message_filter.cc
+++ b/content/browser/frame_host/render_frame_message_filter.cc
@@ -22,6 +22,7 @@
 #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/browser/frame_host/ipc_utils.h"
 #include "content/browser/frame_host/render_frame_host_impl.h"
 #include "content/browser/gpu/gpu_data_manager_impl.h"
 #include "content/browser/renderer_host/render_widget_helper.h"
@@ -518,14 +519,9 @@
 
 void RenderFrameMessageFilter::OnDownloadUrl(
     const FrameHostMsg_DownloadUrl_Params& params) {
-  mojo::ScopedMessagePipeHandle blob_url_token_handle(params.blob_url_token);
-  blink::mojom::BlobURLTokenPtrInfo blob_url_token(
-      std::move(blob_url_token_handle), blink::mojom::BlobURLToken::Version_);
-  if (blob_url_token && !params.url.SchemeIsBlob()) {
-    bad_message::ReceivedBadMessage(
-        this, bad_message::RFMF_BLOB_URL_TOKEN_FOR_NON_BLOB_URL);
+  blink::mojom::BlobURLTokenPtrInfo blob_url_token;
+  if (!VerifyDownloadUrlParams(render_process_id_, params, &blob_url_token))
     return;
-  }
 
   DownloadUrl(params.render_view_id, params.render_frame_id, params.url,
               params.referrer, params.initiator_origin, params.suggested_name,
diff --git a/content/browser/frame_host/render_frame_proxy_host.cc b/content/browser/frame_host/render_frame_proxy_host.cc
index f075640d..e70b7e4 100644
--- a/content/browser/frame_host/render_frame_proxy_host.cc
+++ b/content/browser/frame_host/render_frame_proxy_host.cc
@@ -17,6 +17,7 @@
 #include "content/browser/frame_host/cross_process_frame_connector.h"
 #include "content/browser/frame_host/frame_tree.h"
 #include "content/browser/frame_host/frame_tree_node.h"
+#include "content/browser/frame_host/ipc_utils.h"
 #include "content/browser/frame_host/navigator.h"
 #include "content/browser/frame_host/render_frame_host_delegate.h"
 #include "content/browser/renderer_host/render_view_host_impl.h"
@@ -283,23 +284,12 @@
 
 void RenderFrameProxyHost::OnOpenURL(
     const FrameHostMsg_OpenURL_Params& params) {
-  GURL validated_url(params.url);
-  GetProcess()->FilterURL(false, &validated_url);
-
-  mojo::ScopedMessagePipeHandle blob_url_token_handle(params.blob_url_token);
-  blink::mojom::BlobURLTokenPtr blob_url_token(
-      blink::mojom::BlobURLTokenPtrInfo(std::move(blob_url_token_handle),
-                                        blink::mojom::BlobURLToken::Version_));
+  // Verify and unpack IPC payload.
+  GURL validated_url;
   scoped_refptr<network::SharedURLLoaderFactory> blob_url_loader_factory;
-  if (blob_url_token) {
-    if (!params.url.SchemeIsBlob()) {
-      bad_message::ReceivedBadMessage(
-          GetProcess(), bad_message::RFPH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL);
-      return;
-    }
-    blob_url_loader_factory =
-        ChromeBlobStorageContext::URLLoaderFactoryForToken(
-            GetSiteInstance()->GetBrowserContext(), std::move(blob_url_token));
+  if (!VerifyOpenURLParams(GetSiteInstance(), params, &validated_url,
+                           &blob_url_loader_factory)) {
+    return;
   }
 
   // Verify that we are in the same BrowsingInstance as the current
@@ -308,15 +298,6 @@
   if (!site_instance_->IsRelatedSiteInstance(current_rfh->GetSiteInstance()))
     return;
 
-  // Verify if the request originator (*not* |current_rfh|) has access to the
-  // contents of the POST body.
-  if (!ChildProcessSecurityPolicyImpl::GetInstance()->CanReadRequestBody(
-          GetSiteInstance(), params.resource_request_body)) {
-    bad_message::ReceivedBadMessage(GetProcess(),
-                                    bad_message::RFPH_ILLEGAL_UPLOAD_PARAMS);
-    return;
-  }
-
   // Since this navigation targeted a specific RenderFrameProxy, it should stay
   // in the current tab.
   DCHECK_EQ(WindowOpenDisposition::CURRENT_TAB, params.disposition);
diff --git a/content/browser/navigation_browsertest.cc b/content/browser/navigation_browsertest.cc
index 513ab44..0dadf66 100644
--- a/content/browser/navigation_browsertest.cc
+++ b/content/browser/navigation_browsertest.cc
@@ -565,7 +565,7 @@
       "document.getElementById('file-form').submit();",
       &result));
   EXPECT_TRUE(result);
-  EXPECT_EQ(bad_message::RFH_ILLEGAL_UPLOAD_PARAMS, process_kill_waiter.Wait());
+  EXPECT_EQ(bad_message::ILLEGAL_UPLOAD_PARAMS, process_kill_waiter.Wait());
 }
 
 // Test case to verify that redirects to data: URLs are properly disallowed,
diff --git a/content/browser/security_exploit_browsertest.cc b/content/browser/security_exploit_browsertest.cc
index 71247da..c6a41c4 100644
--- a/content/browser/security_exploit_browsertest.cc
+++ b/content/browser/security_exploit_browsertest.cc
@@ -744,7 +744,7 @@
   EXPECT_EQ(start_url, root->current_frame_host()->GetLastCommittedURL());
 
   // Verify that the malicious renderer got killed.
-  EXPECT_EQ(bad_message::RFH_ILLEGAL_UPLOAD_PARAMS, kill_waiter.Wait());
+  EXPECT_EQ(bad_message::ILLEGAL_UPLOAD_PARAMS, kill_waiter.Wait());
 }
 
 // Test that forging a frame's unique name and commit won't allow changing the
diff --git a/tools/metrics/histograms/enums.xml b/tools/metrics/histograms/enums.xml
index 93f10f9..4c17ccfd 100644
--- a/tools/metrics/histograms/enums.xml
+++ b/tools/metrics/histograms/enums.xml
@@ -3805,9 +3805,9 @@
   <int value="167" label="RWH_INVALID_FRAME_TOKEN"/>
   <int value="168" label="RWH_BAD_FRAME_SINK_REQUEST"/>
   <int value="169" label="RWH_SURFACE_INVARIANTS_VIOLATION"/>
-  <int value="170" label="RFH_ILLEGAL_UPLOAD_PARAMS"/>
+  <int value="170" label="ILLEGAL_UPLOAD_PARAMS"/>
   <int value="171" label="RFH_BASE_URL_FOR_DATA_URL_SPECIFIED"/>
-  <int value="172" label="RFPH_ILLEGAL_UPLOAD_PARAMS"/>
+  <int value="172" label="OBSOLETE_RFPH_ILLEGAL_UPLOAD_PARAMS"/>
   <int value="173" label="OBSOLETE_SWDH_PROVIDER_CREATED_ILLEGAL_TYPE"/>
   <int value="174"
       label="OBSOLETE_SWDH_PROVIDER_CREATED_ILLEGAL_TYPE_NOT_WINDOW"/>
@@ -3840,10 +3840,10 @@
   <int value="198" label="SYNC_COMPOSITOR_NO_FUTURE_FRAME"/>
   <int value="199" label="SYNC_COMPOSITOR_NO_BEGIN_FRAME"/>
   <int value="200" label="WEBUI_BAD_HOST_ACCESS"/>
-  <int value="201" label="RFMF_BLOB_URL_TOKEN_FOR_NON_BLOB_URL"/>
+  <int value="201" label="OBSOLETE_RFMF_BLOB_URL_TOKEN_FOR_NON_BLOB_URL"/>
   <int value="202" label="PERMISSION_SERVICE_BAD_PERMISSION_DESCRIPTOR"/>
-  <int value="203" label="RFH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL"/>
-  <int value="204" label="RFPH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL"/>
+  <int value="203" label="BLOB_URL_TOKEN_FOR_NON_BLOB_URL"/>
+  <int value="204" label="OBSOLETE_RFPH_BLOB_URL_TOKEN_FOR_NON_BLOB_URL"/>
   <int value="205" label="RFH_ERROR_PROCESS_NON_ERROR_COMMIT"/>
   <int value="206" label="RFH_ERROR_PROCESS_NON_UNIQUE_ORIGIN_COMMIT"/>
   <int value="207" label="RFH_CANNOT_RENDER_FALLBACK_CONTENT"/>