NS_IMETHODIMP nsCrossSiteListenerProxy::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { mRequestApproved = NS_SUCCEEDED(CheckRequestApproved(aRequest, PR_FALSE)); if (!mRequestApproved) { if (nsXMLHttpRequest::sAccessControlCache) { nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); if (channel) { nsCOMPtr<nsIURI> uri; NS_GetFinalChannelURI(channel, getter_AddRefs(uri)); if (uri) { nsXMLHttpRequest::sAccessControlCache-> RemoveEntries(uri, mRequestingPrincipal); } } } aRequest->Cancel(NS_ERROR_DOM_BAD_URI); mOuterListener->OnStartRequest(aRequest, aContext); return NS_ERROR_DOM_BAD_URI; } return mOuterListener->OnStartRequest(aRequest, aContext); }
NS_IMETHODIMP FetchDriver::OnRedirectVerifyCallback(nsresult aResult) { // On a successful redirect we perform the following substeps of Section 4.2, // step 4.10. if (NS_SUCCEEDED(aResult)) { // Step 4.10.3 "Set request's url to locationURL." so that when we set the // Response's URL from the Request's URL in Section 4, step 6, we get the // final value. nsCOMPtr<nsIURI> newURI; nsresult rv = NS_GetFinalChannelURI(mNewRedirectChannel, getter_AddRefs(newURI)); if (NS_WARN_IF(NS_FAILED(rv))) { aResult = rv; } else { // We need to update our request's URL. nsAutoCString newUrl; newURI->GetSpec(newUrl); mRequest->SetURL(newUrl); } } if (NS_FAILED(aResult)) { mOldRedirectChannel->Cancel(aResult); } mOldRedirectChannel = nullptr; mNewRedirectChannel = nullptr; mRedirectCallback->OnRedirectVerifyCallback(aResult); mRedirectCallback = nullptr; return NS_OK; }
/* * Check that this channel passes all security checks. Returns an error code * if this requesst should not be permitted. */ nsresult nsContentSecurityManager::CheckChannel(nsIChannel* aChannel) { nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); MOZ_ASSERT(loadInfo); nsCOMPtr<nsIURI> uri; nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); // Handle cookie policies uint32_t cookiePolicy = loadInfo->GetCookiePolicy(); if (cookiePolicy == nsILoadInfo::SEC_COOKIES_SAME_ORIGIN) { nsIPrincipal* loadingPrincipal = loadInfo->LoadingPrincipal(); // It doesn't matter what we pass for the third, data-inherits, argument. // Any protocol which inherits won't pay attention to cookies anyway. rv = loadingPrincipal->CheckMayLoad(uri, false, false); if (NS_FAILED(rv)) { AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS); } } else if (cookiePolicy == nsILoadInfo::SEC_COOKIES_OMIT) { AddLoadFlags(aChannel, nsIRequest::LOAD_ANONYMOUS); } nsSecurityFlags securityMode = loadInfo->GetSecurityMode(); // CORS mode is handled by nsCORSListenerProxy if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { if (NS_HasBeenCrossOrigin(aChannel)) { loadInfo->MaybeIncreaseTainting(LoadTainting::CORS); } return NS_OK; } // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) || (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) { rv = DoSOPChecks(uri, loadInfo, aChannel); NS_ENSURE_SUCCESS(rv, rv); } if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) || (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) { if (NS_HasBeenCrossOrigin(aChannel)) { loadInfo->MaybeIncreaseTainting(LoadTainting::Opaque); } // Please note that DoCheckLoadURIChecks should only be enforced for // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set // within the loadInfo, then then CheckLoadURIWithPrincipal is performed // within nsCorsListenerProxy rv = DoCheckLoadURIChecks(uri, loadInfo); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
NS_IMETHODIMP EventSource::AsyncOnChannelRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel, uint32_t aFlags, nsIAsyncVerifyRedirectCallback *aCallback) { nsCOMPtr<nsIRequest> aOldRequest = do_QueryInterface(aOldChannel); NS_PRECONDITION(aOldRequest, "Redirect from a null request?"); nsresult rv = CheckHealthOfRequestCallback(aOldRequest); NS_ENSURE_SUCCESS(rv, rv); NS_PRECONDITION(aNewChannel, "Redirect without a channel?"); nsCOMPtr<nsIURI> newURI; rv = NS_GetFinalChannelURI(aNewChannel, getter_AddRefs(newURI)); NS_ENSURE_SUCCESS(rv, rv); bool isValidScheme = (NS_SUCCEEDED(newURI->SchemeIs("http", &isValidScheme)) && isValidScheme) || (NS_SUCCEEDED(newURI->SchemeIs("https", &isValidScheme)) && isValidScheme); rv = CheckInnerWindowCorrectness(); if (NS_FAILED(rv) || !isValidScheme) { DispatchFailConnection(); return NS_ERROR_DOM_SECURITY_ERR; } // update our channel mHttpChannel = do_QueryInterface(aNewChannel); NS_ENSURE_STATE(mHttpChannel); rv = SetupHttpChannel(); NS_ENSURE_SUCCESS(rv, rv); if ((aFlags & nsIChannelEventSink::REDIRECT_PERMANENT) != 0) { rv = NS_GetFinalChannelURI(mHttpChannel, getter_AddRefs(mSrc)); NS_ENSURE_SUCCESS(rv, rv); } aCallback->OnRedirectVerifyCallback(NS_OK); return NS_OK; }
/** * Returns whether or not the sub-resource about to be loaded is eligible * for integrity checks. If it's not, the checks will be skipped and the * sub-resource will be loaded. */ static nsresult IsEligible(nsIChannel* aChannel, const CORSMode aCORSMode, const nsIDocument* aDocument) { NS_ENSURE_ARG_POINTER(aDocument); if (!aChannel) { SRILOG(("SRICheck::IsEligible, null channel")); return NS_ERROR_SRI_NOT_ELIGIBLE; } // Was the sub-resource loaded via CORS? if (aCORSMode != CORS_NONE) { SRILOG(("SRICheck::IsEligible, CORS mode")); return NS_OK; } nsCOMPtr<nsIURI> finalURI; nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIURI> originalURI; rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString requestSpec; rv = originalURI->GetSpec(requestSpec); NS_ENSURE_SUCCESS(rv, rv); if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) { nsAutoCString documentSpec, finalSpec; aDocument->GetDocumentURI()->GetAsciiSpec(documentSpec); if (finalURI) { finalURI->GetSpec(finalSpec); } SRILOG(("SRICheck::IsEligible, documentURI=%s; requestURI=%s; finalURI=%s", documentSpec.get(), requestSpec.get(), finalSpec.get())); } // Is the sub-resource same-origin? nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); if (NS_SUCCEEDED(ssm->CheckSameOriginURI(aDocument->GetDocumentURI(), finalURI, false))) { SRILOG(("SRICheck::IsEligible, same-origin")); return NS_OK; } SRILOG(("SRICheck::IsEligible, NOT same origin")); NS_ConvertUTF8toUTF16 requestSpecUTF16(requestSpec); const char16_t* params[] = { requestSpecUTF16.get() }; nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), aDocument, nsContentUtils::eSECURITY_PROPERTIES, "IneligibleResource", params, ArrayLength(params)); return NS_ERROR_SRI_NOT_ELIGIBLE; }
/* * Based on the security flags provided in the loadInfo of the channel, * doContentSecurityCheck() performs the following content security checks * before opening the channel: * * (1) Same Origin Policy Check (if applicable) * (2) Allow Cross Origin but perform sanity checks whether a principal * is allowed to access the following URL. * (3) Perform CORS check (if applicable) * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...) * * @param aChannel * The channel to perform the security checks on. * @param aInAndOutListener * The streamListener that is passed to channel->AsyncOpen2() that is now potentially * wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs * to be set as new streamListener on the channel. */ nsresult nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel, nsCOMPtr<nsIStreamListener>& aInAndOutListener) { NS_ENSURE_ARG(aChannel); nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); if (!loadInfo) { MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks"); return NS_ERROR_UNEXPECTED; } // if dealing with a redirected channel then we have already installed // streamlistener and redirect proxies and so we are done. if (loadInfo->GetInitialSecurityCheckDone()) { return NS_OK; } // make sure that only one of the five security flags is set in the loadinfo // e.g. do not require same origin and allow cross origin at the same time nsresult rv = ValidateSecurityFlags(loadInfo); NS_ENSURE_SUCCESS(rv, rv); // since aChannel was openend using asyncOpen2() we have to make sure // that redirects of that channel also get openend using asyncOpen2() // please note that some implementations of ::AsyncOpen2 might already // have set that flag to true (e.g. nsViewSourceChannel) in which case // we just set the flag again. rv = loadInfo->SetEnforceSecurity(true); NS_ENSURE_SUCCESS(rv, rv); if (loadInfo->GetSecurityMode() == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener); NS_ENSURE_SUCCESS(rv, rv); } rv = CheckChannel(aChannel); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIURI> finalChannelURI; rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI)); NS_ENSURE_SUCCESS(rv, rv); // Perform all ContentPolicy checks (MixedContent, CSP, ...) rv = DoContentSecurityChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); // now lets set the initalSecurityFlag for subsequent calls rv = loadInfo->SetInitialSecurityCheckDone(true); NS_ENSURE_SUCCESS(rv, rv); // all security checks passed - lets allow the load return NS_OK; }
/** * Returns whether or not the sub-resource about to be loaded is eligible * for integrity checks. If it's not, the checks will be skipped and the * sub-resource will be loaded. */ static nsresult IsEligible(nsIChannel* aChannel, mozilla::LoadTainting aTainting, const nsACString& aSourceFileURI, nsIConsoleReportCollector* aReporter) { NS_ENSURE_ARG_POINTER(aReporter); if (!aChannel) { SRILOG(("SRICheck::IsEligible, null channel")); return NS_ERROR_SRI_NOT_ELIGIBLE; } // Was the sub-resource loaded via CORS? if (aTainting == LoadTainting::CORS) { SRILOG(("SRICheck::IsEligible, CORS mode")); return NS_OK; } nsCOMPtr<nsIURI> finalURI; nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalURI)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIURI> originalURI; rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI)); NS_ENSURE_SUCCESS(rv, rv); nsAutoCString requestSpec; rv = originalURI->GetSpec(requestSpec); NS_ENSURE_SUCCESS(rv, rv); if (MOZ_LOG_TEST(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug)) { SRILOG(("SRICheck::IsEligible, requestURI=%s; finalURI=%s", requestSpec.get(), finalURI ? finalURI->GetSpecOrDefault().get() : "")); } // Is the sub-resource same-origin? if (aTainting == LoadTainting::Basic) { SRILOG(("SRICheck::IsEligible, same-origin")); return NS_OK; } SRILOG(("SRICheck::IsEligible, NOT same origin")); NS_ConvertUTF8toUTF16 requestSpecUTF16(requestSpec); nsTArray<nsString> params; params.AppendElement(requestSpecUTF16); aReporter->AddConsoleReport(nsIScriptError::errorFlag, NS_LITERAL_CSTRING("Sub-resource Integrity"), nsContentUtils::eSECURITY_PROPERTIES, aSourceFileURI, 0, 0, NS_LITERAL_CSTRING("IneligibleResource"), const_cast<const nsTArray<nsString>&>(params)); return NS_ERROR_SRI_NOT_ELIGIBLE; }
NS_IMETHODIMP nsCrossSiteListenerProxy::AsyncOnChannelRedirect(nsIChannel *aOldChannel, nsIChannel *aNewChannel, PRUint32 aFlags, nsIAsyncVerifyRedirectCallback *cb) { nsresult rv; if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) { rv = CheckRequestApproved(aOldChannel, PR_TRUE); if (NS_FAILED(rv)) { if (nsXMLHttpRequest::sAccessControlCache) { nsCOMPtr<nsIURI> oldURI; NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI)); if (oldURI) { nsXMLHttpRequest::sAccessControlCache-> RemoveEntries(oldURI, mRequestingPrincipal); } } aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI); return NS_ERROR_DOM_BAD_URI; } } // Prepare to receive callback mRedirectCallback = cb; mOldRedirectChannel = aOldChannel; mNewRedirectChannel = aNewChannel; nsCOMPtr<nsIChannelEventSink> outer = do_GetInterface(mOuterNotificationCallbacks); if (outer) { rv = outer->AsyncOnChannelRedirect(aOldChannel, aNewChannel, aFlags, this); if (NS_FAILED(rv)) { aOldChannel->Cancel(rv); // is this necessary...? mRedirectCallback = nsnull; mOldRedirectChannel = nsnull; mNewRedirectChannel = nsnull; } return rv; } (void) OnRedirectVerifyCallback(NS_OK); return NS_OK; }
void nsCORSPreflightListener::AddResultToCache(nsIRequest *aRequest) { nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aRequest); NS_ASSERTION(http, "Request was not http"); // The "Access-Control-Max-Age" header should return an age in seconds. nsCAutoString headerVal; http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Max-Age"), headerVal); if (headerVal.IsEmpty()) { return; } // Sanitize the string. We only allow 'delta-seconds' as specified by // http://dev.w3.org/2006/waf/access-control (digits 0-9 with no leading or // trailing non-whitespace characters). PRUint32 age = 0; nsCSubstring::const_char_iterator iter, end; headerVal.BeginReading(iter); headerVal.EndReading(end); while (iter != end) { if (*iter < '0' || *iter > '9') { return; } age = age * 10 + (*iter - '0'); // Cap at 24 hours. This also avoids overflow age = NS_MIN(age, 86400U); ++iter; } if (!age || !EnsurePreflightCache()) { return; } // String seems fine, go ahead and cache. // Note that we have already checked that these headers follow the correct // syntax. nsCOMPtr<nsIURI> uri; NS_GetFinalChannelURI(http, getter_AddRefs(uri)); // PR_Now gives microseconds PRTime expirationTime = PR_Now() + (PRUint64)age * PR_USEC_PER_SEC; nsPreflightCache::CacheEntry* entry = sPreflightCache->GetEntry(uri, mReferrerPrincipal, mWithCredentials, PR_TRUE); if (!entry) { return; } // The "Access-Control-Allow-Methods" header contains a comma separated // list of method names. http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Methods"), headerVal); nsCCharSeparatedTokenizer methods(headerVal, ','); while(methods.hasMoreTokens()) { const nsDependentCSubstring& method = methods.nextToken(); if (method.IsEmpty()) { continue; } PRUint32 i; for (i = 0; i < entry->mMethods.Length(); ++i) { if (entry->mMethods[i].token.Equals(method)) { entry->mMethods[i].expirationTime = expirationTime; break; } } if (i == entry->mMethods.Length()) { nsPreflightCache::TokenTime* newMethod = entry->mMethods.AppendElement(); if (!newMethod) { return; } newMethod->token = method; newMethod->expirationTime = expirationTime; } } // The "Access-Control-Allow-Headers" header contains a comma separated // list of method names. http->GetResponseHeader(NS_LITERAL_CSTRING("Access-Control-Allow-Headers"), headerVal); nsCCharSeparatedTokenizer headers(headerVal, ','); while(headers.hasMoreTokens()) { const nsDependentCSubstring& header = headers.nextToken(); if (header.IsEmpty()) { continue; } PRUint32 i; for (i = 0; i < entry->mHeaders.Length(); ++i) { if (entry->mHeaders[i].token.Equals(header)) { entry->mHeaders[i].expirationTime = expirationTime; break; } } if (i == entry->mHeaders.Length()) { nsPreflightCache::TokenTime* newHeader = entry->mHeaders.AppendElement(); if (!newHeader) { return; } newHeader->token = header; newHeader->expirationTime = expirationTime; } } }
/* * Based on the security flags provided in the loadInfo of the channel, * doContentSecurityCheck() performs the following content security checks * before opening the channel: * * (1) Same Origin Policy Check (if applicable) * (2) Allow Cross Origin but perform sanity checks whether a principal * is allowed to access the following URL. * (3) Perform CORS check (if applicable) * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...) * * @param aChannel * The channel to perform the security checks on. * @param aInAndOutListener * The streamListener that is passed to channel->AsyncOpen2() that is now potentially * wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs * to be set as new streamListener on the channel. */ nsresult nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel, nsCOMPtr<nsIStreamListener>& aInAndOutListener) { NS_ENSURE_ARG(aChannel); nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); if (!loadInfo) { MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks"); return NS_ERROR_UNEXPECTED; } // make sure that only one of the five security flags is set in the loadinfo // e.g. do not require same origin and allow cross origin at the same time nsresult rv = ValidateSecurityFlags(loadInfo); NS_ENSURE_SUCCESS(rv, rv); // lets store the initialSecurityCheckDone flag which indicates whether the channel // was initialy evaluated by the contentSecurityManager. Once the inital // asyncOpen() of the channel went through the contentSecurityManager then // redirects do not have perform all the security checks, e.g. no reason // to setup CORS again. bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone(); // now lets set the initalSecurityFlag for subsequent calls rv = loadInfo->SetInitialSecurityCheckDone(true); NS_ENSURE_SUCCESS(rv, rv); // since aChannel was openend using asyncOpen2() we have to make sure // that redirects of that channel also get openend using asyncOpen2() // please note that some implementations of ::AsyncOpen2 might already // have set that flag to true (e.g. nsViewSourceChannel) in which case // we just set the flag again. rv = loadInfo->SetEnforceSecurity(true); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIURI> finalChannelURI; rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI)); NS_ENSURE_SUCCESS(rv, rv); // Perform Same Origin Policy check rv = DoSOPChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); // if dealing with a redirected channel then we only enforce SOP // and can return at this point. if (initialSecurityCheckDone) { return NS_OK; } rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); // Check if CORS needs to be set up rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener); NS_ENSURE_SUCCESS(rv, rv); // Perform all ContentPolicy checks (MixedContent, CSP, ...) rv = DoContentSecurityChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); // all security checks passed - lets allow the load return NS_OK; }
nsresult nsCrossSiteListenerProxy::UpdateChannel(nsIChannel* aChannel) { nsCOMPtr<nsIURI> uri, originalURI; nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); rv = aChannel->GetOriginalURI(getter_AddRefs(originalURI)); NS_ENSURE_SUCCESS(rv, rv); // Check that the uri is ok to load rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(mRequestingPrincipal, uri, nsIScriptSecurityManager::STANDARD); NS_ENSURE_SUCCESS(rv, rv); if (originalURI != uri) { rv = nsContentUtils::GetSecurityManager()-> CheckLoadURIWithPrincipal(mRequestingPrincipal, originalURI, nsIScriptSecurityManager::STANDARD); NS_ENSURE_SUCCESS(rv, rv); } if (!mHasBeenCrossSite && NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(uri, PR_FALSE)) && (originalURI == uri || NS_SUCCEEDED(mRequestingPrincipal->CheckMayLoad(originalURI, PR_FALSE)))) { return NS_OK; } // It's a cross site load mHasBeenCrossSite = PR_TRUE; nsCString userpass; uri->GetUserPass(userpass); NS_ENSURE_TRUE(userpass.IsEmpty(), NS_ERROR_DOM_BAD_URI); // Add the Origin header nsCAutoString origin; rv = nsContentUtils::GetASCIIOrigin(mRequestingPrincipal, origin); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIHttpChannel> http = do_QueryInterface(aChannel); NS_ENSURE_TRUE(http, NS_ERROR_FAILURE); rv = http->SetRequestHeader(NS_LITERAL_CSTRING("Origin"), origin, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); // Add preflight headers if this is a preflight request if (mIsPreflight) { rv = http-> SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Method"), mPreflightMethod, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); if (!mPreflightHeaders.IsEmpty()) { nsCAutoString headers; for (PRUint32 i = 0; i < mPreflightHeaders.Length(); ++i) { if (i != 0) { headers += ','; } headers += mPreflightHeaders[i]; } rv = http-> SetRequestHeader(NS_LITERAL_CSTRING("Access-Control-Request-Headers"), headers, PR_FALSE); NS_ENSURE_SUCCESS(rv, rv); } } // Make cookie-less if needed if (mIsPreflight || !mWithCredentials) { nsLoadFlags flags; rv = http->GetLoadFlags(&flags); NS_ENSURE_SUCCESS(rv, rv); flags |= nsIRequest::LOAD_ANONYMOUS; rv = http->SetLoadFlags(flags); NS_ENSURE_SUCCESS(rv, rv); } return NS_OK; }
/* * Based on the security flags provided in the loadInfo of the channel, * doContentSecurityCheck() performs the following content security checks * before opening the channel: * * (1) Same Origin Policy Check (if applicable) * (2) Allow Cross Origin but perform sanity checks whether a principal * is allowed to access the following URL. * (3) Perform CORS check (if applicable) * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...) * * @param aChannel * The channel to perform the security checks on. * @param aInAndOutListener * The streamListener that is passed to channel->AsyncOpen2() that is now potentially * wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs * to be set as new streamListener on the channel. */ nsresult nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel, nsCOMPtr<nsIStreamListener>& aInAndOutListener) { NS_ENSURE_ARG(aChannel); nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo(); if (!loadInfo) { MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks"); return NS_ERROR_UNEXPECTED; } // make sure that only one of the five security flags is set in the loadinfo // e.g. do not require same origin and allow cross origin at the same time nsresult rv = ValidateSecurityFlags(loadInfo); NS_ENSURE_SUCCESS(rv, rv); // lets store the initialSecurityCheckDone flag which indicates whether the channel // was initialy evaluated by the contentSecurityManager. Once the inital // asyncOpen() of the channel went through the contentSecurityManager then // redirects do not have perform all the security checks, e.g. no reason // to setup CORS again. bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone(); // now lets set the initalSecurityFlag for subsequent calls rv = loadInfo->SetInitialSecurityCheckDone(true); NS_ENSURE_SUCCESS(rv, rv); // since aChannel was openend using asyncOpen2() we have to make sure // that redirects of that channel also get openend using asyncOpen2() // please note that some implementations of ::AsyncOpen2 might already // have set that flag to true (e.g. nsViewSourceChannel) in which case // we just set the flag again. rv = loadInfo->SetEnforceSecurity(true); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIURI> finalChannelURI; rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI)); NS_ENSURE_SUCCESS(rv, rv); nsSecurityFlags securityMode = loadInfo->GetSecurityMode(); // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply if ((securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) || (securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) { rv = DoSOPChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); } // if dealing with a redirected channel then we only enforce SOP // and can return at this point. if (initialSecurityCheckDone) { return NS_OK; } if ((securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) || (securityMode == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) { // Please note that DoCheckLoadURIChecks should only be enforced for // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set // within the loadInfo, then then CheckLoadURIWithPrincipal is performed // within nsCorsListenerProxy rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); } if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) { rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener); NS_ENSURE_SUCCESS(rv, rv); } // Perform all ContentPolicy checks (MixedContent, CSP, ...) rv = DoContentSecurityChecks(finalChannelURI, loadInfo); NS_ENSURE_SUCCESS(rv, rv); // all security checks passed - lets allow the load return NS_OK; }
nsresult nsScriptLoader::PrepareLoadedRequest(nsScriptLoadRequest* aRequest, nsIStreamLoader* aLoader, nsresult aStatus, PRUint32 aStringLen, const PRUint8* aString) { if (NS_FAILED(aStatus)) { return aStatus; } // If we don't have a document, then we need to abort further // evaluation. if (!mDocument) { return NS_ERROR_NOT_AVAILABLE; } // If the load returned an error page, then we need to abort nsCOMPtr<nsIRequest> req; nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); NS_ASSERTION(req, "StreamLoader's request went away prematurely"); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(req); if (httpChannel) { PRBool requestSucceeded; rv = httpChannel->GetRequestSucceeded(&requestSucceeded); if (NS_SUCCEEDED(rv) && !requestSucceeded) { return NS_ERROR_NOT_AVAILABLE; } } nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); NS_GetFinalChannelURI(channel, getter_AddRefs(aRequest->mFinalURI)); if (aStringLen) { // Check the charset attribute to determine script charset. nsAutoString hintCharset; aRequest->mElement->GetScriptCharset(hintCharset); rv = ConvertToUTF16(channel, aString, aStringLen, hintCharset, mDocument, aRequest->mScriptText); NS_ASSERTION(NS_SUCCEEDED(rv), "Could not convert external JavaScript to Unicode!"); NS_ENSURE_SUCCESS(rv, rv); if (!ShouldExecuteScript(mDocument, channel)) { return NS_ERROR_NOT_AVAILABLE; } } // This assertion could fire errorously if we ran out of memory when // inserting the request in the array. However it's an unlikely case // so if you see this assertion it is likely something else that is // wrong, especially if you see it more than once. NS_ASSERTION(mPendingRequests.IndexOf(aRequest) >= 0, "aRequest should be pending!"); // Mark this as loaded aRequest->mLoading = PR_FALSE; return NS_OK; }
NS_IMETHODIMP FetchDriver::OnRedirectVerifyCallback(nsresult aResult) { // On a successful redirect we perform the following substeps of HTTP Fetch, // step 5, "redirect status", step 11. if (NS_SUCCEEDED(aResult)) { // Step 11.5 "Append locationURL to request's url list." so that when we set the // Response's URL from the Request's URL in Main Fetch, step 15, we get the // final value. Note, we still use a single URL value instead of a list. nsCOMPtr<nsIURI> newURI; nsresult rv = NS_GetFinalChannelURI(mNewRedirectChannel, getter_AddRefs(newURI)); if (NS_WARN_IF(NS_FAILED(rv))) { aResult = rv; } else { // We need to update our request's URL. nsAutoCString newUrl; newURI->GetSpec(newUrl); mRequest->SetURL(newUrl); } } if (NS_FAILED(aResult)) { mOldRedirectChannel->Cancel(aResult); } // Implement Main Fetch step 8 again on redirect. MainFetchOp nextOp = SetTaintingAndGetNextOp(mCORSFlagEverSet); if (nextOp.mType == NETWORK_ERROR) { // Cancel the channel if Main Fetch blocks the redirect from continuing. aResult = NS_ERROR_DOM_BAD_URI; mOldRedirectChannel->Cancel(aResult); } else { // Otherwise, we rely on necko and the CORS proxy to do the right thing // as the redirect is followed. In general this means basic or http // fetch. If we've ever been CORS, we need to stay CORS. MOZ_ASSERT(nextOp.mType == BASIC_FETCH || nextOp.mType == HTTP_FETCH); MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mType == HTTP_FETCH); MOZ_ASSERT_IF(mCORSFlagEverSet, nextOp.mCORSFlag); // Examine and possibly set the LOAD_ANONYMOUS flag on the channel. nsLoadFlags flags; aResult = mNewRedirectChannel->GetLoadFlags(&flags); if (NS_SUCCEEDED(aResult)) { if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && mRequest->GetResponseTainting() == InternalRequest::RESPONSETAINT_OPAQUE) { // In the case of a "no-cors" mode request with "same-origin" credentials, // we have to set LOAD_ANONYMOUS manually here in order to avoid sending // credentials on a cross-origin redirect. flags |= nsIRequest::LOAD_ANONYMOUS; aResult = mNewRedirectChannel->SetLoadFlags(flags); } else if (mRequest->GetCredentialsMode() == RequestCredentials::Omit) { // Make sure nothing in the redirect chain screws up our credentials // settings. LOAD_ANONYMOUS must be set if we RequestCredentials is "omit". MOZ_ASSERT(flags & nsIRequest::LOAD_ANONYMOUS); } else if (mRequest->GetCredentialsMode() == RequestCredentials::Same_origin && nextOp.mCORSFlag) { // We also want to verify the LOAD_ANONYMOUS flag is set when we are in // "same-origin" credentials mode and the CORS flag is set. We can't // unconditionally assert here, however, because the nsCORSListenerProxy // will set the flag later in the redirect callback chain. Instead, // perform a weaker assertion here by checking if CORS flag was set // before this redirect. In that case LOAD_ANONYMOUS must still be set. MOZ_ASSERT_IF(mCORSFlagEverSet, flags & nsIRequest::LOAD_ANONYMOUS); } else { // Otherwise, we should be sending credentials MOZ_ASSERT(!(flags & nsIRequest::LOAD_ANONYMOUS)); } } // Track the CORSFlag through redirects. mCORSFlagEverSet = mCORSFlagEverSet || nextOp.mCORSFlag; } mOldRedirectChannel = nullptr; mNewRedirectChannel = nullptr; mRedirectCallback->OnRedirectVerifyCallback(aResult); mRedirectCallback = nullptr; return NS_OK; }
NS_IMETHODIMP FetchDriver::OnStartRequest(nsIRequest* aRequest, nsISupports* aContext) { workers::AssertIsOnMainThread(); // Note, this can be called multiple times if we are doing an opaqueredirect. // In that case we will get a simulated OnStartRequest() and then the real // channel will call in with an errored OnStartRequest(). nsresult rv; aRequest->GetStatus(&rv); if (NS_FAILED(rv)) { FailWithNetworkError(); return rv; } // We should only get to the following code once. MOZ_ASSERT(!mPipeOutputStream); MOZ_ASSERT(mObserver); RefPtr<InternalResponse> response; nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest); nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aRequest); // On a successful redirect we perform the following substeps of HTTP Fetch, // step 5, "redirect status", step 11. // Step 11.5 "Append locationURL to request's url list." so that when we set the // Response's URL from the Request's URL in Main Fetch, step 15, we get the // final value. Note, we still use a single URL value instead of a list. // Because of that we only need to do this after the request finishes. nsCOMPtr<nsIURI> newURI; rv = NS_GetFinalChannelURI(channel, getter_AddRefs(newURI)); if (NS_FAILED(rv)) { FailWithNetworkError(); return rv; } nsAutoCString newUrl; newURI->GetSpec(newUrl); mRequest->SetURL(newUrl); bool foundOpaqueRedirect = false; if (httpChannel) { uint32_t responseStatus; httpChannel->GetResponseStatus(&responseStatus); if (mozilla::net::nsHttpChannel::IsRedirectStatus(responseStatus)) { if (mRequest->GetRedirectMode() == RequestRedirect::Error) { FailWithNetworkError(); return NS_BINDING_FAILED; } if (mRequest->GetRedirectMode() == RequestRedirect::Manual) { foundOpaqueRedirect = true; } } nsAutoCString statusText; httpChannel->GetResponseStatusText(statusText); response = new InternalResponse(responseStatus, statusText); RefPtr<FillResponseHeaders> visitor = new FillResponseHeaders(response); rv = httpChannel->VisitResponseHeaders(visitor); if (NS_WARN_IF(NS_FAILED(rv))) { NS_WARNING("Failed to visit all headers."); } } else { response = new InternalResponse(200, NS_LITERAL_CSTRING("OK")); ErrorResult result; nsAutoCString contentType; rv = channel->GetContentType(contentType); if (NS_SUCCEEDED(rv) && !contentType.IsEmpty()) { nsAutoCString contentCharset; channel->GetContentCharset(contentCharset); if (NS_SUCCEEDED(rv) && !contentCharset.IsEmpty()) { contentType += NS_LITERAL_CSTRING(";charset=") + contentCharset; } response->Headers()->Append(NS_LITERAL_CSTRING("Content-Type"), contentType, result); MOZ_ASSERT(!result.Failed()); } int64_t contentLength; rv = channel->GetContentLength(&contentLength); if (NS_SUCCEEDED(rv) && contentLength) { nsAutoCString contentLenStr; contentLenStr.AppendInt(contentLength); response->Headers()->Append(NS_LITERAL_CSTRING("Content-Length"), contentLenStr, result); MOZ_ASSERT(!result.Failed()); } } // We open a pipe so that we can immediately set the pipe's read end as the // response's body. Setting the segment size to UINT32_MAX means that the // pipe has infinite space. The nsIChannel will continue to buffer data in // xpcom events even if we block on a fixed size pipe. It might be possible // to suspend the channel and then resume when there is space available, but // for now use an infinite pipe to avoid blocking. nsCOMPtr<nsIInputStream> pipeInputStream; rv = NS_NewPipe(getter_AddRefs(pipeInputStream), getter_AddRefs(mPipeOutputStream), 0, /* default segment size */ UINT32_MAX /* infinite pipe */, true /* non-blocking input, otherwise you deadlock */, false /* blocking output, since the pipe is 'in'finite */ ); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); // Cancel request. return rv; } response->SetBody(pipeInputStream); response->InitChannelInfo(channel); nsCOMPtr<nsIURI> channelURI; rv = channel->GetURI(getter_AddRefs(channelURI)); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); // Cancel request. return rv; } nsCOMPtr<nsILoadInfo> loadInfo; rv = channel->GetLoadInfo(getter_AddRefs(loadInfo)); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); return rv; } // Propagate any tainting from the channel back to our response here. This // step is not reflected in the spec because the spec is written such that // FetchEvent.respondWith() just passes the already-tainted Response back to // the outer fetch(). In gecko, however, we serialize the Response through // the channel and must regenerate the tainting from the channel in the // interception case. mRequest->MaybeIncreaseResponseTainting(loadInfo->GetTainting()); // Resolves fetch() promise which may trigger code running in a worker. Make // sure the Response is fully initialized before calling this. mResponse = BeginAndGetFilteredResponse(response, channelURI, foundOpaqueRedirect); nsCOMPtr<nsIEventTarget> sts = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv); if (NS_WARN_IF(NS_FAILED(rv))) { FailWithNetworkError(); // Cancel request. return rv; } // Try to retarget off main thread. if (nsCOMPtr<nsIThreadRetargetableRequest> rr = do_QueryInterface(aRequest)) { NS_WARN_IF(NS_FAILED(rr->RetargetDeliveryTo(sts))); } return NS_OK; }
nsresult NS_StartCORSPreflight(nsIChannel* aRequestChannel, nsIStreamListener* aListener, nsIPrincipal* aPrincipal, PRBool aWithCredentials, nsTArray<nsCString>& aUnsafeHeaders, nsIChannel** aPreflightChannel) { *aPreflightChannel = nsnull; nsCAutoString method; nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel)); NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED); httpChannel->GetRequestMethod(method); nsCOMPtr<nsIURI> uri; nsresult rv = NS_GetFinalChannelURI(aRequestChannel, getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); nsPreflightCache::CacheEntry* entry = sPreflightCache ? sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, PR_FALSE) : nsnull; if (entry && entry->CheckRequest(method, aUnsafeHeaders)) { // We have a cached preflight result, just start the original channel return aRequestChannel->AsyncOpen(aListener, nsnull); } // Either it wasn't cached or the cached result has expired. Build a // channel for the OPTIONS request. nsCOMPtr<nsILoadGroup> loadGroup; rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup)); NS_ENSURE_SUCCESS(rv, rv); nsLoadFlags loadFlags; rv = aRequestChannel->GetLoadFlags(&loadFlags); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIChannel> preflightChannel; rv = NS_NewChannel(getter_AddRefs(preflightChannel), uri, nsnull, loadGroup, nsnull, loadFlags); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIHttpChannel> preHttp = do_QueryInterface(preflightChannel); NS_ASSERTION(preHttp, "Failed to QI to nsIHttpChannel!"); rv = preHttp->SetRequestMethod(NS_LITERAL_CSTRING("OPTIONS")); NS_ENSURE_SUCCESS(rv, rv); // Set up listener which will start the original channel nsCOMPtr<nsIStreamListener> preflightListener = new nsCORSPreflightListener(aRequestChannel, aListener, nsnull, aPrincipal, method, aWithCredentials); NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY); preflightListener = new nsCORSListenerProxy(preflightListener, aPrincipal, preflightChannel, aWithCredentials, method, aUnsafeHeaders, &rv); NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY); NS_ENSURE_SUCCESS(rv, rv); // Start preflight rv = preflightChannel->AsyncOpen(preflightListener, nsnull); NS_ENSURE_SUCCESS(rv, rv); // Return newly created preflight channel preflightChannel.forget(aPreflightChannel); return NS_OK; }
static nsresult DoContentSecurityChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo) { nsCOMPtr<nsIURI> uri; nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri)); NS_ENSURE_SUCCESS(rv, rv); nsContentPolicyType contentPolicyType = aLoadInfo->GetExternalContentPolicyType(); nsContentPolicyType internalContentPolicyType = aLoadInfo->InternalContentPolicyType(); nsCString mimeTypeGuess; nsCOMPtr<nsINode> requestingContext = nullptr; #ifdef DEBUG // Don't enforce TYPE_DOCUMENT assertions for loads // initiated by javascript tests. bool skipContentTypeCheck = false; skipContentTypeCheck = Preferences::GetBool("network.loadinfo.skip_type_assertion"); #endif switch(contentPolicyType) { case nsIContentPolicy::TYPE_OTHER: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_SCRIPT: { mimeTypeGuess = NS_LITERAL_CSTRING("application/javascript"); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_IMAGE: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_STYLESHEET: { mimeTypeGuess = NS_LITERAL_CSTRING("text/css"); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_OBJECT: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_DOCUMENT: { MOZ_ASSERT(skipContentTypeCheck || false, "contentPolicyType not supported yet"); break; } case nsIContentPolicy::TYPE_SUBDOCUMENT: { mimeTypeGuess = NS_LITERAL_CSTRING("text/html"); requestingContext = aLoadInfo->LoadingNode(); MOZ_ASSERT(!requestingContext || requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, "type_subdocument requires requestingContext of type Document"); break; } case nsIContentPolicy::TYPE_REFRESH: { MOZ_ASSERT(false, "contentPolicyType not supported yet"); break; } case nsIContentPolicy::TYPE_XBL: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_PING: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_XMLHTTPREQUEST: { // alias nsIContentPolicy::TYPE_DATAREQUEST: requestingContext = aLoadInfo->LoadingNode(); MOZ_ASSERT(!requestingContext || requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, "type_xml requires requestingContext of type Document"); // We're checking for the external TYPE_XMLHTTPREQUEST here in case // an addon creates a request with that type. if (internalContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_XMLHTTPREQUEST || internalContentPolicyType == nsIContentPolicy::TYPE_XMLHTTPREQUEST) { mimeTypeGuess = EmptyCString(); } else { MOZ_ASSERT(internalContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_EVENTSOURCE, "can not set mime type guess for unexpected internal type"); mimeTypeGuess = NS_LITERAL_CSTRING(TEXT_EVENT_STREAM); } break; } case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); MOZ_ASSERT(!requestingContext || requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE, "type_subrequest requires requestingContext of type Element"); break; } case nsIContentPolicy::TYPE_DTD: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); MOZ_ASSERT(!requestingContext || requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, "type_dtd requires requestingContext of type Document"); break; } case nsIContentPolicy::TYPE_FONT: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_MEDIA: { if (internalContentPolicyType == nsIContentPolicy::TYPE_INTERNAL_TRACK) { mimeTypeGuess = NS_LITERAL_CSTRING("text/vtt"); } else { mimeTypeGuess = EmptyCString(); } requestingContext = aLoadInfo->LoadingNode(); MOZ_ASSERT(!requestingContext || requestingContext->NodeType() == nsIDOMNode::ELEMENT_NODE, "type_media requires requestingContext of type Element"); break; } case nsIContentPolicy::TYPE_WEBSOCKET: { // Websockets have to use the proxied URI: // ws:// instead of http:// for CSP checks nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal = do_QueryInterface(aChannel); MOZ_ASSERT(httpChannelInternal); if (httpChannelInternal) { httpChannelInternal->GetProxyURI(getter_AddRefs(uri)); } mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_CSP_REPORT: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_XSLT: { mimeTypeGuess = NS_LITERAL_CSTRING("application/xml"); requestingContext = aLoadInfo->LoadingNode(); MOZ_ASSERT(!requestingContext || requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, "type_xslt requires requestingContext of type Document"); break; } case nsIContentPolicy::TYPE_BEACON: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); MOZ_ASSERT(!requestingContext || requestingContext->NodeType() == nsIDOMNode::DOCUMENT_NODE, "type_beacon requires requestingContext of type Document"); break; } case nsIContentPolicy::TYPE_FETCH: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_IMAGESET: { mimeTypeGuess = EmptyCString(); requestingContext = aLoadInfo->LoadingNode(); break; } case nsIContentPolicy::TYPE_WEB_MANIFEST: { mimeTypeGuess = NS_LITERAL_CSTRING("application/manifest+json"); requestingContext = aLoadInfo->LoadingNode(); break; } default: // nsIContentPolicy::TYPE_INVALID MOZ_ASSERT(false, "can not perform security check without a valid contentType"); } int16_t shouldLoad = nsIContentPolicy::ACCEPT; rv = NS_CheckContentLoadPolicy(internalContentPolicyType, uri, aLoadInfo->LoadingPrincipal(), requestingContext, mimeTypeGuess, nullptr, //extra, &shouldLoad, nsContentUtils::GetContentPolicy(), nsContentUtils::GetSecurityManager()); NS_ENSURE_SUCCESS(rv, rv); if (NS_CP_REJECTED(shouldLoad)) { return NS_ERROR_CONTENT_BLOCKED; } if (nsMixedContentBlocker::sSendHSTSPriming) { rv = nsMixedContentBlocker::MarkLoadInfoForPriming(uri, requestingContext, aLoadInfo); return rv; } return NS_OK; }
nsresult nsDOMWorkerScriptLoader::OnStreamCompleteInternal(nsIStreamLoader* aLoader, nsISupports* aContext, nsresult aStatus, PRUint32 aStringLen, const PRUint8* aString) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsCOMPtr<nsISupportsPRUint32> indexSupports(do_QueryInterface(aContext)); NS_ENSURE_TRUE(indexSupports, NS_ERROR_NO_INTERFACE); PRUint32 index = PR_UINT32_MAX; indexSupports->GetData(&index); if (index >= mScriptCount) { NS_NOTREACHED("This really can't fail or we'll hang!"); return NS_ERROR_FAILURE; } ScriptLoadInfo& loadInfo = mLoadInfos[index]; NS_ASSERTION(!loadInfo.done, "Got complete on the same load twice!"); loadInfo.done = PR_TRUE; // Use an alias to keep rv and loadInfo.result in sync. nsresult& rv = loadInfo.result; if (NS_FAILED(aStatus)) { return rv = aStatus; } if (!(aStringLen && aString)) { return rv = NS_ERROR_UNEXPECTED; } // Make sure we're not seeing the result of a 404 or something by checking the // 'requestSucceeded' attribute on the http channel. nsCOMPtr<nsIRequest> request; rv = aLoader->GetRequest(getter_AddRefs(request)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(request); if (httpChannel) { PRBool requestSucceeded; rv = httpChannel->GetRequestSucceeded(&requestSucceeded); NS_ENSURE_SUCCESS(rv, rv); if (!requestSucceeded) { return rv = NS_ERROR_NOT_AVAILABLE; } } // May be null. nsIDocument* parentDoc = mWorker->Pool()->ParentDocument(); // Use the regular nsScriptLoader for this grunt work! Should be just fine // because we're running on the main thread. rv = nsScriptLoader::ConvertToUTF16(loadInfo.channel, aString, aStringLen, EmptyString(), parentDoc, loadInfo.scriptText); if (NS_FAILED(rv)) { return rv; } if (loadInfo.scriptText.IsEmpty()) { return rv = NS_ERROR_FAILURE; } nsCString filename; rv = loadInfo.finalURI->GetSpec(filename); NS_ENSURE_SUCCESS(rv, rv); if (filename.IsEmpty()) { filename.Assign(NS_LossyConvertUTF16toASCII(loadInfo.url)); } else { // This will help callers figure out what their script url resolved to in // case of errors. loadInfo.url.Assign(NS_ConvertUTF8toUTF16(filename)); } // Update the principal of the worker and its base URI if we just loaded the // worker's primary script. if (mForWorker) { nsCOMPtr<nsIChannel> channel = do_QueryInterface(request); NS_ASSERTION(channel, "This should never fail!"); // Take care of the base URI first. nsCOMPtr<nsIURI> finalURI; rv = NS_GetFinalChannelURI(channel, getter_AddRefs(finalURI)); NS_ENSURE_SUCCESS(rv, rv); mWorker->SetBaseURI(finalURI); // Now to figure out which principal to give this worker. nsRefPtr<nsDOMWorker> parent = mWorker->GetParent(); NS_ASSERTION(mWorker->GetPrincipal() || parent, "Must have one of these!"); nsCOMPtr<nsIPrincipal> loadPrincipal = mWorker->GetPrincipal() ? mWorker->GetPrincipal() : parent->GetPrincipal(); nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ASSERTION(ssm, "Should never be null!"); nsCOMPtr<nsIPrincipal> channelPrincipal; rv = ssm->GetChannelPrincipal(channel, getter_AddRefs(channelPrincipal)); NS_ENSURE_SUCCESS(rv, rv); // See if this is a resource URI. Since JSMs usually come from resource:// // URIs we're currently considering all URIs with the URI_IS_UI_RESOURCE // flag as valid for creating privileged workers. if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) { PRBool isResource; rv = NS_URIChainHasFlags(finalURI, nsIProtocolHandler::URI_IS_UI_RESOURCE, &isResource); NS_ENSURE_SUCCESS(rv, rv); if (isResource) { rv = ssm->GetSystemPrincipal(getter_AddRefs(channelPrincipal)); NS_ENSURE_SUCCESS(rv, rv); } } // If the load principal is the system principal then the channel principal // must also be the system principal (we do not allow chrome code to create // workers with non-chrome scripts). Otherwise this channel principal must // be same origin with the load principal (we check again here in case // redirects changed the location of the script). if (nsContentUtils::IsSystemPrincipal(loadPrincipal)) { if (!nsContentUtils::IsSystemPrincipal(channelPrincipal)) { return rv = NS_ERROR_DOM_BAD_URI; } } else if (NS_FAILED(loadPrincipal->CheckMayLoad(finalURI, PR_FALSE))) { return rv = NS_ERROR_DOM_BAD_URI; } mWorker->SetPrincipal(channelPrincipal); } nsRefPtr<ScriptCompiler> compiler = new ScriptCompiler(this, loadInfo.scriptText, filename, loadInfo.scriptObj); NS_ASSERTION(compiler, "Out of memory!"); if (!compiler) { return rv = NS_ERROR_OUT_OF_MEMORY; } rv = mTarget->Dispatch(compiler, NS_DISPATCH_NORMAL); NS_ENSURE_SUCCESS(rv, rv); return rv; }