Add browser-side checks for registerProtocolHandler

This adds browser-side checks for the registerProtocolHandler API. In
particular:
1) Checks that the origin of the URL handler matches the origin of the RFH
where the call originates.
2) Checks that the protocol being handled is one of the safelisted
schemes.
3) Checks that the scheme of the URL handler is either http or https.

Check (1) is implemented in the content/ layer so that non-Chrome
embedders can benefit from this check.

Checks (2) and (3) are implemented further down in Chrome because we
need to ensure that if existing handlers are registered that we don't
load them from prefs.

Tests are added.

We may want to consider restricting this API to secure contexts in a
follow-up but this will need to be vetted by blink-dev@.

Bug: 971917,952974
Change-Id: I08b1c46b8e8493679adf7f252d09a224d67e64e7
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1652756
Commit-Queue: Raymes Khoury <[email protected]>
Reviewed-by: Christian Dullweber <[email protected]>
Reviewed-by: Charlie Reis <[email protected]>
Reviewed-by: Trent Apted <[email protected]>
Reviewed-by: Ben Wells <[email protected]>
Cr-Commit-Position: refs/heads/master@{#675929}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 24ea4bd..3cc697a 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -302,6 +302,24 @@
   return a->frame_tree_node()->depth() < b->frame_tree_node()->depth();
 }
 
+bool AreValidRegisterProtocolHandlerArguments(const std::string& protocol,
+                                              const GURL& url,
+                                              const url::Origin& origin) {
+  ChildProcessSecurityPolicyImpl* policy =
+      ChildProcessSecurityPolicyImpl::GetInstance();
+  if (policy->IsPseudoScheme(protocol))
+    return false;
+
+  if (!url.SchemeIsHTTPOrHTTPS())
+    return false;
+
+  url::Origin url_origin = url::Origin::Create(url);
+  if (!url_origin.IsSameOriginWith(origin))
+    return false;
+
+  return true;
+}
+
 }  // namespace
 
 std::unique_ptr<WebContents> WebContents::Create(
@@ -4832,10 +4850,12 @@
   if (!delegate_)
     return;
 
-  ChildProcessSecurityPolicyImpl* policy =
-      ChildProcessSecurityPolicyImpl::GetInstance();
-  if (policy->IsPseudoScheme(protocol))
+  if (!AreValidRegisterProtocolHandlerArguments(
+          protocol, url, source->GetLastCommittedOrigin())) {
+    ReceivedBadMessage(source->GetProcess(),
+                       bad_message::REGISTER_PROTOCOL_HANDLER_INVALID_URL);
     return;
+  }
 
   delegate_->RegisterProtocolHandler(this, protocol, url, user_gesture);
 }
@@ -4849,10 +4869,12 @@
   if (!delegate_)
     return;
 
-  ChildProcessSecurityPolicyImpl* policy =
-      ChildProcessSecurityPolicyImpl::GetInstance();
-  if (policy->IsPseudoScheme(protocol))
+  if (!AreValidRegisterProtocolHandlerArguments(
+          protocol, url, source->GetLastCommittedOrigin())) {
+    ReceivedBadMessage(source->GetProcess(),
+                       bad_message::REGISTER_PROTOCOL_HANDLER_INVALID_URL);
     return;
+  }
 
   delegate_->UnregisterProtocolHandler(this, protocol, url, user_gesture);
 }