Line data Source code
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 "nsAttrValue.h"
8 : #include "nsCharSeparatedTokenizer.h"
9 : #include "nsContentUtils.h"
10 : #include "nsCSPUtils.h"
11 : #include "nsDebug.h"
12 : #include "nsIConsoleService.h"
13 : #include "nsICryptoHash.h"
14 : #include "nsIScriptError.h"
15 : #include "nsIServiceManager.h"
16 : #include "nsIStringBundle.h"
17 : #include "nsIURL.h"
18 : #include "nsReadableUtils.h"
19 : #include "nsSandboxFlags.h"
20 :
21 : #define DEFAULT_PORT -1
22 :
23 : static mozilla::LogModule*
24 0 : GetCspUtilsLog()
25 : {
26 : static mozilla::LazyLogModule gCspUtilsPRLog("CSPUtils");
27 0 : return gCspUtilsPRLog;
28 : }
29 :
30 : #define CSPUTILSLOG(args) MOZ_LOG(GetCspUtilsLog(), mozilla::LogLevel::Debug, args)
31 : #define CSPUTILSLOGENABLED() MOZ_LOG_TEST(GetCspUtilsLog(), mozilla::LogLevel::Debug)
32 :
33 : void
34 0 : CSP_PercentDecodeStr(const nsAString& aEncStr, nsAString& outDecStr)
35 : {
36 0 : outDecStr.Truncate();
37 :
38 : // helper function that should not be visible outside this methods scope
39 : struct local {
40 0 : static inline char16_t convertHexDig(char16_t aHexDig) {
41 0 : if (isNumberToken(aHexDig)) {
42 0 : return aHexDig - '0';
43 : }
44 0 : if (aHexDig >= 'A' && aHexDig <= 'F') {
45 0 : return aHexDig - 'A' + 10;
46 : }
47 : // must be a lower case character
48 : // (aHexDig >= 'a' && aHexDig <= 'f')
49 0 : return aHexDig - 'a' + 10;
50 : }
51 : };
52 :
53 : const char16_t *cur, *end, *hexDig1, *hexDig2;
54 0 : cur = aEncStr.BeginReading();
55 0 : end = aEncStr.EndReading();
56 :
57 0 : while (cur != end) {
58 : // if it's not a percent sign then there is
59 : // nothing to do for that character
60 0 : if (*cur != PERCENT_SIGN) {
61 0 : outDecStr.Append(*cur);
62 0 : cur++;
63 0 : continue;
64 : }
65 :
66 : // get the two hexDigs following the '%'-sign
67 0 : hexDig1 = cur + 1;
68 0 : hexDig2 = cur + 2;
69 :
70 : // if there are no hexdigs after the '%' then
71 : // there is nothing to do for us.
72 0 : if (hexDig1 == end || hexDig2 == end ||
73 0 : !isValidHexDig(*hexDig1) ||
74 0 : !isValidHexDig(*hexDig2)) {
75 0 : outDecStr.Append(PERCENT_SIGN);
76 0 : cur++;
77 0 : continue;
78 : }
79 :
80 : // decode "% hexDig1 hexDig2" into a character.
81 0 : char16_t decChar = (local::convertHexDig(*hexDig1) << 4) +
82 0 : local::convertHexDig(*hexDig2);
83 0 : outDecStr.Append(decChar);
84 :
85 : // increment 'cur' to after the second hexDig
86 0 : cur = ++hexDig2;
87 : }
88 0 : }
89 :
90 : void
91 0 : CSP_GetLocalizedStr(const char16_t* aName,
92 : const char16_t** aParams,
93 : uint32_t aLength,
94 : char16_t** outResult)
95 : {
96 0 : nsCOMPtr<nsIStringBundle> keyStringBundle;
97 : nsCOMPtr<nsIStringBundleService> stringBundleService =
98 0 : mozilla::services::GetStringBundleService();
99 :
100 0 : NS_ASSERTION(stringBundleService, "String bundle service must be present!");
101 0 : stringBundleService->CreateBundle("chrome://global/locale/security/csp.properties",
102 0 : getter_AddRefs(keyStringBundle));
103 :
104 0 : NS_ASSERTION(keyStringBundle, "Key string bundle must be available!");
105 :
106 0 : if (!keyStringBundle) {
107 0 : return;
108 : }
109 0 : keyStringBundle->FormatStringFromName(aName, aParams, aLength, outResult);
110 : }
111 :
112 : void
113 0 : CSP_LogStrMessage(const nsAString& aMsg)
114 : {
115 0 : nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
116 :
117 0 : if (!console) {
118 0 : return;
119 : }
120 0 : nsString msg = PromiseFlatString(aMsg);
121 0 : console->LogStringMessage(msg.get());
122 : }
123 :
124 : void
125 0 : CSP_LogMessage(const nsAString& aMessage,
126 : const nsAString& aSourceName,
127 : const nsAString& aSourceLine,
128 : uint32_t aLineNumber,
129 : uint32_t aColumnNumber,
130 : uint32_t aFlags,
131 : const char *aCategory,
132 : uint64_t aInnerWindowID)
133 : {
134 0 : nsCOMPtr<nsIConsoleService> console(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
135 :
136 0 : nsCOMPtr<nsIScriptError> error(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
137 :
138 0 : if (!console || !error) {
139 0 : return;
140 : }
141 :
142 : // Prepending CSP to the outgoing console message
143 0 : nsString cspMsg;
144 0 : cspMsg.Append(NS_LITERAL_STRING("Content Security Policy: "));
145 0 : cspMsg.Append(aMessage);
146 :
147 : // Currently 'aSourceLine' is not logged to the console, because similar
148 : // information is already included within the source link of the message.
149 : // For inline violations however, the line and column number are 0 and
150 : // information contained within 'aSourceLine' can be really useful for devs.
151 : // E.g. 'aSourceLine' might be: 'onclick attribute on DIV element'.
152 : // In such cases we append 'aSourceLine' directly to the error message.
153 0 : if (!aSourceLine.IsEmpty()) {
154 0 : cspMsg.Append(NS_LITERAL_STRING(" Source: "));
155 0 : cspMsg.Append(aSourceLine);
156 0 : cspMsg.Append(NS_LITERAL_STRING("."));
157 : }
158 :
159 : nsresult rv;
160 0 : if (aInnerWindowID > 0) {
161 0 : nsCString catStr;
162 0 : catStr.AssignASCII(aCategory);
163 0 : rv = error->InitWithWindowID(cspMsg, aSourceName,
164 : aSourceLine, aLineNumber,
165 : aColumnNumber, aFlags,
166 0 : catStr, aInnerWindowID);
167 : }
168 : else {
169 0 : rv = error->Init(cspMsg, aSourceName,
170 : aSourceLine, aLineNumber,
171 : aColumnNumber, aFlags,
172 0 : aCategory);
173 : }
174 0 : if (NS_FAILED(rv)) {
175 0 : return;
176 : }
177 0 : console->LogMessage(error);
178 : }
179 :
180 : /**
181 : * Combines CSP_LogMessage and CSP_GetLocalizedStr into one call.
182 : */
183 : void
184 0 : CSP_LogLocalizedStr(const char16_t* aName,
185 : const char16_t** aParams,
186 : uint32_t aLength,
187 : const nsAString& aSourceName,
188 : const nsAString& aSourceLine,
189 : uint32_t aLineNumber,
190 : uint32_t aColumnNumber,
191 : uint32_t aFlags,
192 : const char* aCategory,
193 : uint64_t aInnerWindowID)
194 : {
195 0 : nsXPIDLString logMsg;
196 0 : CSP_GetLocalizedStr(aName, aParams, aLength, getter_Copies(logMsg));
197 : CSP_LogMessage(logMsg, aSourceName, aSourceLine,
198 : aLineNumber, aColumnNumber, aFlags,
199 0 : aCategory, aInnerWindowID);
200 0 : }
201 :
202 : /* ===== Helpers ============================ */
203 : CSPDirective
204 0 : CSP_ContentTypeToDirective(nsContentPolicyType aType)
205 : {
206 0 : switch (aType) {
207 : case nsIContentPolicy::TYPE_IMAGE:
208 : case nsIContentPolicy::TYPE_IMAGESET:
209 0 : return nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE;
210 :
211 : // BLock XSLT as script, see bug 910139
212 : case nsIContentPolicy::TYPE_XSLT:
213 : case nsIContentPolicy::TYPE_SCRIPT:
214 : case nsIContentPolicy::TYPE_INTERNAL_SCRIPT:
215 : case nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD:
216 : case nsIContentPolicy::TYPE_INTERNAL_WORKER_IMPORT_SCRIPTS:
217 0 : return nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE;
218 :
219 : case nsIContentPolicy::TYPE_STYLESHEET:
220 0 : return nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE;
221 :
222 : case nsIContentPolicy::TYPE_FONT:
223 0 : return nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE;
224 :
225 : case nsIContentPolicy::TYPE_MEDIA:
226 0 : return nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE;
227 :
228 : case nsIContentPolicy::TYPE_WEB_MANIFEST:
229 0 : return nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE;
230 :
231 : case nsIContentPolicy::TYPE_INTERNAL_WORKER:
232 : case nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER:
233 : case nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER:
234 0 : return nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE;
235 :
236 : case nsIContentPolicy::TYPE_SUBDOCUMENT:
237 0 : return nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE;
238 :
239 : case nsIContentPolicy::TYPE_WEBSOCKET:
240 : case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
241 : case nsIContentPolicy::TYPE_BEACON:
242 : case nsIContentPolicy::TYPE_PING:
243 : case nsIContentPolicy::TYPE_FETCH:
244 0 : return nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE;
245 :
246 : case nsIContentPolicy::TYPE_OBJECT:
247 : case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
248 0 : return nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE;
249 :
250 : case nsIContentPolicy::TYPE_XBL:
251 : case nsIContentPolicy::TYPE_DTD:
252 : case nsIContentPolicy::TYPE_OTHER:
253 0 : return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
254 :
255 : // csp shold not block top level loads, e.g. in case
256 : // of a redirect.
257 : case nsIContentPolicy::TYPE_DOCUMENT:
258 : // CSP can not block csp reports
259 : case nsIContentPolicy::TYPE_CSP_REPORT:
260 0 : return nsIContentSecurityPolicy::NO_DIRECTIVE;
261 :
262 : // Fall through to error for all other directives
263 : default:
264 0 : MOZ_ASSERT(false, "Can not map nsContentPolicyType to CSPDirective");
265 : }
266 : return nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE;
267 : }
268 :
269 : nsCSPHostSrc*
270 0 : CSP_CreateHostSrcFromSelfURI(nsIURI* aSelfURI)
271 : {
272 : // Create the host first
273 0 : nsCString host;
274 0 : aSelfURI->GetAsciiHost(host);
275 0 : nsCSPHostSrc *hostsrc = new nsCSPHostSrc(NS_ConvertUTF8toUTF16(host));
276 0 : hostsrc->setGeneratedFromSelfKeyword();
277 :
278 : // Add the scheme.
279 0 : nsCString scheme;
280 0 : aSelfURI->GetScheme(scheme);
281 0 : hostsrc->setScheme(NS_ConvertUTF8toUTF16(scheme));
282 :
283 : int32_t port;
284 0 : aSelfURI->GetPort(&port);
285 : // Only add port if it's not default port.
286 0 : if (port > 0) {
287 0 : nsAutoString portStr;
288 0 : portStr.AppendInt(port);
289 0 : hostsrc->setPort(portStr);
290 : }
291 0 : return hostsrc;
292 : }
293 :
294 : bool
295 0 : CSP_IsValidDirective(const nsAString& aDir)
296 : {
297 0 : uint32_t numDirs = (sizeof(CSPStrDirectives) / sizeof(CSPStrDirectives[0]));
298 :
299 0 : for (uint32_t i = 0; i < numDirs; i++) {
300 0 : if (aDir.LowerCaseEqualsASCII(CSPStrDirectives[i])) {
301 0 : return true;
302 : }
303 : }
304 0 : return false;
305 : }
306 : bool
307 0 : CSP_IsDirective(const nsAString& aValue, CSPDirective aDir)
308 : {
309 0 : return aValue.LowerCaseEqualsASCII(CSP_CSPDirectiveToString(aDir));
310 : }
311 :
312 : bool
313 0 : CSP_IsKeyword(const nsAString& aValue, enum CSPKeyword aKey)
314 : {
315 0 : return aValue.LowerCaseEqualsASCII(CSP_EnumToKeyword(aKey));
316 : }
317 :
318 : bool
319 0 : CSP_IsQuotelessKeyword(const nsAString& aKey)
320 : {
321 0 : nsString lowerKey = PromiseFlatString(aKey);
322 0 : ToLowerCase(lowerKey);
323 :
324 : static_assert(CSP_LAST_KEYWORD_VALUE ==
325 : (sizeof(CSPStrKeywords) / sizeof(CSPStrKeywords[0])),
326 : "CSP_LAST_KEYWORD_VALUE does not match length of CSPStrKeywords");
327 :
328 0 : nsAutoString keyword;
329 0 : for (uint32_t i = 0; i < CSP_LAST_KEYWORD_VALUE; i++) {
330 : // skipping the leading ' and trimming the trailing '
331 0 : keyword.AssignASCII(CSPStrKeywords[i] + 1);
332 0 : keyword.Trim("'", false, true);
333 0 : if (lowerKey.Equals(keyword)) {
334 0 : return true;
335 : }
336 : }
337 0 : return false;
338 : }
339 :
340 : /*
341 : * Checks whether the current directive permits a specific
342 : * scheme. This function is called from nsCSPSchemeSrc() and
343 : * also nsCSPHostSrc.
344 : * @param aEnforcementScheme
345 : * The scheme that this directive allows
346 : * @param aUri
347 : * The uri of the subresource load.
348 : * @param aReportOnly
349 : * Whether the enforced policy is report only or not.
350 : * @param aUpgradeInsecure
351 : * Whether the policy makes use of the directive
352 : * 'upgrade-insecure-requests'.
353 : * @param aFromSelfURI
354 : * Whether a scheme was generated from the keyword 'self'
355 : * which then allows schemeless sources to match ws and wss.
356 : */
357 :
358 : bool
359 0 : permitsScheme(const nsAString& aEnforcementScheme,
360 : nsIURI* aUri,
361 : bool aReportOnly,
362 : bool aUpgradeInsecure,
363 : bool aFromSelfURI)
364 : {
365 0 : nsAutoCString scheme;
366 0 : nsresult rv = aUri->GetScheme(scheme);
367 0 : NS_ENSURE_SUCCESS(rv, false);
368 :
369 : // no scheme to enforce, let's allow the load (e.g. script-src *)
370 0 : if (aEnforcementScheme.IsEmpty()) {
371 0 : return true;
372 : }
373 :
374 : // if the scheme matches, all good - allow the load
375 0 : if (aEnforcementScheme.EqualsASCII(scheme.get())) {
376 0 : return true;
377 : }
378 :
379 : // allow scheme-less sources where the protected resource is http
380 : // and the load is https, see:
381 : // http://www.w3.org/TR/CSP2/#match-source-expression
382 0 : if (aEnforcementScheme.EqualsASCII("http")) {
383 0 : if (scheme.EqualsASCII("https")) {
384 0 : return true;
385 : }
386 0 : if ((scheme.EqualsASCII("ws") || scheme.EqualsASCII("wss")) && aFromSelfURI) {
387 0 : return true;
388 : }
389 : }
390 0 : if (aEnforcementScheme.EqualsASCII("https")) {
391 0 : if (scheme.EqualsLiteral("wss") && aFromSelfURI) {
392 0 : return true;
393 : }
394 : }
395 0 : if (aEnforcementScheme.EqualsASCII("ws") && scheme.EqualsASCII("wss")) {
396 0 : return true;
397 : }
398 :
399 : // Allow the load when enforcing upgrade-insecure-requests with the
400 : // promise the request gets upgraded from http to https and ws to wss.
401 : // See nsHttpChannel::Connect() and also WebSocket.cpp. Please note,
402 : // the report only policies should not allow the load and report
403 : // the error back to the page.
404 0 : return ((aUpgradeInsecure && !aReportOnly) &&
405 0 : ((scheme.EqualsASCII("http") && aEnforcementScheme.EqualsASCII("https")) ||
406 0 : (scheme.EqualsASCII("ws") && aEnforcementScheme.EqualsASCII("wss"))));
407 : }
408 :
409 : /*
410 : * A helper function for appending a CSP header to an existing CSP
411 : * policy.
412 : *
413 : * @param aCsp the CSP policy
414 : * @param aHeaderValue the header
415 : * @param aReportOnly is this a report-only header?
416 : */
417 :
418 : nsresult
419 0 : CSP_AppendCSPFromHeader(nsIContentSecurityPolicy* aCsp,
420 : const nsAString& aHeaderValue,
421 : bool aReportOnly)
422 : {
423 0 : NS_ENSURE_ARG(aCsp);
424 :
425 : // Need to tokenize the header value since multiple headers could be
426 : // concatenated into one comma-separated list of policies.
427 : // See RFC2616 section 4.2 (last paragraph)
428 0 : nsresult rv = NS_OK;
429 0 : nsCharSeparatedTokenizer tokenizer(aHeaderValue, ',');
430 0 : while (tokenizer.hasMoreTokens()) {
431 0 : const nsAString& policy = tokenizer.nextToken();
432 0 : rv = aCsp->AppendPolicy(policy, aReportOnly, false);
433 0 : NS_ENSURE_SUCCESS(rv, rv);
434 : {
435 0 : CSPUTILSLOG(("CSP refined with policy: \"%s\"",
436 : NS_ConvertUTF16toUTF8(policy).get()));
437 : }
438 : }
439 0 : return NS_OK;
440 : }
441 :
442 : /* ===== nsCSPSrc ============================ */
443 :
444 0 : nsCSPBaseSrc::nsCSPBaseSrc()
445 0 : : mInvalidated(false)
446 : {
447 0 : }
448 :
449 0 : nsCSPBaseSrc::~nsCSPBaseSrc()
450 : {
451 0 : }
452 :
453 : // ::permits is only called for external load requests, therefore:
454 : // nsCSPKeywordSrc and nsCSPHashSource fall back to this base class
455 : // implementation which will never allow the load.
456 : bool
457 0 : nsCSPBaseSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
458 : bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const
459 : {
460 0 : if (CSPUTILSLOGENABLED()) {
461 0 : CSPUTILSLOG(("nsCSPBaseSrc::permits, aUri: %s",
462 : aUri->GetSpecOrDefault().get()));
463 : }
464 0 : return false;
465 : }
466 :
467 : // ::allows is only called for inlined loads, therefore:
468 : // nsCSPSchemeSrc, nsCSPHostSrc fall back
469 : // to this base class implementation which will never allow the load.
470 : bool
471 0 : nsCSPBaseSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
472 : bool aParserCreated) const
473 : {
474 0 : CSPUTILSLOG(("nsCSPBaseSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
475 : aKeyword == CSP_HASH ? "hash" : CSP_EnumToKeyword(aKeyword),
476 : NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
477 0 : return false;
478 : }
479 :
480 : /* ====== nsCSPSchemeSrc ===================== */
481 :
482 0 : nsCSPSchemeSrc::nsCSPSchemeSrc(const nsAString& aScheme)
483 0 : : mScheme(aScheme)
484 : {
485 0 : ToLowerCase(mScheme);
486 0 : }
487 :
488 0 : nsCSPSchemeSrc::~nsCSPSchemeSrc()
489 : {
490 0 : }
491 :
492 : bool
493 0 : nsCSPSchemeSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
494 : bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const
495 : {
496 0 : if (CSPUTILSLOGENABLED()) {
497 0 : CSPUTILSLOG(("nsCSPSchemeSrc::permits, aUri: %s",
498 : aUri->GetSpecOrDefault().get()));
499 : }
500 0 : MOZ_ASSERT((!mScheme.EqualsASCII("")), "scheme can not be the empty string");
501 0 : if (mInvalidated) {
502 0 : return false;
503 : }
504 0 : return permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, false);
505 : }
506 :
507 : bool
508 0 : nsCSPSchemeSrc::visit(nsCSPSrcVisitor* aVisitor) const
509 : {
510 0 : return aVisitor->visitSchemeSrc(*this);
511 : }
512 :
513 : void
514 0 : nsCSPSchemeSrc::toString(nsAString& outStr) const
515 : {
516 0 : outStr.Append(mScheme);
517 0 : outStr.AppendASCII(":");
518 0 : }
519 :
520 : /* ===== nsCSPHostSrc ======================== */
521 :
522 0 : nsCSPHostSrc::nsCSPHostSrc(const nsAString& aHost)
523 : : mHost(aHost)
524 : , mGeneratedFromSelfKeyword(false)
525 0 : , mWithinFrameAncstorsDir(false)
526 : {
527 0 : ToLowerCase(mHost);
528 0 : }
529 :
530 0 : nsCSPHostSrc::~nsCSPHostSrc()
531 : {
532 0 : }
533 :
534 : /*
535 : * Checks whether the current directive permits a specific port.
536 : * @param aEnforcementScheme
537 : * The scheme that this directive allows
538 : * (used to query the default port for that scheme)
539 : * @param aEnforcementPort
540 : * The port that this directive allows
541 : * @param aResourceURI
542 : * The uri of the subresource load
543 : */
544 : bool
545 0 : permitsPort(const nsAString& aEnforcementScheme,
546 : const nsAString& aEnforcementPort,
547 : nsIURI* aResourceURI)
548 : {
549 : // If enforcement port is the wildcard, don't block the load.
550 0 : if (aEnforcementPort.EqualsASCII("*")) {
551 0 : return true;
552 : }
553 :
554 : int32_t resourcePort;
555 0 : nsresult rv = aResourceURI->GetPort(&resourcePort);
556 0 : NS_ENSURE_SUCCESS(rv, false);
557 :
558 : // Avoid unnecessary string creation/manipulation and don't block the
559 : // load if the resource to be loaded uses the default port for that
560 : // scheme and there is no port to be enforced.
561 : // Note, this optimization relies on scheme checks within permitsScheme().
562 0 : if (resourcePort == DEFAULT_PORT && aEnforcementPort.IsEmpty()) {
563 0 : return true;
564 : }
565 :
566 : // By now we know at that either the resourcePort does not use the default
567 : // port or there is a port restriction to be enforced. A port value of -1
568 : // corresponds to the protocol's default port (eg. -1 implies port 80 for
569 : // http URIs), in such a case we have to query the default port of the
570 : // resource to be loaded.
571 0 : if (resourcePort == DEFAULT_PORT) {
572 0 : nsAutoCString resourceScheme;
573 0 : rv = aResourceURI->GetScheme(resourceScheme);
574 0 : NS_ENSURE_SUCCESS(rv, false);
575 0 : resourcePort = NS_GetDefaultPort(resourceScheme.get());
576 : }
577 :
578 : // If there is a port to be enforced and the ports match, then
579 : // don't block the load.
580 0 : nsString resourcePortStr;
581 0 : resourcePortStr.AppendInt(resourcePort);
582 0 : if (aEnforcementPort.Equals(resourcePortStr)) {
583 0 : return true;
584 : }
585 :
586 : // If there is no port to be enforced, query the default port for the load.
587 0 : nsString enforcementPort(aEnforcementPort);
588 0 : if (enforcementPort.IsEmpty()) {
589 : // For scheme less sources, our parser always generates a scheme
590 : // which is the scheme of the protected resource.
591 0 : MOZ_ASSERT(!aEnforcementScheme.IsEmpty(),
592 : "need a scheme to query default port");
593 : int32_t defaultEnforcementPort =
594 0 : NS_GetDefaultPort(NS_ConvertUTF16toUTF8(aEnforcementScheme).get());
595 0 : enforcementPort.Truncate();
596 0 : enforcementPort.AppendInt(defaultEnforcementPort);
597 : }
598 :
599 : // If default ports match, don't block the load
600 0 : if (enforcementPort.Equals(resourcePortStr)) {
601 0 : return true;
602 : }
603 :
604 : // Additional port matching where the regular URL matching algorithm
605 : // treats insecure ports as matching their secure variants.
606 : // default port for http is :80
607 : // default port for https is :443
608 0 : if (enforcementPort.EqualsLiteral("80") &&
609 0 : resourcePortStr.EqualsLiteral("443")) {
610 0 : return true;
611 : }
612 :
613 : // ports do not match, block the load.
614 0 : return false;
615 : }
616 :
617 : bool
618 0 : nsCSPHostSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
619 : bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const
620 : {
621 0 : if (CSPUTILSLOGENABLED()) {
622 0 : CSPUTILSLOG(("nsCSPHostSrc::permits, aUri: %s",
623 : aUri->GetSpecOrDefault().get()));
624 : }
625 :
626 0 : if (mInvalidated) {
627 0 : return false;
628 : }
629 :
630 : // we are following the enforcement rules from the spec, see:
631 : // http://www.w3.org/TR/CSP11/#match-source-expression
632 :
633 : // 4.3) scheme matching: Check if the scheme matches.
634 0 : if (!permitsScheme(mScheme, aUri, aReportOnly, aUpgradeInsecure, mGeneratedFromSelfKeyword)) {
635 0 : return false;
636 : }
637 :
638 : // The host in nsCSpHostSrc should never be empty. In case we are enforcing
639 : // just a specific scheme, the parser should generate a nsCSPSchemeSource.
640 0 : NS_ASSERTION((!mHost.IsEmpty()), "host can not be the empty string");
641 :
642 : // 2) host matching: Enforce a single *
643 0 : if (mHost.EqualsASCII("*")) {
644 : // The single ASTERISK character (*) does not match a URI's scheme of a type
645 : // designating a globally unique identifier (such as blob:, data:, or filesystem:)
646 : // At the moment firefox does not support filesystem; but for future compatibility
647 : // we support it in CSP according to the spec, see: 4.2.2 Matching Source Expressions
648 : // Note, that whitelisting any of these schemes would call nsCSPSchemeSrc::permits().
649 : bool isBlobScheme =
650 0 : (NS_SUCCEEDED(aUri->SchemeIs("blob", &isBlobScheme)) && isBlobScheme);
651 : bool isDataScheme =
652 0 : (NS_SUCCEEDED(aUri->SchemeIs("data", &isDataScheme)) && isDataScheme);
653 : bool isFileScheme =
654 0 : (NS_SUCCEEDED(aUri->SchemeIs("filesystem", &isFileScheme)) && isFileScheme);
655 :
656 0 : if (isBlobScheme || isDataScheme || isFileScheme) {
657 0 : return false;
658 : }
659 0 : return true;
660 : }
661 :
662 : // Before we can check if the host matches, we have to
663 : // extract the host part from aUri.
664 0 : nsAutoCString uriHost;
665 0 : nsresult rv = aUri->GetAsciiHost(uriHost);
666 0 : NS_ENSURE_SUCCESS(rv, false);
667 :
668 0 : nsString decodedUriHost;
669 0 : CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriHost), decodedUriHost);
670 :
671 : // 4.5) host matching: Check if the allowed host starts with a wilcard.
672 0 : if (mHost.First() == '*') {
673 0 : NS_ASSERTION(mHost[1] == '.', "Second character needs to be '.' whenever host starts with '*'");
674 :
675 : // Eliminate leading "*", but keeping the FULL STOP (.) thereafter before checking
676 : // if the remaining characters match
677 0 : nsString wildCardHost = mHost;
678 0 : wildCardHost = Substring(wildCardHost, 1, wildCardHost.Length() - 1);
679 0 : if (!StringEndsWith(decodedUriHost, wildCardHost)) {
680 0 : return false;
681 : }
682 : }
683 : // 4.6) host matching: Check if hosts match.
684 0 : else if (!mHost.Equals(decodedUriHost)) {
685 0 : return false;
686 : }
687 :
688 : // Port matching: Check if the ports match.
689 0 : if (!permitsPort(mScheme, mPort, aUri)) {
690 0 : return false;
691 : }
692 :
693 : // 4.9) Path matching: If there is a path, we have to enforce
694 : // path-level matching, unless the channel got redirected, see:
695 : // http://www.w3.org/TR/CSP11/#source-list-paths-and-redirects
696 0 : if (!aWasRedirected && !mPath.IsEmpty()) {
697 : // converting aUri into nsIURL so we can strip query and ref
698 : // example.com/test#foo -> example.com/test
699 : // example.com/test?val=foo -> example.com/test
700 0 : nsCOMPtr<nsIURL> url = do_QueryInterface(aUri);
701 0 : if (!url) {
702 0 : NS_ASSERTION(false, "can't QI into nsIURI");
703 0 : return false;
704 : }
705 0 : nsAutoCString uriPath;
706 0 : rv = url->GetFilePath(uriPath);
707 0 : NS_ENSURE_SUCCESS(rv, false);
708 :
709 0 : if (mWithinFrameAncstorsDir) {
710 : // no path matching for frame-ancestors to not leak any path information.
711 0 : return true;
712 : }
713 :
714 0 : nsString decodedUriPath;
715 0 : CSP_PercentDecodeStr(NS_ConvertUTF8toUTF16(uriPath), decodedUriPath);
716 :
717 : // check if the last character of mPath is '/'; if so
718 : // we just have to check loading resource is within
719 : // the allowed path.
720 0 : if (mPath.Last() == '/') {
721 0 : if (!StringBeginsWith(decodedUriPath, mPath)) {
722 0 : return false;
723 : }
724 : }
725 : // otherwise mPath whitelists a specific file, and we have to
726 : // check if the loading resource matches that whitelisted file.
727 : else {
728 0 : if (!mPath.Equals(decodedUriPath)) {
729 0 : return false;
730 : }
731 : }
732 : }
733 :
734 : // At the end: scheme, host, port and path match -> allow the load.
735 0 : return true;
736 : }
737 :
738 : bool
739 0 : nsCSPHostSrc::visit(nsCSPSrcVisitor* aVisitor) const
740 : {
741 0 : return aVisitor->visitHostSrc(*this);
742 : }
743 :
744 : void
745 0 : nsCSPHostSrc::toString(nsAString& outStr) const
746 : {
747 : // If mHost is a single "*", we append the wildcard and return.
748 0 : if (mHost.EqualsASCII("*") &&
749 0 : mScheme.IsEmpty() &&
750 0 : mPort.IsEmpty()) {
751 0 : outStr.Append(mHost);
752 0 : return;
753 : }
754 :
755 : // append scheme
756 0 : outStr.Append(mScheme);
757 :
758 : // append host
759 0 : outStr.AppendASCII("://");
760 0 : outStr.Append(mHost);
761 :
762 : // append port
763 0 : if (!mPort.IsEmpty()) {
764 0 : outStr.AppendASCII(":");
765 0 : outStr.Append(mPort);
766 : }
767 :
768 : // append path
769 0 : outStr.Append(mPath);
770 : }
771 :
772 : void
773 0 : nsCSPHostSrc::setScheme(const nsAString& aScheme)
774 : {
775 0 : mScheme = aScheme;
776 0 : ToLowerCase(mScheme);
777 0 : }
778 :
779 : void
780 0 : nsCSPHostSrc::setPort(const nsAString& aPort)
781 : {
782 0 : mPort = aPort;
783 0 : }
784 :
785 : void
786 0 : nsCSPHostSrc::appendPath(const nsAString& aPath)
787 : {
788 0 : mPath.Append(aPath);
789 0 : }
790 :
791 : /* ===== nsCSPKeywordSrc ===================== */
792 :
793 0 : nsCSPKeywordSrc::nsCSPKeywordSrc(enum CSPKeyword aKeyword)
794 0 : : mKeyword(aKeyword)
795 : {
796 0 : NS_ASSERTION((aKeyword != CSP_SELF),
797 : "'self' should have been replaced in the parser");
798 0 : }
799 :
800 0 : nsCSPKeywordSrc::~nsCSPKeywordSrc()
801 : {
802 0 : }
803 :
804 : bool
805 0 : nsCSPKeywordSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
806 : bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const
807 : {
808 : // no need to check for invalidated, this will always return false unless
809 : // it is an nsCSPKeywordSrc for 'strict-dynamic', which should allow non
810 : // parser created scripts.
811 0 : return ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated);
812 : }
813 :
814 : bool
815 0 : nsCSPKeywordSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
816 : bool aParserCreated) const
817 : {
818 0 : CSPUTILSLOG(("nsCSPKeywordSrc::allows, aKeyWord: %s, aHashOrNonce: %s, mInvalidated: %s",
819 : CSP_EnumToKeyword(aKeyword),
820 : NS_ConvertUTF16toUTF8(aHashOrNonce).get(),
821 : mInvalidated ? "yes" : "false"));
822 :
823 0 : if (mInvalidated) {
824 : // only 'self' and 'unsafe-inline' are keywords that can be ignored. Please note that
825 : // the parser already translates 'self' into a uri (see assertion in constructor).
826 0 : MOZ_ASSERT(mKeyword == CSP_UNSAFE_INLINE,
827 : "should only invalidate unsafe-inline");
828 0 : return false;
829 : }
830 : // either the keyword allows the load or the policy contains 'strict-dynamic', in which
831 : // case we have to make sure the script is not parser created before allowing the load.
832 0 : return ((mKeyword == aKeyword) ||
833 0 : ((mKeyword == CSP_STRICT_DYNAMIC) && !aParserCreated));
834 : }
835 :
836 : bool
837 0 : nsCSPKeywordSrc::visit(nsCSPSrcVisitor* aVisitor) const
838 : {
839 0 : return aVisitor->visitKeywordSrc(*this);
840 : }
841 :
842 : void
843 0 : nsCSPKeywordSrc::toString(nsAString& outStr) const
844 : {
845 0 : outStr.AppendASCII(CSP_EnumToKeyword(mKeyword));
846 0 : }
847 :
848 : /* ===== nsCSPNonceSrc ==================== */
849 :
850 0 : nsCSPNonceSrc::nsCSPNonceSrc(const nsAString& aNonce)
851 0 : : mNonce(aNonce)
852 : {
853 0 : }
854 :
855 0 : nsCSPNonceSrc::~nsCSPNonceSrc()
856 : {
857 0 : }
858 :
859 : bool
860 0 : nsCSPNonceSrc::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
861 : bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const
862 : {
863 0 : if (CSPUTILSLOGENABLED()) {
864 0 : CSPUTILSLOG(("nsCSPNonceSrc::permits, aUri: %s, aNonce: %s",
865 : aUri->GetSpecOrDefault().get(),
866 : NS_ConvertUTF16toUTF8(aNonce).get()));
867 : }
868 :
869 : // nonces can not be invalidated by strict-dynamic
870 0 : return mNonce.Equals(aNonce);
871 : }
872 :
873 : bool
874 0 : nsCSPNonceSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
875 : bool aParserCreated) const
876 : {
877 0 : CSPUTILSLOG(("nsCSPNonceSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
878 : CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
879 :
880 0 : if (aKeyword != CSP_NONCE) {
881 0 : return false;
882 : }
883 : // nonces can not be invalidated by strict-dynamic
884 0 : return mNonce.Equals(aHashOrNonce);
885 : }
886 :
887 : bool
888 0 : nsCSPNonceSrc::visit(nsCSPSrcVisitor* aVisitor) const
889 : {
890 0 : return aVisitor->visitNonceSrc(*this);
891 : }
892 :
893 : void
894 0 : nsCSPNonceSrc::toString(nsAString& outStr) const
895 : {
896 0 : outStr.AppendASCII(CSP_EnumToKeyword(CSP_NONCE));
897 0 : outStr.Append(mNonce);
898 0 : outStr.AppendASCII("'");
899 0 : }
900 :
901 : /* ===== nsCSPHashSrc ===================== */
902 :
903 0 : nsCSPHashSrc::nsCSPHashSrc(const nsAString& aAlgo, const nsAString& aHash)
904 : : mAlgorithm(aAlgo)
905 0 : , mHash(aHash)
906 : {
907 : // Only the algo should be rewritten to lowercase, the hash must remain the same.
908 0 : ToLowerCase(mAlgorithm);
909 0 : }
910 :
911 0 : nsCSPHashSrc::~nsCSPHashSrc()
912 : {
913 0 : }
914 :
915 : bool
916 0 : nsCSPHashSrc::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
917 : bool aParserCreated) const
918 : {
919 0 : CSPUTILSLOG(("nsCSPHashSrc::allows, aKeyWord: %s, a HashOrNonce: %s",
920 : CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
921 :
922 0 : if (aKeyword != CSP_HASH) {
923 0 : return false;
924 : }
925 :
926 : // hashes can not be invalidated by strict-dynamic
927 :
928 : // Convert aHashOrNonce to UTF-8
929 0 : NS_ConvertUTF16toUTF8 utf8_hash(aHashOrNonce);
930 :
931 : nsresult rv;
932 0 : nsCOMPtr<nsICryptoHash> hasher;
933 0 : hasher = do_CreateInstance("@mozilla.org/security/hash;1", &rv);
934 0 : NS_ENSURE_SUCCESS(rv, false);
935 :
936 0 : rv = hasher->InitWithString(NS_ConvertUTF16toUTF8(mAlgorithm));
937 0 : NS_ENSURE_SUCCESS(rv, false);
938 :
939 0 : rv = hasher->Update((uint8_t *)utf8_hash.get(), utf8_hash.Length());
940 0 : NS_ENSURE_SUCCESS(rv, false);
941 :
942 0 : nsAutoCString hash;
943 0 : rv = hasher->Finish(true, hash);
944 0 : NS_ENSURE_SUCCESS(rv, false);
945 :
946 0 : return NS_ConvertUTF16toUTF8(mHash).Equals(hash);
947 : }
948 :
949 : bool
950 0 : nsCSPHashSrc::visit(nsCSPSrcVisitor* aVisitor) const
951 : {
952 0 : return aVisitor->visitHashSrc(*this);
953 : }
954 :
955 : void
956 0 : nsCSPHashSrc::toString(nsAString& outStr) const
957 : {
958 0 : outStr.AppendASCII("'");
959 0 : outStr.Append(mAlgorithm);
960 0 : outStr.AppendASCII("-");
961 0 : outStr.Append(mHash);
962 0 : outStr.AppendASCII("'");
963 0 : }
964 :
965 : /* ===== nsCSPReportURI ===================== */
966 :
967 0 : nsCSPReportURI::nsCSPReportURI(nsIURI *aURI)
968 0 : :mReportURI(aURI)
969 : {
970 0 : }
971 :
972 0 : nsCSPReportURI::~nsCSPReportURI()
973 : {
974 0 : }
975 :
976 : bool
977 0 : nsCSPReportURI::visit(nsCSPSrcVisitor* aVisitor) const
978 : {
979 0 : return false;
980 : }
981 :
982 : void
983 0 : nsCSPReportURI::toString(nsAString& outStr) const
984 : {
985 0 : nsAutoCString spec;
986 0 : nsresult rv = mReportURI->GetSpec(spec);
987 0 : if (NS_FAILED(rv)) {
988 0 : return;
989 : }
990 0 : outStr.AppendASCII(spec.get());
991 : }
992 :
993 : /* ===== nsCSPSandboxFlags ===================== */
994 :
995 0 : nsCSPSandboxFlags::nsCSPSandboxFlags(const nsAString& aFlags)
996 0 : : mFlags(aFlags)
997 : {
998 0 : ToLowerCase(mFlags);
999 0 : }
1000 :
1001 0 : nsCSPSandboxFlags::~nsCSPSandboxFlags()
1002 : {
1003 0 : }
1004 :
1005 : bool
1006 0 : nsCSPSandboxFlags::visit(nsCSPSrcVisitor* aVisitor) const
1007 : {
1008 0 : return false;
1009 : }
1010 :
1011 : void
1012 0 : nsCSPSandboxFlags::toString(nsAString& outStr) const
1013 : {
1014 0 : outStr.Append(mFlags);
1015 0 : }
1016 :
1017 : /* ===== nsCSPDirective ====================== */
1018 :
1019 0 : nsCSPDirective::nsCSPDirective(CSPDirective aDirective)
1020 : {
1021 0 : mDirective = aDirective;
1022 0 : }
1023 :
1024 0 : nsCSPDirective::~nsCSPDirective()
1025 : {
1026 0 : for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1027 0 : delete mSrcs[i];
1028 : }
1029 0 : }
1030 :
1031 : bool
1032 0 : nsCSPDirective::permits(nsIURI* aUri, const nsAString& aNonce, bool aWasRedirected,
1033 : bool aReportOnly, bool aUpgradeInsecure, bool aParserCreated) const
1034 : {
1035 0 : if (CSPUTILSLOGENABLED()) {
1036 0 : CSPUTILSLOG(("nsCSPDirective::permits, aUri: %s",
1037 : aUri->GetSpecOrDefault().get()));
1038 : }
1039 :
1040 0 : for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1041 0 : if (mSrcs[i]->permits(aUri, aNonce, aWasRedirected, aReportOnly, aUpgradeInsecure, aParserCreated)) {
1042 0 : return true;
1043 : }
1044 : }
1045 0 : return false;
1046 : }
1047 :
1048 : bool
1049 0 : nsCSPDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
1050 : bool aParserCreated) const
1051 : {
1052 0 : CSPUTILSLOG(("nsCSPDirective::allows, aKeyWord: %s, a HashOrNonce: %s",
1053 : CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
1054 :
1055 0 : for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1056 0 : if (mSrcs[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) {
1057 0 : return true;
1058 : }
1059 : }
1060 0 : return false;
1061 : }
1062 :
1063 : void
1064 0 : nsCSPDirective::toString(nsAString& outStr) const
1065 : {
1066 : // Append directive name
1067 0 : outStr.AppendASCII(CSP_CSPDirectiveToString(mDirective));
1068 0 : outStr.AppendASCII(" ");
1069 :
1070 : // Append srcs
1071 0 : uint32_t length = mSrcs.Length();
1072 0 : for (uint32_t i = 0; i < length; i++) {
1073 0 : mSrcs[i]->toString(outStr);
1074 0 : if (i != (length - 1)) {
1075 0 : outStr.AppendASCII(" ");
1076 : }
1077 : }
1078 0 : }
1079 :
1080 : void
1081 0 : nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const
1082 : {
1083 0 : mozilla::dom::Sequence<nsString> srcs;
1084 0 : nsString src;
1085 0 : for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1086 0 : src.Truncate();
1087 0 : mSrcs[i]->toString(src);
1088 0 : srcs.AppendElement(src, mozilla::fallible);
1089 : }
1090 :
1091 0 : switch(mDirective) {
1092 : case nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE:
1093 0 : outCSP.mDefault_src.Construct();
1094 0 : outCSP.mDefault_src.Value() = mozilla::Move(srcs);
1095 0 : return;
1096 :
1097 : case nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE:
1098 0 : outCSP.mScript_src.Construct();
1099 0 : outCSP.mScript_src.Value() = mozilla::Move(srcs);
1100 0 : return;
1101 :
1102 : case nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE:
1103 0 : outCSP.mObject_src.Construct();
1104 0 : outCSP.mObject_src.Value() = mozilla::Move(srcs);
1105 0 : return;
1106 :
1107 : case nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE:
1108 0 : outCSP.mStyle_src.Construct();
1109 0 : outCSP.mStyle_src.Value() = mozilla::Move(srcs);
1110 0 : return;
1111 :
1112 : case nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE:
1113 0 : outCSP.mImg_src.Construct();
1114 0 : outCSP.mImg_src.Value() = mozilla::Move(srcs);
1115 0 : return;
1116 :
1117 : case nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE:
1118 0 : outCSP.mMedia_src.Construct();
1119 0 : outCSP.mMedia_src.Value() = mozilla::Move(srcs);
1120 0 : return;
1121 :
1122 : case nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE:
1123 0 : outCSP.mFrame_src.Construct();
1124 0 : outCSP.mFrame_src.Value() = mozilla::Move(srcs);
1125 0 : return;
1126 :
1127 : case nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE:
1128 0 : outCSP.mFont_src.Construct();
1129 0 : outCSP.mFont_src.Value() = mozilla::Move(srcs);
1130 0 : return;
1131 :
1132 : case nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE:
1133 0 : outCSP.mConnect_src.Construct();
1134 0 : outCSP.mConnect_src.Value() = mozilla::Move(srcs);
1135 0 : return;
1136 :
1137 : case nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE:
1138 0 : outCSP.mReport_uri.Construct();
1139 0 : outCSP.mReport_uri.Value() = mozilla::Move(srcs);
1140 0 : return;
1141 :
1142 : case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE:
1143 0 : outCSP.mFrame_ancestors.Construct();
1144 0 : outCSP.mFrame_ancestors.Value() = mozilla::Move(srcs);
1145 0 : return;
1146 :
1147 : case nsIContentSecurityPolicy::WEB_MANIFEST_SRC_DIRECTIVE:
1148 0 : outCSP.mManifest_src.Construct();
1149 0 : outCSP.mManifest_src.Value() = mozilla::Move(srcs);
1150 0 : return;
1151 : // not supporting REFLECTED_XSS_DIRECTIVE
1152 :
1153 : case nsIContentSecurityPolicy::BASE_URI_DIRECTIVE:
1154 0 : outCSP.mBase_uri.Construct();
1155 0 : outCSP.mBase_uri.Value() = mozilla::Move(srcs);
1156 0 : return;
1157 :
1158 : case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE:
1159 0 : outCSP.mForm_action.Construct();
1160 0 : outCSP.mForm_action.Value() = mozilla::Move(srcs);
1161 0 : return;
1162 :
1163 : case nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT:
1164 0 : outCSP.mBlock_all_mixed_content.Construct();
1165 : // does not have any srcs
1166 0 : return;
1167 :
1168 : case nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE:
1169 0 : outCSP.mUpgrade_insecure_requests.Construct();
1170 : // does not have any srcs
1171 0 : return;
1172 :
1173 : case nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE:
1174 0 : outCSP.mChild_src.Construct();
1175 0 : outCSP.mChild_src.Value() = mozilla::Move(srcs);
1176 0 : return;
1177 :
1178 : case nsIContentSecurityPolicy::SANDBOX_DIRECTIVE:
1179 0 : outCSP.mSandbox.Construct();
1180 0 : outCSP.mSandbox.Value() = mozilla::Move(srcs);
1181 0 : return;
1182 :
1183 : // REFERRER_DIRECTIVE and REQUIRE_SRI_FOR are handled in nsCSPPolicy::toDomCSPStruct()
1184 :
1185 : default:
1186 0 : NS_ASSERTION(false, "cannot find directive to convert CSP to JSON");
1187 : }
1188 : }
1189 :
1190 :
1191 : bool
1192 0 : nsCSPDirective::restrictsContentType(nsContentPolicyType aContentType) const
1193 : {
1194 : // make sure we do not check for the default src before any other sources
1195 0 : if (isDefaultDirective()) {
1196 0 : return false;
1197 : }
1198 0 : return mDirective == CSP_ContentTypeToDirective(aContentType);
1199 : }
1200 :
1201 : void
1202 0 : nsCSPDirective::getReportURIs(nsTArray<nsString> &outReportURIs) const
1203 : {
1204 0 : NS_ASSERTION((mDirective == nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE), "not a report-uri directive");
1205 :
1206 : // append uris
1207 0 : nsString tmpReportURI;
1208 0 : for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1209 0 : tmpReportURI.Truncate();
1210 0 : mSrcs[i]->toString(tmpReportURI);
1211 0 : outReportURIs.AppendElement(tmpReportURI);
1212 : }
1213 0 : }
1214 :
1215 : bool
1216 0 : nsCSPDirective::visitSrcs(nsCSPSrcVisitor* aVisitor) const
1217 : {
1218 0 : for (uint32_t i = 0; i < mSrcs.Length(); i++) {
1219 0 : if (!mSrcs[i]->visit(aVisitor)) {
1220 0 : return false;
1221 : }
1222 : }
1223 0 : return true;
1224 : }
1225 :
1226 0 : bool nsCSPDirective::equals(CSPDirective aDirective) const
1227 : {
1228 0 : return (mDirective == aDirective);
1229 : }
1230 :
1231 : /* =============== nsCSPChildSrcDirective ============= */
1232 :
1233 0 : nsCSPChildSrcDirective::nsCSPChildSrcDirective(CSPDirective aDirective)
1234 : : nsCSPDirective(aDirective)
1235 0 : , mHandleFrameSrc(false)
1236 : {
1237 0 : }
1238 :
1239 0 : nsCSPChildSrcDirective::~nsCSPChildSrcDirective()
1240 : {
1241 0 : }
1242 :
1243 0 : void nsCSPChildSrcDirective::setHandleFrameSrc()
1244 : {
1245 0 : mHandleFrameSrc = true;
1246 0 : }
1247 :
1248 0 : bool nsCSPChildSrcDirective::restrictsContentType(nsContentPolicyType aContentType) const
1249 : {
1250 0 : if (aContentType == nsIContentPolicy::TYPE_SUBDOCUMENT) {
1251 0 : return mHandleFrameSrc;
1252 : }
1253 :
1254 : return (aContentType == nsIContentPolicy::TYPE_INTERNAL_WORKER
1255 0 : || aContentType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER
1256 0 : || aContentType == nsIContentPolicy::TYPE_INTERNAL_SERVICE_WORKER
1257 0 : );
1258 : }
1259 :
1260 0 : bool nsCSPChildSrcDirective::equals(CSPDirective aDirective) const
1261 : {
1262 0 : if (aDirective == nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE) {
1263 0 : return mHandleFrameSrc;
1264 : }
1265 :
1266 0 : return (aDirective == nsIContentSecurityPolicy::CHILD_SRC_DIRECTIVE);
1267 : }
1268 :
1269 : /* =============== nsBlockAllMixedContentDirective ============= */
1270 :
1271 0 : nsBlockAllMixedContentDirective::nsBlockAllMixedContentDirective(CSPDirective aDirective)
1272 0 : : nsCSPDirective(aDirective)
1273 : {
1274 0 : }
1275 :
1276 0 : nsBlockAllMixedContentDirective::~nsBlockAllMixedContentDirective()
1277 : {
1278 0 : }
1279 :
1280 : void
1281 0 : nsBlockAllMixedContentDirective::toString(nsAString& outStr) const
1282 : {
1283 0 : outStr.AppendASCII(CSP_CSPDirectiveToString(
1284 0 : nsIContentSecurityPolicy::BLOCK_ALL_MIXED_CONTENT));
1285 0 : }
1286 :
1287 : /* =============== nsUpgradeInsecureDirective ============= */
1288 :
1289 0 : nsUpgradeInsecureDirective::nsUpgradeInsecureDirective(CSPDirective aDirective)
1290 0 : : nsCSPDirective(aDirective)
1291 : {
1292 0 : }
1293 :
1294 0 : nsUpgradeInsecureDirective::~nsUpgradeInsecureDirective()
1295 : {
1296 0 : }
1297 :
1298 : void
1299 0 : nsUpgradeInsecureDirective::toString(nsAString& outStr) const
1300 : {
1301 0 : outStr.AppendASCII(CSP_CSPDirectiveToString(
1302 0 : nsIContentSecurityPolicy::UPGRADE_IF_INSECURE_DIRECTIVE));
1303 0 : }
1304 :
1305 : /* ===== nsRequireSRIForDirective ========================= */
1306 :
1307 0 : nsRequireSRIForDirective::nsRequireSRIForDirective(CSPDirective aDirective)
1308 0 : : nsCSPDirective(aDirective)
1309 : {
1310 0 : }
1311 :
1312 0 : nsRequireSRIForDirective::~nsRequireSRIForDirective()
1313 : {
1314 0 : }
1315 :
1316 : void
1317 0 : nsRequireSRIForDirective::toString(nsAString &outStr) const
1318 : {
1319 0 : outStr.AppendASCII(CSP_CSPDirectiveToString(
1320 0 : nsIContentSecurityPolicy::REQUIRE_SRI_FOR));
1321 0 : for (uint32_t i = 0; i < mTypes.Length(); i++) {
1322 0 : if (mTypes[i] == nsIContentPolicy::TYPE_SCRIPT) {
1323 0 : outStr.AppendASCII(" script");
1324 : }
1325 0 : else if (mTypes[i] == nsIContentPolicy::TYPE_STYLESHEET) {
1326 0 : outStr.AppendASCII(" style");
1327 : }
1328 : }
1329 0 : }
1330 :
1331 : bool
1332 0 : nsRequireSRIForDirective::hasType(nsContentPolicyType aType) const
1333 : {
1334 0 : for (uint32_t i = 0; i < mTypes.Length(); i++) {
1335 0 : if (mTypes[i] == aType) {
1336 0 : return true;
1337 : }
1338 : }
1339 0 : return false;
1340 : }
1341 :
1342 : bool
1343 0 : nsRequireSRIForDirective::restrictsContentType(const nsContentPolicyType aType) const
1344 : {
1345 0 : return this->hasType(aType);
1346 : }
1347 :
1348 : bool
1349 0 : nsRequireSRIForDirective::allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce,
1350 : bool aParserCreated) const
1351 : {
1352 : // can only disallow CSP_REQUIRE_SRI_FOR.
1353 0 : return (aKeyword != CSP_REQUIRE_SRI_FOR);
1354 : }
1355 :
1356 : /* ===== nsCSPPolicy ========================= */
1357 :
1358 0 : nsCSPPolicy::nsCSPPolicy()
1359 : : mUpgradeInsecDir(nullptr)
1360 0 : , mReportOnly(false)
1361 : {
1362 0 : CSPUTILSLOG(("nsCSPPolicy::nsCSPPolicy"));
1363 0 : }
1364 :
1365 0 : nsCSPPolicy::~nsCSPPolicy()
1366 : {
1367 0 : CSPUTILSLOG(("nsCSPPolicy::~nsCSPPolicy"));
1368 :
1369 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1370 0 : delete mDirectives[i];
1371 : }
1372 0 : }
1373 :
1374 : bool
1375 0 : nsCSPPolicy::permits(CSPDirective aDir,
1376 : nsIURI* aUri,
1377 : bool aSpecific) const
1378 : {
1379 0 : nsString outp;
1380 0 : return this->permits(aDir, aUri, EmptyString(), false, aSpecific, false, outp);
1381 : }
1382 :
1383 : bool
1384 0 : nsCSPPolicy::permits(CSPDirective aDir,
1385 : nsIURI* aUri,
1386 : const nsAString& aNonce,
1387 : bool aWasRedirected,
1388 : bool aSpecific,
1389 : bool aParserCreated,
1390 : nsAString& outViolatedDirective) const
1391 : {
1392 0 : if (CSPUTILSLOGENABLED()) {
1393 0 : CSPUTILSLOG(("nsCSPPolicy::permits, aUri: %s, aDir: %d, aSpecific: %s",
1394 : aUri->GetSpecOrDefault().get(), aDir,
1395 : aSpecific ? "true" : "false"));
1396 : }
1397 :
1398 0 : NS_ASSERTION(aUri, "permits needs an uri to perform the check!");
1399 0 : outViolatedDirective.Truncate();
1400 :
1401 0 : nsCSPDirective* defaultDir = nullptr;
1402 :
1403 : // Try to find a relevant directive
1404 : // These directive arrays are short (1-5 elements), not worth using a hashtable.
1405 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1406 0 : if (mDirectives[i]->equals(aDir)) {
1407 0 : if (!mDirectives[i]->permits(aUri, aNonce, aWasRedirected, mReportOnly,
1408 0 : mUpgradeInsecDir, aParserCreated)) {
1409 0 : mDirectives[i]->toString(outViolatedDirective);
1410 0 : return false;
1411 : }
1412 0 : return true;
1413 : }
1414 0 : if (mDirectives[i]->isDefaultDirective()) {
1415 0 : defaultDir = mDirectives[i];
1416 : }
1417 : }
1418 :
1419 : // If the above loop runs through, we haven't found a matching directive.
1420 : // Avoid relooping, just store the result of default-src while looping.
1421 0 : if (!aSpecific && defaultDir) {
1422 0 : if (!defaultDir->permits(aUri, aNonce, aWasRedirected, mReportOnly,
1423 0 : mUpgradeInsecDir, aParserCreated)) {
1424 0 : defaultDir->toString(outViolatedDirective);
1425 0 : return false;
1426 : }
1427 0 : return true;
1428 : }
1429 :
1430 : // Nothing restricts this, so we're allowing the load
1431 : // See bug 764937
1432 0 : return true;
1433 : }
1434 :
1435 : bool
1436 0 : nsCSPPolicy::allows(nsContentPolicyType aContentType,
1437 : enum CSPKeyword aKeyword,
1438 : const nsAString& aHashOrNonce,
1439 : bool aParserCreated) const
1440 : {
1441 0 : CSPUTILSLOG(("nsCSPPolicy::allows, aKeyWord: %s, a HashOrNonce: %s",
1442 : CSP_EnumToKeyword(aKeyword), NS_ConvertUTF16toUTF8(aHashOrNonce).get()));
1443 :
1444 0 : nsCSPDirective* defaultDir = nullptr;
1445 :
1446 : // Try to find a matching directive
1447 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1448 0 : if (mDirectives[i]->restrictsContentType(aContentType)) {
1449 0 : if (mDirectives[i]->allows(aKeyword, aHashOrNonce, aParserCreated)) {
1450 0 : return true;
1451 : }
1452 0 : return false;
1453 : }
1454 0 : if (mDirectives[i]->isDefaultDirective()) {
1455 0 : defaultDir = mDirectives[i];
1456 : }
1457 : }
1458 :
1459 : // {nonce,hash}-source should not consult default-src:
1460 : // * return false if default-src is specified
1461 : // * but allow the load if default-src is *not* specified (Bug 1198422)
1462 0 : if (aKeyword == CSP_NONCE || aKeyword == CSP_HASH) {
1463 0 : if (!defaultDir) {
1464 0 : return true;
1465 : }
1466 0 : return false;
1467 : }
1468 :
1469 : // If the above loop runs through, we haven't found a matching directive.
1470 : // Avoid relooping, just store the result of default-src while looping.
1471 0 : if (defaultDir) {
1472 0 : return defaultDir->allows(aKeyword, aHashOrNonce, aParserCreated);
1473 : }
1474 :
1475 : // Allowing the load; see Bug 885433
1476 : // a) inline scripts (also unsafe eval) should only be blocked
1477 : // if there is a [script-src] or [default-src]
1478 : // b) inline styles should only be blocked
1479 : // if there is a [style-src] or [default-src]
1480 0 : return true;
1481 : }
1482 :
1483 : bool
1484 0 : nsCSPPolicy::allows(nsContentPolicyType aContentType,
1485 : enum CSPKeyword aKeyword) const
1486 : {
1487 0 : return allows(aContentType, aKeyword, NS_LITERAL_STRING(""), false);
1488 : }
1489 :
1490 : void
1491 0 : nsCSPPolicy::toString(nsAString& outStr) const
1492 : {
1493 0 : uint32_t length = mDirectives.Length();
1494 0 : for (uint32_t i = 0; i < length; ++i) {
1495 :
1496 0 : if (mDirectives[i]->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
1497 0 : outStr.AppendASCII(CSP_CSPDirectiveToString(nsIContentSecurityPolicy::REFERRER_DIRECTIVE));
1498 0 : outStr.AppendASCII(" ");
1499 0 : outStr.Append(mReferrerPolicy);
1500 : } else {
1501 0 : mDirectives[i]->toString(outStr);
1502 : }
1503 0 : if (i != (length - 1)) {
1504 0 : outStr.AppendASCII("; ");
1505 : }
1506 : }
1507 0 : }
1508 :
1509 : void
1510 0 : nsCSPPolicy::toDomCSPStruct(mozilla::dom::CSP& outCSP) const
1511 : {
1512 0 : outCSP.mReport_only = mReportOnly;
1513 :
1514 0 : for (uint32_t i = 0; i < mDirectives.Length(); ++i) {
1515 0 : if (mDirectives[i]->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
1516 0 : mozilla::dom::Sequence<nsString> srcs;
1517 0 : srcs.AppendElement(mReferrerPolicy, mozilla::fallible);
1518 0 : outCSP.mReferrer.Construct();
1519 0 : outCSP.mReferrer.Value() = srcs;
1520 : } else {
1521 0 : mDirectives[i]->toDomCSPStruct(outCSP);
1522 : }
1523 : }
1524 0 : }
1525 :
1526 : bool
1527 0 : nsCSPPolicy::hasDirective(CSPDirective aDir) const
1528 : {
1529 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1530 0 : if (mDirectives[i]->equals(aDir)) {
1531 0 : return true;
1532 : }
1533 : }
1534 0 : return false;
1535 : }
1536 :
1537 : /*
1538 : * Use this function only after ::allows() returned 'false'. Most and
1539 : * foremost it's used to get the violated directive before sending reports.
1540 : * The parameter outDirective is the equivalent of 'outViolatedDirective'
1541 : * for the ::permits() function family.
1542 : */
1543 : void
1544 0 : nsCSPPolicy::getDirectiveStringForContentType(nsContentPolicyType aContentType,
1545 : nsAString& outDirective) const
1546 : {
1547 0 : nsCSPDirective* defaultDir = nullptr;
1548 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1549 0 : if (mDirectives[i]->restrictsContentType(aContentType)) {
1550 0 : mDirectives[i]->toString(outDirective);
1551 0 : return;
1552 : }
1553 0 : if (mDirectives[i]->isDefaultDirective()) {
1554 0 : defaultDir = mDirectives[i];
1555 : }
1556 : }
1557 : // if we haven't found a matching directive yet,
1558 : // the contentType must be restricted by the default directive
1559 0 : if (defaultDir) {
1560 0 : defaultDir->toString(outDirective);
1561 0 : return;
1562 : }
1563 0 : NS_ASSERTION(false, "Can not query directive string for contentType!");
1564 0 : outDirective.AppendASCII("couldNotQueryViolatedDirective");
1565 : }
1566 :
1567 : void
1568 0 : nsCSPPolicy::getDirectiveAsString(CSPDirective aDir, nsAString& outDirective) const
1569 : {
1570 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1571 0 : if (mDirectives[i]->equals(aDir)) {
1572 0 : mDirectives[i]->toString(outDirective);
1573 0 : return;
1574 : }
1575 : }
1576 : }
1577 :
1578 : /*
1579 : * Helper function that returns the underlying bit representation of sandbox
1580 : * flags. The function returns SANDBOXED_NONE if there are no sandbox
1581 : * directives.
1582 : */
1583 : uint32_t
1584 0 : nsCSPPolicy::getSandboxFlags() const
1585 : {
1586 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1587 0 : if (mDirectives[i]->equals(nsIContentSecurityPolicy::SANDBOX_DIRECTIVE)) {
1588 0 : nsAutoString flags;
1589 0 : mDirectives[i]->toString(flags);
1590 :
1591 0 : if (flags.IsEmpty()) {
1592 0 : return SANDBOX_ALL_FLAGS;
1593 : }
1594 :
1595 0 : nsAttrValue attr;
1596 0 : attr.ParseAtomArray(flags);
1597 :
1598 0 : return nsContentUtils::ParseSandboxAttributeToFlags(&attr);
1599 : }
1600 : }
1601 :
1602 0 : return SANDBOXED_NONE;
1603 : }
1604 :
1605 : void
1606 0 : nsCSPPolicy::getReportURIs(nsTArray<nsString>& outReportURIs) const
1607 : {
1608 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1609 0 : if (mDirectives[i]->equals(nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE)) {
1610 0 : mDirectives[i]->getReportURIs(outReportURIs);
1611 0 : return;
1612 : }
1613 : }
1614 : }
1615 :
1616 : bool
1617 0 : nsCSPPolicy::visitDirectiveSrcs(CSPDirective aDir, nsCSPSrcVisitor* aVisitor) const
1618 : {
1619 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1620 0 : if (mDirectives[i]->equals(aDir)) {
1621 0 : return mDirectives[i]->visitSrcs(aVisitor);
1622 : }
1623 : }
1624 0 : return false;
1625 : }
1626 :
1627 : bool
1628 0 : nsCSPPolicy::requireSRIForType(nsContentPolicyType aContentType)
1629 : {
1630 0 : for (uint32_t i = 0; i < mDirectives.Length(); i++) {
1631 0 : if (mDirectives[i]->equals(nsIContentSecurityPolicy::REQUIRE_SRI_FOR)) {
1632 0 : return static_cast<nsRequireSRIForDirective*>(mDirectives[i])->hasType(aContentType);
1633 : }
1634 : }
1635 0 : return false;
1636 : }
|