1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
7 #include "CommonSocketControl.h"
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"
24 using namespace mozilla
;
26 extern LazyLogModule gPIPNSSLog
;
28 NS_IMPL_ISUPPORTS(CommonSocketControl
, nsITLSSocketControl
)
30 CommonSocketControl::CommonSocketControl(const nsCString
& aHostName
,
31 int32_t aPort
, uint32_t aProviderFlags
)
32 : mHostName(aHostName
),
35 mHandshakeCompleted(false),
37 mSentClientCert(false),
38 mFailedVerification(false),
39 mSSLVersionUsed(nsITLSSocketControl::SSL_VERSION_UNKNOWN
),
40 mProviderFlags(aProviderFlags
),
44 mCertificateTransparencyStatus(0),
45 mMadeOCSPRequests(false),
46 mUsedPrivateDNS(false),
49 mIsBuiltCertChainRootBuiltInRoot(false) {
50 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
51 mOwningThread
= PR_GetCurrentThread();
55 void CommonSocketControl::SetStatusErrorBits(
56 const nsCOMPtr
<nsIX509Cert
>& cert
,
57 nsITransportSecurityInfo::OverridableErrorCategory
58 overridableErrorCategory
) {
59 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
60 SetServerCert(cert
, mozilla::psm::EVStatus::NotEV
);
61 mOverridableErrorCategory
= Some(overridableErrorCategory
);
64 static void CreateCertChain(nsTArray
<RefPtr
<nsIX509Cert
>>& aOutput
,
65 nsTArray
<nsTArray
<uint8_t>>&& aCertList
) {
66 nsTArray
<nsTArray
<uint8_t>> certList
= std::move(aCertList
);
68 for (auto& certBytes
: certList
) {
69 RefPtr
<nsIX509Cert
> cert
= new nsNSSCertificate(std::move(certBytes
));
70 aOutput
.AppendElement(cert
);
74 void CommonSocketControl::SetServerCert(
75 const nsCOMPtr
<nsIX509Cert
>& aServerCert
,
76 mozilla::psm::EVStatus aEVStatus
) {
77 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
78 mServerCert
= aServerCert
;
79 mIsEV
= Some(aEVStatus
== mozilla::psm::EVStatus::EV
);
82 void CommonSocketControl::SetSucceededCertChain(
83 nsTArray
<nsTArray
<uint8_t>>&& aCertList
) {
84 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
85 return CreateCertChain(mSucceededCertChain
, std::move(aCertList
));
88 void CommonSocketControl::SetFailedCertChain(
89 nsTArray
<nsTArray
<uint8_t>>&& aCertList
) {
90 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
91 return CreateCertChain(mFailedCertChain
, std::move(aCertList
));
94 void CommonSocketControl::SetCanceled(PRErrorCode errorCode
) {
95 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
96 MOZ_ASSERT(errorCode
!= 0);
98 errorCode
= SEC_ERROR_LIBRARY_FAILURE
;
101 mErrorCode
= errorCode
;
105 // NB: GetErrorCode may be called before an error code is set (if ever). In that
106 // case, this returns 0, which is treated as a successful value.
107 int32_t CommonSocketControl::GetErrorCode() {
108 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
109 // We're in an inconsistent state if we think we've been canceled but no error
110 // code was set or we haven't been canceled but an error code was set.
112 !((mCanceled
&& mErrorCode
== 0) || (!mCanceled
&& mErrorCode
!= 0)));
113 if ((mCanceled
&& mErrorCode
== 0) || (!mCanceled
&& mErrorCode
!= 0)) {
115 mErrorCode
= SEC_ERROR_LIBRARY_FAILURE
;
122 CommonSocketControl::ProxyStartSSL(void) { return NS_ERROR_NOT_IMPLEMENTED
; }
125 CommonSocketControl::StartTLS(void) { return NS_ERROR_NOT_IMPLEMENTED
; }
128 CommonSocketControl::AsyncStartTLS(JSContext
* aCx
,
129 mozilla::dom::Promise
** aPromise
) {
130 return NS_ERROR_NOT_IMPLEMENTED
;
134 CommonSocketControl::SetNPNList(nsTArray
<nsCString
>& aNPNList
) {
135 return NS_ERROR_NOT_IMPLEMENTED
;
139 CommonSocketControl::GetAlpnEarlySelection(nsACString
& _retval
) {
140 return NS_ERROR_NOT_IMPLEMENTED
;
144 CommonSocketControl::GetEarlyDataAccepted(bool* aEarlyDataAccepted
) {
145 return NS_ERROR_NOT_IMPLEMENTED
;
149 CommonSocketControl::DriveHandshake(void) { return NS_ERROR_NOT_IMPLEMENTED
; }
152 CommonSocketControl::JoinConnection(const nsACString
& npnProtocol
,
153 const nsACString
& hostname
, int32_t port
,
155 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
156 nsresult rv
= TestJoinConnection(npnProtocol
, hostname
, port
, _retval
);
157 if (NS_SUCCEEDED(rv
) && *_retval
) {
158 // All tests pass - this is joinable
165 CommonSocketControl::TestJoinConnection(const nsACString
& npnProtocol
,
166 const nsACString
& hostname
,
167 int32_t port
, bool* _retval
) {
168 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
171 // Different ports may not be joined together
172 if (port
!= GetPort()) return NS_OK
;
174 // Make sure NPN has been completed and matches requested npnProtocol
175 if (!mNPNCompleted
|| !mNegotiatedNPN
.Equals(npnProtocol
)) {
179 IsAcceptableForHost(hostname
, _retval
); // sets _retval
184 CommonSocketControl::IsAcceptableForHost(const nsACString
& hostname
,
186 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
187 NS_ENSURE_ARG(_retval
);
191 // If this is the same hostname then the certicate status does not
192 // need to be considered. They are joinable.
193 if (hostname
.Equals(GetHostName())) {
198 // Before checking the server certificate we need to make sure the
199 // handshake has completed.
200 if (!mHandshakeCompleted
|| !HasServerCert()) {
204 // Security checks can only be skipped when running xpcshell tests.
205 if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
206 nsCOMPtr
<nsICertOverrideService
> overrideService
=
207 do_GetService(NS_CERTOVERRIDE_CONTRACTID
);
208 if (overrideService
) {
209 bool securityCheckDisabled
= false;
210 overrideService
->GetSecurityCheckDisabled(&securityCheckDisabled
);
211 if (securityCheckDisabled
) {
218 // If the cert has error bits (e.g. it is untrusted) then do not join.
219 if (mOverridableErrorCategory
.isSome()) {
223 // If the connection is using client certificates then do not join
224 // because the user decides on whether to send client certs to hosts on a
226 if (mSentClientCert
) return NS_OK
;
228 // Ensure that the server certificate covers the hostname that would
229 // like to join this connection
231 nsCOMPtr
<nsIX509Cert
> cert(GetServerCert());
235 nsTArray
<uint8_t> certDER
;
236 if (NS_FAILED(cert
->GetRawDER(certDER
))) {
240 // An empty mSucceededCertChain means the server certificate verification
241 // failed before, so don't join in this case.
242 if (mSucceededCertChain
.IsEmpty()) {
246 // See where CheckCertHostname() is called in
247 // CertVerifier::VerifySSLServerCert. We are doing the same hostname-specific
248 // checks here. If any hostname-specific checks are added to
249 // CertVerifier::VerifySSLServerCert we need to add them here too.
250 pkix::Input serverCertInput
;
251 mozilla::pkix::Result rv
=
252 serverCertInput
.Init(certDER
.Elements(), certDER
.Length());
253 if (rv
!= pkix::Success
) {
257 pkix::Input hostnameInput
;
258 rv
= hostnameInput
.Init(
259 BitwiseCast
<const uint8_t*, const char*>(hostname
.BeginReading()),
261 if (rv
!= pkix::Success
) {
265 rv
= CheckCertHostname(serverCertInput
, hostnameInput
);
266 if (rv
!= pkix::Success
) {
270 nsTArray
<nsTArray
<uint8_t>> rawDerCertList
;
271 nsTArray
<Span
<const uint8_t>> derCertSpanList
;
272 for (const auto& cert
: mSucceededCertChain
) {
273 rawDerCertList
.EmplaceBack();
274 nsresult nsrv
= cert
->GetRawDER(rawDerCertList
.LastElement());
275 if (NS_FAILED(nsrv
)) {
278 derCertSpanList
.EmplaceBack(rawDerCertList
.LastElement());
280 bool chainHasValidPins
;
281 nsresult nsrv
= mozilla::psm::PublicKeyPinningService::ChainHasValidPins(
282 derCertSpanList
, PromiseFlatCString(hostname
).BeginReading(), pkix::Now(),
283 mIsBuiltCertChainRootBuiltInRoot
, chainHasValidPins
, nullptr);
284 if (NS_FAILED(nsrv
)) {
288 if (!chainHasValidPins
) {
297 void CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache() {
298 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
299 if (!mSessionCacheInfo
) {
301 gPIPNSSLog
, LogLevel::Debug
,
302 ("CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache cannot "
303 "find cached info."));
307 mozilla::net::SessionCacheInfo
& info
= *mSessionCacheInfo
;
308 nsCOMPtr
<nsIX509Cert
> cert(
309 new nsNSSCertificate(std::move(info
.mServerCertBytes
)));
310 if (info
.mOverridableErrorCategory
==
311 nsITransportSecurityInfo::OverridableErrorCategory::ERROR_UNSET
) {
312 SetServerCert(cert
, info
.mEVStatus
);
314 SetStatusErrorBits(cert
, info
.mOverridableErrorCategory
);
316 SetCertificateTransparencyStatus(info
.mCertificateTransparencyStatus
);
317 if (info
.mSucceededCertChainBytes
) {
318 SetSucceededCertChain(std::move(*info
.mSucceededCertChainBytes
));
321 if (info
.mIsBuiltCertChainRootBuiltInRoot
) {
322 SetIsBuiltCertChainRootBuiltInRoot(*info
.mIsBuiltCertChainRootBuiltInRoot
);
325 if (info
.mFailedCertChainBytes
) {
326 SetFailedCertChain(std::move(*info
.mFailedCertChainBytes
));
331 CommonSocketControl::GetKEAUsed(int16_t* aKEAUsed
) {
332 return NS_ERROR_NOT_IMPLEMENTED
;
336 CommonSocketControl::GetKEAKeyBits(uint32_t* aKEAKeyBits
) {
337 return NS_ERROR_NOT_IMPLEMENTED
;
341 CommonSocketControl::GetProviderFlags(uint32_t* aProviderFlags
) {
342 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
343 *aProviderFlags
= mProviderFlags
;
348 CommonSocketControl::GetSSLVersionUsed(int16_t* aSSLVersionUsed
) {
349 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
350 *aSSLVersionUsed
= mSSLVersionUsed
;
355 CommonSocketControl::GetSSLVersionOffered(int16_t* aSSLVersionOffered
) {
356 return NS_ERROR_NOT_IMPLEMENTED
;
360 CommonSocketControl::GetMACAlgorithmUsed(int16_t* aMACAlgorithmUsed
) {
361 return NS_ERROR_NOT_IMPLEMENTED
;
364 bool CommonSocketControl::GetDenyClientCert() { return true; }
366 void CommonSocketControl::SetDenyClientCert(bool aDenyClientCert
) {}
369 CommonSocketControl::GetClientCertSent(bool* arg
) {
370 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
371 *arg
= mSentClientCert
;
376 CommonSocketControl::GetFailedVerification(bool* arg
) {
377 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
378 *arg
= mFailedVerification
;
383 CommonSocketControl::GetEsniTxt(nsACString
& aEsniTxt
) {
384 return NS_ERROR_NOT_IMPLEMENTED
;
388 CommonSocketControl::SetEsniTxt(const nsACString
& aEsniTxt
) {
389 return NS_ERROR_NOT_IMPLEMENTED
;
393 CommonSocketControl::GetEchConfig(nsACString
& aEchConfig
) {
394 return NS_ERROR_NOT_IMPLEMENTED
;
398 CommonSocketControl::SetEchConfig(const nsACString
& aEchConfig
) {
399 return NS_ERROR_NOT_IMPLEMENTED
;
403 CommonSocketControl::GetRetryEchConfig(nsACString
& aEchConfig
) {
404 return NS_ERROR_NOT_IMPLEMENTED
;
408 CommonSocketControl::SetHandshakeCallbackListener(
409 nsITlsHandshakeCallbackListener
* callback
) {
410 return NS_ERROR_NOT_IMPLEMENTED
;
414 CommonSocketControl::DisableEarlyData(void) { return NS_ERROR_NOT_IMPLEMENTED
; }
417 CommonSocketControl::GetPeerId(nsACString
& aResult
) {
418 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
419 if (!mPeerId
.IsEmpty()) {
420 aResult
.Assign(mPeerId
);
425 nsISocketProvider::ANONYMOUS_CONNECT
) { // See bug 466080
426 mPeerId
.AppendLiteral("anon:");
428 if (mProviderFlags
& nsISocketProvider::NO_PERMANENT_STORAGE
) {
429 mPeerId
.AppendLiteral("private:");
431 if (mProviderFlags
& nsISocketProvider::BE_CONSERVATIVE
) {
432 mPeerId
.AppendLiteral("beConservative:");
435 mPeerId
.Append(mHostName
);
437 mPeerId
.AppendInt(GetPort());
438 nsAutoCString suffix
;
439 mOriginAttributes
.CreateSuffix(suffix
);
440 mPeerId
.Append(suffix
);
442 aResult
.Assign(mPeerId
);
447 CommonSocketControl::GetSecurityInfo(nsITransportSecurityInfo
** aSecurityInfo
) {
448 COMMON_SOCKET_CONTROL_ASSERT_ON_OWNING_THREAD();
449 // Make sure peerId is set.
450 nsAutoCString unused
;
451 nsresult rv
= GetPeerId(unused
);
455 nsCOMPtr
<nsITransportSecurityInfo
> securityInfo(
456 new psm::TransportSecurityInfo(
457 mSecurityState
, mErrorCode
, mFailedCertChain
.Clone(), mServerCert
,
458 mSucceededCertChain
.Clone(), mCipherSuite
, mKeaGroupName
,
459 mSignatureSchemeName
, mProtocolVersion
,
460 mCertificateTransparencyStatus
, mIsAcceptedEch
,
461 mIsDelegatedCredential
, mOverridableErrorCategory
, mMadeOCSPRequests
,
462 mUsedPrivateDNS
, mIsEV
, mNPNCompleted
, mNegotiatedNPN
, mResumed
,
463 mIsBuiltCertChainRootBuiltInRoot
, mPeerId
));
464 securityInfo
.forget(aSecurityInfo
);
469 CommonSocketControl::AsyncGetSecurityInfo(JSContext
* aCx
,
470 mozilla::dom::Promise
** aPromise
) {
471 MOZ_RELEASE_ASSERT(NS_IsMainThread());
472 NS_ENSURE_ARG_POINTER(aCx
);
473 NS_ENSURE_ARG_POINTER(aPromise
);
475 nsIGlobalObject
* globalObject
= xpc::CurrentNativeGlobal(aCx
);
477 return NS_ERROR_UNEXPECTED
;
481 RefPtr
<mozilla::dom::Promise
> promise
=
482 mozilla::dom::Promise::Create(globalObject
, result
);
483 if (result
.Failed()) {
484 return result
.StealNSResult();
486 nsCOMPtr
<nsIRunnable
> runnable(NS_NewRunnableFunction(
487 "CommonSocketControl::AsyncGetSecurityInfo",
488 [promise
, self
= RefPtr
{this}]() mutable {
489 nsCOMPtr
<nsITransportSecurityInfo
> securityInfo
;
490 nsresult rv
= self
->GetSecurityInfo(getter_AddRefs(securityInfo
));
491 nsCOMPtr
<nsIRunnable
> runnable(NS_NewRunnableFunction(
492 "CommonSocketControl::AsyncGetSecurityInfoResolve",
493 [rv
, promise
= std::move(promise
),
494 securityInfo
= std::move(securityInfo
)]() {
496 promise
->MaybeReject(rv
);
498 promise
->MaybeResolve(securityInfo
);
501 NS_DispatchToMainThread(runnable
.forget());
503 nsCOMPtr
<nsIEventTarget
> target(
504 do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID
));
506 return NS_ERROR_FAILURE
;
508 nsresult rv
= target
->Dispatch(runnable
, NS_DISPATCH_NORMAL
);
513 promise
.forget(aPromise
);
517 NS_IMETHODIMP
CommonSocketControl::Claim() { return NS_ERROR_NOT_IMPLEMENTED
; }
519 NS_IMETHODIMP
CommonSocketControl::SetBrowserId(uint64_t) {
520 return NS_ERROR_NOT_IMPLEMENTED
;
523 NS_IMETHODIMP
CommonSocketControl::GetBrowserId(uint64_t*) {
524 return NS_ERROR_NOT_IMPLEMENTED
;