Skip to content

Commit

Permalink
Bug 1226909 part 4: Make AsyncOpen2 set taining information on channe…
Browse files Browse the repository at this point in the history
…ls. Use this information in XHR and fetch(). r=bkelly
  • Loading branch information
sicking committed Dec 5, 2015
1 parent ff12f48 commit 7fae3fd
Show file tree
Hide file tree
Showing 11 changed files with 176 additions and 253 deletions.
140 changes: 46 additions & 94 deletions dom/base/nsXMLHttpRequest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,10 @@ using namespace mozilla::dom;
#define XML_HTTP_REQUEST_PARSEBODY (1 << 9) // Internal
#define XML_HTTP_REQUEST_SYNCLOOPING (1 << 10) // Internal
#define XML_HTTP_REQUEST_BACKGROUND (1 << 13) // Internal
#define XML_HTTP_REQUEST_USE_XSITE_AC (1 << 14) // Internal
#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 15) // Internal
#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 16) // Internal
#define XML_HTTP_REQUEST_TIMED_OUT (1 << 17) // Internal
#define XML_HTTP_REQUEST_DELETED (1 << 18) // Internal
#define XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND (1 << 14) // Internal
#define XML_HTTP_REQUEST_AC_WITH_CREDENTIALS (1 << 15) // Internal
#define XML_HTTP_REQUEST_TIMED_OUT (1 << 16) // Internal
#define XML_HTTP_REQUEST_DELETED (1 << 17) // Internal

#define XML_HTTP_REQUEST_LOADSTATES \
(XML_HTTP_REQUEST_UNSENT | \
Expand Down Expand Up @@ -1070,9 +1069,22 @@ nsXMLHttpRequest::GetResponse(JSContext* aCx,
}

bool
nsXMLHttpRequest::IsDeniedCrossSiteRequest()
nsXMLHttpRequest::IsCrossSiteCORSRequest()
{
if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && mChannel) {
if (!mChannel) {
return false;
}

nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
MOZ_ASSERT(loadInfo);

return loadInfo->GetTainting() == LoadTainting::CORS;
}

bool
nsXMLHttpRequest::IsDeniedCrossSiteCORSRequest()
{
if (IsCrossSiteCORSRequest()) {
nsresult rv;
mChannel->GetStatus(&rv);
if (NS_FAILED(rv)) {
Expand All @@ -1094,7 +1106,7 @@ nsXMLHttpRequest::GetResponseURL(nsAString& aUrl)

// Make sure we don't leak responseURL information from denied cross-site
// requests.
if (IsDeniedCrossSiteRequest()) {
if (IsDeniedCrossSiteCORSRequest()) {
return;
}

Expand Down Expand Up @@ -1122,7 +1134,7 @@ nsXMLHttpRequest::Status()
{
// Make sure we don't leak status information from denied cross-site
// requests.
if (IsDeniedCrossSiteRequest()) {
if (IsDeniedCrossSiteCORSRequest()) {
return 0;
}

Expand Down Expand Up @@ -1172,7 +1184,7 @@ nsXMLHttpRequest::GetStatusText(nsCString& aStatusText)

// Make sure we don't leak status information from denied cross-site
// requests.
if (IsDeniedCrossSiteRequest()) {
if (IsDeniedCrossSiteCORSRequest()) {
return;
}

Expand Down Expand Up @@ -1267,7 +1279,7 @@ nsXMLHttpRequest::IsSafeHeader(const nsACString& header, nsIHttpChannel* httpCha
return false;
}
// if this is not a CORS call all headers are safe
if (!(mState & XML_HTTP_REQUEST_USE_XSITE_AC)){
if (!IsCrossSiteCORSRequest()) {
return true;
}
// Check for dangerous headers
Expand Down Expand Up @@ -1529,17 +1541,6 @@ nsXMLHttpRequest::IsSystemXHR()
return mIsSystem || nsContentUtils::IsSystemPrincipal(mPrincipal);
}

void
nsXMLHttpRequest::CheckChannelForCrossSiteRequest(nsIChannel* aChannel)
{
// A system XHR (chrome code or a web app with the right permission) can
// load anything, and same-origin loads are always allowed.
if (!IsSystemXHR() &&
!nsContentUtils::CheckMayLoad(mPrincipal, aChannel, true)) {
mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
}
}

NS_IMETHODIMP
nsXMLHttpRequest::Open(const nsACString& method, const nsACString& url,
bool async, const nsAString& user,
Expand Down Expand Up @@ -1716,8 +1717,7 @@ nsXMLHttpRequest::Open(const nsACString& inMethod, const nsACString& url,

NS_ENSURE_SUCCESS(rv, rv);

mState &= ~(XML_HTTP_REQUEST_USE_XSITE_AC |
XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
mState &= ~XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND;

nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
if (httpChannel) {
Expand Down Expand Up @@ -1924,12 +1924,6 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
return NS_OK;
}

// Always treat tainted channels as cross-origin.
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
if (loadInfo && loadInfo->GetTainting() != LoadTainting::Basic) {
mState |= XML_HTTP_REQUEST_USE_XSITE_AC;
}

// Don't do anything if we have been aborted
if (mState & XML_HTTP_REQUEST_UNSENT)
return NS_OK;
Expand Down Expand Up @@ -2115,7 +2109,11 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)
mResponseXML->ForceEnableXULXBL();
}

if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
MOZ_ASSERT(loadInfo);
bool isCrossSite = loadInfo->GetTainting() != LoadTainting::Basic;

if (isCrossSite) {
nsCOMPtr<nsIHTMLDocument> htmlDoc = do_QueryInterface(mResponseXML);
if (htmlDoc) {
htmlDoc->DisableCookieAccess();
Expand All @@ -2128,7 +2126,7 @@ nsXMLHttpRequest::OnStartRequest(nsIRequest *request, nsISupports *ctxt)

rv = mResponseXML->StartDocumentLoad(kLoadAsData, channel, loadGroup,
nullptr, getter_AddRefs(listener),
!(mState & XML_HTTP_REQUEST_USE_XSITE_AC));
!isCrossSite);
NS_ENSURE_SUCCESS(rv, rv);

mXMLParserStreamListener = listener;
Expand Down Expand Up @@ -2796,8 +2794,6 @@ nsXMLHttpRequest::Send(nsIVariant* aVariant, const Nullable<RequestBody>& aBody)

ResetResponse();

CheckChannelForCrossSiteRequest(mChannel);

bool withCredentials = !!(mState & XML_HTTP_REQUEST_AC_WITH_CREDENTIALS);

if (!IsSystemXHR() && withCredentials) {
Expand Down Expand Up @@ -3308,46 +3304,6 @@ nsXMLHttpRequest::ChangeState(uint32_t aState, bool aBroadcast)
return rv;
}

/*
* Simple helper class that just forwards the redirect callback back
* to the nsXMLHttpRequest.
*/
class AsyncVerifyRedirectCallbackForwarder final : public nsIAsyncVerifyRedirectCallback
{
public:
explicit AsyncVerifyRedirectCallbackForwarder(nsXMLHttpRequest* xhr)
: mXHR(xhr)
{
}

NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(AsyncVerifyRedirectCallbackForwarder)

// nsIAsyncVerifyRedirectCallback implementation
NS_IMETHOD OnRedirectVerifyCallback(nsresult result) override
{
mXHR->OnRedirectVerifyCallback(result);

return NS_OK;
}

private:
~AsyncVerifyRedirectCallbackForwarder() {}

RefPtr<nsXMLHttpRequest> mXHR;
};

NS_IMPL_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder, mXHR)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(AsyncVerifyRedirectCallbackForwarder)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(AsyncVerifyRedirectCallbackForwarder)
NS_IMPL_CYCLE_COLLECTING_RELEASE(AsyncVerifyRedirectCallbackForwarder)


/////////////////////////////////////////////////////
// nsIChannelEventSink methods:
//
Expand All @@ -3359,23 +3315,17 @@ nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
{
NS_PRECONDITION(aNewChannel, "Redirect without a channel?");

nsresult rv;

if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags)) {
CheckChannelForCrossSiteRequest(aNewChannel);
}

// Prepare to receive callback
mRedirectCallback = callback;
mNewRedirectChannel = aNewChannel;

if (mChannelEventSink) {
RefPtr<AsyncVerifyRedirectCallbackForwarder> fwd =
new AsyncVerifyRedirectCallbackForwarder(this);
nsCOMPtr<nsIAsyncVerifyRedirectCallback> fwd =
EnsureXPCOMifier();

rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
aNewChannel,
aFlags, fwd);
nsresult rv = mChannelEventSink->AsyncOnChannelRedirect(aOldChannel,
aNewChannel,
aFlags, fwd);
if (NS_FAILED(rv)) {
mRedirectCallback = nullptr;
mNewRedirectChannel = nullptr;
Expand All @@ -3386,7 +3336,7 @@ nsXMLHttpRequest::AsyncOnChannelRedirect(nsIChannel *aOldChannel,
return NS_OK;
}

void
nsresult
nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
{
NS_ASSERTION(mRedirectCallback, "mRedirectCallback not set in callback");
Expand All @@ -3398,13 +3348,12 @@ nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)
nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(mChannel));
if (httpChannel) {
// Ensure all original headers are duplicated for the new channel (bug #553888)
for (uint32_t i = mModifiedRequestHeaders.Length(); i > 0; ) {
--i;
if (mModifiedRequestHeaders[i].value.IsEmpty()) {
httpChannel->SetEmptyRequestHeader(mModifiedRequestHeaders[i].header);
for (RequestHeader& requestHeader : mModifiedRequestHeaders) {
if (requestHeader.value.IsEmpty()) {
httpChannel->SetEmptyRequestHeader(requestHeader.header);
} else {
httpChannel->SetRequestHeader(mModifiedRequestHeaders[i].header,
mModifiedRequestHeaders[i].value,
httpChannel->SetRequestHeader(requestHeader.header,
requestHeader.value,
false);
}
}
Expand All @@ -3417,6 +3366,8 @@ nsXMLHttpRequest::OnRedirectVerifyCallback(nsresult result)

mRedirectCallback->OnRedirectVerifyCallback(result);
mRedirectCallback = nullptr;

return result;
}

/////////////////////////////////////////////////////
Expand Down Expand Up @@ -3520,7 +3471,7 @@ nsXMLHttpRequest::OnStatus(nsIRequest *aRequest, nsISupports *aContext, nsresult
bool
nsXMLHttpRequest::AllowUploadProgress()
{
return !(mState & XML_HTTP_REQUEST_USE_XSITE_AC) ||
return !IsCrossSiteCORSRequest() ||
(mState & XML_HTTP_REQUEST_HAD_UPLOAD_LISTENERS_ON_SEND);
}

Expand Down Expand Up @@ -3581,7 +3532,7 @@ nsXMLHttpRequest::GetInterface(const nsIID & aIID, void **aResult)
// If authentication fails, XMLHttpRequest origin and
// the request URL are same origin, ...
/* Disabled - bug: 799540
if (mState & XML_HTTP_REQUEST_USE_XSITE_AC) {
if (IsCrossSiteCORSRequest()) {
showPrompt = false;
}
*/
Expand Down Expand Up @@ -3788,6 +3739,7 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsXMLHttpRequestXPCOMifier)
NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
NS_INTERFACE_MAP_ENTRY(nsIChannelEventSink)
NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
NS_INTERFACE_MAP_ENTRY(nsIProgressEventSink)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
Expand Down
17 changes: 5 additions & 12 deletions dom/base/nsXMLHttpRequest.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@
#undef Status
#endif

class AsyncVerifyRedirectCallbackForwarder;
class nsFormData;
class nsIJARChannel;
class nsILoadGroup;
Expand Down Expand Up @@ -426,7 +425,8 @@ class nsXMLHttpRequest final : public nsXHREventTarget,
return Send(Nullable<RequestBody>(aBody));
}

bool IsDeniedCrossSiteRequest();
bool IsCrossSiteCORSRequest();
bool IsDeniedCrossSiteCORSRequest();

// Tell our channel what network interface ID we were told to use.
// If it's an HTTP channel and we were told to use a non-default
Expand Down Expand Up @@ -620,18 +620,9 @@ class nsXMLHttpRequest final : public nsXHREventTarget,

void ChangeStateToDone();

/**
* Check if aChannel is ok for a cross-site request by making sure no
* inappropriate headers are set, and no username/password is set.
*
* Also updates the XML_HTTP_REQUEST_USE_XSITE_AC bit.
*/
void CheckChannelForCrossSiteRequest(nsIChannel* aChannel);

void StartProgressEventTimer();

friend class AsyncVerifyRedirectCallbackForwarder;
void OnRedirectVerifyCallback(nsresult result);
nsresult OnRedirectVerifyCallback(nsresult result);

nsresult Open(const nsACString& method, const nsACString& url, bool async,
const mozilla::dom::Optional<nsAString>& user,
Expand Down Expand Up @@ -840,6 +831,7 @@ class MOZ_STACK_CLASS AutoDontWarnAboutSyncXHR
// XMLHttpRequest via XPCOM stuff.
class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener,
public nsIChannelEventSink,
public nsIAsyncVerifyRedirectCallback,
public nsIProgressEventSink,
public nsIInterfaceRequestor,
public nsITimerCallback
Expand All @@ -864,6 +856,7 @@ class nsXMLHttpRequestXPCOMifier final : public nsIStreamListener,
NS_FORWARD_NSISTREAMLISTENER(mXHR->)
NS_FORWARD_NSIREQUESTOBSERVER(mXHR->)
NS_FORWARD_NSICHANNELEVENTSINK(mXHR->)
NS_FORWARD_NSIASYNCVERIFYREDIRECTCALLBACK(mXHR->)
NS_FORWARD_NSIPROGRESSEVENTSINK(mXHR->)
NS_FORWARD_NSITIMERCALLBACK(mXHR->)

Expand Down
Loading

0 comments on commit 7fae3fd

Please sign in to comment.