michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=8 et : michael@0: */ 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 file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "AppProcessChecker.h" michael@0: #include "nsIPermissionManager.h" michael@0: #ifdef MOZ_CHILD_PERMISSIONS michael@0: #include "ContentParent.h" michael@0: #include "mozIApplication.h" michael@0: #include "mozilla/hal_sandbox/PHalParent.h" michael@0: #include "nsIAppsService.h" michael@0: #include "nsIPrincipal.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "nsPrintfCString.h" michael@0: #include "nsIURI.h" michael@0: #include "nsNetUtil.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: #include "TabParent.h" michael@0: michael@0: #include michael@0: michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::hal_sandbox; michael@0: using namespace mozilla::services; michael@0: #else michael@0: namespace mozilla { michael@0: namespace dom { michael@0: class PContentParent; michael@0: } michael@0: } michael@0: michael@0: class nsIPrincipal; michael@0: #endif michael@0: michael@0: namespace mozilla { michael@0: michael@0: #ifdef MOZ_CHILD_PERMISSIONS michael@0: michael@0: bool michael@0: AssertAppProcess(PBrowserParent* aActor, michael@0: AssertAppProcessType aType, michael@0: const char* aCapability) michael@0: { michael@0: if (!aActor) { michael@0: NS_WARNING("Testing process capability for null actor"); michael@0: return false; michael@0: } michael@0: michael@0: TabParent* tab = static_cast(aActor); michael@0: nsCOMPtr app = tab->GetOwnOrContainingApp(); michael@0: bool aValid = false; michael@0: michael@0: // isBrowser frames inherit their app descriptor to identify their michael@0: // data storage, but they don't inherit the capability associated michael@0: // with that descriptor. michael@0: if (app && (aType == ASSERT_APP_HAS_PERMISSION || !tab->IsBrowserElement())) { michael@0: switch (aType) { michael@0: case ASSERT_APP_HAS_PERMISSION: michael@0: case ASSERT_APP_PROCESS_PERMISSION: michael@0: if (!NS_SUCCEEDED(app->HasPermission(aCapability, &aValid))) { michael@0: aValid = false; michael@0: } michael@0: break; michael@0: case ASSERT_APP_PROCESS_MANIFEST_URL: { michael@0: nsAutoString manifestURL; michael@0: if (NS_SUCCEEDED(app->GetManifestURL(manifestURL)) && michael@0: manifestURL.EqualsASCII(aCapability)) { michael@0: aValid = true; michael@0: } michael@0: break; michael@0: } michael@0: default: michael@0: break; michael@0: } michael@0: } michael@0: return aValid; michael@0: } michael@0: michael@0: bool michael@0: AssertAppStatus(PBrowserParent* aActor, michael@0: unsigned short aStatus) michael@0: { michael@0: if (!aActor) { michael@0: NS_WARNING("Testing process capability for null actor"); michael@0: return false; michael@0: } michael@0: michael@0: TabParent* tab = static_cast(aActor); michael@0: nsCOMPtr app = tab->GetOwnOrContainingApp(); michael@0: michael@0: bool valid = false; michael@0: michael@0: if (app) { michael@0: unsigned short appStatus = 0; michael@0: if (NS_SUCCEEDED(app->GetAppStatus(&appStatus))) { michael@0: valid = appStatus == aStatus; michael@0: } michael@0: } michael@0: michael@0: return valid; michael@0: } michael@0: michael@0: bool michael@0: AssertAppProcess(PContentParent* aActor, michael@0: AssertAppProcessType aType, michael@0: const char* aCapability) michael@0: { michael@0: const InfallibleTArray& browsers = michael@0: aActor->ManagedPBrowserParent(); michael@0: for (uint32_t i = 0; i < browsers.Length(); ++i) { michael@0: if (AssertAppProcess(browsers[i], aType, aCapability)) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: NS_ERROR( michael@0: nsPrintfCString( michael@0: "Security problem: Content process does not have `%s'. It will be killed.\n", michael@0: aCapability).get()); michael@0: michael@0: static_cast(aActor)->KillHard(); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: AssertAppStatus(PContentParent* aActor, michael@0: unsigned short aStatus) michael@0: { michael@0: const InfallibleTArray& browsers = michael@0: aActor->ManagedPBrowserParent(); michael@0: for (uint32_t i = 0; i < browsers.Length(); ++i) { michael@0: if (AssertAppStatus(browsers[i], aStatus)) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: NS_ERROR( michael@0: nsPrintfCString( michael@0: "Security problem: Content process does not have `%d' status. It will be killed.", michael@0: aStatus).get()); michael@0: michael@0: static_cast(aActor)->KillHard(); michael@0: michael@0: return false; michael@0: } michael@0: michael@0: bool michael@0: AssertAppProcess(PHalParent* aActor, michael@0: AssertAppProcessType aType, michael@0: const char* aCapability) michael@0: { michael@0: return AssertAppProcess(aActor->Manager(), aType, aCapability); michael@0: } michael@0: michael@0: bool michael@0: AssertAppPrincipal(PContentParent* aActor, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: if (!aPrincipal) { michael@0: NS_WARNING("Principal is invalid, killing app process"); michael@0: static_cast(aActor)->KillHard(); michael@0: return false; michael@0: } michael@0: michael@0: uint32_t principalAppId = aPrincipal->GetAppId(); michael@0: bool inBrowserElement = aPrincipal->GetIsInBrowserElement(); michael@0: michael@0: // Check if the permission's appId matches a child we manage. michael@0: const InfallibleTArray& browsers = michael@0: aActor->ManagedPBrowserParent(); michael@0: for (uint32_t i = 0; i < browsers.Length(); ++i) { michael@0: TabParent* tab = static_cast(browsers[i]); michael@0: if (tab->OwnOrContainingAppId() == principalAppId) { michael@0: // If the child only runs inBrowserElement content and the principal claims michael@0: // it's not in a browser element, it's lying. michael@0: if (!tab->IsBrowserElement() || inBrowserElement) { michael@0: return true; michael@0: } michael@0: break; michael@0: } michael@0: } michael@0: michael@0: NS_WARNING("Principal is invalid, killing app process"); michael@0: static_cast(aActor)->KillHard(); michael@0: return false; michael@0: } michael@0: michael@0: already_AddRefed michael@0: GetAppPrincipal(uint32_t aAppId) michael@0: { michael@0: nsCOMPtr appsService = do_GetService(APPS_SERVICE_CONTRACTID); michael@0: michael@0: nsCOMPtr app; michael@0: nsresult rv = appsService->GetAppByLocalId(aAppId, getter_AddRefs(app)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: nsString origin; michael@0: rv = app->GetOrigin(origin); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: michael@0: nsCOMPtr uri; michael@0: NS_NewURI(getter_AddRefs(uri), origin); michael@0: michael@0: nsCOMPtr secMan = michael@0: do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); michael@0: michael@0: nsCOMPtr appPrincipal; michael@0: rv = secMan->GetAppCodebasePrincipal(uri, aAppId, false, michael@0: getter_AddRefs(appPrincipal)); michael@0: NS_ENSURE_SUCCESS(rv, nullptr); michael@0: return appPrincipal.forget(); michael@0: } michael@0: michael@0: uint32_t michael@0: CheckPermission(PContentParent* aActor, michael@0: nsIPrincipal* aPrincipal, michael@0: const char* aPermission) michael@0: { michael@0: if (!AssertAppPrincipal(aActor, aPrincipal)) { michael@0: return nsIPermissionManager::DENY_ACTION; michael@0: } michael@0: michael@0: nsCOMPtr pm = michael@0: do_GetService(NS_PERMISSIONMANAGER_CONTRACTID); michael@0: NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION); michael@0: michael@0: // Make sure that `aPermission' is an app permission before checking the origin. michael@0: nsCOMPtr appPrincipal = GetAppPrincipal(aPrincipal->GetAppId()); michael@0: uint32_t appPerm = nsIPermissionManager::UNKNOWN_ACTION; michael@0: nsresult rv = pm->TestExactPermissionFromPrincipal(appPrincipal, aPermission, &appPerm); michael@0: NS_ENSURE_SUCCESS(rv, nsIPermissionManager::UNKNOWN_ACTION); michael@0: // Setting to "deny" in the settings UI should deny everywhere. michael@0: if (appPerm == nsIPermissionManager::UNKNOWN_ACTION || michael@0: appPerm == nsIPermissionManager::DENY_ACTION) { michael@0: return appPerm; michael@0: } michael@0: michael@0: uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION; michael@0: rv = pm->TestExactPermissionFromPrincipal(aPrincipal, aPermission, &permission); michael@0: NS_ENSURE_SUCCESS(rv, nsIPermissionManager::UNKNOWN_ACTION); michael@0: if (permission == nsIPermissionManager::UNKNOWN_ACTION || michael@0: permission == nsIPermissionManager::DENY_ACTION) { michael@0: return permission; michael@0: } michael@0: michael@0: // For browser content (and if the app hasn't explicitly denied this), michael@0: // consider the requesting origin, not the app. michael@0: if (appPerm == nsIPermissionManager::PROMPT_ACTION && michael@0: aPrincipal->GetIsInBrowserElement()) { michael@0: return permission; michael@0: } michael@0: michael@0: // Setting to "prompt" in the settings UI should prompt everywhere in michael@0: // non-browser content. michael@0: if (appPerm == nsIPermissionManager::PROMPT_ACTION || michael@0: permission == nsIPermissionManager::PROMPT_ACTION) { michael@0: return nsIPermissionManager::PROMPT_ACTION; michael@0: } michael@0: michael@0: if (appPerm == nsIPermissionManager::ALLOW_ACTION || michael@0: permission == nsIPermissionManager::ALLOW_ACTION) { michael@0: return nsIPermissionManager::ALLOW_ACTION; michael@0: } michael@0: michael@0: NS_RUNTIMEABORT("Invalid permission value"); michael@0: return nsIPermissionManager::DENY_ACTION; michael@0: } michael@0: michael@0: #else michael@0: michael@0: bool michael@0: AssertAppProcess(mozilla::dom::PBrowserParent* aActor, michael@0: AssertAppProcessType aType, michael@0: const char* aCapability) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AssertAppStatus(mozilla::dom::PBrowserParent* aActor, michael@0: unsigned short aStatus) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: michael@0: bool michael@0: AssertAppProcess(mozilla::dom::PContentParent* aActor, michael@0: AssertAppProcessType aType, michael@0: const char* aCapability) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AssertAppStatus(mozilla::dom::PContentParent* aActor, michael@0: unsigned short aStatus) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AssertAppProcess(mozilla::hal_sandbox::PHalParent* aActor, michael@0: AssertAppProcessType aType, michael@0: const char* aCapability) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: AssertAppPrincipal(mozilla::dom::PContentParent* aActor, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: uint32_t michael@0: CheckPermission(mozilla::dom::PContentParent* aActor, michael@0: nsIPrincipal* aPrincipal, michael@0: const char* aPermission) michael@0: { michael@0: return nsIPermissionManager::ALLOW_ACTION; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: } // namespace mozilla