Line data Source code
1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* vim: set ts=2 sw=2 et 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 "ContentPrincipal.h"
8 :
9 : #include "mozIThirdPartyUtil.h"
10 : #include "nscore.h"
11 : #include "nsScriptSecurityManager.h"
12 : #include "nsString.h"
13 : #include "nsReadableUtils.h"
14 : #include "pratom.h"
15 : #include "nsIURI.h"
16 : #include "nsIURL.h"
17 : #include "nsIStandardURL.h"
18 : #include "nsIURIWithPrincipal.h"
19 : #include "nsJSPrincipals.h"
20 : #include "nsIEffectiveTLDService.h"
21 : #include "nsIClassInfoImpl.h"
22 : #include "nsIObjectInputStream.h"
23 : #include "nsIObjectOutputStream.h"
24 : #include "nsIProtocolHandler.h"
25 : #include "nsError.h"
26 : #include "nsIContentSecurityPolicy.h"
27 : #include "nsNetCID.h"
28 : #include "jswrapper.h"
29 :
30 : #include "mozilla/dom/nsCSPContext.h"
31 : #include "mozilla/dom/ScriptSettings.h"
32 : #include "mozilla/ClearOnShutdown.h"
33 : #include "mozilla/Preferences.h"
34 : #include "mozilla/HashFunctions.h"
35 :
36 : using namespace mozilla;
37 :
38 : static bool gCodeBasePrincipalSupport = false;
39 :
40 200 : static bool URIIsImmutable(nsIURI* aURI)
41 : {
42 400 : nsCOMPtr<nsIMutable> mutableObj(do_QueryInterface(aURI));
43 : bool isMutable;
44 : return
45 199 : mutableObj &&
46 399 : NS_SUCCEEDED(mutableObj->GetMutable(&isMutable)) &&
47 599 : !isMutable;
48 : }
49 :
50 : static nsIAddonPolicyService*
51 0 : GetAddonPolicyService(nsresult* aRv)
52 : {
53 0 : static nsCOMPtr<nsIAddonPolicyService> addonPolicyService;
54 :
55 0 : *aRv = NS_OK;
56 0 : if (!addonPolicyService) {
57 0 : addonPolicyService = do_GetService("@mozilla.org/addons/policy-service;1", aRv);
58 0 : ClearOnShutdown(&addonPolicyService);
59 : }
60 0 : return addonPolicyService;
61 : }
62 :
63 3 : NS_IMPL_CLASSINFO(ContentPrincipal, nullptr, nsIClassInfo::MAIN_THREAD_ONLY,
64 : NS_PRINCIPAL_CID)
65 2986 : NS_IMPL_QUERY_INTERFACE_CI(ContentPrincipal,
66 : nsIPrincipal,
67 : nsISerializable)
68 2 : NS_IMPL_CI_INTERFACE_GETTER(ContentPrincipal,
69 : nsIPrincipal,
70 : nsISerializable)
71 :
72 : // Called at startup:
73 : /* static */ void
74 3 : ContentPrincipal::InitializeStatics()
75 : {
76 : Preferences::AddBoolVarCache(&gCodeBasePrincipalSupport,
77 : "signed.applets.codebase_principal_support",
78 3 : false);
79 3 : }
80 :
81 199 : ContentPrincipal::ContentPrincipal()
82 : : BasePrincipal(eCodebasePrincipal)
83 : , mCodebaseImmutable(false)
84 199 : , mDomainImmutable(false)
85 : {
86 199 : }
87 :
88 339 : ContentPrincipal::~ContentPrincipal()
89 : {
90 : // let's clear the principal within the csp to avoid a tangling pointer
91 113 : if (mCSP) {
92 0 : static_cast<nsCSPContext*>(mCSP.get())->clearLoadingPrincipal();
93 : }
94 339 : }
95 :
96 : nsresult
97 199 : ContentPrincipal::Init(nsIURI *aCodebase,
98 : const OriginAttributes& aOriginAttributes,
99 : const nsACString& aOriginNoSuffix)
100 : {
101 199 : NS_ENSURE_ARG(aCodebase);
102 :
103 : // Assert that the URI we get here isn't any of the schemes that we know we
104 : // should not get here. These schemes always either inherit their principal
105 : // or fall back to a null principal. These are schemes which return
106 : // URI_INHERITS_SECURITY_CONTEXT from their protocol handler's
107 : // GetProtocolFlags function.
108 : bool hasFlag;
109 : Unused << hasFlag; // silence possible compiler warnings.
110 199 : MOZ_DIAGNOSTIC_ASSERT(
111 : NS_SUCCEEDED(NS_URIChainHasFlags(aCodebase,
112 : nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
113 : &hasFlag)) &&
114 : !hasFlag);
115 :
116 199 : mCodebase = NS_TryToMakeImmutable(aCodebase);
117 199 : mCodebaseImmutable = URIIsImmutable(mCodebase);
118 :
119 199 : FinishInit(aOriginNoSuffix, aOriginAttributes);
120 :
121 199 : return NS_OK;
122 : }
123 :
124 : nsresult
125 0 : ContentPrincipal::GetScriptLocation(nsACString &aStr)
126 : {
127 0 : return mCodebase->GetSpec(aStr);
128 : }
129 :
130 : /* static */ nsresult
131 206 : ContentPrincipal::GenerateOriginNoSuffixFromURI(nsIURI* aURI,
132 : nsACString& aOriginNoSuffix)
133 : {
134 206 : if (!aURI) {
135 0 : return NS_ERROR_FAILURE;
136 : }
137 :
138 412 : nsCOMPtr<nsIURI> origin = NS_GetInnermostURI(aURI);
139 206 : if (!origin) {
140 0 : return NS_ERROR_FAILURE;
141 : }
142 :
143 206 : MOZ_ASSERT(!NS_IsAboutBlank(origin),
144 : "The inner URI for about:blank must be moz-safe-about:blank");
145 :
146 : // Handle non-strict file:// uris.
147 412 : if (!nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
148 206 : NS_URIIsLocalFile(origin)) {
149 : // If strict file origin policy is not in effect, all local files are
150 : // considered to be same-origin, so return a known dummy origin here.
151 0 : aOriginNoSuffix.AssignLiteral("file://UNIVERSAL_FILE_URI_ORIGIN");
152 0 : return NS_OK;
153 : }
154 :
155 :
156 : nsresult rv;
157 : // NB: This is only compiled for Thunderbird/Suite.
158 : #if IS_ORIGIN_IS_FULL_SPEC_DEFINED
159 : bool fullSpec = false;
160 : rv = NS_URIChainHasFlags(origin, nsIProtocolHandler::ORIGIN_IS_FULL_SPEC, &fullSpec);
161 : NS_ENSURE_SUCCESS(rv, rv);
162 : if (fullSpec) {
163 : return origin->GetAsciiSpec(aOriginNoSuffix);
164 : }
165 : #endif
166 :
167 : // We want the invariant that prinA.origin == prinB.origin i.f.f.
168 : // prinA.equals(prinB). However, this requires that we impose certain constraints
169 : // on the behavior and origin semantics of principals, and in particular, forbid
170 : // creating origin strings for principals whose equality constraints are not
171 : // expressible as strings (i.e. object equality). Moreover, we want to forbid URIs
172 : // containing the magic "^" we use as a separating character for origin
173 : // attributes.
174 : //
175 : // These constraints can generally be achieved by restricting .origin to
176 : // nsIStandardURL-based URIs, but there are a few other URI schemes that we need
177 : // to handle.
178 : bool isBehaved;
179 1215 : if ((NS_SUCCEEDED(origin->SchemeIs("about", &isBehaved)) && isBehaved) ||
180 426 : (NS_SUCCEEDED(origin->SchemeIs("moz-safe-about", &isBehaved)) && isBehaved &&
181 : // We generally consider two about:foo origins to be same-origin, but
182 : // about:blank is special since it can be generated from different sources.
183 : // We check for moz-safe-about:blank since origin is an innermost URI.
184 866 : !origin->GetSpecOrDefault().EqualsLiteral("moz-safe-about:blank")) ||
185 384 : (NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) {
186 14 : rv = origin->GetAsciiSpec(aOriginNoSuffix);
187 14 : NS_ENSURE_SUCCESS(rv, rv);
188 : // These URIs could technically contain a '^', but they never should.
189 14 : if (NS_WARN_IF(aOriginNoSuffix.FindChar('^', 0) != -1)) {
190 0 : aOriginNoSuffix.Truncate();
191 0 : return NS_ERROR_FAILURE;
192 : }
193 14 : return NS_OK;
194 : }
195 :
196 : // This URL can be a blobURL. In this case, we should use the 'parent'
197 : // principal instead.
198 384 : nsCOMPtr<nsIURIWithPrincipal> uriWithPrincipal = do_QueryInterface(origin);
199 192 : if (uriWithPrincipal) {
200 0 : nsCOMPtr<nsIPrincipal> uriPrincipal;
201 0 : rv = uriWithPrincipal->GetPrincipal(getter_AddRefs(uriPrincipal));
202 0 : NS_ENSURE_SUCCESS(rv, rv);
203 :
204 0 : if (uriPrincipal) {
205 0 : return uriPrincipal->GetOriginNoSuffix(aOriginNoSuffix);
206 : }
207 : }
208 :
209 : // If we reached this branch, we can only create an origin if we have a
210 : // nsIStandardURL. So, we query to a nsIStandardURL, and fail if we aren't
211 : // an instance of an nsIStandardURL nsIStandardURLs have the good property
212 : // of escaping the '^' character in their specs, which means that we can be
213 : // sure that the caret character (which is reserved for delimiting the end
214 : // of the spec, and the beginning of the origin attributes) is not present
215 : // in the origin string
216 384 : nsCOMPtr<nsIStandardURL> standardURL = do_QueryInterface(origin);
217 192 : if (!standardURL) {
218 7 : return NS_ERROR_FAILURE;
219 : }
220 :
221 : // See whether we have a useful hostPort. If we do, use that.
222 370 : nsAutoCString hostPort;
223 185 : bool isChrome = false;
224 185 : rv = origin->SchemeIs("chrome", &isChrome);
225 185 : NS_ENSURE_SUCCESS(rv, rv);
226 185 : if (!isChrome) {
227 105 : rv = origin->GetAsciiHostPort(hostPort);
228 105 : NS_ENSURE_SUCCESS(rv, rv);
229 : }
230 185 : if (!hostPort.IsEmpty()) {
231 105 : rv = origin->GetScheme(aOriginNoSuffix);
232 105 : NS_ENSURE_SUCCESS(rv, rv);
233 105 : aOriginNoSuffix.AppendLiteral("://");
234 105 : aOriginNoSuffix.Append(hostPort);
235 105 : return NS_OK;
236 : }
237 :
238 80 : rv = aURI->GetAsciiSpec(aOriginNoSuffix);
239 80 : NS_ENSURE_SUCCESS(rv, rv);
240 :
241 : // The origin, when taken from the spec, should not contain the ref part of
242 : // the URL.
243 :
244 80 : int32_t pos = aOriginNoSuffix.FindChar('?');
245 80 : int32_t hashPos = aOriginNoSuffix.FindChar('#');
246 :
247 80 : if (hashPos != kNotFound && (pos == kNotFound || hashPos < pos)) {
248 4 : pos = hashPos;
249 : }
250 :
251 80 : if (pos != kNotFound) {
252 4 : aOriginNoSuffix.Truncate(pos);
253 : }
254 :
255 80 : return NS_OK;
256 : }
257 :
258 : bool
259 84 : ContentPrincipal::SubsumesInternal(nsIPrincipal* aOther,
260 : BasePrincipal::DocumentDomainConsideration aConsideration)
261 : {
262 84 : MOZ_ASSERT(aOther);
263 :
264 : // For ContentPrincipal, Subsumes is equivalent to Equals.
265 84 : if (aOther == this) {
266 0 : return true;
267 : }
268 :
269 : // If either the subject or the object has changed its principal by
270 : // explicitly setting document.domain then the other must also have
271 : // done so in order to be considered the same origin. This prevents
272 : // DNS spoofing based on document.domain (154930)
273 : nsresult rv;
274 84 : if (aConsideration == ConsiderDocumentDomain) {
275 : // Get .domain on each principal.
276 0 : nsCOMPtr<nsIURI> thisDomain, otherDomain;
277 0 : GetDomain(getter_AddRefs(thisDomain));
278 0 : aOther->GetDomain(getter_AddRefs(otherDomain));
279 :
280 : // If either has .domain set, we have equality i.f.f. the domains match.
281 : // Otherwise, we fall through to the non-document-domain-considering case.
282 0 : if (thisDomain || otherDomain) {
283 0 : return nsScriptSecurityManager::SecurityCompareURIs(thisDomain, otherDomain);
284 : }
285 : }
286 :
287 168 : nsCOMPtr<nsIURI> otherURI;
288 84 : rv = aOther->GetURI(getter_AddRefs(otherURI));
289 84 : NS_ENSURE_SUCCESS(rv, false);
290 :
291 : // Compare codebases.
292 84 : return nsScriptSecurityManager::SecurityCompareURIs(mCodebase, otherURI);
293 : }
294 :
295 : NS_IMETHODIMP
296 155 : ContentPrincipal::GetURI(nsIURI** aURI)
297 : {
298 155 : if (mCodebaseImmutable) {
299 155 : NS_ADDREF(*aURI = mCodebase);
300 155 : return NS_OK;
301 : }
302 :
303 0 : if (!mCodebase) {
304 0 : *aURI = nullptr;
305 0 : return NS_OK;
306 : }
307 :
308 0 : return NS_EnsureSafeToReturn(mCodebase, aURI);
309 : }
310 :
311 : bool
312 5 : ContentPrincipal::MayLoadInternal(nsIURI* aURI)
313 : {
314 : // See if aURI is something like a Blob URI that is actually associated with
315 : // a principal.
316 10 : nsCOMPtr<nsIURIWithPrincipal> uriWithPrin = do_QueryInterface(aURI);
317 10 : nsCOMPtr<nsIPrincipal> uriPrin;
318 5 : if (uriWithPrin) {
319 0 : uriWithPrin->GetPrincipal(getter_AddRefs(uriPrin));
320 : }
321 5 : if (uriPrin) {
322 0 : return nsIPrincipal::Subsumes(uriPrin);
323 : }
324 :
325 : // If this principal is associated with an addon, check whether that addon
326 : // has been given permission to load from this domain.
327 5 : if (AddonAllowsLoad(aURI)) {
328 0 : return true;
329 : }
330 :
331 5 : if (nsScriptSecurityManager::SecurityCompareURIs(mCodebase, aURI)) {
332 3 : return true;
333 : }
334 :
335 : // If strict file origin policy is in effect, local files will always fail
336 : // SecurityCompareURIs unless they are identical. Explicitly check file origin
337 : // policy, in that case.
338 4 : if (nsScriptSecurityManager::GetStrictFileOriginPolicy() &&
339 2 : NS_URIIsLocalFile(aURI) &&
340 0 : NS_RelaxStrictFileOriginPolicy(aURI, mCodebase)) {
341 0 : return true;
342 : }
343 :
344 2 : return false;
345 : }
346 :
347 : NS_IMETHODIMP
348 0 : ContentPrincipal::GetHashValue(uint32_t* aValue)
349 : {
350 0 : NS_PRECONDITION(mCodebase, "Need a codebase");
351 :
352 0 : *aValue = nsScriptSecurityManager::HashPrincipalByOrigin(this);
353 0 : return NS_OK;
354 : }
355 :
356 : NS_IMETHODIMP
357 0 : ContentPrincipal::GetDomain(nsIURI** aDomain)
358 : {
359 0 : if (!mDomain) {
360 0 : *aDomain = nullptr;
361 0 : return NS_OK;
362 : }
363 :
364 0 : if (mDomainImmutable) {
365 0 : NS_ADDREF(*aDomain = mDomain);
366 0 : return NS_OK;
367 : }
368 :
369 0 : return NS_EnsureSafeToReturn(mDomain, aDomain);
370 : }
371 :
372 : NS_IMETHODIMP
373 1 : ContentPrincipal::SetDomain(nsIURI* aDomain)
374 : {
375 1 : mDomain = NS_TryToMakeImmutable(aDomain);
376 1 : mDomainImmutable = URIIsImmutable(mDomain);
377 1 : SetHasExplicitDomain();
378 :
379 : // Recompute all wrappers between compartments using this principal and other
380 : // non-chrome compartments.
381 2 : AutoSafeJSContext cx;
382 1 : JSPrincipals *principals = nsJSPrincipals::get(static_cast<nsIPrincipal*>(this));
383 2 : bool success = js::RecomputeWrappers(cx, js::ContentCompartmentsOnly(),
384 3 : js::CompartmentsWithPrincipals(principals));
385 1 : NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
386 2 : success = js::RecomputeWrappers(cx, js::CompartmentsWithPrincipals(principals),
387 3 : js::ContentCompartmentsOnly());
388 1 : NS_ENSURE_TRUE(success, NS_ERROR_FAILURE);
389 :
390 1 : return NS_OK;
391 : }
392 :
393 : NS_IMETHODIMP
394 23 : ContentPrincipal::GetBaseDomain(nsACString& aBaseDomain)
395 : {
396 : // For a file URI, we return the file path.
397 23 : if (NS_URIIsLocalFile(mCodebase)) {
398 0 : nsCOMPtr<nsIURL> url = do_QueryInterface(mCodebase);
399 :
400 0 : if (url) {
401 0 : return url->GetFilePath(aBaseDomain);
402 : }
403 : }
404 :
405 : bool hasNoRelativeFlag;
406 23 : nsresult rv = NS_URIChainHasFlags(mCodebase,
407 : nsIProtocolHandler::URI_NORELATIVE,
408 23 : &hasNoRelativeFlag);
409 23 : if (NS_WARN_IF(NS_FAILED(rv))) {
410 0 : return rv;
411 : }
412 :
413 23 : if (hasNoRelativeFlag) {
414 0 : return mCodebase->GetSpec(aBaseDomain);
415 : }
416 :
417 : // For everything else, we ask the TLD service via
418 : // the ThirdPartyUtil.
419 : nsCOMPtr<mozIThirdPartyUtil> thirdPartyUtil =
420 46 : do_GetService(THIRDPARTYUTIL_CONTRACTID);
421 23 : if (thirdPartyUtil) {
422 23 : return thirdPartyUtil->GetBaseDomain(mCodebase, aBaseDomain);
423 : }
424 :
425 0 : return NS_OK;
426 : }
427 :
428 : NS_IMETHODIMP
429 33 : ContentPrincipal::GetAddonId(nsAString& aAddonId)
430 : {
431 33 : if (mAddonIdCache.isSome()) {
432 9 : aAddonId.Assign(mAddonIdCache.ref());
433 9 : return NS_OK;
434 : }
435 :
436 24 : NS_ENSURE_TRUE(mCodebase, NS_ERROR_FAILURE);
437 :
438 : nsresult rv;
439 : bool isMozExt;
440 24 : if (NS_SUCCEEDED(mCodebase->SchemeIs("moz-extension", &isMozExt)) && isMozExt) {
441 0 : nsIAddonPolicyService* addonPolicyService = GetAddonPolicyService(&rv);
442 0 : NS_ENSURE_SUCCESS(rv, rv);
443 :
444 0 : nsAutoString addonId;
445 0 : rv = addonPolicyService->ExtensionURIToAddonId(mCodebase, addonId);
446 0 : NS_ENSURE_SUCCESS(rv, rv);
447 :
448 0 : mAddonIdCache.emplace(addonId);
449 : } else {
450 24 : mAddonIdCache.emplace();
451 : }
452 :
453 24 : aAddonId.Assign(mAddonIdCache.ref());
454 24 : return NS_OK;
455 : };
456 :
457 : NS_IMETHODIMP
458 1 : ContentPrincipal::Read(nsIObjectInputStream* aStream)
459 : {
460 2 : nsCOMPtr<nsISupports> supports;
461 2 : nsCOMPtr<nsIURI> codebase;
462 1 : nsresult rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
463 1 : if (NS_FAILED(rv)) {
464 0 : return rv;
465 : }
466 :
467 1 : codebase = do_QueryInterface(supports);
468 :
469 2 : nsCOMPtr<nsIURI> domain;
470 1 : rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
471 1 : if (NS_FAILED(rv)) {
472 0 : return rv;
473 : }
474 :
475 1 : domain = do_QueryInterface(supports);
476 :
477 2 : nsAutoCString suffix;
478 1 : rv = aStream->ReadCString(suffix);
479 1 : NS_ENSURE_SUCCESS(rv, rv);
480 :
481 2 : OriginAttributes attrs;
482 1 : bool ok = attrs.PopulateFromSuffix(suffix);
483 1 : NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE);
484 :
485 1 : rv = NS_ReadOptionalObject(aStream, true, getter_AddRefs(supports));
486 1 : NS_ENSURE_SUCCESS(rv, rv);
487 :
488 2 : nsAutoCString originNoSuffix;
489 1 : rv = GenerateOriginNoSuffixFromURI(codebase, originNoSuffix);
490 1 : NS_ENSURE_SUCCESS(rv, rv);
491 :
492 1 : rv = Init(codebase, attrs, originNoSuffix);
493 1 : NS_ENSURE_SUCCESS(rv, rv);
494 :
495 1 : mCSP = do_QueryInterface(supports, &rv);
496 : // make sure setRequestContext is called after Init(),
497 : // to make sure the principals URI been initalized.
498 1 : if (mCSP) {
499 0 : mCSP->SetRequestContext(nullptr, this);
500 : }
501 :
502 1 : SetDomain(domain);
503 :
504 1 : return NS_OK;
505 : }
506 :
507 : NS_IMETHODIMP
508 1 : ContentPrincipal::Write(nsIObjectOutputStream* aStream)
509 : {
510 1 : NS_ENSURE_STATE(mCodebase);
511 : nsresult rv = NS_WriteOptionalCompoundObject(aStream, mCodebase, NS_GET_IID(nsIURI),
512 1 : true);
513 1 : if (NS_FAILED(rv)) {
514 0 : return rv;
515 : }
516 :
517 : rv = NS_WriteOptionalCompoundObject(aStream, mDomain, NS_GET_IID(nsIURI),
518 1 : true);
519 1 : if (NS_FAILED(rv)) {
520 0 : return rv;
521 : }
522 :
523 2 : nsAutoCString suffix;
524 1 : OriginAttributesRef().CreateSuffix(suffix);
525 :
526 1 : rv = aStream->WriteStringZ(suffix.get());
527 1 : NS_ENSURE_SUCCESS(rv, rv);
528 :
529 : rv = NS_WriteOptionalCompoundObject(aStream, mCSP,
530 : NS_GET_IID(nsIContentSecurityPolicy),
531 1 : true);
532 1 : if (NS_FAILED(rv)) {
533 0 : return rv;
534 : }
535 :
536 : // mCodebaseImmutable and mDomainImmutable will be recomputed based
537 : // on the deserialized URIs in Read().
538 :
539 1 : return NS_OK;
540 : }
|