CommonSocketControl.cpp (16128B)
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 #include "CommonSocketControl.h" 8 9 #include "PublicKeyPinningService.h" 10 #include "SharedCertVerifier.h" 11 #include "mozilla/ErrorResult.h" 12 #include "mozilla/StaticPrefs_network.h" 13 #include "mozilla/dom/Promise.h" 14 #include "nsICertOverrideService.h" 15 #include "nsISocketProvider.h" 16 #include "nsITlsHandshakeListener.h" 17 #include "nsNSSCertificate.h" 18 #include "nsNSSComponent.h" 19 #include "nsNSSHelper.h" 20 #include "secerr.h" 21 #include "ssl.h" 22 #include "sslt.h" 23 24 using namespace mozilla; 25 26 extern LazyLogModule gPIPNSSLog; 27 28 NS_IMPL_ISUPPORTS(CommonSocketControl, nsITLSSocketControl) 29 30 CommonSocketControl::CommonSocketControl(const nsCString& aHostName, 31 int32_t aPort, uint32_t aProviderFlags) 32 : mHostName(aHostName), 33 mPort(aPort), 34 mCanceled(false), 35 mHandshakeCompleted(false), 36 mJoined(false), 37 mSentClientCert(false), 38 mFailedVerification(false), 39 mSSLVersionUsed(nsITLSSocketControl::SSL_VERSION_UNKNOWN), 40 mProviderFlags(aProviderFlags), 41 mSecurityState(0), 42 mErrorCode(0), 43 mServerCert(nullptr), 44 mCertificateTransparencyStatus(0), 45 mMadeOCSPRequests(false), 46 mUsedPrivateDNS(aProviderFlags & nsISocketProvider::USED_PRIVATE_DNS), 47 mNPNCompleted(false), 48 mResumed(false), 49 mIsBuiltCertChainRootBuiltInRoot(false) { 50 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) 51 mOwningThread = PR_GetCurrentThread(); 52 #endif 53 } 54 55 void CommonSocketControl::SetStatusErrorBits( 56 nsITransportSecurityInfo::OverridableErrorCategory 57 overridableErrorCategory) { 58 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 59 mOverridableErrorCategory = Some(overridableErrorCategory); 60 } 61 62 static void CreateCertChain(nsTArray<RefPtr<nsIX509Cert>>& aOutput, 63 nsTArray<nsTArray<uint8_t>>&& aCertList) { 64 nsTArray<nsTArray<uint8_t>> certList = std::move(aCertList); 65 aOutput.Clear(); 66 for (auto& certBytes : certList) { 67 RefPtr<nsIX509Cert> cert = new nsNSSCertificate(std::move(certBytes)); 68 aOutput.AppendElement(cert); 69 } 70 } 71 72 void CommonSocketControl::SetServerCert( 73 const nsCOMPtr<nsIX509Cert>& aServerCert, 74 mozilla::psm::EVStatus aEVStatus) { 75 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 76 mServerCert = aServerCert; 77 mIsEV = Some(aEVStatus == mozilla::psm::EVStatus::EV); 78 } 79 80 void CommonSocketControl::SetSucceededCertChain( 81 nsTArray<nsTArray<uint8_t>>&& aCertList) { 82 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 83 return CreateCertChain(mSucceededCertChain, std::move(aCertList)); 84 } 85 86 void CommonSocketControl::SetHandshakeCertificates( 87 nsTArray<nsTArray<uint8_t>>&& aCertList) { 88 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 89 return CreateCertChain(mHandshakeCertificates, std::move(aCertList)); 90 } 91 92 void CommonSocketControl::SetCanceled(PRErrorCode errorCode) { 93 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 94 MOZ_ASSERT(errorCode != 0); 95 if (errorCode == 0) { 96 errorCode = SEC_ERROR_LIBRARY_FAILURE; 97 } 98 99 mErrorCode = errorCode; 100 mCanceled = true; 101 } 102 103 // NB: GetErrorCode may be called before an error code is set (if ever). In that 104 // case, this returns 0, which is treated as a successful value. 105 int32_t CommonSocketControl::GetErrorCode() { 106 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 107 // We're in an inconsistent state if we think we've been canceled but no error 108 // code was set or we haven't been canceled but an error code was set. 109 MOZ_ASSERT( 110 !((mCanceled && mErrorCode == 0) || (!mCanceled && mErrorCode != 0))); 111 if ((mCanceled && mErrorCode == 0) || (!mCanceled && mErrorCode != 0)) { 112 mCanceled = true; 113 mErrorCode = SEC_ERROR_LIBRARY_FAILURE; 114 } 115 116 return mErrorCode; 117 } 118 119 NS_IMETHODIMP 120 CommonSocketControl::ProxyStartSSL(void) { return NS_ERROR_NOT_IMPLEMENTED; } 121 122 NS_IMETHODIMP 123 CommonSocketControl::StartTLS(void) { return NS_ERROR_NOT_IMPLEMENTED; } 124 125 NS_IMETHODIMP 126 CommonSocketControl::AsyncStartTLS(JSContext* aCx, 127 mozilla::dom::Promise** aPromise) { 128 return NS_ERROR_NOT_IMPLEMENTED; 129 } 130 131 NS_IMETHODIMP 132 CommonSocketControl::SetNPNList(nsTArray<nsCString>& aNPNList) { 133 return NS_ERROR_NOT_IMPLEMENTED; 134 } 135 136 NS_IMETHODIMP 137 CommonSocketControl::GetAlpnEarlySelection(nsACString& _retval) { 138 return NS_ERROR_NOT_IMPLEMENTED; 139 } 140 141 NS_IMETHODIMP 142 CommonSocketControl::GetEarlyDataAccepted(bool* aEarlyDataAccepted) { 143 return NS_ERROR_NOT_IMPLEMENTED; 144 } 145 146 NS_IMETHODIMP 147 CommonSocketControl::DriveHandshake(void) { return NS_ERROR_NOT_IMPLEMENTED; } 148 149 NS_IMETHODIMP 150 CommonSocketControl::JoinConnection(const nsACString& npnProtocol, 151 const nsACString& hostname, int32_t port, 152 bool* _retval) { 153 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 154 nsresult rv = TestJoinConnection(npnProtocol, hostname, port, _retval); 155 if (NS_SUCCEEDED(rv) && *_retval) { 156 // All tests pass - this is joinable 157 mJoined = true; 158 } 159 return rv; 160 } 161 162 NS_IMETHODIMP 163 CommonSocketControl::TestJoinConnection(const nsACString& npnProtocol, 164 const nsACString& hostname, 165 int32_t port, bool* _retval) { 166 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 167 *_retval = false; 168 169 // Different ports may not be joined together 170 if (port != GetPort()) return NS_OK; 171 172 // Make sure NPN has been completed and matches requested npnProtocol 173 if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol)) { 174 return NS_OK; 175 } 176 177 IsAcceptableForHost(hostname, _retval); // sets _retval 178 return NS_OK; 179 } 180 181 NS_IMETHODIMP 182 CommonSocketControl::IsAcceptableForHost(const nsACString& hostname, 183 bool* _retval) { 184 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 185 NS_ENSURE_ARG(_retval); 186 187 *_retval = false; 188 189 // If this is the same hostname then the certicate status does not 190 // need to be considered. They are joinable. 191 if (hostname.Equals(GetHostName())) { 192 *_retval = true; 193 return NS_OK; 194 } 195 196 // Before checking the server certificate we need to make sure the 197 // handshake has completed. 198 if (!mHandshakeCompleted || !HasServerCert()) { 199 return NS_OK; 200 } 201 202 // Security checks can only be skipped when running xpcshell tests. 203 if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) { 204 nsCOMPtr<nsICertOverrideService> overrideService = 205 do_GetService(NS_CERTOVERRIDE_CONTRACTID); 206 if (overrideService) { 207 bool securityCheckDisabled = false; 208 overrideService->GetSecurityCheckDisabled(&securityCheckDisabled); 209 if (securityCheckDisabled) { 210 *_retval = true; 211 return NS_OK; 212 } 213 } 214 } 215 216 // If the cert has error bits (e.g. it is untrusted) then do not join. 217 if (mOverridableErrorCategory.isSome()) { 218 return NS_OK; 219 } 220 221 // If the connection is using client certificates then do not join 222 // because the user decides on whether to send client certs to hosts on a 223 // per-domain basis. 224 if (mSentClientCert) return NS_OK; 225 226 // Ensure that the server certificate covers the hostname that would 227 // like to join this connection 228 229 nsCOMPtr<nsIX509Cert> cert(GetServerCert()); 230 if (!cert) { 231 return NS_OK; 232 } 233 nsTArray<uint8_t> certDER; 234 if (NS_FAILED(cert->GetRawDER(certDER))) { 235 return NS_OK; 236 } 237 238 // An empty mSucceededCertChain means the server certificate verification 239 // failed before, so don't join in this case. 240 if (mSucceededCertChain.IsEmpty()) { 241 return NS_OK; 242 } 243 244 // See where CheckCertHostname() is called in 245 // CertVerifier::VerifySSLServerCert. We are doing the same hostname-specific 246 // checks here. If any hostname-specific checks are added to 247 // CertVerifier::VerifySSLServerCert we need to add them here too. 248 pkix::Input serverCertInput; 249 mozilla::pkix::Result rv = 250 serverCertInput.Init(certDER.Elements(), certDER.Length()); 251 if (rv != pkix::Success) { 252 return NS_OK; 253 } 254 255 pkix::Input hostnameInput; 256 rv = hostnameInput.Init( 257 BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()), 258 hostname.Length()); 259 if (rv != pkix::Success) { 260 return NS_OK; 261 } 262 263 rv = CheckCertHostname(serverCertInput, hostnameInput); 264 if (rv != pkix::Success) { 265 return NS_OK; 266 } 267 268 nsTArray<nsTArray<uint8_t>> rawDerCertList; 269 nsTArray<Span<const uint8_t>> derCertSpanList; 270 for (const auto& cert : mSucceededCertChain) { 271 rawDerCertList.EmplaceBack(); 272 nsresult nsrv = cert->GetRawDER(rawDerCertList.LastElement()); 273 if (NS_FAILED(nsrv)) { 274 return nsrv; 275 } 276 derCertSpanList.EmplaceBack(rawDerCertList.LastElement()); 277 } 278 bool chainHasValidPins; 279 nsresult nsrv = mozilla::psm::PublicKeyPinningService::ChainHasValidPins( 280 derCertSpanList, PromiseFlatCString(hostname).BeginReading(), pkix::Now(), 281 mIsBuiltCertChainRootBuiltInRoot, chainHasValidPins, nullptr); 282 if (NS_FAILED(nsrv)) { 283 return NS_OK; 284 } 285 286 if (!chainHasValidPins) { 287 return NS_OK; 288 } 289 290 // All tests pass 291 *_retval = true; 292 return NS_OK; 293 } 294 295 void CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache() { 296 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 297 if (!mSessionCacheInfo) { 298 MOZ_LOG( 299 gPIPNSSLog, LogLevel::Debug, 300 ("CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache cannot " 301 "find cached info.")); 302 return; 303 } 304 305 mozilla::net::SessionCacheInfo& info = *mSessionCacheInfo; 306 nsCOMPtr<nsIX509Cert> cert( 307 new nsNSSCertificate(std::move(info.mServerCertBytes))); 308 SetServerCert(cert, info.mEVStatus); 309 if (info.mOverridableErrorCategory != 310 nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET) { 311 SetStatusErrorBits(info.mOverridableErrorCategory); 312 } 313 SetCertificateTransparencyStatus(info.mCertificateTransparencyStatus); 314 if (info.mSucceededCertChainBytes) { 315 SetSucceededCertChain(std::move(*info.mSucceededCertChainBytes)); 316 } 317 318 if (info.mIsBuiltCertChainRootBuiltInRoot) { 319 SetIsBuiltCertChainRootBuiltInRoot(*info.mIsBuiltCertChainRootBuiltInRoot); 320 } 321 322 if (info.mHandshakeCertificatesBytes) { 323 SetHandshakeCertificates(std::move(*info.mHandshakeCertificatesBytes)); 324 } 325 } 326 327 NS_IMETHODIMP 328 CommonSocketControl::GetKEAUsed(int16_t* aKEAUsed) { 329 return NS_ERROR_NOT_IMPLEMENTED; 330 } 331 332 NS_IMETHODIMP 333 CommonSocketControl::GetKEAKeyBits(uint32_t* aKEAKeyBits) { 334 return NS_ERROR_NOT_IMPLEMENTED; 335 } 336 337 NS_IMETHODIMP 338 CommonSocketControl::GetProviderFlags(uint32_t* aProviderFlags) { 339 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 340 *aProviderFlags = mProviderFlags; 341 return NS_OK; 342 } 343 344 NS_IMETHODIMP 345 CommonSocketControl::GetSSLVersionUsed(int16_t* aSSLVersionUsed) { 346 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 347 *aSSLVersionUsed = mSSLVersionUsed; 348 return NS_OK; 349 } 350 351 NS_IMETHODIMP 352 CommonSocketControl::GetSSLVersionOffered(int16_t* aSSLVersionOffered) { 353 return NS_ERROR_NOT_IMPLEMENTED; 354 } 355 356 NS_IMETHODIMP 357 CommonSocketControl::GetMACAlgorithmUsed(int16_t* aMACAlgorithmUsed) { 358 return NS_ERROR_NOT_IMPLEMENTED; 359 } 360 361 bool CommonSocketControl::GetDenyClientCert() { return true; } 362 363 void CommonSocketControl::SetDenyClientCert(bool aDenyClientCert) {} 364 365 NS_IMETHODIMP 366 CommonSocketControl::GetClientCertSent(bool* arg) { 367 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 368 *arg = mSentClientCert; 369 return NS_OK; 370 } 371 372 NS_IMETHODIMP 373 CommonSocketControl::GetFailedVerification(bool* arg) { 374 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 375 *arg = mFailedVerification; 376 return NS_OK; 377 } 378 379 NS_IMETHODIMP 380 CommonSocketControl::GetEsniTxt(nsACString& aEsniTxt) { 381 return NS_ERROR_NOT_IMPLEMENTED; 382 } 383 384 NS_IMETHODIMP 385 CommonSocketControl::SetEsniTxt(const nsACString& aEsniTxt) { 386 return NS_ERROR_NOT_IMPLEMENTED; 387 } 388 389 NS_IMETHODIMP 390 CommonSocketControl::GetEchConfig(nsACString& aEchConfig) { 391 return NS_ERROR_NOT_IMPLEMENTED; 392 } 393 394 NS_IMETHODIMP 395 CommonSocketControl::SetEchConfig(const nsACString& aEchConfig) { 396 return NS_ERROR_NOT_IMPLEMENTED; 397 } 398 399 NS_IMETHODIMP 400 CommonSocketControl::GetRetryEchConfig(nsACString& aEchConfig) { 401 return NS_ERROR_NOT_IMPLEMENTED; 402 } 403 404 NS_IMETHODIMP 405 CommonSocketControl::SetHandshakeCallbackListener( 406 nsITlsHandshakeCallbackListener* callback) { 407 return NS_ERROR_NOT_IMPLEMENTED; 408 } 409 410 NS_IMETHODIMP 411 CommonSocketControl::DisableEarlyData(void) { return NS_ERROR_NOT_IMPLEMENTED; } 412 413 NS_IMETHODIMP 414 CommonSocketControl::GetPeerId(nsACString& aResult) { 415 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 416 if (!mPeerId.IsEmpty()) { 417 aResult.Assign(mPeerId); 418 return NS_OK; 419 } 420 421 if (mProviderFlags & 422 nsISocketProvider::ANONYMOUS_CONNECT) { // See bug 466080 423 mPeerId.AppendLiteral("anon:"); 424 } 425 if (mProviderFlags & nsISocketProvider::NO_PERMANENT_STORAGE) { 426 mPeerId.AppendLiteral("private:"); 427 } 428 if (mProviderFlags & nsISocketProvider::BE_CONSERVATIVE) { 429 mPeerId.AppendLiteral("beConservative:"); 430 } 431 432 mPeerId.Append(mHostName); 433 mPeerId.Append(':'); 434 mPeerId.AppendInt(GetPort()); 435 nsAutoCString suffix; 436 mOriginAttributes.CreateSuffix(suffix); 437 mPeerId.Append(suffix); 438 439 aResult.Assign(mPeerId); 440 return NS_OK; 441 } 442 443 NS_IMETHODIMP 444 CommonSocketControl::GetSecurityInfo(nsITransportSecurityInfo** aSecurityInfo) { 445 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD(); 446 // Make sure peerId is set. 447 nsAutoCString unused; 448 nsresult rv = GetPeerId(unused); 449 if (NS_FAILED(rv)) { 450 return rv; 451 } 452 nsCOMPtr<nsITransportSecurityInfo> securityInfo( 453 new psm::TransportSecurityInfo( 454 mSecurityState, mErrorCode, mHandshakeCertificates.Clone(), 455 mServerCert, mSucceededCertChain.Clone(), mCipherSuite, mKeaGroupName, 456 mSignatureSchemeName, mProtocolVersion, 457 mCertificateTransparencyStatus, mIsAcceptedEch, 458 mIsDelegatedCredential, mOverridableErrorCategory, mMadeOCSPRequests, 459 mUsedPrivateDNS, mIsEV, mNPNCompleted, mNegotiatedNPN, mResumed, 460 mIsBuiltCertChainRootBuiltInRoot, mPeerId)); 461 securityInfo.forget(aSecurityInfo); 462 return NS_OK; 463 } 464 465 NS_IMETHODIMP 466 CommonSocketControl::AsyncGetSecurityInfo(JSContext* aCx, 467 mozilla::dom::Promise** aPromise) { 468 MOZ_RELEASE_ASSERT(NS_IsMainThread()); 469 NS_ENSURE_ARG_POINTER(aCx); 470 NS_ENSURE_ARG_POINTER(aPromise); 471 472 nsIGlobalObject* globalObject = xpc::CurrentNativeGlobal(aCx); 473 if (!globalObject) { 474 return NS_ERROR_UNEXPECTED; 475 } 476 477 ErrorResult result; 478 RefPtr<mozilla::dom::Promise> promise = 479 mozilla::dom::Promise::Create(globalObject, result); 480 if (result.Failed()) { 481 return result.StealNSResult(); 482 } 483 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction( 484 "CommonSocketControl::AsyncGetSecurityInfo", 485 [promise, self = RefPtr{this}]() mutable { 486 nsCOMPtr<nsITransportSecurityInfo> securityInfo; 487 nsresult rv = self->GetSecurityInfo(getter_AddRefs(securityInfo)); 488 nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction( 489 "CommonSocketControl::AsyncGetSecurityInfoResolve", 490 [rv, promise = std::move(promise), 491 securityInfo = std::move(securityInfo)]() { 492 if (NS_FAILED(rv)) { 493 promise->MaybeReject(rv); 494 } else { 495 promise->MaybeResolve(securityInfo); 496 } 497 })); 498 NS_DispatchToMainThread(runnable.forget()); 499 })); 500 nsCOMPtr<nsIEventTarget> target( 501 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID)); 502 if (!target) { 503 return NS_ERROR_FAILURE; 504 } 505 nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL); 506 if (NS_FAILED(rv)) { 507 return rv; 508 } 509 510 promise.forget(aPromise); 511 return NS_OK; 512 } 513 514 NS_IMETHODIMP CommonSocketControl::Claim() { return NS_ERROR_NOT_IMPLEMENTED; } 515 516 NS_IMETHODIMP CommonSocketControl::SetBrowserId(uint64_t) { 517 return NS_ERROR_NOT_IMPLEMENTED; 518 } 519 520 NS_IMETHODIMP CommonSocketControl::GetBrowserId(uint64_t*) { 521 return NS_ERROR_NOT_IMPLEMENTED; 522 }