/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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 "DomainPolicy.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/ipc/URIUtils.h" #include "mozilla/Unused.h" #include "nsIMessageManager.h" #include "nsScriptSecurityManager.h" namespace mozilla { using namespace ipc; using namespace dom; NS_IMPL_ISUPPORTS(DomainPolicy, nsIDomainPolicy) static nsresult BroadcastDomainSetChange(DomainSetType aSetType, DomainSetChangeType aChangeType, nsIURI* aDomain = nullptr) { MOZ_ASSERT(XRE_IsParentProcess(), "DomainPolicy should only be exposed to the chrome process."); nsTArray parents; ContentParent::GetAll(parents); if (!parents.Length()) { return NS_OK; } OptionalURIParams uri; SerializeURI(aDomain, uri); for (uint32_t i = 0; i < parents.Length(); i++) { Unused << parents[i]->SendDomainSetChanged(aSetType, aChangeType, uri); } return NS_OK; } DomainPolicy::DomainPolicy() : mBlacklist(new DomainSet(BLACKLIST)) , mSuperBlacklist(new DomainSet(SUPER_BLACKLIST)) , mWhitelist(new DomainSet(WHITELIST)) , mSuperWhitelist(new DomainSet(SUPER_WHITELIST)) { if (XRE_IsParentProcess()) { BroadcastDomainSetChange(NO_TYPE, ACTIVATE_POLICY); } } DomainPolicy::~DomainPolicy() { // The SSM holds a strong ref to the DomainPolicy until Deactivate() is // invoked, so we should never hit the destructor until that happens. MOZ_ASSERT(!mBlacklist && !mSuperBlacklist && !mWhitelist && !mSuperWhitelist); } NS_IMETHODIMP DomainPolicy::GetBlacklist(nsIDomainSet** aSet) { nsCOMPtr set = mBlacklist.get(); set.forget(aSet); return NS_OK; } NS_IMETHODIMP DomainPolicy::GetSuperBlacklist(nsIDomainSet** aSet) { nsCOMPtr set = mSuperBlacklist.get(); set.forget(aSet); return NS_OK; } NS_IMETHODIMP DomainPolicy::GetWhitelist(nsIDomainSet** aSet) { nsCOMPtr set = mWhitelist.get(); set.forget(aSet); return NS_OK; } NS_IMETHODIMP DomainPolicy::GetSuperWhitelist(nsIDomainSet** aSet) { nsCOMPtr set = mSuperWhitelist.get(); set.forget(aSet); return NS_OK; } NS_IMETHODIMP DomainPolicy::Deactivate() { // Clear the hashtables first to free up memory, since script might // hold the doomed sets alive indefinitely. mBlacklist->Clear(); mSuperBlacklist->Clear(); mWhitelist->Clear(); mSuperWhitelist->Clear(); // Null them out. mBlacklist = nullptr; mSuperBlacklist = nullptr; mWhitelist = nullptr; mSuperWhitelist = nullptr; // Inform the SSM. nsScriptSecurityManager* ssm = nsScriptSecurityManager::GetScriptSecurityManager(); if (ssm) { ssm->DeactivateDomainPolicy(); } if (XRE_IsParentProcess()) { BroadcastDomainSetChange(NO_TYPE, DEACTIVATE_POLICY); } return NS_OK; } void DomainPolicy::CloneDomainPolicy(DomainPolicyClone* aClone) { aClone->active() = true; mBlacklist->CloneSet(&aClone->blacklist()); mSuperBlacklist->CloneSet(&aClone->superBlacklist()); mWhitelist->CloneSet(&aClone->whitelist()); mSuperWhitelist->CloneSet(&aClone->superWhitelist()); } static void CopyURIs(const InfallibleTArray& aDomains, nsIDomainSet* aSet) { for (uint32_t i = 0; i < aDomains.Length(); i++) { nsCOMPtr uri = DeserializeURI(aDomains[i]); aSet->Add(uri); } } void DomainPolicy::ApplyClone(DomainPolicyClone* aClone) { CopyURIs(aClone->blacklist(), mBlacklist); CopyURIs(aClone->whitelist(), mWhitelist); CopyURIs(aClone->superBlacklist(), mSuperBlacklist); CopyURIs(aClone->superWhitelist(), mSuperWhitelist); } static already_AddRefed GetCanonicalClone(nsIURI* aURI) { nsCOMPtr clone; nsresult rv = aURI->Clone(getter_AddRefs(clone)); NS_ENSURE_SUCCESS(rv, nullptr); rv = clone->SetUserPass(EmptyCString()); NS_ENSURE_SUCCESS(rv, nullptr); rv = clone->SetPath(EmptyCString()); NS_ENSURE_SUCCESS(rv, nullptr); return clone.forget(); } NS_IMPL_ISUPPORTS(DomainSet, nsIDomainSet) NS_IMETHODIMP DomainSet::Add(nsIURI* aDomain) { nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); mHashTable.PutEntry(clone); if (XRE_IsParentProcess()) return BroadcastDomainSetChange(mType, ADD_DOMAIN, aDomain); return NS_OK; } NS_IMETHODIMP DomainSet::Remove(nsIURI* aDomain) { nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); mHashTable.RemoveEntry(clone); if (XRE_IsParentProcess()) return BroadcastDomainSetChange(mType, REMOVE_DOMAIN, aDomain); return NS_OK; } NS_IMETHODIMP DomainSet::Clear() { mHashTable.Clear(); if (XRE_IsParentProcess()) return BroadcastDomainSetChange(mType, CLEAR_DOMAINS); return NS_OK; } NS_IMETHODIMP DomainSet::Contains(nsIURI* aDomain, bool* aContains) { *aContains = false; nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); *aContains = mHashTable.Contains(clone); return NS_OK; } NS_IMETHODIMP DomainSet::ContainsSuperDomain(nsIURI* aDomain, bool* aContains) { *aContains = false; nsCOMPtr clone = GetCanonicalClone(aDomain); NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE); nsAutoCString domain; nsresult rv = clone->GetHost(domain); NS_ENSURE_SUCCESS(rv, rv); while (true) { // Check the current domain. if (mHashTable.Contains(clone)) { *aContains = true; return NS_OK; } // Chop off everything before the first dot, or break if there are no // dots left. int32_t index = domain.Find("."); if (index == kNotFound) break; domain.Assign(Substring(domain, index + 1)); rv = clone->SetHost(domain); NS_ENSURE_SUCCESS(rv, rv); } // No match. return NS_OK; } NS_IMETHODIMP DomainSet::GetType(uint32_t* aType) { *aType = mType; return NS_OK; } void DomainSet::CloneSet(InfallibleTArray* aDomains) { for (auto iter = mHashTable.Iter(); !iter.Done(); iter.Next()) { nsIURI* key = iter.Get()->GetKey(); URIParams uri; SerializeURI(key, uri); aDomains->AppendElement(uri); } } } /* namespace mozilla */