Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/security/nsCSPService.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 "mozilla/Logging.h"
8
#include "mozilla/Preferences.h"
9
#include "mozilla/StaticPrefs.h"
10
#include "nsString.h"
11
#include "nsCOMPtr.h"
12
#include "nsIURI.h"
13
#include "nsIPrincipal.h"
14
#include "nsIObserver.h"
15
#include "nsIContent.h"
16
#include "nsCSPService.h"
17
#include "nsIContentSecurityPolicy.h"
18
#include "nsError.h"
19
#include "nsIAsyncVerifyRedirectCallback.h"
20
#include "nsAsyncRedirectVerifyHelper.h"
21
#include "nsIScriptError.h"
22
#include "nsContentUtils.h"
23
#include "nsContentPolicyUtils.h"
24
25
using namespace mozilla;
26
27
static LazyLogModule gCspPRLog("CSP");
28
29
CSPService::CSPService()
30
0
{
31
0
}
32
33
CSPService::~CSPService()
34
0
{
35
0
  mAppStatusCache.Clear();
36
0
}
37
38
NS_IMPL_ISUPPORTS(CSPService, nsIContentPolicy, nsIChannelEventSink)
39
40
// Helper function to identify protocols and content types not subject to CSP.
41
bool
42
0
subjectToCSP(nsIURI* aURI, nsContentPolicyType aContentType) {
43
0
44
0
  nsContentPolicyType contentType =
45
0
    nsContentUtils::InternalContentPolicyTypeToExternal(aContentType);
46
0
47
0
  // These content types are not subject to CSP content policy checks:
48
0
  // TYPE_CSP_REPORT -- csp can't block csp reports
49
0
  // TYPE_REFRESH    -- never passed to ShouldLoad (see nsIContentPolicy.idl)
50
0
  // TYPE_DOCUMENT   -- used for frame-ancestors
51
0
  if (contentType == nsIContentPolicy::TYPE_CSP_REPORT ||
52
0
      contentType == nsIContentPolicy::TYPE_REFRESH ||
53
0
      contentType == nsIContentPolicy::TYPE_DOCUMENT) {
54
0
    return false;
55
0
  }
56
0
57
0
  // The three protocols: data:, blob: and filesystem: share the same
58
0
  // protocol flag (URI_IS_LOCAL_RESOURCE) with other protocols,
59
0
  // but those three protocols get special attention in CSP and
60
0
  // are subject to CSP, hence we have to make sure those
61
0
  // protocols are subject to CSP, see:
62
0
  // http://www.w3.org/TR/CSP2/#source-list-guid-matching
63
0
  bool match = false;
64
0
  nsresult rv = aURI->SchemeIs("data", &match);
65
0
  if (NS_SUCCEEDED(rv) && match) {
66
0
    return true;
67
0
  }
68
0
  rv = aURI->SchemeIs("blob", &match);
69
0
  if (NS_SUCCEEDED(rv) && match) {
70
0
    return true;
71
0
  }
72
0
  rv = aURI->SchemeIs("filesystem", &match);
73
0
  if (NS_SUCCEEDED(rv) && match) {
74
0
    return true;
75
0
  }
76
0
77
0
  // Finally we have to whitelist "about:" which does not fall into
78
0
  // the category underneath and also "javascript:" which is not
79
0
  // subject to CSP content loading rules.
80
0
  rv = aURI->SchemeIs("about", &match);
81
0
  if (NS_SUCCEEDED(rv) && match) {
82
0
    return false;
83
0
  }
84
0
  rv = aURI->SchemeIs("javascript", &match);
85
0
  if (NS_SUCCEEDED(rv) && match) {
86
0
    return false;
87
0
  }
88
0
89
0
  // Please note that it should be possible for websites to
90
0
  // whitelist their own protocol handlers with respect to CSP,
91
0
  // hence we use protocol flags to accomplish that, but we also
92
0
  // want resource:, chrome: and moz-icon to be subject to CSP
93
0
  // (which also use URI_IS_LOCAL_RESOURCE).
94
0
  // Exception to the rule are images, styles, and localization DTDs
95
0
  // using a scheme of resource: or chrome:
96
0
  bool isImgOrStyleOrDTD = contentType == nsIContentPolicy::TYPE_IMAGE ||
97
0
                      contentType == nsIContentPolicy::TYPE_STYLESHEET ||
98
0
                      contentType == nsIContentPolicy::TYPE_DTD;
99
0
  rv = aURI->SchemeIs("resource", &match);
100
0
  if (NS_SUCCEEDED(rv) && match && !isImgOrStyleOrDTD) {
101
0
    return true;
102
0
  }
103
0
  rv = aURI->SchemeIs("chrome", &match);
104
0
  if (NS_SUCCEEDED(rv) && match && !isImgOrStyleOrDTD) {
105
0
    return true;
106
0
  }
107
0
  rv = aURI->SchemeIs("moz-icon", &match);
108
0
  if (NS_SUCCEEDED(rv) && match) {
109
0
    return true;
110
0
  }
111
0
  rv = NS_URIChainHasFlags(aURI, nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, &match);
112
0
  if (NS_SUCCEEDED(rv) && match) {
113
0
    return false;
114
0
  }
115
0
  // all other protocols are subject To CSP.
116
0
  return true;
117
0
}
118
119
/* nsIContentPolicy implementation */
120
NS_IMETHODIMP
121
CSPService::ShouldLoad(nsIURI *aContentLocation,
122
                       nsILoadInfo* aLoadInfo,
123
                       const nsACString &aMimeTypeGuess,
124
                       int16_t *aDecision)
125
0
{
126
0
  if (!aContentLocation) {
127
0
    return NS_ERROR_FAILURE;
128
0
  }
129
0
130
0
  uint32_t contentType = aLoadInfo->InternalContentPolicyType();
131
0
  nsCOMPtr<nsISupports> requestContext = aLoadInfo->GetLoadingContext();
132
0
  nsCOMPtr<nsIPrincipal> requestPrincipal = aLoadInfo->TriggeringPrincipal();
133
0
  nsCOMPtr<nsIURI> requestOrigin;
134
0
  nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadInfo->LoadingPrincipal();
135
0
  if (loadingPrincipal) {
136
0
    loadingPrincipal->GetURI(getter_AddRefs(requestOrigin));
137
0
  }
138
0
139
0
  if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
140
0
    MOZ_LOG(gCspPRLog, LogLevel::Debug,
141
0
           ("CSPService::ShouldLoad called for %s",
142
0
           aContentLocation->GetSpecOrDefault().get()));
143
0
  }
144
0
145
0
  // default decision, CSP can revise it if there's a policy to enforce
146
0
  *aDecision = nsIContentPolicy::ACCEPT;
147
0
148
0
  // No need to continue processing if CSP is disabled or if the protocol
149
0
  // or type is *not* subject to CSP.
150
0
  // Please note, the correct way to opt-out of CSP using a custom
151
0
  // protocolHandler is to set one of the nsIProtocolHandler flags
152
0
  // that are whitelistet in subjectToCSP()
153
0
  if (!StaticPrefs::security_csp_enable() ||
154
0
      !subjectToCSP(aContentLocation, contentType)) {
155
0
    return NS_OK;
156
0
  }
157
0
158
0
  // Find a principal to retrieve the CSP from. If we don't have a context node
159
0
  // (because, for instance, the load originates in a service worker), or the
160
0
  // requesting principal's CSP overrides our document CSP, use the request
161
0
  // principal. Otherwise, use the document principal.
162
0
  nsCOMPtr<nsINode> node(do_QueryInterface(requestContext));
163
0
  nsCOMPtr<nsIPrincipal> principal;
164
0
  if (!node || (requestPrincipal &&
165
0
                BasePrincipal::Cast(requestPrincipal)->OverridesCSP(node->NodePrincipal()))) {
166
0
    principal = requestPrincipal;
167
0
  } else  {
168
0
    principal = node->NodePrincipal();
169
0
  }
170
0
  if (!principal) {
171
0
    // if we can't query a principal, then there is nothing to do.
172
0
    return NS_OK;
173
0
  }
174
0
  nsresult rv = NS_OK;
175
0
176
0
  // 1) Apply speculate CSP for preloads
177
0
  bool isPreload = nsContentUtils::IsPreloadType(contentType);
178
0
179
0
  if (isPreload) {
180
0
    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
181
0
    rv = principal->GetPreloadCsp(getter_AddRefs(preloadCsp));
182
0
    NS_ENSURE_SUCCESS(rv, rv);
183
0
184
0
    if (preloadCsp) {
185
0
      // obtain the enforcement decision
186
0
      rv = preloadCsp->ShouldLoad(contentType,
187
0
                                  aContentLocation,
188
0
                                  requestOrigin,
189
0
                                  requestContext,
190
0
                                  aMimeTypeGuess,
191
0
                                  nullptr, // no redirect, aOriginal URL is null.
192
0
                                  aLoadInfo->GetSendCSPViolationEvents(),
193
0
                                  aDecision);
194
0
      NS_ENSURE_SUCCESS(rv, rv);
195
0
196
0
      // if the preload policy already denied the load, then there
197
0
      // is no point in checking the real policy
198
0
      if (NS_CP_REJECTED(*aDecision)) {
199
0
        return NS_OK;
200
0
      }
201
0
    }
202
0
  }
203
0
204
0
  // 2) Apply actual CSP to all loads
205
0
  nsCOMPtr<nsIContentSecurityPolicy> csp;
206
0
  rv = principal->GetCsp(getter_AddRefs(csp));
207
0
  NS_ENSURE_SUCCESS(rv, rv);
208
0
209
0
  if (csp) {
210
0
    // obtain the enforcement decision
211
0
    rv = csp->ShouldLoad(contentType,
212
0
                         aContentLocation,
213
0
                         requestOrigin,
214
0
                         requestContext,
215
0
                         aMimeTypeGuess,
216
0
                         nullptr, // no redirect, aOriginal URL is null.
217
0
                         aLoadInfo->GetSendCSPViolationEvents(),
218
0
                         aDecision);
219
0
    NS_ENSURE_SUCCESS(rv, rv);
220
0
  }
221
0
  return NS_OK;
222
0
}
223
224
NS_IMETHODIMP
225
CSPService::ShouldProcess(nsIURI           *aContentLocation,
226
                          nsILoadInfo*     aLoadInfo,
227
                          const nsACString &aMimeTypeGuess,
228
                          int16_t          *aDecision)
229
0
{
230
0
  if (!aContentLocation) {
231
0
    return NS_ERROR_FAILURE;
232
0
  }
233
0
  uint32_t contentType = aLoadInfo->InternalContentPolicyType();
234
0
235
0
  if (MOZ_LOG_TEST(gCspPRLog, LogLevel::Debug)) {
236
0
    MOZ_LOG(gCspPRLog, LogLevel::Debug,
237
0
            ("CSPService::ShouldProcess called for %s",
238
0
            aContentLocation->GetSpecOrDefault().get()));
239
0
  }
240
0
241
0
  // ShouldProcess is only relevant to TYPE_OBJECT, so let's convert the
242
0
  // internal contentPolicyType to the mapping external one.
243
0
  // If it is not TYPE_OBJECT, we can return at this point.
244
0
  // Note that we should still pass the internal contentPolicyType
245
0
  // (contentType) to ShouldLoad().
246
0
  uint32_t policyType =
247
0
    nsContentUtils::InternalContentPolicyTypeToExternal(contentType);
248
0
249
0
  if (policyType != nsIContentPolicy::TYPE_OBJECT) {
250
0
    *aDecision = nsIContentPolicy::ACCEPT;
251
0
    return NS_OK;
252
0
  }
253
0
254
0
  return ShouldLoad(aContentLocation,
255
0
                    aLoadInfo,
256
0
                    aMimeTypeGuess,
257
0
                    aDecision);
258
0
}
259
260
/* nsIChannelEventSink implementation */
261
NS_IMETHODIMP
262
CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
263
                                   nsIChannel *newChannel,
264
                                   uint32_t flags,
265
                                   nsIAsyncVerifyRedirectCallback *callback)
266
0
{
267
0
  net::nsAsyncRedirectAutoCallback autoCallback(callback);
268
0
269
0
  nsCOMPtr<nsIURI> newUri;
270
0
  nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
271
0
  NS_ENSURE_SUCCESS(rv, rv);
272
0
273
0
  nsCOMPtr<nsILoadInfo> loadInfo = oldChannel->GetLoadInfo();
274
0
275
0
  // if no loadInfo on the channel, nothing for us to do
276
0
  if (!loadInfo) {
277
0
    return NS_OK;
278
0
  }
279
0
280
0
  // No need to continue processing if CSP is disabled or if the protocol
281
0
  // is *not* subject to CSP.
282
0
  // Please note, the correct way to opt-out of CSP using a custom
283
0
  // protocolHandler is to set one of the nsIProtocolHandler flags
284
0
  // that are whitelistet in subjectToCSP()
285
0
  nsContentPolicyType policyType = loadInfo->InternalContentPolicyType();
286
0
  if (!StaticPrefs::security_csp_enable() ||
287
0
      !subjectToCSP(newUri, policyType)) {
288
0
    return NS_OK;
289
0
  }
290
0
291
0
  /* Since redirecting channels don't call into nsIContentPolicy, we call our
292
0
   * Content Policy implementation directly when redirects occur using the
293
0
   * information set in the LoadInfo when channels are created.
294
0
   *
295
0
   * We check if the CSP permits this host for this type of load, if not,
296
0
   * we cancel the load now.
297
0
   */
298
0
  nsCOMPtr<nsIURI> originalUri;
299
0
  rv = oldChannel->GetOriginalURI(getter_AddRefs(originalUri));
300
0
  if (NS_FAILED(rv)) {
301
0
    autoCallback.DontCallback();
302
0
    oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
303
0
    return rv;
304
0
  }
305
0
306
0
  bool isPreload = nsContentUtils::IsPreloadType(policyType);
307
0
308
0
  /* On redirect, if the content policy is a preload type, rejecting the preload
309
0
   * results in the load silently failing, so we convert preloads to the actual
310
0
   * type. See Bug 1219453.
311
0
   */
312
0
  policyType =
313
0
    nsContentUtils::InternalContentPolicyTypeToExternalOrWorker(policyType);
314
0
315
0
  int16_t aDecision = nsIContentPolicy::ACCEPT;
316
0
  nsCOMPtr<nsISupports> requestContext = loadInfo->GetLoadingContext();
317
0
  // 1) Apply speculative CSP for preloads
318
0
  if (isPreload) {
319
0
    nsCOMPtr<nsIContentSecurityPolicy> preloadCsp;
320
0
    loadInfo->LoadingPrincipal()->GetPreloadCsp(getter_AddRefs(preloadCsp));
321
0
322
0
    if (preloadCsp) {
323
0
      // Pass  originalURI to indicate the redirect
324
0
      preloadCsp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
325
0
                             newUri,         // nsIURI
326
0
                             nullptr,        // nsIURI
327
0
                             requestContext, // nsISupports
328
0
                             EmptyCString(), // ACString - MIME guess
329
0
                             originalUri,    // Original nsIURI
330
0
                             true,           // aSendViolationReports
331
0
                             &aDecision);
332
0
333
0
      // if the preload policy already denied the load, then there
334
0
      // is no point in checking the real policy
335
0
      if (NS_CP_REJECTED(aDecision)) {
336
0
        autoCallback.DontCallback();
337
0
        oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
338
0
        return NS_BINDING_FAILED;
339
0
      }
340
0
    }
341
0
  }
342
0
343
0
  // 2) Apply actual CSP to all loads
344
0
  nsCOMPtr<nsIContentSecurityPolicy> csp;
345
0
  loadInfo->LoadingPrincipal()->GetCsp(getter_AddRefs(csp));
346
0
347
0
  if (csp) {
348
0
    // Pass  originalURI to indicate the redirect
349
0
    csp->ShouldLoad(policyType,     // load type per nsIContentPolicy (uint32_t)
350
0
                    newUri,         // nsIURI
351
0
                    nullptr,        // nsIURI
352
0
                    requestContext, // nsISupports
353
0
                    EmptyCString(), // ACString - MIME guess
354
0
                    originalUri,    // Original nsIURI
355
0
                    true,           // aSendViolationReports
356
0
                    &aDecision);
357
0
  }
358
0
359
0
  // if ShouldLoad doesn't accept the load, cancel the request
360
0
  if (!NS_CP_ACCEPTED(aDecision)) {
361
0
    autoCallback.DontCallback();
362
0
    oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
363
0
    return NS_BINDING_FAILED;
364
0
  }
365
0
  return NS_OK;
366
0
}