tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

TLSClientAuthCertSelection.cpp (45440B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 *
      3 * This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 // Implements the client authentication certificate selection callback for NSS.
      8 // nsNSSIOLayer.cpp sets the callback by calling SSL_GetClientAuthDataHook and
      9 // identifying SSLGetClientAuthDataHook as the function to call when a TLS
     10 // server requests a client authentication certificate.
     11 //
     12 // In the general case, SSLGetClientAuthDataHook (running on the socket
     13 // thread), does next to nothing. It may return early if it determines it would
     14 // not be suitable to send a client authentication certificate on this
     15 // connection (particularly for speculative connections, which also get
     16 // canceled at this time), but otherwise it notes that a certificate was
     17 // requested and returns an indication that the connection would block to NSS.
     18 //
     19 // When the server certificate verifies successfully, nsSSLIOLayerPoll (running
     20 // on the socket thread) will see that a certificate has been requested on that
     21 // connection, whereupon it calls DoSelectClientAuthCertificate to do the work
     22 // of selecting a certificate. In general, this involves dispatching an event
     23 // to the main thread to ask the user to select a client authentication
     24 // certificate. When the user selects a client certificate (or opts not to send
     25 // one), an event is dispatched to the socket thread that gives NSS the
     26 // appropriate information to proceed with the TLS connection.
     27 //
     28 // If networking is being done on the socket process,
     29 // DoSelectClientAuthCertificate sends an IPC call to the parent process to ask
     30 // the user to select a certificate. When a certificate (or no certificate) has
     31 // been selected, the parent process sends an IPC call back to the socket
     32 // process, which causes an event to be dispatched to the socket thread to
     33 // continue to the TLS connection.
     34 
     35 #include "TLSClientAuthCertSelection.h"
     36 #include "cert_storage/src/cert_storage.h"
     37 #include "mozilla/Logging.h"
     38 #include "mozilla/dom/BrowsingContext.h"
     39 #include "mozilla/dom/CanonicalBrowsingContext.h"
     40 #include "mozilla/glean/SecurityManagerSslMetrics.h"
     41 #include "mozilla/ipc/Endpoint.h"
     42 #include "mozilla/net/DocumentLoadListener.h"
     43 #include "mozilla/net/SocketProcessBackgroundChild.h"
     44 #include "mozilla/psm/SelectTLSClientAuthCertChild.h"
     45 #include "mozilla/psm/SelectTLSClientAuthCertParent.h"
     46 #include "nsArray.h"
     47 #include "nsArrayUtils.h"
     48 #include "nsNSSComponent.h"
     49 #include "nsIClientAuthDialogService.h"
     50 #include "nsIMutableArray.h"
     51 #include "nsINSSComponent.h"
     52 #include "NSSCertDBTrustDomain.h"
     53 #include "nsIClientAuthRememberService.h"
     54 #include "nsIX509CertDB.h"
     55 #include "nsNSSHelper.h"
     56 #include "mozpkix/pkixnss.h"
     57 #include "mozpkix/pkixutil.h"
     58 #include "mozpkix/pkix.h"
     59 #include "secerr.h"
     60 #include "sslerr.h"
     61 
     62 #ifdef MOZ_WIDGET_ANDROID
     63 #  include "mozilla/java/ClientAuthCertificateManagerWrappers.h"
     64 #endif  // MOZ_WIDGET_ANDROID
     65 
     66 using namespace mozilla;
     67 using namespace mozilla::pkix;
     68 using namespace mozilla::psm;
     69 
     70 extern LazyLogModule gPIPNSSLog;
     71 
     72 mozilla::pkix::Result BuildChainForCertificate(
     73    nsTArray<uint8_t>& certBytes, nsTArray<nsTArray<uint8_t>>& certChainBytes,
     74    const nsTArray<nsTArray<uint8_t>>& caNames,
     75    const nsTArray<nsTArray<uint8_t>>& enterpriseCertificates);
     76 
     77 // Possible behaviors for choosing a cert for client auth.
     78 enum class UserCertChoice {
     79  // Ask the user to choose a cert.
     80  Ask = 0,
     81  // Automatically choose a cert.
     82  Auto = 1,
     83 };
     84 
     85 // Returns the most appropriate user cert choice based on the value of the
     86 // security.default_personal_cert preference.
     87 UserCertChoice nsGetUserCertChoice() {
     88  nsAutoCString value;
     89  nsresult rv =
     90      Preferences::GetCString("security.default_personal_cert", value);
     91  if (NS_FAILED(rv)) {
     92    return UserCertChoice::Ask;
     93  }
     94 
     95  // There are three cases for what the preference could be set to:
     96  //   1. "Select Automatically" -> Auto.
     97  //   2. "Ask Every Time" -> Ask.
     98  //   3. Something else -> Ask. This might be a nickname from a migrated cert,
     99  //      but we no longer support this case.
    100  return value.EqualsLiteral("Select Automatically") ? UserCertChoice::Auto
    101                                                     : UserCertChoice::Ask;
    102 }
    103 
    104 static bool hasExplicitKeyUsageNonRepudiation(CERTCertificate* cert) {
    105  // There is no extension, v1 or v2 certificate
    106  if (!cert->extensions) return false;
    107 
    108  SECStatus srv;
    109  SECItem keyUsageItem;
    110  keyUsageItem.data = nullptr;
    111 
    112  srv = CERT_FindKeyUsageExtension(cert, &keyUsageItem);
    113  if (srv == SECFailure) return false;
    114 
    115  unsigned char keyUsage = keyUsageItem.data[0];
    116  PORT_Free(keyUsageItem.data);
    117 
    118  return !!(keyUsage & KU_NON_REPUDIATION);
    119 }
    120 
    121 ClientAuthInfo::ClientAuthInfo(const nsACString& hostName,
    122                               const OriginAttributes& originAttributes,
    123                               int32_t port, uint32_t providerFlags,
    124                               uint32_t providerTlsFlags)
    125    : mHostName(hostName),
    126      mOriginAttributes(originAttributes),
    127      mPort(port),
    128      mProviderFlags(providerFlags),
    129      mProviderTlsFlags(providerTlsFlags) {}
    130 
    131 ClientAuthInfo::ClientAuthInfo(ClientAuthInfo&& aOther) noexcept
    132    : mHostName(std::move(aOther.mHostName)),
    133      mOriginAttributes(std::move(aOther.mOriginAttributes)),
    134      mPort(aOther.mPort),
    135      mProviderFlags(aOther.mProviderFlags),
    136      mProviderTlsFlags(aOther.mProviderTlsFlags) {}
    137 
    138 const nsACString& ClientAuthInfo::HostName() const { return mHostName; }
    139 
    140 const OriginAttributes& ClientAuthInfo::OriginAttributesRef() const {
    141  return mOriginAttributes;
    142 }
    143 
    144 int32_t ClientAuthInfo::Port() const { return mPort; }
    145 
    146 uint32_t ClientAuthInfo::ProviderFlags() const { return mProviderFlags; }
    147 
    148 uint32_t ClientAuthInfo::ProviderTlsFlags() const { return mProviderTlsFlags; }
    149 
    150 nsTArray<nsTArray<uint8_t>> CollectCANames(CERTDistNames* caNames) {
    151  MOZ_ASSERT(caNames);
    152 
    153  nsTArray<nsTArray<uint8_t>> collectedCANames;
    154  if (!caNames) {
    155    return collectedCANames;
    156  }
    157 
    158  for (int i = 0; i < caNames->nnames; i++) {
    159    nsTArray<uint8_t> caName;
    160    caName.AppendElements(caNames->names[i].data, caNames->names[i].len);
    161    collectedCANames.AppendElement(std::move(caName));
    162  }
    163  return collectedCANames;
    164 }
    165 
    166 // This TrustDomain only exists to facilitate the mozilla::pkix path building
    167 // algorithm. It considers any certificate with an issuer distinguished name in
    168 // the set of given CA names to be a trust anchor. It does essentially no
    169 // validation or verification (in particular, the signature checking function
    170 // always returns "Success").
    171 class ClientAuthCertNonverifyingTrustDomain final : public TrustDomain {
    172 public:
    173  ClientAuthCertNonverifyingTrustDomain(
    174      const nsTArray<nsTArray<uint8_t>>& caNames,
    175      const nsTArray<nsTArray<uint8_t>>& thirdPartyCertificates)
    176      : mCANames(caNames),
    177        mCertStorage(do_GetService(NS_CERT_STORAGE_CID)),
    178        mThirdPartyCertificates(thirdPartyCertificates) {}
    179 
    180  virtual mozilla::pkix::Result GetCertTrust(
    181      pkix::EndEntityOrCA endEntityOrCA, const pkix::CertPolicyId& policy,
    182      pkix::Input candidateCertDER,
    183      /*out*/ pkix::TrustLevel& trustLevel) override;
    184  virtual mozilla::pkix::Result FindIssuer(pkix::Input encodedIssuerName,
    185                                           IssuerChecker& checker,
    186                                           pkix::Time time) override;
    187 
    188  virtual mozilla::pkix::Result CheckRevocation(
    189      EndEntityOrCA endEntityOrCA, const pkix::CertID& certID, Time time,
    190      mozilla::pkix::Duration validityDuration,
    191      /*optional*/ const Input* stapledOCSPresponse,
    192      /*optional*/ const Input* aiaExtension) override {
    193    return pkix::Success;
    194  }
    195 
    196  virtual mozilla::pkix::Result IsChainValid(
    197      const pkix::DERArray& certChain, pkix::Time time,
    198      const pkix::CertPolicyId& requiredPolicy) override;
    199 
    200  virtual mozilla::pkix::Result CheckSignatureDigestAlgorithm(
    201      pkix::DigestAlgorithm digestAlg, pkix::EndEntityOrCA endEntityOrCA,
    202      pkix::Time notBefore) override {
    203    return pkix::Success;
    204  }
    205  virtual mozilla::pkix::Result CheckRSAPublicKeyModulusSizeInBits(
    206      pkix::EndEntityOrCA endEntityOrCA,
    207      unsigned int modulusSizeInBits) override {
    208    return pkix::Success;
    209  }
    210  virtual mozilla::pkix::Result VerifyRSAPKCS1SignedData(
    211      pkix::Input data, pkix::DigestAlgorithm, pkix::Input signature,
    212      pkix::Input subjectPublicKeyInfo) override {
    213    return pkix::Success;
    214  }
    215  virtual mozilla::pkix::Result VerifyRSAPSSSignedData(
    216      pkix::Input data, pkix::DigestAlgorithm, pkix::Input signature,
    217      pkix::Input subjectPublicKeyInfo) override {
    218    return pkix::Success;
    219  }
    220  virtual mozilla::pkix::Result CheckECDSACurveIsAcceptable(
    221      pkix::EndEntityOrCA endEntityOrCA, pkix::NamedCurve curve) override {
    222    return pkix::Success;
    223  }
    224  virtual mozilla::pkix::Result VerifyECDSASignedData(
    225      pkix::Input data, pkix::DigestAlgorithm, pkix::Input signature,
    226      pkix::Input subjectPublicKeyInfo) override {
    227    return pkix::Success;
    228  }
    229  virtual mozilla::pkix::Result CheckValidityIsAcceptable(
    230      pkix::Time notBefore, pkix::Time notAfter,
    231      pkix::EndEntityOrCA endEntityOrCA,
    232      pkix::KeyPurposeId keyPurpose) override {
    233    return pkix::Success;
    234  }
    235  virtual void NoteAuxiliaryExtension(pkix::AuxiliaryExtension extension,
    236                                      pkix::Input extensionData) override {}
    237  virtual mozilla::pkix::Result DigestBuf(pkix::Input item,
    238                                          pkix::DigestAlgorithm digestAlg,
    239                                          /*out*/ uint8_t* digestBuf,
    240                                          size_t digestBufLen) override {
    241    return pkix::DigestBufNSS(item, digestAlg, digestBuf, digestBufLen);
    242  }
    243 
    244  nsTArray<nsTArray<uint8_t>> TakeBuiltChain() {
    245    return std::move(mBuiltChain);
    246  }
    247 
    248 private:
    249  const nsTArray<nsTArray<uint8_t>>& mCANames;  // non-owning
    250  nsCOMPtr<nsICertStorage> mCertStorage;
    251  const nsTArray<nsTArray<uint8_t>>& mThirdPartyCertificates;  // non-owning
    252  nsTArray<nsTArray<uint8_t>> mBuiltChain;
    253 };
    254 
    255 mozilla::pkix::Result ClientAuthCertNonverifyingTrustDomain::GetCertTrust(
    256    pkix::EndEntityOrCA endEntityOrCA, const pkix::CertPolicyId& policy,
    257    pkix::Input candidateCertDER,
    258    /*out*/ pkix::TrustLevel& trustLevel) {
    259  // If the server did not specify any CA names, all client certificates are
    260  // acceptable.
    261  if (mCANames.Length() == 0) {
    262    trustLevel = pkix::TrustLevel::TrustAnchor;
    263    return pkix::Success;
    264  }
    265  BackCert cert(candidateCertDER, endEntityOrCA, nullptr);
    266  mozilla::pkix::Result rv = cert.Init();
    267  if (rv != pkix::Success) {
    268    return rv;
    269  }
    270  // If this certificate's issuer distinguished name is in the set of acceptable
    271  // CA names, we say this is a trust anchor so that the client certificate
    272  // issued from this certificate will be presented as an option for the user.
    273  // We also check the certificate's subject distinguished name to account for
    274  // the case where client certificates that have the id-kp-OCSPSigning EKU
    275  // can't be trust anchors according to mozilla::pkix, and thus we may be
    276  // looking directly at the issuer.
    277  pkix::Input issuer(cert.GetIssuer());
    278  pkix::Input subject(cert.GetSubject());
    279  for (const auto& caName : mCANames) {
    280    pkix::Input caNameInput;
    281    rv = caNameInput.Init(caName.Elements(), caName.Length());
    282    if (rv != pkix::Success) {
    283      continue;  // probably too big
    284    }
    285    if (InputsAreEqual(issuer, caNameInput) ||
    286        InputsAreEqual(subject, caNameInput)) {
    287      trustLevel = pkix::TrustLevel::TrustAnchor;
    288      return pkix::Success;
    289    }
    290  }
    291  trustLevel = pkix::TrustLevel::InheritsTrust;
    292  return pkix::Success;
    293 }
    294 
    295 // In theory this implementation should only need to consider intermediate
    296 // certificates, since in theory it should only need to look at the issuer
    297 // distinguished name of each certificate to determine if the client
    298 // certificate is considered acceptable to the server.
    299 // However, because we need to account for client certificates with the
    300 // id-kp-OCSPSigning EKU, and because mozilla::pkix doesn't allow such
    301 // certificates to be trust anchors, we need to consider the issuers of such
    302 // certificates directly. These issuers could be roots, so we have to consider
    303 // roots here.
    304 mozilla::pkix::Result ClientAuthCertNonverifyingTrustDomain::FindIssuer(
    305    pkix::Input encodedIssuerName, IssuerChecker& checker, pkix::Time time) {
    306  // First try all relevant certificates known to Gecko, which avoids calling
    307  // CERT_CreateSubjectCertList, because that can be expensive.
    308  Vector<pkix::Input> geckoCandidates;
    309  if (!mCertStorage) {
    310    return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    311  }
    312  nsTArray<uint8_t> subject;
    313  subject.AppendElements(encodedIssuerName.UnsafeGetData(),
    314                         encodedIssuerName.GetLength());
    315  nsTArray<nsTArray<uint8_t>> certs;
    316  nsresult rv = mCertStorage->FindCertsBySubject(subject, certs);
    317  if (NS_FAILED(rv)) {
    318    return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    319  }
    320  for (auto& cert : certs) {
    321    pkix::Input certDER;
    322    mozilla::pkix::Result rv = certDER.Init(cert.Elements(), cert.Length());
    323    if (rv != pkix::Success) {
    324      continue;  // probably too big
    325    }
    326    if (!geckoCandidates.append(certDER)) {
    327      return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
    328    }
    329  }
    330 
    331  for (const auto& thirdPartyCertificate : mThirdPartyCertificates) {
    332    pkix::Input thirdPartyCertificateInput;
    333    mozilla::pkix::Result rv = thirdPartyCertificateInput.Init(
    334        thirdPartyCertificate.Elements(), thirdPartyCertificate.Length());
    335    if (rv != pkix::Success) {
    336      continue;  // probably too big
    337    }
    338    if (!geckoCandidates.append(thirdPartyCertificateInput)) {
    339      return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
    340    }
    341  }
    342 
    343  bool keepGoing = true;
    344  for (pkix::Input candidate : geckoCandidates) {
    345    mozilla::pkix::Result rv = checker.Check(candidate, nullptr, keepGoing);
    346    if (rv != pkix::Success) {
    347      return rv;
    348    }
    349    if (!keepGoing) {
    350      return pkix::Success;
    351    }
    352  }
    353 
    354  SECItem encodedIssuerNameItem =
    355      pkix::UnsafeMapInputToSECItem(encodedIssuerName);
    356  // NSS seems not to differentiate between "no potential issuers found" and
    357  // "there was an error trying to retrieve the potential issuers." We assume
    358  // there was no error if CERT_CreateSubjectCertList returns nullptr.
    359  UniqueCERTCertList candidates(CERT_CreateSubjectCertList(
    360      nullptr, CERT_GetDefaultCertDB(), &encodedIssuerNameItem, 0, false));
    361  Vector<pkix::Input> nssCandidates;
    362  if (candidates) {
    363    for (CERTCertListNode* n = CERT_LIST_HEAD(candidates);
    364         !CERT_LIST_END(n, candidates); n = CERT_LIST_NEXT(n)) {
    365      pkix::Input certDER;
    366      mozilla::pkix::Result rv =
    367          certDER.Init(n->cert->derCert.data, n->cert->derCert.len);
    368      if (rv != pkix::Success) {
    369        continue;  // probably too big
    370      }
    371      if (!nssCandidates.append(certDER)) {
    372        return mozilla::pkix::Result::FATAL_ERROR_NO_MEMORY;
    373      }
    374    }
    375  }
    376 
    377  for (pkix::Input candidate : nssCandidates) {
    378    mozilla::pkix::Result rv = checker.Check(candidate, nullptr, keepGoing);
    379    if (rv != pkix::Success) {
    380      return rv;
    381    }
    382    if (!keepGoing) {
    383      return pkix::Success;
    384    }
    385  }
    386  return pkix::Success;
    387 }
    388 
    389 mozilla::pkix::Result ClientAuthCertNonverifyingTrustDomain::IsChainValid(
    390    const pkix::DERArray& certArray, pkix::Time, const pkix::CertPolicyId&) {
    391  mBuiltChain.Clear();
    392 
    393  size_t numCerts = certArray.GetLength();
    394  for (size_t i = 0; i < numCerts; ++i) {
    395    nsTArray<uint8_t> certBytes;
    396    const pkix::Input* certInput = certArray.GetDER(i);
    397    MOZ_ASSERT(certInput != nullptr);
    398    if (!certInput) {
    399      return mozilla::pkix::Result::FATAL_ERROR_LIBRARY_FAILURE;
    400    }
    401    certBytes.AppendElements(certInput->UnsafeGetData(),
    402                             certInput->GetLength());
    403    mBuiltChain.AppendElement(std::move(certBytes));
    404  }
    405 
    406  return pkix::Success;
    407 }
    408 
    409 nsTArray<nsTArray<uint8_t>> GetEnterpriseCertificates() {
    410  nsTArray<nsTArray<uint8_t>> enterpriseCertificates;
    411  nsCOMPtr<nsINSSComponent> component(do_GetService(PSM_COMPONENT_CONTRACTID));
    412  if (!component) {
    413    return nsTArray<nsTArray<uint8_t>>{};
    414  }
    415  nsresult rv = component->GetEnterpriseIntermediates(enterpriseCertificates);
    416  if (NS_FAILED(rv)) {
    417    return nsTArray<nsTArray<uint8_t>>{};
    418  }
    419  nsTArray<nsTArray<uint8_t>> enterpriseRoots;
    420  rv = component->GetEnterpriseRoots(enterpriseRoots);
    421  if (NS_FAILED(rv)) {
    422    return nsTArray<nsTArray<uint8_t>>{};
    423  }
    424  enterpriseCertificates.AppendElements(std::move(enterpriseRoots));
    425  return enterpriseCertificates;
    426 }
    427 
    428 bool FindRememberedDecision(
    429    const ClientAuthInfo& clientAuthInfo,
    430    const nsTArray<nsTArray<uint8_t>>& caNames,
    431    const nsTArray<nsTArray<uint8_t>>& enterpriseCertificates,
    432    nsTArray<uint8_t>& rememberedCertBytes,
    433    nsTArray<nsTArray<uint8_t>>& rememberedCertChainBytes) {
    434  rememberedCertBytes.Clear();
    435  rememberedCertChainBytes.Clear();
    436 
    437  if (clientAuthInfo.ProviderTlsFlags() != 0) {
    438    return false;
    439  }
    440 
    441  nsCOMPtr<nsIClientAuthRememberService> clientAuthRememberService(
    442      do_GetService(NS_CLIENTAUTHREMEMBERSERVICE_CONTRACTID));
    443  if (!clientAuthRememberService) {
    444    return false;
    445  }
    446 
    447  nsCString rememberedDBKey;
    448  bool found;
    449  nsresult rv = clientAuthRememberService->HasRememberedDecision(
    450      clientAuthInfo.HostName(), clientAuthInfo.OriginAttributesRef(),
    451      rememberedDBKey, &found);
    452  if (NS_FAILED(rv)) {
    453    return false;
    454  }
    455  if (!found) {
    456    return false;
    457  }
    458  // An empty dbKey indicates that the user chose not to use a certificate
    459  // and chose to remember this decision
    460  if (rememberedDBKey.IsEmpty()) {
    461    return true;
    462  }
    463  nsCOMPtr<nsIX509CertDB> certdb(do_GetService(NS_X509CERTDB_CONTRACTID));
    464  if (!certdb) {
    465    return false;
    466  }
    467  nsCOMPtr<nsIX509Cert> foundCert;
    468  rv = certdb->FindCertByDBKey(rememberedDBKey, getter_AddRefs(foundCert));
    469  if (NS_FAILED(rv)) {
    470    return false;
    471  }
    472  if (!foundCert) {
    473    return false;
    474  }
    475  rv = foundCert->GetRawDER(rememberedCertBytes);
    476  if (NS_FAILED(rv)) {
    477    return false;
    478  }
    479  if (BuildChainForCertificate(rememberedCertBytes, rememberedCertChainBytes,
    480                               caNames, enterpriseCertificates) != Success) {
    481    return false;
    482  }
    483  return true;
    484 }
    485 
    486 // Filter potential client certificates by the specified CA names, if any. This
    487 // operation potentially builds a certificate chain for each candidate client
    488 // certificate. Keeping those chains around means they don't have to be
    489 // re-built later when the user selects a particular client certificate.
    490 void FilterPotentialClientCertificatesByCANames(
    491    UniqueCERTCertList& potentialClientCertificates,
    492    const nsTArray<nsTArray<uint8_t>>& caNames,
    493    const nsTArray<nsTArray<uint8_t>>& enterpriseCertificates,
    494    nsTArray<nsTArray<nsTArray<uint8_t>>>& potentialClientCertificateChains) {
    495  if (!potentialClientCertificates) {
    496    return;
    497  }
    498 
    499  CERTCertListNode* n = CERT_LIST_HEAD(potentialClientCertificates);
    500  while (!CERT_LIST_END(n, potentialClientCertificates)) {
    501    nsTArray<nsTArray<uint8_t>> builtChain;
    502    nsTArray<uint8_t> certBytes;
    503    certBytes.AppendElements(n->cert->derCert.data, n->cert->derCert.len);
    504    mozilla::pkix::Result result = BuildChainForCertificate(
    505        certBytes, builtChain, caNames, enterpriseCertificates);
    506    if (result != pkix::Success) {
    507      MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
    508              ("removing cert '%s'", n->cert->subjectName));
    509      CERTCertListNode* toRemove = n;
    510      n = CERT_LIST_NEXT(n);
    511      CERT_RemoveCertListNode(toRemove);
    512      continue;
    513    }
    514    potentialClientCertificateChains.AppendElement(std::move(builtChain));
    515    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
    516            ("keeping cert '%s'\n", n->cert->subjectName));
    517    n = CERT_LIST_NEXT(n);
    518  }
    519 }
    520 
    521 void ClientAuthCertificateSelectedBase::SetSelectedClientAuthData(
    522    nsTArray<uint8_t>&& selectedCertBytes,
    523    nsTArray<nsTArray<uint8_t>>&& selectedCertChainBytes) {
    524  mSelectedCertBytes = std::move(selectedCertBytes);
    525  mSelectedCertChainBytes = std::move(selectedCertChainBytes);
    526 }
    527 
    528 NS_IMETHODIMP
    529 ClientAuthCertificateSelected::Run() {
    530  mSocketInfo->ClientAuthCertificateSelected(mSelectedCertBytes,
    531                                             mSelectedCertChainBytes);
    532  return NS_OK;
    533 }
    534 
    535 void SelectClientAuthCertificate::DispatchContinuation(
    536    nsTArray<uint8_t>&& selectedCertBytes) {
    537  nsTArray<nsTArray<uint8_t>> selectedCertChainBytes;
    538  // Attempt to find a pre-built certificate chain corresponding to the
    539  // selected certificate.
    540  // On Android, there are no pre-built certificate chains, so use what the OS
    541  // says is the issuer certificate chain.
    542 #ifdef MOZ_WIDGET_ANDROID
    543  if (jni::IsAvailable()) {
    544    jni::ByteArray::LocalRef certBytes = jni::ByteArray::New(
    545        reinterpret_cast<const int8_t*>(selectedCertBytes.Elements()),
    546        selectedCertBytes.Length());
    547    jni::ObjectArray::LocalRef issuersBytes =
    548        java::ClientAuthCertificateManager::GetCertificateIssuersBytes(
    549            certBytes);
    550    if (issuersBytes) {
    551      for (size_t i = 0; i < issuersBytes->Length(); i++) {
    552        jni::ByteArray::LocalRef issuer = issuersBytes->GetElement(i);
    553        nsTArray<uint8_t> issuerBytes(
    554            reinterpret_cast<uint8_t*>(issuer->GetElements().Elements()),
    555            issuer->Length());
    556        selectedCertChainBytes.AppendElement(std::move(issuerBytes));
    557      }
    558    }
    559  }
    560 #else
    561  for (const auto& clientCertificateChain : mPotentialClientCertificateChains) {
    562    if (clientCertificateChain.Length() > 0 &&
    563        clientCertificateChain[0] == selectedCertBytes) {
    564      for (const auto& certificateBytes : clientCertificateChain) {
    565        selectedCertChainBytes.AppendElement(certificateBytes.Clone());
    566      }
    567      break;
    568    }
    569  }
    570 #endif  // MOZ_WIDGET_ANDROID
    571  mContinuation->SetSelectedClientAuthData(std::move(selectedCertBytes),
    572                                           std::move(selectedCertChainBytes));
    573  nsCOMPtr<nsIEventTarget> socketThread(
    574      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID));
    575  if (socketThread) {
    576    (void)socketThread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
    577  }
    578 }
    579 
    580 // Helper function to build a certificate chain from the given certificate to a
    581 // trust anchor in the set indicated by the peer (mCANames). This is essentially
    582 // best-effort, so no signature verification occurs.
    583 mozilla::pkix::Result BuildChainForCertificate(
    584    nsTArray<uint8_t>& certBytes, nsTArray<nsTArray<uint8_t>>& certChainBytes,
    585    const nsTArray<nsTArray<uint8_t>>& caNames,
    586    const nsTArray<nsTArray<uint8_t>>& enterpriseCertificates) {
    587  ClientAuthCertNonverifyingTrustDomain trustDomain(caNames,
    588                                                    enterpriseCertificates);
    589  pkix::Input certDER;
    590  mozilla::pkix::Result result =
    591      certDER.Init(certBytes.Elements(), certBytes.Length());
    592  if (result != pkix::Success) {
    593    return result;
    594  }
    595  // Client certificates shouldn't be CAs, but for interoperability reasons we
    596  // attempt to build a path with each certificate as an end entity and then as
    597  // a CA if that fails.
    598  const pkix::EndEntityOrCA kEndEntityOrCAParams[] = {
    599      pkix::EndEntityOrCA::MustBeEndEntity, pkix::EndEntityOrCA::MustBeCA};
    600  // mozilla::pkix rejects certificates with id-kp-OCSPSigning unless it is
    601  // specifically required. A client certificate should never have this EKU.
    602  // Unfortunately, there are some client certificates in private PKIs that
    603  // have this EKU. For interoperability, we attempt to work around this
    604  // restriction in mozilla::pkix by first building the certificate chain with
    605  // no particular EKU required and then again with id-kp-OCSPSigning required
    606  // if that fails.
    607  const pkix::KeyPurposeId kKeyPurposeIdParams[] = {
    608      pkix::KeyPurposeId::anyExtendedKeyUsage,
    609      pkix::KeyPurposeId::id_kp_OCSPSigning};
    610  for (const auto& endEntityOrCAParam : kEndEntityOrCAParams) {
    611    for (const auto& keyPurposeIdParam : kKeyPurposeIdParams) {
    612      mozilla::pkix::Result result = BuildCertChain(
    613          trustDomain, certDER, Now(), endEntityOrCAParam,
    614          KeyUsage::noParticularKeyUsageRequired, keyPurposeIdParam,
    615          pkix::CertPolicyId::anyPolicy, nullptr);
    616      if (result == pkix::Success) {
    617        certChainBytes = trustDomain.TakeBuiltChain();
    618        return pkix::Success;
    619      }
    620    }
    621  }
    622  return mozilla::pkix::Result::ERROR_UNKNOWN_ISSUER;
    623 }
    624 
    625 class ClientAuthDialogCallback : public nsIClientAuthDialogCallback {
    626 public:
    627  NS_DECL_ISUPPORTS
    628  NS_DECL_NSICLIENTAUTHDIALOGCALLBACK
    629 
    630  explicit ClientAuthDialogCallback(
    631      SelectClientAuthCertificate* selectClientAuthCertificate)
    632      : mSelectClientAuthCertificate(selectClientAuthCertificate) {}
    633 
    634 private:
    635  virtual ~ClientAuthDialogCallback() = default;
    636 
    637  RefPtr<SelectClientAuthCertificate> mSelectClientAuthCertificate;
    638 };
    639 
    640 NS_IMPL_ISUPPORTS(ClientAuthDialogCallback, nsIClientAuthDialogCallback)
    641 
    642 NS_IMETHODIMP
    643 ClientAuthDialogCallback::CertificateChosen(
    644    nsIX509Cert* cert,
    645    nsIClientAuthRememberService::Duration rememberDuration) {
    646  MOZ_ASSERT(mSelectClientAuthCertificate);
    647  if (!mSelectClientAuthCertificate) {
    648    return NS_ERROR_FAILURE;
    649  }
    650  const ClientAuthInfo& info = mSelectClientAuthCertificate->Info();
    651  nsCOMPtr<nsIClientAuthRememberService> clientAuthRememberService(
    652      do_GetService(NS_CLIENTAUTHREMEMBERSERVICE_CONTRACTID));
    653  if (info.ProviderTlsFlags() == 0 && clientAuthRememberService) {
    654    (void)clientAuthRememberService->RememberDecision(
    655        info.HostName(), info.OriginAttributesRef(), cert, rememberDuration);
    656  }
    657  nsTArray<uint8_t> selectedCertBytes;
    658  if (cert) {
    659    nsresult rv = cert->GetRawDER(selectedCertBytes);
    660    if (NS_FAILED(rv)) {
    661      selectedCertBytes.Clear();
    662      mSelectClientAuthCertificate->DispatchContinuation(
    663          std::move(selectedCertBytes));
    664      return rv;
    665    }
    666  }
    667  mSelectClientAuthCertificate->DispatchContinuation(
    668      std::move(selectedCertBytes));
    669  return NS_OK;
    670 }
    671 
    672 NS_IMETHODIMP
    673 SelectClientAuthCertificate::Run() {
    674  // We check the value of a pref, so this should only be run on the main
    675  // thread.
    676  MOZ_ASSERT(NS_IsMainThread());
    677 
    678  nsTArray<uint8_t> selectedCertBytes;
    679 
    680  // find valid user cert and key pair
    681  if (nsGetUserCertChoice() == UserCertChoice::Auto) {
    682    // automatically find the right cert
    683    UniqueCERTCertificate lowPrioNonrepCert;
    684    // loop through the list until we find a cert with a key
    685    for (CERTCertListNode* node = CERT_LIST_HEAD(mPotentialClientCertificates);
    686         !CERT_LIST_END(node, mPotentialClientCertificates);
    687         node = CERT_LIST_NEXT(node)) {
    688      UniqueSECKEYPrivateKey tmpKey(PK11_FindKeyByAnyCert(node->cert, nullptr));
    689      if (tmpKey) {
    690        if (hasExplicitKeyUsageNonRepudiation(node->cert)) {
    691          // Not a preferred cert
    692          if (!lowPrioNonrepCert) {  // did not yet find a low prio cert
    693            lowPrioNonrepCert.reset(CERT_DupCertificate(node->cert));
    694          }
    695        } else {
    696          // this is a good cert to present
    697          selectedCertBytes.AppendElements(node->cert->derCert.data,
    698                                           node->cert->derCert.len);
    699          DispatchContinuation(std::move(selectedCertBytes));
    700          return NS_OK;
    701        }
    702      }
    703      if (PR_GetError() == SEC_ERROR_BAD_PASSWORD) {
    704        // problem with password: bail
    705        break;
    706      }
    707    }
    708 
    709    if (lowPrioNonrepCert) {
    710      selectedCertBytes.AppendElements(lowPrioNonrepCert->derCert.data,
    711                                       lowPrioNonrepCert->derCert.len);
    712    }
    713    DispatchContinuation(std::move(selectedCertBytes));
    714    return NS_OK;
    715  }
    716 
    717  // Not Auto => ask the user to select a certificate
    718  nsTArray<RefPtr<nsIX509Cert>> certArray;
    719  for (CERTCertListNode* node = CERT_LIST_HEAD(mPotentialClientCertificates);
    720       !CERT_LIST_END(node, mPotentialClientCertificates);
    721       node = CERT_LIST_NEXT(node)) {
    722    RefPtr<nsIX509Cert> tempCert(new nsNSSCertificate(node->cert));
    723    certArray.AppendElement(tempCert);
    724  }
    725 
    726  nsCOMPtr<nsIClientAuthDialogService> clientAuthDialogService(
    727      do_GetService(NS_CLIENTAUTHDIALOGSERVICE_CONTRACTID));
    728  if (!clientAuthDialogService) {
    729    DispatchContinuation(std::move(selectedCertBytes));
    730    return NS_ERROR_FAILURE;
    731  }
    732 
    733  RefPtr<mozilla::dom::BrowsingContext> browsingContext;
    734  if (mBrowserId) {
    735    browsingContext =
    736        mozilla::dom::BrowsingContext::GetCurrentTopByBrowserId(mBrowserId);
    737  }
    738 
    739  // Prevent HTTPS-Only/-First from downgrading the load in the browsing context
    740  // while the dialog is open by setting HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS
    741  // early. Otherwise this would only get set once
    742  // DocumentLoadListener::DoOnStartRequest is reached.
    743  if (browsingContext) {
    744    RefPtr<net::DocumentLoadListener> loadListener =
    745        browsingContext->Canonical()->GetCurrentLoad();
    746    if (loadListener) {
    747      nsCOMPtr<nsIHttpChannel> channel =
    748          do_QueryInterface(loadListener->GetChannel());
    749      if (channel) {
    750        nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo();
    751        uint32_t httpsOnlyStatus = loadInfo->GetHttpsOnlyStatus();
    752        httpsOnlyStatus |= nsILoadInfo::HTTPS_ONLY_TOP_LEVEL_LOAD_IN_PROGRESS;
    753        loadInfo->SetHttpsOnlyStatus(httpsOnlyStatus);
    754      }
    755    }
    756  }
    757 
    758  RefPtr<nsIClientAuthDialogCallback> callback(
    759      new ClientAuthDialogCallback(this));
    760  nsresult rv = clientAuthDialogService->ChooseCertificate(
    761      mInfo.HostName(), certArray, browsingContext, mCANames, callback);
    762  if (NS_FAILED(rv)) {
    763    DispatchContinuation(std::move(selectedCertBytes));
    764    return rv;
    765  }
    766  return NS_OK;
    767 }
    768 
    769 SECStatus SSLGetClientAuthDataHook(void* arg, PRFileDesc* socket,
    770                                   CERTDistNames* caNamesDecoded,
    771                                   CERTCertificate** pRetCert,
    772                                   SECKEYPrivateKey** pRetKey) {
    773  MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
    774          ("[%p][%p] SSLGetClientAuthDataHook", socket, arg));
    775 
    776  if (!arg || !socket || !caNamesDecoded || !pRetCert || !pRetKey) {
    777    PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
    778    return SECFailure;
    779  }
    780 
    781  *pRetCert = nullptr;
    782  *pRetKey = nullptr;
    783 
    784  RefPtr<NSSSocketControl> info(static_cast<NSSSocketControl*>(arg));
    785  glean::security::client_auth_cert_usage.Get("requested"_ns).Add(1);
    786 
    787  if (info->GetDenyClientCert()) {
    788    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
    789            ("[%p] Not returning client cert due to denyClientCert attribute",
    790             socket));
    791    return SECSuccess;
    792  }
    793 
    794  if (info->GetJoined()) {
    795    // We refuse to send a client certificate when there are multiple hostnames
    796    // joined on this connection, because we only show the user one hostname
    797    // (mHostName) in the client certificate UI.
    798    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
    799            ("[%p] Not returning client cert due to previous join", socket));
    800    return SECSuccess;
    801  }
    802 
    803  // If the connection corresponding to this socket hasn't been claimed, it is
    804  // a speculative connection. The connection will block until the "choose a
    805  // client auth certificate" dialog has been shown. The dialog will only be
    806  // shown when this connection gets claimed. However, necko will never claim
    807  // the connection as long as it is blocking. Thus, this connection can't
    808  // proceed, so it's best to cancel it. Necko will create a new,
    809  // non-speculative connection instead.
    810  if (info->CancelIfNotClaimed()) {
    811    MOZ_LOG(
    812        gPIPNSSLog, LogLevel::Debug,
    813        ("[%p] Cancelling unclaimed connection with client certificate request",
    814         socket));
    815    return SECSuccess;
    816  }
    817 
    818  UniqueCERTCertificate serverCert(SSL_PeerCertificate(socket));
    819  if (!serverCert) {
    820    PR_SetError(SSL_ERROR_NO_CERTIFICATE, 0);
    821    return SECFailure;
    822  }
    823 
    824  nsTArray<nsTArray<uint8_t>> caNames(CollectCANames(caNamesDecoded));
    825  info->SetClientAuthCertificateRequest(std::move(serverCert),
    826                                        std::move(caNames));
    827  PR_SetError(PR_WOULD_BLOCK_ERROR, 0);
    828  return SECWouldBlock;
    829 }
    830 
    831 void DoSelectClientAuthCertificate(NSSSocketControl* info,
    832                                   UniqueCERTCertificate&& serverCert,
    833                                   nsTArray<nsTArray<uint8_t>>&& caNames) {
    834  MOZ_ASSERT(info);
    835  uint64_t browserId;
    836  if (NS_FAILED(info->GetBrowserId(&browserId))) {
    837    info->SetCanceled(SEC_ERROR_LIBRARY_FAILURE);
    838    return;
    839  }
    840 
    841  RefPtr<ClientAuthCertificateSelected> continuation(
    842      new ClientAuthCertificateSelected(info));
    843  // If this is the socket process, dispatch an IPC call to select a client
    844  // authentication certificate in the parent process.
    845  // Otherwise, dispatch an event to the main thread to do the selection.
    846  // When those events finish, they will run the continuation, which gives the
    847  // appropriate information to the NSSSocketControl, which then calls
    848  // SSL_ClientCertCallbackComplete to continue the connection.
    849  if (XRE_IsSocketProcess()) {
    850    RefPtr<SelectTLSClientAuthCertChild> selectClientAuthCertificate(
    851        new SelectTLSClientAuthCertChild(continuation));
    852    nsAutoCString hostname(info->GetHostName());
    853    nsTArray<uint8_t> serverCertBytes;
    854    nsTArray<ByteArray> caNamesBytes;
    855    for (const auto& caName : caNames) {
    856      caNamesBytes.AppendElement(ByteArray(std::move(caName)));
    857    }
    858    serverCertBytes.AppendElements(serverCert->derCert.data,
    859                                   serverCert->derCert.len);
    860    OriginAttributes originAttributes(info->GetOriginAttributes());
    861    int32_t port(info->GetPort());
    862    uint32_t providerFlags(info->GetProviderFlags());
    863    uint32_t providerTlsFlags(info->GetProviderTlsFlags());
    864    nsCOMPtr<nsIRunnable> remoteSelectClientAuthCertificate(
    865        NS_NewRunnableFunction(
    866            "RemoteSelectClientAuthCertificate",
    867            [selectClientAuthCertificate(
    868                 std::move(selectClientAuthCertificate)),
    869             hostname(std::move(hostname)),
    870             originAttributes(std::move(originAttributes)), port, providerFlags,
    871             providerTlsFlags, serverCertBytes(std::move(serverCertBytes)),
    872             caNamesBytes(std::move(caNamesBytes)),
    873             browserId(browserId)]() mutable {
    874              ipc::Endpoint<PSelectTLSClientAuthCertParent> parentEndpoint;
    875              ipc::Endpoint<PSelectTLSClientAuthCertChild> childEndpoint;
    876              PSelectTLSClientAuthCert::CreateEndpoints(&parentEndpoint,
    877                                                        &childEndpoint);
    878              if (NS_FAILED(net::SocketProcessBackgroundChild::WithActor(
    879                      "SendInitSelectTLSClientAuthCert",
    880                      [endpoint = std::move(parentEndpoint),
    881                       hostname(std::move(hostname)),
    882                       originAttributes(std::move(originAttributes)), port,
    883                       providerFlags, providerTlsFlags,
    884                       serverCertBytes(std::move(serverCertBytes)),
    885                       caNamesBytes(std::move(caNamesBytes)), browserId](
    886                          net::SocketProcessBackgroundChild* aActor) mutable {
    887                        (void)aActor->SendInitSelectTLSClientAuthCert(
    888                            std::move(endpoint), hostname, originAttributes,
    889                            port, providerFlags, providerTlsFlags,
    890                            ByteArray(serverCertBytes), caNamesBytes,
    891                            browserId);
    892                      }))) {
    893                return;
    894              }
    895 
    896              if (!childEndpoint.Bind(selectClientAuthCertificate)) {
    897                return;
    898              }
    899            }));
    900    (void)NS_DispatchToMainThread(remoteSelectClientAuthCertificate);
    901    return;
    902  }
    903 
    904  ClientAuthInfo authInfo(info->GetHostName(), info->GetOriginAttributes(),
    905                          info->GetPort(), info->GetProviderFlags(),
    906                          info->GetProviderTlsFlags());
    907  nsTArray<nsTArray<uint8_t>> enterpriseCertificates(
    908      GetEnterpriseCertificates());
    909  nsTArray<uint8_t> rememberedCertBytes;
    910  nsTArray<nsTArray<uint8_t>> rememberedCertChainBytes;
    911  if (FindRememberedDecision(authInfo, caNames, enterpriseCertificates,
    912                             rememberedCertBytes, rememberedCertChainBytes)) {
    913    continuation->SetSelectedClientAuthData(
    914        std::move(rememberedCertBytes), std::move(rememberedCertChainBytes));
    915    (void)NS_DispatchToCurrentThread(continuation);
    916    return;
    917  }
    918 
    919  // Instantiating certificates in NSS is not thread-safe and has performance
    920  // implications, so search for them here (on the socket thread).
    921  UniqueCERTCertList potentialClientCertificates(
    922      FindClientCertificatesWithPrivateKeys());
    923  if (!potentialClientCertificates) {
    924    MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
    925            ("[%p] FindClientCertificatesWithPrivateKeys() returned null (out "
    926             "of memory?)",
    927             &info));
    928    info->SetCanceled(SEC_ERROR_LIBRARY_FAILURE);
    929    return;
    930  }
    931 
    932  nsTArray<nsTArray<nsTArray<uint8_t>>> potentialClientCertificateChains;
    933 
    934  // On Android, gathering potential client certificates and filtering them by
    935  // issuer is handled by the OS, so `potentialClientCertificates` is expected
    936  // to be empty here.
    937 #ifndef MOZ_WIDGET_ANDROID
    938  FilterPotentialClientCertificatesByCANames(potentialClientCertificates,
    939                                             caNames, enterpriseCertificates,
    940                                             potentialClientCertificateChains);
    941  if (CERT_LIST_EMPTY(potentialClientCertificates)) {
    942    MOZ_LOG(
    943        gPIPNSSLog, LogLevel::Debug,
    944        ("[%p] no client certificates available after filtering by CA", &info));
    945    // By default, the continuation will continue the connection with no client
    946    // auth certificate.
    947    (void)NS_DispatchToCurrentThread(continuation);
    948    return;
    949  }
    950 #endif  // MOZ_WIDGET_ANDROID
    951 
    952  nsCOMPtr<nsIRunnable> selectClientAuthCertificate(
    953      new SelectClientAuthCertificate(
    954          std::move(authInfo), std::move(serverCert),
    955          std::move(potentialClientCertificates),
    956          std::move(potentialClientCertificateChains), std::move(caNames),
    957          continuation, browserId));
    958  (void)NS_DispatchToMainThread(selectClientAuthCertificate);
    959 }
    960 
    961 // Helper continuation for when a client authentication certificate has been
    962 // selected in the parent process and the information needs to be sent to the
    963 // socket process.
    964 class RemoteClientAuthCertificateSelected
    965    : public ClientAuthCertificateSelectedBase {
    966 public:
    967  explicit RemoteClientAuthCertificateSelected(
    968      SelectTLSClientAuthCertParent* selectTLSClientAuthCertParent)
    969      : mSelectTLSClientAuthCertParent(selectTLSClientAuthCertParent),
    970        mEventTarget(GetCurrentSerialEventTarget()) {}
    971 
    972  NS_IMETHOD Run() override;
    973 
    974 private:
    975  RefPtr<SelectTLSClientAuthCertParent> mSelectTLSClientAuthCertParent;
    976  nsCOMPtr<nsISerialEventTarget> mEventTarget;
    977 };
    978 
    979 NS_IMETHODIMP
    980 RemoteClientAuthCertificateSelected::Run() {
    981  // When this runs, it dispatches an event to the IPC thread it originally came
    982  // from in order to send the IPC call to the socket process that a client
    983  // authentication certificate has been selected.
    984  return mEventTarget->Dispatch(
    985      NS_NewRunnableFunction(
    986          "psm::RemoteClientAuthCertificateSelected::Run",
    987          [parent(mSelectTLSClientAuthCertParent),
    988           certBytes(std::move(mSelectedCertBytes)),
    989           builtCertChain(std::move(mSelectedCertChainBytes))]() mutable {
    990            parent->TLSClientAuthCertSelected(certBytes,
    991                                              std::move(builtCertChain));
    992          }),
    993      NS_DISPATCH_NORMAL);
    994 }
    995 
    996 namespace mozilla::psm {
    997 
    998 // Given some information from the socket process about a connection that
    999 // requested a client authentication certificate, this function dispatches an
   1000 // event to the main thread to ask the user to select one. When the user does so
   1001 // (or selects no certificate), the continuation runs and sends the information
   1002 // back via IPC.
   1003 bool SelectTLSClientAuthCertParent::Dispatch(
   1004    const nsACString& aHostName, const OriginAttributes& aOriginAttributes,
   1005    const int32_t& aPort, const uint32_t& aProviderFlags,
   1006    const uint32_t& aProviderTlsFlags, const ByteArray& aServerCertBytes,
   1007    nsTArray<ByteArray>&& aCANames, const uint64_t& aBrowserId) {
   1008  RefPtr<ClientAuthCertificateSelectedBase> continuation(
   1009      new RemoteClientAuthCertificateSelected(this));
   1010  ClientAuthInfo authInfo(aHostName, aOriginAttributes, aPort, aProviderFlags,
   1011                          aProviderTlsFlags);
   1012  nsCOMPtr<nsIEventTarget> socketThread =
   1013      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
   1014  if (NS_WARN_IF(!socketThread)) {
   1015    return false;
   1016  }
   1017  // Dispatch the work of instantiating a CERTCertificate and searching for
   1018  // client certificates to the socket thread.
   1019  nsresult rv = socketThread->Dispatch(NS_NewRunnableFunction(
   1020      "SelectTLSClientAuthCertParent::Dispatch",
   1021      [authInfo(std::move(authInfo)), continuation(std::move(continuation)),
   1022       serverCertBytes(aServerCertBytes), caNames(std::move(aCANames)),
   1023       browserId(aBrowserId)]() mutable {
   1024        SECItem serverCertItem{
   1025            siBuffer,
   1026            const_cast<uint8_t*>(serverCertBytes.data().Elements()),
   1027            static_cast<unsigned int>(serverCertBytes.data().Length()),
   1028        };
   1029        UniqueCERTCertificate serverCert(CERT_NewTempCertificate(
   1030            CERT_GetDefaultCertDB(), &serverCertItem, nullptr, false, true));
   1031        if (!serverCert) {
   1032          return;
   1033        }
   1034        nsTArray<nsTArray<uint8_t>> caNamesArray;
   1035        for (auto& caName : caNames) {
   1036          caNamesArray.AppendElement(std::move(caName.data()));
   1037        }
   1038        nsTArray<nsTArray<uint8_t>> enterpriseCertificates(
   1039            GetEnterpriseCertificates());
   1040        nsTArray<uint8_t> rememberedCertBytes;
   1041        nsTArray<nsTArray<uint8_t>> rememberedCertChainBytes;
   1042        if (FindRememberedDecision(authInfo, caNamesArray,
   1043                                   enterpriseCertificates, rememberedCertBytes,
   1044                                   rememberedCertChainBytes)) {
   1045          continuation->SetSelectedClientAuthData(
   1046              std::move(rememberedCertBytes),
   1047              std::move(rememberedCertChainBytes));
   1048          (void)NS_DispatchToCurrentThread(continuation);
   1049          return;
   1050        }
   1051        UniqueCERTCertList potentialClientCertificates(
   1052            FindClientCertificatesWithPrivateKeys());
   1053        nsTArray<nsTArray<nsTArray<uint8_t>>> potentialClientCertificateChains;
   1054        FilterPotentialClientCertificatesByCANames(
   1055            potentialClientCertificates, caNamesArray, enterpriseCertificates,
   1056            potentialClientCertificateChains);
   1057        RefPtr<SelectClientAuthCertificate> selectClientAuthCertificate(
   1058            new SelectClientAuthCertificate(
   1059                std::move(authInfo), std::move(serverCert),
   1060                std::move(potentialClientCertificates),
   1061                std::move(potentialClientCertificateChains),
   1062                std::move(caNamesArray), continuation, browserId));
   1063        (void)NS_DispatchToMainThread(selectClientAuthCertificate);
   1064      }));
   1065  return NS_SUCCEEDED(rv);
   1066 }
   1067 
   1068 void SelectTLSClientAuthCertParent::TLSClientAuthCertSelected(
   1069    const nsTArray<uint8_t>& aSelectedCertBytes,
   1070    nsTArray<nsTArray<uint8_t>>&& aSelectedCertChainBytes) {
   1071  if (!CanSend()) {
   1072    return;
   1073  }
   1074 
   1075  nsTArray<ByteArray> selectedCertChainBytes;
   1076  for (auto& certBytes : aSelectedCertChainBytes) {
   1077    selectedCertChainBytes.AppendElement(ByteArray(certBytes));
   1078  }
   1079 
   1080  (void)SendTLSClientAuthCertSelected(aSelectedCertBytes,
   1081                                      selectedCertChainBytes);
   1082  Close();
   1083 }
   1084 
   1085 void SelectTLSClientAuthCertParent::ActorDestroy(
   1086    mozilla::ipc::IProtocol::ActorDestroyReason aWhy) {}
   1087 
   1088 SelectTLSClientAuthCertChild::SelectTLSClientAuthCertChild(
   1089    ClientAuthCertificateSelected* continuation)
   1090    : mContinuation(continuation) {}
   1091 
   1092 // When the user has selected (or not) a client authentication certificate in
   1093 // the parent, this function receives that information in the socket process and
   1094 // dispatches a continuation to the socket process to continue the connection.
   1095 ipc::IPCResult SelectTLSClientAuthCertChild::RecvTLSClientAuthCertSelected(
   1096    ByteArray&& aSelectedCertBytes,
   1097    nsTArray<ByteArray>&& aSelectedCertChainBytes) {
   1098  nsTArray<uint8_t> selectedCertBytes(std::move(aSelectedCertBytes.data()));
   1099  nsTArray<nsTArray<uint8_t>> selectedCertChainBytes;
   1100  for (auto& certBytes : aSelectedCertChainBytes) {
   1101    selectedCertChainBytes.AppendElement(std::move(certBytes.data()));
   1102  }
   1103  mContinuation->SetSelectedClientAuthData(std::move(selectedCertBytes),
   1104                                           std::move(selectedCertChainBytes));
   1105 
   1106  nsCOMPtr<nsIEventTarget> socketThread =
   1107      do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
   1108  if (NS_WARN_IF(!socketThread)) {
   1109    return IPC_OK();
   1110  }
   1111  nsresult rv = socketThread->Dispatch(mContinuation, NS_DISPATCH_NORMAL);
   1112  (void)NS_WARN_IF(NS_FAILED(rv));
   1113 
   1114  return IPC_OK();
   1115 }
   1116 
   1117 }  // namespace mozilla::psm