Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* This Source Code Form is subject to the terms of the Mozilla Public
3
* License, v. 2.0. If a copy of the MPL was not distributed with this
4
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6
#include "xpcpublic.h"
7
#include "nsString.h"
8
#include "nsJSPrincipals.h"
9
#include "plstr.h"
10
#include "nsCOMPtr.h"
11
#include "nsMemory.h"
12
#include "nsStringBuffer.h"
13
#include "mozilla/BasePrincipal.h"
14
#include "mozilla/dom/StructuredCloneTags.h"
15
// for mozilla::dom::workerinternals::kJSPrincipalsDebugToken
16
#include "mozilla/dom/workerinternals/JSSettings.h"
17
// for mozilla::dom::worklet::kJSPrincipalsDebugToken
18
#include "mozilla/dom/WorkletPrincipals.h"
19
#include "mozilla/ipc/BackgroundUtils.h"
20
21
using namespace mozilla;
22
using namespace mozilla::ipc;
23
24
NS_IMETHODIMP_(MozExternalRefCountType)
25
nsJSPrincipals::AddRef() {
26
MOZ_ASSERT(NS_IsMainThread());
27
MOZ_ASSERT(int32_t(refcount) >= 0, "illegal refcnt");
28
nsrefcnt count = ++refcount;
29
NS_LOG_ADDREF(this, count, "nsJSPrincipals", sizeof(*this));
30
return count;
31
}
32
33
NS_IMETHODIMP_(MozExternalRefCountType)
34
nsJSPrincipals::Release() {
35
MOZ_ASSERT(NS_IsMainThread());
36
MOZ_ASSERT(0 != refcount, "dup release");
37
nsrefcnt count = --refcount;
38
NS_LOG_RELEASE(this, count, "nsJSPrincipals");
39
if (count == 0) {
40
delete this;
41
}
42
43
return count;
44
}
45
46
/* static */
47
bool nsJSPrincipals::Subsume(JSPrincipals* jsprin, JSPrincipals* other) {
48
bool result;
49
nsresult rv = nsJSPrincipals::get(jsprin)->Subsumes(
50
nsJSPrincipals::get(other), &result);
51
return NS_SUCCEEDED(rv) && result;
52
}
53
54
/* static */
55
void nsJSPrincipals::Destroy(JSPrincipals* jsprin) {
56
// The JS runtime can call this method during the last GC when
57
// nsScriptSecurityManager is destroyed. So we must not assume here that
58
// the security manager still exists.
59
60
nsJSPrincipals* nsjsprin = nsJSPrincipals::get(jsprin);
61
62
// We need to destroy the nsIPrincipal. We'll do this by adding
63
// to the refcount and calling release
64
65
#ifdef NS_BUILD_REFCNT_LOGGING
66
// The refcount logging considers AddRef-to-1 to indicate creation,
67
// so trick it into thinking it's otherwise, but balance the
68
// Release() we do below.
69
nsjsprin->refcount++;
70
nsjsprin->AddRef();
71
nsjsprin->refcount--;
72
#else
73
nsjsprin->refcount++;
74
#endif
75
nsjsprin->Release();
76
}
77
78
#ifdef DEBUG
79
80
// Defined here so one can do principals->dump() in the debugger
81
JS_PUBLIC_API void JSPrincipals::dump() {
82
if (debugToken == nsJSPrincipals::DEBUG_TOKEN) {
83
nsAutoCString str;
84
nsresult rv = static_cast<nsJSPrincipals*>(this)->GetScriptLocation(str);
85
fprintf(stderr, "nsIPrincipal (%p) = %s\n", static_cast<void*>(this),
86
NS_SUCCEEDED(rv) ? str.get() : "(unknown)");
87
} else if (debugToken == dom::workerinternals::kJSPrincipalsDebugToken) {
88
fprintf(stderr, "Web Worker principal singleton (%p)\n", this);
89
} else if (debugToken == dom::WorkletPrincipals::kJSPrincipalsDebugToken) {
90
fprintf(stderr, "Web Worklet principal (%p)\n", this);
91
} else {
92
fprintf(stderr,
93
"!!! JSPrincipals (%p) is not nsJSPrincipals instance - bad token: "
94
"actual=0x%x expected=0x%x\n",
95
this, unsigned(debugToken), unsigned(nsJSPrincipals::DEBUG_TOKEN));
96
}
97
}
98
99
#endif
100
101
/* static */
102
bool nsJSPrincipals::ReadPrincipals(JSContext* aCx,
103
JSStructuredCloneReader* aReader,
104
JSPrincipals** aOutPrincipals) {
105
uint32_t tag;
106
uint32_t unused;
107
if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
108
return false;
109
}
110
111
if (!(tag == SCTAG_DOM_NULL_PRINCIPAL || tag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
112
tag == SCTAG_DOM_CONTENT_PRINCIPAL ||
113
tag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
114
tag == SCTAG_DOM_WORKER_PRINCIPAL)) {
115
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
116
return false;
117
}
118
119
return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
120
}
121
122
static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
123
OriginAttributes& aAttrs, nsACString& aSpec,
124
nsACString& aOriginNoSuffix,
125
nsACString& aBaseDomain) {
126
uint32_t suffixLength, specLength;
127
if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
128
return false;
129
}
130
131
nsAutoCString suffix;
132
if (!suffix.SetLength(suffixLength, fallible)) {
133
return false;
134
}
135
136
if (!JS_ReadBytes(aReader, suffix.BeginWriting(), suffixLength)) {
137
return false;
138
}
139
140
if (!aAttrs.PopulateFromSuffix(suffix)) {
141
return false;
142
}
143
144
if (!aSpec.SetLength(specLength, fallible)) {
145
return false;
146
}
147
148
if (!JS_ReadBytes(aReader, aSpec.BeginWriting(), specLength)) {
149
return false;
150
}
151
152
uint32_t originNoSuffixLength, dummy;
153
if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &dummy)) {
154
return false;
155
}
156
157
MOZ_ASSERT(dummy == 0);
158
if (dummy != 0) {
159
return false;
160
}
161
162
if (!aOriginNoSuffix.SetLength(originNoSuffixLength, fallible)) {
163
return false;
164
}
165
166
if (!JS_ReadBytes(aReader, aOriginNoSuffix.BeginWriting(),
167
originNoSuffixLength)) {
168
return false;
169
}
170
171
uint32_t baseDomainIsVoid, baseDomainLength;
172
if (!JS_ReadUint32Pair(aReader, &baseDomainIsVoid, &baseDomainLength)) {
173
return false;
174
}
175
176
MOZ_ASSERT(baseDomainIsVoid == 0 || baseDomainIsVoid == 1);
177
178
if (baseDomainIsVoid) {
179
MOZ_ASSERT(baseDomainLength == 0);
180
181
aBaseDomain.SetIsVoid(true);
182
return true;
183
}
184
185
if (!aBaseDomain.SetLength(baseDomainLength, fallible)) {
186
return false;
187
}
188
189
if (!JS_ReadBytes(aReader, aBaseDomain.BeginWriting(), baseDomainLength)) {
190
return false;
191
}
192
193
return true;
194
}
195
196
static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
197
PrincipalInfo& aInfo) {
198
if (aTag == SCTAG_DOM_SYSTEM_PRINCIPAL) {
199
aInfo = SystemPrincipalInfo();
200
} else if (aTag == SCTAG_DOM_NULL_PRINCIPAL) {
201
OriginAttributes attrs;
202
nsAutoCString spec;
203
nsAutoCString originNoSuffix;
204
nsAutoCString baseDomain;
205
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
206
return false;
207
}
208
aInfo = NullPrincipalInfo(attrs, spec);
209
} else if (aTag == SCTAG_DOM_EXPANDED_PRINCIPAL) {
210
uint32_t length, unused;
211
if (!JS_ReadUint32Pair(aReader, &length, &unused)) {
212
return false;
213
}
214
215
ExpandedPrincipalInfo expanded;
216
217
for (uint32_t i = 0; i < length; i++) {
218
uint32_t tag;
219
if (!JS_ReadUint32Pair(aReader, &tag, &unused)) {
220
return false;
221
}
222
223
PrincipalInfo sub;
224
if (!ReadPrincipalInfo(aReader, tag, sub)) {
225
return false;
226
}
227
expanded.allowlist().AppendElement(sub);
228
}
229
230
aInfo = expanded;
231
} else if (aTag == SCTAG_DOM_CONTENT_PRINCIPAL) {
232
OriginAttributes attrs;
233
nsAutoCString spec;
234
nsAutoCString originNoSuffix;
235
nsAutoCString baseDomain;
236
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
237
return false;
238
}
239
240
#ifdef FUZZING
241
if (originNoSuffix.IsEmpty()) {
242
return false;
243
}
244
#endif
245
246
MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty());
247
248
// XXX: Do we care about mDomain for structured clone?
249
aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(),
250
baseDomain);
251
} else {
252
#ifdef FUZZING
253
return false;
254
#else
255
MOZ_CRASH("unexpected principal structured clone tag");
256
#endif
257
}
258
259
return true;
260
}
261
262
static StaticRefPtr<nsIPrincipal> sActiveWorkerPrincipal;
263
264
nsJSPrincipals::AutoSetActiveWorkerPrincipal::AutoSetActiveWorkerPrincipal(
265
nsIPrincipal* aPrincipal) {
266
MOZ_ASSERT(NS_IsMainThread());
267
MOZ_RELEASE_ASSERT(!sActiveWorkerPrincipal);
268
sActiveWorkerPrincipal = aPrincipal;
269
}
270
271
nsJSPrincipals::AutoSetActiveWorkerPrincipal::~AutoSetActiveWorkerPrincipal() {
272
sActiveWorkerPrincipal = nullptr;
273
}
274
275
/* static */
276
bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
277
JSStructuredCloneReader* aReader,
278
uint32_t aTag,
279
JSPrincipals** aOutPrincipals) {
280
MOZ_ASSERT(aTag == SCTAG_DOM_NULL_PRINCIPAL ||
281
aTag == SCTAG_DOM_SYSTEM_PRINCIPAL ||
282
aTag == SCTAG_DOM_CONTENT_PRINCIPAL ||
283
aTag == SCTAG_DOM_EXPANDED_PRINCIPAL ||
284
aTag == SCTAG_DOM_WORKER_PRINCIPAL);
285
286
if (NS_WARN_IF(!NS_IsMainThread())) {
287
xpc::Throw(aCx, NS_ERROR_UNCATCHABLE_EXCEPTION);
288
return false;
289
}
290
291
if (aTag == SCTAG_DOM_WORKER_PRINCIPAL) {
292
// When reading principals which were written on a worker thread, we need to
293
// know the principal of the worker which did the write.
294
if (!sActiveWorkerPrincipal) {
295
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
296
return false;
297
}
298
RefPtr<nsJSPrincipals> retval = get(sActiveWorkerPrincipal);
299
retval.forget(aOutPrincipals);
300
return true;
301
}
302
303
PrincipalInfo info;
304
if (!ReadPrincipalInfo(aReader, aTag, info)) {
305
return false;
306
}
307
308
nsresult rv;
309
nsCOMPtr<nsIPrincipal> prin = PrincipalInfoToPrincipal(info, &rv);
310
if (NS_WARN_IF(NS_FAILED(rv))) {
311
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
312
return false;
313
}
314
315
*aOutPrincipals = get(prin.forget().take());
316
return true;
317
}
318
319
static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
320
const OriginAttributes& aAttrs,
321
const nsCString& aSpec,
322
const nsCString& aOriginNoSuffix,
323
const nsCString& aBaseDomain) {
324
nsAutoCString suffix;
325
aAttrs.CreateSuffix(suffix);
326
327
if (!(JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
328
JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
329
JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) &&
330
JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), 0) &&
331
JS_WriteBytes(aWriter, aOriginNoSuffix.get(),
332
aOriginNoSuffix.Length()))) {
333
return false;
334
}
335
336
if (aBaseDomain.IsVoid()) {
337
return JS_WriteUint32Pair(aWriter, 1, 0);
338
}
339
340
return JS_WriteUint32Pair(aWriter, 0, aBaseDomain.Length()) &&
341
JS_WriteBytes(aWriter, aBaseDomain.get(), aBaseDomain.Length());
342
}
343
344
/* static */
345
bool nsJSPrincipals::WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
346
const PrincipalInfo& aInfo) {
347
if (aInfo.type() == PrincipalInfo::TNullPrincipalInfo) {
348
const NullPrincipalInfo& nullInfo = aInfo;
349
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) &&
350
::WritePrincipalInfo(aWriter, nullInfo.attrs(), nullInfo.spec(),
351
EmptyCString(), EmptyCString());
352
}
353
if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
354
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
355
}
356
if (aInfo.type() == PrincipalInfo::TExpandedPrincipalInfo) {
357
const ExpandedPrincipalInfo& expanded = aInfo;
358
if (!JS_WriteUint32Pair(aWriter, SCTAG_DOM_EXPANDED_PRINCIPAL, 0) ||
359
!JS_WriteUint32Pair(aWriter, expanded.allowlist().Length(), 0)) {
360
return false;
361
}
362
363
for (uint32_t i = 0; i < expanded.allowlist().Length(); i++) {
364
if (!WritePrincipalInfo(aWriter, expanded.allowlist()[i])) {
365
return false;
366
}
367
}
368
return true;
369
}
370
371
MOZ_ASSERT(aInfo.type() == PrincipalInfo::TContentPrincipalInfo);
372
const ContentPrincipalInfo& cInfo = aInfo;
373
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
374
::WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(),
375
cInfo.originNoSuffix(), cInfo.baseDomain());
376
}
377
378
bool nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter) {
379
PrincipalInfo info;
380
if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(this, &info)))) {
381
xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR);
382
return false;
383
}
384
385
return WritePrincipalInfo(aWriter, info);
386
}
387
388
bool nsJSPrincipals::isSystemOrAddonPrincipal() {
389
JS::AutoSuppressGCAnalysis suppress;
390
return this->IsSystemPrincipal() ||
391
this->GetIsAddonOrExpandedAddonPrincipal();
392
}