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