Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=2 sw=2 et 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 "ExpandedPrincipal.h"
8
#include "nsIClassInfoImpl.h"
9
#include "mozilla/Base64.h"
10
11
using namespace mozilla;
12
13
NS_IMPL_CLASSINFO(ExpandedPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
14
NS_EXPANDEDPRINCIPAL_CID)
15
NS_IMPL_QUERY_INTERFACE_CI(ExpandedPrincipal, nsIPrincipal,
16
nsIExpandedPrincipal, nsISerializable)
17
NS_IMPL_CI_INTERFACE_GETTER(ExpandedPrincipal, nsIPrincipal,
18
nsIExpandedPrincipal, nsISerializable)
19
20
struct OriginComparator {
21
bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const {
22
nsAutoCString originA;
23
DebugOnly<nsresult> rv = a->GetOrigin(originA);
24
MOZ_ASSERT(NS_SUCCEEDED(rv));
25
nsAutoCString originB;
26
rv = b->GetOrigin(originB);
27
MOZ_ASSERT(NS_SUCCEEDED(rv));
28
return originA < originB;
29
}
30
31
bool Equals(nsIPrincipal* a, nsIPrincipal* b) const {
32
nsAutoCString originA;
33
DebugOnly<nsresult> rv = a->GetOrigin(originA);
34
MOZ_ASSERT(NS_SUCCEEDED(rv));
35
nsAutoCString originB;
36
rv = b->GetOrigin(originB);
37
MOZ_ASSERT(NS_SUCCEEDED(rv));
38
return a == b;
39
}
40
};
41
42
ExpandedPrincipal::ExpandedPrincipal(
43
nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList)
44
: BasePrincipal(eExpandedPrincipal) {
45
// We force the principals to be sorted by origin so that ExpandedPrincipal
46
// origins can have a canonical form.
47
OriginComparator c;
48
for (size_t i = 0; i < aAllowList.Length(); ++i) {
49
mPrincipals.InsertElementSorted(aAllowList[i], c);
50
}
51
}
52
53
ExpandedPrincipal::ExpandedPrincipal() : BasePrincipal(eExpandedPrincipal) {}
54
55
ExpandedPrincipal::~ExpandedPrincipal() {}
56
57
already_AddRefed<ExpandedPrincipal> ExpandedPrincipal::Create(
58
nsTArray<nsCOMPtr<nsIPrincipal>>& aAllowList,
59
const OriginAttributes& aAttrs) {
60
RefPtr<ExpandedPrincipal> ep = new ExpandedPrincipal(aAllowList);
61
62
nsAutoCString origin;
63
origin.AssignLiteral("[Expanded Principal [");
64
for (size_t i = 0; i < ep->mPrincipals.Length(); ++i) {
65
if (i != 0) {
66
origin.AppendLiteral(", ");
67
}
68
69
nsAutoCString subOrigin;
70
DebugOnly<nsresult> rv = ep->mPrincipals.ElementAt(i)->GetOrigin(subOrigin);
71
MOZ_ASSERT(NS_SUCCEEDED(rv));
72
origin.Append(subOrigin);
73
}
74
origin.AppendLiteral("]]");
75
76
ep->FinishInit(origin, aAttrs);
77
return ep.forget();
78
}
79
80
NS_IMETHODIMP
81
ExpandedPrincipal::GetDomain(nsIURI** aDomain) {
82
*aDomain = nullptr;
83
return NS_OK;
84
}
85
86
NS_IMETHODIMP
87
ExpandedPrincipal::SetDomain(nsIURI* aDomain) { return NS_OK; }
88
89
bool ExpandedPrincipal::SubsumesInternal(
90
nsIPrincipal* aOther,
91
BasePrincipal::DocumentDomainConsideration aConsideration) {
92
// If aOther is an ExpandedPrincipal too, we break it down into its component
93
// nsIPrincipals, and check subsumes on each one.
94
if (Cast(aOther)->Is<ExpandedPrincipal>()) {
95
auto* expanded = Cast(aOther)->As<ExpandedPrincipal>();
96
97
for (auto& other : expanded->AllowList()) {
98
// Use SubsumesInternal rather than Subsumes here, since OriginAttribute
99
// checks are only done between non-expanded sub-principals, and we don't
100
// need to incur the extra virtual call overhead.
101
if (!SubsumesInternal(other, aConsideration)) {
102
return false;
103
}
104
}
105
return true;
106
}
107
108
// We're dealing with a regular principal. One of our principals must subsume
109
// it.
110
for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
111
if (Cast(mPrincipals[i])->Subsumes(aOther, aConsideration)) {
112
return true;
113
}
114
}
115
116
return false;
117
}
118
119
bool ExpandedPrincipal::MayLoadInternal(nsIURI* uri) {
120
for (uint32_t i = 0; i < mPrincipals.Length(); ++i) {
121
if (BasePrincipal::Cast(mPrincipals[i])->MayLoadInternal(uri)) {
122
return true;
123
}
124
}
125
126
return false;
127
}
128
129
uint32_t ExpandedPrincipal::GetHashValue() {
130
MOZ_CRASH("extended principal should never be used as key in a hash map");
131
}
132
133
NS_IMETHODIMP
134
ExpandedPrincipal::GetURI(nsIURI** aURI) {
135
*aURI = nullptr;
136
return NS_OK;
137
}
138
139
const nsTArray<nsCOMPtr<nsIPrincipal>>& ExpandedPrincipal::AllowList() {
140
return mPrincipals;
141
}
142
143
NS_IMETHODIMP
144
ExpandedPrincipal::GetBaseDomain(nsACString& aBaseDomain) {
145
return NS_ERROR_NOT_AVAILABLE;
146
}
147
148
NS_IMETHODIMP
149
ExpandedPrincipal::GetAddonId(nsAString& aAddonId) {
150
aAddonId.Truncate();
151
return NS_OK;
152
};
153
154
bool ExpandedPrincipal::AddonHasPermission(const nsAtom* aPerm) {
155
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
156
if (BasePrincipal::Cast(mPrincipals[i])->AddonHasPermission(aPerm)) {
157
return true;
158
}
159
}
160
return false;
161
}
162
163
bool ExpandedPrincipal::AddonAllowsLoad(nsIURI* aURI,
164
bool aExplicit /* = false */) {
165
for (const auto& principal : mPrincipals) {
166
if (Cast(principal)->AddonAllowsLoad(aURI, aExplicit)) {
167
return true;
168
}
169
}
170
return false;
171
}
172
173
void ExpandedPrincipal::SetCsp(nsIContentSecurityPolicy* aCSP) { mCSP = aCSP; }
174
175
NS_IMETHODIMP
176
ExpandedPrincipal::GetCsp(nsIContentSecurityPolicy** aCsp) {
177
NS_IF_ADDREF(*aCsp = mCSP);
178
return NS_OK;
179
}
180
181
nsIPrincipal* ExpandedPrincipal::PrincipalToInherit(nsIURI* aRequestedURI) {
182
if (aRequestedURI) {
183
// If a given sub-principal subsumes the given URI, use that principal for
184
// inheritance. In general, this only happens with certain CORS modes, loads
185
// with forced principal inheritance, and creation of XML documents from
186
// XMLHttpRequests or fetch requests. For URIs that normally inherit a
187
// principal (such as data: URIs), we fall back to the last principal in the
188
// allowlist.
189
for (const auto& principal : mPrincipals) {
190
if (Cast(principal)->MayLoadInternal(aRequestedURI)) {
191
return principal;
192
}
193
}
194
}
195
return mPrincipals.LastElement();
196
}
197
198
nsresult ExpandedPrincipal::GetScriptLocation(nsACString& aStr) {
199
aStr.AssignLiteral("[Expanded Principal [");
200
for (size_t i = 0; i < mPrincipals.Length(); ++i) {
201
if (i != 0) {
202
aStr.AppendLiteral(", ");
203
}
204
205
nsAutoCString spec;
206
nsresult rv =
207
nsJSPrincipals::get(mPrincipals.ElementAt(i))->GetScriptLocation(spec);
208
NS_ENSURE_SUCCESS(rv, rv);
209
210
aStr.Append(spec);
211
}
212
aStr.AppendLiteral("]]");
213
return NS_OK;
214
}
215
216
//////////////////////////////////////////
217
// Methods implementing nsISerializable //
218
//////////////////////////////////////////
219
220
// We've had way too many issues with unversioned serializations, so
221
// explicitly version this one.
222
static const uint32_t kSerializationVersion = 1;
223
224
NS_IMETHODIMP
225
ExpandedPrincipal::Read(nsIObjectInputStream* aStream) {
226
uint32_t version;
227
nsresult rv = aStream->Read32(&version);
228
if (version != kSerializationVersion) {
229
MOZ_ASSERT(false,
230
"We really need to add handling of the old(?) version here");
231
return NS_ERROR_UNEXPECTED;
232
}
233
234
uint32_t count;
235
rv = aStream->Read32(&count);
236
if (NS_FAILED(rv)) {
237
return rv;
238
}
239
240
if (!mPrincipals.SetCapacity(count, fallible)) {
241
return NS_ERROR_OUT_OF_MEMORY;
242
}
243
244
OriginComparator c;
245
for (uint32_t i = 0; i < count; ++i) {
246
nsCOMPtr<nsISupports> read;
247
rv = aStream->ReadObject(true, getter_AddRefs(read));
248
if (NS_FAILED(rv)) {
249
return rv;
250
}
251
252
nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(read);
253
if (!principal) {
254
return NS_ERROR_UNEXPECTED;
255
}
256
257
// Play it safe and InsertElementSorted, in case the sort order
258
// changed for some bizarre reason.
259
mPrincipals.InsertElementSorted(std::move(principal), c);
260
}
261
262
return NS_OK;
263
}
264
265
NS_IMETHODIMP
266
ExpandedPrincipal::Write(nsIObjectOutputStream* aStream) {
267
// Read is used still for legacy principals
268
MOZ_RELEASE_ASSERT(false, "Old style serialization is removed");
269
return NS_OK;
270
}
271
272
nsresult ExpandedPrincipal::GetSiteIdentifier(SiteIdentifier& aSite) {
273
// Call GetSiteIdentifier on each of our principals and return a new
274
// ExpandedPrincipal.
275
276
nsTArray<nsCOMPtr<nsIPrincipal>> allowlist;
277
for (const auto& principal : mPrincipals) {
278
SiteIdentifier site;
279
nsresult rv = Cast(principal)->GetSiteIdentifier(site);
280
NS_ENSURE_SUCCESS(rv, rv);
281
allowlist.AppendElement(site.GetPrincipal());
282
}
283
284
RefPtr<ExpandedPrincipal> expandedPrincipal =
285
ExpandedPrincipal::Create(allowlist, OriginAttributesRef());
286
MOZ_ASSERT(expandedPrincipal, "ExpandedPrincipal::Create returned nullptr?");
287
288
aSite.Init(expandedPrincipal);
289
return NS_OK;
290
}
291
292
nsresult ExpandedPrincipal::PopulateJSONObject(Json::Value& aObject) {
293
nsAutoCString principalList;
294
// First item through we have a blank separator and append the next result
295
nsAutoCString sep;
296
for (auto& principal : mPrincipals) {
297
nsAutoCString JSON;
298
BasePrincipal::Cast(principal)->ToJSON(JSON);
299
// Values currently only copes with strings so encode into base64 to allow a
300
// CSV safely.
301
nsAutoCString result;
302
nsresult rv;
303
rv = Base64Encode(JSON, result);
304
NS_ENSURE_SUCCESS(rv, rv);
305
// This is blank for the first run through so the last in the list doesn't
306
// add a separator
307
principalList.Append(sep);
308
principalList.Append(result);
309
sep = ',';
310
}
311
aObject[std::to_string(eSpecs)] = principalList.get();
312
313
nsAutoCString suffix;
314
OriginAttributesRef().CreateSuffix(suffix);
315
if (suffix.Length() > 0) {
316
aObject[std::to_string(eSuffix)] = suffix.get();
317
}
318
319
return NS_OK;
320
}
321
322
already_AddRefed<BasePrincipal> ExpandedPrincipal::FromProperties(
323
nsTArray<ExpandedPrincipal::KeyVal>& aFields) {
324
MOZ_ASSERT(aFields.Length() == eMax + 1, "Must have all the keys");
325
nsTArray<nsCOMPtr<nsIPrincipal>> allowList;
326
OriginAttributes attrs;
327
// The odd structure here is to make the code to not compile
328
// if all the switch enum cases haven't been codified
329
for (const auto& field : aFields) {
330
switch (field.key) {
331
case ExpandedPrincipal::eSpecs:
332
if (!field.valueWasSerialized) {
333
MOZ_ASSERT(false,
334
"Expanded principals require specs in serialized JSON");
335
return nullptr;
336
}
337
for (const nsACString& each : field.value.Split(',')) {
338
nsAutoCString result;
339
nsresult rv;
340
rv = Base64Decode(each, result);
341
MOZ_ASSERT(NS_SUCCEEDED(rv), "failed to decode");
342
343
NS_ENSURE_SUCCESS(rv, nullptr);
344
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(result);
345
allowList.AppendElement(principal);
346
}
347
break;
348
case ExpandedPrincipal::eSuffix:
349
if (field.valueWasSerialized) {
350
bool ok = attrs.PopulateFromSuffix(field.value);
351
if (!ok) {
352
return nullptr;
353
}
354
}
355
break;
356
}
357
}
358
359
if (allowList.Length() == 0) {
360
return nullptr;
361
}
362
363
RefPtr<ExpandedPrincipal> expandedPrincipal =
364
ExpandedPrincipal::Create(allowList, attrs);
365
366
return expandedPrincipal.forget();
367
}