/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 | } |