1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=4 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "DomainPolicy.h"
8 #include "mozilla/dom/ContentParent.h"
9 #include "mozilla/ipc/URIUtils.h"
10 #include "mozilla/Unused.h"
11 #include "nsIURIMutator.h"
12 #include "nsScriptSecurityManager.h"
13 
14 namespace mozilla {
15 
16 using namespace ipc;
17 using namespace dom;
18 
NS_IMPL_ISUPPORTS(DomainPolicy,nsIDomainPolicy)19 NS_IMPL_ISUPPORTS(DomainPolicy, nsIDomainPolicy)
20 
21 static nsresult BroadcastDomainSetChange(DomainSetType aSetType,
22                                          DomainSetChangeType aChangeType,
23                                          nsIURI* aDomain = nullptr) {
24   MOZ_ASSERT(XRE_IsParentProcess(),
25              "DomainPolicy should only be exposed to the chrome process.");
26 
27   nsTArray<ContentParent*> parents;
28   ContentParent::GetAll(parents);
29   if (!parents.Length()) {
30     return NS_OK;
31   }
32 
33   for (uint32_t i = 0; i < parents.Length(); i++) {
34     Unused << parents[i]->SendDomainSetChanged(aSetType, aChangeType, aDomain);
35   }
36   return NS_OK;
37 }
38 
DomainPolicy()39 DomainPolicy::DomainPolicy()
40     : mBlocklist(new DomainSet(BLOCKLIST)),
41       mSuperBlocklist(new DomainSet(SUPER_BLOCKLIST)),
42       mAllowlist(new DomainSet(ALLOWLIST)),
43       mSuperAllowlist(new DomainSet(SUPER_ALLOWLIST)) {
44   if (XRE_IsParentProcess()) {
45     BroadcastDomainSetChange(NO_TYPE, ACTIVATE_POLICY);
46   }
47 }
48 
~DomainPolicy()49 DomainPolicy::~DomainPolicy() {
50   // The SSM holds a strong ref to the DomainPolicy until Deactivate() is
51   // invoked, so we should never hit the destructor until that happens.
52   MOZ_ASSERT(!mBlocklist && !mSuperBlocklist && !mAllowlist &&
53              !mSuperAllowlist);
54 }
55 
56 NS_IMETHODIMP
GetBlocklist(nsIDomainSet ** aSet)57 DomainPolicy::GetBlocklist(nsIDomainSet** aSet) {
58   nsCOMPtr<nsIDomainSet> set = mBlocklist.get();
59   set.forget(aSet);
60   return NS_OK;
61 }
62 
63 NS_IMETHODIMP
GetSuperBlocklist(nsIDomainSet ** aSet)64 DomainPolicy::GetSuperBlocklist(nsIDomainSet** aSet) {
65   nsCOMPtr<nsIDomainSet> set = mSuperBlocklist.get();
66   set.forget(aSet);
67   return NS_OK;
68 }
69 
70 NS_IMETHODIMP
GetAllowlist(nsIDomainSet ** aSet)71 DomainPolicy::GetAllowlist(nsIDomainSet** aSet) {
72   nsCOMPtr<nsIDomainSet> set = mAllowlist.get();
73   set.forget(aSet);
74   return NS_OK;
75 }
76 
77 NS_IMETHODIMP
GetSuperAllowlist(nsIDomainSet ** aSet)78 DomainPolicy::GetSuperAllowlist(nsIDomainSet** aSet) {
79   nsCOMPtr<nsIDomainSet> set = mSuperAllowlist.get();
80   set.forget(aSet);
81   return NS_OK;
82 }
83 
84 NS_IMETHODIMP
Deactivate()85 DomainPolicy::Deactivate() {
86   // Clear the hashtables first to free up memory, since script might
87   // hold the doomed sets alive indefinitely.
88   mBlocklist->Clear();
89   mSuperBlocklist->Clear();
90   mAllowlist->Clear();
91   mSuperAllowlist->Clear();
92 
93   // Null them out.
94   mBlocklist = nullptr;
95   mSuperBlocklist = nullptr;
96   mAllowlist = nullptr;
97   mSuperAllowlist = nullptr;
98 
99   // Inform the SSM.
100   nsScriptSecurityManager* ssm =
101       nsScriptSecurityManager::GetScriptSecurityManager();
102   if (ssm) {
103     ssm->DeactivateDomainPolicy();
104   }
105   if (XRE_IsParentProcess()) {
106     BroadcastDomainSetChange(NO_TYPE, DEACTIVATE_POLICY);
107   }
108   return NS_OK;
109 }
110 
CloneDomainPolicy(DomainPolicyClone * aClone)111 void DomainPolicy::CloneDomainPolicy(DomainPolicyClone* aClone) {
112   aClone->active() = true;
113   mBlocklist->CloneSet(&aClone->blocklist());
114   mSuperBlocklist->CloneSet(&aClone->superBlocklist());
115   mAllowlist->CloneSet(&aClone->allowlist());
116   mSuperAllowlist->CloneSet(&aClone->superAllowlist());
117 }
118 
CopyURIs(const nsTArray<RefPtr<nsIURI>> & aDomains,nsIDomainSet * aSet)119 static void CopyURIs(const nsTArray<RefPtr<nsIURI>>& aDomains,
120                      nsIDomainSet* aSet) {
121   for (uint32_t i = 0; i < aDomains.Length(); i++) {
122     if (NS_WARN_IF(!aDomains[i])) {
123       continue;
124     }
125     aSet->Add(aDomains[i]);
126   }
127 }
128 
ApplyClone(const DomainPolicyClone * aClone)129 void DomainPolicy::ApplyClone(const DomainPolicyClone* aClone) {
130   CopyURIs(aClone->blocklist(), mBlocklist);
131   CopyURIs(aClone->allowlist(), mAllowlist);
132   CopyURIs(aClone->superBlocklist(), mSuperBlocklist);
133   CopyURIs(aClone->superAllowlist(), mSuperAllowlist);
134 }
135 
GetCanonicalClone(nsIURI * aURI)136 static already_AddRefed<nsIURI> GetCanonicalClone(nsIURI* aURI) {
137   nsCOMPtr<nsIURI> clone;
138   nsresult rv =
139       NS_MutateURI(aURI).SetUserPass(""_ns).SetPathQueryRef(""_ns).Finalize(
140           clone);
141   NS_ENSURE_SUCCESS(rv, nullptr);
142   return clone.forget();
143 }
144 
NS_IMPL_ISUPPORTS(DomainSet,nsIDomainSet)145 NS_IMPL_ISUPPORTS(DomainSet, nsIDomainSet)
146 
147 NS_IMETHODIMP
148 DomainSet::Add(nsIURI* aDomain) {
149   nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
150   NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
151   mHashTable.Insert(clone);
152   if (XRE_IsParentProcess()) {
153     return BroadcastDomainSetChange(mType, ADD_DOMAIN, aDomain);
154   }
155 
156   return NS_OK;
157 }
158 
159 NS_IMETHODIMP
Remove(nsIURI * aDomain)160 DomainSet::Remove(nsIURI* aDomain) {
161   nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
162   NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
163   mHashTable.Remove(clone);
164   if (XRE_IsParentProcess()) {
165     return BroadcastDomainSetChange(mType, REMOVE_DOMAIN, aDomain);
166   }
167 
168   return NS_OK;
169 }
170 
171 NS_IMETHODIMP
Clear()172 DomainSet::Clear() {
173   mHashTable.Clear();
174   if (XRE_IsParentProcess()) {
175     return BroadcastDomainSetChange(mType, CLEAR_DOMAINS);
176   }
177 
178   return NS_OK;
179 }
180 
181 NS_IMETHODIMP
Contains(nsIURI * aDomain,bool * aContains)182 DomainSet::Contains(nsIURI* aDomain, bool* aContains) {
183   *aContains = false;
184   nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
185   NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
186   *aContains = mHashTable.Contains(clone);
187   return NS_OK;
188 }
189 
190 NS_IMETHODIMP
ContainsSuperDomain(nsIURI * aDomain,bool * aContains)191 DomainSet::ContainsSuperDomain(nsIURI* aDomain, bool* aContains) {
192   *aContains = false;
193   nsCOMPtr<nsIURI> clone = GetCanonicalClone(aDomain);
194   NS_ENSURE_TRUE(clone, NS_ERROR_FAILURE);
195   nsAutoCString domain;
196   nsresult rv = clone->GetHost(domain);
197   NS_ENSURE_SUCCESS(rv, rv);
198   while (true) {
199     // Check the current domain.
200     if (mHashTable.Contains(clone)) {
201       *aContains = true;
202       return NS_OK;
203     }
204 
205     // Chop off everything before the first dot, or break if there are no
206     // dots left.
207     int32_t index = domain.Find(".");
208     if (index == kNotFound) break;
209     domain.Assign(Substring(domain, index + 1));
210     rv = NS_MutateURI(clone).SetHost(domain).Finalize(clone);
211     NS_ENSURE_SUCCESS(rv, rv);
212   }
213 
214   // No match.
215   return NS_OK;
216 }
217 
CloneSet(nsTArray<RefPtr<nsIURI>> * aDomains)218 void DomainSet::CloneSet(nsTArray<RefPtr<nsIURI>>* aDomains) {
219   AppendToArray(*aDomains, mHashTable);
220 }
221 
222 } /* namespace mozilla */
223