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