//------------------------------------------------------------ // Functions to overlay the permission manager calls in case // we're in private browsing mode. //------------------------------------------------------------ nsresult nsSiteSecurityService::AddPermission(nsIURI *aURI, const char *aType, uint32_t aPermission, uint32_t aExpireType, int64_t aExpireTime, bool aIsPrivate) { // Private mode doesn't address user-set (EXPIRE_NEVER) permissions: let // those be stored persistently. if (!aIsPrivate || aExpireType == nsIPermissionManager::EXPIRE_NEVER) { // Not in private mode, or manually-set permission nsCOMPtr<nsIPrincipal> principal; nsresult rv = GetPrincipalForURI(aURI, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); return mPermMgr->AddFromPrincipal(principal, aType, aPermission, aExpireType, aExpireTime); } nsAutoCString host; nsresult rv = GetHost(aURI, host); NS_ENSURE_SUCCESS(rv, rv); SSSLOG(("AddPermission for entry for %s", host.get())); // Update in mPrivateModeHostTable only, so any changes will be rolled // back when exiting private mode. // Note: EXPIRE_NEVER permissions should trump anything that shows up in // the HTTP header, so if there's an EXPIRE_NEVER permission already // don't store anything new. // Currently there's no way to get the type of expiry out of the // permission manager, but that's okay since there's nothing that stores // EXPIRE_NEVER permissions. // PutEntry returns an existing entry if there already is one, or it // creates a new one if there isn't. nsSSSHostEntry* entry = mPrivateModeHostTable.PutEntry(host.get()); if (!entry) { return NS_ERROR_OUT_OF_MEMORY; } SSSLOG(("Created private mode entry for %s", host.get())); // AddPermission() will be called twice if the STS header encountered has // includeSubdomains (first for the main permission and second for the // subdomains permission). If AddPermission() gets called a second time // with the STS_SUBDOMAIN_PERMISSION, we just have to flip that bit in // the nsSSSHostEntry. if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) { entry->mIncludeSubdomains = true; } else if (strcmp(aType, STS_PERMISSION) == 0) { entry->mStsPermission = aPermission; } // Also refresh the expiration time. entry->SetExpireTime(aExpireTime); return NS_OK; }
nsresult nsSiteSecurityService::RemovePermission(const nsCString &aHost, const char *aType, bool aIsPrivate) { // Build up a principal for use with the permission manager. // normalize all URIs with https:// nsCOMPtr<nsIURI> uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + aHost); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIPrincipal> principal; rv = GetPrincipalForURI(uri, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); if (!aIsPrivate) { // Not in private mode: remove permissions persistently. // This means setting the permission to STS_KNOCKOUT in case // this host is on the preload list (so we can override it). return mPermMgr->AddFromPrincipal(principal, aType, STS_KNOCKOUT, nsIPermissionManager::EXPIRE_NEVER, 0); } // Make changes in mPrivateModeHostTable only, so any changes will be // rolled back when exiting private mode. nsSSSHostEntry* entry = mPrivateModeHostTable.GetEntry(aHost.get()); if (!entry) { entry = mPrivateModeHostTable.PutEntry(aHost.get()); if (!entry) { return NS_ERROR_OUT_OF_MEMORY; } SSSLOG(("Created private mode deleted mask for %s", aHost.get())); } if (strcmp(aType, STS_PERMISSION) == 0) { entry->mStsPermission = STS_KNOCKOUT; } else if (strcmp(aType, STS_SUBDOMAIN_PERMISSION) == 0) { entry->mIncludeSubdomains = false; } return NS_OK; }
NS_IMETHODIMP nsStrictTransportSecurityService::IsStsURI(nsIURI* aURI, bool* aResult) { // Should be called on the main thread (or via proxy) since the permission // manager is used and it's not threadsafe. NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_UNEXPECTED); // set default in case if we can't find any STS information *aResult = false; nsAutoCString host; nsresult rv = GetHost(aURI, host); NS_ENSURE_SUCCESS(rv, rv); const nsSTSPreload *preload = nullptr; nsSTSHostEntry *pbEntry = nullptr; if (mInPrivateMode) { pbEntry = mPrivateModeHostTable.GetEntry(host.get()); } nsCOMPtr<nsIPrincipal> principal; rv = GetPrincipalForURI(aURI, getter_AddRefs(principal)); NS_ENSURE_SUCCESS(rv, rv); uint32_t permMgrPermission; rv = mPermMgr->TestExactPermissionFromPrincipal(principal, STS_PERMISSION, &permMgrPermission); NS_ENSURE_SUCCESS(rv, rv); // First check the exact host. This involves first checking for an entry in // the private browsing table. If that entry exists, we don't want to check // in either the permission manager or the preload list. We only want to use // the stored permission if it is not a knockout entry, however. // Additionally, if it is a knockout entry, we want to stop looking for data // on the host, because the knockout entry indicates "we have no information // regarding the sts status of this host". if (pbEntry && pbEntry->mStsPermission != STS_UNSET) { STSLOG(("Found private browsing table entry for %s", host.get())); if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) { *aResult = true; return NS_OK; } } // Next we look in the permission manager. Same story here regarding // knockout entries. else if (permMgrPermission != STS_UNSET) { STSLOG(("Found permission manager entry for %s", host.get())); if (permMgrPermission == STS_SET) { *aResult = true; return NS_OK; } } // Finally look in the preloaded list. This is the exact host, // so if an entry exists at all, this host is sts. else if (GetPreloadListEntry(host.get())) { STSLOG(("%s is a preloaded STS host", host.get())); *aResult = true; return NS_OK; } // Used for testing permissions as we walk up the domain tree. nsCOMPtr<nsIURI> domainWalkURI; nsCOMPtr<nsIPrincipal> domainWalkPrincipal; const char *subdomain; STSLOG(("no HSTS data for %s found, walking up domain", host.get())); uint32_t offset = 0; for (offset = host.FindChar('.', offset) + 1; offset > 0; offset = host.FindChar('.', offset) + 1) { subdomain = host.get() + offset; // If we get an empty string, don't continue. if (strlen(subdomain) < 1) { break; } if (mInPrivateMode) { pbEntry = mPrivateModeHostTable.GetEntry(subdomain); } // normalize all URIs with https:// rv = NS_NewURI(getter_AddRefs(domainWalkURI), NS_LITERAL_CSTRING("https://") + Substring(host, offset)); NS_ENSURE_SUCCESS(rv, rv); rv = GetPrincipalForURI(domainWalkURI, getter_AddRefs(domainWalkPrincipal)); NS_ENSURE_SUCCESS(rv, rv); rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal, STS_PERMISSION, &permMgrPermission); NS_ENSURE_SUCCESS(rv, rv); // Do the same thing as with the exact host, except now we're looking at // ancestor domains of the original host. So, we have to look at the // include subdomains permissions (although we still have to check for the // STS_PERMISSION first to check that this is an sts host and not a // knockout entry - and again, if it is a knockout entry, we stop looking // for data on it and skip to the next higher up ancestor domain). if (pbEntry && pbEntry->mStsPermission != STS_UNSET) { STSLOG(("Found private browsing table entry for %s", subdomain)); if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) { *aResult = pbEntry->mIncludeSubdomains; break; } } else if (permMgrPermission != STS_UNSET) { STSLOG(("Found permission manager entry for %s", subdomain)); if (permMgrPermission == STS_SET) { uint32_t subdomainPermission; rv = mPermMgr->TestExactPermissionFromPrincipal(domainWalkPrincipal, STS_SUBDOMAIN_PERMISSION, &subdomainPermission); NS_ENSURE_SUCCESS(rv, rv); *aResult = (subdomainPermission == STS_SET); break; } } // This is an ancestor, so if we get a match, we have to check if the // preloaded entry includes subdomains. else if ((preload = GetPreloadListEntry(subdomain)) != nullptr) { if (preload->mIncludeSubdomains) { STSLOG(("%s is a preloaded STS host", subdomain)); *aResult = true; break; } } STSLOG(("no HSTS data for %s found, walking up domain", subdomain)); } // Use whatever we ended up with, which defaults to false. return NS_OK; }