caps/src/nsScriptSecurityManager.cpp

Tue, 06 Jan 2015 21:39:09 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Tue, 06 Jan 2015 21:39:09 +0100
branch
TOR_BUG_9701
changeset 8
97036ab72558
permissions
-rw-r--r--

Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
michael@0 2 /* vim: set ts=4 et sw=4 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 #include "nsScriptSecurityManager.h"
michael@0 8
michael@0 9 #include "mozilla/ArrayUtils.h"
michael@0 10
michael@0 11 #include "js/OldDebugAPI.h"
michael@0 12 #include "xpcprivate.h"
michael@0 13 #include "XPCWrapper.h"
michael@0 14 #include "nsIServiceManager.h"
michael@0 15 #include "nsIScriptObjectPrincipal.h"
michael@0 16 #include "nsIScriptContext.h"
michael@0 17 #include "nsIURL.h"
michael@0 18 #include "nsINestedURI.h"
michael@0 19 #include "nspr.h"
michael@0 20 #include "nsJSPrincipals.h"
michael@0 21 #include "nsSystemPrincipal.h"
michael@0 22 #include "nsPrincipal.h"
michael@0 23 #include "nsNullPrincipal.h"
michael@0 24 #include "DomainPolicy.h"
michael@0 25 #include "nsXPIDLString.h"
michael@0 26 #include "nsCRT.h"
michael@0 27 #include "nsCRTGlue.h"
michael@0 28 #include "nsError.h"
michael@0 29 #include "nsDOMCID.h"
michael@0 30 #include "nsIXPConnect.h"
michael@0 31 #include "nsIXPCSecurityManager.h"
michael@0 32 #include "nsTextFormatter.h"
michael@0 33 #include "nsIStringBundle.h"
michael@0 34 #include "nsNetUtil.h"
michael@0 35 #include "nsIEffectiveTLDService.h"
michael@0 36 #include "nsIProperties.h"
michael@0 37 #include "nsDirectoryServiceDefs.h"
michael@0 38 #include "nsIFile.h"
michael@0 39 #include "nsIFileURL.h"
michael@0 40 #include "nsIZipReader.h"
michael@0 41 #include "nsIXPConnect.h"
michael@0 42 #include "nsIScriptGlobalObject.h"
michael@0 43 #include "nsPIDOMWindow.h"
michael@0 44 #include "nsIDocShell.h"
michael@0 45 #include "nsIPrompt.h"
michael@0 46 #include "nsIWindowWatcher.h"
michael@0 47 #include "nsIConsoleService.h"
michael@0 48 #include "nsIJSRuntimeService.h"
michael@0 49 #include "nsIObserverService.h"
michael@0 50 #include "nsIContent.h"
michael@0 51 #include "nsAutoPtr.h"
michael@0 52 #include "nsDOMJSUtils.h"
michael@0 53 #include "nsAboutProtocolUtils.h"
michael@0 54 #include "nsIClassInfo.h"
michael@0 55 #include "nsIURIFixup.h"
michael@0 56 #include "nsCDefaultURIFixup.h"
michael@0 57 #include "nsIChromeRegistry.h"
michael@0 58 #include "nsIContentSecurityPolicy.h"
michael@0 59 #include "nsIAsyncVerifyRedirectCallback.h"
michael@0 60 #include "mozilla/Preferences.h"
michael@0 61 #include "mozilla/dom/BindingUtils.h"
michael@0 62 #include <stdint.h>
michael@0 63 #include "mozilla/ClearOnShutdown.h"
michael@0 64 #include "mozilla/StaticPtr.h"
michael@0 65 #include "nsContentUtils.h"
michael@0 66 #include "nsCxPusher.h"
michael@0 67 #include "nsJSUtils.h"
michael@0 68
michael@0 69 // This should be probably defined on some other place... but I couldn't find it
michael@0 70 #define WEBAPPS_PERM_NAME "webapps-manage"
michael@0 71
michael@0 72 using namespace mozilla;
michael@0 73 using namespace mozilla::dom;
michael@0 74
michael@0 75 static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID);
michael@0 76
michael@0 77 nsIIOService *nsScriptSecurityManager::sIOService = nullptr;
michael@0 78 nsIStringBundle *nsScriptSecurityManager::sStrBundle = nullptr;
michael@0 79 JSRuntime *nsScriptSecurityManager::sRuntime = 0;
michael@0 80 bool nsScriptSecurityManager::sStrictFileOriginPolicy = true;
michael@0 81
michael@0 82 bool
michael@0 83 nsScriptSecurityManager::SubjectIsPrivileged()
michael@0 84 {
michael@0 85 JSContext *cx = GetCurrentJSContext();
michael@0 86 if (cx && xpc::IsUniversalXPConnectEnabled(cx))
michael@0 87 return true;
michael@0 88 bool isSystem = false;
michael@0 89 return NS_SUCCEEDED(SubjectPrincipalIsSystem(&isSystem)) && isSystem;
michael@0 90 }
michael@0 91
michael@0 92 ///////////////////////////
michael@0 93 // Convenience Functions //
michael@0 94 ///////////////////////////
michael@0 95 // Result of this function should not be freed.
michael@0 96 static inline const char16_t *
michael@0 97 IDToString(JSContext *cx, jsid id_)
michael@0 98 {
michael@0 99 JS::RootedId id(cx, id_);
michael@0 100 if (JSID_IS_STRING(id))
michael@0 101 return JS_GetInternedStringChars(JSID_TO_STRING(id));
michael@0 102
michael@0 103 JS::Rooted<JS::Value> idval(cx);
michael@0 104 if (!JS_IdToValue(cx, id, &idval))
michael@0 105 return nullptr;
michael@0 106 JSString *str = JS::ToString(cx, idval);
michael@0 107 if(!str)
michael@0 108 return nullptr;
michael@0 109 return JS_GetStringCharsZ(cx, str);
michael@0 110 }
michael@0 111
michael@0 112 class nsAutoInPrincipalDomainOriginSetter {
michael@0 113 public:
michael@0 114 nsAutoInPrincipalDomainOriginSetter() {
michael@0 115 ++sInPrincipalDomainOrigin;
michael@0 116 }
michael@0 117 ~nsAutoInPrincipalDomainOriginSetter() {
michael@0 118 --sInPrincipalDomainOrigin;
michael@0 119 }
michael@0 120 static uint32_t sInPrincipalDomainOrigin;
michael@0 121 };
michael@0 122 uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin;
michael@0 123
michael@0 124 static
michael@0 125 nsresult
michael@0 126 GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin)
michael@0 127 {
michael@0 128 if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) {
michael@0 129 // Allow a single recursive call to GetPrincipalDomainOrigin, since that
michael@0 130 // might be happening on a different principal from the first call. But
michael@0 131 // after that, cut off the recursion; it just indicates that something
michael@0 132 // we're doing in this method causes us to reenter a security check here.
michael@0 133 return NS_ERROR_NOT_AVAILABLE;
michael@0 134 }
michael@0 135
michael@0 136 nsAutoInPrincipalDomainOriginSetter autoSetter;
michael@0 137
michael@0 138 nsCOMPtr<nsIURI> uri = NS_GetInnermostURI(aURI);
michael@0 139 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
michael@0 140
michael@0 141 nsAutoCString hostPort;
michael@0 142
michael@0 143 nsresult rv = uri->GetHostPort(hostPort);
michael@0 144 if (NS_SUCCEEDED(rv)) {
michael@0 145 nsAutoCString scheme;
michael@0 146 rv = uri->GetScheme(scheme);
michael@0 147 NS_ENSURE_SUCCESS(rv, rv);
michael@0 148 aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort;
michael@0 149 }
michael@0 150 else {
michael@0 151 // Some URIs (e.g., nsSimpleURI) don't support host. Just
michael@0 152 // get the full spec.
michael@0 153 rv = uri->GetSpec(aOrigin);
michael@0 154 NS_ENSURE_SUCCESS(rv, rv);
michael@0 155 }
michael@0 156
michael@0 157 return NS_OK;
michael@0 158 }
michael@0 159
michael@0 160 static
michael@0 161 nsresult
michael@0 162 GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal,
michael@0 163 nsACString& aOrigin)
michael@0 164 {
michael@0 165
michael@0 166 nsCOMPtr<nsIURI> uri;
michael@0 167 aPrincipal->GetDomain(getter_AddRefs(uri));
michael@0 168 if (!uri) {
michael@0 169 aPrincipal->GetURI(getter_AddRefs(uri));
michael@0 170 }
michael@0 171 NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED);
michael@0 172
michael@0 173 return GetOriginFromURI(uri, aOrigin);
michael@0 174 }
michael@0 175
michael@0 176 inline void SetPendingException(JSContext *cx, const char *aMsg)
michael@0 177 {
michael@0 178 JS_ReportError(cx, "%s", aMsg);
michael@0 179 }
michael@0 180
michael@0 181 inline void SetPendingException(JSContext *cx, const char16_t *aMsg)
michael@0 182 {
michael@0 183 JS_ReportError(cx, "%hs", aMsg);
michael@0 184 }
michael@0 185
michael@0 186 // Helper class to get stuff from the ClassInfo and not waste extra time with
michael@0 187 // virtual method calls for things it has already gotten
michael@0 188 class ClassInfoData
michael@0 189 {
michael@0 190 public:
michael@0 191 ClassInfoData(nsIClassInfo *aClassInfo, const char *aName)
michael@0 192 : mClassInfo(aClassInfo),
michael@0 193 mName(const_cast<char *>(aName)),
michael@0 194 mDidGetFlags(false),
michael@0 195 mMustFreeName(false)
michael@0 196 {
michael@0 197 }
michael@0 198
michael@0 199 ~ClassInfoData()
michael@0 200 {
michael@0 201 if (mMustFreeName)
michael@0 202 nsMemory::Free(mName);
michael@0 203 }
michael@0 204
michael@0 205 uint32_t GetFlags()
michael@0 206 {
michael@0 207 if (!mDidGetFlags) {
michael@0 208 if (mClassInfo) {
michael@0 209 nsresult rv = mClassInfo->GetFlags(&mFlags);
michael@0 210 if (NS_FAILED(rv)) {
michael@0 211 mFlags = 0;
michael@0 212 }
michael@0 213 } else {
michael@0 214 mFlags = 0;
michael@0 215 }
michael@0 216
michael@0 217 mDidGetFlags = true;
michael@0 218 }
michael@0 219
michael@0 220 return mFlags;
michael@0 221 }
michael@0 222
michael@0 223 bool IsDOMClass()
michael@0 224 {
michael@0 225 return !!(GetFlags() & nsIClassInfo::DOM_OBJECT);
michael@0 226 }
michael@0 227
michael@0 228 const char* GetName()
michael@0 229 {
michael@0 230 if (!mName) {
michael@0 231 if (mClassInfo) {
michael@0 232 mClassInfo->GetClassDescription(&mName);
michael@0 233 }
michael@0 234
michael@0 235 if (mName) {
michael@0 236 mMustFreeName = true;
michael@0 237 } else {
michael@0 238 mName = const_cast<char *>("UnnamedClass");
michael@0 239 }
michael@0 240 }
michael@0 241
michael@0 242 return mName;
michael@0 243 }
michael@0 244
michael@0 245 private:
michael@0 246 nsIClassInfo *mClassInfo; // WEAK
michael@0 247 uint32_t mFlags;
michael@0 248 char *mName;
michael@0 249 bool mDidGetFlags;
michael@0 250 bool mMustFreeName;
michael@0 251 };
michael@0 252
michael@0 253 JSContext *
michael@0 254 nsScriptSecurityManager::GetCurrentJSContext()
michael@0 255 {
michael@0 256 // Get JSContext from stack.
michael@0 257 return nsXPConnect::XPConnect()->GetCurrentJSContext();
michael@0 258 }
michael@0 259
michael@0 260 JSContext *
michael@0 261 nsScriptSecurityManager::GetSafeJSContext()
michael@0 262 {
michael@0 263 // Get JSContext from stack.
michael@0 264 return nsXPConnect::XPConnect()->GetSafeJSContext();
michael@0 265 }
michael@0 266
michael@0 267 /* static */
michael@0 268 bool
michael@0 269 nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI,
michael@0 270 nsIURI* aTargetURI)
michael@0 271 {
michael@0 272 return NS_SecurityCompareURIs(aSourceURI, aTargetURI, sStrictFileOriginPolicy);
michael@0 273 }
michael@0 274
michael@0 275 // SecurityHashURI is consistent with SecurityCompareURIs because NS_SecurityHashURI
michael@0 276 // is consistent with NS_SecurityCompareURIs. See nsNetUtil.h.
michael@0 277 uint32_t
michael@0 278 nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI)
michael@0 279 {
michael@0 280 return NS_SecurityHashURI(aURI);
michael@0 281 }
michael@0 282
michael@0 283 NS_IMETHODIMP
michael@0 284 nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel,
michael@0 285 nsIPrincipal** aPrincipal)
michael@0 286 {
michael@0 287 NS_PRECONDITION(aChannel, "Must have channel!");
michael@0 288 nsCOMPtr<nsISupports> owner;
michael@0 289 aChannel->GetOwner(getter_AddRefs(owner));
michael@0 290 if (owner) {
michael@0 291 CallQueryInterface(owner, aPrincipal);
michael@0 292 if (*aPrincipal) {
michael@0 293 return NS_OK;
michael@0 294 }
michael@0 295 }
michael@0 296
michael@0 297 // OK, get the principal from the URI. Make sure this does the same thing
michael@0 298 // as nsDocument::Reset and XULDocument::StartDocumentLoad.
michael@0 299 nsCOMPtr<nsIURI> uri;
michael@0 300 nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
michael@0 301 NS_ENSURE_SUCCESS(rv, rv);
michael@0 302
michael@0 303 nsCOMPtr<nsIDocShell> docShell;
michael@0 304 NS_QueryNotificationCallbacks(aChannel, docShell);
michael@0 305
michael@0 306 if (docShell) {
michael@0 307 return GetDocShellCodebasePrincipal(uri, docShell, aPrincipal);
michael@0 308 }
michael@0 309
michael@0 310 return GetCodebasePrincipalInternal(uri, UNKNOWN_APP_ID,
michael@0 311 /* isInBrowserElement */ false, aPrincipal);
michael@0 312 }
michael@0 313
michael@0 314 NS_IMETHODIMP
michael@0 315 nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal,
michael@0 316 bool* aIsSystem)
michael@0 317 {
michael@0 318 *aIsSystem = (aPrincipal == mSystemPrincipal);
michael@0 319 return NS_OK;
michael@0 320 }
michael@0 321
michael@0 322 NS_IMETHODIMP_(nsIPrincipal *)
michael@0 323 nsScriptSecurityManager::GetCxSubjectPrincipal(JSContext *cx)
michael@0 324 {
michael@0 325 NS_ASSERTION(cx == GetCurrentJSContext(),
michael@0 326 "Uh, cx is not the current JS context!");
michael@0 327
michael@0 328 nsresult rv = NS_ERROR_FAILURE;
michael@0 329 nsIPrincipal *principal = GetSubjectPrincipal(cx, &rv);
michael@0 330 if (NS_FAILED(rv))
michael@0 331 return nullptr;
michael@0 332
michael@0 333 return principal;
michael@0 334 }
michael@0 335
michael@0 336 /////////////////////////////
michael@0 337 // nsScriptSecurityManager //
michael@0 338 /////////////////////////////
michael@0 339
michael@0 340 ////////////////////////////////////
michael@0 341 // Methods implementing ISupports //
michael@0 342 ////////////////////////////////////
michael@0 343 NS_IMPL_ISUPPORTS(nsScriptSecurityManager,
michael@0 344 nsIScriptSecurityManager,
michael@0 345 nsIXPCSecurityManager,
michael@0 346 nsIChannelEventSink,
michael@0 347 nsIObserver)
michael@0 348
michael@0 349 ///////////////////////////////////////////////////
michael@0 350 // Methods implementing nsIScriptSecurityManager //
michael@0 351 ///////////////////////////////////////////////////
michael@0 352
michael@0 353 ///////////////// Security Checks /////////////////
michael@0 354
michael@0 355 bool
michael@0 356 nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx)
michael@0 357 {
michael@0 358 // Get the security manager
michael@0 359 nsScriptSecurityManager *ssm =
michael@0 360 nsScriptSecurityManager::GetScriptSecurityManager();
michael@0 361
michael@0 362 NS_ASSERTION(ssm, "Failed to get security manager service");
michael@0 363 if (!ssm)
michael@0 364 return false;
michael@0 365
michael@0 366 nsresult rv;
michael@0 367 nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv);
michael@0 368
michael@0 369 NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context");
michael@0 370 if (NS_FAILED(rv))
michael@0 371 return false; // Not just absence of principal, but failure.
michael@0 372
michael@0 373 if (!subjectPrincipal)
michael@0 374 return true;
michael@0 375
michael@0 376 nsCOMPtr<nsIContentSecurityPolicy> csp;
michael@0 377 rv = subjectPrincipal->GetCsp(getter_AddRefs(csp));
michael@0 378 NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal.");
michael@0 379
michael@0 380 // don't do anything unless there's a CSP
michael@0 381 if (!csp)
michael@0 382 return true;
michael@0 383
michael@0 384 bool evalOK = true;
michael@0 385 bool reportViolation = false;
michael@0 386 rv = csp->GetAllowsEval(&reportViolation, &evalOK);
michael@0 387
michael@0 388 if (NS_FAILED(rv))
michael@0 389 {
michael@0 390 NS_WARNING("CSP: failed to get allowsEval");
michael@0 391 return true; // fail open to not break sites.
michael@0 392 }
michael@0 393
michael@0 394 if (reportViolation) {
michael@0 395 nsAutoString fileName;
michael@0 396 unsigned lineNum = 0;
michael@0 397 NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP");
michael@0 398
michael@0 399 JS::AutoFilename scriptFilename;
michael@0 400 if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum)) {
michael@0 401 if (const char *file = scriptFilename.get()) {
michael@0 402 CopyUTF8toUTF16(nsDependentCString(file), fileName);
michael@0 403 }
michael@0 404 }
michael@0 405 csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL,
michael@0 406 fileName,
michael@0 407 scriptSample,
michael@0 408 lineNum,
michael@0 409 EmptyString(),
michael@0 410 EmptyString());
michael@0 411 }
michael@0 412
michael@0 413 return evalOK;
michael@0 414 }
michael@0 415
michael@0 416 // static
michael@0 417 bool
michael@0 418 nsScriptSecurityManager::JSPrincipalsSubsume(JSPrincipals *first,
michael@0 419 JSPrincipals *second)
michael@0 420 {
michael@0 421 return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second));
michael@0 422 }
michael@0 423
michael@0 424 NS_IMETHODIMP
michael@0 425 nsScriptSecurityManager::CheckSameOrigin(JSContext* cx,
michael@0 426 nsIURI* aTargetURI)
michael@0 427 {
michael@0 428 nsresult rv;
michael@0 429
michael@0 430 // Get a context if necessary
michael@0 431 if (!cx)
michael@0 432 {
michael@0 433 cx = GetCurrentJSContext();
michael@0 434 if (!cx)
michael@0 435 return NS_OK; // No JS context, so allow access
michael@0 436 }
michael@0 437
michael@0 438 // Get a principal from the context
michael@0 439 nsIPrincipal* sourcePrincipal = GetSubjectPrincipal(cx, &rv);
michael@0 440 if (NS_FAILED(rv))
michael@0 441 return rv;
michael@0 442
michael@0 443 if (!sourcePrincipal)
michael@0 444 {
michael@0 445 NS_WARNING("CheckSameOrigin called on script w/o principals; should this happen?");
michael@0 446 return NS_OK;
michael@0 447 }
michael@0 448
michael@0 449 if (sourcePrincipal == mSystemPrincipal)
michael@0 450 {
michael@0 451 // This is a system (chrome) script, so allow access
michael@0 452 return NS_OK;
michael@0 453 }
michael@0 454
michael@0 455 // Get the original URI from the source principal.
michael@0 456 // This has the effect of ignoring any change to document.domain
michael@0 457 // which must be done to avoid DNS spoofing (bug 154930)
michael@0 458 nsCOMPtr<nsIURI> sourceURI;
michael@0 459 sourcePrincipal->GetDomain(getter_AddRefs(sourceURI));
michael@0 460 if (!sourceURI) {
michael@0 461 sourcePrincipal->GetURI(getter_AddRefs(sourceURI));
michael@0 462 NS_ENSURE_TRUE(sourceURI, NS_ERROR_FAILURE);
michael@0 463 }
michael@0 464
michael@0 465 // Compare origins
michael@0 466 if (!SecurityCompareURIs(sourceURI, aTargetURI))
michael@0 467 {
michael@0 468 ReportError(cx, NS_LITERAL_STRING("CheckSameOriginError"), sourceURI, aTargetURI);
michael@0 469 return NS_ERROR_DOM_BAD_URI;
michael@0 470 }
michael@0 471 return NS_OK;
michael@0 472 }
michael@0 473
michael@0 474 NS_IMETHODIMP
michael@0 475 nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI,
michael@0 476 nsIURI* aTargetURI,
michael@0 477 bool reportError)
michael@0 478 {
michael@0 479 if (!SecurityCompareURIs(aSourceURI, aTargetURI))
michael@0 480 {
michael@0 481 if (reportError) {
michael@0 482 ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"),
michael@0 483 aSourceURI, aTargetURI);
michael@0 484 }
michael@0 485 return NS_ERROR_DOM_BAD_URI;
michael@0 486 }
michael@0 487 return NS_OK;
michael@0 488 }
michael@0 489
michael@0 490 /*static*/ uint32_t
michael@0 491 nsScriptSecurityManager::HashPrincipalByOrigin(nsIPrincipal* aPrincipal)
michael@0 492 {
michael@0 493 nsCOMPtr<nsIURI> uri;
michael@0 494 aPrincipal->GetDomain(getter_AddRefs(uri));
michael@0 495 if (!uri)
michael@0 496 aPrincipal->GetURI(getter_AddRefs(uri));
michael@0 497 return SecurityHashURI(uri);
michael@0 498 }
michael@0 499
michael@0 500 /* static */ bool
michael@0 501 nsScriptSecurityManager::AppAttributesEqual(nsIPrincipal* aFirst,
michael@0 502 nsIPrincipal* aSecond)
michael@0 503 {
michael@0 504 MOZ_ASSERT(aFirst && aSecond, "Don't pass null pointers!");
michael@0 505
michael@0 506 uint32_t firstAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
michael@0 507 if (!aFirst->GetUnknownAppId()) {
michael@0 508 firstAppId = aFirst->GetAppId();
michael@0 509 }
michael@0 510
michael@0 511 uint32_t secondAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID;
michael@0 512 if (!aSecond->GetUnknownAppId()) {
michael@0 513 secondAppId = aSecond->GetAppId();
michael@0 514 }
michael@0 515
michael@0 516 return ((firstAppId == secondAppId) &&
michael@0 517 (aFirst->GetIsInBrowserElement() == aSecond->GetIsInBrowserElement()));
michael@0 518 }
michael@0 519
michael@0 520 NS_IMETHODIMP
michael@0 521 nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI)
michael@0 522 {
michael@0 523 // Get principal of currently executing script.
michael@0 524 nsresult rv;
michael@0 525 nsIPrincipal* principal = GetSubjectPrincipal(cx, &rv);
michael@0 526 if (NS_FAILED(rv))
michael@0 527 return rv;
michael@0 528
michael@0 529 // Native code can load all URIs.
michael@0 530 if (!principal)
michael@0 531 return NS_OK;
michael@0 532
michael@0 533 rv = CheckLoadURIWithPrincipal(principal, aURI,
michael@0 534 nsIScriptSecurityManager::STANDARD);
michael@0 535 if (NS_SUCCEEDED(rv)) {
michael@0 536 // OK to load
michael@0 537 return NS_OK;
michael@0 538 }
michael@0 539
michael@0 540 // See if we're attempting to load a file: URI. If so, let a
michael@0 541 // UniversalXPConnect capability trump the above check.
michael@0 542 bool isFile = false;
michael@0 543 bool isRes = false;
michael@0 544 if (NS_FAILED(aURI->SchemeIs("file", &isFile)) ||
michael@0 545 NS_FAILED(aURI->SchemeIs("resource", &isRes)))
michael@0 546 return NS_ERROR_FAILURE;
michael@0 547 if (isFile || isRes)
michael@0 548 {
michael@0 549 if (SubjectIsPrivileged())
michael@0 550 return NS_OK;
michael@0 551 }
michael@0 552
michael@0 553 // Report error.
michael@0 554 nsAutoCString spec;
michael@0 555 if (NS_FAILED(aURI->GetAsciiSpec(spec)))
michael@0 556 return NS_ERROR_FAILURE;
michael@0 557 nsAutoCString msg("Access to '");
michael@0 558 msg.Append(spec);
michael@0 559 msg.AppendLiteral("' from script denied");
michael@0 560 SetPendingException(cx, msg.get());
michael@0 561 return NS_ERROR_DOM_BAD_URI;
michael@0 562 }
michael@0 563
michael@0 564 /**
michael@0 565 * Helper method to handle cases where a flag passed to
michael@0 566 * CheckLoadURIWithPrincipal means denying loading if the given URI has certain
michael@0 567 * nsIProtocolHandler flags set.
michael@0 568 * @return if success, access is allowed. Otherwise, deny access
michael@0 569 */
michael@0 570 static nsresult
michael@0 571 DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags)
michael@0 572 {
michael@0 573 NS_PRECONDITION(aURI, "Must have URI!");
michael@0 574
michael@0 575 bool uriHasFlags;
michael@0 576 nsresult rv =
michael@0 577 NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags);
michael@0 578 NS_ENSURE_SUCCESS(rv, rv);
michael@0 579
michael@0 580 if (uriHasFlags) {
michael@0 581 return NS_ERROR_DOM_BAD_URI;
michael@0 582 }
michael@0 583
michael@0 584 return NS_OK;
michael@0 585 }
michael@0 586
michael@0 587 static bool
michael@0 588 EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase)
michael@0 589 {
michael@0 590 // Make a clone of the incoming URI, because we're going to mutate it.
michael@0 591 nsCOMPtr<nsIURI> probe;
michael@0 592 nsresult rv = aProbeArg->Clone(getter_AddRefs(probe));
michael@0 593 NS_ENSURE_SUCCESS(rv, false);
michael@0 594
michael@0 595 nsCOMPtr<nsIEffectiveTLDService> tldService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID);
michael@0 596 NS_ENSURE_TRUE(tldService, false);
michael@0 597 while (true) {
michael@0 598 if (nsScriptSecurityManager::SecurityCompareURIs(probe, aBase)) {
michael@0 599 return true;
michael@0 600 }
michael@0 601
michael@0 602 nsAutoCString host, newHost;
michael@0 603 nsresult rv = probe->GetHost(host);
michael@0 604 NS_ENSURE_SUCCESS(rv, false);
michael@0 605
michael@0 606 rv = tldService->GetNextSubDomain(host, newHost);
michael@0 607 if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
michael@0 608 return false;
michael@0 609 }
michael@0 610 NS_ENSURE_SUCCESS(rv, false);
michael@0 611 rv = probe->SetHost(newHost);
michael@0 612 NS_ENSURE_SUCCESS(rv, false);
michael@0 613 }
michael@0 614 }
michael@0 615
michael@0 616 NS_IMETHODIMP
michael@0 617 nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal,
michael@0 618 nsIURI *aTargetURI,
michael@0 619 uint32_t aFlags)
michael@0 620 {
michael@0 621 NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal");
michael@0 622 // If someone passes a flag that we don't understand, we should
michael@0 623 // fail, because they may need a security check that we don't
michael@0 624 // provide.
michael@0 625 NS_ENSURE_FALSE(aFlags & ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
michael@0 626 nsIScriptSecurityManager::ALLOW_CHROME |
michael@0 627 nsIScriptSecurityManager::DISALLOW_SCRIPT |
michael@0 628 nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL |
michael@0 629 nsIScriptSecurityManager::DONT_REPORT_ERRORS),
michael@0 630 NS_ERROR_UNEXPECTED);
michael@0 631 NS_ENSURE_ARG_POINTER(aPrincipal);
michael@0 632 NS_ENSURE_ARG_POINTER(aTargetURI);
michael@0 633
michael@0 634 // If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which
michael@0 635 // would do such inheriting. That would be URIs that do not have their own
michael@0 636 // security context. We do this even for the system principal.
michael@0 637 if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) {
michael@0 638 nsresult rv =
michael@0 639 DenyAccessIfURIHasFlags(aTargetURI,
michael@0 640 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT);
michael@0 641 NS_ENSURE_SUCCESS(rv, rv);
michael@0 642 }
michael@0 643
michael@0 644 if (aPrincipal == mSystemPrincipal) {
michael@0 645 // Allow access
michael@0 646 return NS_OK;
michael@0 647 }
michael@0 648
michael@0 649 nsCOMPtr<nsIURI> sourceURI;
michael@0 650 aPrincipal->GetURI(getter_AddRefs(sourceURI));
michael@0 651 if (!sourceURI) {
michael@0 652 nsCOMPtr<nsIExpandedPrincipal> expanded = do_QueryInterface(aPrincipal);
michael@0 653 if (expanded) {
michael@0 654 nsTArray< nsCOMPtr<nsIPrincipal> > *whiteList;
michael@0 655 expanded->GetWhiteList(&whiteList);
michael@0 656 for (uint32_t i = 0; i < whiteList->Length(); ++i) {
michael@0 657 nsresult rv = CheckLoadURIWithPrincipal((*whiteList)[i],
michael@0 658 aTargetURI,
michael@0 659 aFlags);
michael@0 660 if (NS_SUCCEEDED(rv)) {
michael@0 661 // Allow access if it succeeded with one of the white listed principals
michael@0 662 return NS_OK;
michael@0 663 }
michael@0 664 }
michael@0 665 // None of our whitelisted principals worked.
michael@0 666 return NS_ERROR_DOM_BAD_URI;
michael@0 667 }
michael@0 668 NS_ERROR("Non-system principals or expanded principal passed to CheckLoadURIWithPrincipal "
michael@0 669 "must have a URI!");
michael@0 670 return NS_ERROR_UNEXPECTED;
michael@0 671 }
michael@0 672
michael@0 673 // Automatic loads are not allowed from certain protocols.
michael@0 674 if (aFlags & nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) {
michael@0 675 nsresult rv =
michael@0 676 DenyAccessIfURIHasFlags(sourceURI,
michael@0 677 nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT);
michael@0 678 NS_ENSURE_SUCCESS(rv, rv);
michael@0 679 }
michael@0 680
michael@0 681 // If either URI is a nested URI, get the base URI
michael@0 682 nsCOMPtr<nsIURI> sourceBaseURI = NS_GetInnermostURI(sourceURI);
michael@0 683 nsCOMPtr<nsIURI> targetBaseURI = NS_GetInnermostURI(aTargetURI);
michael@0 684
michael@0 685 //-- get the target scheme
michael@0 686 nsAutoCString targetScheme;
michael@0 687 nsresult rv = targetBaseURI->GetScheme(targetScheme);
michael@0 688 if (NS_FAILED(rv)) return rv;
michael@0 689
michael@0 690 //-- Some callers do not allow loading javascript:
michael@0 691 if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) &&
michael@0 692 targetScheme.EqualsLiteral("javascript"))
michael@0 693 {
michael@0 694 return NS_ERROR_DOM_BAD_URI;
michael@0 695 }
michael@0 696
michael@0 697 NS_NAMED_LITERAL_STRING(errorTag, "CheckLoadURIError");
michael@0 698 bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS);
michael@0 699
michael@0 700 // Check for uris that are only loadable by principals that subsume them
michael@0 701 bool hasFlags;
michael@0 702 rv = NS_URIChainHasFlags(targetBaseURI,
michael@0 703 nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS,
michael@0 704 &hasFlags);
michael@0 705 NS_ENSURE_SUCCESS(rv, rv);
michael@0 706
michael@0 707 if (hasFlags) {
michael@0 708 return aPrincipal->CheckMayLoad(targetBaseURI, true, false);
michael@0 709 }
michael@0 710
michael@0 711 //-- get the source scheme
michael@0 712 nsAutoCString sourceScheme;
michael@0 713 rv = sourceBaseURI->GetScheme(sourceScheme);
michael@0 714 if (NS_FAILED(rv)) return rv;
michael@0 715
michael@0 716 if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) {
michael@0 717 // A null principal can target its own URI.
michael@0 718 if (sourceURI == aTargetURI) {
michael@0 719 return NS_OK;
michael@0 720 }
michael@0 721 }
michael@0 722 else if (targetScheme.Equals(sourceScheme,
michael@0 723 nsCaseInsensitiveCStringComparator()))
michael@0 724 {
michael@0 725 // every scheme can access another URI from the same scheme,
michael@0 726 // as long as they don't represent null principals...
michael@0 727 // Or they don't require an special permission to do so
michael@0 728 // See bug#773886
michael@0 729
michael@0 730 bool hasFlags;
michael@0 731 rv = NS_URIChainHasFlags(targetBaseURI,
michael@0 732 nsIProtocolHandler::URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM,
michael@0 733 &hasFlags);
michael@0 734 NS_ENSURE_SUCCESS(rv, rv);
michael@0 735
michael@0 736 if (hasFlags) {
michael@0 737 // In this case, we allow opening only if the source and target URIS
michael@0 738 // are on the same domain, or the opening URI has the webapps
michael@0 739 // permision granted
michael@0 740 if (!SecurityCompareURIs(sourceBaseURI,targetBaseURI) &&
michael@0 741 !nsContentUtils::IsExactSitePermAllow(aPrincipal,WEBAPPS_PERM_NAME)){
michael@0 742 return NS_ERROR_DOM_BAD_URI;
michael@0 743 }
michael@0 744 }
michael@0 745 return NS_OK;
michael@0 746 }
michael@0 747
michael@0 748 // If the schemes don't match, the policy is specified by the protocol
michael@0 749 // flags on the target URI. Note that the order of policy checks here is
michael@0 750 // very important! We start from most restrictive and work our way down.
michael@0 751 // Note that since we're working with the innermost URI, we can just use
michael@0 752 // the methods that work on chains of nested URIs and they will only look
michael@0 753 // at the flags for our one URI.
michael@0 754
michael@0 755 // Check for system target URI
michael@0 756 rv = DenyAccessIfURIHasFlags(targetBaseURI,
michael@0 757 nsIProtocolHandler::URI_DANGEROUS_TO_LOAD);
michael@0 758 if (NS_FAILED(rv)) {
michael@0 759 // Deny access, since the origin principal is not system
michael@0 760 if (reportErrors) {
michael@0 761 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
michael@0 762 }
michael@0 763 return rv;
michael@0 764 }
michael@0 765
michael@0 766 // Check for chrome target URI
michael@0 767 rv = NS_URIChainHasFlags(targetBaseURI,
michael@0 768 nsIProtocolHandler::URI_IS_UI_RESOURCE,
michael@0 769 &hasFlags);
michael@0 770 NS_ENSURE_SUCCESS(rv, rv);
michael@0 771 if (hasFlags) {
michael@0 772 if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) {
michael@0 773 if (!targetScheme.EqualsLiteral("chrome")) {
michael@0 774 // for now don't change behavior for resource: or moz-icon:
michael@0 775 return NS_OK;
michael@0 776 }
michael@0 777
michael@0 778 // allow load only if chrome package is whitelisted
michael@0 779 nsCOMPtr<nsIXULChromeRegistry> reg(do_GetService(
michael@0 780 NS_CHROMEREGISTRY_CONTRACTID));
michael@0 781 if (reg) {
michael@0 782 bool accessAllowed = false;
michael@0 783 reg->AllowContentToAccess(targetBaseURI, &accessAllowed);
michael@0 784 if (accessAllowed) {
michael@0 785 return NS_OK;
michael@0 786 }
michael@0 787 }
michael@0 788 }
michael@0 789
michael@0 790 // resource: and chrome: are equivalent, securitywise
michael@0 791 // That's bogus!! Fix this. But watch out for
michael@0 792 // the view-source stylesheet?
michael@0 793 bool sourceIsChrome;
michael@0 794 rv = NS_URIChainHasFlags(sourceBaseURI,
michael@0 795 nsIProtocolHandler::URI_IS_UI_RESOURCE,
michael@0 796 &sourceIsChrome);
michael@0 797 NS_ENSURE_SUCCESS(rv, rv);
michael@0 798 if (sourceIsChrome) {
michael@0 799 return NS_OK;
michael@0 800 }
michael@0 801 if (reportErrors) {
michael@0 802 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
michael@0 803 }
michael@0 804 return NS_ERROR_DOM_BAD_URI;
michael@0 805 }
michael@0 806
michael@0 807 // Check for target URI pointing to a file
michael@0 808 rv = NS_URIChainHasFlags(targetBaseURI,
michael@0 809 nsIProtocolHandler::URI_IS_LOCAL_FILE,
michael@0 810 &hasFlags);
michael@0 811 NS_ENSURE_SUCCESS(rv, rv);
michael@0 812 if (hasFlags) {
michael@0 813 // Allow domains that were whitelisted in the prefs. In 99.9% of cases,
michael@0 814 // this array is empty.
michael@0 815 for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) {
michael@0 816 if (EqualOrSubdomain(sourceURI, mFileURIWhitelist[i])) {
michael@0 817 return NS_OK;
michael@0 818 }
michael@0 819 }
michael@0 820
michael@0 821 // resource: and chrome: are equivalent, securitywise
michael@0 822 // That's bogus!! Fix this. But watch out for
michael@0 823 // the view-source stylesheet?
michael@0 824 bool sourceIsChrome;
michael@0 825 rv = NS_URIChainHasFlags(sourceURI,
michael@0 826 nsIProtocolHandler::URI_IS_UI_RESOURCE,
michael@0 827 &sourceIsChrome);
michael@0 828 NS_ENSURE_SUCCESS(rv, rv);
michael@0 829 if (sourceIsChrome) {
michael@0 830 return NS_OK;
michael@0 831 }
michael@0 832
michael@0 833 if (reportErrors) {
michael@0 834 ReportError(nullptr, errorTag, sourceURI, aTargetURI);
michael@0 835 }
michael@0 836 return NS_ERROR_DOM_BAD_URI;
michael@0 837 }
michael@0 838
michael@0 839 // OK, everyone is allowed to load this, since unflagged handlers are
michael@0 840 // deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we
michael@0 841 // need to warn. At some point we'll want to make this warning into an
michael@0 842 // error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD.
michael@0 843 rv = NS_URIChainHasFlags(targetBaseURI,
michael@0 844 nsIProtocolHandler::URI_LOADABLE_BY_ANYONE,
michael@0 845 &hasFlags);
michael@0 846 NS_ENSURE_SUCCESS(rv, rv);
michael@0 847 if (!hasFlags) {
michael@0 848 nsXPIDLString message;
michael@0 849 NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme);
michael@0 850 const char16_t* formatStrings[] = { ucsTargetScheme.get() };
michael@0 851 rv = sStrBundle->
michael@0 852 FormatStringFromName(MOZ_UTF16("ProtocolFlagError"),
michael@0 853 formatStrings,
michael@0 854 ArrayLength(formatStrings),
michael@0 855 getter_Copies(message));
michael@0 856 if (NS_SUCCEEDED(rv)) {
michael@0 857 nsCOMPtr<nsIConsoleService> console(
michael@0 858 do_GetService("@mozilla.org/consoleservice;1"));
michael@0 859 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
michael@0 860
michael@0 861 console->LogStringMessage(message.get());
michael@0 862 }
michael@0 863 }
michael@0 864
michael@0 865 return NS_OK;
michael@0 866 }
michael@0 867
michael@0 868 nsresult
michael@0 869 nsScriptSecurityManager::ReportError(JSContext* cx, const nsAString& messageTag,
michael@0 870 nsIURI* aSource, nsIURI* aTarget)
michael@0 871 {
michael@0 872 nsresult rv;
michael@0 873 NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER);
michael@0 874
michael@0 875 // Get the source URL spec
michael@0 876 nsAutoCString sourceSpec;
michael@0 877 rv = aSource->GetAsciiSpec(sourceSpec);
michael@0 878 NS_ENSURE_SUCCESS(rv, rv);
michael@0 879
michael@0 880 // Get the target URL spec
michael@0 881 nsAutoCString targetSpec;
michael@0 882 rv = aTarget->GetAsciiSpec(targetSpec);
michael@0 883 NS_ENSURE_SUCCESS(rv, rv);
michael@0 884
michael@0 885 // Localize the error message
michael@0 886 nsXPIDLString message;
michael@0 887 NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec);
michael@0 888 NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec);
michael@0 889 const char16_t *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() };
michael@0 890 rv = sStrBundle->FormatStringFromName(PromiseFlatString(messageTag).get(),
michael@0 891 formatStrings,
michael@0 892 ArrayLength(formatStrings),
michael@0 893 getter_Copies(message));
michael@0 894 NS_ENSURE_SUCCESS(rv, rv);
michael@0 895
michael@0 896 // If a JS context was passed in, set a JS exception.
michael@0 897 // Otherwise, print the error message directly to the JS console
michael@0 898 // and to standard output
michael@0 899 if (cx)
michael@0 900 {
michael@0 901 SetPendingException(cx, message.get());
michael@0 902 }
michael@0 903 else // Print directly to the console
michael@0 904 {
michael@0 905 nsCOMPtr<nsIConsoleService> console(
michael@0 906 do_GetService("@mozilla.org/consoleservice;1"));
michael@0 907 NS_ENSURE_TRUE(console, NS_ERROR_FAILURE);
michael@0 908
michael@0 909 console->LogStringMessage(message.get());
michael@0 910 }
michael@0 911 return NS_OK;
michael@0 912 }
michael@0 913
michael@0 914 NS_IMETHODIMP
michael@0 915 nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal,
michael@0 916 const nsACString& aTargetURIStr,
michael@0 917 uint32_t aFlags)
michael@0 918 {
michael@0 919 nsresult rv;
michael@0 920 nsCOMPtr<nsIURI> target;
michael@0 921 rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr,
michael@0 922 nullptr, nullptr, sIOService);
michael@0 923 NS_ENSURE_SUCCESS(rv, rv);
michael@0 924
michael@0 925 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
michael@0 926 if (rv == NS_ERROR_DOM_BAD_URI) {
michael@0 927 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
michael@0 928 // return values.
michael@0 929 return rv;
michael@0 930 }
michael@0 931 NS_ENSURE_SUCCESS(rv, rv);
michael@0 932
michael@0 933 // Now start testing fixup -- since aTargetURIStr is a string, not
michael@0 934 // an nsIURI, we may well end up fixing it up before loading.
michael@0 935 // Note: This needs to stay in sync with the nsIURIFixup api.
michael@0 936 nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
michael@0 937 if (!fixup) {
michael@0 938 return rv;
michael@0 939 }
michael@0 940
michael@0 941 uint32_t flags[] = {
michael@0 942 nsIURIFixup::FIXUP_FLAG_NONE,
michael@0 943 nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS,
michael@0 944 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP,
michael@0 945 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI,
michael@0 946 nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP |
michael@0 947 nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI
michael@0 948 };
michael@0 949
michael@0 950 for (uint32_t i = 0; i < ArrayLength(flags); ++i) {
michael@0 951 rv = fixup->CreateFixupURI(aTargetURIStr, flags[i], nullptr,
michael@0 952 getter_AddRefs(target));
michael@0 953 NS_ENSURE_SUCCESS(rv, rv);
michael@0 954
michael@0 955 rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags);
michael@0 956 if (rv == NS_ERROR_DOM_BAD_URI) {
michael@0 957 // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected
michael@0 958 // return values.
michael@0 959 return rv;
michael@0 960 }
michael@0 961 NS_ENSURE_SUCCESS(rv, rv);
michael@0 962 }
michael@0 963
michael@0 964 return rv;
michael@0 965 }
michael@0 966
michael@0 967 bool
michael@0 968 nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal)
michael@0 969 {
michael@0 970 MOZ_ASSERT(aGlobal);
michael@0 971 MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal));
michael@0 972 AutoJSContext cx;
michael@0 973 JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false));
michael@0 974
michael@0 975 // Check the bits on the compartment private.
michael@0 976 return xpc::Scriptability::Get(aGlobal).Allowed();
michael@0 977 }
michael@0 978
michael@0 979 ///////////////// Principals ///////////////////////
michael@0 980 NS_IMETHODIMP
michael@0 981 nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **aSubjectPrincipal)
michael@0 982 {
michael@0 983 nsresult rv;
michael@0 984 *aSubjectPrincipal = doGetSubjectPrincipal(&rv);
michael@0 985 if (NS_SUCCEEDED(rv))
michael@0 986 NS_IF_ADDREF(*aSubjectPrincipal);
michael@0 987 return rv;
michael@0 988 }
michael@0 989
michael@0 990 nsIPrincipal*
michael@0 991 nsScriptSecurityManager::doGetSubjectPrincipal(nsresult* rv)
michael@0 992 {
michael@0 993 NS_PRECONDITION(rv, "Null out param");
michael@0 994 JSContext *cx = GetCurrentJSContext();
michael@0 995 if (!cx)
michael@0 996 {
michael@0 997 *rv = NS_OK;
michael@0 998 return nullptr;
michael@0 999 }
michael@0 1000 return GetSubjectPrincipal(cx, rv);
michael@0 1001 }
michael@0 1002
michael@0 1003 NS_IMETHODIMP
michael@0 1004 nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result)
michael@0 1005 {
michael@0 1006 NS_ADDREF(*result = mSystemPrincipal);
michael@0 1007
michael@0 1008 return NS_OK;
michael@0 1009 }
michael@0 1010
michael@0 1011 NS_IMETHODIMP
michael@0 1012 nsScriptSecurityManager::SubjectPrincipalIsSystem(bool* aIsSystem)
michael@0 1013 {
michael@0 1014 NS_ENSURE_ARG_POINTER(aIsSystem);
michael@0 1015 *aIsSystem = false;
michael@0 1016
michael@0 1017 if (!mSystemPrincipal)
michael@0 1018 return NS_OK;
michael@0 1019
michael@0 1020 nsCOMPtr<nsIPrincipal> subject;
michael@0 1021 nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject));
michael@0 1022 if (NS_FAILED(rv))
michael@0 1023 return rv;
michael@0 1024
michael@0 1025 if(!subject)
michael@0 1026 {
michael@0 1027 // No subject principal means no JS is running;
michael@0 1028 // this is the equivalent of system principal code
michael@0 1029 *aIsSystem = true;
michael@0 1030 return NS_OK;
michael@0 1031 }
michael@0 1032
michael@0 1033 return mSystemPrincipal->Equals(subject, aIsSystem);
michael@0 1034 }
michael@0 1035
michael@0 1036 nsresult
michael@0 1037 nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId,
michael@0 1038 bool aInMozBrowser,
michael@0 1039 nsIPrincipal **result)
michael@0 1040 {
michael@0 1041 // I _think_ it's safe to not create null principals here based on aURI.
michael@0 1042 // At least all the callers would do the right thing in those cases, as far
michael@0 1043 // as I can tell. --bz
michael@0 1044
michael@0 1045 nsCOMPtr<nsIURIWithPrincipal> uriPrinc = do_QueryInterface(aURI);
michael@0 1046 if (uriPrinc) {
michael@0 1047 nsCOMPtr<nsIPrincipal> principal;
michael@0 1048 uriPrinc->GetPrincipal(getter_AddRefs(principal));
michael@0 1049 if (!principal || principal == mSystemPrincipal) {
michael@0 1050 return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
michael@0 1051 }
michael@0 1052
michael@0 1053 principal.forget(result);
michael@0 1054
michael@0 1055 return NS_OK;
michael@0 1056 }
michael@0 1057
michael@0 1058 nsRefPtr<nsPrincipal> codebase = new nsPrincipal();
michael@0 1059 if (!codebase)
michael@0 1060 return NS_ERROR_OUT_OF_MEMORY;
michael@0 1061
michael@0 1062 nsresult rv = codebase->Init(aURI, aAppId, aInMozBrowser);
michael@0 1063 if (NS_FAILED(rv))
michael@0 1064 return rv;
michael@0 1065
michael@0 1066 NS_ADDREF(*result = codebase);
michael@0 1067
michael@0 1068 return NS_OK;
michael@0 1069 }
michael@0 1070
michael@0 1071 NS_IMETHODIMP
michael@0 1072 nsScriptSecurityManager::GetSimpleCodebasePrincipal(nsIURI* aURI,
michael@0 1073 nsIPrincipal** aPrincipal)
michael@0 1074 {
michael@0 1075 return GetCodebasePrincipalInternal(aURI,
michael@0 1076 nsIScriptSecurityManager::UNKNOWN_APP_ID,
michael@0 1077 false, aPrincipal);
michael@0 1078 }
michael@0 1079
michael@0 1080 NS_IMETHODIMP
michael@0 1081 nsScriptSecurityManager::GetNoAppCodebasePrincipal(nsIURI* aURI,
michael@0 1082 nsIPrincipal** aPrincipal)
michael@0 1083 {
michael@0 1084 return GetCodebasePrincipalInternal(aURI, nsIScriptSecurityManager::NO_APP_ID,
michael@0 1085 false, aPrincipal);
michael@0 1086 }
michael@0 1087
michael@0 1088 NS_IMETHODIMP
michael@0 1089 nsScriptSecurityManager::GetCodebasePrincipal(nsIURI* aURI,
michael@0 1090 nsIPrincipal** aPrincipal)
michael@0 1091 {
michael@0 1092 return GetNoAppCodebasePrincipal(aURI, aPrincipal);
michael@0 1093 }
michael@0 1094
michael@0 1095 NS_IMETHODIMP
michael@0 1096 nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI,
michael@0 1097 uint32_t aAppId,
michael@0 1098 bool aInMozBrowser,
michael@0 1099 nsIPrincipal** aPrincipal)
michael@0 1100 {
michael@0 1101 NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID,
michael@0 1102 NS_ERROR_INVALID_ARG);
michael@0 1103
michael@0 1104 return GetCodebasePrincipalInternal(aURI, aAppId, aInMozBrowser, aPrincipal);
michael@0 1105 }
michael@0 1106
michael@0 1107 NS_IMETHODIMP
michael@0 1108 nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI,
michael@0 1109 nsIDocShell* aDocShell,
michael@0 1110 nsIPrincipal** aPrincipal)
michael@0 1111 {
michael@0 1112 return GetCodebasePrincipalInternal(aURI,
michael@0 1113 aDocShell->GetAppId(),
michael@0 1114 aDocShell->GetIsInBrowserElement(),
michael@0 1115 aPrincipal);
michael@0 1116 }
michael@0 1117
michael@0 1118 nsresult
michael@0 1119 nsScriptSecurityManager::GetCodebasePrincipalInternal(nsIURI *aURI,
michael@0 1120 uint32_t aAppId,
michael@0 1121 bool aInMozBrowser,
michael@0 1122 nsIPrincipal **result)
michael@0 1123 {
michael@0 1124 NS_ENSURE_ARG(aURI);
michael@0 1125
michael@0 1126 bool inheritsPrincipal;
michael@0 1127 nsresult rv =
michael@0 1128 NS_URIChainHasFlags(aURI,
michael@0 1129 nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
michael@0 1130 &inheritsPrincipal);
michael@0 1131 if (NS_FAILED(rv) || inheritsPrincipal) {
michael@0 1132 return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result);
michael@0 1133 }
michael@0 1134
michael@0 1135 nsCOMPtr<nsIPrincipal> principal;
michael@0 1136 rv = CreateCodebasePrincipal(aURI, aAppId, aInMozBrowser,
michael@0 1137 getter_AddRefs(principal));
michael@0 1138 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1139 NS_IF_ADDREF(*result = principal);
michael@0 1140
michael@0 1141 return NS_OK;
michael@0 1142 }
michael@0 1143
michael@0 1144 nsIPrincipal*
michael@0 1145 nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
michael@0 1146 nsresult* rv)
michael@0 1147 {
michael@0 1148 *rv = NS_OK;
michael@0 1149 JSCompartment *compartment = js::GetContextCompartment(cx);
michael@0 1150
michael@0 1151 // The context should always be in a compartment, either one it has entered
michael@0 1152 // or the one associated with its global.
michael@0 1153 MOZ_ASSERT(!!compartment);
michael@0 1154
michael@0 1155 JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
michael@0 1156 return nsJSPrincipals::get(principals);
michael@0 1157 }
michael@0 1158
michael@0 1159 // static
michael@0 1160 nsIPrincipal*
michael@0 1161 nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj)
michael@0 1162 {
michael@0 1163 JSCompartment *compartment = js::GetObjectCompartment(aObj);
michael@0 1164 JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment);
michael@0 1165 return nsJSPrincipals::get(principals);
michael@0 1166 }
michael@0 1167
michael@0 1168 ////////////////////////////////////////////////
michael@0 1169 // Methods implementing nsIXPCSecurityManager //
michael@0 1170 ////////////////////////////////////////////////
michael@0 1171
michael@0 1172 NS_IMETHODIMP
michael@0 1173 nsScriptSecurityManager::CanCreateWrapper(JSContext *cx,
michael@0 1174 const nsIID &aIID,
michael@0 1175 nsISupports *aObj,
michael@0 1176 nsIClassInfo *aClassInfo)
michael@0 1177 {
michael@0 1178 // XXX Special case for nsIXPCException ?
michael@0 1179 ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nullptr);
michael@0 1180 if (objClassInfo.IsDOMClass())
michael@0 1181 {
michael@0 1182 return NS_OK;
michael@0 1183 }
michael@0 1184
michael@0 1185 // We give remote-XUL whitelisted domains a free pass here. See bug 932906.
michael@0 1186 if (!xpc::AllowXBLScope(js::GetContextCompartment(cx)))
michael@0 1187 {
michael@0 1188 return NS_OK;
michael@0 1189 }
michael@0 1190
michael@0 1191 if (SubjectIsPrivileged())
michael@0 1192 {
michael@0 1193 return NS_OK;
michael@0 1194 }
michael@0 1195
michael@0 1196 //-- Access denied, report an error
michael@0 1197 NS_ConvertUTF8toUTF16 strName("CreateWrapperDenied");
michael@0 1198 nsAutoCString origin;
michael@0 1199 nsresult rv2;
michael@0 1200 nsIPrincipal* subjectPrincipal = doGetSubjectPrincipal(&rv2);
michael@0 1201 if (NS_SUCCEEDED(rv2) && subjectPrincipal) {
michael@0 1202 GetPrincipalDomainOrigin(subjectPrincipal, origin);
michael@0 1203 }
michael@0 1204 NS_ConvertUTF8toUTF16 originUnicode(origin);
michael@0 1205 NS_ConvertUTF8toUTF16 classInfoName(objClassInfo.GetName());
michael@0 1206 const char16_t* formatStrings[] = {
michael@0 1207 classInfoName.get(),
michael@0 1208 originUnicode.get()
michael@0 1209 };
michael@0 1210 uint32_t length = ArrayLength(formatStrings);
michael@0 1211 if (originUnicode.IsEmpty()) {
michael@0 1212 --length;
michael@0 1213 } else {
michael@0 1214 strName.AppendLiteral("ForOrigin");
michael@0 1215 }
michael@0 1216 nsXPIDLString errorMsg;
michael@0 1217 // We need to keep our existing failure rv and not override it
michael@0 1218 // with a likely success code from the following string bundle
michael@0 1219 // call in order to throw the correct security exception later.
michael@0 1220 rv2 = sStrBundle->FormatStringFromName(strName.get(),
michael@0 1221 formatStrings,
michael@0 1222 length,
michael@0 1223 getter_Copies(errorMsg));
michael@0 1224 NS_ENSURE_SUCCESS(rv2, rv2);
michael@0 1225
michael@0 1226 SetPendingException(cx, errorMsg.get());
michael@0 1227 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
michael@0 1228 }
michael@0 1229
michael@0 1230 NS_IMETHODIMP
michael@0 1231 nsScriptSecurityManager::CanCreateInstance(JSContext *cx,
michael@0 1232 const nsCID &aCID)
michael@0 1233 {
michael@0 1234 if (SubjectIsPrivileged()) {
michael@0 1235 return NS_OK;
michael@0 1236 }
michael@0 1237
michael@0 1238 //-- Access denied, report an error
michael@0 1239 nsAutoCString errorMsg("Permission denied to create instance of class. CID=");
michael@0 1240 char cidStr[NSID_LENGTH];
michael@0 1241 aCID.ToProvidedString(cidStr);
michael@0 1242 errorMsg.Append(cidStr);
michael@0 1243 SetPendingException(cx, errorMsg.get());
michael@0 1244 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
michael@0 1245 }
michael@0 1246
michael@0 1247 NS_IMETHODIMP
michael@0 1248 nsScriptSecurityManager::CanGetService(JSContext *cx,
michael@0 1249 const nsCID &aCID)
michael@0 1250 {
michael@0 1251 if (SubjectIsPrivileged()) {
michael@0 1252 return NS_OK;
michael@0 1253 }
michael@0 1254
michael@0 1255 //-- Access denied, report an error
michael@0 1256 nsAutoCString errorMsg("Permission denied to get service. CID=");
michael@0 1257 char cidStr[NSID_LENGTH];
michael@0 1258 aCID.ToProvidedString(cidStr);
michael@0 1259 errorMsg.Append(cidStr);
michael@0 1260 SetPendingException(cx, errorMsg.get());
michael@0 1261 return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED;
michael@0 1262 }
michael@0 1263
michael@0 1264 /////////////////////////////////////////////
michael@0 1265 // Method implementing nsIChannelEventSink //
michael@0 1266 /////////////////////////////////////////////
michael@0 1267 NS_IMETHODIMP
michael@0 1268 nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel,
michael@0 1269 nsIChannel* newChannel,
michael@0 1270 uint32_t redirFlags,
michael@0 1271 nsIAsyncVerifyRedirectCallback *cb)
michael@0 1272 {
michael@0 1273 nsCOMPtr<nsIPrincipal> oldPrincipal;
michael@0 1274 GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal));
michael@0 1275
michael@0 1276 nsCOMPtr<nsIURI> newURI;
michael@0 1277 newChannel->GetURI(getter_AddRefs(newURI));
michael@0 1278 nsCOMPtr<nsIURI> newOriginalURI;
michael@0 1279 newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI));
michael@0 1280
michael@0 1281 NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI);
michael@0 1282
michael@0 1283 const uint32_t flags =
michael@0 1284 nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT |
michael@0 1285 nsIScriptSecurityManager::DISALLOW_SCRIPT;
michael@0 1286 nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags);
michael@0 1287 if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) {
michael@0 1288 rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags);
michael@0 1289 }
michael@0 1290
michael@0 1291 if (NS_FAILED(rv))
michael@0 1292 return rv;
michael@0 1293
michael@0 1294 cb->OnRedirectVerifyCallback(NS_OK);
michael@0 1295 return NS_OK;
michael@0 1296 }
michael@0 1297
michael@0 1298
michael@0 1299 /////////////////////////////////////
michael@0 1300 // Method implementing nsIObserver //
michael@0 1301 /////////////////////////////////////
michael@0 1302 const char sJSEnabledPrefName[] = "javascript.enabled";
michael@0 1303 const char sFileOriginPolicyPrefName[] =
michael@0 1304 "security.fileuri.strict_origin_policy";
michael@0 1305
michael@0 1306 static const char* kObservedPrefs[] = {
michael@0 1307 sJSEnabledPrefName,
michael@0 1308 sFileOriginPolicyPrefName,
michael@0 1309 "capability.policy.",
michael@0 1310 nullptr
michael@0 1311 };
michael@0 1312
michael@0 1313
michael@0 1314 NS_IMETHODIMP
michael@0 1315 nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic,
michael@0 1316 const char16_t* aMessage)
michael@0 1317 {
michael@0 1318 ScriptSecurityPrefChanged();
michael@0 1319 return NS_OK;
michael@0 1320 }
michael@0 1321
michael@0 1322 /////////////////////////////////////////////
michael@0 1323 // Constructor, Destructor, Initialization //
michael@0 1324 /////////////////////////////////////////////
michael@0 1325 nsScriptSecurityManager::nsScriptSecurityManager(void)
michael@0 1326 : mPrefInitialized(false)
michael@0 1327 , mIsJavaScriptEnabled(false)
michael@0 1328 {
michael@0 1329 static_assert(sizeof(intptr_t) == sizeof(void*),
michael@0 1330 "intptr_t and void* have different lengths on this platform. "
michael@0 1331 "This may cause a security failure with the SecurityLevel union.");
michael@0 1332 }
michael@0 1333
michael@0 1334 nsresult nsScriptSecurityManager::Init()
michael@0 1335 {
michael@0 1336 nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService);
michael@0 1337 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1338
michael@0 1339 InitPrefs();
michael@0 1340
michael@0 1341 nsCOMPtr<nsIStringBundleService> bundleService =
michael@0 1342 mozilla::services::GetStringBundleService();
michael@0 1343 if (!bundleService)
michael@0 1344 return NS_ERROR_FAILURE;
michael@0 1345
michael@0 1346 rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle);
michael@0 1347 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1348
michael@0 1349 // Create our system principal singleton
michael@0 1350 nsRefPtr<nsSystemPrincipal> system = new nsSystemPrincipal();
michael@0 1351 NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY);
michael@0 1352
michael@0 1353 mSystemPrincipal = system;
michael@0 1354
michael@0 1355 //-- Register security check callback in the JS engine
michael@0 1356 // Currently this is used to control access to function.caller
michael@0 1357 rv = nsXPConnect::XPConnect()->GetRuntime(&sRuntime);
michael@0 1358 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1359
michael@0 1360 static const JSSecurityCallbacks securityCallbacks = {
michael@0 1361 ContentSecurityPolicyPermitsJSAction,
michael@0 1362 JSPrincipalsSubsume,
michael@0 1363 };
michael@0 1364
michael@0 1365 MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime));
michael@0 1366 JS_SetSecurityCallbacks(sRuntime, &securityCallbacks);
michael@0 1367 JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy);
michael@0 1368
michael@0 1369 JS_SetTrustedPrincipals(sRuntime, system);
michael@0 1370
michael@0 1371 return NS_OK;
michael@0 1372 }
michael@0 1373
michael@0 1374 static StaticRefPtr<nsScriptSecurityManager> gScriptSecMan;
michael@0 1375
michael@0 1376 nsScriptSecurityManager::~nsScriptSecurityManager(void)
michael@0 1377 {
michael@0 1378 Preferences::RemoveObservers(this, kObservedPrefs);
michael@0 1379 if (mDomainPolicy)
michael@0 1380 mDomainPolicy->Deactivate();
michael@0 1381 MOZ_ASSERT(!mDomainPolicy);
michael@0 1382 }
michael@0 1383
michael@0 1384 void
michael@0 1385 nsScriptSecurityManager::Shutdown()
michael@0 1386 {
michael@0 1387 if (sRuntime) {
michael@0 1388 JS_SetSecurityCallbacks(sRuntime, nullptr);
michael@0 1389 JS_SetTrustedPrincipals(sRuntime, nullptr);
michael@0 1390 sRuntime = nullptr;
michael@0 1391 }
michael@0 1392
michael@0 1393 NS_IF_RELEASE(sIOService);
michael@0 1394 NS_IF_RELEASE(sStrBundle);
michael@0 1395 }
michael@0 1396
michael@0 1397 nsScriptSecurityManager *
michael@0 1398 nsScriptSecurityManager::GetScriptSecurityManager()
michael@0 1399 {
michael@0 1400 if (!gScriptSecMan && nsXPConnect::XPConnect())
michael@0 1401 {
michael@0 1402 nsRefPtr<nsScriptSecurityManager> ssManager = new nsScriptSecurityManager();
michael@0 1403
michael@0 1404 nsresult rv;
michael@0 1405 rv = ssManager->Init();
michael@0 1406 if (NS_FAILED(rv)) {
michael@0 1407 return nullptr;
michael@0 1408 }
michael@0 1409
michael@0 1410 rv = nsXPConnect::XPConnect()->
michael@0 1411 SetDefaultSecurityManager(ssManager);
michael@0 1412 if (NS_FAILED(rv)) {
michael@0 1413 NS_WARNING("Failed to install xpconnect security manager!");
michael@0 1414 return nullptr;
michael@0 1415 }
michael@0 1416
michael@0 1417 ClearOnShutdown(&gScriptSecMan);
michael@0 1418 gScriptSecMan = ssManager;
michael@0 1419 }
michael@0 1420 return gScriptSecMan;
michael@0 1421 }
michael@0 1422
michael@0 1423 // Currently this nsGenericFactory constructor is used only from FastLoad
michael@0 1424 // (XPCOM object deserialization) code, when "creating" the system principal
michael@0 1425 // singleton.
michael@0 1426 nsSystemPrincipal *
michael@0 1427 nsScriptSecurityManager::SystemPrincipalSingletonConstructor()
michael@0 1428 {
michael@0 1429 nsIPrincipal *sysprin = nullptr;
michael@0 1430 if (gScriptSecMan)
michael@0 1431 NS_ADDREF(sysprin = gScriptSecMan->mSystemPrincipal);
michael@0 1432 return static_cast<nsSystemPrincipal*>(sysprin);
michael@0 1433 }
michael@0 1434
michael@0 1435 struct IsWhitespace {
michael@0 1436 static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); };
michael@0 1437 };
michael@0 1438 struct IsWhitespaceOrComma {
michael@0 1439 static bool Test(char aChar) { return aChar == ',' || NS_IsAsciiWhitespace(aChar); };
michael@0 1440 };
michael@0 1441
michael@0 1442 template <typename Predicate>
michael@0 1443 uint32_t SkipPast(const nsCString& str, uint32_t base)
michael@0 1444 {
michael@0 1445 while (base < str.Length() && Predicate::Test(str[base])) {
michael@0 1446 ++base;
michael@0 1447 }
michael@0 1448 return base;
michael@0 1449 }
michael@0 1450
michael@0 1451 template <typename Predicate>
michael@0 1452 uint32_t SkipUntil(const nsCString& str, uint32_t base)
michael@0 1453 {
michael@0 1454 while (base < str.Length() && !Predicate::Test(str[base])) {
michael@0 1455 ++base;
michael@0 1456 }
michael@0 1457 return base;
michael@0 1458 }
michael@0 1459
michael@0 1460 inline void
michael@0 1461 nsScriptSecurityManager::ScriptSecurityPrefChanged()
michael@0 1462 {
michael@0 1463 MOZ_ASSERT(mPrefInitialized);
michael@0 1464 mIsJavaScriptEnabled =
michael@0 1465 Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled);
michael@0 1466 sStrictFileOriginPolicy =
michael@0 1467 Preferences::GetBool(sFileOriginPolicyPrefName, false);
michael@0 1468
michael@0 1469 //
michael@0 1470 // Rebuild the set of principals for which we allow file:// URI loads. This
michael@0 1471 // implements a small subset of an old pref-based CAPS people that people
michael@0 1472 // have come to depend on. See bug 995943.
michael@0 1473 //
michael@0 1474
michael@0 1475 mFileURIWhitelist.Clear();
michael@0 1476 auto policies = mozilla::Preferences::GetCString("capability.policy.policynames");
michael@0 1477 for (uint32_t base = SkipPast<IsWhitespaceOrComma>(policies, 0), bound = 0;
michael@0 1478 base < policies.Length();
michael@0 1479 base = SkipPast<IsWhitespaceOrComma>(policies, bound))
michael@0 1480 {
michael@0 1481 // Grab the current policy name.
michael@0 1482 bound = SkipUntil<IsWhitespaceOrComma>(policies, base);
michael@0 1483 auto policyName = Substring(policies, base, bound - base);
michael@0 1484
michael@0 1485 // Figure out if this policy allows loading file:// URIs. If not, we can skip it.
michael@0 1486 nsCString checkLoadURIPrefName = NS_LITERAL_CSTRING("capability.policy.") +
michael@0 1487 policyName +
michael@0 1488 NS_LITERAL_CSTRING(".checkloaduri.enabled");
michael@0 1489 if (!Preferences::GetString(checkLoadURIPrefName.get()).LowerCaseEqualsLiteral("allaccess")) {
michael@0 1490 continue;
michael@0 1491 }
michael@0 1492
michael@0 1493 // Grab the list of domains associated with this policy.
michael@0 1494 nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") +
michael@0 1495 policyName +
michael@0 1496 NS_LITERAL_CSTRING(".sites");
michael@0 1497 auto siteList = Preferences::GetCString(domainPrefName.get());
michael@0 1498 AddSitesToFileURIWhitelist(siteList);
michael@0 1499 }
michael@0 1500 }
michael@0 1501
michael@0 1502 void
michael@0 1503 nsScriptSecurityManager::AddSitesToFileURIWhitelist(const nsCString& aSiteList)
michael@0 1504 {
michael@0 1505 for (uint32_t base = SkipPast<IsWhitespace>(aSiteList, 0), bound = 0;
michael@0 1506 base < aSiteList.Length();
michael@0 1507 base = SkipPast<IsWhitespace>(aSiteList, bound))
michael@0 1508 {
michael@0 1509 // Grab the current site.
michael@0 1510 bound = SkipUntil<IsWhitespace>(aSiteList, base);
michael@0 1511 nsAutoCString site(Substring(aSiteList, base, bound - base));
michael@0 1512
michael@0 1513 // Check if the URI is schemeless. If so, add both http and https.
michael@0 1514 nsAutoCString unused;
michael@0 1515 if (NS_FAILED(sIOService->ExtractScheme(site, unused))) {
michael@0 1516 AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("http://") + site);
michael@0 1517 AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("https://") + site);
michael@0 1518 continue;
michael@0 1519 }
michael@0 1520
michael@0 1521 // Convert it to a URI and add it to our list.
michael@0 1522 nsCOMPtr<nsIURI> uri;
michael@0 1523 nsresult rv = NS_NewURI(getter_AddRefs(uri), site, nullptr, nullptr, sIOService);
michael@0 1524 if (NS_SUCCEEDED(rv)) {
michael@0 1525 mFileURIWhitelist.AppendElement(uri);
michael@0 1526 } else {
michael@0 1527 nsCOMPtr<nsIConsoleService> console(do_GetService("@mozilla.org/consoleservice;1"));
michael@0 1528 if (console) {
michael@0 1529 nsAutoString msg = NS_LITERAL_STRING("Unable to to add site to file:// URI whitelist: ") +
michael@0 1530 NS_ConvertASCIItoUTF16(site);
michael@0 1531 console->LogStringMessage(msg.get());
michael@0 1532 }
michael@0 1533 }
michael@0 1534 }
michael@0 1535 }
michael@0 1536
michael@0 1537 nsresult
michael@0 1538 nsScriptSecurityManager::InitPrefs()
michael@0 1539 {
michael@0 1540 nsIPrefBranch* branch = Preferences::GetRootBranch();
michael@0 1541 NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE);
michael@0 1542
michael@0 1543 mPrefInitialized = true;
michael@0 1544
michael@0 1545 // Set the initial value of the "javascript.enabled" prefs
michael@0 1546 ScriptSecurityPrefChanged();
michael@0 1547
michael@0 1548 // set observer callbacks in case the value of the prefs change
michael@0 1549 Preferences::AddStrongObservers(this, kObservedPrefs);
michael@0 1550
michael@0 1551 return NS_OK;
michael@0 1552 }
michael@0 1553
michael@0 1554 namespace mozilla {
michael@0 1555
michael@0 1556 void
michael@0 1557 GetJarPrefix(uint32_t aAppId, bool aInMozBrowser, nsACString& aJarPrefix)
michael@0 1558 {
michael@0 1559 MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
michael@0 1560
michael@0 1561 if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
michael@0 1562 aAppId = nsIScriptSecurityManager::NO_APP_ID;
michael@0 1563 }
michael@0 1564
michael@0 1565 aJarPrefix.Truncate();
michael@0 1566
michael@0 1567 // Fallback.
michael@0 1568 if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInMozBrowser) {
michael@0 1569 return;
michael@0 1570 }
michael@0 1571
michael@0 1572 // aJarPrefix = appId + "+" + { 't', 'f' } + "+";
michael@0 1573 aJarPrefix.AppendInt(aAppId);
michael@0 1574 aJarPrefix.Append('+');
michael@0 1575 aJarPrefix.Append(aInMozBrowser ? 't' : 'f');
michael@0 1576 aJarPrefix.Append('+');
michael@0 1577
michael@0 1578 return;
michael@0 1579 }
michael@0 1580
michael@0 1581 } // namespace mozilla
michael@0 1582
michael@0 1583 NS_IMETHODIMP
michael@0 1584 nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId,
michael@0 1585 bool aInMozBrowser,
michael@0 1586 nsACString& aJarPrefix)
michael@0 1587 {
michael@0 1588 MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID);
michael@0 1589
michael@0 1590 mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix);
michael@0 1591 return NS_OK;
michael@0 1592 }
michael@0 1593
michael@0 1594 NS_IMETHODIMP
michael@0 1595 nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv)
michael@0 1596 {
michael@0 1597 *aRv = !!mDomainPolicy;
michael@0 1598 return NS_OK;
michael@0 1599 }
michael@0 1600
michael@0 1601 NS_IMETHODIMP
michael@0 1602 nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv)
michael@0 1603 {
michael@0 1604 // We only allow one domain policy at a time. The holder of the previous
michael@0 1605 // policy must explicitly deactivate it first.
michael@0 1606 if (mDomainPolicy) {
michael@0 1607 return NS_ERROR_SERVICE_NOT_AVAILABLE;
michael@0 1608 }
michael@0 1609
michael@0 1610 mDomainPolicy = new DomainPolicy();
michael@0 1611 nsCOMPtr<nsIDomainPolicy> ptr = mDomainPolicy;
michael@0 1612 ptr.forget(aRv);
michael@0 1613 return NS_OK;
michael@0 1614 }
michael@0 1615
michael@0 1616 // Intentionally non-scriptable. Script must have a reference to the
michael@0 1617 // nsIDomainPolicy to deactivate it.
michael@0 1618 void
michael@0 1619 nsScriptSecurityManager::DeactivateDomainPolicy()
michael@0 1620 {
michael@0 1621 mDomainPolicy = nullptr;
michael@0 1622 }
michael@0 1623
michael@0 1624 NS_IMETHODIMP
michael@0 1625 nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv)
michael@0 1626 {
michael@0 1627 nsresult rv;
michael@0 1628
michael@0 1629 // Compute our rule. If we don't have any domain policy set up that might
michael@0 1630 // provide exceptions to this rule, we're done.
michael@0 1631 *aRv = mIsJavaScriptEnabled;
michael@0 1632 if (!mDomainPolicy) {
michael@0 1633 return NS_OK;
michael@0 1634 }
michael@0 1635
michael@0 1636 // We have a domain policy. Grab the appropriate set of exceptions to the
michael@0 1637 // rule (either the blacklist or the whitelist, depending on whether script
michael@0 1638 // is enabled or disabled by default).
michael@0 1639 nsCOMPtr<nsIDomainSet> exceptions;
michael@0 1640 nsCOMPtr<nsIDomainSet> superExceptions;
michael@0 1641 if (*aRv) {
michael@0 1642 mDomainPolicy->GetBlacklist(getter_AddRefs(exceptions));
michael@0 1643 mDomainPolicy->GetSuperBlacklist(getter_AddRefs(superExceptions));
michael@0 1644 } else {
michael@0 1645 mDomainPolicy->GetWhitelist(getter_AddRefs(exceptions));
michael@0 1646 mDomainPolicy->GetSuperWhitelist(getter_AddRefs(superExceptions));
michael@0 1647 }
michael@0 1648
michael@0 1649 bool contains;
michael@0 1650 rv = exceptions->Contains(aURI, &contains);
michael@0 1651 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1652 if (contains) {
michael@0 1653 *aRv = !*aRv;
michael@0 1654 return NS_OK;
michael@0 1655 }
michael@0 1656 rv = superExceptions->ContainsSuperDomain(aURI, &contains);
michael@0 1657 NS_ENSURE_SUCCESS(rv, rv);
michael@0 1658 if (contains) {
michael@0 1659 *aRv = !*aRv;
michael@0 1660 }
michael@0 1661
michael@0 1662 return NS_OK;
michael@0 1663 }

mercurial