/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "FramingChecker.h" #include "nsCharSeparatedTokenizer.h" #include "nsCSPUtils.h" #include "nsDocShell.h" #include "nsIChannel.h" #include "nsIConsoleService.h" #include "nsIContentSecurityPolicy.h" #include "nsIScriptError.h" #include "nsNetUtil.h" #include "nsQueryObject.h" #include "mozilla/dom/nsCSPUtils.h" #include "mozilla/dom/LoadURIOptionsBinding.h" #include "mozilla/NullPrincipal.h" #include "nsIStringBundle.h" using namespace mozilla; void FramingChecker::ReportError(const char* aMessageTag, nsIDocShellTreeItem* aParentDocShellItem, nsIURI* aChildURI, const nsAString& aPolicy) { MOZ_ASSERT(aParentDocShellItem, "Need a parent docshell"); if (!aChildURI || !aParentDocShellItem) { return; } Document* parentDocument = aParentDocShellItem->GetDocument(); nsCOMPtr parentURI; parentDocument->NodePrincipal()->GetURI(getter_AddRefs(parentURI)); MOZ_ASSERT(!parentDocument->NodePrincipal()->IsSystemPrincipal(), "Should not get system principal here."); // Get the parent URL spec nsAutoCString parentSpec; nsresult rv; rv = parentURI->GetAsciiSpec(parentSpec); if (NS_FAILED(rv)) { return; } // Get the child URL spec nsAutoCString childSpec; rv = aChildURI->GetAsciiSpec(childSpec); if (NS_FAILED(rv)) { return; } nsCOMPtr bundleService = mozilla::services::GetStringBundleService(); nsCOMPtr bundle; rv = bundleService->CreateBundle( "chrome://global/locale/security/security.properties", getter_AddRefs(bundle)); if (NS_FAILED(rv)) { return; } if (NS_WARN_IF(!bundle)) { return; } nsCOMPtr console( do_GetService(NS_CONSOLESERVICE_CONTRACTID)); nsCOMPtr error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID)); if (!console || !error) { return; } // Localize the error message nsAutoString message; AutoTArray formatStrings; formatStrings.AppendElement(aPolicy); CopyASCIItoUTF16(childSpec, *formatStrings.AppendElement()); CopyASCIItoUTF16(parentSpec, *formatStrings.AppendElement()); rv = bundle->FormatStringFromName(aMessageTag, formatStrings, message); if (NS_FAILED(rv)) { return; } rv = error->InitWithWindowID(message, EmptyString(), EmptyString(), 0, 0, nsIScriptError::errorFlag, "X-Frame-Options", parentDocument->InnerWindowID()); if (NS_FAILED(rv)) { return; } console->LogMessage(error); } /* static */ bool FramingChecker::CheckOneFrameOptionsPolicy(nsIHttpChannel* aHttpChannel, const nsAString& aPolicy, nsIDocShell* aDocShell) { nsresult rv; // Find the top docshell in our parent chain that doesn't have the system // principal and use it for the principal comparison. Finding the top // content-type docshell doesn't work because some chrome documents are // loaded in content docshells (see bug 593387). nsCOMPtr thisDocShellItem(aDocShell); nsCOMPtr parentDocShellItem; nsCOMPtr curDocShellItem = thisDocShellItem; nsCOMPtr topDoc; nsCOMPtr ssm = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv); if (!ssm) { MOZ_CRASH(); } nsCOMPtr uri; aHttpChannel->GetURI(getter_AddRefs(uri)); // return early if header does not have one of the values with meaning if (!aPolicy.LowerCaseEqualsLiteral("deny") && !aPolicy.LowerCaseEqualsLiteral("sameorigin")) { nsCOMPtr root; curDocShellItem->GetInProcessSameTypeRootTreeItem(getter_AddRefs(root)); ReportError("XFOInvalid", root, uri, aPolicy); return true; } // XXXkhuey when does this happen? Is returning true safe here? if (!aDocShell) { return true; } // We need to check the location of this window and the location of the top // window, if we're not the top. X-F-O: SAMEORIGIN requires that the // document must be same-origin with top window. X-F-O: DENY requires that // the document must never be framed. nsCOMPtr thisWindow = aDocShell->GetWindow(); // If we don't have DOMWindow there is no risk of clickjacking if (!thisWindow) { return true; } // GetInProcessScriptableTop, not GetTop, because we want this to respect //