michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ michael@0: /* vim: set ts=4 et sw=4 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "nsScriptSecurityManager.h" michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: michael@0: #include "js/OldDebugAPI.h" michael@0: #include "xpcprivate.h" michael@0: #include "XPCWrapper.h" michael@0: #include "nsIServiceManager.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIURL.h" michael@0: #include "nsINestedURI.h" michael@0: #include "nspr.h" michael@0: #include "nsJSPrincipals.h" michael@0: #include "nsSystemPrincipal.h" michael@0: #include "nsPrincipal.h" michael@0: #include "nsNullPrincipal.h" michael@0: #include "DomainPolicy.h" michael@0: #include "nsXPIDLString.h" michael@0: #include "nsCRT.h" michael@0: #include "nsCRTGlue.h" michael@0: #include "nsError.h" michael@0: #include "nsDOMCID.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIXPCSecurityManager.h" michael@0: #include "nsTextFormatter.h" michael@0: #include "nsIStringBundle.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsIEffectiveTLDService.h" michael@0: #include "nsIProperties.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsIFile.h" michael@0: #include "nsIFileURL.h" michael@0: #include "nsIZipReader.h" michael@0: #include "nsIXPConnect.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsIDocShell.h" michael@0: #include "nsIPrompt.h" michael@0: #include "nsIWindowWatcher.h" michael@0: #include "nsIConsoleService.h" michael@0: #include "nsIJSRuntimeService.h" michael@0: #include "nsIObserverService.h" michael@0: #include "nsIContent.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsDOMJSUtils.h" michael@0: #include "nsAboutProtocolUtils.h" michael@0: #include "nsIClassInfo.h" michael@0: #include "nsIURIFixup.h" michael@0: #include "nsCDefaultURIFixup.h" michael@0: #include "nsIChromeRegistry.h" michael@0: #include "nsIContentSecurityPolicy.h" michael@0: #include "nsIAsyncVerifyRedirectCallback.h" michael@0: #include "mozilla/Preferences.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: #include michael@0: #include "mozilla/ClearOnShutdown.h" michael@0: #include "mozilla/StaticPtr.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsJSUtils.h" michael@0: michael@0: // This should be probably defined on some other place... but I couldn't find it michael@0: #define WEBAPPS_PERM_NAME "webapps-manage" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: michael@0: static NS_DEFINE_CID(kZipReaderCID, NS_ZIPREADER_CID); michael@0: michael@0: nsIIOService *nsScriptSecurityManager::sIOService = nullptr; michael@0: nsIStringBundle *nsScriptSecurityManager::sStrBundle = nullptr; michael@0: JSRuntime *nsScriptSecurityManager::sRuntime = 0; michael@0: bool nsScriptSecurityManager::sStrictFileOriginPolicy = true; michael@0: michael@0: bool michael@0: nsScriptSecurityManager::SubjectIsPrivileged() michael@0: { michael@0: JSContext *cx = GetCurrentJSContext(); michael@0: if (cx && xpc::IsUniversalXPConnectEnabled(cx)) michael@0: return true; michael@0: bool isSystem = false; michael@0: return NS_SUCCEEDED(SubjectPrincipalIsSystem(&isSystem)) && isSystem; michael@0: } michael@0: michael@0: /////////////////////////// michael@0: // Convenience Functions // michael@0: /////////////////////////// michael@0: // Result of this function should not be freed. michael@0: static inline const char16_t * michael@0: IDToString(JSContext *cx, jsid id_) michael@0: { michael@0: JS::RootedId id(cx, id_); michael@0: if (JSID_IS_STRING(id)) michael@0: return JS_GetInternedStringChars(JSID_TO_STRING(id)); michael@0: michael@0: JS::Rooted idval(cx); michael@0: if (!JS_IdToValue(cx, id, &idval)) michael@0: return nullptr; michael@0: JSString *str = JS::ToString(cx, idval); michael@0: if(!str) michael@0: return nullptr; michael@0: return JS_GetStringCharsZ(cx, str); michael@0: } michael@0: michael@0: class nsAutoInPrincipalDomainOriginSetter { michael@0: public: michael@0: nsAutoInPrincipalDomainOriginSetter() { michael@0: ++sInPrincipalDomainOrigin; michael@0: } michael@0: ~nsAutoInPrincipalDomainOriginSetter() { michael@0: --sInPrincipalDomainOrigin; michael@0: } michael@0: static uint32_t sInPrincipalDomainOrigin; michael@0: }; michael@0: uint32_t nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin; michael@0: michael@0: static michael@0: nsresult michael@0: GetOriginFromURI(nsIURI* aURI, nsACString& aOrigin) michael@0: { michael@0: if (nsAutoInPrincipalDomainOriginSetter::sInPrincipalDomainOrigin > 1) { michael@0: // Allow a single recursive call to GetPrincipalDomainOrigin, since that michael@0: // might be happening on a different principal from the first call. But michael@0: // after that, cut off the recursion; it just indicates that something michael@0: // we're doing in this method causes us to reenter a security check here. michael@0: return NS_ERROR_NOT_AVAILABLE; michael@0: } michael@0: michael@0: nsAutoInPrincipalDomainOriginSetter autoSetter; michael@0: michael@0: nsCOMPtr uri = NS_GetInnermostURI(aURI); michael@0: NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); michael@0: michael@0: nsAutoCString hostPort; michael@0: michael@0: nsresult rv = uri->GetHostPort(hostPort); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsAutoCString scheme; michael@0: rv = uri->GetScheme(scheme); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: aOrigin = scheme + NS_LITERAL_CSTRING("://") + hostPort; michael@0: } michael@0: else { michael@0: // Some URIs (e.g., nsSimpleURI) don't support host. Just michael@0: // get the full spec. michael@0: rv = uri->GetSpec(aOrigin); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static michael@0: nsresult michael@0: GetPrincipalDomainOrigin(nsIPrincipal* aPrincipal, michael@0: nsACString& aOrigin) michael@0: { michael@0: michael@0: nsCOMPtr uri; michael@0: aPrincipal->GetDomain(getter_AddRefs(uri)); michael@0: if (!uri) { michael@0: aPrincipal->GetURI(getter_AddRefs(uri)); michael@0: } michael@0: NS_ENSURE_TRUE(uri, NS_ERROR_UNEXPECTED); michael@0: michael@0: return GetOriginFromURI(uri, aOrigin); michael@0: } michael@0: michael@0: inline void SetPendingException(JSContext *cx, const char *aMsg) michael@0: { michael@0: JS_ReportError(cx, "%s", aMsg); michael@0: } michael@0: michael@0: inline void SetPendingException(JSContext *cx, const char16_t *aMsg) michael@0: { michael@0: JS_ReportError(cx, "%hs", aMsg); michael@0: } michael@0: michael@0: // Helper class to get stuff from the ClassInfo and not waste extra time with michael@0: // virtual method calls for things it has already gotten michael@0: class ClassInfoData michael@0: { michael@0: public: michael@0: ClassInfoData(nsIClassInfo *aClassInfo, const char *aName) michael@0: : mClassInfo(aClassInfo), michael@0: mName(const_cast(aName)), michael@0: mDidGetFlags(false), michael@0: mMustFreeName(false) michael@0: { michael@0: } michael@0: michael@0: ~ClassInfoData() michael@0: { michael@0: if (mMustFreeName) michael@0: nsMemory::Free(mName); michael@0: } michael@0: michael@0: uint32_t GetFlags() michael@0: { michael@0: if (!mDidGetFlags) { michael@0: if (mClassInfo) { michael@0: nsresult rv = mClassInfo->GetFlags(&mFlags); michael@0: if (NS_FAILED(rv)) { michael@0: mFlags = 0; michael@0: } michael@0: } else { michael@0: mFlags = 0; michael@0: } michael@0: michael@0: mDidGetFlags = true; michael@0: } michael@0: michael@0: return mFlags; michael@0: } michael@0: michael@0: bool IsDOMClass() michael@0: { michael@0: return !!(GetFlags() & nsIClassInfo::DOM_OBJECT); michael@0: } michael@0: michael@0: const char* GetName() michael@0: { michael@0: if (!mName) { michael@0: if (mClassInfo) { michael@0: mClassInfo->GetClassDescription(&mName); michael@0: } michael@0: michael@0: if (mName) { michael@0: mMustFreeName = true; michael@0: } else { michael@0: mName = const_cast("UnnamedClass"); michael@0: } michael@0: } michael@0: michael@0: return mName; michael@0: } michael@0: michael@0: private: michael@0: nsIClassInfo *mClassInfo; // WEAK michael@0: uint32_t mFlags; michael@0: char *mName; michael@0: bool mDidGetFlags; michael@0: bool mMustFreeName; michael@0: }; michael@0: michael@0: JSContext * michael@0: nsScriptSecurityManager::GetCurrentJSContext() michael@0: { michael@0: // Get JSContext from stack. michael@0: return nsXPConnect::XPConnect()->GetCurrentJSContext(); michael@0: } michael@0: michael@0: JSContext * michael@0: nsScriptSecurityManager::GetSafeJSContext() michael@0: { michael@0: // Get JSContext from stack. michael@0: return nsXPConnect::XPConnect()->GetSafeJSContext(); michael@0: } michael@0: michael@0: /* static */ michael@0: bool michael@0: nsScriptSecurityManager::SecurityCompareURIs(nsIURI* aSourceURI, michael@0: nsIURI* aTargetURI) michael@0: { michael@0: return NS_SecurityCompareURIs(aSourceURI, aTargetURI, sStrictFileOriginPolicy); michael@0: } michael@0: michael@0: // SecurityHashURI is consistent with SecurityCompareURIs because NS_SecurityHashURI michael@0: // is consistent with NS_SecurityCompareURIs. See nsNetUtil.h. michael@0: uint32_t michael@0: nsScriptSecurityManager::SecurityHashURI(nsIURI* aURI) michael@0: { michael@0: return NS_SecurityHashURI(aURI); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetChannelPrincipal(nsIChannel* aChannel, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: NS_PRECONDITION(aChannel, "Must have channel!"); michael@0: nsCOMPtr owner; michael@0: aChannel->GetOwner(getter_AddRefs(owner)); michael@0: if (owner) { michael@0: CallQueryInterface(owner, aPrincipal); michael@0: if (*aPrincipal) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // OK, get the principal from the URI. Make sure this does the same thing michael@0: // as nsDocument::Reset and XULDocument::StartDocumentLoad. michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: nsCOMPtr docShell; michael@0: NS_QueryNotificationCallbacks(aChannel, docShell); michael@0: michael@0: if (docShell) { michael@0: return GetDocShellCodebasePrincipal(uri, docShell, aPrincipal); michael@0: } michael@0: michael@0: return GetCodebasePrincipalInternal(uri, UNKNOWN_APP_ID, michael@0: /* isInBrowserElement */ false, aPrincipal); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::IsSystemPrincipal(nsIPrincipal* aPrincipal, michael@0: bool* aIsSystem) michael@0: { michael@0: *aIsSystem = (aPrincipal == mSystemPrincipal); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP_(nsIPrincipal *) michael@0: nsScriptSecurityManager::GetCxSubjectPrincipal(JSContext *cx) michael@0: { michael@0: NS_ASSERTION(cx == GetCurrentJSContext(), michael@0: "Uh, cx is not the current JS context!"); michael@0: michael@0: nsresult rv = NS_ERROR_FAILURE; michael@0: nsIPrincipal *principal = GetSubjectPrincipal(cx, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return nullptr; michael@0: michael@0: return principal; michael@0: } michael@0: michael@0: ///////////////////////////// michael@0: // nsScriptSecurityManager // michael@0: ///////////////////////////// michael@0: michael@0: //////////////////////////////////// michael@0: // Methods implementing ISupports // michael@0: //////////////////////////////////// michael@0: NS_IMPL_ISUPPORTS(nsScriptSecurityManager, michael@0: nsIScriptSecurityManager, michael@0: nsIXPCSecurityManager, michael@0: nsIChannelEventSink, michael@0: nsIObserver) michael@0: michael@0: /////////////////////////////////////////////////// michael@0: // Methods implementing nsIScriptSecurityManager // michael@0: /////////////////////////////////////////////////// michael@0: michael@0: ///////////////// Security Checks ///////////////// michael@0: michael@0: bool michael@0: nsScriptSecurityManager::ContentSecurityPolicyPermitsJSAction(JSContext *cx) michael@0: { michael@0: // Get the security manager michael@0: nsScriptSecurityManager *ssm = michael@0: nsScriptSecurityManager::GetScriptSecurityManager(); michael@0: michael@0: NS_ASSERTION(ssm, "Failed to get security manager service"); michael@0: if (!ssm) michael@0: return false; michael@0: michael@0: nsresult rv; michael@0: nsIPrincipal* subjectPrincipal = ssm->GetSubjectPrincipal(cx, &rv); michael@0: michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get nsIPrincipal from js context"); michael@0: if (NS_FAILED(rv)) michael@0: return false; // Not just absence of principal, but failure. michael@0: michael@0: if (!subjectPrincipal) michael@0: return true; michael@0: michael@0: nsCOMPtr csp; michael@0: rv = subjectPrincipal->GetCsp(getter_AddRefs(csp)); michael@0: NS_ASSERTION(NS_SUCCEEDED(rv), "CSP: Failed to get CSP from principal."); michael@0: michael@0: // don't do anything unless there's a CSP michael@0: if (!csp) michael@0: return true; michael@0: michael@0: bool evalOK = true; michael@0: bool reportViolation = false; michael@0: rv = csp->GetAllowsEval(&reportViolation, &evalOK); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: { michael@0: NS_WARNING("CSP: failed to get allowsEval"); michael@0: return true; // fail open to not break sites. michael@0: } michael@0: michael@0: if (reportViolation) { michael@0: nsAutoString fileName; michael@0: unsigned lineNum = 0; michael@0: NS_NAMED_LITERAL_STRING(scriptSample, "call to eval() or related function blocked by CSP"); michael@0: michael@0: JS::AutoFilename scriptFilename; michael@0: if (JS::DescribeScriptedCaller(cx, &scriptFilename, &lineNum)) { michael@0: if (const char *file = scriptFilename.get()) { michael@0: CopyUTF8toUTF16(nsDependentCString(file), fileName); michael@0: } michael@0: } michael@0: csp->LogViolationDetails(nsIContentSecurityPolicy::VIOLATION_TYPE_EVAL, michael@0: fileName, michael@0: scriptSample, michael@0: lineNum, michael@0: EmptyString(), michael@0: EmptyString()); michael@0: } michael@0: michael@0: return evalOK; michael@0: } michael@0: michael@0: // static michael@0: bool michael@0: nsScriptSecurityManager::JSPrincipalsSubsume(JSPrincipals *first, michael@0: JSPrincipals *second) michael@0: { michael@0: return nsJSPrincipals::get(first)->Subsumes(nsJSPrincipals::get(second)); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CheckSameOrigin(JSContext* cx, michael@0: nsIURI* aTargetURI) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Get a context if necessary michael@0: if (!cx) michael@0: { michael@0: cx = GetCurrentJSContext(); michael@0: if (!cx) michael@0: return NS_OK; // No JS context, so allow access michael@0: } michael@0: michael@0: // Get a principal from the context michael@0: nsIPrincipal* sourcePrincipal = GetSubjectPrincipal(cx, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if (!sourcePrincipal) michael@0: { michael@0: NS_WARNING("CheckSameOrigin called on script w/o principals; should this happen?"); michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (sourcePrincipal == mSystemPrincipal) michael@0: { michael@0: // This is a system (chrome) script, so allow access michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Get the original URI from the source principal. michael@0: // This has the effect of ignoring any change to document.domain michael@0: // which must be done to avoid DNS spoofing (bug 154930) michael@0: nsCOMPtr sourceURI; michael@0: sourcePrincipal->GetDomain(getter_AddRefs(sourceURI)); michael@0: if (!sourceURI) { michael@0: sourcePrincipal->GetURI(getter_AddRefs(sourceURI)); michael@0: NS_ENSURE_TRUE(sourceURI, NS_ERROR_FAILURE); michael@0: } michael@0: michael@0: // Compare origins michael@0: if (!SecurityCompareURIs(sourceURI, aTargetURI)) michael@0: { michael@0: ReportError(cx, NS_LITERAL_STRING("CheckSameOriginError"), sourceURI, aTargetURI); michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CheckSameOriginURI(nsIURI* aSourceURI, michael@0: nsIURI* aTargetURI, michael@0: bool reportError) michael@0: { michael@0: if (!SecurityCompareURIs(aSourceURI, aTargetURI)) michael@0: { michael@0: if (reportError) { michael@0: ReportError(nullptr, NS_LITERAL_STRING("CheckSameOriginError"), michael@0: aSourceURI, aTargetURI); michael@0: } michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: /*static*/ uint32_t michael@0: nsScriptSecurityManager::HashPrincipalByOrigin(nsIPrincipal* aPrincipal) michael@0: { michael@0: nsCOMPtr uri; michael@0: aPrincipal->GetDomain(getter_AddRefs(uri)); michael@0: if (!uri) michael@0: aPrincipal->GetURI(getter_AddRefs(uri)); michael@0: return SecurityHashURI(uri); michael@0: } michael@0: michael@0: /* static */ bool michael@0: nsScriptSecurityManager::AppAttributesEqual(nsIPrincipal* aFirst, michael@0: nsIPrincipal* aSecond) michael@0: { michael@0: MOZ_ASSERT(aFirst && aSecond, "Don't pass null pointers!"); michael@0: michael@0: uint32_t firstAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID; michael@0: if (!aFirst->GetUnknownAppId()) { michael@0: firstAppId = aFirst->GetAppId(); michael@0: } michael@0: michael@0: uint32_t secondAppId = nsIScriptSecurityManager::UNKNOWN_APP_ID; michael@0: if (!aSecond->GetUnknownAppId()) { michael@0: secondAppId = aSecond->GetAppId(); michael@0: } michael@0: michael@0: return ((firstAppId == secondAppId) && michael@0: (aFirst->GetIsInBrowserElement() == aSecond->GetIsInBrowserElement())); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CheckLoadURIFromScript(JSContext *cx, nsIURI *aURI) michael@0: { michael@0: // Get principal of currently executing script. michael@0: nsresult rv; michael@0: nsIPrincipal* principal = GetSubjectPrincipal(cx, &rv); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: // Native code can load all URIs. michael@0: if (!principal) michael@0: return NS_OK; michael@0: michael@0: rv = CheckLoadURIWithPrincipal(principal, aURI, michael@0: nsIScriptSecurityManager::STANDARD); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // OK to load michael@0: return NS_OK; michael@0: } michael@0: michael@0: // See if we're attempting to load a file: URI. If so, let a michael@0: // UniversalXPConnect capability trump the above check. michael@0: bool isFile = false; michael@0: bool isRes = false; michael@0: if (NS_FAILED(aURI->SchemeIs("file", &isFile)) || michael@0: NS_FAILED(aURI->SchemeIs("resource", &isRes))) michael@0: return NS_ERROR_FAILURE; michael@0: if (isFile || isRes) michael@0: { michael@0: if (SubjectIsPrivileged()) michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Report error. michael@0: nsAutoCString spec; michael@0: if (NS_FAILED(aURI->GetAsciiSpec(spec))) michael@0: return NS_ERROR_FAILURE; michael@0: nsAutoCString msg("Access to '"); michael@0: msg.Append(spec); michael@0: msg.AppendLiteral("' from script denied"); michael@0: SetPendingException(cx, msg.get()); michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: michael@0: /** michael@0: * Helper method to handle cases where a flag passed to michael@0: * CheckLoadURIWithPrincipal means denying loading if the given URI has certain michael@0: * nsIProtocolHandler flags set. michael@0: * @return if success, access is allowed. Otherwise, deny access michael@0: */ michael@0: static nsresult michael@0: DenyAccessIfURIHasFlags(nsIURI* aURI, uint32_t aURIFlags) michael@0: { michael@0: NS_PRECONDITION(aURI, "Must have URI!"); michael@0: michael@0: bool uriHasFlags; michael@0: nsresult rv = michael@0: NS_URIChainHasFlags(aURI, aURIFlags, &uriHasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (uriHasFlags) { michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static bool michael@0: EqualOrSubdomain(nsIURI* aProbeArg, nsIURI* aBase) michael@0: { michael@0: // Make a clone of the incoming URI, because we're going to mutate it. michael@0: nsCOMPtr probe; michael@0: nsresult rv = aProbeArg->Clone(getter_AddRefs(probe)); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: nsCOMPtr tldService = do_GetService(NS_EFFECTIVETLDSERVICE_CONTRACTID); michael@0: NS_ENSURE_TRUE(tldService, false); michael@0: while (true) { michael@0: if (nsScriptSecurityManager::SecurityCompareURIs(probe, aBase)) { michael@0: return true; michael@0: } michael@0: michael@0: nsAutoCString host, newHost; michael@0: nsresult rv = probe->GetHost(host); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: michael@0: rv = tldService->GetNextSubDomain(host, newHost); michael@0: if (rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) { michael@0: return false; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: rv = probe->SetHost(newHost); michael@0: NS_ENSURE_SUCCESS(rv, false); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CheckLoadURIWithPrincipal(nsIPrincipal* aPrincipal, michael@0: nsIURI *aTargetURI, michael@0: uint32_t aFlags) michael@0: { michael@0: NS_PRECONDITION(aPrincipal, "CheckLoadURIWithPrincipal must have a principal"); michael@0: // If someone passes a flag that we don't understand, we should michael@0: // fail, because they may need a security check that we don't michael@0: // provide. michael@0: NS_ENSURE_FALSE(aFlags & ~(nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT | michael@0: nsIScriptSecurityManager::ALLOW_CHROME | michael@0: nsIScriptSecurityManager::DISALLOW_SCRIPT | michael@0: nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL | michael@0: nsIScriptSecurityManager::DONT_REPORT_ERRORS), michael@0: NS_ERROR_UNEXPECTED); michael@0: NS_ENSURE_ARG_POINTER(aPrincipal); michael@0: NS_ENSURE_ARG_POINTER(aTargetURI); michael@0: michael@0: // If DISALLOW_INHERIT_PRINCIPAL is set, we prevent loading of URIs which michael@0: // would do such inheriting. That would be URIs that do not have their own michael@0: // security context. We do this even for the system principal. michael@0: if (aFlags & nsIScriptSecurityManager::DISALLOW_INHERIT_PRINCIPAL) { michael@0: nsresult rv = michael@0: DenyAccessIfURIHasFlags(aTargetURI, michael@0: nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: if (aPrincipal == mSystemPrincipal) { michael@0: // Allow access michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsCOMPtr sourceURI; michael@0: aPrincipal->GetURI(getter_AddRefs(sourceURI)); michael@0: if (!sourceURI) { michael@0: nsCOMPtr expanded = do_QueryInterface(aPrincipal); michael@0: if (expanded) { michael@0: nsTArray< nsCOMPtr > *whiteList; michael@0: expanded->GetWhiteList(&whiteList); michael@0: for (uint32_t i = 0; i < whiteList->Length(); ++i) { michael@0: nsresult rv = CheckLoadURIWithPrincipal((*whiteList)[i], michael@0: aTargetURI, michael@0: aFlags); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: // Allow access if it succeeded with one of the white listed principals michael@0: return NS_OK; michael@0: } michael@0: } michael@0: // None of our whitelisted principals worked. michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: NS_ERROR("Non-system principals or expanded principal passed to CheckLoadURIWithPrincipal " michael@0: "must have a URI!"); michael@0: return NS_ERROR_UNEXPECTED; michael@0: } michael@0: michael@0: // Automatic loads are not allowed from certain protocols. michael@0: if (aFlags & nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT) { michael@0: nsresult rv = michael@0: DenyAccessIfURIHasFlags(sourceURI, michael@0: nsIProtocolHandler::URI_FORBIDS_AUTOMATIC_DOCUMENT_REPLACEMENT); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: // If either URI is a nested URI, get the base URI michael@0: nsCOMPtr sourceBaseURI = NS_GetInnermostURI(sourceURI); michael@0: nsCOMPtr targetBaseURI = NS_GetInnermostURI(aTargetURI); michael@0: michael@0: //-- get the target scheme michael@0: nsAutoCString targetScheme; michael@0: nsresult rv = targetBaseURI->GetScheme(targetScheme); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: //-- Some callers do not allow loading javascript: michael@0: if ((aFlags & nsIScriptSecurityManager::DISALLOW_SCRIPT) && michael@0: targetScheme.EqualsLiteral("javascript")) michael@0: { michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: michael@0: NS_NAMED_LITERAL_STRING(errorTag, "CheckLoadURIError"); michael@0: bool reportErrors = !(aFlags & nsIScriptSecurityManager::DONT_REPORT_ERRORS); michael@0: michael@0: // Check for uris that are only loadable by principals that subsume them michael@0: bool hasFlags; michael@0: rv = NS_URIChainHasFlags(targetBaseURI, michael@0: nsIProtocolHandler::URI_LOADABLE_BY_SUBSUMERS, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (hasFlags) { michael@0: return aPrincipal->CheckMayLoad(targetBaseURI, true, false); michael@0: } michael@0: michael@0: //-- get the source scheme michael@0: nsAutoCString sourceScheme; michael@0: rv = sourceBaseURI->GetScheme(sourceScheme); michael@0: if (NS_FAILED(rv)) return rv; michael@0: michael@0: if (sourceScheme.LowerCaseEqualsLiteral(NS_NULLPRINCIPAL_SCHEME)) { michael@0: // A null principal can target its own URI. michael@0: if (sourceURI == aTargetURI) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: else if (targetScheme.Equals(sourceScheme, michael@0: nsCaseInsensitiveCStringComparator())) michael@0: { michael@0: // every scheme can access another URI from the same scheme, michael@0: // as long as they don't represent null principals... michael@0: // Or they don't require an special permission to do so michael@0: // See bug#773886 michael@0: michael@0: bool hasFlags; michael@0: rv = NS_URIChainHasFlags(targetBaseURI, michael@0: nsIProtocolHandler::URI_CROSS_ORIGIN_NEEDS_WEBAPPS_PERM, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: if (hasFlags) { michael@0: // In this case, we allow opening only if the source and target URIS michael@0: // are on the same domain, or the opening URI has the webapps michael@0: // permision granted michael@0: if (!SecurityCompareURIs(sourceBaseURI,targetBaseURI) && michael@0: !nsContentUtils::IsExactSitePermAllow(aPrincipal,WEBAPPS_PERM_NAME)){ michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: // If the schemes don't match, the policy is specified by the protocol michael@0: // flags on the target URI. Note that the order of policy checks here is michael@0: // very important! We start from most restrictive and work our way down. michael@0: // Note that since we're working with the innermost URI, we can just use michael@0: // the methods that work on chains of nested URIs and they will only look michael@0: // at the flags for our one URI. michael@0: michael@0: // Check for system target URI michael@0: rv = DenyAccessIfURIHasFlags(targetBaseURI, michael@0: nsIProtocolHandler::URI_DANGEROUS_TO_LOAD); michael@0: if (NS_FAILED(rv)) { michael@0: // Deny access, since the origin principal is not system michael@0: if (reportErrors) { michael@0: ReportError(nullptr, errorTag, sourceURI, aTargetURI); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: // Check for chrome target URI michael@0: rv = NS_URIChainHasFlags(targetBaseURI, michael@0: nsIProtocolHandler::URI_IS_UI_RESOURCE, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (hasFlags) { michael@0: if (aFlags & nsIScriptSecurityManager::ALLOW_CHROME) { michael@0: if (!targetScheme.EqualsLiteral("chrome")) { michael@0: // for now don't change behavior for resource: or moz-icon: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // allow load only if chrome package is whitelisted michael@0: nsCOMPtr reg(do_GetService( michael@0: NS_CHROMEREGISTRY_CONTRACTID)); michael@0: if (reg) { michael@0: bool accessAllowed = false; michael@0: reg->AllowContentToAccess(targetBaseURI, &accessAllowed); michael@0: if (accessAllowed) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // resource: and chrome: are equivalent, securitywise michael@0: // That's bogus!! Fix this. But watch out for michael@0: // the view-source stylesheet? michael@0: bool sourceIsChrome; michael@0: rv = NS_URIChainHasFlags(sourceBaseURI, michael@0: nsIProtocolHandler::URI_IS_UI_RESOURCE, michael@0: &sourceIsChrome); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (sourceIsChrome) { michael@0: return NS_OK; michael@0: } michael@0: if (reportErrors) { michael@0: ReportError(nullptr, errorTag, sourceURI, aTargetURI); michael@0: } michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: michael@0: // Check for target URI pointing to a file michael@0: rv = NS_URIChainHasFlags(targetBaseURI, michael@0: nsIProtocolHandler::URI_IS_LOCAL_FILE, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (hasFlags) { michael@0: // Allow domains that were whitelisted in the prefs. In 99.9% of cases, michael@0: // this array is empty. michael@0: for (size_t i = 0; i < mFileURIWhitelist.Length(); ++i) { michael@0: if (EqualOrSubdomain(sourceURI, mFileURIWhitelist[i])) { michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // resource: and chrome: are equivalent, securitywise michael@0: // That's bogus!! Fix this. But watch out for michael@0: // the view-source stylesheet? michael@0: bool sourceIsChrome; michael@0: rv = NS_URIChainHasFlags(sourceURI, michael@0: nsIProtocolHandler::URI_IS_UI_RESOURCE, michael@0: &sourceIsChrome); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (sourceIsChrome) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (reportErrors) { michael@0: ReportError(nullptr, errorTag, sourceURI, aTargetURI); michael@0: } michael@0: return NS_ERROR_DOM_BAD_URI; michael@0: } michael@0: michael@0: // OK, everyone is allowed to load this, since unflagged handlers are michael@0: // deprecated but treated as URI_LOADABLE_BY_ANYONE. But check whether we michael@0: // need to warn. At some point we'll want to make this warning into an michael@0: // error and treat unflagged handlers as URI_DANGEROUS_TO_LOAD. michael@0: rv = NS_URIChainHasFlags(targetBaseURI, michael@0: nsIProtocolHandler::URI_LOADABLE_BY_ANYONE, michael@0: &hasFlags); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (!hasFlags) { michael@0: nsXPIDLString message; michael@0: NS_ConvertASCIItoUTF16 ucsTargetScheme(targetScheme); michael@0: const char16_t* formatStrings[] = { ucsTargetScheme.get() }; michael@0: rv = sStrBundle-> michael@0: FormatStringFromName(MOZ_UTF16("ProtocolFlagError"), michael@0: formatStrings, michael@0: ArrayLength(formatStrings), michael@0: getter_Copies(message)); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: nsCOMPtr console( michael@0: do_GetService("@mozilla.org/consoleservice;1")); michael@0: NS_ENSURE_TRUE(console, NS_ERROR_FAILURE); michael@0: michael@0: console->LogStringMessage(message.get()); michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptSecurityManager::ReportError(JSContext* cx, const nsAString& messageTag, michael@0: nsIURI* aSource, nsIURI* aTarget) michael@0: { michael@0: nsresult rv; michael@0: NS_ENSURE_TRUE(aSource && aTarget, NS_ERROR_NULL_POINTER); michael@0: michael@0: // Get the source URL spec michael@0: nsAutoCString sourceSpec; michael@0: rv = aSource->GetAsciiSpec(sourceSpec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Get the target URL spec michael@0: nsAutoCString targetSpec; michael@0: rv = aTarget->GetAsciiSpec(targetSpec); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Localize the error message michael@0: nsXPIDLString message; michael@0: NS_ConvertASCIItoUTF16 ucsSourceSpec(sourceSpec); michael@0: NS_ConvertASCIItoUTF16 ucsTargetSpec(targetSpec); michael@0: const char16_t *formatStrings[] = { ucsSourceSpec.get(), ucsTargetSpec.get() }; michael@0: rv = sStrBundle->FormatStringFromName(PromiseFlatString(messageTag).get(), michael@0: formatStrings, michael@0: ArrayLength(formatStrings), michael@0: getter_Copies(message)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // If a JS context was passed in, set a JS exception. michael@0: // Otherwise, print the error message directly to the JS console michael@0: // and to standard output michael@0: if (cx) michael@0: { michael@0: SetPendingException(cx, message.get()); michael@0: } michael@0: else // Print directly to the console michael@0: { michael@0: nsCOMPtr console( michael@0: do_GetService("@mozilla.org/consoleservice;1")); michael@0: NS_ENSURE_TRUE(console, NS_ERROR_FAILURE); michael@0: michael@0: console->LogStringMessage(message.get()); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CheckLoadURIStrWithPrincipal(nsIPrincipal* aPrincipal, michael@0: const nsACString& aTargetURIStr, michael@0: uint32_t aFlags) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr target; michael@0: rv = NS_NewURI(getter_AddRefs(target), aTargetURIStr, michael@0: nullptr, nullptr, sIOService); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags); michael@0: if (rv == NS_ERROR_DOM_BAD_URI) { michael@0: // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected michael@0: // return values. michael@0: return rv; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Now start testing fixup -- since aTargetURIStr is a string, not michael@0: // an nsIURI, we may well end up fixing it up before loading. michael@0: // Note: This needs to stay in sync with the nsIURIFixup api. michael@0: nsCOMPtr fixup = do_GetService(NS_URIFIXUP_CONTRACTID); michael@0: if (!fixup) { michael@0: return rv; michael@0: } michael@0: michael@0: uint32_t flags[] = { michael@0: nsIURIFixup::FIXUP_FLAG_NONE, michael@0: nsIURIFixup::FIXUP_FLAG_FIX_SCHEME_TYPOS, michael@0: nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP, michael@0: nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI, michael@0: nsIURIFixup::FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP | michael@0: nsIURIFixup::FIXUP_FLAGS_MAKE_ALTERNATE_URI michael@0: }; michael@0: michael@0: for (uint32_t i = 0; i < ArrayLength(flags); ++i) { michael@0: rv = fixup->CreateFixupURI(aTargetURIStr, flags[i], nullptr, michael@0: getter_AddRefs(target)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: rv = CheckLoadURIWithPrincipal(aPrincipal, target, aFlags); michael@0: if (rv == NS_ERROR_DOM_BAD_URI) { michael@0: // Don't warn because NS_ERROR_DOM_BAD_URI is one of the expected michael@0: // return values. michael@0: return rv; michael@0: } michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: bool michael@0: nsScriptSecurityManager::ScriptAllowed(JSObject *aGlobal) michael@0: { michael@0: MOZ_ASSERT(aGlobal); michael@0: MOZ_ASSERT(JS_IsGlobalObject(aGlobal) || js::IsOuterObject(aGlobal)); michael@0: AutoJSContext cx; michael@0: JS::RootedObject global(cx, js::UncheckedUnwrap(aGlobal, /* stopAtOuter = */ false)); michael@0: michael@0: // Check the bits on the compartment private. michael@0: return xpc::Scriptability::Get(aGlobal).Allowed(); michael@0: } michael@0: michael@0: ///////////////// Principals /////////////////////// michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetSubjectPrincipal(nsIPrincipal **aSubjectPrincipal) michael@0: { michael@0: nsresult rv; michael@0: *aSubjectPrincipal = doGetSubjectPrincipal(&rv); michael@0: if (NS_SUCCEEDED(rv)) michael@0: NS_IF_ADDREF(*aSubjectPrincipal); michael@0: return rv; michael@0: } michael@0: michael@0: nsIPrincipal* michael@0: nsScriptSecurityManager::doGetSubjectPrincipal(nsresult* rv) michael@0: { michael@0: NS_PRECONDITION(rv, "Null out param"); michael@0: JSContext *cx = GetCurrentJSContext(); michael@0: if (!cx) michael@0: { michael@0: *rv = NS_OK; michael@0: return nullptr; michael@0: } michael@0: return GetSubjectPrincipal(cx, rv); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetSystemPrincipal(nsIPrincipal **result) michael@0: { michael@0: NS_ADDREF(*result = mSystemPrincipal); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::SubjectPrincipalIsSystem(bool* aIsSystem) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aIsSystem); michael@0: *aIsSystem = false; michael@0: michael@0: if (!mSystemPrincipal) michael@0: return NS_OK; michael@0: michael@0: nsCOMPtr subject; michael@0: nsresult rv = GetSubjectPrincipal(getter_AddRefs(subject)); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: if(!subject) michael@0: { michael@0: // No subject principal means no JS is running; michael@0: // this is the equivalent of system principal code michael@0: *aIsSystem = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return mSystemPrincipal->Equals(subject, aIsSystem); michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptSecurityManager::CreateCodebasePrincipal(nsIURI* aURI, uint32_t aAppId, michael@0: bool aInMozBrowser, michael@0: nsIPrincipal **result) michael@0: { michael@0: // I _think_ it's safe to not create null principals here based on aURI. michael@0: // At least all the callers would do the right thing in those cases, as far michael@0: // as I can tell. --bz michael@0: michael@0: nsCOMPtr uriPrinc = do_QueryInterface(aURI); michael@0: if (uriPrinc) { michael@0: nsCOMPtr principal; michael@0: uriPrinc->GetPrincipal(getter_AddRefs(principal)); michael@0: if (!principal || principal == mSystemPrincipal) { michael@0: return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result); michael@0: } michael@0: michael@0: principal.forget(result); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr codebase = new nsPrincipal(); michael@0: if (!codebase) michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: michael@0: nsresult rv = codebase->Init(aURI, aAppId, aInMozBrowser); michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: NS_ADDREF(*result = codebase); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetSimpleCodebasePrincipal(nsIURI* aURI, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: return GetCodebasePrincipalInternal(aURI, michael@0: nsIScriptSecurityManager::UNKNOWN_APP_ID, michael@0: false, aPrincipal); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetNoAppCodebasePrincipal(nsIURI* aURI, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: return GetCodebasePrincipalInternal(aURI, nsIScriptSecurityManager::NO_APP_ID, michael@0: false, aPrincipal); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetCodebasePrincipal(nsIURI* aURI, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: return GetNoAppCodebasePrincipal(aURI, aPrincipal); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI, michael@0: uint32_t aAppId, michael@0: bool aInMozBrowser, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID, michael@0: NS_ERROR_INVALID_ARG); michael@0: michael@0: return GetCodebasePrincipalInternal(aURI, aAppId, aInMozBrowser, aPrincipal); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetDocShellCodebasePrincipal(nsIURI* aURI, michael@0: nsIDocShell* aDocShell, michael@0: nsIPrincipal** aPrincipal) michael@0: { michael@0: return GetCodebasePrincipalInternal(aURI, michael@0: aDocShell->GetAppId(), michael@0: aDocShell->GetIsInBrowserElement(), michael@0: aPrincipal); michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptSecurityManager::GetCodebasePrincipalInternal(nsIURI *aURI, michael@0: uint32_t aAppId, michael@0: bool aInMozBrowser, michael@0: nsIPrincipal **result) michael@0: { michael@0: NS_ENSURE_ARG(aURI); michael@0: michael@0: bool inheritsPrincipal; michael@0: nsresult rv = michael@0: NS_URIChainHasFlags(aURI, michael@0: nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, michael@0: &inheritsPrincipal); michael@0: if (NS_FAILED(rv) || inheritsPrincipal) { michael@0: return CallCreateInstance(NS_NULLPRINCIPAL_CONTRACTID, result); michael@0: } michael@0: michael@0: nsCOMPtr principal; michael@0: rv = CreateCodebasePrincipal(aURI, aAppId, aInMozBrowser, michael@0: getter_AddRefs(principal)); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: NS_IF_ADDREF(*result = principal); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIPrincipal* michael@0: nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx, michael@0: nsresult* rv) michael@0: { michael@0: *rv = NS_OK; michael@0: JSCompartment *compartment = js::GetContextCompartment(cx); michael@0: michael@0: // The context should always be in a compartment, either one it has entered michael@0: // or the one associated with its global. michael@0: MOZ_ASSERT(!!compartment); michael@0: michael@0: JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment); michael@0: return nsJSPrincipals::get(principals); michael@0: } michael@0: michael@0: // static michael@0: nsIPrincipal* michael@0: nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj) michael@0: { michael@0: JSCompartment *compartment = js::GetObjectCompartment(aObj); michael@0: JSPrincipals *principals = JS_GetCompartmentPrincipals(compartment); michael@0: return nsJSPrincipals::get(principals); michael@0: } michael@0: michael@0: //////////////////////////////////////////////// michael@0: // Methods implementing nsIXPCSecurityManager // michael@0: //////////////////////////////////////////////// michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CanCreateWrapper(JSContext *cx, michael@0: const nsIID &aIID, michael@0: nsISupports *aObj, michael@0: nsIClassInfo *aClassInfo) michael@0: { michael@0: // XXX Special case for nsIXPCException ? michael@0: ClassInfoData objClassInfo = ClassInfoData(aClassInfo, nullptr); michael@0: if (objClassInfo.IsDOMClass()) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We give remote-XUL whitelisted domains a free pass here. See bug 932906. michael@0: if (!xpc::AllowXBLScope(js::GetContextCompartment(cx))) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (SubjectIsPrivileged()) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: //-- Access denied, report an error michael@0: NS_ConvertUTF8toUTF16 strName("CreateWrapperDenied"); michael@0: nsAutoCString origin; michael@0: nsresult rv2; michael@0: nsIPrincipal* subjectPrincipal = doGetSubjectPrincipal(&rv2); michael@0: if (NS_SUCCEEDED(rv2) && subjectPrincipal) { michael@0: GetPrincipalDomainOrigin(subjectPrincipal, origin); michael@0: } michael@0: NS_ConvertUTF8toUTF16 originUnicode(origin); michael@0: NS_ConvertUTF8toUTF16 classInfoName(objClassInfo.GetName()); michael@0: const char16_t* formatStrings[] = { michael@0: classInfoName.get(), michael@0: originUnicode.get() michael@0: }; michael@0: uint32_t length = ArrayLength(formatStrings); michael@0: if (originUnicode.IsEmpty()) { michael@0: --length; michael@0: } else { michael@0: strName.AppendLiteral("ForOrigin"); michael@0: } michael@0: nsXPIDLString errorMsg; michael@0: // We need to keep our existing failure rv and not override it michael@0: // with a likely success code from the following string bundle michael@0: // call in order to throw the correct security exception later. michael@0: rv2 = sStrBundle->FormatStringFromName(strName.get(), michael@0: formatStrings, michael@0: length, michael@0: getter_Copies(errorMsg)); michael@0: NS_ENSURE_SUCCESS(rv2, rv2); michael@0: michael@0: SetPendingException(cx, errorMsg.get()); michael@0: return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CanCreateInstance(JSContext *cx, michael@0: const nsCID &aCID) michael@0: { michael@0: if (SubjectIsPrivileged()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: //-- Access denied, report an error michael@0: nsAutoCString errorMsg("Permission denied to create instance of class. CID="); michael@0: char cidStr[NSID_LENGTH]; michael@0: aCID.ToProvidedString(cidStr); michael@0: errorMsg.Append(cidStr); michael@0: SetPendingException(cx, errorMsg.get()); michael@0: return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::CanGetService(JSContext *cx, michael@0: const nsCID &aCID) michael@0: { michael@0: if (SubjectIsPrivileged()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: //-- Access denied, report an error michael@0: nsAutoCString errorMsg("Permission denied to get service. CID="); michael@0: char cidStr[NSID_LENGTH]; michael@0: aCID.ToProvidedString(cidStr); michael@0: errorMsg.Append(cidStr); michael@0: SetPendingException(cx, errorMsg.get()); michael@0: return NS_ERROR_DOM_XPCONNECT_ACCESS_DENIED; michael@0: } michael@0: michael@0: ///////////////////////////////////////////// michael@0: // Method implementing nsIChannelEventSink // michael@0: ///////////////////////////////////////////// michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::AsyncOnChannelRedirect(nsIChannel* oldChannel, michael@0: nsIChannel* newChannel, michael@0: uint32_t redirFlags, michael@0: nsIAsyncVerifyRedirectCallback *cb) michael@0: { michael@0: nsCOMPtr oldPrincipal; michael@0: GetChannelPrincipal(oldChannel, getter_AddRefs(oldPrincipal)); michael@0: michael@0: nsCOMPtr newURI; michael@0: newChannel->GetURI(getter_AddRefs(newURI)); michael@0: nsCOMPtr newOriginalURI; michael@0: newChannel->GetOriginalURI(getter_AddRefs(newOriginalURI)); michael@0: michael@0: NS_ENSURE_STATE(oldPrincipal && newURI && newOriginalURI); michael@0: michael@0: const uint32_t flags = michael@0: nsIScriptSecurityManager::LOAD_IS_AUTOMATIC_DOCUMENT_REPLACEMENT | michael@0: nsIScriptSecurityManager::DISALLOW_SCRIPT; michael@0: nsresult rv = CheckLoadURIWithPrincipal(oldPrincipal, newURI, flags); michael@0: if (NS_SUCCEEDED(rv) && newOriginalURI != newURI) { michael@0: rv = CheckLoadURIWithPrincipal(oldPrincipal, newOriginalURI, flags); michael@0: } michael@0: michael@0: if (NS_FAILED(rv)) michael@0: return rv; michael@0: michael@0: cb->OnRedirectVerifyCallback(NS_OK); michael@0: return NS_OK; michael@0: } michael@0: michael@0: michael@0: ///////////////////////////////////// michael@0: // Method implementing nsIObserver // michael@0: ///////////////////////////////////// michael@0: const char sJSEnabledPrefName[] = "javascript.enabled"; michael@0: const char sFileOriginPolicyPrefName[] = michael@0: "security.fileuri.strict_origin_policy"; michael@0: michael@0: static const char* kObservedPrefs[] = { michael@0: sJSEnabledPrefName, michael@0: sFileOriginPolicyPrefName, michael@0: "capability.policy.", michael@0: nullptr michael@0: }; michael@0: michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::Observe(nsISupports* aObject, const char* aTopic, michael@0: const char16_t* aMessage) michael@0: { michael@0: ScriptSecurityPrefChanged(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: ///////////////////////////////////////////// michael@0: // Constructor, Destructor, Initialization // michael@0: ///////////////////////////////////////////// michael@0: nsScriptSecurityManager::nsScriptSecurityManager(void) michael@0: : mPrefInitialized(false) michael@0: , mIsJavaScriptEnabled(false) michael@0: { michael@0: static_assert(sizeof(intptr_t) == sizeof(void*), michael@0: "intptr_t and void* have different lengths on this platform. " michael@0: "This may cause a security failure with the SecurityLevel union."); michael@0: } michael@0: michael@0: nsresult nsScriptSecurityManager::Init() michael@0: { michael@0: nsresult rv = CallGetService(NS_IOSERVICE_CONTRACTID, &sIOService); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: InitPrefs(); michael@0: michael@0: nsCOMPtr bundleService = michael@0: mozilla::services::GetStringBundleService(); michael@0: if (!bundleService) michael@0: return NS_ERROR_FAILURE; michael@0: michael@0: rv = bundleService->CreateBundle("chrome://global/locale/security/caps.properties", &sStrBundle); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: // Create our system principal singleton michael@0: nsRefPtr system = new nsSystemPrincipal(); michael@0: NS_ENSURE_TRUE(system, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: mSystemPrincipal = system; michael@0: michael@0: //-- Register security check callback in the JS engine michael@0: // Currently this is used to control access to function.caller michael@0: rv = nsXPConnect::XPConnect()->GetRuntime(&sRuntime); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: static const JSSecurityCallbacks securityCallbacks = { michael@0: ContentSecurityPolicyPermitsJSAction, michael@0: JSPrincipalsSubsume, michael@0: }; michael@0: michael@0: MOZ_ASSERT(!JS_GetSecurityCallbacks(sRuntime)); michael@0: JS_SetSecurityCallbacks(sRuntime, &securityCallbacks); michael@0: JS_InitDestroyPrincipalsCallback(sRuntime, nsJSPrincipals::Destroy); michael@0: michael@0: JS_SetTrustedPrincipals(sRuntime, system); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: static StaticRefPtr gScriptSecMan; michael@0: michael@0: nsScriptSecurityManager::~nsScriptSecurityManager(void) michael@0: { michael@0: Preferences::RemoveObservers(this, kObservedPrefs); michael@0: if (mDomainPolicy) michael@0: mDomainPolicy->Deactivate(); michael@0: MOZ_ASSERT(!mDomainPolicy); michael@0: } michael@0: michael@0: void michael@0: nsScriptSecurityManager::Shutdown() michael@0: { michael@0: if (sRuntime) { michael@0: JS_SetSecurityCallbacks(sRuntime, nullptr); michael@0: JS_SetTrustedPrincipals(sRuntime, nullptr); michael@0: sRuntime = nullptr; michael@0: } michael@0: michael@0: NS_IF_RELEASE(sIOService); michael@0: NS_IF_RELEASE(sStrBundle); michael@0: } michael@0: michael@0: nsScriptSecurityManager * michael@0: nsScriptSecurityManager::GetScriptSecurityManager() michael@0: { michael@0: if (!gScriptSecMan && nsXPConnect::XPConnect()) michael@0: { michael@0: nsRefPtr ssManager = new nsScriptSecurityManager(); michael@0: michael@0: nsresult rv; michael@0: rv = ssManager->Init(); michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: rv = nsXPConnect::XPConnect()-> michael@0: SetDefaultSecurityManager(ssManager); michael@0: if (NS_FAILED(rv)) { michael@0: NS_WARNING("Failed to install xpconnect security manager!"); michael@0: return nullptr; michael@0: } michael@0: michael@0: ClearOnShutdown(&gScriptSecMan); michael@0: gScriptSecMan = ssManager; michael@0: } michael@0: return gScriptSecMan; michael@0: } michael@0: michael@0: // Currently this nsGenericFactory constructor is used only from FastLoad michael@0: // (XPCOM object deserialization) code, when "creating" the system principal michael@0: // singleton. michael@0: nsSystemPrincipal * michael@0: nsScriptSecurityManager::SystemPrincipalSingletonConstructor() michael@0: { michael@0: nsIPrincipal *sysprin = nullptr; michael@0: if (gScriptSecMan) michael@0: NS_ADDREF(sysprin = gScriptSecMan->mSystemPrincipal); michael@0: return static_cast(sysprin); michael@0: } michael@0: michael@0: struct IsWhitespace { michael@0: static bool Test(char aChar) { return NS_IsAsciiWhitespace(aChar); }; michael@0: }; michael@0: struct IsWhitespaceOrComma { michael@0: static bool Test(char aChar) { return aChar == ',' || NS_IsAsciiWhitespace(aChar); }; michael@0: }; michael@0: michael@0: template michael@0: uint32_t SkipPast(const nsCString& str, uint32_t base) michael@0: { michael@0: while (base < str.Length() && Predicate::Test(str[base])) { michael@0: ++base; michael@0: } michael@0: return base; michael@0: } michael@0: michael@0: template michael@0: uint32_t SkipUntil(const nsCString& str, uint32_t base) michael@0: { michael@0: while (base < str.Length() && !Predicate::Test(str[base])) { michael@0: ++base; michael@0: } michael@0: return base; michael@0: } michael@0: michael@0: inline void michael@0: nsScriptSecurityManager::ScriptSecurityPrefChanged() michael@0: { michael@0: MOZ_ASSERT(mPrefInitialized); michael@0: mIsJavaScriptEnabled = michael@0: Preferences::GetBool(sJSEnabledPrefName, mIsJavaScriptEnabled); michael@0: sStrictFileOriginPolicy = michael@0: Preferences::GetBool(sFileOriginPolicyPrefName, false); michael@0: michael@0: // michael@0: // Rebuild the set of principals for which we allow file:// URI loads. This michael@0: // implements a small subset of an old pref-based CAPS people that people michael@0: // have come to depend on. See bug 995943. michael@0: // michael@0: michael@0: mFileURIWhitelist.Clear(); michael@0: auto policies = mozilla::Preferences::GetCString("capability.policy.policynames"); michael@0: for (uint32_t base = SkipPast(policies, 0), bound = 0; michael@0: base < policies.Length(); michael@0: base = SkipPast(policies, bound)) michael@0: { michael@0: // Grab the current policy name. michael@0: bound = SkipUntil(policies, base); michael@0: auto policyName = Substring(policies, base, bound - base); michael@0: michael@0: // Figure out if this policy allows loading file:// URIs. If not, we can skip it. michael@0: nsCString checkLoadURIPrefName = NS_LITERAL_CSTRING("capability.policy.") + michael@0: policyName + michael@0: NS_LITERAL_CSTRING(".checkloaduri.enabled"); michael@0: if (!Preferences::GetString(checkLoadURIPrefName.get()).LowerCaseEqualsLiteral("allaccess")) { michael@0: continue; michael@0: } michael@0: michael@0: // Grab the list of domains associated with this policy. michael@0: nsCString domainPrefName = NS_LITERAL_CSTRING("capability.policy.") + michael@0: policyName + michael@0: NS_LITERAL_CSTRING(".sites"); michael@0: auto siteList = Preferences::GetCString(domainPrefName.get()); michael@0: AddSitesToFileURIWhitelist(siteList); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsScriptSecurityManager::AddSitesToFileURIWhitelist(const nsCString& aSiteList) michael@0: { michael@0: for (uint32_t base = SkipPast(aSiteList, 0), bound = 0; michael@0: base < aSiteList.Length(); michael@0: base = SkipPast(aSiteList, bound)) michael@0: { michael@0: // Grab the current site. michael@0: bound = SkipUntil(aSiteList, base); michael@0: nsAutoCString site(Substring(aSiteList, base, bound - base)); michael@0: michael@0: // Check if the URI is schemeless. If so, add both http and https. michael@0: nsAutoCString unused; michael@0: if (NS_FAILED(sIOService->ExtractScheme(site, unused))) { michael@0: AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("http://") + site); michael@0: AddSitesToFileURIWhitelist(NS_LITERAL_CSTRING("https://") + site); michael@0: continue; michael@0: } michael@0: michael@0: // Convert it to a URI and add it to our list. michael@0: nsCOMPtr uri; michael@0: nsresult rv = NS_NewURI(getter_AddRefs(uri), site, nullptr, nullptr, sIOService); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: mFileURIWhitelist.AppendElement(uri); michael@0: } else { michael@0: nsCOMPtr console(do_GetService("@mozilla.org/consoleservice;1")); michael@0: if (console) { michael@0: nsAutoString msg = NS_LITERAL_STRING("Unable to to add site to file:// URI whitelist: ") + michael@0: NS_ConvertASCIItoUTF16(site); michael@0: console->LogStringMessage(msg.get()); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: nsresult michael@0: nsScriptSecurityManager::InitPrefs() michael@0: { michael@0: nsIPrefBranch* branch = Preferences::GetRootBranch(); michael@0: NS_ENSURE_TRUE(branch, NS_ERROR_FAILURE); michael@0: michael@0: mPrefInitialized = true; michael@0: michael@0: // Set the initial value of the "javascript.enabled" prefs michael@0: ScriptSecurityPrefChanged(); michael@0: michael@0: // set observer callbacks in case the value of the prefs change michael@0: Preferences::AddStrongObservers(this, kObservedPrefs); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: namespace mozilla { michael@0: michael@0: void michael@0: GetJarPrefix(uint32_t aAppId, bool aInMozBrowser, nsACString& aJarPrefix) michael@0: { michael@0: MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID); michael@0: michael@0: if (aAppId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { michael@0: aAppId = nsIScriptSecurityManager::NO_APP_ID; michael@0: } michael@0: michael@0: aJarPrefix.Truncate(); michael@0: michael@0: // Fallback. michael@0: if (aAppId == nsIScriptSecurityManager::NO_APP_ID && !aInMozBrowser) { michael@0: return; michael@0: } michael@0: michael@0: // aJarPrefix = appId + "+" + { 't', 'f' } + "+"; michael@0: aJarPrefix.AppendInt(aAppId); michael@0: aJarPrefix.Append('+'); michael@0: aJarPrefix.Append(aInMozBrowser ? 't' : 'f'); michael@0: aJarPrefix.Append('+'); michael@0: michael@0: return; michael@0: } michael@0: michael@0: } // namespace mozilla michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetJarPrefix(uint32_t aAppId, michael@0: bool aInMozBrowser, michael@0: nsACString& aJarPrefix) michael@0: { michael@0: MOZ_ASSERT(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID); michael@0: michael@0: mozilla::GetJarPrefix(aAppId, aInMozBrowser, aJarPrefix); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::GetDomainPolicyActive(bool *aRv) michael@0: { michael@0: *aRv = !!mDomainPolicy; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::ActivateDomainPolicy(nsIDomainPolicy** aRv) michael@0: { michael@0: // We only allow one domain policy at a time. The holder of the previous michael@0: // policy must explicitly deactivate it first. michael@0: if (mDomainPolicy) { michael@0: return NS_ERROR_SERVICE_NOT_AVAILABLE; michael@0: } michael@0: michael@0: mDomainPolicy = new DomainPolicy(); michael@0: nsCOMPtr ptr = mDomainPolicy; michael@0: ptr.forget(aRv); michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Intentionally non-scriptable. Script must have a reference to the michael@0: // nsIDomainPolicy to deactivate it. michael@0: void michael@0: nsScriptSecurityManager::DeactivateDomainPolicy() michael@0: { michael@0: mDomainPolicy = nullptr; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsScriptSecurityManager::PolicyAllowsScript(nsIURI* aURI, bool *aRv) michael@0: { michael@0: nsresult rv; michael@0: michael@0: // Compute our rule. If we don't have any domain policy set up that might michael@0: // provide exceptions to this rule, we're done. michael@0: *aRv = mIsJavaScriptEnabled; michael@0: if (!mDomainPolicy) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: // We have a domain policy. Grab the appropriate set of exceptions to the michael@0: // rule (either the blacklist or the whitelist, depending on whether script michael@0: // is enabled or disabled by default). michael@0: nsCOMPtr exceptions; michael@0: nsCOMPtr superExceptions; michael@0: if (*aRv) { michael@0: mDomainPolicy->GetBlacklist(getter_AddRefs(exceptions)); michael@0: mDomainPolicy->GetSuperBlacklist(getter_AddRefs(superExceptions)); michael@0: } else { michael@0: mDomainPolicy->GetWhitelist(getter_AddRefs(exceptions)); michael@0: mDomainPolicy->GetSuperWhitelist(getter_AddRefs(superExceptions)); michael@0: } michael@0: michael@0: bool contains; michael@0: rv = exceptions->Contains(aURI, &contains); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (contains) { michael@0: *aRv = !*aRv; michael@0: return NS_OK; michael@0: } michael@0: rv = superExceptions->ContainsSuperDomain(aURI, &contains); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: if (contains) { michael@0: *aRv = !*aRv; michael@0: } michael@0: michael@0: return NS_OK; michael@0: }