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