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 "BRNameMatchingPolicy.h"
10 #include "PublicKeyPinningService.h"
11 #include "SharedCertVerifier.h"
12 #include "nsNSSComponent.h"
13 #include "SharedSSLState.h"
14 #include "sslt.h"
15 #include "ssl.h"
16 #include "mozilla/net/SSLTokensCache.h"
17 #include "nsICertOverrideService.h"
18 
19 using namespace mozilla;
20 
21 extern LazyLogModule gPIPNSSLog;
22 
NS_IMPL_ISUPPORTS_INHERITED(CommonSocketControl,TransportSecurityInfo,nsISSLSocketControl)23 NS_IMPL_ISUPPORTS_INHERITED(CommonSocketControl, TransportSecurityInfo,
24                             nsISSLSocketControl)
25 
26 CommonSocketControl::CommonSocketControl(uint32_t aProviderFlags)
27     : mHandshakeCompleted(false),
28       mJoined(false),
29       mSentClientCert(false),
30       mFailedVerification(false),
31       mSSLVersionUsed(nsISSLSocketControl::SSL_VERSION_UNKNOWN),
32       mProviderFlags(aProviderFlags) {}
33 
34 NS_IMETHODIMP
GetNotificationCallbacks(nsIInterfaceRequestor ** aCallbacks)35 CommonSocketControl::GetNotificationCallbacks(
36     nsIInterfaceRequestor** aCallbacks) {
37   MutexAutoLock lock(mMutex);
38   *aCallbacks = mCallbacks;
39   NS_IF_ADDREF(*aCallbacks);
40   return NS_OK;
41 }
42 
43 NS_IMETHODIMP
SetNotificationCallbacks(nsIInterfaceRequestor * aCallbacks)44 CommonSocketControl::SetNotificationCallbacks(
45     nsIInterfaceRequestor* aCallbacks) {
46   MutexAutoLock lock(mMutex);
47   mCallbacks = aCallbacks;
48   return NS_OK;
49 }
50 
51 NS_IMETHODIMP
ProxyStartSSL(void)52 CommonSocketControl::ProxyStartSSL(void) { return NS_ERROR_NOT_IMPLEMENTED; }
53 
54 NS_IMETHODIMP
StartTLS(void)55 CommonSocketControl::StartTLS(void) { return NS_ERROR_NOT_IMPLEMENTED; }
56 
57 NS_IMETHODIMP
SetNPNList(nsTArray<nsCString> & aNPNList)58 CommonSocketControl::SetNPNList(nsTArray<nsCString>& aNPNList) {
59   return NS_ERROR_NOT_IMPLEMENTED;
60 }
61 
62 NS_IMETHODIMP
GetAlpnEarlySelection(nsACString & _retval)63 CommonSocketControl::GetAlpnEarlySelection(nsACString& _retval) {
64   return NS_ERROR_NOT_IMPLEMENTED;
65 }
66 
67 NS_IMETHODIMP
GetEarlyDataAccepted(bool * aEarlyDataAccepted)68 CommonSocketControl::GetEarlyDataAccepted(bool* aEarlyDataAccepted) {
69   return NS_ERROR_NOT_IMPLEMENTED;
70 }
71 
72 NS_IMETHODIMP
DriveHandshake(void)73 CommonSocketControl::DriveHandshake(void) { return NS_ERROR_NOT_IMPLEMENTED; }
74 
75 NS_IMETHODIMP
JoinConnection(const nsACString & npnProtocol,const nsACString & hostname,int32_t port,bool * _retval)76 CommonSocketControl::JoinConnection(const nsACString& npnProtocol,
77                                     const nsACString& hostname, int32_t port,
78                                     bool* _retval) {
79   nsresult rv = TestJoinConnection(npnProtocol, hostname, port, _retval);
80   if (NS_SUCCEEDED(rv) && *_retval) {
81     // All tests pass - this is joinable
82     mJoined = true;
83   }
84   return rv;
85 }
86 
87 NS_IMETHODIMP
TestJoinConnection(const nsACString & npnProtocol,const nsACString & hostname,int32_t port,bool * _retval)88 CommonSocketControl::TestJoinConnection(const nsACString& npnProtocol,
89                                         const nsACString& hostname,
90                                         int32_t port, bool* _retval) {
91   *_retval = false;
92 
93   // Different ports may not be joined together
94   if (port != GetPort()) return NS_OK;
95 
96   {
97     MutexAutoLock lock(mMutex);
98     // Make sure NPN has been completed and matches requested npnProtocol
99     if (!mNPNCompleted || !mNegotiatedNPN.Equals(npnProtocol)) return NS_OK;
100   }
101 
102   IsAcceptableForHost(hostname, _retval);  // sets _retval
103   return NS_OK;
104 }
105 
106 NS_IMETHODIMP
IsAcceptableForHost(const nsACString & hostname,bool * _retval)107 CommonSocketControl::IsAcceptableForHost(const nsACString& hostname,
108                                          bool* _retval) {
109   NS_ENSURE_ARG(_retval);
110 
111   *_retval = false;
112 
113   // If this is the same hostname then the certicate status does not
114   // need to be considered. They are joinable.
115   if (hostname.Equals(GetHostName())) {
116     *_retval = true;
117     return NS_OK;
118   }
119 
120   // Before checking the server certificate we need to make sure the
121   // handshake has completed.
122   if (!mHandshakeCompleted || !HasServerCert()) {
123     return NS_OK;
124   }
125 
126   // Security checks can only be skipped when running xpcshell tests.
127   if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
128     nsCOMPtr<nsICertOverrideService> overrideService =
129         do_GetService(NS_CERTOVERRIDE_CONTRACTID);
130     if (overrideService) {
131       bool securityCheckDisabled = false;
132       overrideService->GetSecurityCheckDisabled(&securityCheckDisabled);
133       if (securityCheckDisabled) {
134         *_retval = true;
135         return NS_OK;
136       }
137     }
138   }
139 
140   // If the cert has error bits (e.g. it is untrusted) then do not join.
141   // The value of mHaveCertErrorBits is only reliable because we know that
142   // the handshake completed.
143   if (mHaveCertErrorBits) {
144     return NS_OK;
145   }
146 
147   // If the connection is using client certificates then do not join
148   // because the user decides on whether to send client certs to hosts on a
149   // per-domain basis.
150   if (mSentClientCert) return NS_OK;
151 
152   // Ensure that the server certificate covers the hostname that would
153   // like to join this connection
154 
155   UniqueCERTCertificate nssCert;
156 
157   nsCOMPtr<nsIX509Cert> cert;
158   if (NS_FAILED(GetServerCert(getter_AddRefs(cert)))) {
159     return NS_OK;
160   }
161   if (cert) {
162     nssCert.reset(cert->GetCert());
163   }
164 
165   if (!nssCert) {
166     return NS_OK;
167   }
168 
169   MutexAutoLock lock(mMutex);
170 
171   // An empty mSucceededCertChain means the server certificate verification
172   // failed before, so don't join in this case.
173   if (mSucceededCertChain.IsEmpty()) {
174     return NS_OK;
175   }
176 
177   // See where CheckCertHostname() is called in
178   // CertVerifier::VerifySSLServerCert. We are doing the same hostname-specific
179   // checks here. If any hostname-specific checks are added to
180   // CertVerifier::VerifySSLServerCert we need to add them here too.
181   Input serverCertInput;
182   mozilla::pkix::Result rv =
183       serverCertInput.Init(nssCert->derCert.data, nssCert->derCert.len);
184   if (rv != Success) {
185     return NS_OK;
186   }
187 
188   Input hostnameInput;
189   rv = hostnameInput.Init(
190       BitwiseCast<const uint8_t*, const char*>(hostname.BeginReading()),
191       hostname.Length());
192   if (rv != Success) {
193     return NS_OK;
194   }
195 
196   mozilla::psm::BRNameMatchingPolicy nameMatchingPolicy(
197       mIsBuiltCertChainRootBuiltInRoot
198           ? mozilla::psm::PublicSSLState()->NameMatchingMode()
199           : mozilla::psm::BRNameMatchingPolicy::Mode::DoNotEnforce);
200   rv = CheckCertHostname(serverCertInput, hostnameInput, nameMatchingPolicy);
201   if (rv != Success) {
202     return NS_OK;
203   }
204 
205   nsTArray<nsTArray<uint8_t>> rawDerCertList;
206   nsTArray<Span<const uint8_t>> derCertSpanList;
207   for (const auto& cert : mSucceededCertChain) {
208     rawDerCertList.EmplaceBack();
209     nsresult nsrv = cert->GetRawDER(rawDerCertList.LastElement());
210     if (NS_FAILED(nsrv)) {
211       return nsrv;
212     }
213     derCertSpanList.EmplaceBack(rawDerCertList.LastElement());
214   }
215   bool chainHasValidPins;
216   nsresult nsrv = mozilla::psm::PublicKeyPinningService::ChainHasValidPins(
217       derCertSpanList, PromiseFlatCString(hostname).BeginReading(), Now(),
218       mIsBuiltCertChainRootBuiltInRoot, chainHasValidPins, nullptr);
219   if (NS_FAILED(nsrv)) {
220     return NS_OK;
221   }
222 
223   if (!chainHasValidPins) {
224     return NS_OK;
225   }
226 
227   // All tests pass
228   *_retval = true;
229   return NS_OK;
230 }
231 
RebuildCertificateInfoFromSSLTokenCache()232 void CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache() {
233   nsAutoCString key;
234   GetPeerId(key);
235   mozilla::net::SessionCacheInfo info;
236   if (!mozilla::net::SSLTokensCache::GetSessionCacheInfo(key, info)) {
237     MOZ_LOG(
238         gPIPNSSLog, LogLevel::Debug,
239         ("CommonSocketControl::RebuildCertificateInfoFromSSLTokenCache cannot "
240          "find cached info."));
241     return;
242   }
243 
244   RefPtr<nsNSSCertificate> nssc = nsNSSCertificate::ConstructFromDER(
245       BitwiseCast<char*, uint8_t*>(info.mServerCertBytes.Elements()),
246       info.mServerCertBytes.Length());
247   if (!nssc) {
248     MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
249             ("RebuildCertificateInfoFromSSLTokenCache failed to construct "
250              "server cert"));
251     return;
252   }
253 
254   SetServerCert(nssc, info.mEVStatus);
255   SetCertificateTransparencyStatus(info.mCertificateTransparencyStatus);
256   if (info.mSucceededCertChainBytes) {
257     SetSucceededCertChain(std::move(*info.mSucceededCertChainBytes));
258   }
259 
260   if (info.mIsBuiltCertChainRootBuiltInRoot) {
261     SetIsBuiltCertChainRootBuiltInRoot(*info.mIsBuiltCertChainRootBuiltInRoot);
262   }
263 }
264 
265 NS_IMETHODIMP
GetKEAUsed(int16_t * aKEAUsed)266 CommonSocketControl::GetKEAUsed(int16_t* aKEAUsed) {
267   return NS_ERROR_NOT_IMPLEMENTED;
268 }
269 
270 NS_IMETHODIMP
GetKEAKeyBits(uint32_t * aKEAKeyBits)271 CommonSocketControl::GetKEAKeyBits(uint32_t* aKEAKeyBits) {
272   return NS_ERROR_NOT_IMPLEMENTED;
273 }
274 
275 NS_IMETHODIMP
GetProviderFlags(uint32_t * aProviderFlags)276 CommonSocketControl::GetProviderFlags(uint32_t* aProviderFlags) {
277   *aProviderFlags = mProviderFlags;
278   return NS_OK;
279 }
280 
281 NS_IMETHODIMP
GetProviderTlsFlags(uint32_t * aProviderTlsFlags)282 CommonSocketControl::GetProviderTlsFlags(uint32_t* aProviderTlsFlags) {
283   return NS_ERROR_NOT_IMPLEMENTED;
284 }
285 
286 NS_IMETHODIMP
GetSSLVersionUsed(int16_t * aSSLVersionUsed)287 CommonSocketControl::GetSSLVersionUsed(int16_t* aSSLVersionUsed) {
288   *aSSLVersionUsed = mSSLVersionUsed;
289   return NS_OK;
290 }
291 
292 NS_IMETHODIMP
GetSSLVersionOffered(int16_t * aSSLVersionOffered)293 CommonSocketControl::GetSSLVersionOffered(int16_t* aSSLVersionOffered) {
294   return NS_ERROR_NOT_IMPLEMENTED;
295 }
296 
297 NS_IMETHODIMP
GetMACAlgorithmUsed(int16_t * aMACAlgorithmUsed)298 CommonSocketControl::GetMACAlgorithmUsed(int16_t* aMACAlgorithmUsed) {
299   return NS_ERROR_NOT_IMPLEMENTED;
300 }
301 
GetDenyClientCert()302 bool CommonSocketControl::GetDenyClientCert() { return true; }
303 
SetDenyClientCert(bool aDenyClientCert)304 void CommonSocketControl::SetDenyClientCert(bool aDenyClientCert) {}
305 
306 NS_IMETHODIMP
GetClientCert(nsIX509Cert ** aClientCert)307 CommonSocketControl::GetClientCert(nsIX509Cert** aClientCert) {
308   return NS_ERROR_NOT_IMPLEMENTED;
309 }
310 
311 NS_IMETHODIMP
SetClientCert(nsIX509Cert * aClientCert)312 CommonSocketControl::SetClientCert(nsIX509Cert* aClientCert) {
313   return NS_ERROR_NOT_IMPLEMENTED;
314 }
315 
316 NS_IMETHODIMP
GetClientCertSent(bool * arg)317 CommonSocketControl::GetClientCertSent(bool* arg) {
318   *arg = mSentClientCert;
319   return NS_OK;
320 }
321 
322 NS_IMETHODIMP
GetFailedVerification(bool * arg)323 CommonSocketControl::GetFailedVerification(bool* arg) {
324   *arg = mFailedVerification;
325   return NS_OK;
326 }
327 
328 NS_IMETHODIMP
GetEsniTxt(nsACString & aEsniTxt)329 CommonSocketControl::GetEsniTxt(nsACString& aEsniTxt) {
330   return NS_ERROR_NOT_IMPLEMENTED;
331 }
332 
333 NS_IMETHODIMP
SetEsniTxt(const nsACString & aEsniTxt)334 CommonSocketControl::SetEsniTxt(const nsACString& aEsniTxt) {
335   return NS_ERROR_NOT_IMPLEMENTED;
336 }
337 
338 NS_IMETHODIMP
GetEchConfig(nsACString & aEchConfig)339 CommonSocketControl::GetEchConfig(nsACString& aEchConfig) {
340   return NS_ERROR_NOT_IMPLEMENTED;
341 }
342 
343 NS_IMETHODIMP
SetEchConfig(const nsACString & aEchConfig)344 CommonSocketControl::SetEchConfig(const nsACString& aEchConfig) {
345   // TODO: Implement this in bug 1654507.
346   return NS_OK;
347 }
348 
349 NS_IMETHODIMP
GetPeerId(nsACString & aResult)350 CommonSocketControl::GetPeerId(nsACString& aResult) {
351   return NS_ERROR_NOT_IMPLEMENTED;
352 }
353 
354 NS_IMETHODIMP
GetRetryEchConfig(nsACString & aEchConfig)355 CommonSocketControl::GetRetryEchConfig(nsACString& aEchConfig) {
356   return NS_ERROR_NOT_IMPLEMENTED;
357 }
358