bool AssertAppProcess(TabContext& aContext, AssertAppProcessType aType, const char* aCapability) { nsCOMPtr<mozIApplication> app = aContext.GetOwnOrContainingApp(); return CheckAppTypeHelper(app, aType, aCapability, aContext.IsBrowserElement()); }
uint32_t ContentProcessManager::GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId, const TabId& aChildTabId) { uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; if (aChildCpId && aChildTabId) { TabContext tabContext; if (GetTabContextByProcessAndTabId(aChildCpId, aChildTabId, &tabContext)) { appId = tabContext.OwnOrContainingAppId(); } } return appId; }
nsresult NeckoParent::OfflineNotification(nsISupports *aSubject) { nsCOMPtr<nsIAppOfflineInfo> info(do_QueryInterface(aSubject)); if (!info) { return NS_OK; } uint32_t targetAppId = NECKO_UNKNOWN_APP_ID; info->GetAppId(&targetAppId); nsTArray<TabContext> contextArray = static_cast<ContentParent*>(Manager())->GetManagedTabContext(); for (uint32_t i = 0; i < contextArray.Length(); ++i) { TabContext tabContext = contextArray[i]; uint32_t appId = tabContext.OwnOrContainingAppId(); if (appId == targetAppId) { if (gIOService) { bool offline = false; nsresult rv = gIOService->IsAppOffline(appId, &offline); if (NS_FAILED(rv)) { printf_stderr("Unexpected - NeckoParent: " "appId not found by isAppOffline(): %u\n", appId); break; } if (!SendAppOfflineStatus(appId, offline)) { printf_stderr("NeckoParent: " "SendAppOfflineStatus failed for appId: %u\n", appId); } // Once we found the targetAppId, we don't need to continue break; } } } // XPCShells don't have any TabParents // Just send the ipdl message to the child process. if (!UsingNeckoIPCSecurity()) { bool offline = false; gIOService->IsAppOffline(targetAppId, &offline); if (!SendAppOfflineStatus(targetAppId, offline)) { printf_stderr("NeckoParent: " "SendAppOfflineStatus failed for targetAppId: %u\n", targetAppId); } } return NS_OK; }
bool AssertAppStatus(TabContext& aContext, unsigned short aStatus) { nsCOMPtr<mozIApplication> app = aContext.GetOwnOrContainingApp(); return CheckAppStatusHelper(app, aStatus); }
// static bool IndexedDatabaseManager::TabContextMayAccessOrigin(const TabContext& aContext, const nsACString& aOrigin) { NS_ASSERTION(!aOrigin.IsEmpty(), "Empty origin!"); // If aContext is for a browser element, it's allowed only to access other // browser elements. But if aContext is not for a browser element, it may // access both browser and non-browser elements. nsAutoCString pattern; QuotaManager::GetOriginPatternStringMaybeIgnoreBrowser( aContext.OwnOrContainingAppId(), aContext.IsBrowserElement(), pattern); return PatternMatchesOrigin(pattern, aOrigin); }
bool AssertAppProcess(TabContext& aContext, AssertAppProcessType aType, const char* aCapability) { const mozilla::DocShellOriginAttributes& attr = aContext.OriginAttributesRef(); nsCString suffix; attr.CreateSuffix(suffix); if (!aContext.SignedPkgOriginNoSuffix().IsEmpty()) { LOG("TabContext owning signed package origin: %s, originAttr; %s\n", nsCString(aContext.SignedPkgOriginNoSuffix()).get(), suffix.get()); } // Do a origin-based permission check if the TabContext owns a signed package. if (!aContext.SignedPkgOriginNoSuffix().IsEmpty() && (ASSERT_APP_HAS_PERMISSION == aType || ASSERT_APP_PROCESS_PERMISSION == aType)) { nsCString origin = aContext.SignedPkgOriginNoSuffix() + suffix; return CheckOriginPermission(origin, aCapability); } nsCOMPtr<mozIApplication> app = aContext.GetOwnOrContainingApp(); return CheckAppTypeHelper(app, aType, aCapability, aContext.IsBrowserElement()); }
bool TabContext::UpdateTabContextAfterSwap(const TabContext& aContext) { // This is only used after already initialized. MOZ_ASSERT(mInitialized); // The only permissable change is to `mIsMozBrowserElement`. All other fields // must match for the change to be accepted. if (aContext.OwnAppId() != OwnAppId() || aContext.mContainingAppId != mContainingAppId || aContext.mOriginAttributes != mOriginAttributes || aContext.mSignedPkgOriginNoSuffix != mSignedPkgOriginNoSuffix) { return false; } mIsMozBrowserElement = aContext.mIsMozBrowserElement; return true; }
TabContext::TabContext(const IPCTabContext& aParams) : mInitialized(true) { const IPCTabAppBrowserContext& appBrowser = aParams.appBrowserContext(); switch(appBrowser.type()) { case IPCTabAppBrowserContext::TPopupIPCTabContext: { const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext(); TabContext *context; if (ipcContext.openerParent()) { context = static_cast<TabParent*>(ipcContext.openerParent()); if (context->IsBrowserElement() && !ipcContext.isBrowserElement()) { // If the TabParent corresponds to a browser element, then it can only // open other browser elements, for security reasons. We should have // checked this before calling the TabContext constructor, so this is // a fatal error. MOZ_CRASH(); } } else if (ipcContext.openerChild()) { context = static_cast<TabChild*>(ipcContext.openerChild()); } else { // This should be unreachable because PopupIPCTabContext::opener is not a // nullable field. MOZ_CRASH(); } // If ipcContext is a browser element, then the opener's app-id becomes // our containing app-id. Otherwise, our own and containing app-ids are // directly inherited from our opener. if (ipcContext.isBrowserElement()) { mIsBrowser = true; mOwnAppId = nsIScriptSecurityManager::NO_APP_ID; mContainingAppId = context->OwnAppId(); } else { mIsBrowser = false; mOwnAppId = context->mOwnAppId; mContainingAppId = context->mContainingAppId; } break; } case IPCTabAppBrowserContext::TAppFrameIPCTabContext: { const AppFrameIPCTabContext &ipcContext = appBrowser.get_AppFrameIPCTabContext(); mIsBrowser = false; mOwnAppId = ipcContext.ownAppId(); mContainingAppId = ipcContext.appFrameOwnerAppId(); break; } case IPCTabAppBrowserContext::TBrowserFrameIPCTabContext: { const BrowserFrameIPCTabContext &ipcContext = appBrowser.get_BrowserFrameIPCTabContext(); mIsBrowser = true; mOwnAppId = nsIScriptSecurityManager::NO_APP_ID; mContainingAppId = ipcContext.browserFrameOwnerAppId(); break; } case IPCTabAppBrowserContext::TVanillaFrameIPCTabContext: { mIsBrowser = false; mOwnAppId = nsIScriptSecurityManager::NO_APP_ID; mContainingAppId = nsIScriptSecurityManager::NO_APP_ID; break; } default: { MOZ_CRASH(); } } mScrollingBehavior = aParams.scrollingBehavior(); }
MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams) : mInvalidReason(nullptr) { bool isMozBrowserElement = false; bool isPrerendered = false; uint32_t containingAppId = NO_APP_ID; DocShellOriginAttributes originAttributes; nsAutoCString signedPkgOriginNoSuffix; nsAutoString presentationURL; UIStateChangeType showAccelerators = UIStateChangeType_NoChange; UIStateChangeType showFocusRings = UIStateChangeType_NoChange; switch(aParams.type()) { case IPCTabContext::TPopupIPCTabContext: { const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext(); TabContext *context; if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) { context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent()); if (!context) { mInvalidReason = "Child is-browser process tried to " "open a null tab."; return; } if (context->IsMozBrowserElement() && !ipcContext.isMozBrowserElement()) { // If the TabParent corresponds to a browser element, then it can only // open other browser elements, for security reasons. We should have // checked this before calling the TabContext constructor, so this is // a fatal error. mInvalidReason = "Child is-browser process tried to " "open a non-browser tab."; return; } } else if (ipcContext.opener().type() == PBrowserOrId::TPBrowserChild) { context = static_cast<TabChild*>(ipcContext.opener().get_PBrowserChild()); } else if (ipcContext.opener().type() == PBrowserOrId::TTabId) { // We should never get here because this PopupIPCTabContext is only // used for allocating a new tab id, not for allocating a PBrowser. mInvalidReason = "Child process tried to open an tab without the opener information."; return; } else { // This should be unreachable because PopupIPCTabContext::opener is not a // nullable field. mInvalidReason = "PopupIPCTabContext::opener was null (?!)."; return; } // Browser elements can't nest other browser elements. So if // our opener is browser element, we must be a new DOM window // opened by it. In that case we inherit our containing app ID // (if any). // // Otherwise, we're a new app window and we inherit from our // opener app. isMozBrowserElement = ipcContext.isMozBrowserElement(); originAttributes = context->mOriginAttributes; if (isMozBrowserElement) { containingAppId = context->OwnOrContainingAppId(); } else { containingAppId = context->mContainingAppId; } break; } case IPCTabContext::TFrameIPCTabContext: { const FrameIPCTabContext &ipcContext = aParams.get_FrameIPCTabContext(); isMozBrowserElement = ipcContext.isMozBrowserElement(); isPrerendered = ipcContext.isPrerendered(); containingAppId = ipcContext.frameOwnerAppId(); signedPkgOriginNoSuffix = ipcContext.signedPkgOriginNoSuffix(); presentationURL = ipcContext.presentationURL(); showAccelerators = ipcContext.showAccelerators(); showFocusRings = ipcContext.showFocusRings(); originAttributes = ipcContext.originAttributes(); break; } case IPCTabContext::TUnsafeIPCTabContext: { // XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow. // It is meant as a temporary solution until service workers can // provide a TabChild equivalent. Don't allow this on b2g since // it might be used to escalate privileges. #ifdef MOZ_B2G mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported."; return; #endif if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) { mInvalidReason = "ServiceWorkers should be enabled."; return; } containingAppId = NO_APP_ID; break; } default: { MOZ_CRASH(); } } nsCOMPtr<mozIApplication> ownApp; if (!isMozBrowserElement) { // mAppId corresponds to OwnOrContainingAppId; if isMozBrowserElement is // false then it's ownApp otherwise it's containingApp ownApp = GetAppForId(originAttributes.mAppId); if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) { mInvalidReason = "Got an ownAppId that didn't correspond to an app."; return; } } nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId); if ((containingApp == nullptr) != (containingAppId == NO_APP_ID)) { mInvalidReason = "Got a containingAppId that didn't correspond to an app."; return; } bool rv; rv = mTabContext.SetTabContext(isMozBrowserElement, isPrerendered, ownApp, containingApp, showAccelerators, showFocusRings, originAttributes, signedPkgOriginNoSuffix, presentationURL); if (!rv) { mInvalidReason = "Couldn't initialize TabContext."; } }
PRemoteOpenFileParent* NeckoParent::AllocPRemoteOpenFileParent(const SerializedLoadContext& aSerialized, const URIParams& aURI, const OptionalURIParams& aAppURI) { nsCOMPtr<nsIURI> uri = DeserializeURI(aURI); nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(uri); if (!fileURL) { return nullptr; } // security checks if (UsingNeckoIPCSecurity()) { nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); if (!appsService) { return nullptr; } bool haveValidBrowser = false; bool hasManage = false; nsCOMPtr<mozIApplication> mozApp; nsTArray<TabContext> contextArray = static_cast<ContentParent*>(Manager())->GetManagedTabContext(); for (uint32_t i = 0; i < contextArray.Length(); i++) { TabContext tabContext = contextArray[i]; uint32_t appId = tabContext.OwnOrContainingAppId(); // Note: this enforces that SerializedLoadContext.appID is one of the apps // in the child process, but there's currently no way to verify the // request is not from a different app in that process. if (appId == aSerialized.mAppId) { nsresult rv = appsService->GetAppByLocalId(appId, getter_AddRefs(mozApp)); if (NS_FAILED(rv) || !mozApp) { break; } rv = mozApp->HasPermission("webapps-manage", &hasManage); if (NS_FAILED(rv)) { break; } haveValidBrowser = true; break; } } if (!haveValidBrowser) { return nullptr; } nsAutoCString requestedPath; fileURL->GetPath(requestedPath); NS_UnescapeURL(requestedPath); // Check if we load the whitelisted app uri for the neterror page. bool netErrorWhiteList = false; nsCOMPtr<nsIURI> appUri = DeserializeURI(aAppURI); if (appUri) { nsAdoptingString netErrorURI; netErrorURI = Preferences::GetString("b2g.neterror.url"); if (netErrorURI) { nsAutoCString spec; appUri->GetSpec(spec); netErrorWhiteList = spec.Equals(NS_ConvertUTF16toUTF8(netErrorURI).get()); } } // Check if we load a resource from the shared theme url space. // If we try to load the theme but have no permission, refuse to load. bool themeWhitelist = false; if (Preferences::GetBool("dom.mozApps.themable") && appUri) { nsAutoCString origin; nsPrincipal::GetOriginForURI(appUri, getter_Copies(origin)); nsAutoCString themeOrigin; themeOrigin = Preferences::GetCString("b2g.theme.origin"); themeWhitelist = origin.Equals(themeOrigin); if (themeWhitelist) { bool hasThemePerm = false; mozApp->HasPermission("themeable", &hasThemePerm); if (!hasThemePerm) { return nullptr; } } } if (hasManage || netErrorWhiteList || themeWhitelist) { // webapps-manage permission means allow reading any application.zip file // in either the regular webapps directory, or the core apps directory (if // we're using one). NS_NAMED_LITERAL_CSTRING(appzip, "/application.zip"); nsAutoCString pathEnd; requestedPath.Right(pathEnd, appzip.Length()); if (!pathEnd.Equals(appzip)) { return nullptr; } nsAutoCString pathStart; requestedPath.Left(pathStart, mWebAppsBasePath.Length()); if (!pathStart.Equals(mWebAppsBasePath)) { if (mCoreAppsBasePath.IsEmpty()) { return nullptr; } requestedPath.Left(pathStart, mCoreAppsBasePath.Length()); if (!pathStart.Equals(mCoreAppsBasePath)) { return nullptr; } } // Finally: make sure there are no "../" in URI. // Note: not checking for symlinks (would cause I/O for each path // component). So it's up to us to avoid creating symlinks that could // provide attack vectors. if (PL_strnstr(requestedPath.BeginReading(), "/../", requestedPath.Length())) { printf_stderr("NeckoParent::AllocPRemoteOpenFile: " "FATAL error: requested file URI '%s' contains '/../' " "KILLING CHILD PROCESS\n", requestedPath.get()); return nullptr; } } else { // regular packaged apps can only access their own application.zip file nsAutoString basePath; nsresult rv = mozApp->GetBasePath(basePath); if (NS_FAILED(rv)) { return nullptr; } nsAutoString uuid; rv = mozApp->GetId(uuid); if (NS_FAILED(rv)) { return nullptr; } nsPrintfCString mustMatch("%s/%s/application.zip", NS_LossyConvertUTF16toASCII(basePath).get(), NS_LossyConvertUTF16toASCII(uuid).get()); if (!requestedPath.Equals(mustMatch)) { printf_stderr("NeckoParent::AllocPRemoteOpenFile: " "FATAL error: app without webapps-manage permission is " "requesting file '%s' but is only allowed to open its " "own application.zip at %s: KILLING CHILD PROCESS\n", requestedPath.get(), mustMatch.get()); return nullptr; } } } RemoteOpenFileParent* parent = new RemoteOpenFileParent(fileURL); return parent; }
TabContext::TabContext(const IPCTabContext& aParams) : mInitialized(true) { const IPCTabAppBrowserContext& appBrowser = aParams.appBrowserContext(); switch(appBrowser.type()) { case IPCTabAppBrowserContext::TPopupIPCTabContext: { const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext(); TabContext *context; if (ipcContext.openerParent()) { context = static_cast<TabParent*>(ipcContext.openerParent()); if (context->IsBrowserElement() && !ipcContext.isBrowserElement()) { // If the TabParent corresponds to a browser element, then it can only // open other browser elements, for security reasons. We should have // checked this before calling the TabContext constructor, so this is // a fatal error. MOZ_CRASH(); } } else if (ipcContext.openerChild()) { context = static_cast<TabChild*>(ipcContext.openerChild()); } else { // This should be unreachable because PopupIPCTabContext::opener is not a // nullable field. MOZ_CRASH(); } // Browser elements can't nest other browser elements. So if // our opener is browser element, we must be a new DOM window // opened by it. In that case we inherit our containing app ID // (if any). // // Otherwise, we're a new app window and we inherit from our // opener app. if (ipcContext.isBrowserElement()) { mIsBrowser = true; mOwnAppId = nsIScriptSecurityManager::NO_APP_ID; mContainingAppId = context->OwnOrContainingAppId(); } else { mIsBrowser = false; mOwnAppId = context->mOwnAppId; mContainingAppId = context->mContainingAppId; } break; } case IPCTabAppBrowserContext::TAppFrameIPCTabContext: { const AppFrameIPCTabContext &ipcContext = appBrowser.get_AppFrameIPCTabContext(); mIsBrowser = false; mOwnAppId = ipcContext.ownAppId(); mContainingAppId = ipcContext.appFrameOwnerAppId(); break; } case IPCTabAppBrowserContext::TBrowserFrameIPCTabContext: { const BrowserFrameIPCTabContext &ipcContext = appBrowser.get_BrowserFrameIPCTabContext(); mIsBrowser = true; mOwnAppId = nsIScriptSecurityManager::NO_APP_ID; mContainingAppId = ipcContext.browserFrameOwnerAppId(); break; } case IPCTabAppBrowserContext::TVanillaFrameIPCTabContext: { mIsBrowser = false; mOwnAppId = nsIScriptSecurityManager::NO_APP_ID; mContainingAppId = nsIScriptSecurityManager::NO_APP_ID; break; } default: { MOZ_CRASH(); } } mScrollingBehavior = aParams.scrollingBehavior(); }
MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams) : mInvalidReason(nullptr) { bool isBrowser = false; uint32_t ownAppId = NO_APP_ID; uint32_t containingAppId = NO_APP_ID; const IPCTabAppBrowserContext& appBrowser = aParams.appBrowserContext(); switch(appBrowser.type()) { case IPCTabAppBrowserContext::TPopupIPCTabContext: { const PopupIPCTabContext &ipcContext = appBrowser.get_PopupIPCTabContext(); TabContext *context; if (ipcContext.openerParent()) { context = static_cast<TabParent*>(ipcContext.openerParent()); if (context->IsBrowserElement() && !ipcContext.isBrowserElement()) { // If the TabParent corresponds to a browser element, then it can only // open other browser elements, for security reasons. We should have // checked this before calling the TabContext constructor, so this is // a fatal error. mInvalidReason = "Child is-browser process tried to " "open a non-browser tab."; return; } } else if (ipcContext.openerChild()) { context = static_cast<TabChild*>(ipcContext.openerChild()); } else { // This should be unreachable because PopupIPCTabContext::opener is not a // nullable field. mInvalidReason = "PopupIPCTabContext::opener was null (?!)."; return; } // Browser elements can't nest other browser elements. So if // our opener is browser element, we must be a new DOM window // opened by it. In that case we inherit our containing app ID // (if any). // // Otherwise, we're a new app window and we inherit from our // opener app. if (ipcContext.isBrowserElement()) { isBrowser = true; ownAppId = NO_APP_ID; containingAppId = context->OwnOrContainingAppId(); } else { isBrowser = false; ownAppId = context->mOwnAppId; containingAppId = context->mContainingAppId; } break; } case IPCTabAppBrowserContext::TAppFrameIPCTabContext: { const AppFrameIPCTabContext &ipcContext = appBrowser.get_AppFrameIPCTabContext(); isBrowser = false; ownAppId = ipcContext.ownAppId(); containingAppId = ipcContext.appFrameOwnerAppId(); break; } case IPCTabAppBrowserContext::TBrowserFrameIPCTabContext: { const BrowserFrameIPCTabContext &ipcContext = appBrowser.get_BrowserFrameIPCTabContext(); isBrowser = true; ownAppId = NO_APP_ID; containingAppId = ipcContext.browserFrameOwnerAppId(); break; } case IPCTabAppBrowserContext::TVanillaFrameIPCTabContext: { isBrowser = false; ownAppId = NO_APP_ID; containingAppId = NO_APP_ID; break; } default: { MOZ_CRASH(); } } nsCOMPtr<mozIApplication> ownApp = GetAppForId(ownAppId); if ((ownApp == nullptr) != (ownAppId == NO_APP_ID)) { mInvalidReason = "Got an ownAppId that didn't correspond to an app."; return; } nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId); if ((containingApp == nullptr) != (containingAppId == NO_APP_ID)) { mInvalidReason = "Got a containingAppId that didn't correspond to an app."; return; } bool rv; if (isBrowser) { rv = mTabContext.SetTabContextForBrowserFrame(containingApp, aParams.scrollingBehavior()); } else { rv = mTabContext.SetTabContextForAppFrame(ownApp, containingApp, aParams.scrollingBehavior()); } if (!rv) { mInvalidReason = "Couldn't initialize TabContext."; } }