The parent derived the native helper-launch decision from a content-process supplied aWasFileChannel flag and URI rather than binding it to the sending process's capabilities. Stop sending aWasFileChannel over IPC and derive it on the parent from the actual URI, and reject a file:// URI in PExternalHelperApp from a process that is not allowed to load local files (mirrors the file:// policy in ValidatePrincipalCouldPotentiallyBeLoadedBy). Differential Revision: https://phabricator.services.mozilla.com/D309577
484 lines
12 KiB
C++
484 lines
12 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "mozilla/DebugOnly.h"
|
|
|
|
#include "ExternalHelperAppParent.h"
|
|
#include "nsExternalHelperAppService.h"
|
|
#include "nsIContent.h"
|
|
#include "nsCExternalHandlerService.h"
|
|
#include "nsIExternalHelperAppService.h"
|
|
#include "mozilla/dom/ContentParent.h"
|
|
#include "mozilla/dom/Element.h"
|
|
#include "mozilla/dom/BrowserParent.h"
|
|
#include "nsStringStream.h"
|
|
#include "mozilla/ipc/URIUtils.h"
|
|
#include "nsNetUtil.h"
|
|
#include "mozilla/dom/Document.h"
|
|
#include "mozilla/dom/CanonicalBrowsingContext.h"
|
|
#include "mozilla/dom/WindowGlobalParent.h"
|
|
#include "nsQueryObject.h"
|
|
|
|
using namespace mozilla::ipc;
|
|
|
|
namespace mozilla {
|
|
namespace dom {
|
|
|
|
NS_IMPL_ISUPPORTS_INHERITED(ExternalHelperAppParent, nsHashPropertyBag,
|
|
nsIRequest, nsIChannel, nsIMultiPartChannel,
|
|
nsIPrivateBrowsingChannel, nsIResumableChannel,
|
|
nsIStreamListener, nsIExternalHelperAppParent)
|
|
|
|
ExternalHelperAppParent::ExternalHelperAppParent(
|
|
nsIURI* uri, const int64_t& aContentLength,
|
|
const nsACString& aContentDispositionHeader,
|
|
const uint32_t& aContentDispositionHint,
|
|
const nsAString& aContentDispositionFilename)
|
|
: mURI(uri),
|
|
mPending(false),
|
|
mIPCClosed(false),
|
|
mLoadFlags(0),
|
|
mStatus(NS_OK),
|
|
mCanceled(false),
|
|
mContentLength(aContentLength),
|
|
// Never trust a child-supplied flag for the native helper-launch
|
|
// decision: derive it from the actual URI the parent will operate on.
|
|
mWasFileChannel(uri && uri->SchemeIs("file")) {
|
|
mContentDispositionHeader = aContentDispositionHeader;
|
|
if (!mContentDispositionHeader.IsEmpty()) {
|
|
NS_GetFilenameFromDisposition(mContentDispositionFilename,
|
|
mContentDispositionHeader);
|
|
mContentDisposition =
|
|
NS_GetContentDispositionFromHeader(mContentDispositionHeader, this);
|
|
} else {
|
|
mContentDisposition = aContentDispositionHint;
|
|
mContentDispositionFilename = aContentDispositionFilename;
|
|
}
|
|
}
|
|
|
|
bool ExternalHelperAppParent::Init(
|
|
const mozilla::net::LoadInfoArgs& aLoadInfoArgs,
|
|
const nsACString& aMimeContentType, const bool& aForceSave,
|
|
nsIURI* aReferrer, BrowsingContext* aContext) {
|
|
nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(
|
|
aLoadInfoArgs, ContentParent::Cast(Manager())->GetRemoteType(),
|
|
getter_AddRefs(mLoadInfo));
|
|
if (NS_FAILED(rv)) {
|
|
return false;
|
|
}
|
|
|
|
nsCOMPtr<nsIExternalHelperAppService> helperAppService =
|
|
do_GetService(NS_EXTERNALHELPERAPPSERVICE_CONTRACTID);
|
|
NS_ASSERTION(helperAppService, "No Helper App Service!");
|
|
|
|
if (aReferrer) {
|
|
SetPropertyAsInterface(u"docshell.internalReferrer"_ns, aReferrer);
|
|
}
|
|
|
|
if (aContext) {
|
|
WindowGlobalParent* parent =
|
|
aContext->Canonical()->GetCurrentWindowGlobal();
|
|
if (parent) {
|
|
RefPtr<BrowserParent> browser = parent->GetBrowserParent();
|
|
if (browser) {
|
|
bool isPrivate = false;
|
|
nsCOMPtr<nsILoadContext> loadContext = browser->GetLoadContext();
|
|
loadContext->GetUsePrivateBrowsing(&isPrivate);
|
|
SetPrivate(isPrivate);
|
|
}
|
|
}
|
|
}
|
|
|
|
helperAppService->CreateListener(aMimeContentType, this, aContext, aForceSave,
|
|
nullptr, getter_AddRefs(mListener));
|
|
return mListener != nullptr;
|
|
}
|
|
|
|
void ExternalHelperAppParent::ActorDestroy(ActorDestroyReason why) {
|
|
mIPCClosed = true;
|
|
}
|
|
|
|
void ExternalHelperAppParent::Delete() {
|
|
if (!mIPCClosed) {
|
|
(void)Send__delete__(this);
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ExternalHelperAppParent::RecvOnStartRequest(
|
|
const nsACString& entityID) {
|
|
mEntityID = entityID;
|
|
mPending = true;
|
|
RefPtr<nsIStreamListener> listener = mListener;
|
|
mStatus = listener->OnStartRequest(this);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ExternalHelperAppParent::RecvOnDataAvailable(
|
|
const nsACString& data, const uint64_t& offset) {
|
|
if (NS_FAILED(mStatus)) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
MOZ_ASSERT(mPending, "must be pending!");
|
|
|
|
nsCOMPtr<nsIInputStream> stringStream;
|
|
DebugOnly<nsresult> rv = NS_NewByteInputStream(
|
|
getter_AddRefs(stringStream), Span(data), NS_ASSIGNMENT_DEPEND);
|
|
NS_ASSERTION(NS_SUCCEEDED(rv), "failed to create dependent string!");
|
|
RefPtr<nsIStreamListener> listener = mListener;
|
|
mStatus =
|
|
listener->OnDataAvailable(this, stringStream, offset, data.Length());
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ExternalHelperAppParent::RecvOnStopRequest(
|
|
const nsresult& code) {
|
|
mPending = false;
|
|
RefPtr<nsIStreamListener> listener = mListener;
|
|
listener->OnStopRequest(
|
|
this, (NS_SUCCEEDED(code) && NS_FAILED(mStatus)) ? mStatus : code);
|
|
Delete();
|
|
return IPC_OK();
|
|
}
|
|
|
|
//
|
|
// nsIStreamListener
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::OnDataAvailable(nsIRequest* request,
|
|
nsIInputStream* input, uint64_t offset,
|
|
uint32_t count) {
|
|
RefPtr<nsIStreamListener> listener = mListener;
|
|
return listener->OnDataAvailable(request, input, offset, count);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::OnStartRequest(nsIRequest* request) {
|
|
RefPtr<nsIStreamListener> listener = mListener;
|
|
return listener->OnStartRequest(request);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::OnStopRequest(nsIRequest* request, nsresult status) {
|
|
RefPtr<nsIStreamListener> listener = mListener;
|
|
nsresult rv = listener->OnStopRequest(request, status);
|
|
Delete();
|
|
return rv;
|
|
}
|
|
|
|
ExternalHelperAppParent::~ExternalHelperAppParent() = default;
|
|
|
|
//
|
|
// nsIRequest implementation...
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetName(nsACString& aResult) {
|
|
if (!mURI) {
|
|
aResult.Truncate();
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
mURI->GetAsciiSpec(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::IsPending(bool* aResult) {
|
|
*aResult = mPending;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetStatus(nsresult* aResult) {
|
|
*aResult = mStatus;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP ExternalHelperAppParent::SetCanceledReason(
|
|
const nsACString& aReason) {
|
|
return SetCanceledReasonImpl(aReason);
|
|
}
|
|
|
|
NS_IMETHODIMP ExternalHelperAppParent::GetCanceledReason(nsACString& aReason) {
|
|
return GetCanceledReasonImpl(aReason);
|
|
}
|
|
|
|
NS_IMETHODIMP ExternalHelperAppParent::CancelWithReason(
|
|
nsresult aStatus, const nsACString& aReason) {
|
|
return CancelWithReasonImpl(aStatus, aReason);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::Cancel(nsresult aStatus) {
|
|
mCanceled = true;
|
|
mStatus = aStatus;
|
|
(void)SendCancel(aStatus);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetCanceled(bool* aCanceled) {
|
|
*aCanceled = mCanceled;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::Suspend() { return NS_ERROR_NOT_IMPLEMENTED; }
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::Resume() { return NS_ERROR_NOT_IMPLEMENTED; }
|
|
|
|
//
|
|
// nsIChannel implementation
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetOriginalURI(nsIURI** aURI) {
|
|
NS_IF_ADDREF(*aURI = mURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetOriginalURI(nsIURI* aURI) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetURI(nsIURI** aURI) {
|
|
NS_IF_ADDREF(*aURI = mURI);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::Open(nsIInputStream** aResult) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::AsyncOpen(nsIStreamListener* aListener) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetLoadFlags(nsLoadFlags* aLoadFlags) {
|
|
*aLoadFlags = mLoadFlags;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetLoadFlags(nsLoadFlags aLoadFlags) {
|
|
mLoadFlags = aLoadFlags;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetTRRMode(nsIRequest::TRRMode* aTRRMode) {
|
|
return GetTRRModeImpl(aTRRMode);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetTRRMode(nsIRequest::TRRMode aTRRMode) {
|
|
return SetTRRModeImpl(aTRRMode);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetIsDocument(bool* aIsDocument) {
|
|
return NS_GetIsDocumentChannel(this, aIsDocument);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetLoadGroup(nsILoadGroup** aLoadGroup) {
|
|
*aLoadGroup = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetLoadGroup(nsILoadGroup* aLoadGroup) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetOwner(nsISupports** aOwner) {
|
|
*aOwner = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetOwner(nsISupports* aOwner) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetLoadInfo(nsILoadInfo** aLoadInfo) {
|
|
NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetLoadInfo(nsILoadInfo* aLoadInfo) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetParentProcessChannelHandle(
|
|
mozilla::dom::ParentProcessChannelHandle** aValue) {
|
|
*aValue = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetParentProcessChannelHandle(
|
|
mozilla::dom::ParentProcessChannelHandle* aValue) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetNotificationCallbacks(
|
|
nsIInterfaceRequestor** aCallbacks) {
|
|
*aCallbacks = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetNotificationCallbacks(
|
|
nsIInterfaceRequestor* aCallbacks) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetSecurityInfo(
|
|
nsITransportSecurityInfo** aSecurityInfo) {
|
|
*aSecurityInfo = nullptr;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetContentType(nsACString& aContentType) {
|
|
aContentType.Truncate();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetContentType(const nsACString& aContentType) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetContentCharset(nsACString& aContentCharset) {
|
|
aContentCharset.Truncate();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetContentCharset(const nsACString& aContentCharset) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetContentDisposition(uint32_t* aContentDisposition) {
|
|
// NB: mContentDisposition may or may not be set to a non UINT32_MAX value in
|
|
// nsExternalHelperAppService::DoContentContentProcessHelper
|
|
if (mContentDispositionHeader.IsEmpty() && mContentDisposition == UINT32_MAX)
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
*aContentDisposition = mContentDisposition;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetContentDisposition(uint32_t aContentDisposition) {
|
|
mContentDisposition = aContentDisposition;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetContentDispositionFilename(
|
|
nsAString& aContentDispositionFilename) {
|
|
if (mContentDispositionFilename.IsEmpty()) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
aContentDispositionFilename = mContentDispositionFilename;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetContentDispositionFilename(
|
|
const nsAString& aContentDispositionFilename) {
|
|
mContentDispositionFilename = aContentDispositionFilename;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetContentDispositionHeader(
|
|
nsACString& aContentDispositionHeader) {
|
|
if (mContentDispositionHeader.IsEmpty()) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
aContentDispositionHeader = mContentDispositionHeader;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetContentLength(int64_t* aContentLength) {
|
|
if (mContentLength < 0) {
|
|
*aContentLength = -1;
|
|
} else {
|
|
*aContentLength = mContentLength;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::SetContentLength(int64_t aContentLength) {
|
|
mContentLength = aContentLength;
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// nsIResumableChannel implementation
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::ResumeAt(uint64_t startPos,
|
|
const nsACString& entityID) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetEntityID(nsACString& aEntityID) {
|
|
aEntityID = mEntityID;
|
|
return NS_OK;
|
|
}
|
|
|
|
//
|
|
// nsIMultiPartChannel implementation
|
|
//
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetBaseChannel(nsIChannel** aChannel) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetPartID(uint32_t* aPartID) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetIsFirstPart(bool* aIsLastPart) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ExternalHelperAppParent::GetIsLastPart(bool* aIsLastPart) {
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|