Commit f7eb8952 authored by Artur Iunusov's avatar Artur Iunusov Committed by Pier Angelo Vendrame
Browse files

Bug 2010310 - Add CanNavigate() check in RecvLoadURI() and RecvInternalLoad(), r=smaug,tschuster

parent 048987f8
Loading
Loading
Loading
Loading
+82 −0
Original line number Diff line number Diff line
@@ -208,6 +208,8 @@
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/ViewTransition.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/WorkerCommon.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/dom/WorkerRunnable.h"
@@ -2378,6 +2380,86 @@ nsIPrincipal* nsContentUtils::GetAttrTriggeringPrincipal(
  return contentPrin;
}

// static
bool nsContentUtils::CanNavigate(mozilla::dom::BrowsingContext* aSource,
                                 mozilla::dom::BrowsingContext* aTarget,
                                 nsIPrincipal* aDocumentPrincipal,
                                 bool aConsiderOpener) {
  MOZ_DIAGNOSTIC_ASSERT(
      aSource->Group() == aTarget->Group(),
      "Source and target BrowsingContexts must be in the same group");
  if (aSource->Group() != aTarget->Group()) {
    return false;
  }

  auto isFileScheme = [](nsIPrincipal* aPrincipal) -> bool {
    // NOTE: This code previously checked for a file scheme using
    // `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer
    // use GetURI, as it has been deprecated, and it makes more sense to take
    // advantage of the pre-computed origin, which will already use the
    // innermost URI (bug 1810619)
    nsAutoCString origin, scheme;
    return NS_SUCCEEDED(aPrincipal->GetOriginNoSuffix(origin)) &&
           NS_SUCCEEDED(net_ExtractURLScheme(origin, scheme)) &&
           scheme == "file"_ns;
  };

  // A frame can navigate itself and its own root.
  if (aTarget == aSource || aTarget == aSource->Top()) {
    return true;
  }

  // If the target frame doesn't yet have a WindowContext, start checking
  // principals from its direct ancestor instead. It would inherit its principal
  // from this document upon creation.
  dom::WindowContext* initialWc = aTarget->GetCurrentWindowContext();
  if (!initialWc) {
    initialWc = aTarget->GetParentWindowContext();
  }

  // A frame can navigate any frame with a same-origin ancestor.
  bool isFileDocument = isFileScheme(aDocumentPrincipal);
  for (dom::WindowContext* wc = initialWc; wc;
       wc = wc->GetParentWindowContext()) {
    nsIPrincipal* documentPrincipal = nullptr;
    if (XRE_IsParentProcess()) {
      dom::WindowGlobalParent* wgp = wc->Canonical();
      if (!wgp) {
        continue;
      }
      documentPrincipal = wgp->DocumentPrincipal();
    } else {
      dom::WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
      if (!wgc) {
        continue;  // not same-origin.
      }
      documentPrincipal = wgc->DocumentPrincipal();
    }

    if (aDocumentPrincipal->Equals(documentPrincipal)) {
      return true;
    }

    // Not strictly equal, special case if both are file: URIs.
    //
    // file: URIs are considered the same domain for the purpose of frame
    // navigation, regardless of script accessibility (bug 420425).
    if (isFileDocument && isFileScheme(documentPrincipal)) {
      return true;
    }
  }

  // If the target is a top-level document, a frame can navigate it
  // when the source is allowed to navigate the opener
  if (aConsiderOpener && !aTarget->GetParent()) {
    if (RefPtr<dom::BrowsingContext> opener = aTarget->GetOpener()) {
      return CanNavigate(aSource, opener, aDocumentPrincipal, false);
    }
  }

  return false;
}

// static
bool nsContentUtils::IsAbsoluteURL(const nsACString& aURL) {
  nsAutoCString scheme;
+8 −0
Original line number Diff line number Diff line
@@ -199,6 +199,7 @@ enum class ShadowRootMode : uint8_t;
class ShadowRoot;
struct StructuredSerializeOptions;
class TrustedHTMLOrString;
class WindowContext;
class WorkerPrivate;
enum class ElementCallbackType;
enum class ReferrerPolicy : uint8_t;
@@ -950,6 +951,13 @@ class nsContentUtils {
      nsIContent* aContent, const nsAString& aAttrValue,
      nsIPrincipal* aSubjectPrincipal);

  /* Returns true if browser allowed to navigate aTarget BrowsingContext using
   * aSource BrowsingContext and aDocumentPrincipal */
  static bool CanNavigate(mozilla::dom::BrowsingContext* aSource,
                          mozilla::dom::BrowsingContext* aTarget,
                          nsIPrincipal* aDocumentPrincipal,
                          bool aConsiderOpener);

  /**
   * Returns true if the given string is guaranteed to be treated as an absolute
   * URL, rather than a relative URL. In practice, this means any complete URL
+2 −60
Original line number Diff line number Diff line
@@ -718,66 +718,8 @@ bool WindowGlobalChild::SameOriginWithTop() {
// Bug 1810619: Crash at null in nsDocShell::ValidateOrigin
bool WindowGlobalChild::CanNavigate(dom::BrowsingContext* aTarget,
                                    bool aConsiderOpener) {
  MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aTarget->Group(),
                        "A WindowGlobalChild should never try to navigate a "
                        "BrowsingContext from another group");

  auto isFileScheme = [](nsIPrincipal* aPrincipal) -> bool {
    // NOTE: This code previously checked for a file scheme using
    // `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer
    // use GetURI, as it has been deprecated, and it makes more sense to take
    // advantage of the pre-computed origin, which will already use the
    // innermost URI (bug 1810619)
    nsAutoCString origin, scheme;
    return NS_SUCCEEDED(aPrincipal->GetOriginNoSuffix(origin)) &&
           NS_SUCCEEDED(net_ExtractURLScheme(origin, scheme)) &&
           scheme == "file"_ns;
  };

  // A frame can navigate itself and its own root.
  if (aTarget == BrowsingContext() || aTarget == BrowsingContext()->Top()) {
    return true;
  }

  // If the target frame doesn't yet have a WindowContext, start checking
  // principals from its direct ancestor instead. It would inherit its principal
  // from this document upon creation.
  dom::WindowContext* initialWc = aTarget->GetCurrentWindowContext();
  if (!initialWc) {
    initialWc = aTarget->GetParentWindowContext();
  }

  // A frame can navigate any frame with a same-origin ancestor.
  bool isFileDocument = isFileScheme(DocumentPrincipal());
  for (dom::WindowContext* wc = initialWc; wc;
       wc = wc->GetParentWindowContext()) {
    dom::WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
    if (!wgc) {
      continue;  // out-of process, so not same-origin.
    }

    if (DocumentPrincipal()->Equals(wgc->DocumentPrincipal())) {
      return true;
    }

    // Not strictly equal, special case if both are file: URIs.
    //
    // file: URIs are considered the same domain for the purpose of frame
    // navigation, regardless of script accessibility (bug 420425).
    if (isFileDocument && isFileScheme(wgc->DocumentPrincipal())) {
      return true;
    }
  }

  // If the target is a top-level document, a frame can navigate it if it can
  // navigate its opener.
  if (aConsiderOpener && !aTarget->GetParent()) {
    if (RefPtr<dom::BrowsingContext> opener = aTarget->GetOpener()) {
      return CanNavigate(opener, false);
    }
  }

  return false;
  return nsContentUtils::CanNavigate(BrowsingContext(), aTarget,
                                     DocumentPrincipal(), aConsiderOpener);
}

// FindWithName follows the rules for choosing a browsing context,
+12 −6
Original line number Diff line number Diff line
@@ -338,13 +338,16 @@ mozilla::ipc::IPCResult WindowGlobalParent::RecvLoadURI(

  RefPtr<CanonicalBrowsingContext> targetBC = aTargetBC.get_canonical();

  // FIXME: For cross-process loads, we should double check CanAccess() for the
  // source browsing context in the parent process.

  if (targetBC->Group() != BrowsingContext()->Group()) {
    return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
  }

  if (!nsContentUtils::CanNavigate(BrowsingContext(), targetBC.get(),
                                   DocumentPrincipal(), true)) {
    return IPC_FAIL(this,
                    "Illegal cross-process load attempt (!CanNavigate())");
  }

  // FIXME: We should really initiate the load in the parent before bouncing
  // back down to the child.

@@ -372,13 +375,16 @@ mozilla::ipc::IPCResult WindowGlobalParent::RecvInternalLoad(
  RefPtr<CanonicalBrowsingContext> targetBC =
      aLoadState->TargetBrowsingContext().get_canonical();

  // FIXME: For cross-process loads, we should double check CanAccess() for the
  // source browsing context in the parent process.

  if (targetBC->Group() != BrowsingContext()->Group()) {
    return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
  }

  if (!nsContentUtils::CanNavigate(BrowsingContext(), targetBC.get(),
                                   DocumentPrincipal(), true)) {
    return IPC_FAIL(this,
                    "Illegal cross-process load attempt (!CanNavigate())");
  }

  // FIXME: We should really initiate the load in the parent before bouncing
  // back down to the child.