/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ClientValidation.h" #include "mozilla/StaticPrefs_security.h" #include "mozilla/dom/ClientIPCTypes.h" #include "mozilla/dom/LoadedOriginSet.h" #include "mozilla/dom/ProcessIsolation.h" #include "mozilla/dom/RemoteType.h" #include "mozilla/ipc/PBackgroundSharedTypes.h" #include "mozilla/net/MozURL.h" namespace mozilla::dom { using mozilla::ipc::ContentPrincipalInfo; using mozilla::ipc::PrincipalInfo; using mozilla::net::MozURL; bool ClientIsValidPrincipalInfo(const PrincipalInfo& aPrincipalInfo, LoadedOriginSet* aLoadedOrigins) { auto result = mozilla::ipc::PrincipalInfoToPrincipal(aPrincipalInfo); if (NS_WARN_IF(result.isErr())) { return false; } nsCOMPtr principal = result.unwrap(); // FIXME: Remove the system allowance once for non-inference processes once we // can load documents with the system principal into content. if (aLoadedOrigins && NS_WARN_IF(!aLoadedOrigins->ValidatePrincipal( principal, {ValidatePrincipalOptions::AllowSystemIfLoaded}))) { return false; } // Windows and workers should not have expanded principals, etc. return principal->IsSystemPrincipal() || principal->GetIsNullPrincipal() || principal->GetIsContentPrincipal(); } bool IsValidClientOpConstructorArgs(const ClientOpConstructorArgs& aArgs, LoadedOriginSet* aLoadedOrigins) { switch (aArgs.type()) { case ClientOpConstructorArgs::TClientControlledArgs: return ClientIsValidPrincipalInfo( aArgs.get_ClientControlledArgs().serviceWorker().principalInfo(), aLoadedOrigins); case ClientOpConstructorArgs::TClientNavigateArgs: { const ClientNavigateArgs& args = aArgs.get_ClientNavigateArgs(); return ClientIsValidPrincipalInfo(args.target().principalInfo(), aLoadedOrigins) && ClientIsValidPrincipalInfo(args.serviceWorker().principalInfo(), aLoadedOrigins); } case ClientOpConstructorArgs::TClientPostMessageArgs: return ClientIsValidPrincipalInfo( aArgs.get_ClientPostMessageArgs().serviceWorker().principalInfo(), aLoadedOrigins); case ClientOpConstructorArgs::TClientMatchAllArgs: return ClientIsValidPrincipalInfo( aArgs.get_ClientMatchAllArgs().serviceWorker().principalInfo(), aLoadedOrigins); case ClientOpConstructorArgs::TClientClaimArgs: return ClientIsValidPrincipalInfo( aArgs.get_ClientClaimArgs().serviceWorker().principalInfo(), aLoadedOrigins); case ClientOpConstructorArgs::TClientGetInfoAndStateArgs: return ClientIsValidPrincipalInfo( aArgs.get_ClientGetInfoAndStateArgs().principalInfo(), aLoadedOrigins); case ClientOpConstructorArgs::TClientOpenWindowArgs: return ClientIsValidPrincipalInfo( aArgs.get_ClientOpenWindowArgs().principalInfo(), aLoadedOrigins); case ClientOpConstructorArgs::TClientFocusArgs: case ClientOpConstructorArgs::TClientEvictBFCacheArgs: // No principals. return true; case ClientOpConstructorArgs::T__None: break; } MOZ_CRASH("Unhandled ClientOpConstructorArgs"); } bool ClientIsValidCreationURL(const PrincipalInfo& aPrincipalInfo, const nsACString& aURL) { RefPtr url; nsresult rv = MozURL::Init(getter_AddRefs(url), aURL); NS_ENSURE_SUCCESS(rv, false); switch (aPrincipalInfo.type()) { case PrincipalInfo::TContentPrincipalInfo: { // Any origin can create an about:blank or about:srcdoc Client. if (aURL.LowerCaseEqualsLiteral("about:blank") || aURL.LowerCaseEqualsLiteral("about:srcdoc")) { return true; } const ContentPrincipalInfo& content = aPrincipalInfo.get_ContentPrincipalInfo(); // Parse the principal origin URL as well. This ensures any MozURL // parser issues effect both URLs equally. RefPtr principalURL; rv = MozURL::Init(getter_AddRefs(principalURL), content.originNoSuffix()); NS_ENSURE_SUCCESS(rv, false); nsAutoCString origin; url->Origin(origin); nsAutoCString principalOrigin; principalURL->Origin(principalOrigin); // The vast majority of sites should simply result in the same principal // and URL origin. if (principalOrigin == origin) { return true; } nsDependentCSubstring scheme = url->Scheme(); // Generally any origin can also open javascript: windows and workers. if (scheme.LowerCaseEqualsLiteral("javascript")) { return true; } // Otherwise don't support this URL type in the clients sub-system for // now. This will exclude a variety of internal browser clients, but // currently we don't need to support those. This function can be // expanded to handle more cases as necessary. return false; } case PrincipalInfo::TSystemPrincipalInfo: { nsDependentCSubstring scheme = url->Scheme(); // While many types of documents can be created with a system principal, // there are only a few that can reasonably become windows. We attempt // to validate the list of known cases here with a simple scheme check. return scheme.LowerCaseEqualsLiteral("about") || scheme.LowerCaseEqualsLiteral("chrome") || scheme.LowerCaseEqualsLiteral("resource") || scheme.LowerCaseEqualsLiteral("blob") || scheme.LowerCaseEqualsLiteral("javascript") || scheme.LowerCaseEqualsLiteral("view-source"); } case PrincipalInfo::TNullPrincipalInfo: { // A wide variety of clients can have a null principal. For example, // sandboxed iframes can have a normal content URL. For now allow // any parsable URL for null principals. This is relatively safe since // null principals have unique origins and won't most ClientManagerService // queries anyway. return true; } default: { break; } } // Clients (windows/workers) should never have an expanded principal type. return false; } } // namespace mozilla::dom