Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/security/nsContentSecurityManager.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsContentSecurityManager.h"
8
#include "nsEscape.h"
9
#include "nsDataHandler.h"
10
#include "nsIChannel.h"
11
#include "nsIHttpChannelInternal.h"
12
#include "nsINode.h"
13
#include "nsIStreamListener.h"
14
#include "nsILoadInfo.h"
15
#include "nsIOService.h"
16
#include "nsContentUtils.h"
17
#include "nsCORSListenerProxy.h"
18
#include "nsIStreamListener.h"
19
#include "nsCDefaultURIFixup.h"
20
#include "nsIURIFixup.h"
21
#include "nsIImageLoadingContent.h"
22
23
#include "mozilla/dom/Element.h"
24
#include "mozilla/dom/nsMixedContentBlocker.h"
25
#include "mozilla/dom/TabChild.h"
26
27
NS_IMPL_ISUPPORTS(nsContentSecurityManager,
28
                  nsIContentSecurityManager,
29
                  nsIChannelEventSink)
30
31
/* static */ bool
32
nsContentSecurityManager::AllowTopLevelNavigationToDataURI(nsIChannel* aChannel)
33
0
{
34
0
  // Let's block all toplevel document navigations to a data: URI.
35
0
  // In all cases where the toplevel document is navigated to a
36
0
  // data: URI the triggeringPrincipal is a codeBasePrincipal, or
37
0
  // a NullPrincipal. In other cases, e.g. typing a data: URL into
38
0
  // the URL-Bar, the triggeringPrincipal is a SystemPrincipal;
39
0
  // we don't want to block those loads. Only exception, loads coming
40
0
  // from an external applicaton (e.g. Thunderbird) don't load
41
0
  // using a codeBasePrincipal, but we want to block those loads.
42
0
  if (!mozilla::net::nsIOService::BlockToplevelDataUriNavigations()) {
43
0
    return true;
44
0
  }
45
0
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
46
0
  if (!loadInfo) {
47
0
    return true;
48
0
  }
49
0
  if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT) {
50
0
    return true;
51
0
  }
52
0
  if (loadInfo->GetForceAllowDataURI()) {
53
0
    // if the loadinfo explicitly allows the data URI navigation, let's allow it now
54
0
    return true;
55
0
  }
56
0
  nsCOMPtr<nsIURI> uri;
57
0
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
58
0
  NS_ENSURE_SUCCESS(rv, true);
59
0
  bool isDataURI =
60
0
    (NS_SUCCEEDED(uri->SchemeIs("data", &isDataURI)) && isDataURI);
61
0
  if (!isDataURI) {
62
0
    return true;
63
0
  }
64
0
65
0
  nsAutoCString spec;
66
0
  rv = uri->GetSpec(spec);
67
0
  NS_ENSURE_SUCCESS(rv, true);
68
0
  nsAutoCString contentType;
69
0
  bool base64;
70
0
  rv = nsDataHandler::ParseURI(spec, contentType, nullptr,
71
0
                               base64, nullptr);
72
0
  NS_ENSURE_SUCCESS(rv, true);
73
0
74
0
  // Whitelist data: images as long as they are not SVGs
75
0
  if (StringBeginsWith(contentType, NS_LITERAL_CSTRING("image/")) &&
76
0
      !contentType.EqualsLiteral("image/svg+xml")) {
77
0
    return true;
78
0
  }
79
0
  // Whitelist all plain text types as well as data: PDFs.
80
0
  if (nsContentUtils::IsPlainTextType(contentType) ||
81
0
      contentType.EqualsLiteral("application/pdf")) {
82
0
    return true;
83
0
  }
84
0
  // Redirecting to a toplevel data: URI is not allowed, hence we make
85
0
  // sure the RedirectChain is empty.
86
0
  if (!loadInfo->GetLoadTriggeredFromExternal() &&
87
0
      nsContentUtils::IsSystemPrincipal(loadInfo->TriggeringPrincipal()) &&
88
0
      loadInfo->RedirectChain().IsEmpty()) {
89
0
    return true;
90
0
  }
91
0
  nsAutoCString dataSpec;
92
0
  uri->GetSpec(dataSpec);
93
0
  if (dataSpec.Length() > 50) {
94
0
    dataSpec.Truncate(50);
95
0
    dataSpec.AppendLiteral("...");
96
0
  }
97
0
  nsCOMPtr<nsISupports> context = loadInfo->ContextForTopLevelLoad();
98
0
  nsCOMPtr<nsITabChild> tabChild = do_QueryInterface(context);
99
0
  nsCOMPtr<nsIDocument> doc;
100
0
  if (tabChild) {
101
0
    doc = static_cast<mozilla::dom::TabChild*>(tabChild.get())->GetDocument();
102
0
  }
103
0
  NS_ConvertUTF8toUTF16 specUTF16(NS_UnescapeURL(dataSpec));
104
0
  const char16_t* params[] = { specUTF16.get() };
105
0
  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
106
0
                                  NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
107
0
                                  doc,
108
0
                                  nsContentUtils::eSECURITY_PROPERTIES,
109
0
                                  "BlockTopLevelDataURINavigation",
110
0
                                  params, ArrayLength(params));
111
0
  return false;
112
0
}
113
114
/* static */ bool
115
nsContentSecurityManager::AllowInsecureRedirectToDataURI(nsIChannel* aNewChannel)
116
0
{
117
0
  nsCOMPtr<nsILoadInfo> loadInfo = aNewChannel->GetLoadInfo();
118
0
  if (!loadInfo) {
119
0
    return true;
120
0
  }
121
0
  if (loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_SCRIPT) {
122
0
    return true;
123
0
  }
124
0
  nsCOMPtr<nsIURI> newURI;
125
0
  nsresult rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
126
0
  if (NS_FAILED(rv) || !newURI) {
127
0
    return true;
128
0
  }
129
0
  bool isDataURI = (NS_SUCCEEDED(newURI->SchemeIs("data", &isDataURI)) && isDataURI);
130
0
  if (!isDataURI) {
131
0
    return true;
132
0
  }
133
0
134
0
  // Web Extensions are exempt from that restriction and are allowed to redirect
135
0
  // a channel to a data: URI. When a web extension redirects a channel, we set
136
0
  // a flag on the loadInfo which allows us to identify such redirects here.
137
0
  if (loadInfo->GetAllowInsecureRedirectToDataURI()) {
138
0
    return true;
139
0
  }
140
0
141
0
  nsAutoCString dataSpec;
142
0
  newURI->GetSpec(dataSpec);
143
0
  if (dataSpec.Length() > 50) {
144
0
    dataSpec.Truncate(50);
145
0
    dataSpec.AppendLiteral("...");
146
0
  }
147
0
  nsCOMPtr<nsIDocument> doc;
148
0
  nsINode* node = loadInfo->LoadingNode();
149
0
  if (node) {
150
0
    doc = node->OwnerDoc();
151
0
  }
152
0
  NS_ConvertUTF8toUTF16 specUTF16(NS_UnescapeURL(dataSpec));
153
0
  const char16_t* params[] = { specUTF16.get() };
154
0
  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
155
0
                                  NS_LITERAL_CSTRING("DATA_URI_BLOCKED"),
156
0
                                  doc,
157
0
                                  nsContentUtils::eSECURITY_PROPERTIES,
158
0
                                  "BlockSubresourceRedirectToData",
159
0
                                  params, ArrayLength(params));
160
0
  return false;
161
0
}
162
163
/* static */ nsresult
164
nsContentSecurityManager::CheckFTPSubresourceLoad(nsIChannel* aChannel)
165
5
{
166
5
  // We dissallow using FTP resources as a subresource almost everywhere.
167
5
  // The only valid way to use FTP resources is loading it as
168
5
  // a top level document.
169
5
  if (!mozilla::net::nsIOService::BlockFTPSubresources()) {
170
0
    return NS_OK;
171
0
  }
172
5
173
5
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
174
5
  if (!loadInfo) {
175
0
    return NS_OK;
176
0
  }
177
5
178
5
  nsContentPolicyType type = loadInfo->GetExternalContentPolicyType();
179
5
180
5
  // Allow top-level FTP documents and save-as download of FTP files on
181
5
  // HTTP pages.
182
5
  if (type == nsIContentPolicy::TYPE_DOCUMENT ||
183
5
      type == nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD) {
184
0
    return NS_OK;
185
0
  }
186
5
187
5
  // Allow the system principal to load everything. This is meant to
188
5
  // temporarily fix downloads and pdf.js.
189
5
  nsIPrincipal* triggeringPrincipal = loadInfo->TriggeringPrincipal();
190
5
  if (nsContentUtils::IsSystemPrincipal(triggeringPrincipal)) {
191
5
    return NS_OK;
192
5
  }
193
0
194
0
  nsCOMPtr<nsIURI> uri;
195
0
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
196
0
  NS_ENSURE_SUCCESS(rv, rv);
197
0
  if (!uri) {
198
0
    return NS_OK;
199
0
  }
200
0
201
0
  bool isFtpURI = (NS_SUCCEEDED(uri->SchemeIs("ftp", &isFtpURI)) && isFtpURI);
202
0
  if (!isFtpURI) {
203
0
    return NS_OK;
204
0
  }
205
0
206
0
  // Allow loading FTP subresources in FTP documents, like XML.
207
0
  nsCOMPtr<nsIURI> triggeringURI;
208
0
  triggeringPrincipal->GetURI(getter_AddRefs(triggeringURI));
209
0
  if (triggeringURI && nsContentUtils::SchemeIs(triggeringURI, "ftp")) {
210
0
    return NS_OK;
211
0
  }
212
0
213
0
  nsCOMPtr<nsIDocument> doc;
214
0
  if (nsINode* node = loadInfo->LoadingNode()) {
215
0
    doc = node->OwnerDoc();
216
0
  }
217
0
218
0
  nsAutoCString spec;
219
0
  uri->GetSpec(spec);
220
0
  NS_ConvertUTF8toUTF16 specUTF16(NS_UnescapeURL(spec));
221
0
  const char16_t* params[] = { specUTF16.get() };
222
0
223
0
  nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
224
0
                                  NS_LITERAL_CSTRING("FTP_URI_BLOCKED"),
225
0
                                  doc,
226
0
                                  nsContentUtils::eSECURITY_PROPERTIES,
227
0
                                  "BlockSubresourceFTP",
228
0
                                  params, ArrayLength(params));
229
0
230
0
  return NS_ERROR_CONTENT_BLOCKED;
231
0
}
232
233
static nsresult
234
ValidateSecurityFlags(nsILoadInfo* aLoadInfo)
235
5
{
236
5
  nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
237
5
238
5
  // We should never perform a security check on a loadInfo that uses the flag
239
5
  // SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK, because that is only used for temporary
240
5
  // loadInfos used for explicit nsIContentPolicy checks, but never be set as
241
5
  // a security flag on an actual channel.
242
5
  if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
243
5
      securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
244
5
      securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
245
5
      securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
246
5
      securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
247
0
    MOZ_ASSERT(false, "need one securityflag from nsILoadInfo to perform security checks");
248
0
    return NS_ERROR_FAILURE;
249
0
  }
250
5
251
5
  // all good, found the right security flags
252
5
  return NS_OK;
253
5
}
254
255
static bool IsImageLoadInEditorAppType(nsILoadInfo* aLoadInfo)
256
0
{
257
0
  // Editor apps get special treatment here, editors can load images
258
0
  // from anywhere.  This allows editor to insert images from file://
259
0
  // into documents that are being edited.
260
0
  nsContentPolicyType type = aLoadInfo->InternalContentPolicyType();
261
0
  if (type != nsIContentPolicy::TYPE_INTERNAL_IMAGE  &&
262
0
      type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_PRELOAD &&
263
0
      type != nsIContentPolicy::TYPE_INTERNAL_IMAGE_FAVICON &&
264
0
      type != nsIContentPolicy::TYPE_IMAGESET) {
265
0
    return false;
266
0
  }
267
0
268
0
  uint32_t appType = nsIDocShell::APP_TYPE_UNKNOWN;
269
0
  nsINode* node = aLoadInfo->LoadingNode();
270
0
  if (!node) {
271
0
    return false;
272
0
  }
273
0
  nsIDocument* doc = node->OwnerDoc();
274
0
  if (!doc) {
275
0
    return false;
276
0
  }
277
0
278
0
  nsCOMPtr<nsIDocShellTreeItem> docShellTreeItem = doc->GetDocShell();
279
0
  if (!docShellTreeItem) {
280
0
    return false;
281
0
  }
282
0
283
0
  nsCOMPtr<nsIDocShellTreeItem> root;
284
0
  docShellTreeItem->GetRootTreeItem(getter_AddRefs(root));
285
0
  nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(root));
286
0
  if (!docShell || NS_FAILED(docShell->GetAppType(&appType))) {
287
0
    appType = nsIDocShell::APP_TYPE_UNKNOWN;
288
0
  }
289
0
290
0
  return appType == nsIDocShell::APP_TYPE_EDITOR;
291
0
}
292
293
static nsresult
294
DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
295
0
{
296
0
  // Bug 1228117: determine the correct security policy for DTD loads
297
0
  if (aLoadInfo->GetExternalContentPolicyType() == nsIContentPolicy::TYPE_DTD) {
298
0
    return NS_OK;
299
0
  }
300
0
301
0
  if (IsImageLoadInEditorAppType(aLoadInfo)) {
302
0
    return NS_OK;
303
0
  }
304
0
305
0
  uint32_t flags = nsIScriptSecurityManager::STANDARD;
306
0
  if (aLoadInfo->GetAllowChrome()) {
307
0
    flags |= nsIScriptSecurityManager::ALLOW_CHROME;
308
0
  }
309
0
  if (aLoadInfo->GetDisallowScript()) {
310
0
    flags |= nsIScriptSecurityManager::DISALLOW_SCRIPT;
311
0
  }
312
0
313
0
  // Only call CheckLoadURIWithPrincipal() using the TriggeringPrincipal and not
314
0
  // the LoadingPrincipal when SEC_ALLOW_CROSS_ORIGIN_* security flags are set,
315
0
  // to allow, e.g. user stylesheets to load chrome:// URIs.
316
0
  return nsContentUtils::GetSecurityManager()->
317
0
           CheckLoadURIWithPrincipal(aLoadInfo->TriggeringPrincipal(),
318
0
                                     aURI,
319
0
                                     flags);
320
0
}
321
322
static bool
323
URIHasFlags(nsIURI* aURI, uint32_t aURIFlags)
324
0
{
325
0
  bool hasFlags;
326
0
  nsresult rv = NS_URIChainHasFlags(aURI, aURIFlags, &hasFlags);
327
0
  NS_ENSURE_SUCCESS(rv, false);
328
0
329
0
  return hasFlags;
330
0
}
331
332
static nsresult
333
DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo, nsIChannel* aChannel)
334
0
{
335
0
  if (aLoadInfo->GetAllowChrome() &&
336
0
      (URIHasFlags(aURI, nsIProtocolHandler::URI_IS_UI_RESOURCE) ||
337
0
       nsContentUtils::SchemeIs(aURI, "moz-safe-about"))) {
338
0
    // UI resources are allowed.
339
0
    return DoCheckLoadURIChecks(aURI, aLoadInfo);
340
0
  }
341
0
342
0
  NS_ENSURE_FALSE(NS_HasBeenCrossOrigin(aChannel, true),
343
0
                  NS_ERROR_DOM_BAD_URI);
344
0
345
0
  return NS_OK;
346
0
}
347
348
static nsresult
349
DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
350
             nsCOMPtr<nsIStreamListener>& aInAndOutListener)
351
0
{
352
0
  MOZ_RELEASE_ASSERT(aInAndOutListener, "can not perform CORS checks without a listener");
353
0
354
0
  // No need to set up CORS if TriggeringPrincipal is the SystemPrincipal.
355
0
  // For example, allow user stylesheets to load XBL from external files
356
0
  // without requiring CORS.
357
0
  if (nsContentUtils::IsSystemPrincipal(aLoadInfo->TriggeringPrincipal())) {
358
0
    return NS_OK;
359
0
  }
360
0
361
0
  nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
362
0
  RefPtr<nsCORSListenerProxy> corsListener =
363
0
    new nsCORSListenerProxy(aInAndOutListener,
364
0
                            loadingPrincipal,
365
0
                            aLoadInfo->GetCookiePolicy() ==
366
0
                              nsILoadInfo::SEC_COOKIES_INCLUDE);
367
0
  // XXX: @arg: DataURIHandling::Allow
368
0
  // lets use  DataURIHandling::Allow for now and then decide on callsite basis. see also:
369
0
  // http://mxr.mozilla.org/mozilla-central/source/dom/security/nsCORSListenerProxy.h#33
370
0
  nsresult rv = corsListener->Init(aChannel, DataURIHandling::Allow);
371
0
  NS_ENSURE_SUCCESS(rv, rv);
372
0
  aInAndOutListener = corsListener;
373
0
  return NS_OK;
374
0
}
375
376
static nsresult
377
DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo)
378
5
{
379
5
  nsContentPolicyType contentPolicyType =
380
5
    aLoadInfo->GetExternalContentPolicyType();
381
5
  nsContentPolicyType internalContentPolicyType =
382
5
    aLoadInfo->InternalContentPolicyType();
383
5
  nsCString mimeTypeGuess;
384
5
385
5
  nsCOMPtr<nsIURI> uri;
386
5
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
387
5
  NS_ENSURE_SUCCESS(rv, rv);
388
5
389
5
  if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
390
5
      contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
391
0
    // TYPE_DOCUMENT and TYPE_SUBDOCUMENT loads might potentially
392
0
    // be wyciwyg:// channels. Let's fix up the URI so we can
393
0
    // perform proper security checks.
394
0
    nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv));
395
0
    if (NS_SUCCEEDED(rv) && urifixup) {
396
0
      nsCOMPtr<nsIURI> fixedURI;
397
0
      rv = urifixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
398
0
      if (NS_SUCCEEDED(rv)) {
399
0
        uri = fixedURI;
400
0
      }
401
0
    }
402
0
  }
403
5
404
5
  switch(contentPolicyType) {
405
5
    case nsIContentPolicy::TYPE_OTHER: {
406
0
      mimeTypeGuess = EmptyCString();
407
0
      break;
408
5
    }
409
5
410
5
    case nsIContentPolicy::TYPE_SCRIPT: {
411
5
      mimeTypeGuess = NS_LITERAL_CSTRING("application/javascript");
412
5
      break;
413
5
    }
414
5
415
5
    case nsIContentPolicy::TYPE_IMAGE: {
416
0
      mimeTypeGuess = EmptyCString();
417
0
      break;
418
5
    }
419
5
420
5
    case nsIContentPolicy::TYPE_STYLESHEET: {
421
0
      mimeTypeGuess = NS_LITERAL_CSTRING("text/css");
422
0
      break;
423
5
    }
424
5
425
5
    case nsIContentPolicy::TYPE_OBJECT: {
426
0
      mimeTypeGuess = EmptyCString();
427
0
      break;
428
5
    }
429
5
430
5
    case nsIContentPolicy::TYPE_DOCUMENT: {
431
0
      mimeTypeGuess = EmptyCString();
432
0
      break;
433
5
    }
434
5
435
5
    case nsIContentPolicy::TYPE_SUBDOCUMENT: {
436
0
      mimeTypeGuess = NS_LITERAL_CSTRING("text/html");
437
0
      break;
438
5
    }
439
5
440
5
    case nsIContentPolicy::TYPE_REFRESH: {
441
0
      MOZ_ASSERT(false, "contentPolicyType not supported yet");
442
0
      break;
443
5
    }
444
5
445
5
    case nsIContentPolicy::TYPE_XBL: {
446
0
      mimeTypeGuess = EmptyCString();
447
0
      break;
448
5
    }
449
5
450
5
    case nsIContentPolicy::TYPE_PING: {
451
0
      mimeTypeGuess = EmptyCString();
452
0
      break;
453
5
    }
454
5
455
5
    case nsIContentPolicy::TYPE_XMLHTTPREQUEST: {
456
0
      // alias nsIContentPolicy::TYPE_DATAREQUEST:
457
#ifdef DEBUG
458
      {
459
        nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
460
        MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
461
                   "type_xml requires requestingContext of type Document");
462
      }
463
#endif
464
      // We're checking for the external TYPE_XMLHTTPREQUEST here in case
465
0
      // an addon creates a request with that type.
466
0
      if (internalContentPolicyType ==
467
0
            nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST ||
468
0
          internalContentPolicyType ==
469
0
            nsIContentPolicy::TYPE_XMLHTTPREQUEST) {
470
0
        mimeTypeGuess = EmptyCString();
471
0
      }
472
0
      else {
473
0
        MOZ_ASSERT(internalContentPolicyType ==
474
0
                   nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE,
475
0
                   "can not set mime type guess for unexpected internal type");
476
0
        mimeTypeGuess = NS_LITERAL_CSTRING(TEXT_EVENT_STREAM);
477
0
      }
478
0
      break;
479
5
    }
480
5
481
5
    case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: {
482
0
      mimeTypeGuess = EmptyCString();
483
#ifdef DEBUG
484
      {
485
        nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
486
        MOZ_ASSERT(!node || node->NodeType() == nsINode::ELEMENT_NODE,
487
                   "type_subrequest requires requestingContext of type Element");
488
      }
489
#endif
490
      break;
491
5
    }
492
5
493
5
    case nsIContentPolicy::TYPE_DTD: {
494
0
      mimeTypeGuess = EmptyCString();
495
#ifdef DEBUG
496
      {
497
        nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
498
        MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
499
                   "type_dtd requires requestingContext of type Document");
500
      }
501
#endif
502
      break;
503
5
    }
504
5
505
5
    case nsIContentPolicy::TYPE_FONT: {
506
0
      mimeTypeGuess = EmptyCString();
507
0
      break;
508
5
    }
509
5
510
5
    case nsIContentPolicy::TYPE_MEDIA: {
511
0
      if (internalContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_TRACK) {
512
0
        mimeTypeGuess = NS_LITERAL_CSTRING("text/vtt");
513
0
      }
514
0
      else {
515
0
        mimeTypeGuess = EmptyCString();
516
0
      }
517
#ifdef DEBUG
518
      {
519
        nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
520
        MOZ_ASSERT(!node || node->NodeType() == nsINode::ELEMENT_NODE,
521
                   "type_media requires requestingContext of type Element");
522
      }
523
#endif
524
      break;
525
5
    }
526
5
527
5
    case nsIContentPolicy::TYPE_WEBSOCKET: {
528
0
      // Websockets have to use the proxied URI:
529
0
      // ws:// instead of http:// for CSP checks
530
0
      nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal
531
0
        = do_QueryInterface(aChannel);
532
0
      MOZ_ASSERT(httpChannelInternal);
533
0
      if (httpChannelInternal) {
534
0
        rv = httpChannelInternal->GetProxyURI(getter_AddRefs(uri));
535
0
        MOZ_ASSERT(NS_SUCCEEDED(rv));
536
0
      }
537
0
      mimeTypeGuess = EmptyCString();
538
0
      break;
539
5
    }
540
5
541
5
    case nsIContentPolicy::TYPE_CSP_REPORT: {
542
0
      mimeTypeGuess = EmptyCString();
543
0
      break;
544
5
    }
545
5
546
5
    case nsIContentPolicy::TYPE_XSLT: {
547
0
      mimeTypeGuess = NS_LITERAL_CSTRING("application/xml");
548
#ifdef DEBUG
549
      {
550
        nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
551
        MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
552
                   "type_xslt requires requestingContext of type Document");
553
      }
554
#endif
555
      break;
556
5
    }
557
5
558
5
    case nsIContentPolicy::TYPE_BEACON: {
559
0
      mimeTypeGuess = EmptyCString();
560
#ifdef DEBUG
561
      {
562
        nsCOMPtr<nsINode> node = aLoadInfo->LoadingNode();
563
        MOZ_ASSERT(!node || node->NodeType() == nsINode::DOCUMENT_NODE,
564
                   "type_beacon requires requestingContext of type Document");
565
      }
566
#endif
567
      break;
568
5
    }
569
5
570
5
    case nsIContentPolicy::TYPE_FETCH: {
571
0
      mimeTypeGuess = EmptyCString();
572
0
      break;
573
5
    }
574
5
575
5
    case nsIContentPolicy::TYPE_IMAGESET: {
576
0
      mimeTypeGuess = EmptyCString();
577
0
      break;
578
5
    }
579
5
580
5
    case nsIContentPolicy::TYPE_WEB_MANIFEST: {
581
0
      mimeTypeGuess = NS_LITERAL_CSTRING("application/manifest+json");
582
0
      break;
583
5
    }
584
5
585
5
    case nsIContentPolicy::TYPE_SAVEAS_DOWNLOAD: {
586
0
      mimeTypeGuess = EmptyCString();
587
0
      break;
588
5
    }
589
5
590
5
    case nsIContentPolicy::TYPE_SPECULATIVE: {
591
0
      mimeTypeGuess = EmptyCString();
592
0
      break;
593
5
    }
594
5
595
5
    default:
596
0
      // nsIContentPolicy::TYPE_INVALID
597
0
      MOZ_ASSERT(false, "can not perform security check without a valid contentType");
598
5
  }
599
5
600
5
  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
601
5
  rv = NS_CheckContentLoadPolicy(uri,
602
5
                                 aLoadInfo,
603
5
                                 mimeTypeGuess,
604
5
                                 &shouldLoad,
605
5
                                 nsContentUtils::GetContentPolicy());
606
5
607
5
  if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
608
0
    if ((NS_SUCCEEDED(rv) && shouldLoad == nsIContentPolicy::REJECT_TYPE) &&
609
0
        (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
610
0
         contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT)) {
611
0
      // for docshell loads we might have to return SHOW_ALT.
612
0
      return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
613
0
    }
614
0
    return NS_ERROR_CONTENT_BLOCKED;
615
0
  }
616
5
617
5
  return NS_OK;
618
5
}
619
620
/*
621
 * Based on the security flags provided in the loadInfo of the channel,
622
 * doContentSecurityCheck() performs the following content security checks
623
 * before opening the channel:
624
 *
625
 * (1) Same Origin Policy Check (if applicable)
626
 * (2) Allow Cross Origin but perform sanity checks whether a principal
627
 *     is allowed to access the following URL.
628
 * (3) Perform CORS check (if applicable)
629
 * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...)
630
 *
631
 * @param aChannel
632
 *    The channel to perform the security checks on.
633
 * @param aInAndOutListener
634
 *    The streamListener that is passed to channel->AsyncOpen2() that is now potentially
635
 *    wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs
636
 *    to be set as new streamListener on the channel.
637
 */
638
nsresult
639
nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel,
640
                                                 nsCOMPtr<nsIStreamListener>& aInAndOutListener)
641
5
{
642
5
  NS_ENSURE_ARG(aChannel);
643
5
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
644
5
645
5
  if (!loadInfo) {
646
0
    MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks");
647
0
    return NS_ERROR_UNEXPECTED;
648
0
  }
649
5
650
5
  // if dealing with a redirected channel then we have already installed
651
5
  // streamlistener and redirect proxies and so we are done.
652
5
  if (loadInfo->GetInitialSecurityCheckDone()) {
653
0
    return NS_OK;
654
0
  }
655
5
656
5
  // make sure that only one of the five security flags is set in the loadinfo
657
5
  // e.g. do not require same origin and allow cross origin at the same time
658
5
  nsresult rv = ValidateSecurityFlags(loadInfo);
659
5
  NS_ENSURE_SUCCESS(rv, rv);
660
5
661
5
  // since aChannel was openend using asyncOpen2() we have to make sure
662
5
  // that redirects of that channel also get openend using asyncOpen2()
663
5
  // please note that some implementations of ::AsyncOpen2 might already
664
5
  // have set that flag to true (e.g. nsViewSourceChannel) in which case
665
5
  // we just set the flag again.
666
5
  loadInfo->SetEnforceSecurity(true);
667
5
668
5
  if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
669
0
    rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
670
0
    NS_ENSURE_SUCCESS(rv, rv);
671
0
  }
672
5
673
5
  rv = CheckChannel(aChannel);
674
5
  NS_ENSURE_SUCCESS(rv, rv);
675
5
676
5
  // Perform all ContentPolicy checks (MixedContent, CSP, ...)
677
5
  rv = DoContentSecurityChecks(aChannel, loadInfo);
678
5
  NS_ENSURE_SUCCESS(rv, rv);
679
5
680
5
  // Apply this after CSP to match Chrome.
681
5
  rv = CheckFTPSubresourceLoad(aChannel);
682
5
  NS_ENSURE_SUCCESS(rv, rv);
683
5
684
5
  // now lets set the initalSecurityFlag for subsequent calls
685
5
  loadInfo->SetInitialSecurityCheckDone(true);
686
5
687
5
  // all security checks passed - lets allow the load
688
5
  return NS_OK;
689
5
}
690
691
NS_IMETHODIMP
692
nsContentSecurityManager::AsyncOnChannelRedirect(nsIChannel* aOldChannel,
693
                                                 nsIChannel* aNewChannel,
694
                                                 uint32_t aRedirFlags,
695
                                                 nsIAsyncVerifyRedirectCallback *aCb)
696
0
{
697
0
  nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->GetLoadInfo();
698
0
  // Are we enforcing security using LoadInfo?
699
0
  if (loadInfo && loadInfo->GetEnforceSecurity()) {
700
0
    nsresult rv = CheckChannel(aNewChannel);
701
0
    if (NS_SUCCEEDED(rv)) {
702
0
      rv = CheckFTPSubresourceLoad(aNewChannel);
703
0
    }
704
0
    if (NS_FAILED(rv)) {
705
0
      aOldChannel->Cancel(rv);
706
0
      return rv;
707
0
    }
708
0
  }
709
0
710
0
  // Also verify that the redirecting server is allowed to redirect to the
711
0
  // given URI
712
0
  nsCOMPtr<nsIPrincipal> oldPrincipal;
713
0
  nsContentUtils::GetSecurityManager()->
714
0
    GetChannelResultPrincipal(aOldChannel, getter_AddRefs(oldPrincipal));
715
0
716
0
  nsCOMPtr<nsIURI> newURI;
717
0
  Unused << NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI));
718
0
  NS_ENSURE_STATE(oldPrincipal && newURI);
719
0
720
0
  // Do not allow insecure redirects to data: URIs
721
0
  if (!AllowInsecureRedirectToDataURI(aNewChannel)) {
722
0
    // cancel the old channel and return an error
723
0
    aOldChannel->Cancel(NS_ERROR_CONTENT_BLOCKED);
724
0
    return NS_ERROR_CONTENT_BLOCKED;
725
0
  }
726
0
727
0
  const uint32_t flags =
728
0
      nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
729
0
      nsIScriptSecurityManager::DISALLOW_SCRIPT;
730
0
  nsresult rv = nsContentUtils::GetSecurityManager()->
731
0
    CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
732
0
  NS_ENSURE_SUCCESS(rv, rv);
733
0
734
0
  aCb->OnRedirectVerifyCallback(NS_OK);
735
0
  return NS_OK;
736
0
}
737
738
static void
739
AddLoadFlags(nsIRequest *aRequest, nsLoadFlags aNewFlags)
740
0
{
741
0
  nsLoadFlags flags;
742
0
  aRequest->GetLoadFlags(&flags);
743
0
  flags |= aNewFlags;
744
0
  aRequest->SetLoadFlags(flags);
745
0
}
746
747
/*
748
 * Check that this channel passes all security checks. Returns an error code
749
 * if this requesst should not be permitted.
750
 */
751
nsresult
752
nsContentSecurityManager::CheckChannel(nsIChannel* aChannel)
753
5
{
754
5
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
755
5
  MOZ_ASSERT(loadInfo);
756
5
757
5
  nsCOMPtr<nsIURI> uri;
758
5
  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
759
5
  NS_ENSURE_SUCCESS(rv, rv);
760
5
761
5
  nsContentPolicyType contentPolicyType =
762
5
    loadInfo->GetExternalContentPolicyType();
763
5
764
5
  if (contentPolicyType == nsIContentPolicy::TYPE_DOCUMENT ||
765
5
      contentPolicyType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
766
0
    // TYPE_DOCUMENT and TYPE_SUBDOCUMENT loads might potentially
767
0
    // be wyciwyg:// channels. Let's fix up the URI so we can
768
0
    // perform proper security checks.
769
0
    nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID, &rv));
770
0
    if (NS_SUCCEEDED(rv) && urifixup) {
771
0
      nsCOMPtr<nsIURI> fixedURI;
772
0
      rv = urifixup->CreateExposableURI(uri, getter_AddRefs(fixedURI));
773
0
      if (NS_SUCCEEDED(rv)) {
774
0
        uri = fixedURI;
775
0
      }
776
0
    }
777
0
  }
778
5
779
5
  // Handle cookie policies
780
5
  uint32_t cookiePolicy = loadInfo->GetCookiePolicy();
781
5
  if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) {
782
0
783
0
    // We shouldn't have the SEC_COOKIES_SAME_ORIGIN flag for top level loads
784
0
    MOZ_ASSERT(loadInfo->GetExternalContentPolicyType() !=
785
0
               nsIContentPolicy::TYPE_DOCUMENT);
786
0
    nsIPrincipal* loadingPrincipal = loadInfo->LoadingPrincipal();
787
0
788
0
    // It doesn't matter what we pass for the third, data-inherits, argument.
789
0
    // Any protocol which inherits won't pay attention to cookies anyway.
790
0
    rv = loadingPrincipal->CheckMayLoad(uri, false, false);
791
0
    if (NS_FAILED(rv)) {
792
0
      AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
793
0
    }
794
0
  }
795
5
  else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) {
796
0
    AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS);
797
0
  }
798
5
799
5
  nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
800
5
801
5
  // CORS mode is handled by nsCORSListenerProxy
802
5
  if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
803
0
    if (NS_HasBeenCrossOrigin(aChannel)) {
804
0
      loadInfo->MaybeIncreaseTainting(LoadTainting::CORS);
805
0
    }
806
0
    return NS_OK;
807
0
  }
808
5
809
5
  // Allow subresource loads if TriggeringPrincipal is the SystemPrincipal.
810
5
  // For example, allow user stylesheets to load XBL from external files.
811
5
  if (nsContentUtils::IsSystemPrincipal(loadInfo->TriggeringPrincipal()) &&
812
5
      loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_DOCUMENT &&
813
5
      loadInfo->GetExternalContentPolicyType() != nsIContentPolicy::TYPE_SUBDOCUMENT) {
814
5
    return NS_OK;
815
5
  }
816
0
817
0
  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
818
0
  if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) ||
819
0
      (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
820
0
    rv = DoSOPChecks(uri, loadInfo, aChannel);
821
0
    NS_ENSURE_SUCCESS(rv, rv);
822
0
  }
823
0
824
0
  if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) ||
825
0
      (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
826
0
    if (NS_HasBeenCrossOrigin(aChannel)) {
827
0
      NS_ENSURE_FALSE(loadInfo->GetDontFollowRedirects(), NS_ERROR_DOM_BAD_URI);
828
0
      loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque);
829
0
    }
830
0
    // Please note that DoCheckLoadURIChecks should only be enforced for
831
0
    // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
832
0
    // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
833
0
    // within nsCorsListenerProxy
834
0
    rv = DoCheckLoadURIChecks(uri, loadInfo);
835
0
    NS_ENSURE_SUCCESS(rv, rv);
836
0
    // TODO: Bug 1371237
837
0
    // consider calling SetBlockedRequest in nsContentSecurityManager::CheckChannel
838
0
  }
839
0
840
0
  return NS_OK;
841
0
}
842
843
// ==== nsIContentSecurityManager implementation =====
844
845
NS_IMETHODIMP
846
nsContentSecurityManager::PerformSecurityCheck(nsIChannel* aChannel,
847
                                               nsIStreamListener* aStreamListener,
848
                                               nsIStreamListener** outStreamListener)
849
0
{
850
0
  nsCOMPtr<nsIStreamListener> inAndOutListener = aStreamListener;
851
0
  nsresult rv = doContentSecurityCheck(aChannel, inAndOutListener);
852
0
  NS_ENSURE_SUCCESS(rv, rv);
853
0
854
0
  inAndOutListener.forget(outStreamListener);
855
0
  return NS_OK;
856
0
}
857
858
NS_IMETHODIMP
859
nsContentSecurityManager::IsOriginPotentiallyTrustworthy(nsIPrincipal* aPrincipal,
860
                                                         bool* aIsTrustWorthy)
861
0
{
862
0
  MOZ_ASSERT(NS_IsMainThread());
863
0
  NS_ENSURE_ARG_POINTER(aPrincipal);
864
0
  NS_ENSURE_ARG_POINTER(aIsTrustWorthy);
865
0
866
0
  if (aPrincipal->GetIsSystemPrincipal()) {
867
0
    *aIsTrustWorthy = true;
868
0
    return NS_OK;
869
0
  }
870
0
871
0
  // The following implements:
872
0
  // https://w3c.github.io/webappsec-secure-contexts/#is-origin-trustworthy
873
0
874
0
  *aIsTrustWorthy = false;
875
0
876
0
  if (aPrincipal->GetIsNullPrincipal()) {
877
0
    return NS_OK;
878
0
  }
879
0
880
0
  MOZ_ASSERT(aPrincipal->GetIsCodebasePrincipal(),
881
0
             "Nobody is expected to call us with an nsIExpandedPrincipal");
882
0
883
0
  nsCOMPtr<nsIURI> uri;
884
0
  aPrincipal->GetURI(getter_AddRefs(uri));
885
0
886
0
  nsAutoCString scheme;
887
0
  nsresult rv = uri->GetScheme(scheme);
888
0
  if (NS_FAILED(rv)) {
889
0
    return NS_OK;
890
0
  }
891
0
892
0
  // Blobs are expected to inherit their principal so we don't expect to have
893
0
  // a codebase principal with scheme 'blob' here.  We can't assert that though
894
0
  // since someone could mess with a non-blob URI to give it that scheme.
895
0
  NS_WARNING_ASSERTION(!scheme.EqualsLiteral("blob"),
896
0
                       "IsOriginPotentiallyTrustworthy ignoring blob scheme");
897
0
898
0
  // According to the specification, the user agent may choose to extend the
899
0
  // trust to other, vendor-specific URL schemes. We use this for "resource:",
900
0
  // which is technically a substituting protocol handler that is not limited to
901
0
  // local resource mapping, but in practice is never mapped remotely as this
902
0
  // would violate assumptions a lot of code makes.
903
0
  // We use nsIProtocolHandler flags to determine which protocols we consider a priori
904
0
  // authenticated.
905
0
  bool aPrioriAuthenticated = false;
906
0
  if (NS_FAILED(NS_URIChainHasFlags(uri,
907
0
                                    nsIProtocolHandler::URI_IS_POTENTIALLY_TRUSTWORTHY,
908
0
                                    &aPrioriAuthenticated))) {
909
0
    return NS_ERROR_UNEXPECTED;
910
0
  }
911
0
912
0
  if (aPrioriAuthenticated) {
913
0
    *aIsTrustWorthy = true;
914
0
    return NS_OK;
915
0
  }
916
0
917
0
  nsAutoCString host;
918
0
  rv = uri->GetHost(host);
919
0
  if (NS_FAILED(rv)) {
920
0
    return NS_OK;
921
0
  }
922
0
923
0
  if (host.EqualsLiteral("127.0.0.1") ||
924
0
      host.EqualsLiteral("localhost") ||
925
0
      host.EqualsLiteral("::1")) {
926
0
    *aIsTrustWorthy = true;
927
0
    return NS_OK;
928
0
  }
929
0
930
0
  // If a host is not considered secure according to the default algorithm, then
931
0
  // check to see if it has been whitelisted by the user.  We only apply this
932
0
  // whitelist for network resources, i.e., those with scheme "http" or "ws".
933
0
  // The pref should contain a comma-separated list of hostnames.
934
0
  if (scheme.EqualsLiteral("http") || scheme.EqualsLiteral("ws")) {
935
0
    nsAutoCString whitelist;
936
0
    nsresult rv =
937
0
      Preferences::GetCString("dom.securecontext.whitelist", whitelist);
938
0
    if (NS_SUCCEEDED(rv)) {
939
0
      nsCCharSeparatedTokenizer tokenizer(whitelist, ',');
940
0
      while (tokenizer.hasMoreTokens()) {
941
0
        const nsACString& allowedHost = tokenizer.nextToken();
942
0
        if (host.Equals(allowedHost)) {
943
0
          *aIsTrustWorthy = true;
944
0
          return NS_OK;
945
0
        }
946
0
      }
947
0
    }
948
0
    // Maybe we have a .onion URL. Treat it as whitelisted as well if
949
0
    // `dom.securecontext.whitelist_onions` is `true`.
950
0
    if (nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(uri)) {
951
0
      *aIsTrustWorthy = true;
952
0
      return NS_OK;
953
0
    }
954
0
  }
955
0
956
0
  return NS_OK;
957
0
}