mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 07:45:30 +00:00
Bug 1589275 - Run DocumentChannel CSP checks in the parent, and send only the violations to the content process. r=nika,ckerschb
Differential Revision: https://phabricator.services.mozilla.com/D68497 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
70bbf6da2a
commit
c7b55e1d51
@ -8,6 +8,7 @@
|
||||
#define mozilla_net_ADocumentChannelBridge_h
|
||||
|
||||
#include "mozilla/net/PDocumentChannelParent.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
@ -37,11 +38,17 @@ class ADocumentChannelBridge {
|
||||
// Delete the bridge, and drop any refs to the DocumentLoadListener
|
||||
virtual void Delete() = 0;
|
||||
|
||||
// Checks if we should allow a redirect to aNewURI.
|
||||
// We need this because we currently can only do CSP checks in the content
|
||||
// process in order to get the right events fired.
|
||||
virtual RefPtr<PDocumentChannelParent::ConfirmRedirectPromise>
|
||||
ConfirmRedirect(const LoadInfoArgs& aLoadInfo, nsIURI* aNewURI) = 0;
|
||||
// Report a CSP violation event in the originating process, using
|
||||
// nsCSPContext::AsyncReportViolation.
|
||||
// aIsCspToInherit is true if aContext is the CSP to inherit (from
|
||||
// the nsDocShellLoadState), which is used to determine the right
|
||||
// loading Document when deserializing aContext. This should no longer be
|
||||
// necessary after bug 1625366.
|
||||
virtual void CSPViolation(
|
||||
nsCSPContext* aContext, bool aIsCspToInherit, nsIURI* aBlockedURI,
|
||||
nsCSPContext::BlockedContentSource aBlockedContentSource,
|
||||
nsIURI* aOriginalURI, const nsAString& aViolatedDirective,
|
||||
uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject) = 0;
|
||||
|
||||
// Initate a switch from the DocumentChannel to the protocol-specific
|
||||
// real channel.
|
||||
|
@ -353,31 +353,56 @@ DocumentChannelChild::OnRedirectVerifyCallback(nsresult aStatusCode) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
IPCResult DocumentChannelChild::RecvConfirmRedirect(
|
||||
LoadInfoArgs&& aLoadInfo, nsIURI* aNewUri,
|
||||
ConfirmRedirectResolver&& aResolve) {
|
||||
// This is effectively the same as AsyncOnChannelRedirect, except since we're
|
||||
// not propagating the redirect into this process, we don't have an nsIChannel
|
||||
// for the redirection and we have to do the checks manually.
|
||||
// This just checks CSP thus far, hopefully there's not much else needed.
|
||||
RefPtr<dom::Document> cspToInheritLoadingDocument;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> policy = mLoadState->Csp();
|
||||
if (policy) {
|
||||
mozilla::ipc::IPCResult DocumentChannelChild::RecvCSPViolation(
|
||||
const CSPInfo& aCSP, bool aIsCspToInherit, nsIURI* aBlockedURI,
|
||||
uint32_t aBlockedContentSource, nsIURI* aOriginalURI,
|
||||
const nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex,
|
||||
const nsAString& aObserverSubject) {
|
||||
if (aBlockedContentSource > nsCSPContext::BlockedContentSource::eSelf) {
|
||||
return IPC_FAIL(this, "Invalid BlockedContentSource value");
|
||||
}
|
||||
nsCSPContext::BlockedContentSource blockedContentSource =
|
||||
static_cast<nsCSPContext::BlockedContentSource>(aBlockedContentSource);
|
||||
|
||||
RefPtr<dom::Document> cspLoadingDocument;
|
||||
if (aIsCspToInherit) {
|
||||
// If this is the cspToInherit from the loadstate, then it should match
|
||||
// the csp from the load state. Copy across the loading context from
|
||||
// that policy.
|
||||
nsCOMPtr<nsIContentSecurityPolicy> policy = mLoadState->Csp();
|
||||
MOZ_ASSERT(policy,
|
||||
"How is this a CSP to inherit violation if we didn't have a "
|
||||
"policy to inherit!");
|
||||
nsWeakPtr ctx =
|
||||
static_cast<nsCSPContext*>(policy.get())->GetLoadingContext();
|
||||
cspToInheritLoadingDocument = do_QueryReferent(ctx);
|
||||
cspLoadingDocument = do_QueryReferent(ctx);
|
||||
} else {
|
||||
// Otherwise we're the normal csp (preload csp is never used for
|
||||
// Documents), so the loading context is that of our embedder
|
||||
// Element. Note that this won't necessarily work with fission
|
||||
// enabled, since the embedder Element might be OOP, bug 1625366
|
||||
// is filed to fix this.
|
||||
nsCOMPtr<nsINode> loadingContext;
|
||||
RefPtr<BrowsingContext> frameBrowsingContext =
|
||||
GetDocShell()->GetBrowsingContext();
|
||||
if (frameBrowsingContext) {
|
||||
loadingContext = frameBrowsingContext->GetEmbedderElement();
|
||||
if (loadingContext) {
|
||||
cspLoadingDocument = loadingContext->OwnerDoc();
|
||||
}
|
||||
}
|
||||
}
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp =
|
||||
CSPInfoToCSP(aCSP, cspLoadingDocument);
|
||||
if (!csp) {
|
||||
return IPC_OK();
|
||||
}
|
||||
nsCOMPtr<nsILoadInfo> loadInfo;
|
||||
MOZ_ALWAYS_SUCCEEDS(LoadInfoArgsToLoadInfo(Some(std::move(aLoadInfo)),
|
||||
cspToInheritLoadingDocument,
|
||||
getter_AddRefs(loadInfo)));
|
||||
|
||||
nsCOMPtr<nsIURI> originalUri;
|
||||
GetOriginalURI(getter_AddRefs(originalUri));
|
||||
Maybe<nsresult> cancelCode;
|
||||
nsresult rv = CSPService::ConsultCSPForRedirect(originalUri, aNewUri,
|
||||
loadInfo, cancelCode);
|
||||
aResolve(Tuple<const nsresult&, const Maybe<nsresult>&>(rv, cancelCode));
|
||||
nsCSPContext::AsyncReportViolation(
|
||||
static_cast<nsCSPContext*>(csp.get()), nullptr, nullptr, aBlockedURI,
|
||||
blockedContentSource, aOriginalURI, aViolatedDirective,
|
||||
aViolatedPolicyIndex, aObserverSubject, nsString(), nsString(), 0, 0);
|
||||
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "mozilla/net/PDocumentChannelChild.h"
|
||||
#include "mozilla/net/DocumentChannel.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
@ -48,9 +49,11 @@ class DocumentChannelChild final : public DocumentChannel,
|
||||
mozilla::ipc::IPCResult RecvAttachStreamFilter(
|
||||
Endpoint<extensions::PStreamFilterParent>&& aEndpoint);
|
||||
|
||||
mozilla::ipc::IPCResult RecvConfirmRedirect(
|
||||
LoadInfoArgs&& aLoadInfo, nsIURI* aNewUri,
|
||||
ConfirmRedirectResolver&& aResolve);
|
||||
mozilla::ipc::IPCResult RecvCSPViolation(
|
||||
const CSPInfo& aCSP, bool aIsCspToInherit, nsIURI* aBlockedURI,
|
||||
uint32_t aBlockedContentSource, nsIURI* aOriginalURI,
|
||||
const nsAString& aViolatedDirective, uint32_t aViolatedPolicyIndex,
|
||||
const nsAString& aObserverSubject);
|
||||
|
||||
private:
|
||||
void ShutdownListeners(nsresult aStatusCode);
|
||||
|
@ -73,6 +73,19 @@ DocumentChannelParent::RedirectToRealChannel(uint32_t aRedirectFlags,
|
||||
return SendRedirectToRealChannel(args);
|
||||
}
|
||||
|
||||
void DocumentChannelParent::CSPViolation(
|
||||
nsCSPContext* aContext, bool aIsCspToInherit, nsIURI* aBlockedURI,
|
||||
nsCSPContext::BlockedContentSource aBlockedContentSource,
|
||||
nsIURI* aOriginalURI, const nsAString& aViolatedDirective,
|
||||
uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject) {
|
||||
CSPInfo cspInfo;
|
||||
Unused << NS_WARN_IF(NS_FAILED(CSPToCSPInfo(aContext, &cspInfo)));
|
||||
Unused << SendCSPViolation(
|
||||
cspInfo, aIsCspToInherit, aBlockedURI, aBlockedContentSource,
|
||||
aOriginalURI, PromiseFlatString(aViolatedDirective), aViolatedPolicyIndex,
|
||||
PromiseFlatString(aObserverSubject));
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -61,10 +61,12 @@ class DocumentChannelParent final : public ADocumentChannelBridge,
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<PDocumentChannelParent::ConfirmRedirectPromise> ConfirmRedirect(
|
||||
const LoadInfoArgs& aLoadInfo, nsIURI* aNewURI) override {
|
||||
return SendConfirmRedirect(aLoadInfo, aNewURI);
|
||||
}
|
||||
void CSPViolation(nsCSPContext* aContext, bool aIsCspToInherit,
|
||||
nsIURI* aBlockedURI,
|
||||
nsCSPContext::BlockedContentSource aBlockedContentSource,
|
||||
nsIURI* aOriginalURI, const nsAString& aViolatedDirective,
|
||||
uint32_t aViolatedPolicyIndex,
|
||||
const nsAString& aObserverSubject) override;
|
||||
|
||||
virtual ProcessId OtherPid() const override { return IProtocol::OtherPid(); }
|
||||
|
||||
|
@ -1181,41 +1181,65 @@ DocumentLoadListener::AsyncOnChannelRedirect(
|
||||
return NS_BINDING_ABORTED;
|
||||
}
|
||||
|
||||
// Currently the CSP code expects to run in the content
|
||||
// process so that it can send events. Send a message to
|
||||
// our content process to ask CSP if we should allow this
|
||||
// redirect, and wait for confirmation.
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->LoadInfo();
|
||||
Maybe<LoadInfoArgs> loadInfoArgs;
|
||||
MOZ_ALWAYS_SUCCEEDS(ipc::LoadInfoToLoadInfoArgs(loadInfo, &loadInfoArgs));
|
||||
MOZ_ASSERT(loadInfoArgs.isSome());
|
||||
|
||||
nsCOMPtr<nsIURI> newUri;
|
||||
nsresult rv = aNewChannel->GetURI(getter_AddRefs(newUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIAsyncVerifyRedirectCallback> callback(aCallback);
|
||||
nsCOMPtr<nsIChannel> oldChannel(aOldChannel);
|
||||
mDocumentChannelBridge->ConfirmRedirect(*loadInfoArgs, newUri)
|
||||
->Then(
|
||||
GetCurrentThreadSerialEventTarget(), __func__,
|
||||
[callback,
|
||||
oldChannel](const Tuple<nsresult, Maybe<nsresult>>& aResult) {
|
||||
if (Get<1>(aResult)) {
|
||||
oldChannel->Cancel(*Get<1>(aResult));
|
||||
}
|
||||
callback->OnRedirectVerifyCallback(Get<0>(aResult));
|
||||
},
|
||||
[callback, oldChannel](const mozilla::ipc::ResponseRejectReason) {
|
||||
oldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
|
||||
callback->OnRedirectVerifyCallback(NS_BINDING_ABORTED);
|
||||
});
|
||||
|
||||
// Clear out our nsIParentChannel functions, since a normal parent
|
||||
// channel would actually redirect and not have those values on the new one.
|
||||
// We expect the URI classifier to run on the redirected channel with
|
||||
// the new URI and set these again.
|
||||
mIParentChannelFunctions.Clear();
|
||||
|
||||
nsCOMPtr<nsILoadInfo> loadInfo = aOldChannel->LoadInfo();
|
||||
|
||||
nsCOMPtr<nsIURI> originalUri;
|
||||
nsresult rv = aOldChannel->GetOriginalURI(getter_AddRefs(originalUri));
|
||||
if (NS_FAILED(rv)) {
|
||||
aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> newUri;
|
||||
rv = aNewChannel->GetURI(getter_AddRefs(newUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
RefPtr<ADocumentChannelBridge> bridge = mDocumentChannelBridge;
|
||||
auto callback =
|
||||
[bridge, loadInfo](
|
||||
nsCSPContext* aContext, mozilla::dom::Element* aTriggeringElement,
|
||||
nsICSPEventListener* aCSPEventListener, nsIURI* aBlockedURI,
|
||||
nsCSPContext::BlockedContentSource aBlockedContentSource,
|
||||
nsIURI* aOriginalURI, const nsAString& aViolatedDirective,
|
||||
uint32_t aViolatedPolicyIndex, const nsAString& aObserverSubject,
|
||||
const nsAString& aSourceFile, const nsAString& aScriptSample,
|
||||
uint32_t aLineNum, uint32_t aColumnNum) -> nsresult {
|
||||
MOZ_ASSERT(!aTriggeringElement);
|
||||
MOZ_ASSERT(!aCSPEventListener);
|
||||
MOZ_ASSERT(aSourceFile.IsVoid() || aSourceFile.IsEmpty());
|
||||
MOZ_ASSERT(aScriptSample.IsVoid() || aScriptSample.IsEmpty());
|
||||
nsCOMPtr<nsIContentSecurityPolicy> cspToInherit =
|
||||
loadInfo->GetCspToInherit();
|
||||
|
||||
// The CSPContext normally contains the loading Document (used
|
||||
// for targeting events), but this gets lost when serializing across
|
||||
// IPDL. We need to know which CSPContext we're serializing, so that
|
||||
// we can find the right loading Document on the content process
|
||||
// side.
|
||||
bool isCspToInherit = (aContext == cspToInherit);
|
||||
bridge->CSPViolation(aContext, isCspToInherit, aBlockedURI,
|
||||
aBlockedContentSource, aOriginalURI,
|
||||
aViolatedDirective, aViolatedPolicyIndex,
|
||||
aObserverSubject);
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
Maybe<nsresult> cancelCode;
|
||||
rv = CSPService::ConsultCSPForRedirect(callback, originalUri, newUri,
|
||||
loadInfo, cancelCode);
|
||||
|
||||
if (cancelCode) {
|
||||
aOldChannel->Cancel(*cancelCode);
|
||||
}
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
aCallback->OnRedirectVerifyCallback(NS_OK);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -55,7 +55,10 @@ child:
|
||||
async RedirectToRealChannel(RedirectToRealChannelArgs args)
|
||||
returns (nsresult rv);
|
||||
|
||||
async ConfirmRedirect(LoadInfoArgs aLoadInfo, nsIURI aNewURI) returns(nsresult rv, nsresult? cancelCode);
|
||||
async CSPViolation(CSPInfo aCSP, bool aIsCspToInherit,
|
||||
nsIURI aBlockedURI, uint32_t aBlockedContentSource,
|
||||
nsIURI aOriginalURI, nsString aViolatedDirective, uint32_t aViolatedPolicyIndex,
|
||||
nsString aObserverSubject);
|
||||
|
||||
// Tell child to delete channel (all IPDL deletes must be done from child to
|
||||
// avoid races: see bug 591708).
|
||||
|
Loading…
Reference in New Issue
Block a user