Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/webauthn/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 19 kB image not shown  

Quelle  WebAuthnService.cpp

  Sprache: 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 "WebAuthnService.h"

#include "WebAuthnEnumStrings.h"
#include "WebAuthnTransportIdentifiers.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPrefs_security.h"
#include "nsIObserverService.h"
#include "nsTextFormatter.h"
#include "nsThreadUtils.h"

namespace mozilla::dom {

already_AddRefed<nsIWebAuthnService> NewWebAuthnService() {
  nsCOMPtr<nsIWebAuthnService> webauthnService(new WebAuthnService());
  return webauthnService.forget();
}

NS_IMPL_ISUPPORTS(WebAuthnService, nsIWebAuthnService)

void WebAuthnService::ShowAttestationConsentPrompt(
    const nsString& aOrigin, uint64_t aTransactionId,
    uint64_t aBrowsingContextId) {
  RefPtr<WebAuthnService> self = this;
#ifdef MOZ_WIDGET_ANDROID
  // We don't have a way to prompt the user for consent on Android, so just
  // assume consent not granted.
  nsCOMPtr<nsIRunnable> runnable(
      NS_NewRunnableFunction(__func__, [self, aTransactionId]() {
        self->SetHasAttestationConsent(
            aTransactionId,
            StaticPrefs::security_webauthn_always_allow_direct_attestation());
      }));
#else
  nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
      __func__, [self, aOrigin, aTransactionId, aBrowsingContextId]() {
        if (StaticPrefs::security_webauthn_always_allow_direct_attestation()) {
          self->SetHasAttestationConsent(aTransactionId, true);
          return;
        }
        nsCOMPtr<nsIObserverService> os = services::GetObserverService();
        if (!os) {
          return;
        }
        const nsLiteralString jsonFmt =
            u"{\"prompt\": {\"type\":\"attestation-consent\"},"_ns
            u"\"origin\": \"%S\","_ns
            u"\"tid\": %llu, \"browsingContextId\": %llu}"_ns;
        nsString json;
        nsTextFormatter::ssprintf(json, jsonFmt.get(), aOrigin.get(),
                                  aTransactionId, aBrowsingContextId);
        MOZ_ALWAYS_SUCCEEDS(
            os->NotifyObservers(nullptr, "webauthn-prompt", json.get()));
      }));
#endif
  NS_DispatchToMainThread(runnable.forget());
}

NS_IMETHODIMP
WebAuthnService::MakeCredential(uint64_t aTransactionId,
                                uint64_t aBrowsingContextId,
                                nsIWebAuthnRegisterArgs* aArgs,
                                nsIWebAuthnRegisterPromise* aPromise) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aArgs);
  MOZ_ASSERT(aPromise);

  ResetActiveTransaction();
  mActiveTransaction =
      Some(TransactionState{.service = DefaultService(),
                            .transactionId = aTransactionId,
                            .parentRegisterPromise = Some(aPromise)});

  // We may need to show an attestation consent prompt before we return a
  // credential to WebAuthnTransactionParent, so we insert a new promise that
  // chains to `aPromise` here.

  nsString attestation;
  (void)aArgs->GetAttestationConveyancePreference(attestation);
  bool attestationRequested = !attestation.EqualsLiteral(
      MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE);

  nsString origin;
  (void)aArgs->GetOrigin(origin);

  RefPtr<WebAuthnRegisterPromiseHolder> promiseHolder =
      new WebAuthnRegisterPromiseHolder(GetCurrentSerialEventTarget());

  RefPtr<WebAuthnService> self = this;
  RefPtr<WebAuthnRegisterPromise> promise = promiseHolder->Ensure();
  promise
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [self, origin, aTransactionId, aBrowsingContextId,
           attestationRequested](
              const WebAuthnRegisterPromise::ResolveOrRejectValue& aValue) {
            MOZ_ASSERT(NS_IsMainThread());
            if (self->mActiveTransaction.isNothing()) {
              return;
            }
            MOZ_ASSERT(
                self->mActiveTransaction.ref().parentRegisterPromise.isSome());
            MOZ_ASSERT(
                self->mActiveTransaction.ref().registerResult.isNothing());
            MOZ_ASSERT(
                self->mActiveTransaction.ref().childRegisterRequest.Exists());

            self->mActiveTransaction.ref().childRegisterRequest.Complete();

            if (aValue.IsReject()) {
              self->mActiveTransaction.ref()
                  .parentRegisterPromise.ref()
                  ->Reject(aValue.RejectValue());
              self->mActiveTransaction.reset();
              return;
            }

            nsIWebAuthnRegisterResult* result = aValue.ResolveValue();
            // We can return whatever result we have if the authenticator
            // handled attestation consent for us.
            bool attestationConsentPromptShown = false;
            (void)result->GetAttestationConsentPromptShown(
                &attestationConsentPromptShown);
            if (attestationConsentPromptShown) {
              self->mActiveTransaction.ref()
                  .parentRegisterPromise.ref()
                  ->Resolve(result);
              self->mActiveTransaction.reset();
              return;
            }
            // If the RP requested attestation and the response contains
            // identifying information, then we need to show a consent
            // prompt.
            bool resultIsIdentifying = true;
            (void)result->HasIdentifyingAttestation(&resultIsIdentifying);
            if (attestationRequested && resultIsIdentifying) {
              self->mActiveTransaction.ref().registerResult = Some(result);
              self->ShowAttestationConsentPrompt(origin, aTransactionId,
                                                 aBrowsingContextId);
              return;
            }
            // In all other cases we strip out identifying information.
            result->Anonymize();
            self->mActiveTransaction.ref().parentRegisterPromise.ref()->Resolve(
                result);
            self->mActiveTransaction.reset();
          })
      ->Track(mActiveTransaction.ref().childRegisterRequest);

  nsresult rv = ActiveService()->MakeCredential(
      aTransactionId, aBrowsingContextId, aArgs, promiseHolder);
  if (NS_FAILED(rv)) {
    promiseHolder->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
  }
  return NS_OK;
}

NS_IMETHODIMP
WebAuthnService::GetAssertion(uint64_t aTransactionId,
                              uint64_t aBrowsingContextId,
                              nsIWebAuthnSignArgs* aArgs,
                              nsIWebAuthnSignPromise* aPromise) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aArgs);
  MOZ_ASSERT(aPromise);

  bool conditionallyMediated;
  (void)aArgs->GetConditionallyMediated(&conditionallyMediated);

  if (conditionallyMediated) {
    // Conditional gets coexist with transactions from other browsing
    // contexts. We hold them here until they are promoted to a modal flow via
    // SelectAutoFillEntry or ResumeConditionalGet, or cancelled.
    mConditionalGets.RemoveElementsBy(
        [aBrowsingContextId](ConditionalGet& aEntry) {
          if (aEntry.browsingContextId != aBrowsingContextId) {
            return false;
          }
          aEntry.signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
          return true;
        });
    mConditionalGets.AppendElement(ConditionalGet{
        .transactionId = aTransactionId,
        .browsingContextId = aBrowsingContextId,
        .signArgs = aArgs,
        .signPromise = aPromise,
    });

    // Defer the observer notification to the next event-loop turn so it fires
    // after this call has fully returned to the caller.
    nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(__func__, []() {
      nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
      if (os) {
        os->NotifyObservers(nullptr, "webauthn:conditional-get-pending",
                            nullptr);
      }
    }));
    NS_DispatchToMainThread(runnable.forget());
    return NS_OK;
  }

  ResetActiveTransaction();
  mActiveTransaction = Some(TransactionState{.service = DefaultService(),
                                             .transactionId = aTransactionId});

#if defined(XP_MACOSX)
  if (__builtin_available(macos 14.5, *)) {
    // This branch is intentionally empty; using `!__builtin_available(...)`
    // results in a compiler warning.
  } else {
    // On macOS < 14.5 the security key API doesn't handle the AppID
    // extension. So we'll use authenticator-rs if it's likely that the
    // request requires AppID. We consider it likely if 1) the AppID
    // extension is present, 2) the allow list is non-empty, and 3) none of
    // the allowed credentials use the "internal" or "hybrid" transport.
    nsString appId;
    nsresult rv = aArgs->GetAppId(appId);
    if (rv == NS_OK) {  // AppID is set
      uint8_t transportSet = 0;
      nsTArray<uint8_t> allowListTransports;
      (void)aArgs->GetAllowListTransports(allowListTransports);
      for (const uint8_t& transport : allowListTransports) {
        transportSet |= transport;
      }
      uint8_t passkeyTransportMask =
          MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL |
          MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID;
      if (allowListTransports.Length() > 0 &&
          (transportSet & passkeyTransportMask) == 0) {
        mActiveTransaction.ref().service = AuthrsService();
      }
    }
  }
#endif

  nsresult rv = ActiveService()->GetAssertion(
      aTransactionId, aBrowsingContextId, aArgs, aPromise);
  if (NS_FAILED(rv)) {
    mActiveTransaction.reset();
    return rv;
  }
  return NS_OK;
}

NS_IMETHODIMP
WebAuthnService::GetIsUVPAA(bool* aAvailable) {
  return DefaultService()->GetIsUVPAA(aAvailable);
}

NS_IMETHODIMP
WebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
                                          const nsAString& aOrigin,
                                          uint64_t* aRv) {
  MOZ_ASSERT(NS_IsMainThread());
  for (const auto& entry : mConditionalGets) {
    if (entry.browsingContextId != aBrowsingContextId) {
      continue;
    }
    nsString entryOrigin;
    (void)entry.signArgs->GetOrigin(entryOrigin);
    if (entryOrigin == aOrigin) {
      *aRv = entry.transactionId;
      return NS_OK;
    }
  }
  *aRv = 0;
  return NS_OK;
}

NS_IMETHODIMP
WebAuthnService::GetAutoFillEntries(
    uint64_t aTransactionId, nsIWebAuthnAutoFillEntriesCallback* aCallback) {
  MOZ_ASSERT(NS_IsMainThread());
  nsCOMPtr<nsIWebAuthnSignArgs> signArgs;
  for (const auto& entry : mConditionalGets) {
    if (entry.transactionId == aTransactionId) {
      signArgs = entry.signArgs;
      break;
    }
  }
  if (!signArgs) {
    aCallback->Reject(NS_ERROR_NOT_AVAILABLE);
    return NS_OK;
  }
  nsString rpId;
  nsTArray<nsTArray<uint8_t>> allowList;
  (void)signArgs->GetRpId(rpId);
  (void)signArgs->GetAllowList(allowList);
  return DefaultService()->GetAutoFillEntriesForRpId(rpId, allowList,
                                                     aCallback);
}

NS_IMETHODIMP
WebAuthnService::GetAutoFillEntriesForRpId(
    const nsAString& aRpId, const nsTArray<nsTArray<uint8_t>>& aAllowList,
    nsIWebAuthnAutoFillEntriesCallback* aCallback) {
  MOZ_ASSERT(NS_IsMainThread());
  return DefaultService()->GetAutoFillEntriesForRpId(aRpId, aAllowList,
                                                     aCallback);
}

Maybe<WebAuthnService::ConditionalGet> WebAuthnService::TakeConditionalByTid(
    uint64_t aTransactionId) {
  MOZ_ASSERT(NS_IsMainThread());
  Maybe<ConditionalGet> found;
  mConditionalGets.RemoveElementsBy([&](ConditionalGet& aEntry) {
    if (aEntry.transactionId != aTransactionId) {
      return false;
    }
    found = Some(std::move(aEntry));
    return true;
  });
  return found;
}

nsresult WebAuthnService::DispatchConditionalGetAssertion(
    const ConditionalGet& aPending, nsIWebAuthnSignArgs* aArgs) {
  MOZ_ASSERT(NS_IsMainThread());
  ResetActiveTransaction();
  mActiveTransaction = Some(TransactionState{
      .service = DefaultService(), .transactionId = aPending.transactionId});
  nsresult rv = ActiveService()->GetAssertion(aPending.transactionId,
                                              aPending.browsingContextId, aArgs,
                                              aPending.signPromise);
  if (NS_FAILED(rv)) {
    aPending.signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
    mActiveTransaction.reset();
    return rv;
  }
  return NS_OK;
}

NS_IMETHODIMP
WebAuthnService::SelectAutoFillEntry(uint64_t aTransactionId,
                                     const nsTArray<uint8_t>& aCredentialId) {
  MOZ_ASSERT(NS_IsMainThread());
  Maybe<ConditionalGet> found = TakeConditionalByTid(aTransactionId);
  if (found.isNothing()) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  // If the original allow list is non-empty, the selected credential must be
  // in it.
  nsTArray<nsTArray<uint8_t>> allowList;
  (void)found.ref().signArgs->GetAllowList(allowList);
  if (!allowList.IsEmpty() && !allowList.Contains(aCredentialId)) {
    found.ref().signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIWebAuthnSignArgs> args;
  nsresult rv = found.ref().signArgs->CloneWithSelectedCredential(
      aCredentialId, getter_AddRefs(args));
  if (NS_FAILED(rv)) {
    found.ref().signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return rv;
  }
  return DispatchConditionalGetAssertion(found.ref(), args);
}

NS_IMETHODIMP
WebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
  MOZ_ASSERT(NS_IsMainThread());
  Maybe<ConditionalGet> found = TakeConditionalByTid(aTransactionId);
  if (found.isNothing()) {
    return NS_ERROR_NOT_AVAILABLE;
  }
  return DispatchConditionalGetAssertion(found.ref(), found.ref().signArgs);
}

void WebAuthnService::RejectActiveRegisterPromise() {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(mActiveTransaction.isSome());
  mActiveTransaction.ref().childRegisterRequest.DisconnectIfExists();
  if (mActiveTransaction.ref().parentRegisterPromise.isSome()) {
    mActiveTransaction.ref().parentRegisterPromise.ref()->Reject(
        NS_ERROR_DOM_NOT_ALLOWED_ERR);
  }
}

void WebAuthnService::ResetActiveTransaction() {
  MOZ_ASSERT(NS_IsMainThread());
  if (mActiveTransaction.isSome()) {
    RejectActiveRegisterPromise();
    ActiveService()->Reset();
    mActiveTransaction.reset();
  }
}

NS_IMETHODIMP
WebAuthnService::Reset() {
  MOZ_ASSERT(NS_IsMainThread());
  ResetActiveTransaction();
  return NS_OK;
}

NS_IMETHODIMP
WebAuthnService::Cancel(uint64_t aTransactionId) {
  MOZ_ASSERT(NS_IsMainThread());
  if (Maybe<ConditionalGet> found = TakeConditionalByTid(aTransactionId);
      found.isSome()) {
    found.ref().signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return NS_OK;
  }

  if (mActiveTransaction.isSome() &&
      mActiveTransaction.ref().transactionId == aTransactionId) {
    RejectActiveRegisterPromise();
    ActiveService()->Cancel(aTransactionId);
    mActiveTransaction.reset();
    return NS_OK;
  }

  // about:webauthn drives an interactive management session through Listen()
  // and RunCommand(), which always run on the authrs service and are not
  // tracked as an mActiveTransaction. Its Cancel() must reach authrs too, or
  // the session's status receiver (a blocking background task) is never torn
  // down and blocks shutdown.
  AuthrsService()->Cancel(aTransactionId);
  return NS_OK;
}

NS_IMETHODIMP
WebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId,
                                          bool aHasConsent) {
  MOZ_ASSERT(NS_IsMainThread());
  if (mActiveTransaction.isNothing() ||
      mActiveTransaction.ref().transactionId != aTransactionId) {
    // This could happen if the transaction was reset just when the prompt was
    // receiving user input.
    return NS_OK;
  }

  MOZ_ASSERT(mActiveTransaction.ref().parentRegisterPromise.isSome());
  MOZ_ASSERT(mActiveTransaction.ref().registerResult.isSome());
  MOZ_ASSERT(!mActiveTransaction.ref().childRegisterRequest.Exists());

  if (!aHasConsent) {
    mActiveTransaction.ref().registerResult.ref()->Anonymize();
  }
  mActiveTransaction.ref().parentRegisterPromise.ref()->Resolve(
      mActiveTransaction.ref().registerResult.ref());

  mActiveTransaction.reset();
  return NS_OK;
}

NS_IMETHODIMP
WebAuthnService::PinCallback(uint64_t aTransactionId, const nsACString& aPin) {
  MOZ_ASSERT(NS_IsMainThread());
  if (mActiveTransaction.isNothing() ||
      mActiveTransaction.ref().transactionId != aTransactionId) {
    return NS_OK;
  }
  return ActiveService()->PinCallback(aTransactionId, aPin);
}

NS_IMETHODIMP
WebAuthnService::SelectionCallback(uint64_t aTransactionId, uint64_t aIndex) {
  MOZ_ASSERT(NS_IsMainThread());
  if (mActiveTransaction.isNothing() ||
      mActiveTransaction.ref().transactionId != aTransactionId) {
    return NS_OK;
  }
  return ActiveService()->SelectionCallback(aTransactionId, aIndex);
}

NS_IMETHODIMP
WebAuthnService::AddVirtualAuthenticator(
    const nsACString& aProtocol, const nsACString& aTransport,
    bool aHasResidentKey, bool aHasUserVerification, bool aIsUserConsenting,
    bool aIsUserVerified, nsACString& aRetval) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->AddVirtualAuthenticator(
      aProtocol, aTransport, aHasResidentKey, aHasUserVerification,
      aIsUserConsenting, aIsUserVerified, aRetval);
}

NS_IMETHODIMP
WebAuthnService::RemoveVirtualAuthenticator(
    const nsACString& aAuthenticatorId) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->RemoveVirtualAuthenticator(aAuthenticatorId);
}

NS_IMETHODIMP
WebAuthnService::AddCredential(const nsACString& aAuthenticatorId,
                               const nsACString& aCredentialId,
                               bool aIsResidentCredential,
                               const nsACString& aRpId,
                               const nsACString& aPrivateKey,
                               const nsACString& aUserHandle,
                               uint32_t aSignCount) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->AddCredential(aAuthenticatorId, aCredentialId,
                                        aIsResidentCredential, aRpId,
                                        aPrivateKey, aUserHandle, aSignCount);
}

NS_IMETHODIMP
WebAuthnService::GetCredentials(
    const nsACString& aAuthenticatorId,
    nsTArray<RefPtr<nsICredentialParameters>>& aRetval) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->GetCredentials(aAuthenticatorId, aRetval);
}

NS_IMETHODIMP
WebAuthnService::RemoveCredential(const nsACString& aAuthenticatorId,
                                  const nsACString& aCredentialId) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->RemoveCredential(aAuthenticatorId, aCredentialId);
}

NS_IMETHODIMP
WebAuthnService::RemoveAllCredentials(const nsACString& aAuthenticatorId) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->RemoveAllCredentials(aAuthenticatorId);
}

NS_IMETHODIMP
WebAuthnService::SetUserVerified(const nsACString& aAuthenticatorId,
                                 bool aIsUserVerified) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->SetUserVerified(aAuthenticatorId, aIsUserVerified);
}

NS_IMETHODIMP
WebAuthnService::Listen() {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->Listen();
}

NS_IMETHODIMP
WebAuthnService::RunCommand(const nsACString& aCmd) {
  MOZ_ASSERT(NS_IsMainThread());
  return AuthrsService()->RunCommand(aCmd);
}

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=94 H=100 G=96

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.