Bug 1403818 - Fix WebAuthn IPC crashes by keeping the child actor alive until process shutdown r=jcj

Summary:
We currently call ChildActor.send__delete() when clearing an active transaction
and thereby destroy the child actor. If that happens, e.g. due to a tab switch,
while a message is in the IPC buffer waiting to be delivered, we crash.

This patch creates the child actor lazily as before, but keeps it around until
the WebAuthnManager goes away, which will be at process shutdown.

Each transaction now has a unique id, that the parent process will include in
any of the ConfirmRegister, ConfirmSign, or Abort messages. That way we can
easily ignore stale messages that were in the buffer while we started a new
transaction or cancelled the current one.

Reviewers: jcj

Reviewed By: jcj

Bug #: 1403818

Differential Revision: https://phabricator.services.mozilla.com/D149
This commit is contained in:
Tim Taubert 2017-10-25 15:59:53 +02:00
Родитель e166144d3b
Коммит c29f1dbeb7
15 изменённых файлов: 226 добавлений и 151 удалений

Просмотреть файл

@ -104,12 +104,6 @@ U2FManager::ClearTransaction()
}
mTransaction.reset();
if (mChild) {
RefPtr<U2FTransactionChild> c;
mChild.swap(c);
c->Send__delete__(c);
}
}
void
@ -126,8 +120,8 @@ U2FManager::RejectTransaction(const nsresult& aError)
void
U2FManager::CancelTransaction(const nsresult& aError)
{
if (mChild) {
mChild->SendRequestCancel();
if (!NS_WARN_IF(!mChild || mTransaction.isNothing())) {
mChild->SendRequestCancel(mTransaction.ref().mId);
}
RejectTransaction(aError);
@ -140,6 +134,12 @@ U2FManager::~U2FManager()
if (mTransaction.isSome()) {
RejectTransaction(NS_ERROR_ABORT);
}
if (mChild) {
RefPtr<U2FTransactionChild> c;
mChild.swap(c);
c->Send__delete__(c);
}
}
bool
@ -281,11 +281,9 @@ U2FManager::Register(nsPIDOMWindowInner* aParent, const nsCString& aRpId,
extensions);
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(aParent, Move(info), aClientDataJSON));
mChild->SendRequestRegister(mTransaction.ref().mInfo);
mChild->SendRequestRegister(mTransaction.ref().mId, mTransaction.ref().mInfo);
return mTransaction.ref().mPromise.Ensure(__func__);
}
@ -327,18 +325,18 @@ U2FManager::Sign(nsPIDOMWindowInner* aParent,
MOZ_ASSERT(mTransaction.isNothing());
mTransaction = Some(U2FTransaction(aParent, Move(info), aClientDataJSON));
mChild->SendRequestSign(mTransaction.ref().mInfo);
mChild->SendRequestSign(mTransaction.ref().mId, mTransaction.ref().mInfo);
return mTransaction.ref().mPromise.Ensure(__func__);
}
void
U2FManager::FinishRegister(nsTArray<uint8_t>& aRegBuffer)
U2FManager::FinishRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aRegBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
// Check for a valid transaction.
if (mTransaction.isNothing()) {
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
@ -383,13 +381,14 @@ U2FManager::FinishRegister(nsTArray<uint8_t>& aRegBuffer)
}
void
U2FManager::FinishSign(nsTArray<uint8_t>& aCredentialId,
U2FManager::FinishSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
// Check for a valid transaction.
if (mTransaction.isNothing()) {
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
@ -442,11 +441,12 @@ U2FManager::FinishSign(nsTArray<uint8_t>& aCredentialId,
}
void
U2FManager::RequestAborted(const nsresult& aError)
U2FManager::RequestAborted(const uint64_t& aTransactionId,
const nsresult& aError)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransaction.isSome()) {
if (mTransaction.isSome() && mTransaction.ref().mId == aTransactionId) {
RejectTransaction(aError);
}
}
@ -456,7 +456,6 @@ U2FManager::HandleEvent(nsIDOMEvent* aEvent)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aEvent);
MOZ_ASSERT(mChild);
nsAutoString type;
aEvent->GetType(type);
@ -466,9 +465,11 @@ U2FManager::HandleEvent(nsIDOMEvent* aEvent)
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
MOZ_ASSERT(doc);
if (NS_WARN_IF(!doc)) {
return NS_ERROR_FAILURE;
}
if (doc && doc->Hidden()) {
if (doc->Hidden()) {
MOZ_LOG(gU2FManagerLog, LogLevel::Debug,
("Visibility change: U2F window is hidden, cancelling job."));
@ -481,6 +482,7 @@ U2FManager::HandleEvent(nsIDOMEvent* aEvent)
void
U2FManager::ActorDestroyed()
{
MOZ_ASSERT(NS_IsMainThread());
mChild = nullptr;
}

Просмотреть файл

@ -61,7 +61,10 @@ public:
: mParent(aParent)
, mInfo(aInfo)
, mClientData(aClientData)
{ }
, mId(NextId())
{
MOZ_ASSERT(mId > 0);
}
// Parent of the context we're running the transaction in.
nsCOMPtr<nsPIDOMWindowInner> mParent;
@ -75,6 +78,18 @@ public:
// Client data used to assemble reply objects.
nsCString mClientData;
// Unique transaction id.
uint64_t mId;
private:
// Generates a unique id for new transactions. This doesn't have to be unique
// forever, it's sufficient to differentiate between temporally close
// transactions, where messages can intersect. Can overflow.
static uint64_t NextId() {
static uint64_t id = 0;
return ++id;
}
};
class U2FManager final : public nsIDOMEventListener
@ -97,10 +112,12 @@ public:
const uint32_t& aTimeoutMillis,
const nsTArray<WebAuthnScopedCredentialDescriptor>& aKeyList);
void FinishRegister(nsTArray<uint8_t>& aRegBuffer);
void FinishSign(nsTArray<uint8_t>& aCredentialId,
void FinishRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aRegBuffer);
void FinishSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer);
void RequestAborted(const nsresult& aError);
void RequestAborted(const uint64_t& aTransactionId, const nsresult& aError);
// XXX This is exposed only until we fix bug 1410346.
void MaybeCancelTransaction(const nsresult& aError) {
@ -129,11 +146,9 @@ private:
// parent) and rejects it by calling RejectTransaction().
void CancelTransaction(const nsresult& aError);
typedef MozPromise<nsresult, nsresult, false> BackgroundActorPromise;
bool MaybeCreateBackgroundActor();
// IPC Channel for the current transaction.
// IPC Channel to the parent process.
RefPtr<U2FTransactionChild> mChild;
// The current transaction, if any.

Просмотреть файл

@ -18,30 +18,33 @@ U2FTransactionChild::U2FTransactionChild()
}
mozilla::ipc::IPCResult
U2FTransactionChild::RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer)
U2FTransactionChild::RecvConfirmRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aRegBuffer)
{
RefPtr<U2FManager> mgr = U2FManager::Get();
MOZ_ASSERT(mgr);
mgr->FinishRegister(aRegBuffer);
mgr->FinishRegister(aTransactionId, aRegBuffer);
return IPC_OK();
}
mozilla::ipc::IPCResult
U2FTransactionChild::RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
U2FTransactionChild::RecvConfirmSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aCredentialId,
nsTArray<uint8_t>&& aBuffer)
{
RefPtr<U2FManager> mgr = U2FManager::Get();
MOZ_ASSERT(mgr);
mgr->FinishSign(aCredentialId, aBuffer);
mgr->FinishSign(aTransactionId, aCredentialId, aBuffer);
return IPC_OK();
}
mozilla::ipc::IPCResult
U2FTransactionChild::RecvAbort(const nsresult& aError)
U2FTransactionChild::RecvAbort(const uint64_t& aTransactionId,
const nsresult& aError)
{
RefPtr<U2FManager> mgr = U2FManager::Get();
MOZ_ASSERT(mgr);
mgr->RequestAborted(aError);
mgr->RequestAborted(aTransactionId, aError);
return IPC_OK();
}

Просмотреть файл

@ -23,11 +23,21 @@ class U2FTransactionChild final : public PWebAuthnTransactionChild
public:
NS_INLINE_DECL_REFCOUNTING(U2FTransactionChild);
U2FTransactionChild();
mozilla::ipc::IPCResult RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer) override;
mozilla::ipc::IPCResult RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
nsTArray<uint8_t>&& aBuffer) override;
mozilla::ipc::IPCResult RecvAbort(const nsresult& aError) override;
mozilla::ipc::IPCResult
RecvConfirmRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aRegBuffer) override;
mozilla::ipc::IPCResult
RecvConfirmSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aCredentialId,
nsTArray<uint8_t>&& aBuffer) override;
mozilla::ipc::IPCResult
RecvAbort(const uint64_t& aTransactionId, const nsresult& aError) override;
void ActorDestroy(ActorDestroyReason why) override;
private:
~U2FTransactionChild() = default;
};

Просмотреть файл

@ -12,29 +12,31 @@ namespace mozilla {
namespace dom {
mozilla::ipc::IPCResult
U2FTransactionParent::RecvRequestRegister(const WebAuthnTransactionInfo& aTransactionInfo)
U2FTransactionParent::RecvRequestRegister(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo)
{
AssertIsOnBackgroundThread();
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->Register(this, aTransactionInfo);
mgr->Register(this, aTransactionId, aTransactionInfo);
return IPC_OK();
}
mozilla::ipc::IPCResult
U2FTransactionParent::RecvRequestSign(const WebAuthnTransactionInfo& aTransactionInfo)
U2FTransactionParent::RecvRequestSign(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo)
{
AssertIsOnBackgroundThread();
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->Sign(this, aTransactionInfo);
mgr->Sign(this, aTransactionId, aTransactionInfo);
return IPC_OK();
}
mozilla::ipc::IPCResult
U2FTransactionParent::RecvRequestCancel()
U2FTransactionParent::RecvRequestCancel(const uint64_t& aTransactionId)
{
AssertIsOnBackgroundThread();
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->Cancel(this);
mgr->Cancel(this, aTransactionId);
return IPC_OK();
}

Просмотреть файл

@ -23,12 +23,20 @@ class U2FTransactionParent final : public PWebAuthnTransactionParent
public:
NS_INLINE_DECL_REFCOUNTING(U2FTransactionParent);
U2FTransactionParent() = default;
virtual mozilla::ipc::IPCResult
RecvRequestRegister(const WebAuthnTransactionInfo& aTransactionInfo) override;
RecvRequestRegister(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo) override;
virtual mozilla::ipc::IPCResult
RecvRequestSign(const WebAuthnTransactionInfo& aTransactionInfo) override;
virtual mozilla::ipc::IPCResult RecvRequestCancel() override;
RecvRequestSign(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo) override;
virtual mozilla::ipc::IPCResult
RecvRequestCancel(const uint64_t& aTransactionId) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private:
~U2FTransactionParent() = default;
};

Просмотреть файл

@ -39,13 +39,13 @@ async protocol PWebAuthnTransaction {
manager PBackground;
parent:
async __delete__();
async RequestRegister(WebAuthnTransactionInfo aTransactionInfo);
async RequestSign(WebAuthnTransactionInfo aTransactionInfo);
async RequestCancel();
async RequestRegister(uint64_t aTransactionId, WebAuthnTransactionInfo aTransactionInfo);
async RequestSign(uint64_t aTransactionId, WebAuthnTransactionInfo aTransactionInfo);
async RequestCancel(uint64_t aTransactionId);
child:
async ConfirmRegister(uint8_t[] RegBuffer);
async ConfirmSign(uint8_t[] CredentialID, uint8_t[] ReplyBuffer);
async Abort(nsresult Error);
async ConfirmRegister(uint64_t aTransactionId, uint8_t[] RegBuffer);
async ConfirmSign(uint64_t aTransactionId, uint8_t[] CredentialID, uint8_t[] ReplyBuffer);
async Abort(uint64_t aTransactionId, nsresult Error);
};
}

Просмотреть файл

@ -120,7 +120,7 @@ NS_IMPL_ISUPPORTS(U2FPrefManager, nsIObserver);
U2FTokenManager::U2FTokenManager()
: mTransactionParent(nullptr)
, mTransactionId(0)
, mLastTransactionId(0)
{
MOZ_ASSERT(XRE_IsParentProcess());
// Create on the main thread to make sure ClearOnShutdown() works.
@ -158,9 +158,10 @@ U2FTokenManager::Get()
}
void
U2FTokenManager::AbortTransaction(const nsresult& aError)
U2FTokenManager::AbortTransaction(const uint64_t& aTransactionId,
const nsresult& aError)
{
Unused << mTransactionParent->SendAbort(aError);
Unused << mTransactionParent->SendAbort(aTransactionId, aError);
ClearTransaction();
}
@ -183,8 +184,8 @@ U2FTokenManager::ClearTransaction()
// Forget promises, if necessary.
mRegisterPromise.DisconnectIfExists();
mSignPromise.DisconnectIfExists();
// Bump transaction id.
mTransactionId++;
// Clear transaction id.
mLastTransactionId = 0;
}
RefPtr<U2FTokenTransport>
@ -218,6 +219,7 @@ U2FTokenManager::GetTokenManagerImpl()
void
U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo)
{
MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthRegister"));
@ -227,7 +229,7 @@ U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
mTokenManagerImpl = GetTokenManagerImpl();
if (!mTokenManagerImpl) {
AbortTransaction(NS_ERROR_DOM_NOT_ALLOWED_ERR);
AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
return;
}
@ -237,11 +239,11 @@ U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
(aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR);
AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
uint64_t tid = mTransactionId;
uint64_t tid = mLastTransactionId = aTransactionId;
mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();
mTokenManagerImpl->Register(aTransactionInfo.Descriptors(),
aTransactionInfo.RpIdHash(),
@ -270,36 +272,31 @@ U2FTokenManager::Register(PWebAuthnTransactionParent* aTransactionParent,
}
void
U2FTokenManager::MaybeConfirmRegister(uint64_t aTransactionId,
U2FTokenManager::MaybeConfirmRegister(const uint64_t& aTransactionId,
U2FRegisterResult& aResult)
{
if (mTransactionId != aTransactionId) {
return;
}
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mRegisterPromise.Complete();
nsTArray<uint8_t> registration;
aResult.ConsumeRegistration(registration);
Unused << mTransactionParent->SendConfirmRegister(registration);
Unused << mTransactionParent->SendConfirmRegister(aTransactionId, registration);
ClearTransaction();
}
void
U2FTokenManager::MaybeAbortRegister(uint64_t aTransactionId,
U2FTokenManager::MaybeAbortRegister(const uint64_t& aTransactionId,
const nsresult& aError)
{
if (mTransactionId != aTransactionId) {
return;
}
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mRegisterPromise.Complete();
AbortTransaction(aError);
AbortTransaction(aTransactionId, aError);
}
void
U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo)
{
MOZ_LOG(gU2FTokenManagerLog, LogLevel::Debug, ("U2FAuthSign"));
@ -309,17 +306,17 @@ U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
mTokenManagerImpl = GetTokenManagerImpl();
if (!mTokenManagerImpl) {
AbortTransaction(NS_ERROR_DOM_NOT_ALLOWED_ERR);
AbortTransaction(aTransactionId, NS_ERROR_DOM_NOT_ALLOWED_ERR);
return;
}
if ((aTransactionInfo.RpIdHash().Length() != SHA256_LENGTH) ||
(aTransactionInfo.ClientDataHash().Length() != SHA256_LENGTH)) {
AbortTransaction(NS_ERROR_DOM_UNKNOWN_ERR);
AbortTransaction(aTransactionId, NS_ERROR_DOM_UNKNOWN_ERR);
return;
}
uint64_t tid = mTransactionId;
uint64_t tid = mLastTransactionId = aTransactionId;
mozilla::TimeStamp startTime = mozilla::TimeStamp::Now();
mTokenManagerImpl->Sign(aTransactionInfo.Descriptors(),
aTransactionInfo.RpIdHash(),
@ -348,13 +345,10 @@ U2FTokenManager::Sign(PWebAuthnTransactionParent* aTransactionParent,
}
void
U2FTokenManager::MaybeConfirmSign(uint64_t aTransactionId,
U2FTokenManager::MaybeConfirmSign(const uint64_t& aTransactionId,
U2FSignResult& aResult)
{
if (mTransactionId != aTransactionId) {
return;
}
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mSignPromise.Complete();
nsTArray<uint8_t> keyHandle;
@ -362,25 +356,24 @@ U2FTokenManager::MaybeConfirmSign(uint64_t aTransactionId,
nsTArray<uint8_t> signature;
aResult.ConsumeSignature(signature);
Unused << mTransactionParent->SendConfirmSign(keyHandle, signature);
Unused << mTransactionParent->SendConfirmSign(aTransactionId, keyHandle, signature);
ClearTransaction();
}
void
U2FTokenManager::MaybeAbortSign(uint64_t aTransactionId, const nsresult& aError)
U2FTokenManager::MaybeAbortSign(const uint64_t& aTransactionId,
const nsresult& aError)
{
if (mTransactionId != aTransactionId) {
return;
}
MOZ_ASSERT(mLastTransactionId == aTransactionId);
mSignPromise.Complete();
AbortTransaction(aError);
AbortTransaction(aTransactionId, aError);
}
void
U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent)
U2FTokenManager::Cancel(PWebAuthnTransactionParent* aParent,
const uint64_t& aTransactionId)
{
if (mTransactionParent != aParent) {
if (mTransactionParent != aParent || mLastTransactionId != aTransactionId) {
return;
}

Просмотреть файл

@ -31,23 +31,25 @@ public:
NS_INLINE_DECL_REFCOUNTING(U2FTokenManager)
static U2FTokenManager* Get();
void Register(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo);
void Sign(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo);
void Cancel(PWebAuthnTransactionParent* aTransactionParent);
void Cancel(PWebAuthnTransactionParent* aTransactionParent,
const uint64_t& aTransactionId);
void MaybeClearTransaction(PWebAuthnTransactionParent* aParent);
static void Initialize();
private:
U2FTokenManager();
~U2FTokenManager();
RefPtr<U2FTokenTransport> GetTokenManagerImpl();
void AbortTransaction(const nsresult& aError);
void AbortTransaction(const uint64_t& aTransactionId, const nsresult& aError);
void ClearTransaction();
void MaybeConfirmRegister(uint64_t aTransactionId,
U2FRegisterResult& aResult);
void MaybeAbortRegister(uint64_t aTransactionId, const nsresult& aError);
void MaybeConfirmSign(uint64_t aTransactionId, U2FSignResult& aResult);
void MaybeAbortSign(uint64_t aTransactionId, const nsresult& aError);
void MaybeConfirmRegister(const uint64_t& aTransactionId, U2FRegisterResult& aResult);
void MaybeAbortRegister(const uint64_t& aTransactionId, const nsresult& aError);
void MaybeConfirmSign(const uint64_t& aTransactionId, U2FSignResult& aResult);
void MaybeAbortSign(const uint64_t& aTransactionId, const nsresult& aError);
// Using a raw pointer here, as the lifetime of the IPC object is managed by
// the PBackground protocol code. This means we cannot be left holding an
// invalid IPC protocol object after the transaction is finished.
@ -55,10 +57,10 @@ private:
RefPtr<U2FTokenTransport> mTokenManagerImpl;
MozPromiseRequestHolder<U2FRegisterPromise> mRegisterPromise;
MozPromiseRequestHolder<U2FSignPromise> mSignPromise;
// Guards the asynchronous promise resolution of token manager impls.
// We don't need to protect this with a lock as it will only be modified
// and checked on the PBackground thread in the parent process.
uint64_t mTransactionId;
// The last transaction id, non-zero if there's an active transaction. This
// guards any cancel messages to ensure we don't cancel newer transactions
// due to a stale message.
uint64_t mLastTransactionId;
};
} // namespace dom

Просмотреть файл

@ -199,12 +199,6 @@ WebAuthnManager::ClearTransaction()
}
mTransaction.reset();
if (mChild) {
RefPtr<WebAuthnTransactionChild> c;
mChild.swap(c);
c->Send__delete__(c);
}
}
void
@ -220,8 +214,8 @@ WebAuthnManager::RejectTransaction(const nsresult& aError)
void
WebAuthnManager::CancelTransaction(const nsresult& aError)
{
if (mChild) {
mChild->SendRequestCancel();
if (!NS_WARN_IF(!mChild || mTransaction.isNothing())) {
mChild->SendRequestCancel(mTransaction.ref().mId);
}
RejectTransaction(aError);
@ -234,6 +228,12 @@ WebAuthnManager::~WebAuthnManager()
if (mTransaction.isSome()) {
RejectTransaction(NS_ERROR_ABORT);
}
if (mChild) {
RefPtr<WebAuthnTransactionChild> c;
mChild.swap(c);
c->Send__delete__(c);
}
}
bool
@ -460,7 +460,7 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent,
}
if (!MaybeCreateBackgroundActor()) {
promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return promise.forget();
}
@ -481,8 +481,7 @@ WebAuthnManager::MakeCredential(nsPIDOMWindowInner* aParent,
Move(info),
Move(clientDataJSON)));
mChild->SendRequestRegister(mTransaction.ref().mInfo);
mChild->SendRequestRegister(mTransaction.ref().mId, mTransaction.ref().mInfo);
return promise.forget();
}
@ -605,7 +604,7 @@ WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
}
if (!MaybeCreateBackgroundActor()) {
promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
promise->MaybeReject(NS_ERROR_DOM_OPERATION_ERR);
return promise.forget();
}
@ -631,8 +630,7 @@ WebAuthnManager::GetAssertion(nsPIDOMWindowInner* aParent,
Move(info),
Move(clientDataJSON)));
mChild->SendRequestSign(mTransaction.ref().mInfo);
mChild->SendRequestSign(mTransaction.ref().mId, mTransaction.ref().mInfo);
return promise.forget();
}
@ -660,12 +658,13 @@ WebAuthnManager::Store(nsPIDOMWindowInner* aParent,
}
void
WebAuthnManager::FinishMakeCredential(nsTArray<uint8_t>& aRegBuffer)
WebAuthnManager::FinishMakeCredential(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aRegBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
// Check for a valid transaction.
if (mTransaction.isNothing()) {
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
@ -787,13 +786,14 @@ WebAuthnManager::FinishMakeCredential(nsTArray<uint8_t>& aRegBuffer)
}
void
WebAuthnManager::FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
WebAuthnManager::FinishGetAssertion(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer)
{
MOZ_ASSERT(NS_IsMainThread());
// Check for a valid transaction.
if (mTransaction.isNothing()) {
if (mTransaction.isNothing() || mTransaction.ref().mId != aTransactionId) {
return;
}
@ -872,11 +872,12 @@ WebAuthnManager::FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
}
void
WebAuthnManager::RequestAborted(const nsresult& aError)
WebAuthnManager::RequestAborted(const uint64_t& aTransactionId,
const nsresult& aError)
{
MOZ_ASSERT(NS_IsMainThread());
if (mTransaction.isSome()) {
if (mTransaction.isSome() && mTransaction.ref().mId == aTransactionId) {
RejectTransaction(aError);
}
}
@ -895,9 +896,11 @@ WebAuthnManager::HandleEvent(nsIDOMEvent* aEvent)
nsCOMPtr<nsIDocument> doc =
do_QueryInterface(aEvent->InternalDOMEvent()->GetTarget());
MOZ_ASSERT(doc);
if (NS_WARN_IF(!doc)) {
return NS_ERROR_FAILURE;
}
if (doc && doc->Hidden()) {
if (doc->Hidden()) {
MOZ_LOG(gWebAuthnManagerLog, LogLevel::Debug,
("Visibility change: WebAuthn window is hidden, cancelling job."));

Просмотреть файл

@ -71,7 +71,10 @@ public:
, mPromise(aPromise)
, mInfo(aInfo)
, mClientData(aClientData)
{ }
, mId(NextId())
{
MOZ_ASSERT(mId > 0);
}
// Parent of the context we're running the transaction in.
nsCOMPtr<nsPIDOMWindowInner> mParent;
@ -85,6 +88,18 @@ public:
// Client data used to assemble reply objects.
nsCString mClientData;
// Unique transaction id.
uint64_t mId;
private:
// Generates a unique id for new transactions. This doesn't have to be unique
// forever, it's sufficient to differentiate between temporally close
// transactions, where messages can intersect. Can overflow.
static uint64_t NextId() {
static uint64_t id = 0;
return ++id;
}
};
class WebAuthnManager final : public nsIDOMEventListener
@ -108,14 +123,16 @@ public:
Store(nsPIDOMWindowInner* aParent, const Credential& aCredential);
void
FinishMakeCredential(nsTArray<uint8_t>& aRegBuffer);
FinishMakeCredential(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aRegBuffer);
void
FinishGetAssertion(nsTArray<uint8_t>& aCredentialId,
FinishGetAssertion(const uint64_t& aTransactionId,
nsTArray<uint8_t>& aCredentialId,
nsTArray<uint8_t>& aSigBuffer);
void
RequestAborted(const nsresult& aError);
RequestAborted(const uint64_t& aTransactionId, const nsresult& aError);
void ActorDestroyed();
@ -131,11 +148,9 @@ private:
// parent) and rejects it by calling RejectTransaction().
void CancelTransaction(const nsresult& aError);
typedef MozPromise<nsresult, nsresult, false> BackgroundActorPromise;
bool MaybeCreateBackgroundActor();
// IPC Channel for the current transaction.
// IPC Channel to the parent process.
RefPtr<WebAuthnTransactionChild> mChild;
// The current transaction, if any.

Просмотреть файл

@ -5,7 +5,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/WebAuthnTransactionChild.h"
#include "mozilla/dom/WebAuthnManager.h"
namespace mozilla {
namespace dom {
@ -19,30 +18,33 @@ WebAuthnTransactionChild::WebAuthnTransactionChild()
}
mozilla::ipc::IPCResult
WebAuthnTransactionChild::RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer)
WebAuthnTransactionChild::RecvConfirmRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aRegBuffer)
{
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
MOZ_ASSERT(mgr);
mgr->FinishMakeCredential(aRegBuffer);
mgr->FinishMakeCredential(aTransactionId, aRegBuffer);
return IPC_OK();
}
mozilla::ipc::IPCResult
WebAuthnTransactionChild::RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
WebAuthnTransactionChild::RecvConfirmSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aCredentialId,
nsTArray<uint8_t>&& aBuffer)
{
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
MOZ_ASSERT(mgr);
mgr->FinishGetAssertion(aCredentialId, aBuffer);
mgr->FinishGetAssertion(aTransactionId, aCredentialId, aBuffer);
return IPC_OK();
}
mozilla::ipc::IPCResult
WebAuthnTransactionChild::RecvAbort(const nsresult& aError)
WebAuthnTransactionChild::RecvAbort(const uint64_t& aTransactionId,
const nsresult& aError)
{
RefPtr<WebAuthnManager> mgr = WebAuthnManager::Get();
MOZ_ASSERT(mgr);
mgr->RequestAborted(aError);
mgr->RequestAborted(aTransactionId, aError);
return IPC_OK();
}

Просмотреть файл

@ -24,11 +24,21 @@ class WebAuthnTransactionChild final : public PWebAuthnTransactionChild
public:
NS_INLINE_DECL_REFCOUNTING(WebAuthnTransactionChild);
WebAuthnTransactionChild();
mozilla::ipc::IPCResult RecvConfirmRegister(nsTArray<uint8_t>&& aRegBuffer) override;
mozilla::ipc::IPCResult RecvConfirmSign(nsTArray<uint8_t>&& aCredentialId,
nsTArray<uint8_t>&& aBuffer) override;
mozilla::ipc::IPCResult RecvAbort(const nsresult& aError) override;
mozilla::ipc::IPCResult
RecvConfirmRegister(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aRegBuffer) override;
mozilla::ipc::IPCResult
RecvConfirmSign(const uint64_t& aTransactionId,
nsTArray<uint8_t>&& aCredentialId,
nsTArray<uint8_t>&& aBuffer) override;
mozilla::ipc::IPCResult
RecvAbort(const uint64_t& aTransactionId, const nsresult& aError) override;
void ActorDestroy(ActorDestroyReason why) override;
private:
~WebAuthnTransactionChild() = default;
};

Просмотреть файл

@ -12,29 +12,31 @@ namespace mozilla {
namespace dom {
mozilla::ipc::IPCResult
WebAuthnTransactionParent::RecvRequestRegister(const WebAuthnTransactionInfo& aTransactionInfo)
WebAuthnTransactionParent::RecvRequestRegister(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo)
{
AssertIsOnBackgroundThread();
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->Register(this, aTransactionInfo);
mgr->Register(this, aTransactionId, aTransactionInfo);
return IPC_OK();
}
mozilla::ipc::IPCResult
WebAuthnTransactionParent::RecvRequestSign(const WebAuthnTransactionInfo& aTransactionInfo)
WebAuthnTransactionParent::RecvRequestSign(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo)
{
AssertIsOnBackgroundThread();
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->Sign(this, aTransactionInfo);
mgr->Sign(this, aTransactionId, aTransactionInfo);
return IPC_OK();
}
mozilla::ipc::IPCResult
WebAuthnTransactionParent::RecvRequestCancel()
WebAuthnTransactionParent::RecvRequestCancel(const uint64_t& aTransactionId)
{
AssertIsOnBackgroundThread();
U2FTokenManager* mgr = U2FTokenManager::Get();
mgr->Cancel(this);
mgr->Cancel(this, aTransactionId);
return IPC_OK();
}

Просмотреть файл

@ -23,12 +23,20 @@ class WebAuthnTransactionParent final : public PWebAuthnTransactionParent
public:
NS_INLINE_DECL_REFCOUNTING(WebAuthnTransactionParent);
WebAuthnTransactionParent() = default;
virtual mozilla::ipc::IPCResult
RecvRequestRegister(const WebAuthnTransactionInfo& aTransactionInfo) override;
RecvRequestRegister(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo) override;
virtual mozilla::ipc::IPCResult
RecvRequestSign(const WebAuthnTransactionInfo& aTransactionInfo) override;
virtual mozilla::ipc::IPCResult RecvRequestCancel() override;
RecvRequestSign(const uint64_t& aTransactionId,
const WebAuthnTransactionInfo& aTransactionInfo) override;
virtual mozilla::ipc::IPCResult
RecvRequestCancel(const uint64_t& aTransactionId) override;
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
private:
~WebAuthnTransactionParent() = default;
};