Reland: 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@.

Note: This CL was originally reverted because it didn't allow Extensions
to register protocol handlers. This has been fixed by allowing the
chrome-extension scheme. This check must be done in the chrome/ layer.

Bug: 971917,952974
Change-Id: I19da557135af9a86c3305ef47e4b06152abd6ddd
Tbr: [email protected],[email protected],[email protected]
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1730689
Commit-Queue: Raymes Khoury <[email protected]>
Reviewed-by: Alex Moshchuk <[email protected]>
Cr-Commit-Position: refs/heads/master@{#684245}
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index 6eb306c8..5d0e901 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -282,6 +282,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;
+
+  url::Origin url_origin = url::Origin::Create(url);
+  if (url_origin.opaque())
+    return false;
+
+  if (!url_origin.IsSameOriginWith(origin))
+    return false;
+
+  return true;
+}
+
 }  // namespace
 
 std::unique_ptr<WebContents> WebContents::Create(
@@ -4846,10 +4864,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);
 }
@@ -4863,10 +4883,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);
 }