NS_IMETHODIMP nsSiteSecurityService::IsSecureHost(uint32_t aType, const char* aHost, uint32_t aFlags, 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); // Only HSTS is supported at the moment. NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS, NS_ERROR_NOT_IMPLEMENTED); /* An IP address never qualifies as a secure URI. */ if (HostIsIPAddress(aHost)) { *aResult = false; return NS_OK; } nsCOMPtr<nsIURI> uri; nsDependentCString hostString(aHost); nsresult rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("https://") + hostString); NS_ENSURE_SUCCESS(rv, rv); return IsSecureURI(aType, uri, aFlags, aResult); }
NS_IMETHODIMP nsSiteSecurityService::IsSecureURI(uint32_t aType, nsIURI* aURI, uint32_t aFlags, 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); // Only HSTS is supported at the moment. NS_ENSURE_TRUE(aType == nsISiteSecurityService::HEADER_HSTS, NS_ERROR_NOT_IMPLEMENTED); // 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); /* An IP address never qualifies as a secure URI. */ if (HostIsIPAddress(host.BeginReading())) { return NS_OK; } // Holepunch chart.apis.google.com and subdomains. if (host.Equals(NS_LITERAL_CSTRING("chart.apis.google.com")) || StringEndsWith(host, NS_LITERAL_CSTRING(".chart.apis.google.com"))) { return NS_OK; } const nsSTSPreload *preload = nullptr; nsSSSHostEntry *pbEntry = nullptr; bool isPrivate = aFlags & nsISocketProvider::NO_PERMANENT_STORAGE; if (isPrivate) { 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) { SSSLOG(("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) { SSSLOG(("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())) { SSSLOG(("%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; SSSLOG(("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 (isPrivate) { 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) { SSSLOG(("Found private browsing table entry for %s", subdomain)); if (!pbEntry->IsExpired() && pbEntry->mStsPermission == STS_SET) { *aResult = pbEntry->mIncludeSubdomains; break; } } else if (permMgrPermission != STS_UNSET) { SSSLOG(("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) { SSSLOG(("%s is a preloaded STS host", subdomain)); *aResult = true; break; } } SSSLOG(("no HSTS data for %s found, walking up domain", subdomain)); } // Use whatever we ended up with, which defaults to false. return NS_OK; }