1 /*
2  * Copyright (C) 2009 Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *     * Redistributions in binary form must reproduce the above
11  * copyright notice, this list of conditions and the following disclaimer
12  * in the documentation and/or other materials provided with the
13  * distribution.
14  *     * Neither the name of Google Inc. nor the names of its
15  * contributors may be used to endorse or promote products derived from
16  * this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "third_party/blink/renderer/bindings/core/v8/binding_security.h"
32 
33 #include "third_party/blink/public/mojom/feature_policy/feature_policy.mojom-blink.h"
34 #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
35 #include "third_party/blink/renderer/bindings/core/v8/v8_location.h"
36 #include "third_party/blink/renderer/bindings/core/v8/v8_window.h"
37 #include "third_party/blink/renderer/core/dom/document.h"
38 #include "third_party/blink/renderer/core/frame/dom_window.h"
39 #include "third_party/blink/renderer/core/frame/local_dom_window.h"
40 #include "third_party/blink/renderer/core/frame/local_frame.h"
41 #include "third_party/blink/renderer/core/frame/location.h"
42 #include "third_party/blink/renderer/core/frame/settings.h"
43 #include "third_party/blink/renderer/core/frame/web_feature.h"
44 #include "third_party/blink/renderer/core/html/html_frame_element_base.h"
45 #include "third_party/blink/renderer/platform/bindings/exception_state.h"
46 #include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
47 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
48 
49 namespace blink {
50 
51 namespace {
52 
53 // Documents that have the same WindowAgentFactory should be able to
54 // share data with each other if they have the same Agent and are
55 // SameOriginDomain.
IsSameWindowAgentFactory(Document * doc1,Document * doc2)56 bool IsSameWindowAgentFactory(Document* doc1, Document* doc2) {
57   return doc1->GetWindowAgentFactory() == doc2->GetWindowAgentFactory();
58 }
59 
60 }  // namespace
61 
Init()62 void BindingSecurity::Init() {
63   BindingSecurityForPlatform::SetShouldAllowAccessToV8ContextWithExceptionState(
64       ShouldAllowAccessToV8Context);
65   BindingSecurityForPlatform::
66       SetShouldAllowAccessToV8ContextWithErrorReportOption(
67           ShouldAllowAccessToV8Context);
68   BindingSecurityForPlatform::SetShouldAllowWrapperCreationOrThrowException(
69       ShouldAllowWrapperCreationOrThrowException);
70   BindingSecurityForPlatform::SetRethrowWrapperCreationException(
71       RethrowWrapperCreationException);
72 }
73 
74 namespace {
75 
ReportOrThrowSecurityError(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,DOMWindow::CrossDocumentAccessPolicy cross_document_access,ExceptionState & exception_state)76 void ReportOrThrowSecurityError(
77     const LocalDOMWindow* accessing_window,
78     const DOMWindow* target_window,
79     DOMWindow::CrossDocumentAccessPolicy cross_document_access,
80     ExceptionState& exception_state) {
81   if (target_window) {
82     exception_state.ThrowSecurityError(
83         target_window->SanitizedCrossDomainAccessErrorMessage(
84             accessing_window, cross_document_access),
85         target_window->CrossDomainAccessErrorMessage(accessing_window,
86                                                      cross_document_access));
87   } else {
88     exception_state.ThrowSecurityError("Cross origin access was denied.");
89   }
90 }
91 
ReportOrThrowSecurityError(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,DOMWindow::CrossDocumentAccessPolicy cross_document_access,BindingSecurity::ErrorReportOption reporting_option)92 void ReportOrThrowSecurityError(
93     const LocalDOMWindow* accessing_window,
94     const DOMWindow* target_window,
95     DOMWindow::CrossDocumentAccessPolicy cross_document_access,
96     BindingSecurity::ErrorReportOption reporting_option) {
97   if (reporting_option == BindingSecurity::ErrorReportOption::kDoNotReport)
98     return;
99 
100   if (accessing_window && target_window) {
101     accessing_window->PrintErrorMessage(
102         target_window->CrossDomainAccessErrorMessage(accessing_window,
103                                                      cross_document_access));
104   } else if (accessing_window) {
105     accessing_window->PrintErrorMessage("Cross origin access was denied.");
106   } else {
107     // Nowhere to report the error.
108   }
109 }
110 
CanAccessWindowInternal(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,DOMWindow::CrossDocumentAccessPolicy * cross_document_access)111 bool CanAccessWindowInternal(
112     const LocalDOMWindow* accessing_window,
113     const DOMWindow* target_window,
114     DOMWindow::CrossDocumentAccessPolicy* cross_document_access) {
115   SECURITY_CHECK(!(target_window && target_window->GetFrame()) ||
116                  target_window == target_window->GetFrame()->DomWindow());
117   DCHECK_EQ(DOMWindow::CrossDocumentAccessPolicy::kAllowed,
118             *cross_document_access);
119 
120   // It's important to check that target_window is a LocalDOMWindow: it's
121   // possible for a remote frame and local frame to have the same security
122   // origin, depending on the model being used to allocate Frames between
123   // processes. See https://crbug.com/601629.
124   const auto* local_target_window = DynamicTo<LocalDOMWindow>(target_window);
125   if (!(accessing_window && local_target_window))
126     return false;
127 
128   const SecurityOrigin* accessing_origin =
129       accessing_window->document()->GetSecurityOrigin();
130 
131   SecurityOrigin::AccessResultDomainDetail detail;
132   bool can_access = accessing_origin->CanAccess(
133       local_target_window->document()->GetSecurityOrigin(), detail);
134   if (detail ==
135           SecurityOrigin::AccessResultDomainDetail::kDomainSetByOnlyOneOrigin ||
136       detail ==
137           SecurityOrigin::AccessResultDomainDetail::kDomainMatchNecessary ||
138       detail == SecurityOrigin::AccessResultDomainDetail::kDomainMismatch) {
139     UseCounter::Count(
140         accessing_window->document(),
141         can_access ? WebFeature::kDocumentDomainEnabledCrossOriginAccess
142                    : WebFeature::kDocumentDomainBlockedCrossOriginAccess);
143   }
144   if (!can_access) {
145     // Ensure that if we got a cluster mismatch that it was due to a feature
146     // policy being enabled and not a logic bug.
147     if (detail == SecurityOrigin::AccessResultDomainDetail::
148                       kDomainNotRelevantAgentClusterMismatch) {
149       // Assert that because the agent clusters are different than the
150       // WindowAgentFactories must also be different.
151       SECURITY_CHECK(!IsSameWindowAgentFactory(
152           accessing_window->document(), local_target_window->document()));
153 
154       *cross_document_access =
155           DOMWindow::CrossDocumentAccessPolicy::kDisallowed;
156     }
157     return false;
158   }
159 
160   // Notify the loader's client if the initial document has been accessed.
161   LocalFrame* target_frame = local_target_window->GetFrame();
162   if (target_frame &&
163       target_frame->Loader().StateMachine()->IsDisplayingInitialEmptyDocument())
164     target_frame->Loader().DidAccessInitialDocument();
165 
166   return true;
167 }
168 
169 template <typename ExceptionStateOrErrorReportOption>
CanAccessWindow(const LocalDOMWindow * accessing_window,const DOMWindow * target_window,ExceptionStateOrErrorReportOption & error_report)170 bool CanAccessWindow(const LocalDOMWindow* accessing_window,
171                      const DOMWindow* target_window,
172                      ExceptionStateOrErrorReportOption& error_report) {
173   DOMWindow::CrossDocumentAccessPolicy cross_document_access =
174       DOMWindow::CrossDocumentAccessPolicy::kAllowed;
175   if (CanAccessWindowInternal(accessing_window, target_window,
176                               &cross_document_access))
177     return true;
178 
179   ReportOrThrowSecurityError(accessing_window, target_window,
180                              cross_document_access, error_report);
181   return false;
182 }
183 
FindWindow(v8::Isolate * isolate,const WrapperTypeInfo * type,v8::Local<v8::Object> holder)184 DOMWindow* FindWindow(v8::Isolate* isolate,
185                       const WrapperTypeInfo* type,
186                       v8::Local<v8::Object> holder) {
187   if (V8Window::GetWrapperTypeInfo()->Equals(type))
188     return V8Window::ToImpl(holder);
189 
190   if (V8Location::GetWrapperTypeInfo()->Equals(type))
191     return V8Location::ToImpl(holder)->DomWindow();
192 
193   // This function can handle only those types listed above.
194   NOTREACHED();
195   return nullptr;
196 }
197 
198 }  // namespace
199 
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const DOMWindow * target,ExceptionState & exception_state)200 bool BindingSecurity::ShouldAllowAccessTo(
201     const LocalDOMWindow* accessing_window,
202     const DOMWindow* target,
203     ExceptionState& exception_state) {
204   DCHECK(target);
205 
206   // TODO(https://crbug.com/723057): This is intended to match the legacy
207   // behavior of when access checks revolved around Frame pointers rather than
208   // DOMWindow pointers. This prevents web-visible behavior changes, since the
209   // previous implementation had to follow the back pointer to the Frame, and
210   // would have to early return when it was null.
211   if (!target->GetFrame())
212     return false;
213   bool can_access = CanAccessWindow(accessing_window, target, exception_state);
214 
215   if (!can_access && accessing_window) {
216     UseCounter::Count(accessing_window->document(),
217                       WebFeature::kCrossOriginPropertyAccess);
218     if (target->opener() == accessing_window) {
219       UseCounter::Count(accessing_window->document(),
220                         WebFeature::kCrossOriginPropertyAccessFromOpener);
221     }
222   }
223 
224   return can_access;
225 }
226 
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const DOMWindow * target,ErrorReportOption reporting_option)227 bool BindingSecurity::ShouldAllowAccessTo(
228     const LocalDOMWindow* accessing_window,
229     const DOMWindow* target,
230     ErrorReportOption reporting_option) {
231   DCHECK(target);
232 
233   // TODO(https://crbug.com/723057): This is intended to match the legacy
234   // behavior of when access checks revolved around Frame pointers rather than
235   // DOMWindow pointers. This prevents web-visible behavior changes, since the
236   // previous implementation had to follow the back pointer to the Frame, and
237   // would have to early return when it was null.
238   if (!target->GetFrame())
239     return false;
240 
241   bool can_access = CanAccessWindow(accessing_window, target, reporting_option);
242 
243   if (!can_access && accessing_window) {
244     UseCounter::Count(accessing_window->document(),
245                       WebFeature::kCrossOriginPropertyAccess);
246     if (target->opener() == accessing_window) {
247       UseCounter::Count(accessing_window->document(),
248                         WebFeature::kCrossOriginPropertyAccessFromOpener);
249     }
250   }
251 
252   return can_access;
253 }
254 
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Location * target,ExceptionState & exception_state)255 bool BindingSecurity::ShouldAllowAccessTo(
256     const LocalDOMWindow* accessing_window,
257     const Location* target,
258     ExceptionState& exception_state) {
259   DCHECK(target);
260 
261   // TODO(https://crbug.com/723057): This is intended to match the legacy
262   // behavior of when access checks revolved around Frame pointers rather than
263   // DOMWindow pointers. This prevents web-visible behavior changes, since the
264   // previous implementation had to follow the back pointer to the Frame, and
265   // would have to early return when it was null.
266   if (!target->DomWindow()->GetFrame())
267     return false;
268 
269   bool can_access =
270       CanAccessWindow(accessing_window, target->DomWindow(), exception_state);
271 
272   if (!can_access && accessing_window) {
273     UseCounter::Count(accessing_window->document(),
274                       WebFeature::kCrossOriginPropertyAccess);
275     if (target->DomWindow()->opener() == accessing_window) {
276       UseCounter::Count(accessing_window->document(),
277                         WebFeature::kCrossOriginPropertyAccessFromOpener);
278     }
279   }
280 
281   return can_access;
282 }
283 
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Location * target,ErrorReportOption reporting_option)284 bool BindingSecurity::ShouldAllowAccessTo(
285     const LocalDOMWindow* accessing_window,
286     const Location* target,
287     ErrorReportOption reporting_option) {
288   DCHECK(target);
289 
290   // TODO(https://crbug.com/723057): This is intended to match the legacy
291   // behavior of when access checks revolved around Frame pointers rather than
292   // DOMWindow pointers. This prevents web-visible behavior changes, since the
293   // previous implementation had to follow the back pointer to the Frame, and
294   // would have to early return when it was null.
295   if (!target->DomWindow()->GetFrame())
296     return false;
297 
298   bool can_access =
299       CanAccessWindow(accessing_window, target->DomWindow(), reporting_option);
300 
301   if (!can_access && accessing_window) {
302     UseCounter::Count(accessing_window->document(),
303                       WebFeature::kCrossOriginPropertyAccess);
304     if (target->DomWindow()->opener() == accessing_window) {
305       UseCounter::Count(accessing_window->document(),
306                         WebFeature::kCrossOriginPropertyAccessFromOpener);
307     }
308   }
309 
310   return can_access;
311 }
312 
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Node * target,ExceptionState & exception_state)313 bool BindingSecurity::ShouldAllowAccessTo(
314     const LocalDOMWindow* accessing_window,
315     const Node* target,
316     ExceptionState& exception_state) {
317   if (!target)
318     return false;
319   return CanAccessWindow(accessing_window, target->GetDocument().domWindow(),
320                          exception_state);
321 }
322 
ShouldAllowAccessTo(const LocalDOMWindow * accessing_window,const Node * target,ErrorReportOption reporting_option)323 bool BindingSecurity::ShouldAllowAccessTo(
324     const LocalDOMWindow* accessing_window,
325     const Node* target,
326     ErrorReportOption reporting_option) {
327   if (!target)
328     return false;
329   return CanAccessWindow(accessing_window, target->GetDocument().domWindow(),
330                          reporting_option);
331 }
332 
ShouldAllowAccessToFrame(const LocalDOMWindow * accessing_window,const Frame * target,ExceptionState & exception_state)333 bool BindingSecurity::ShouldAllowAccessToFrame(
334     const LocalDOMWindow* accessing_window,
335     const Frame* target,
336     ExceptionState& exception_state) {
337   if (!target || !target->GetSecurityContext())
338     return false;
339   return CanAccessWindow(accessing_window, target->DomWindow(),
340                          exception_state);
341 }
342 
ShouldAllowAccessToFrame(const LocalDOMWindow * accessing_window,const Frame * target,ErrorReportOption reporting_option)343 bool BindingSecurity::ShouldAllowAccessToFrame(
344     const LocalDOMWindow* accessing_window,
345     const Frame* target,
346     ErrorReportOption reporting_option) {
347   if (!target || !target->GetSecurityContext())
348     return false;
349   return CanAccessWindow(accessing_window, target->DomWindow(),
350                          reporting_option);
351 }
352 
353 namespace {
354 
355 template <typename ExceptionStateOrErrorReportOption>
ShouldAllowAccessToV8ContextInternal(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> target_context,ExceptionStateOrErrorReportOption & error_report)356 bool ShouldAllowAccessToV8ContextInternal(
357     v8::Local<v8::Context> accessing_context,
358     v8::Local<v8::Context> target_context,
359     ExceptionStateOrErrorReportOption& error_report) {
360   // Fast path for the most likely case.
361   if (accessing_context == target_context)
362     return true;
363 
364   // Workers and worklets do not support multiple contexts, so both of
365   // |accessing_context| and |target_context| must be windows at this point.
366 
367   // remote_object->CreationContext() returns the empty handle. Remote contexts
368   // are unconditionally treated as cross origin.
369   if (target_context.IsEmpty()) {
370     ReportOrThrowSecurityError(ToLocalDOMWindow(accessing_context), nullptr,
371                                DOMWindow::CrossDocumentAccessPolicy::kAllowed,
372                                error_report);
373     return false;
374   }
375 
376   LocalFrame* target_frame = ToLocalFrameIfNotDetached(target_context);
377   // TODO(dcheng): Why doesn't this code just use DOMWindows throughout? Can't
378   // we just always use ToLocalDOMWindow(context)?
379   if (!target_frame) {
380     // Sandbox detached frames - they can't create cross origin objects.
381     LocalDOMWindow* accessing_window = ToLocalDOMWindow(accessing_context);
382     LocalDOMWindow* target_window = ToLocalDOMWindow(target_context);
383 
384     // TODO(https://crbug.com/723057): This is tricky: this intentionally uses
385     // the internal CanAccessWindow() helper rather than ShouldAllowAccessTo().
386     // ShouldAllowAccessTo() unconditionally denies access if the DOMWindow is
387     // not attached to a Frame, but this code is intended for handling the
388     // detached DOMWindow case.
389     return CanAccessWindow(accessing_window, target_window, error_report);
390   }
391 
392   const DOMWrapperWorld& accessing_world =
393       DOMWrapperWorld::World(accessing_context);
394   const DOMWrapperWorld& target_world = DOMWrapperWorld::World(target_context);
395   CHECK_EQ(accessing_world.GetWorldId(), target_world.GetWorldId());
396 
397   return !accessing_world.IsMainWorld() ||
398          BindingSecurity::ShouldAllowAccessToFrame(
399              ToLocalDOMWindow(accessing_context), target_frame, error_report);
400 }
401 
402 }  // namespace
403 
ShouldAllowAccessToV8Context(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> target_context,ExceptionState & exception_state)404 bool BindingSecurity::ShouldAllowAccessToV8Context(
405     v8::Local<v8::Context> accessing_context,
406     v8::Local<v8::Context> target_context,
407     ExceptionState& exception_state) {
408   return ShouldAllowAccessToV8ContextInternal(accessing_context, target_context,
409                                               exception_state);
410 }
411 
ShouldAllowAccessToV8Context(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> target_context,ErrorReportOption reporting_option)412 bool BindingSecurity::ShouldAllowAccessToV8Context(
413     v8::Local<v8::Context> accessing_context,
414     v8::Local<v8::Context> target_context,
415     ErrorReportOption reporting_option) {
416   return ShouldAllowAccessToV8ContextInternal(accessing_context, target_context,
417                                               reporting_option);
418 }
419 
ShouldAllowWrapperCreationOrThrowException(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> creation_context,const WrapperTypeInfo * wrapper_type_info)420 bool BindingSecurity::ShouldAllowWrapperCreationOrThrowException(
421     v8::Local<v8::Context> accessing_context,
422     v8::Local<v8::Context> creation_context,
423     const WrapperTypeInfo* wrapper_type_info) {
424   // Fast path for the most likely case.
425   if (accessing_context == creation_context)
426     return true;
427 
428   // According to
429   // https://html.spec.whatwg.org/C/#security-location,
430   // cross-origin script access to a few properties of Location is allowed.
431   // Location already implements the necessary security checks.
432   if (wrapper_type_info->Equals(V8Location::GetWrapperTypeInfo()))
433     return true;
434 
435   ExceptionState exception_state(accessing_context->GetIsolate(),
436                                  ExceptionState::kConstructionContext,
437                                  wrapper_type_info->interface_name);
438   return ShouldAllowAccessToV8Context(accessing_context, creation_context,
439                                       exception_state);
440 }
441 
RethrowWrapperCreationException(v8::Local<v8::Context> accessing_context,v8::Local<v8::Context> creation_context,const WrapperTypeInfo * wrapper_type_info,v8::Local<v8::Value> cross_context_exception)442 void BindingSecurity::RethrowWrapperCreationException(
443     v8::Local<v8::Context> accessing_context,
444     v8::Local<v8::Context> creation_context,
445     const WrapperTypeInfo* wrapper_type_info,
446     v8::Local<v8::Value> cross_context_exception) {
447   DCHECK(!cross_context_exception.IsEmpty());
448   v8::Isolate* isolate = creation_context->GetIsolate();
449   ExceptionState exception_state(isolate, ExceptionState::kConstructionContext,
450                                  wrapper_type_info->interface_name);
451   if (!ShouldAllowAccessToV8Context(accessing_context, creation_context,
452                                     exception_state)) {
453     // A cross origin exception has turned into a SecurityError.
454     CHECK(exception_state.HadException());
455     return;
456   }
457   exception_state.RethrowV8Exception(cross_context_exception);
458 }
459 
FailedAccessCheckFor(v8::Isolate * isolate,const WrapperTypeInfo * type,v8::Local<v8::Object> holder)460 void BindingSecurity::FailedAccessCheckFor(v8::Isolate* isolate,
461                                            const WrapperTypeInfo* type,
462                                            v8::Local<v8::Object> holder) {
463   DOMWindow* target = FindWindow(isolate, type, holder);
464   // Failing to find a target means something is wrong. Failing to throw an
465   // exception could be a security issue, so just crash.
466   CHECK(target);
467 
468   // TODO(https://crbug.com/723057): This is intended to match the legacy
469   // behavior of when access checks revolved around Frame pointers rather than
470   // DOMWindow pointers. This prevents web-visible behavior changes, since the
471   // previous implementation had to follow the back pointer to the Frame, and
472   // would have to early return when it was null.
473   if (!target->GetFrame())
474     return;
475 
476   auto* local_dom_window = CurrentDOMWindow(isolate);
477   // Determine if the access check failure was because of cross-origin or if the
478   // WindowAgentFactory is different. If the WindowAgentFactories are different
479   // it indicates that the "disallowdocumentaccess" attribute was used on an
480   // iframe somewhere in the ancestor chain so report the error as "restricted"
481   // instead of "cross-origin".
482   DOMWindow::CrossDocumentAccessPolicy cross_document_access =
483       (!target->ToLocalDOMWindow() ||
484        IsSameWindowAgentFactory(local_dom_window->document(),
485                                 target->ToLocalDOMWindow()->document()))
486           ? DOMWindow::CrossDocumentAccessPolicy::kAllowed
487           : DOMWindow::CrossDocumentAccessPolicy::kDisallowed;
488 
489   // TODO(dcheng): Add ContextType, interface name, and property name as
490   // arguments, so the generated exception can be more descriptive.
491   ExceptionState exception_state(isolate, ExceptionState::kUnknownContext,
492                                  nullptr, nullptr);
493   exception_state.ThrowSecurityError(
494       target->SanitizedCrossDomainAccessErrorMessage(local_dom_window,
495                                                      cross_document_access),
496       target->CrossDomainAccessErrorMessage(local_dom_window,
497                                             cross_document_access));
498 }
499 
ShouldAllowNamedAccessTo(const DOMWindow * accessing_window,const DOMWindow * target_window)500 bool BindingSecurity::ShouldAllowNamedAccessTo(
501     const DOMWindow* accessing_window,
502     const DOMWindow* target_window) {
503   const Frame* accessing_frame = accessing_window->GetFrame();
504   DCHECK(accessing_frame);
505   DCHECK(accessing_frame->GetSecurityContext());
506   const SecurityOrigin* accessing_origin =
507       accessing_frame->GetSecurityContext()->GetSecurityOrigin();
508 
509   const Frame* target_frame = target_window->GetFrame();
510   DCHECK(target_frame);
511   DCHECK(target_frame->GetSecurityContext());
512   const SecurityOrigin* target_origin =
513       target_frame->GetSecurityContext()->GetSecurityOrigin();
514   SECURITY_CHECK(!(target_window && target_window->GetFrame()) ||
515                  target_window == target_window->GetFrame()->DomWindow());
516 
517   if (!accessing_origin->CanAccess(target_origin))
518     return false;
519 
520   // Note that there is no need to call back
521   // FrameLoader::didAccessInitialDocument() because |targetWindow| must be
522   // a child window inside iframe or frame and it doesn't have a URL bar,
523   // so there is no need to worry about URL spoofing.
524 
525   return true;
526 }
527 
528 }  // namespace blink
529