Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 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 "nsScriptSecurityManager.h"
8
9
#include "mozilla/ArrayUtils.h"
10
#include "mozilla/StoragePrincipalHelper.h"
11
12
#include "xpcpublic.h"
13
#include "XPCWrapper.h"
14
#include "nsIInputStreamChannel.h"
15
#include "nsILoadContext.h"
16
#include "nsIServiceManager.h"
17
#include "nsIScriptObjectPrincipal.h"
18
#include "nsIScriptContext.h"
19
#include "nsIScriptError.h"
20
#include "nsIURL.h"
21
#include "nsIURIMutator.h"
22
#include "nsINestedURI.h"
23
#include "nspr.h"
24
#include "nsJSPrincipals.h"
25
#include "mozilla/BasePrincipal.h"
26
#include "ExpandedPrincipal.h"
27
#include "SystemPrincipal.h"
28
#include "DomainPolicy.h"
29
#include "nsString.h"
30
#include "nsCRT.h"
31
#include "nsCRTGlue.h"
32
#include "nsContentSecurityManager.h"
33
#include "nsDocShell.h"
34
#include "nsError.h"
35
#include "nsGlobalWindowInner.h"
36
#include "nsDOMCID.h"
37
#include "nsTextFormatter.h"
38
#include "nsIStringBundle.h"
39
#include "nsNetUtil.h"
40
#include "nsIEffectiveTLDService.h"
41
#include "nsIProperties.h"
42
#include "nsDirectoryServiceDefs.h"
43
#include "nsIFile.h"
44
#include "nsIFileURL.h"
45
#include "nsIZipReader.h"
46
#include "nsIScriptGlobalObject.h"
47
#include "nsPIDOMWindow.h"
48
#include "nsIDocShell.h"
49
#include "nsIPrompt.h"
50
#include "nsIWindowWatcher.h"
51
#include "nsIConsoleService.h"
52
#include "nsIOService.h"
53
#include "nsIContent.h"
54
#include "nsDOMJSUtils.h"
55
#include "nsAboutProtocolUtils.h"
56
#include "nsIClassInfo.h"
57
#include "nsIURIFixup.h"
58
#include "nsIChromeRegistry.h"
59
#include "nsIResProtocolHandler.h"
60
#include "nsIContentSecurityPolicy.h"
61
#include "nsIAsyncVerifyRedirectCallback.h"
62
#include "mozilla/Components.h"
63
#include "mozilla/Preferences.h"
64
#include "mozilla/dom/BindingUtils.h"
65
#include "mozilla/NullPrincipal.h"
66
#include <stdint.h>
67
#include "mozilla/dom/nsCSPContext.h"
68
#include "mozilla/dom/ScriptSettings.h"
69
#include "mozilla/ClearOnShutdown.h"
70
#include "mozilla/StaticPtr.h"
71
#include "mozilla/dom/WorkerCommon.h"
72
#include "mozilla/dom/WorkerPrivate.h"
73
#include "nsContentUtils.h"
74
#include "nsJSUtils.h"
75
#include "nsILoadInfo.h"
76
#include "nsIDOMXULCommandDispatcher.h"
77
#include "nsITreeSelection.h"
78
79
// This should be probably defined on some other place... but I couldn't find it
80
#define WEBAPPS_PERM_NAME "webapps-manage"
81
82
using namespace mozilla;
83
using namespace mozilla::dom;
84
85
nsIIOService* nsScriptSecurityManager::sIOService = nullptr;
86
JSContext* nsScriptSecurityManager::sContext = nullptr;
87
bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
88
89
namespace {
90
91
class BundleHelper {
92
public:
93
NS_INLINE_DECL_REFCOUNTING(BundleHelper)
94
95
static nsIStringBundle* GetOrCreate() {
96
MOZ_ASSERT(!sShutdown);
97
98
// Already shutting down. Nothing should require the use of the string
99
// bundle when shutting down.
100
if (sShutdown) {
101
return nullptr;
102
}
103
104
if (!sSelf) {
105
sSelf = new BundleHelper();
106
}
107
108
return sSelf->GetOrCreateInternal();
109
}
110
111
static void Shutdown() {
112
sSelf = nullptr;
113
sShutdown = true;
114
}
115
116
private:
117
~BundleHelper() = default;
118
119
nsIStringBundle* GetOrCreateInternal() {
120
if (!mBundle) {
121
nsCOMPtr<nsIStringBundleService> bundleService =
122
mozilla::services::GetStringBundleService();
123
if (NS_WARN_IF(!bundleService)) {
124
return nullptr;
125
}
126
127
nsresult rv = bundleService->CreateBundle(
129
getter_AddRefs(mBundle));
130
if (NS_WARN_IF(NS_FAILED(rv))) {
131
return nullptr;
132
}
133
}
134
135
return mBundle;
136
}
137
138
nsCOMPtr<nsIStringBundle> mBundle;
139
140
static StaticRefPtr<BundleHelper> sSelf;
141
static bool sShutdown;
142
};
143
144
StaticRefPtr<BundleHelper> BundleHelper::sSelf;
145
bool BundleHelper::sShutdown = false;
146
147
} // namespace
148
149
///////////////////////////
150
// Convenience Functions //
151
///////////////////////////
152
153
class nsAutoInPrincipalDomainOriginSetter {
154
public:
155
nsAutoInPrincipalDomainOriginSetter() { ++sInPrincipalDomainOrigin; }
156
~nsAutoInPrincipalDomainOriginSetter() { --sInPrincipalDomainOrigin; }
157
static uint32_t sInPrincipalDomainOrigin;
158
};
159
uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin;
160
161
static nsresult GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin) {
162
if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) {
163
// Allow a single recursive call to GetPrincipalDomainOrigin, since that
164
// might be happening on a different principal from the first call. But
165
// after that, cut off the recursion; it just indicates that something
166
// we're doing in this method causes us to reenter a security check here.
167
return NS_ERROR_NOT_AVAILABLE;
168
}
169
170
nsAutoInPrincipalDomainOriginSetter autoSetter;
171
172
nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
173
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
174
175
nsAutoCString hostPort;
176
177
nsresult rv = uri->GetHostPort(hostPort);
178
if (NS_SUCCEEDED(rv)) {
179
nsAutoCString scheme;
180
rv = uri->GetScheme(scheme);
181
NS_ENSURE_SUCCESS(rv, rv);
182
aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
183
} else {
184
// Some URIs (e.g., nsSimpleURI) don't support host. Just
185
// get the full spec.
186
rv = uri->GetSpec(aOrigin);
187
NS_ENSURE_SUCCESS(rv, rv);
188
}
189
190
return NS_OK;
191
}
192
193
static nsresult GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
194
nsACString& aOrigin) {
195
nsCOMPtr<nsIURI> uri;
196
aPrincipal->GetDomain(getter_AddRefs(uri));
197
if (!uri) {
198
aPrincipal->GetURI(getter_AddRefs(uri));
199
}
200
NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
201
202
return GetOriginFromURI(uri, aOrigin);
203
}
204
205
inline void SetPendingExceptionASCII(JSContext* cx, const char* aMsg) {
206
JS_ReportErrorASCII(cx, "%s", aMsg);
207
}
208
209
inline void SetPendingException(JSContext* cx, const char16_t* aMsg) {
210
NS_ConvertUTF16toUTF8 msg(aMsg);
211
JS_ReportErrorUTF8(cx, "%s", msg.get());
212
}
213
214
/* static */
215
bool nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
216
nsIURI* aTargetURI) {
217
return NS_SecurityCompareURIs(aSourceURI, aTargetURI,
218
sStrictFileOriginPolicy);
219
}
220
221
// SecurityHashURI is consistent with SecurityCompareURIs because
222
// NS_SecurityHashURI is consistent with NS_SecurityCompareURIs. See
223
// nsNetUtil.h.
224
uint32_t nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI) {
225
return NS_SecurityHashURI(aURI);
226
}
227
228
/*
229
* GetChannelResultPrincipal will return the principal that the resource
230
* returned by this channel will use. For example, if the resource is in
231
* a sandbox, it will return the nullprincipal. If the resource is forced
232
* to inherit principal, it will return the principal of its parent. If
233
* the load doesn't require sandboxing or inheriting, it will return the same
234
* principal as GetChannelURIPrincipal. Namely the principal of the URI
235
* that is being loaded.
236
*/
237
NS_IMETHODIMP
238
nsScriptSecurityManager::GetChannelResultPrincipal(nsIChannel* aChannel,
239
nsIPrincipal** aPrincipal) {
240
return GetChannelResultPrincipal(aChannel, aPrincipal,
241
/*aIgnoreSandboxing*/ false);
242
}
243
244
nsresult nsScriptSecurityManager::GetChannelResultPrincipalIfNotSandboxed(
245
nsIChannel* aChannel, nsIPrincipal** aPrincipal) {
246
return GetChannelResultPrincipal(aChannel, aPrincipal,
247
/*aIgnoreSandboxing*/ true);
248
}
249
250
NS_IMETHODIMP
251
nsScriptSecurityManager::GetChannelResultStoragePrincipal(
252
nsIChannel* aChannel, nsIPrincipal** aPrincipal) {
253
nsCOMPtr<nsIPrincipal> principal;
254
nsresult rv = GetChannelResultPrincipal(aChannel, getter_AddRefs(principal),
255
/*aIgnoreSandboxing*/ false);
256
if (NS_WARN_IF(NS_FAILED(rv))) {
257
return rv;
258
}
259
260
return StoragePrincipalHelper::Create(aChannel, principal, aPrincipal);
261
}
262
263
NS_IMETHODIMP
264
nsScriptSecurityManager::GetChannelResultPrincipals(
265
nsIChannel* aChannel, nsIPrincipal** aPrincipal,
266
nsIPrincipal** aStoragePrincipal) {
267
nsresult rv = GetChannelResultPrincipal(aChannel, aPrincipal,
268
/*aIgnoreSandboxing*/ false);
269
if (NS_WARN_IF(NS_FAILED(rv))) {
270
return rv;
271
}
272
273
return StoragePrincipalHelper::Create(aChannel, *aPrincipal,
274
aStoragePrincipal);
275
}
276
277
nsresult nsScriptSecurityManager::GetChannelResultPrincipal(
278
nsIChannel* aChannel, nsIPrincipal** aPrincipal, bool aIgnoreSandboxing) {
279
MOZ_ASSERT(aChannel, "Must have channel!");
280
281
// Check whether we have an nsILoadInfo that says what we should do.
282
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
283
if (loadInfo->GetForceInheritPrincipalOverruleOwner()) {
284
nsCOMPtr<nsIPrincipal> principalToInherit =
285
loadInfo->FindPrincipalToInherit(aChannel);
286
principalToInherit.forget(aPrincipal);
287
return NS_OK;
288
}
289
290
nsCOMPtr<nsISupports> owner;
291
aChannel->GetOwner(getter_AddRefs(owner));
292
if (owner) {
293
CallQueryInterface(owner, aPrincipal);
294
if (*aPrincipal) {
295
return NS_OK;
296
}
297
}
298
299
if (!aIgnoreSandboxing && loadInfo->GetLoadingSandboxed()) {
300
nsCOMPtr<nsIPrincipal> sandboxedLoadingPrincipal =
301
loadInfo->GetSandboxedLoadingPrincipal();
302
MOZ_ASSERT(sandboxedLoadingPrincipal);
303
sandboxedLoadingPrincipal.forget(aPrincipal);
304
return NS_OK;
305
}
306
307
bool forceInherit = loadInfo->GetForceInheritPrincipal();
308
if (aIgnoreSandboxing && !forceInherit) {
309
// Check if SEC_FORCE_INHERIT_PRINCIPAL was dropped because of
310
// sandboxing:
311
if (loadInfo->GetLoadingSandboxed() &&
312
loadInfo->GetForceInheritPrincipalDropped()) {
313
forceInherit = true;
314
}
315
}
316
if (forceInherit) {
317
nsCOMPtr<nsIPrincipal> principalToInherit =
318
loadInfo->FindPrincipalToInherit(aChannel);
319
principalToInherit.forget(aPrincipal);
320
return NS_OK;
321
}
322
323
auto securityMode = loadInfo->GetSecurityMode();
324
// The data: inheritance flags should only apply to the initial load,
325
// not to loads that it might have redirected to.
326
if (loadInfo->RedirectChain().IsEmpty() &&
327
(securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
328
securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
329
securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS)) {
330
nsCOMPtr<nsIURI> uri;
331
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
332
NS_ENSURE_SUCCESS(rv, rv);
333
334
nsCOMPtr<nsIPrincipal> principalToInherit =
335
loadInfo->FindPrincipalToInherit(aChannel);
336
bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
337
338
if (nsContentUtils::ChannelShouldInheritPrincipal(
339
principalToInherit, uri, inheritForAboutBlank, false)) {
340
principalToInherit.forget(aPrincipal);
341
return NS_OK;
342
}
343
}
344
return GetChannelURIPrincipal(aChannel, aPrincipal);
345
}
346
347
/* The principal of the URI that this channel is loading. This is never
348
* affected by things like sandboxed loads, or loads where we forcefully
349
* inherit the principal. Think of this as the principal of the server
350
* which this channel is loading from. Most callers should use
351
* GetChannelResultPrincipal instead of GetChannelURIPrincipal. Only
352
* call GetChannelURIPrincipal if you are sure that you want the
353
* principal that matches the uri, even in cases when the load is
354
* sandboxed or when the load could be a blob or data uri (i.e even when
355
* you encounter loads that may or may not be sandboxed and loads
356
* that may or may not inherit)."
357
*/
358
NS_IMETHODIMP
359
nsScriptSecurityManager::GetChannelURIPrincipal(nsIChannel* aChannel,
360
nsIPrincipal** aPrincipal) {
361
MOZ_ASSERT(aChannel, "Must have channel!");
362
363
// Get the principal from the URI. Make sure this does the same thing
364
// as Document::Reset and XULDocument::StartDocumentLoad.
365
nsCOMPtr<nsIURI> uri;
366
nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
367
NS_ENSURE_SUCCESS(rv, rv);
368
369
nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
370
371
// Inherit the origin attributes from loadInfo.
372
// If this is a top-level document load, the origin attributes of the
373
// loadInfo will be set from nsDocShell::DoURILoad.
374
// For subresource loading, the origin attributes of the loadInfo is from
375
// its loadingPrincipal.
376
OriginAttributes attrs = loadInfo->GetOriginAttributes();
377
378
nsCOMPtr<nsIPrincipal> prin =
379
BasePrincipal::CreateContentPrincipal(uri, attrs);
380
prin.forget(aPrincipal);
381
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
382
}
383
384
/////////////////////////////
385
// nsScriptSecurityManager //
386
/////////////////////////////
387
388
////////////////////////////////////
389
// Methods implementing ISupports //
390
////////////////////////////////////
391
NS_IMPL_ISUPPORTS(nsScriptSecurityManager, nsIScriptSecurityManager)
392
393
///////////////////////////////////////////////////
394
// Methods implementing nsIScriptSecurityManager //
395
///////////////////////////////////////////////////
396
397
///////////////// Security Checks /////////////////
398
399
bool nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(
400
JSContext* cx, JS::HandleValue aValue) {
401
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
402
403
#if defined(DEBUG) && !defined(ANDROID)
404
nsCOMPtr<nsIPrincipal> subjectPrincipal = nsContentUtils::SubjectPrincipal();
405
nsContentSecurityManager::AssertEvalNotUsingSystemPrincipal(subjectPrincipal,
406
cx);
407
#endif
408
409
// Get the window, if any, corresponding to the current global
410
nsCOMPtr<nsIContentSecurityPolicy> csp;
411
if (nsGlobalWindowInner* win = xpc::CurrentWindowOrNull(cx)) {
412
csp = win->GetCsp();
413
}
414
415
// don't do anything unless there's a CSP
416
if (!csp) return true;
417
418
nsCOMPtr<nsICSPEventListener> cspEventListener;
419
if (!NS_IsMainThread()) {
420
WorkerPrivate* workerPrivate =
421
mozilla::dom::GetWorkerPrivateFromContext(cx);
422
if (workerPrivate) {
423
cspEventListener = workerPrivate->CSPEventListener();
424
}
425
}
426
427
bool evalOK = true;
428
bool reportViolation = false;
429
nsresult rv = csp->GetAllowsEval(&reportViolation, &evalOK);
430
431
if (NS_FAILED(rv)) {
432
NS_WARNING("CSP: failed to get allowsEval");
433
return true; // fail open to not break sites.
434
}
435
436
if (reportViolation) {
437
JS::Rooted<JSString*> jsString(cx, JS::ToString(cx, aValue));
438
if (NS_WARN_IF(!jsString)) {
439
JS_ClearPendingException(cx);
440
return false;
441
}
442
443
nsAutoJSString scriptSample;
444
if (NS_WARN_IF(!scriptSample.init(cx, jsString))) {
445
JS_ClearPendingException(cx);
446
return false;
447
}
448
449
JS::AutoFilename scriptFilename;
450
nsAutoString fileName;
451
unsigned lineNum = 0;
452
unsigned columnNum = 0;
453
if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum, &columnNum)) {
454
if (const char* file = scriptFilename.get()) {
455
CopyUTF8toUTF16(nsDependentCString(file), fileName);
456
}
457
} else {
458
MOZ_ASSERT(!JS_IsExceptionPending(cx));
459
}
460
csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
461
nullptr, // triggering element
462
cspEventListener, fileName, scriptSample, lineNum,
463
columnNum, EmptyString(), EmptyString());
464
}
465
466
return evalOK;
467
}
468
469
// static
470
bool nsScriptSecurityManager::JSPrincipalsSubsume(JSPrincipals* first,
471
JSPrincipals* second) {
472
return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second));
473
}
474
475
NS_IMETHODIMP
476
nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
477
nsIURI* aTargetURI,
478
bool reportError,
479
bool aFromPrivateWindow) {
480
// Please note that aFromPrivateWindow is only 100% accurate if
481
// reportError is true.
482
if (!SecurityCompareURIs(aSourceURI, aTargetURI)) {
483
if (reportError) {
484
ReportError("CheckSameOriginError", aSourceURI, aTargetURI,
485
aFromPrivateWindow);
486
}
487
return NS_ERROR_DOM_BAD_URI;
488
}
489
return NS_OK;
490
}
491
492
/*static*/
493
uint32_t nsScriptSecurityManager::HashPrincipalByOrigin(
494
nsIPrincipal* aPrincipal) {
495
nsCOMPtr<nsIURI> uri;
496
aPrincipal->GetDomain(getter_AddRefs(uri));
497
if (!uri) aPrincipal->GetURI(getter_AddRefs(uri));
498
return SecurityHashURI(uri);
499
}
500
501
NS_IMETHODIMP
502
nsScriptSecurityManager::CheckLoadURIFromScript(JSContext* cx, nsIURI* aURI) {
503
// Get principal of currently executing script.
504
MOZ_ASSERT(cx == nsContentUtils::GetCurrentJSContext());
505
nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
506
nsresult rv = CheckLoadURIWithPrincipal(principal, aURI,
507
nsIScriptSecurityManager::STANDARD);
508
if (NS_SUCCEEDED(rv)) {
509
// OK to load
510
return NS_OK;
511
}
512
513
// Report error.
514
nsAutoCString spec;
515
if (NS_FAILED(aURI->GetAsciiSpec(spec))) return NS_ERROR_FAILURE;
516
nsAutoCString msg("Access to '");
517
msg.Append(spec);
518
msg.AppendLiteral("' from script denied");
519
SetPendingExceptionASCII(cx, msg.get());
520
return NS_ERROR_DOM_BAD_URI;
521
}
522
523
/**
524
* Helper method to handle cases where a flag passed to
525
* CheckLoadURIWithPrincipal means denying loading if the given URI has certain
526
* nsIProtocolHandler flags set.
527
* @return if success, access is allowed. Otherwise, deny access
528
*/
529
static nsresult DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags) {
530
MOZ_ASSERT(aURI, "Must have URI!");
531
532
bool uriHasFlags;
533
nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags);
534
NS_ENSURE_SUCCESS(rv, rv);
535
536
if (uriHasFlags) {
537
return NS_ERROR_DOM_BAD_URI;
538
}
539
540
return NS_OK;
541
}
542
543
static bool EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase) {
544
nsresult rv;
545
nsCOMPtr<nsIURI> probe = aProbeArg;
546
547
nsCOMPtr<nsIEffectiveTLDService> tldService =
548
do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
549
NS_ENSURE_TRUE(tldService, false);
550
while (true) {
551
if (nsScriptSecurityManager::SecurityCompareURIs(probe, aBase)) {
552
return true;
553
}
554
555
nsAutoCString host, newHost;
556
rv = probe->GetHost(host);
557
NS_ENSURE_SUCCESS(rv, false);
558
559
rv = tldService->GetNextSubDomain(host, newHost);
560
if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
561
return false;
562
}
563
NS_ENSURE_SUCCESS(rv, false);
564
rv = NS_MutateURI(probe).SetHost(newHost).Finalize(probe);
565
NS_ENSURE_SUCCESS(rv, false);
566
}
567
}
568
569
NS_IMETHODIMP
570
nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
571
nsIURI* aTargetURI,
572
uint32_t aFlags) {
573
MOZ_ASSERT(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
574
575
// If someone passes a flag that we don't understand, we should
576
// fail, because they may need a security check that we don't
577
// provide.
578
NS_ENSURE_FALSE(
579
aFlags &
580
~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
581
nsIScriptSecurityManager::ALLOW_CHROME |
582
nsIScriptSecurityManager::DISALLOW_SCRIPT |
583
nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
584
nsIScriptSecurityManager::DONT_REPORT_ERRORS),
585
NS_ERROR_UNEXPECTED);
586
NS_ENSURE_ARG_POINTER(aPrincipal);
587
NS_ENSURE_ARG_POINTER(aTargetURI);
588
589
// If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which
590
// would do such inheriting. That would be URIs that do not have their own
591
// security context. We do this even for the system principal.
592
if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) {
593
nsresult rv = DenyAccessIfURIHasFlags(
594
aTargetURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT);
595
NS_ENSURE_SUCCESS(rv, rv);
596
}
597
598
if (aPrincipal == mSystemPrincipal) {
599
// Allow access
600
return NS_OK;
601
}
602
603
nsCOMPtr<nsIURI> sourceURI;
604
aPrincipal->GetURI(getter_AddRefs(sourceURI));
605
if (!sourceURI) {
606
auto* basePrin = BasePrincipal::Cast(aPrincipal);
607
if (basePrin->Is<ExpandedPrincipal>()) {
608
auto expanded = basePrin->As<ExpandedPrincipal>();
609
for (auto& prin : expanded->AllowList()) {
610
nsresult rv = CheckLoadURIWithPrincipal(prin, aTargetURI, aFlags);
611
if (NS_SUCCEEDED(rv)) {
612
// Allow access if it succeeded with one of the allowlisted principals
613
return NS_OK;
614
}
615
}
616
// None of our allowlisted principals worked.
617
return NS_ERROR_DOM_BAD_URI;
618
}
619
NS_ERROR(
620
"Non-system principals or expanded principal passed to "
621
"CheckLoadURIWithPrincipal "
622
"must have a URI!");
623
return NS_ERROR_UNEXPECTED;
624
}
625
626
// Automatic loads are not allowed from certain protocols.
627
if (aFlags &
628
nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) {
629
nsresult rv = DenyAccessIfURIHasFlags(
630
sourceURI,
631
nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT);
632
NS_ENSURE_SUCCESS(rv, rv);
633
}
634
635
// If either URI is a nested URI, get the base URI
636
nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI);
637
nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
638
639
//-- get the target scheme
640
nsAutoCString targetScheme;
641
nsresult rv = targetBaseURI->GetScheme(targetScheme);
642
if (NS_FAILED(rv)) return rv;
643
644
//-- Some callers do not allow loading javascript:
645
if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) &&
646
targetScheme.EqualsLiteral("javascript")) {
647
return NS_ERROR_DOM_BAD_URI;
648
}
649
650
// Check for uris that are only loadable by principals that subsume them
651
bool hasFlags;
652
rv = NS_URIChainHasFlags(
653
targetBaseURI, nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS, &hasFlags);
654
NS_ENSURE_SUCCESS(rv, rv);
655
656
if (hasFlags) {
657
// check nothing else in the URI chain has flags that prevent
658
// access:
659
rv = CheckLoadURIFlags(
660
sourceURI, aTargetURI, sourceBaseURI, targetBaseURI, aFlags,
661
aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0);
662
NS_ENSURE_SUCCESS(rv, rv);
663
// Check the principal is allowed to load the target.
664
return aPrincipal->CheckMayLoad(targetBaseURI, true, false);
665
}
666
667
//-- get the source scheme
668
nsAutoCString sourceScheme;
669
rv = sourceBaseURI->GetScheme(sourceScheme);
670
if (NS_FAILED(rv)) return rv;
671
672
// When comparing schemes, if the relevant pref is set, view-source URIs
673
// are reachable from same-protocol (so e.g. file: can link to
674
// view-source:file). This is required for reftests.
675
static bool sViewSourceReachableFromInner = false;
676
static bool sCachedViewSourcePref = false;
677
if (!sCachedViewSourcePref) {
678
sCachedViewSourcePref = true;
679
mozilla::Preferences::AddBoolVarCache(
680
&sViewSourceReachableFromInner,
681
"security.view-source.reachable-from-inner-protocol");
682
}
683
684
bool targetIsViewSource = false;
685
686
if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
687
// A null principal can target its own URI.
688
if (sourceURI == aTargetURI) {
689
return NS_OK;
690
}
691
} else if (sViewSourceReachableFromInner &&
692
sourceScheme.EqualsIgnoreCase(targetScheme.get()) &&
693
NS_SUCCEEDED(
694
aTargetURI->SchemeIs("view-source", &targetIsViewSource)) &&
695
targetIsViewSource) {
696
// exception for foo: linking to view-source:foo for reftests...
697
return NS_OK;
698
} else if (sourceScheme.EqualsIgnoreCase("file") &&
699
targetScheme.EqualsIgnoreCase("moz-icon")) {
700
// exception for file: linking to moz-icon://.ext?size=...
701
// Note that because targetScheme is the base (innermost) URI scheme,
702
// this does NOT allow file -> moz-icon:file:///... links.
703
// This is intentional.
704
return NS_OK;
705
}
706
707
// Check for webextension
708
rv = NS_URIChainHasFlags(
709
aTargetURI, nsIProtocolHandler::URI_LOADABLE_BY_EXTENSIONS, &hasFlags);
710
NS_ENSURE_SUCCESS(rv, rv);
711
712
if (hasFlags && BasePrincipal::Cast(aPrincipal)->AddonPolicy()) {
713
return NS_OK;
714
}
715
716
// If we get here, check all the schemes can link to each other, from the top
717
// down:
718
nsCaseInsensitiveCStringComparator stringComparator;
719
nsCOMPtr<nsIURI> currentURI = sourceURI;
720
nsCOMPtr<nsIURI> currentOtherURI = aTargetURI;
721
722
bool denySameSchemeLinks = false;
723
rv = NS_URIChainHasFlags(aTargetURI,
724
nsIProtocolHandler::URI_SCHEME_NOT_SELF_LINKABLE,
725
&denySameSchemeLinks);
726
if (NS_FAILED(rv)) return rv;
727
728
while (currentURI && currentOtherURI) {
729
nsAutoCString scheme, otherScheme;
730
currentURI->GetScheme(scheme);
731
currentOtherURI->GetScheme(otherScheme);
732
733
bool schemesMatch = scheme.Equals(otherScheme, stringComparator);
734
bool isSamePage = false;
735
// about: URIs are special snowflakes.
736
if (scheme.EqualsLiteral("about") && schemesMatch) {
737
nsAutoCString moduleName, otherModuleName;
738
// about: pages can always link to themselves:
739
isSamePage =
740
NS_SUCCEEDED(NS_GetAboutModuleName(currentURI, moduleName)) &&
741
NS_SUCCEEDED(
742
NS_GetAboutModuleName(currentOtherURI, otherModuleName)) &&
743
moduleName.Equals(otherModuleName);
744
if (!isSamePage) {
745
// We will have allowed the load earlier if the source page has
746
// system principal. So we know the source has a content
747
// principal, and it's trying to link to something else.
748
// Linkable about: pages are always reachable, even if we hit
749
// the CheckLoadURIFlags call below.
750
// We punch only 1 other hole: iff the source is unlinkable,
751
// we let them link to other pages explicitly marked SAFE
752
// for content. This avoids world-linkable about: pages linking
753
// to non-world-linkable about: pages.
754
nsCOMPtr<nsIAboutModule> module, otherModule;
755
bool knowBothModules =
756
NS_SUCCEEDED(
757
NS_GetAboutModule(currentURI, getter_AddRefs(module))) &&
758
NS_SUCCEEDED(NS_GetAboutModule(currentOtherURI,
759
getter_AddRefs(otherModule)));
760
uint32_t aboutModuleFlags = 0;
761
uint32_t otherAboutModuleFlags = 0;
762
knowBothModules =
763
knowBothModules &&
764
NS_SUCCEEDED(module->GetURIFlags(currentURI, &aboutModuleFlags)) &&
765
NS_SUCCEEDED(otherModule->GetURIFlags(currentOtherURI,
766
&otherAboutModuleFlags));
767
if (knowBothModules) {
768
isSamePage = !(aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) &&
769
(otherAboutModuleFlags &
770
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT);
771
if (isSamePage &&
772
otherAboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) {
773
// XXXgijs: this is a hack. The target will be nested
774
// (with innerURI of moz-safe-about:whatever), and
775
// the source isn't, so we won't pass if we finish
776
// the loop. We *should* pass, though, so return here.
777
// This hack can go away when bug 1228118 is fixed.
778
return NS_OK;
779
}
780
}
781
}
782
} else {
783
bool equalExceptRef = false;
784
rv = currentURI->EqualsExceptRef(currentOtherURI, &equalExceptRef);
785
isSamePage = NS_SUCCEEDED(rv) && equalExceptRef;
786
}
787
788
// If schemes are not equal, or they're equal but the target URI
789
// is different from the source URI and doesn't always allow linking
790
// from the same scheme, check if the URI flags of the current target
791
// URI allow the current source URI to link to it.
792
// The policy is specified by the protocol flags on both URIs.
793
if (!schemesMatch || (denySameSchemeLinks && !isSamePage)) {
794
return CheckLoadURIFlags(
795
currentURI, currentOtherURI, sourceBaseURI, targetBaseURI, aFlags,
796
aPrincipal->OriginAttributesRef().mPrivateBrowsingId > 0);
797
}
798
// Otherwise... check if we can nest another level:
799
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
800
nsCOMPtr<nsINestedURI> nestedOtherURI = do_QueryInterface(currentOtherURI);
801
802
// If schemes match and neither URI is nested further, we're OK.
803
if (!nestedURI && !nestedOtherURI) {
804
return NS_OK;
805
}
806
// If one is nested and the other isn't, something is wrong.
807
if (!nestedURI != !nestedOtherURI) {
808
return NS_ERROR_DOM_BAD_URI;
809
}
810
// Otherwise, both should be nested and we'll go through the loop again.
811
nestedURI->GetInnerURI(getter_AddRefs(currentURI));
812
nestedOtherURI->GetInnerURI(getter_AddRefs(currentOtherURI));
813
}
814
815
// We should never get here. We should always return from inside the loop.
816
return NS_ERROR_DOM_BAD_URI;
817
}
818
819
/**
820
* Helper method to check whether the target URI and its innermost ("base") URI
821
* has protocol flags that should stop it from being loaded by the source URI
822
* (and/or the source URI's innermost ("base") URI), taking into account any
823
* nsIScriptSecurityManager flags originally passed to
824
* CheckLoadURIWithPrincipal and friends.
825
*
826
* @return if success, access is allowed. Otherwise, deny access
827
*/
828
nsresult nsScriptSecurityManager::CheckLoadURIFlags(
829
nsIURI* aSourceURI, nsIURI* aTargetURI, nsIURI* aSourceBaseURI,
830
nsIURI* aTargetBaseURI, uint32_t aFlags, bool aFromPrivateWindow) {
831
// Note that the order of policy checks here is very important!
832
// We start from most restrictive and work our way down.
833
bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS);
834
const char* errorTag = "CheckLoadURIError";
835
836
nsAutoCString targetScheme;
837
nsresult rv = aTargetBaseURI->GetScheme(targetScheme);
838
if (NS_FAILED(rv)) return rv;
839
840
// Check for system target URI
841
rv = DenyAccessIfURIHasFlags(aTargetURI,
842
nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
843
if (NS_FAILED(rv)) {
844
// Deny access, since the origin principal is not system
845
if (reportErrors) {
846
ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
847
}
848
return rv;
849
}
850
851
// Used by ExtensionProtocolHandler to prevent loading extension resources
852
// in private contexts if the extension does not have permission.
853
if (aFromPrivateWindow) {
854
rv = DenyAccessIfURIHasFlags(
855
aTargetURI, nsIProtocolHandler::URI_DISALLOW_IN_PRIVATE_CONTEXT);
856
if (NS_FAILED(rv)) {
857
if (reportErrors) {
858
ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
859
}
860
return rv;
861
}
862
}
863
864
// Check for chrome target URI
865
bool hasFlags = false;
866
rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
867
&hasFlags);
868
NS_ENSURE_SUCCESS(rv, rv);
869
if (hasFlags) {
870
if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
871
// Allow a URI_IS_UI_RESOURCE source to link to a URI_IS_UI_RESOURCE
872
// target if ALLOW_CHROME is set.
873
//
874
// ALLOW_CHROME is a flag that we pass on all loads _except_ docshell
875
// loads (since docshell loads run the loaded content with its origin
876
// principal). So we're effectively allowing resource://, chrome://,
877
// and moz-icon:// source URIs to load resource://, chrome://, and
878
// moz-icon:// files, so long as they're not loading it as a document.
879
bool sourceIsUIResource;
880
rv = NS_URIChainHasFlags(aSourceBaseURI,
881
nsIProtocolHandler::URI_IS_UI_RESOURCE,
882
&sourceIsUIResource);
883
NS_ENSURE_SUCCESS(rv, rv);
884
if (sourceIsUIResource) {
885
return NS_OK;
886
}
887
888
if (targetScheme.EqualsLiteral("resource")) {
889
// Mochitests that need to load resource:// URIs not declared
890
// content-accessible in manifests should set the preference
891
// "security.all_resource_uri_content_accessible" true.
892
static bool sSecurityPrefCached = false;
893
static bool sAllResourceUriContentAccessible = false;
894
if (!sSecurityPrefCached) {
895
sSecurityPrefCached = true;
896
Preferences::AddBoolVarCache(
897
&sAllResourceUriContentAccessible,
898
"security.all_resource_uri_content_accessible", false);
899
}
900
if (sAllResourceUriContentAccessible) {
901
return NS_OK;
902
}
903
904
nsCOMPtr<nsIProtocolHandler> ph;
905
rv = sIOService->GetProtocolHandler("resource", getter_AddRefs(ph));
906
NS_ENSURE_SUCCESS(rv, rv);
907
if (!ph) {
908
return NS_ERROR_DOM_BAD_URI;
909
}
910
911
nsCOMPtr<nsIResProtocolHandler> rph = do_QueryInterface(ph);
912
if (!rph) {
913
return NS_ERROR_DOM_BAD_URI;
914
}
915
916
bool accessAllowed = false;
917
rph->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
918
if (accessAllowed) {
919
return NS_OK;
920
}
921
} else if (targetScheme.EqualsLiteral("chrome")) {
922
// Allow the load only if the chrome package is allowlisted.
923
nsCOMPtr<nsIXULChromeRegistry> reg(
924
do_GetService(NS_CHROMEREGISTRY_CONTRACTID));
925
if (reg) {
926
bool accessAllowed = false;
927
reg->AllowContentToAccess(aTargetBaseURI, &accessAllowed);
928
if (accessAllowed) {
929
return NS_OK;
930
}
931
}
932
}
933
}
934
935
if (reportErrors) {
936
ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
937
}
938
return NS_ERROR_DOM_BAD_URI;
939
}
940
941
// Check for target URI pointing to a file
942
rv = NS_URIChainHasFlags(aTargetURI, nsIProtocolHandler::URI_IS_LOCAL_FILE,
943
&hasFlags);
944
NS_ENSURE_SUCCESS(rv, rv);
945
if (hasFlags) {
946
// Allow domains that were allowlisted in the prefs. In 99.9% of cases,
947
// this array is empty.
948
bool isAllowlisted;
949
MOZ_ALWAYS_SUCCEEDS(InFileURIAllowlist(aSourceURI, &isAllowlisted));
950
if (isAllowlisted) {
951
return NS_OK;
952
}
953
954
// Allow chrome://
955
bool isChrome = false;
956
if (NS_SUCCEEDED(aSourceBaseURI->SchemeIs("chrome", &isChrome)) &&
957
isChrome) {
958
return NS_OK;
959
}
960
961
// Nothing else.
962
if (reportErrors) {
963
ReportError(errorTag, aSourceURI, aTargetURI, aFromPrivateWindow);
964
}
965
return NS_ERROR_DOM_BAD_URI;
966
}
967
968
// OK, everyone is allowed to load this, since unflagged handlers are
969
// deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
970
// need to warn. At some point we'll want to make this warning into an
971
// error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
972
rv = NS_URIChainHasFlags(
973
aTargetBaseURI, nsIProtocolHandler::URI_LOADABLE_BY_ANYONE, &hasFlags);
974
NS_ENSURE_SUCCESS(rv, rv);
975
// NB: we also get here if the base URI is URI_LOADABLE_BY_SUBSUMERS,
976
// and none of the rest of the nested chain of URIs for aTargetURI
977
// prohibits the load, so avoid warning in that case:
978
bool hasSubsumersFlag = false;
979
rv = NS_URIChainHasFlags(aTargetBaseURI,
980
nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
981
&hasSubsumersFlag);
982
NS_ENSURE_SUCCESS(rv, rv);
983
if (!hasFlags && !hasSubsumersFlag) {
984
nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
985
if (bundle) {
986
nsAutoString message;
987
AutoTArray<nsString, 1> formatStrings;
988
CopyASCIItoUTF16(targetScheme, *formatStrings.AppendElement());
989
rv = bundle->FormatStringFromName("ProtocolFlagError", formatStrings,
990
message);
991
if (NS_SUCCEEDED(rv)) {
992
nsCOMPtr<nsIConsoleService> console(
993
do_GetService("@mozilla.org/consoleservice;1"));
994
NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
995
996
console->LogStringMessage(message.get());
997
}
998
}
999
}
1000
1001
return NS_OK;
1002
}
1003
1004
nsresult nsScriptSecurityManager::ReportError(const char* aMessageTag,
1005
nsIURI* aSource, nsIURI* aTarget,
1006
bool aFromPrivateWindow) {
1007
nsresult rv;
1008
NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
1009
1010
// Get the source URL spec
1011
nsAutoCString sourceSpec;
1012
rv = aSource->GetAsciiSpec(sourceSpec);
1013
NS_ENSURE_SUCCESS(rv, rv);
1014
1015
// Get the target URL spec
1016
nsAutoCString targetSpec;
1017
rv = aTarget->GetAsciiSpec(targetSpec);
1018
NS_ENSURE_SUCCESS(rv, rv);
1019
1020
nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
1021
if (NS_WARN_IF(!bundle)) {
1022
return NS_OK;
1023
}
1024
1025
// Localize the error message
1026
nsAutoString message;
1027
AutoTArray<nsString, 2> formatStrings;
1028
CopyASCIItoUTF16(sourceSpec, *formatStrings.AppendElement());
1029
CopyASCIItoUTF16(targetSpec, *formatStrings.AppendElement());
1030
rv = bundle->FormatStringFromName(aMessageTag, formatStrings, message);
1031
NS_ENSURE_SUCCESS(rv, rv);
1032
1033
nsCOMPtr<nsIConsoleService> console(
1034
do_GetService(NS_CONSOLESERVICE_CONTRACTID));
1035
NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
1036
nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
1037
NS_ENSURE_TRUE(error, NS_ERROR_FAILURE);
1038
1039
// using category of "SOP" so we can link to MDN
1040
rv = error->Init(message, EmptyString(), EmptyString(), 0, 0,
1041
nsIScriptError::errorFlag, "SOP", aFromPrivateWindow,
1042
true /* From chrome context */);
1043
NS_ENSURE_SUCCESS(rv, rv);
1044
console->LogMessage(error);
1045
return NS_OK;
1046
}
1047
1048
NS_IMETHODIMP
1049
nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(
1050
nsIPrincipal* aPrincipal, const nsACString& aTargetURIStr,
1051
uint32_t aFlags) {
1052
nsresult rv;
1053
nsCOMPtr<nsIURI> target;
1054
rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr);
1055
NS_ENSURE_SUCCESS(rv, rv);
1056
1057
rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
1058
if (rv == NS_ERROR_DOM_BAD_URI) {
1059
// Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
1060
// return values.
1061
return rv;
1062
}
1063
NS_ENSURE_SUCCESS(rv, rv);
1064
1065
// Now start testing fixup -- since aTargetURIStr is a string, not
1066
// an nsIURI, we may well end up fixing it up before loading.
1067
// Note: This needs to stay in sync with the nsIURIFixup api.
1068
nsCOMPtr<nsIURIFixup> fixup = components::URIFixup::Service();
1069
if (!fixup) {
1070
return rv;
1071
}
1072
1073
uint32_t flags[] = {nsIURIFixup::FIXUP_FLAG_NONE,
1074
nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS,
1075
nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
1076
nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
1077
nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
1078
nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI};
1079
1080
for (uint32_t i = 0; i < ArrayLength(flags); ++i) {
1081
rv = fixup->CreateFixupURI(aTargetURIStr, flags[i], nullptr,
1082
getter_AddRefs(target));
1083
NS_ENSURE_SUCCESS(rv, rv);
1084
1085
rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
1086
if (rv == NS_ERROR_DOM_BAD_URI) {
1087
// Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
1088
// return values.
1089
return rv;
1090
}
1091
NS_ENSURE_SUCCESS(rv, rv);
1092
}
1093
1094
return rv;
1095
}
1096
1097
NS_IMETHODIMP
1098
nsScriptSecurityManager::InFileURIAllowlist(nsIURI* aUri, bool* aResult) {
1099
MOZ_ASSERT(aUri);
1100
MOZ_ASSERT(aResult);
1101
1102
*aResult = false;
1103
for (nsIURI* uri : EnsureFileURIAllowlist()) {
1104
if (EqualOrSubdomain(aUri, uri)) {
1105
*aResult = true;
1106
return NS_OK;
1107
}
1108
}
1109
1110
return NS_OK;
1111
}
1112
1113
///////////////// Principals ///////////////////////
1114
1115
NS_IMETHODIMP
1116
nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal** result) {
1117
NS_ADDREF(*result = mSystemPrincipal);
1118
1119
return NS_OK;
1120
}
1121
1122
NS_IMETHODIMP
1123
nsScriptSecurityManager::CreateContentPrincipal(
1124
nsIURI* aURI, JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx,
1125
nsIPrincipal** aPrincipal) {
1126
OriginAttributes attrs;
1127
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1128
return NS_ERROR_INVALID_ARG;
1129
}
1130
nsCOMPtr<nsIPrincipal> prin =
1131
BasePrincipal::CreateContentPrincipal(aURI, attrs);
1132
prin.forget(aPrincipal);
1133
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1134
}
1135
1136
NS_IMETHODIMP
1137
nsScriptSecurityManager::CreateContentPrincipalFromOrigin(
1138
const nsACString& aOrigin, nsIPrincipal** aPrincipal) {
1139
if (StringBeginsWith(aOrigin, NS_LITERAL_CSTRING("["))) {
1140
return NS_ERROR_INVALID_ARG;
1141
}
1142
1143
if (StringBeginsWith(aOrigin,
1144
NS_LITERAL_CSTRING(NS_NULLPRINCIPAL_SCHEME ":"))) {
1145
return NS_ERROR_INVALID_ARG;
1146
}
1147
1148
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateContentPrincipal(aOrigin);
1149
prin.forget(aPrincipal);
1150
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1151
}
1152
1153
NS_IMETHODIMP
1154
nsScriptSecurityManager::PrincipalToJSON(nsIPrincipal* aPrincipal,
1155
nsACString& aJSON) {
1156
aJSON.Truncate();
1157
if (!aPrincipal) {
1158
return NS_ERROR_FAILURE;
1159
}
1160
1161
BasePrincipal::Cast(aPrincipal)->ToJSON(aJSON);
1162
1163
if (aJSON.IsEmpty()) {
1164
return NS_ERROR_FAILURE;
1165
}
1166
1167
return NS_OK;
1168
}
1169
1170
NS_IMETHODIMP
1171
nsScriptSecurityManager::JSONToPrincipal(const nsACString& aJSON,
1172
nsIPrincipal** aPrincipal) {
1173
if (aJSON.IsEmpty()) {
1174
return NS_ERROR_FAILURE;
1175
}
1176
1177
nsCOMPtr<nsIPrincipal> principal = BasePrincipal::FromJSON(aJSON);
1178
1179
if (!principal) {
1180
return NS_ERROR_FAILURE;
1181
}
1182
1183
principal.forget(aPrincipal);
1184
return NS_OK;
1185
}
1186
1187
NS_IMETHODIMP
1188
nsScriptSecurityManager::CreateNullPrincipal(
1189
JS::Handle<JS::Value> aOriginAttributes, JSContext* aCx,
1190
nsIPrincipal** aPrincipal) {
1191
OriginAttributes attrs;
1192
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1193
return NS_ERROR_INVALID_ARG;
1194
}
1195
nsCOMPtr<nsIPrincipal> prin = NullPrincipal::Create(attrs);
1196
prin.forget(aPrincipal);
1197
return NS_OK;
1198
}
1199
1200
NS_IMETHODIMP
1201
nsScriptSecurityManager::GetLoadContextContentPrincipal(
1202
nsIURI* aURI, nsILoadContext* aLoadContext, nsIPrincipal** aPrincipal) {
1203
NS_ENSURE_STATE(aLoadContext);
1204
OriginAttributes docShellAttrs;
1205
aLoadContext->GetOriginAttributes(docShellAttrs);
1206
1207
nsCOMPtr<nsIPrincipal> prin =
1208
BasePrincipal::CreateContentPrincipal(aURI, docShellAttrs);
1209
prin.forget(aPrincipal);
1210
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1211
}
1212
1213
NS_IMETHODIMP
1214
nsScriptSecurityManager::GetDocShellContentPrincipal(
1215
nsIURI* aURI, nsIDocShell* aDocShell, nsIPrincipal** aPrincipal) {
1216
nsCOMPtr<nsIPrincipal> prin = BasePrincipal::CreateContentPrincipal(
1217
aURI, nsDocShell::Cast(aDocShell)->GetOriginAttributes());
1218
prin.forget(aPrincipal);
1219
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
1220
}
1221
1222
NS_IMETHODIMP
1223
nsScriptSecurityManager::PrincipalWithOA(
1224
nsIPrincipal* aPrincipal, JS::Handle<JS::Value> aOriginAttributes,
1225
JSContext* aCx, nsIPrincipal** aReturnPrincipal) {
1226
if (!aPrincipal) {
1227
return NS_OK;
1228
}
1229
if (aPrincipal->GetIsContentPrincipal()) {
1230
OriginAttributes attrs;
1231
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
1232
return NS_ERROR_INVALID_ARG;
1233
}
1234
RefPtr<ContentPrincipal> copy = new ContentPrincipal();
1235
ContentPrincipal* contentPrincipal =
1236
static_cast<ContentPrincipal*>(aPrincipal);
1237
nsresult rv = copy->Init(contentPrincipal, attrs);
1238
NS_ENSURE_SUCCESS(rv, rv);
1239
copy.forget(aReturnPrincipal);
1240
} else {
1241
// We do this for null principals, system principals (both fine)
1242
// ... and expanded principals, where we should probably do something
1243
// cleverer, but I also don't think we care too much.
1244
nsCOMPtr<nsIPrincipal> prin = aPrincipal;
1245
prin.forget(aReturnPrincipal);
1246
}
1247
1248
return *aReturnPrincipal ? NS_OK : NS_ERROR_FAILURE;
1249
}
1250
1251
NS_IMETHODIMP
1252
nsScriptSecurityManager::CanCreateWrapper(JSContext* cx, const nsIID& aIID,
1253
nsISupports* aObj,
1254
nsIClassInfo* aClassInfo) {
1255
// XXX Special case for Exception ?
1256
1257
// We give remote-XUL allowlisted domains a free pass here. See bug 932906.
1258
JS::Rooted<JS::Realm*> contextRealm(cx, JS::GetCurrentRealmOrNull(cx));
1259
MOZ_RELEASE_ASSERT(contextRealm);
1260
if (!xpc::AllowContentXBLScope(contextRealm)) {
1261
return NS_OK;
1262
}
1263
1264
if (nsContentUtils::IsCallerChrome()) {
1265
return NS_OK;
1266
}
1267
1268
//-- Access denied, report an error
1269
nsAutoCString originUTF8;
1270
nsIPrincipal* subjectPrincipal = nsContentUtils::SubjectPrincipal();
1271
GetPrincipalDomainOrigin(subjectPrincipal, originUTF8);
1272
NS_ConvertUTF8toUTF16 originUTF16(originUTF8);
1273
nsAutoCString classInfoNameUTF8;
1274
if (aClassInfo) {
1275
aClassInfo->GetClassDescription(classInfoNameUTF8);
1276
}
1277
if (classInfoNameUTF8.IsEmpty()) {
1278
classInfoNameUTF8.AssignLiteral("UnnamedClass");
1279
}
1280
1281
nsCOMPtr<nsIStringBundle> bundle = BundleHelper::GetOrCreate();
1282
if (NS_WARN_IF(!bundle)) {
1283
return NS_OK;
1284
}
1285
1286
NS_ConvertUTF8toUTF16 classInfoUTF16(classInfoNameUTF8);
1287
nsresult rv;
1288
nsAutoString errorMsg;
1289
if (originUTF16.IsEmpty()) {
1290
AutoTArray<nsString, 1> formatStrings = {classInfoUTF16};
1291
rv = bundle->FormatStringFromName("CreateWrapperDenied", formatStrings,
1292
errorMsg);
1293
} else {
1294
AutoTArray<nsString, 2> formatStrings = {classInfoUTF16, originUTF16};
1295
rv = bundle->FormatStringFromName("CreateWrapperDeniedForOrigin",
1296
formatStrings, errorMsg);
1297
}
1298
NS_ENSURE_SUCCESS(rv, rv);
1299
1300
SetPendingException(cx, errorMsg.get());
1301
return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1302
}
1303
1304
NS_IMETHODIMP
1305
nsScriptSecurityManager::CanCreateInstance(JSContext* cx, const nsCID& aCID) {
1306
if (nsContentUtils::IsCallerChrome()) {
1307
return NS_OK;
1308
}
1309
1310
//-- Access denied, report an error
1311
nsAutoCString errorMsg("Permission denied to create instance of class. CID=");
1312
char cidStr[NSID_LENGTH];
1313
aCID.ToProvidedString(cidStr);
1314
errorMsg.Append(cidStr);
1315
SetPendingExceptionASCII(cx, errorMsg.get());
1316
return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1317
}
1318
1319
NS_IMETHODIMP
1320
nsScriptSecurityManager::CanGetService(JSContext* cx, const nsCID& aCID) {
1321
if (nsContentUtils::IsCallerChrome()) {
1322
return NS_OK;
1323
}
1324
1325
//-- Access denied, report an error
1326
nsAutoCString errorMsg("Permission denied to get service. CID=");
1327
char cidStr[NSID_LENGTH];
1328
aCID.ToProvidedString(cidStr);
1329
errorMsg.Append(cidStr);
1330
SetPendingExceptionASCII(cx, errorMsg.get());
1331
return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
1332
}
1333
1334
const char sJSEnabledPrefName[] = "javascript.enabled";
1335
const char sFileOriginPolicyPrefName[] =
1336
"security.fileuri.strict_origin_policy";
1337
1338
static const char* kObservedPrefs[] = {sJSEnabledPrefName,
1339
sFileOriginPolicyPrefName,
1340
"capability.policy.", nullptr};
1341
1342
/////////////////////////////////////////////
1343
// Constructor, Destructor, Initialization //
1344
/////////////////////////////////////////////
1345
nsScriptSecurityManager::nsScriptSecurityManager(void)
1346
: mPrefInitialized(false), mIsJavaScriptEnabled(false) {
1347
static_assert(
1348
sizeof(intptr_t) == sizeof(void*),
1349
"intptr_t and void* have different lengths on this platform. "
1350
"This may cause a security failure with the SecurityLevel union.");
1351
}
1352
1353
nsresult nsScriptSecurityManager::Init() {
1354
nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
1355
NS_ENSURE_SUCCESS(rv, rv);
1356
1357
InitPrefs();
1358
1359
// Create our system principal singleton
1360
RefPtr<SystemPrincipal> system = SystemPrincipal::Create();
1361
1362
mSystemPrincipal = system;
1363
1364
//-- Register security check callback in the JS engine
1365
// Currently this is used to control access to function.caller
1366
sContext = danger::GetJSContext();
1367
1368
static const JSSecurityCallbacks securityCallbacks = {
1369
ContentSecurityPolicyPermitsJSAction,
1370
JSPrincipalsSubsume,
1371
};
1372
1373
MOZ_ASSERT(!JS_GetSecurityCallbacks(sContext));
1374
JS_SetSecurityCallbacks(sContext, &securityCallbacks);
1375
JS_InitDestroyPrincipalsCallback(sContext, nsJSPrincipals::Destroy);
1376
1377
JS_SetTrustedPrincipals(sContext, system);
1378
1379
return NS_OK;
1380
}
1381
1382
static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
1383
1384
nsScriptSecurityManager::~nsScriptSecurityManager(void) {
1385
Preferences::UnregisterPrefixCallbacks(
1386
PREF_CHANGE_METHOD(nsScriptSecurityManager::ScriptSecurityPrefChanged),
1387
kObservedPrefs, this);
1388
if (mDomainPolicy) {
1389
mDomainPolicy->Deactivate();
1390
}
1391
// ContentChild might hold a reference to the domain policy,
1392
// and it might release it only after the security manager is
1393
// gone. But we can still assert this for the main process.
1394
MOZ_ASSERT_IF(XRE_IsParentProcess(), !mDomainPolicy);
1395
}
1396
1397
void nsScriptSecurityManager::Shutdown() {
1398
if (sContext) {
1399
JS_SetSecurityCallbacks(sContext, nullptr);
1400
JS_SetTrustedPrincipals(sContext, nullptr);
1401
sContext = nullptr;
1402
}
1403
1404
NS_IF_RELEASE(sIOService);
1405
BundleHelper::Shutdown();
1406
}
1407
1408
nsScriptSecurityManager* nsScriptSecurityManager::GetScriptSecurityManager() {
1409
return gScriptSecMan;
1410
}
1411
1412
/* static */
1413
void nsScriptSecurityManager::InitStatics() {
1414
RefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
1415
nsresult rv = ssManager->Init();
1416
if (NS_FAILED(rv)) {
1417
MOZ_CRASH("ssManager->Init() failed");
1418
}
1419
1420
ClearOnShutdown(&gScriptSecMan);
1421
gScriptSecMan = ssManager;
1422
}
1423
1424
// Currently this nsGenericFactory constructor is used only from FastLoad
1425
// (XPCOM object deserialization) code, when "creating" the system principal
1426
// singleton.
1427
already_AddRefed<SystemPrincipal>
1428
nsScriptSecurityManager::SystemPrincipalSingletonConstructor() {
1429
if (gScriptSecMan)
1430
return do_AddRef(gScriptSecMan->mSystemPrincipal)
1431
.downcast<SystemPrincipal>();
1432
return nullptr;
1433
}
1434
1435
struct IsWhitespace {
1436
static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); };
1437
};
1438
struct IsWhitespaceOrComma {
1439
static bool Test(char aChar) {
1440
return aChar == ',' || NS_IsAsciiWhitespace(aChar);
1441
};
1442
};
1443
1444
template <typename Predicate>
1445
uint32_t SkipPast(const nsCString& str, uint32_t base) {
1446
while (base < str.Length() && Predicate::Test(str[base])) {
1447
++base;
1448
}
1449
return base;
1450
}
1451
1452
template <typename Predicate>
1453
uint32_t SkipUntil(const nsCString& str, uint32_t base) {
1454
while (base < str.Length() && !Predicate::Test(str[base])) {
1455
++base;
1456
}
1457
return base;
1458
}
1459
1460
inline void nsScriptSecurityManager::ScriptSecurityPrefChanged(
1461
const char* aPref) {
1462
MOZ_ASSERT(mPrefInitialized);
1463
mIsJavaScriptEnabled =
1464
Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
1465
sStrictFileOriginPolicy =
1466
Preferences::GetBool(sFileOriginPolicyPrefName, false);
1467
mFileURIAllowlist.reset();
1468
}
1469
1470
void nsScriptSecurityManager::AddSitesToFileURIAllowlist(
1471
const nsCString& aSiteList) {
1472
for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0;
1473
base < aSiteList.Length();
1474
base = SkipPast<IsWhitespace>(aSiteList, bound)) {
1475
// Grab the current site.
1476
bound = SkipUntil<IsWhitespace>(aSiteList, base);
1477
nsAutoCString site(Substring(aSiteList, base, bound - base));
1478
1479
// Check if the URI is schemeless. If so, add both http and https.
1480
nsAutoCString unused;
1481
if (NS_FAILED(sIOService->ExtractScheme(site, unused))) {
1482
AddSitesToFileURIAllowlist(NS_LITERAL_CSTRING("http://") + site);
1483
AddSitesToFileURIAllowlist(NS_LITERAL_CSTRING("https://") + site);
1484
continue;
1485
}
1486
1487
// Convert it to a URI and add it to our list.
1488
nsCOMPtr<nsIURI> uri;
1489
nsresult rv = NS_NewURI(getter_AddRefs(uri), site);
1490
if (NS_SUCCEEDED(rv)) {
1491
mFileURIAllowlist.ref().AppendElement(uri);
1492
} else {
1493
nsCOMPtr<nsIConsoleService> console(
1494
do_GetService("@mozilla.org/consoleservice;1"));
1495
if (console) {
1496
nsAutoString msg =
1497
NS_LITERAL_STRING(
1498
"Unable to to add site to file:// URI allowlist: ") +
1499
NS_ConvertASCIItoUTF16(site);
1500
console->LogStringMessage(msg.get());
1501
}
1502
}
1503
}
1504
}
1505
1506
nsresult nsScriptSecurityManager::InitPrefs() {
1507
nsIPrefBranch* branch = Preferences::GetRootBranch();
1508
NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
1509
1510
mPrefInitialized = true;
1511
1512
// Set the initial value of the "javascript.enabled" prefs
1513
ScriptSecurityPrefChanged();
1514
1515
// set observer callbacks in case the value of the prefs change
1516
Preferences::RegisterPrefixCallbacks(
1517
PREF_CHANGE_METHOD(nsScriptSecurityManager::ScriptSecurityPrefChanged),
1518
kObservedPrefs, this);
1519
1520
OriginAttributes::InitPrefs();
1521
1522
return NS_OK;
1523
}
1524
1525
NS_IMETHODIMP
1526
nsScriptSecurityManager::GetDomainPolicyActive(bool* aRv) {
1527
*aRv = !!mDomainPolicy;
1528
return NS_OK;
1529
}
1530
1531
NS_IMETHODIMP
1532
nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv) {
1533
if (!XRE_IsParentProcess()) {
1534
return NS_ERROR_SERVICE_NOT_AVAILABLE;
1535
}
1536
1537
return ActivateDomainPolicyInternal(aRv);
1538
}
1539
1540
NS_IMETHODIMP
1541
nsScriptSecurityManager::ActivateDomainPolicyInternal(nsIDomainPolicy** aRv) {
1542
// We only allow one domain policy at a time. The holder of the previous
1543
// policy must explicitly deactivate it first.
1544
if (mDomainPolicy) {
1545
return NS_ERROR_SERVICE_NOT_AVAILABLE;
1546
}
1547
1548
mDomainPolicy = new DomainPolicy();
1549
nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy;
1550
ptr.forget(aRv);
1551
return NS_OK;
1552
}
1553
1554
// Intentionally non-scriptable. Script must have a reference to the
1555
// nsIDomainPolicy to deactivate it.
1556
void nsScriptSecurityManager::DeactivateDomainPolicy() {
1557
mDomainPolicy = nullptr;
1558
}
1559
1560
void nsScriptSecurityManager::CloneDomainPolicy(DomainPolicyClone* aClone) {
1561
MOZ_ASSERT(aClone);
1562
if (mDomainPolicy) {
1563
mDomainPolicy->CloneDomainPolicy(aClone);
1564
} else {
1565
aClone->active() = false;
1566
}
1567
}
1568
1569
NS_IMETHODIMP
1570
nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool* aRv) {
1571
nsresult rv;
1572
1573
// Compute our rule. If we don't have any domain policy set up that might
1574
// provide exceptions to this rule, we're done.
1575
*aRv = mIsJavaScriptEnabled;
1576
if (!mDomainPolicy) {
1577
return NS_OK;
1578
}
1579
1580
// We have a domain policy. Grab the appropriate set of exceptions to the
1581
// rule (either the blocklist or the allowlist, depending on whether script
1582
// is enabled or disabled by default).
1583
nsCOMPtr<nsIDomainSet> exceptions;
1584
nsCOMPtr<nsIDomainSet> superExceptions;
1585
if (*aRv) {
1586
mDomainPolicy->GetBlocklist(getter_AddRefs(exceptions));
1587
mDomainPolicy->GetSuperBlocklist(getter_AddRefs(superExceptions));
1588
} else {
1589
mDomainPolicy->GetAllowlist(getter_AddRefs(exceptions));
1590
mDomainPolicy->GetSuperAllowlist(getter_AddRefs(superExceptions));
1591
}
1592
1593
bool contains;
1594
rv = exceptions->Contains(aURI, &contains);
1595
NS_ENSURE_SUCCESS(rv, rv);
1596
if (contains) {
1597
*aRv = !*aRv;
1598
return NS_OK;
1599
}
1600
rv = superExceptions->ContainsSuperDomain(aURI, &contains);
1601
NS_ENSURE_SUCCESS(rv, rv);
1602
if (contains) {
1603
*aRv = !*aRv;
1604
}
1605
1606
return NS_OK;
1607
}
1608
1609
const nsTArray<nsCOMPtr<nsIURI>>&
1610
nsScriptSecurityManager::EnsureFileURIAllowlist() {
1611
if (mFileURIAllowlist.isSome()) {
1612
return mFileURIAllowlist.ref();
1613
}
1614
1615
//
1616
// Rebuild the set of principals for which we allow file:// URI loads. This
1617
// implements a small subset of an old pref-based CAPS people that people
1618
// have come to depend on. See bug 995943.
1619
//
1620
1621
mFileURIAllowlist.emplace();
1622
nsAutoCString policies;
1623
mozilla::Preferences::GetCString("capability.policy.policynames", policies);
1624
for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
1625
base < policies.Length();
1626
base = SkipPast<IsWhitespaceOrComma>(policies, bound)) {
1627
// Grab the current policy name.
1628
bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
1629
auto policyName = Substring(policies, base, bound - base);
1630
1631
// Figure out if this policy allows loading file:// URIs. If not, we can
1632
// skip it.
1633
nsCString checkLoadURIPrefName =
1634
NS_LITERAL_CSTRING("capability.policy.") + policyName +
1635
NS_LITERAL_CSTRING(".checkloaduri.enabled");
1636
nsAutoString value;
1637
nsresult rv = Preferences::GetString(checkLoadURIPrefName.get(), value);
1638
if (NS_FAILED(rv) || !value.LowerCaseEqualsLiteral("allaccess")) {
1639
continue;
1640
}
1641
1642
// Grab the list of domains associated with this policy.
1643
nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
1644
policyName + NS_LITERAL_CSTRING(".sites");
1645
nsAutoCString siteList;
1646
Preferences::GetCString(domainPrefName.get(), siteList);
1647
AddSitesToFileURIAllowlist(siteList);
1648
}
1649
1650
return mFileURIAllowlist.ref();
1651
}