blob: 03a23f0571b2672c07f5234dfd1485499298631e [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2019 The Chromium Authors
Lukasz Anforowicz445abd42019-01-25 21:27:272// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
danakjc492bf82020-09-09 20:02:445#include "content/browser/renderer_host/ipc_utils.h"
Lukasz Anforowicz445abd42019-01-25 21:27:276
Arthur Sonzognic686e8f2024-01-11 08:36:377#include <optional>
Lukasz Anforowicz445abd42019-01-25 21:27:278#include <utility>
9
Lei Zhang58c9bd12025-06-17 23:12:0210#include "base/debug/crash_logging.h"
Helmut Januschka42c832072025-03-02 01:13:1211#include "base/strings/to_string.h"
Lukasz Anforowicz445abd42019-01-25 21:27:2712#include "content/browser/bad_message.h"
13#include "content/browser/blob_storage/chrome_blob_storage_context.h"
14#include "content/browser/child_process_security_policy_impl.h"
Yao Xiao1ac702d2022-06-08 17:20:4915#include "content/browser/renderer_host/frame_tree_node.h"
16#include "content/browser/renderer_host/render_frame_host_impl.h"
Alex Moshchuk4043b732024-06-14 06:02:3917#include "content/common/features.h"
Lukasz Anforowicz445abd42019-01-25 21:27:2718#include "content/common/frame.mojom.h"
danakj837b1c42020-11-25 23:59:5419#include "content/common/navigation_params_utils.h"
Marijn Kruisselbrink8ffda442020-09-03 18:29:4720#include "content/public/browser/browser_context.h"
Lukasz Anforowicz445abd42019-01-25 21:27:2721#include "content/public/browser/browser_thread.h"
Will Harriscd57b832023-01-05 20:03:1022#include "content/public/browser/child_process_host.h"
Lukasz Anforowicz445abd42019-01-25 21:27:2723#include "content/public/browser/render_process_host.h"
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:2924#include "content/public/common/url_constants.h"
Lukasz Anforowicz445abd42019-01-25 21:27:2725#include "mojo/public/cpp/system/message_pipe.h"
Minggang Wangb9f3fa92021-07-01 15:30:3126#include "third_party/blink/public/mojom/navigation/navigation_params.mojom.h"
Lukasz Anforowicz445abd42019-01-25 21:27:2727
28namespace content {
29
30namespace {
31
Daniel Cheng247bd932020-06-04 20:09:2532// Validates that |received_token| is non-null iff associated with a blob: URL.
33bool VerifyBlobToken(
34 int process_id,
35 const mojo::PendingRemote<blink::mojom::BlobURLToken>& received_token,
36 const GURL& received_url) {
Lukasz Anforowicz445abd42019-01-25 21:27:2737 DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id);
Lukasz Anforowicz445abd42019-01-25 21:27:2738
Daniel Cheng13251e52020-11-25 17:41:2339 if (received_token.is_valid()) {
Lukasz Anforowicz445abd42019-01-25 21:27:2740 if (!received_url.SchemeIsBlob()) {
41 bad_message::ReceivedBadMessage(
42 process_id, bad_message::BLOB_URL_TOKEN_FOR_NON_BLOB_URL);
43 return false;
44 }
45 }
46
Lukasz Anforowicz445abd42019-01-25 21:27:2747 return true;
48}
49
Rakina Zata Amnifad6ad4b2023-08-23 02:22:3650bool VerifyInitiatorOrigin(
51 int process_id,
52 const url::Origin& initiator_origin,
Rakina Zata Amni60131052023-09-05 22:33:5453 const RenderFrameHostImpl* current_rfh = nullptr,
Rakina Zata Amnifad6ad4b2023-08-23 02:22:3654 GURL* navigation_url = nullptr,
Arthur Sonzognic686e8f2024-01-11 08:36:3755 std::optional<blink::LocalFrameToken>* initiator_frame_token = nullptr) {
Alex Moshchuk4043b732024-06-14 06:02:3956 // TODO(crbug.com/40109437): Ideally, origin verification should be performed
57 // even if `initiator_origin` is opaque, to ensure that the precursor origin
58 // matches the process lock. However, there are a couple of cases where this
59 // doesn't yet work, which are documented and skipped below.
60 if (initiator_origin.opaque()) {
61 // TODO(alexmos): This used to allow all opaque origins; this behavior is
62 // now behind a kill switch and should be removed once the rollout in M128
63 // is complete.
64 if (!base::FeatureList::IsEnabled(
65 features::kAdditionalOpaqueOriginEnforcements)) {
66 return true;
67 }
68
69 // Reloads initiated from error pages may currently lead to a precursor
70 // mismatch, since the error page loads with an opaque origin with the
71 // original URL's origin as its precursor, which may not match the error
72 // page's process lock. This is seen in the following
73 // RenderFrameHostManagerTest tests:
74 // 1. ErrorPageNavigationReload:
75 // - renderer origin lock = chrome-error://chromewebdata/
76 // - precursor of initiator origin = http://127.0.0.1:.../
77 // 2. ErrorPageNavigationReload_InSubframe_BlockedByClient
78 // - renderer origin lock = http://b.com:.../
79 // - precursor of initiator origin = http://c.com:.../
80 if (current_rfh && current_rfh->IsErrorDocument()) {
81 return true;
82 }
83
84 // Certain (e.g., data:) navigations in subframes of MHTML documents may
85 // have precursor origins that do not match the process lock of the MHTML
86 // document. This is seen in NavigationMhtmlBrowserTest.DataIframe, where:
87 // - renderer origin lock = { file:/// sandboxed }
88 // - precursor of initiator origin = http://8.8.8.8/
89 // Note that RenderFrameHostImpl::CanCommitOriginAndUrl() similarly allows
90 // such navigations to commit, and it also ensures that they can only commit
91 // in the main frame MHTML document's process.
92 if (current_rfh && current_rfh->IsMhtmlSubframe()) {
93 return true;
94 }
95 }
Lukasz Anforowicz56211d452019-02-05 18:05:5196
97 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Alex Moshchuk99c22682024-03-28 22:43:4698 if (!policy->HostsOrigin(process_id, initiator_origin)) {
Rakina Zata Amnifad6ad4b2023-08-23 02:22:3699 if (navigation_url) {
100 static auto* const navigation_url_key =
101 base::debug::AllocateCrashKeyString(
102 "navigation_url", base::debug::CrashKeySize::Size256);
103 base::debug::SetCrashKeyString(
104 navigation_url_key,
105 navigation_url->DeprecatedGetOriginAsURL().spec());
106 }
Rakina Zata Amnif6902af2023-11-09 00:07:58107 if (initiator_frame_token && initiator_frame_token->has_value()) {
Rakina Zata Amnifad6ad4b2023-08-23 02:22:36108 if (RenderFrameHostImpl* initiator_render_frame_host =
109 RenderFrameHostImpl::FromFrameToken(
110 process_id, initiator_frame_token->value())) {
111 static auto* const initiator_rfh_origin_key =
112 base::debug::AllocateCrashKeyString(
113 "initiator_rfh_origin", base::debug::CrashKeySize::Size256);
114 base::debug::SetCrashKeyString(
115 initiator_rfh_origin_key,
116 initiator_render_frame_host->GetLastCommittedOrigin()
117 .GetDebugString());
118 }
119 }
120
121 if (current_rfh) {
Helmut Januschka42c832072025-03-02 01:13:12122 auto bool_to_crash_key = [](bool b) { return base::ToString(b); };
Rakina Zata Amnifad6ad4b2023-08-23 02:22:36123 static auto* const is_main_frame_key =
124 base::debug::AllocateCrashKeyString(
125 "is_main_frame", base::debug::CrashKeySize::Size32);
126 base::debug::SetCrashKeyString(
127 is_main_frame_key, bool_to_crash_key(current_rfh->is_main_frame()));
128
129 static auto* const is_outermost_frame_key =
130 base::debug::AllocateCrashKeyString(
131 "is_outermost_frame", base::debug::CrashKeySize::Size32);
132 base::debug::SetCrashKeyString(
133 is_outermost_frame_key,
134 bool_to_crash_key(current_rfh->IsOutermostMainFrame()));
135
136 static auto* const is_on_initial_empty_document_key =
137 base::debug::AllocateCrashKeyString(
138 "is_on_initial_empty_doc", base::debug::CrashKeySize::Size32);
139 base::debug::SetCrashKeyString(
140 is_on_initial_empty_document_key,
141 bool_to_crash_key(
142 current_rfh->frame_tree_node()->is_on_initial_empty_document()));
143
144 static auto* const last_committed_origin_key =
145 base::debug::AllocateCrashKeyString(
146 "last_committed_origin", base::debug::CrashKeySize::Size256);
147 base::debug::SetCrashKeyString(
148 last_committed_origin_key,
149 current_rfh->GetLastCommittedOrigin().GetDebugString());
150
151 if (current_rfh->GetParentOrOuterDocumentOrEmbedder()) {
152 static auto* const parent_etc_origin_key =
153 base::debug::AllocateCrashKeyString(
154 "parent_etc_origin", base::debug::CrashKeySize::Size256);
155 base::debug::SetCrashKeyString(
156 parent_etc_origin_key,
157 current_rfh->GetParentOrOuterDocumentOrEmbedder()
158 ->GetLastCommittedOrigin()
159 .GetDebugString());
160 }
161
162 if (FrameTreeNode* opener = current_rfh->frame_tree_node()->opener()) {
163 static auto* const opener_origin_key =
164 base::debug::AllocateCrashKeyString(
165 "opener_origin", base::debug::CrashKeySize::Size256);
166 base::debug::SetCrashKeyString(opener_origin_key,
167 opener->current_frame_host()
168 ->GetLastCommittedOrigin()
169 .GetDebugString());
170 }
171 }
172
Lukasz Anforowicz56211d452019-02-05 18:05:51173 bad_message::ReceivedBadMessage(process_id,
174 bad_message::INVALID_INITIATOR_ORIGIN);
175 return false;
176 }
177
178 return true;
179}
180
Lukasz Anforowicz445abd42019-01-25 21:27:27181} // namespace
182
Sharon Yang1987cec2023-03-24 23:40:19183bool VerifyDownloadUrlParams(RenderProcessHost* process,
Daniel Cheng247bd932020-06-04 20:09:25184 const blink::mojom::DownloadURLParams& params) {
Lukasz Anforowicz62442712019-10-07 17:16:52185 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Sharon Yang1987cec2023-03-24 23:40:19186 CHECK(process);
Emily Andrewsd15fd762024-12-10 20:41:54187 int process_id = process->GetDeprecatedID();
Lukasz Anforowicz445abd42019-01-25 21:27:27188
Daniel Cheng247bd932020-06-04 20:09:25189 // Verifies |params.blob_url_token| is appropriately set.
190 if (!VerifyBlobToken(process_id, params.blob_url_token, params.url))
Lukasz Anforowicz445abd42019-01-25 21:27:27191 return false;
Lukasz Anforowicz445abd42019-01-25 21:27:27192
Lukasz Anforowicz56211d452019-02-05 18:05:51193 // Verify |params.initiator_origin|.
Daniel Cheng247bd932020-06-04 20:09:25194 if (params.initiator_origin &&
195 !VerifyInitiatorOrigin(process_id, *params.initiator_origin))
Lukasz Anforowicz56211d452019-02-05 18:05:51196 return false;
Lukasz Anforowicz445abd42019-01-25 21:27:27197
Daniel Cheng247bd932020-06-04 20:09:25198 // If |params.url| is not set, this must be a large data URL being passed
199 // through |params.data_url_blob|.
200 if (!params.url.is_valid() && !params.data_url_blob.is_valid())
201 return false;
Min Qin3861ed02019-10-17 23:12:51202
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29203 // Verification succeeded.
Lukasz Anforowicz445abd42019-01-25 21:27:27204 return true;
205}
206
Yao Xiao1ac702d2022-06-08 17:20:49207bool VerifyOpenURLParams(RenderFrameHostImpl* current_rfh,
Sharon Yang59197b2a2023-03-24 17:49:24208 RenderProcessHost* process,
Yeunjoo Choi9232ea32021-03-08 16:25:17209 const blink::mojom::OpenURLParamsPtr& params,
Lukasz Anforowicz445abd42019-01-25 21:27:27210 GURL* out_validated_url,
211 scoped_refptr<network::SharedURLLoaderFactory>*
212 out_blob_url_loader_factory) {
213 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Yao Xiao1ac702d2022-06-08 17:20:49214 DCHECK(current_rfh);
Sharon Yang59197b2a2023-03-24 17:49:24215 DCHECK(process);
Lukasz Anforowicz445abd42019-01-25 21:27:27216 DCHECK(out_validated_url);
217 DCHECK(out_blob_url_loader_factory);
Emily Andrewsd15fd762024-12-10 20:41:54218 int process_id = process->GetDeprecatedID();
Lukasz Anforowicz445abd42019-01-25 21:27:27219
220 // Verify |params.url| and populate |out_validated_url|.
Gyuyoung Kim0028790a2020-06-26 00:09:00221 *out_validated_url = params->url;
Lukasz Anforowicz445abd42019-01-25 21:27:27222 process->FilterURL(false, out_validated_url);
223
224 // Verify |params.blob_url_token| and populate |out_blob_url_loader_factory|.
Daniel Cheng13251e52020-11-25 17:41:23225 if (!VerifyBlobToken(process_id, params->blob_url_token, params->url))
Lukasz Anforowicz445abd42019-01-25 21:27:27226 return false;
Daniel Cheng247bd932020-06-04 20:09:25227
Daniel Cheng13251e52020-11-25 17:41:23228 if (params->blob_url_token.is_valid()) {
Lukasz Anforowicz445abd42019-01-25 21:27:27229 *out_blob_url_loader_factory =
230 ChromeBlobStorageContext::URLLoaderFactoryForToken(
Sharon Yang59197b2a2023-03-24 17:49:24231 process->GetStoragePartition(), std::move(params->blob_url_token));
Lukasz Anforowicz445abd42019-01-25 21:27:27232 }
233
Lukasz Anforowiczf81687c2019-10-25 16:16:39234 // Verify |params.post_body|.
Lukasz Anforowicz445abd42019-01-25 21:27:27235 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Sharon Yang59197b2a2023-03-24 17:49:24236 if (!policy->CanReadRequestBody(process, params->post_body)) {
Lukasz Anforowicz445abd42019-01-25 21:27:27237 bad_message::ReceivedBadMessage(process,
238 bad_message::ILLEGAL_UPLOAD_PARAMS);
239 return false;
240 }
241
Lukasz Anforowicz56211d452019-02-05 18:05:51242 // Verify |params.initiator_origin|.
Rakina Zata Amnifad6ad4b2023-08-23 02:22:36243 if (!VerifyInitiatorOrigin(process_id, params->initiator_origin, current_rfh,
244 &params->url, &params->initiator_frame_token)) {
Lukasz Anforowicz56211d452019-02-05 18:05:51245 return false;
Rakina Zata Amnifad6ad4b2023-08-23 02:22:36246 }
Lukasz Anforowicz445abd42019-01-25 21:27:27247
W. James MacLean8be423a2023-03-31 21:35:52248 if (params->initiator_base_url) {
249 // `initiator_base_url` should only be defined for about:blank and
250 // about:srcdoc navigations, and should never be an empty GURL (if it is not
251 // nullopt).
252 if (params->initiator_base_url->is_empty() ||
253 !(out_validated_url->IsAboutBlank() ||
254 out_validated_url->IsAboutSrcdoc())) {
255 return false;
256 }
257 }
258
Yao Xiao1ac702d2022-06-08 17:20:49259 // Verify that the initiator frame can navigate `current_rfh`.
260 if (!VerifyNavigationInitiator(current_rfh, params->initiator_frame_token,
261 process_id)) {
262 return false;
263 }
264
Sergey Poromovdd557c12023-03-01 11:28:45265 if (params->is_container_initiated) {
266 if (!current_rfh->GetParent() ||
267 (current_rfh->GetParent()->GetFrameToken() !=
268 params->initiator_frame_token)) {
269 mojo::ReportBadMessage(
270 "container initiated navigation from non-parent process");
271 return false;
272 }
273 }
274
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29275 // Verification succeeded.
276 return true;
277}
278
Lucas Furukawa Gadanief8290a2019-07-29 20:27:51279bool VerifyBeginNavigationCommonParams(
Chris Fredrickson73ba7492023-02-21 17:08:06280 const RenderFrameHostImpl& current_rfh,
Rakina Zata Amni60131052023-09-05 22:33:54281 blink::mojom::CommonNavigationParams* common_params,
Arthur Sonzognic686e8f2024-01-11 08:36:37282 std::optional<blink::LocalFrameToken>& initiator_frame_token) {
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29283 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29284 DCHECK(common_params);
Sharon Yang1987cec2023-03-24 23:40:19285 RenderProcessHost* process = current_rfh.GetProcess();
Emily Andrewsd15fd762024-12-10 20:41:54286 int process_id = process->GetDeprecatedID();
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29287
288 // Verify (and possibly rewrite) |url|.
289 process->FilterURL(false, &common_params->url);
290 if (common_params->url.SchemeIs(kChromeErrorScheme)) {
291 mojo::ReportBadMessage("Renderer cannot request error page URLs directly");
292 return false;
293 }
294
Lukasz Anforowiczf81687c2019-10-25 16:16:39295 // Verify |post_data|.
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29296 auto* policy = ChildProcessSecurityPolicyImpl::GetInstance();
Sharon Yang59197b2a2023-03-24 17:49:24297 if (!policy->CanReadRequestBody(process, common_params->post_data)) {
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29298 bad_message::ReceivedBadMessage(process,
299 bad_message::ILLEGAL_UPLOAD_PARAMS);
300 return false;
301 }
302
Mike West9286cf12019-04-01 13:44:58303 // Verify |transition| is webby.
Minggang Wangb9f3fa92021-07-01 15:30:31304 if (!PageTransitionIsWebTriggerable(
305 ui::PageTransitionFromInt(common_params->transition))) {
Mike West9286cf12019-04-01 13:44:58306 bad_message::ReceivedBadMessage(
307 process, bad_message::RFHI_BEGIN_NAVIGATION_NON_WEBBY_TRANSITION);
308 return false;
309 }
310
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29311 // Verify |initiator_origin|.
Lukasz Anforowicz2294ca82019-02-25 18:26:05312 if (!common_params->initiator_origin.has_value()) {
313 bad_message::ReceivedBadMessage(
314 process, bad_message::RFHI_BEGIN_NAVIGATION_MISSING_INITIATOR_ORIGIN);
315 return false;
316 }
Rakina Zata Amni60131052023-09-05 22:33:54317 if (!VerifyInitiatorOrigin(
318 process_id, common_params->initiator_origin.value(), &current_rfh,
319 &common_params->url, &initiator_frame_token)) {
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29320 return false;
321 }
322
323 // Verify |base_url_for_data_url|.
324 if (!common_params->base_url_for_data_url.is_empty()) {
325 // Kills the process. http://crbug.com/726142
326 bad_message::ReceivedBadMessage(
327 process, bad_message::RFH_BASE_URL_FOR_DATA_URL_SPECIFIED);
328 return false;
329 }
330
W. James MacLeanead730a92024-06-21 18:34:20331 // Verify |initiator_base_url|. The value is allowed to be nullopt, but if it
332 // isn't then it's required to be non-empty (the renderer is supposed to
333 // guarantee this). If this condition isn't met, CHECK in NavigationRequest's
334 // constructor will fail.
335 if (common_params->initiator_base_url &&
336 common_params->initiator_base_url->is_empty()) {
337 bad_message::ReceivedBadMessage(
338 process, bad_message::RFH_INITIATOR_BASE_URL_IS_EMPTY);
339 return false;
340 }
341
danakj837b1c42020-11-25 23:59:54342 // Asynchronous (browser-controlled, but) renderer-initiated navigations can
343 // not be same-document. Allowing this incorrectly could have us try to
344 // navigate an existing document to a different site.
345 if (NavigationTypeUtils::IsSameDocument(common_params->navigation_type))
346 return false;
347
Lukasz Anforowicz3cfc1efd2019-02-22 02:29:29348 // Verification succeeded.
Lukasz Anforowicz445abd42019-01-25 21:27:27349 return true;
350}
351
Yao Xiao1ac702d2022-06-08 17:20:49352bool VerifyNavigationInitiator(
353 RenderFrameHostImpl* current_rfh,
Arthur Sonzognic686e8f2024-01-11 08:36:37354 const std::optional<blink::LocalFrameToken>& initiator_frame_token,
Yao Xiao1ac702d2022-06-08 17:20:49355 int initiator_process_id) {
356 // Verify that a frame inside a fenced frame cannot navigate its ancestors,
357 // unless the frame being navigated is the outermost main frame.
358 if (current_rfh->IsOutermostMainFrame())
359 return true;
360
361 if (!initiator_frame_token)
362 return true;
363
364 RenderFrameHostImpl* initiator_render_frame_host =
365 RenderFrameHostImpl::FromFrameToken(initiator_process_id,
366 initiator_frame_token.value());
367 if (!initiator_render_frame_host)
368 return true;
369
370 // Verify that a frame cannot navigate a frame with a different fenced frame
371 // nonce, unless the navigating frame is a fenced frame root and its owner
372 // frame has the same fenced frame nonce as the initiator frame (e.g. in a
373 // A(A1,A2(FF)) setup, A, A1, and A2 are all allowed to navigate FF).
Arthur Sonzognic686e8f2024-01-11 08:36:37374 std::optional<base::UnguessableToken> initiator_fenced_frame_nonce =
Garrett Tanzer34cb92fe2022-09-28 17:50:54375 initiator_render_frame_host->frame_tree_node()->GetFencedFrameNonce();
Yao Xiao1ac702d2022-06-08 17:20:49376 if (initiator_fenced_frame_nonce !=
Garrett Tanzer34cb92fe2022-09-28 17:50:54377 current_rfh->frame_tree_node()->GetFencedFrameNonce()) {
Yao Xiao1ac702d2022-06-08 17:20:49378 if (!current_rfh->IsFencedFrameRoot() ||
379 current_rfh->frame_tree_node()
380 ->GetParentOrOuterDocument()
381 ->frame_tree_node()
Garrett Tanzer34cb92fe2022-09-28 17:50:54382 ->GetFencedFrameNonce() != initiator_fenced_frame_nonce) {
Yao Xiao1ac702d2022-06-08 17:20:49383 mojo::ReportBadMessage(
384 "The fenced frame nonces of initiator and current frame don't match, "
385 "nor is the current frame a fenced frame root whose owner frame has "
386 "the same fenced frame nonce as the initiator frame.");
387 return false;
388 }
389 }
390
391 if (!initiator_render_frame_host->IsNestedWithinFencedFrame())
392 return true;
393
394 FrameTreeNode* node = initiator_render_frame_host->frame_tree_node();
395 if (node == current_rfh->frame_tree_node())
396 return true;
397
398 while (node) {
399 node = node->parent() ? node->parent()->frame_tree_node() : nullptr;
400
401 if (node == current_rfh->frame_tree_node()) {
402 mojo::ReportBadMessage(
403 "A frame in a fenced frame tree cannot navigate an ancestor frame.");
404 return false;
405 }
406 }
407
408 return true;
409}
410
Lukasz Anforowicz445abd42019-01-25 21:27:27411} // namespace content