/src/mozilla-central/security/manager/ssl/SSLServerCertVerification.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | // For connections that are not processed on the socket transport thread, we do |
8 | | // NOT use the async logic described below. Instead, we authenticate the |
9 | | // certificate on the thread that the connection's I/O happens on, |
10 | | // synchronously. This allows us to do certificate verification for blocking |
11 | | // (not non-blocking) sockets and sockets that have their I/O processed on a |
12 | | // thread other than the socket transport service thread. Also, we DO NOT |
13 | | // support blocking sockets on the socket transport service thread at all. |
14 | | // |
15 | | // During certificate authentication, we call CERT_PKIXVerifyCert or |
16 | | // CERT_VerifyCert. These functions may make zero or more HTTP requests |
17 | | // for OCSP responses, CRLs, intermediate certificates, etc. Our fetching logic |
18 | | // for these requests processes them on the socket transport service thread. |
19 | | // |
20 | | // If the connection for which we are verifying the certificate is happening |
21 | | // on the socket transport thread (the usually case, at least for HTTP), then |
22 | | // if our cert auth hook were to call the CERT_*Verify* functions directly, |
23 | | // there would be a deadlock: The CERT_*Verify* function would cause an event |
24 | | // to be asynchronously posted to the socket transport thread, and then it |
25 | | // would block the socket transport thread waiting to be notified of the HTTP |
26 | | // response. However, the HTTP request would never actually be processed |
27 | | // because the socket transport thread would be blocked and so it wouldn't be |
28 | | // able process HTTP requests. (i.e. Deadlock.) |
29 | | // |
30 | | // Consequently, when we are asked to verify a certificate on the socket |
31 | | // transport service thread, we must always call the CERT_*Verify* cert |
32 | | // functions on another thread. To accomplish this, our auth cert hook |
33 | | // dispatches a SSLServerCertVerificationJob to a pool of background threads, |
34 | | // and then immediately returns SECWouldBlock to libssl. These jobs are where |
35 | | // the CERT_*Verify* functions are actually called. |
36 | | // |
37 | | // When our auth cert hook returns SECWouldBlock, libssl will carry on the |
38 | | // handshake while we validate the certificate. This will free up the socket |
39 | | // transport thread so that HTTP requests--in particular, the OCSP/CRL/cert |
40 | | // requests needed for cert verification as mentioned above--can be processed. |
41 | | // |
42 | | // Once the CERT_*Verify* function returns, the cert verification job |
43 | | // dispatches a SSLServerCertVerificationResult to the socket transport thread; |
44 | | // the SSLServerCertVerificationResult will notify libssl that the certificate |
45 | | // authentication is complete. Once libssl is notified that the authentication |
46 | | // is complete, it will continue the SSL handshake (if it hasn't already |
47 | | // finished) and it will begin allowing us to send/receive data on the |
48 | | // connection. |
49 | | // |
50 | | // Timeline of events (for connections managed by the socket transport service): |
51 | | // |
52 | | // * libssl calls SSLServerCertVerificationJob::Dispatch on the socket |
53 | | // transport thread. |
54 | | // * SSLServerCertVerificationJob::Dispatch queues a job |
55 | | // (instance of SSLServerCertVerificationJob) to its background thread |
56 | | // pool and returns. |
57 | | // * One of the background threads calls CERT_*Verify*, which may enqueue |
58 | | // some HTTP request(s) onto the socket transport thread, and then |
59 | | // blocks that background thread waiting for the responses and/or timeouts |
60 | | // or errors for those requests. |
61 | | // * Once those HTTP responses have all come back or failed, the |
62 | | // CERT_*Verify* function returns a result indicating that the validation |
63 | | // succeeded or failed. |
64 | | // * If the validation succeeded, then a SSLServerCertVerificationResult |
65 | | // event is posted to the socket transport thread, and the cert |
66 | | // verification thread becomes free to verify other certificates. |
67 | | // * Otherwise, a CertErrorRunnable is posted to the socket transport thread |
68 | | // and then to the main thread (blocking both, see CertErrorRunnable) to |
69 | | // do cert override processing and bad cert listener notification. Then |
70 | | // the cert verification thread becomes free to verify other certificates. |
71 | | // * After processing cert overrides, the CertErrorRunnable will dispatch a |
72 | | // SSLServerCertVerificationResult event to the socket transport thread to |
73 | | // notify it of the result of the override processing; then it returns, |
74 | | // freeing up the main thread. |
75 | | // * The SSLServerCertVerificationResult event will either wake up the |
76 | | // socket (using SSL_RestartHandshakeAfterServerCert) if validation |
77 | | // succeeded or there was an error override, or it will set an error flag |
78 | | // so that the next I/O operation on the socket will fail, causing the |
79 | | // socket transport thread to close the connection. |
80 | | // |
81 | | // Cert override processing must happen on the main thread because it accesses |
82 | | // the nsICertOverrideService, and that service must be accessed on the main |
83 | | // thread because some extensions (Selenium, in particular) replace it with a |
84 | | // Javascript implementation, and chrome JS must always be run on the main |
85 | | // thread. |
86 | | // |
87 | | // SSLServerCertVerificationResult must be dispatched to the socket transport |
88 | | // thread because we must only call SSL_* functions on the socket transport |
89 | | // thread since they may do I/O, because many parts of nsNSSSocketInfo (the |
90 | | // subclass of TransportSecurityInfo used when validating certificates during |
91 | | // an SSL handshake) and the PSM NSS I/O layer are not thread-safe, and because |
92 | | // we need the event to interrupt the PR_Poll that may waiting for I/O on the |
93 | | // socket for which we are validating the cert. |
94 | | |
95 | | #include "SSLServerCertVerification.h" |
96 | | |
97 | | #include <cstring> |
98 | | |
99 | | #include "BRNameMatchingPolicy.h" |
100 | | #include "CertVerifier.h" |
101 | | #include "CryptoTask.h" |
102 | | #include "ExtendedValidation.h" |
103 | | #include "NSSCertDBTrustDomain.h" |
104 | | #include "PSMRunnable.h" |
105 | | #include "RootCertificateTelemetryUtils.h" |
106 | | #include "ScopedNSSTypes.h" |
107 | | #include "SharedCertVerifier.h" |
108 | | #include "SharedSSLState.h" |
109 | | #include "TransportSecurityInfo.h" // For RememberCertErrorsTable |
110 | | #include "cert.h" |
111 | | #include "mozilla/Assertions.h" |
112 | | #include "mozilla/Casting.h" |
113 | | #include "mozilla/RefPtr.h" |
114 | | #include "mozilla/Telemetry.h" |
115 | | #include "mozilla/UniquePtr.h" |
116 | | #include "mozilla/Unused.h" |
117 | | #include "mozilla/net/DNS.h" |
118 | | #include "nsComponentManagerUtils.h" |
119 | | #include "nsContentUtils.h" |
120 | | #include "nsIBadCertListener2.h" |
121 | | #include "nsICertOverrideService.h" |
122 | | #include "nsISiteSecurityService.h" |
123 | | #include "nsISocketProvider.h" |
124 | | #include "nsThreadPool.h" |
125 | | #include "nsNetUtil.h" |
126 | | #include "nsNSSCertificate.h" |
127 | | #include "nsNSSComponent.h" |
128 | | #include "nsNSSIOLayer.h" |
129 | | #include "nsServiceManagerUtils.h" |
130 | | #include "nsString.h" |
131 | | #include "nsURLHelper.h" |
132 | | #include "nsXPCOMCIDInternal.h" |
133 | | #include "pkix/pkix.h" |
134 | | #include "pkix/pkixnss.h" |
135 | | #include "secerr.h" |
136 | | #include "secoidt.h" |
137 | | #include "secport.h" |
138 | | #include "ssl.h" |
139 | | #include "sslerr.h" |
140 | | |
141 | | extern mozilla::LazyLogModule gPIPNSSLog; |
142 | | |
143 | | using namespace mozilla::pkix; |
144 | | |
145 | | namespace mozilla { namespace psm { |
146 | | |
147 | | namespace { |
148 | | |
149 | | // do not use a nsCOMPtr to avoid static initializer/destructor |
150 | | nsIThreadPool* gCertVerificationThreadPool = nullptr; |
151 | | |
152 | | } // unnamed namespace |
153 | | |
154 | | // Called when the socket transport thread starts, to initialize the SSL cert |
155 | | // verification thread pool. By tying the thread pool startup/shutdown directly |
156 | | // to the STS thread's lifetime, we ensure that they are *always* available for |
157 | | // SSL connections and that there are no races during startup and especially |
158 | | // shutdown. (Previously, we have had multiple problems with races in PSM |
159 | | // background threads, and the race-prevention/shutdown logic used there is |
160 | | // brittle. Since this service is critical to things like downloading updates, |
161 | | // we take no chances.) Also, by doing things this way, we avoid the need for |
162 | | // locks, since gCertVerificationThreadPool is only ever accessed on the socket |
163 | | // transport thread. |
164 | | void |
165 | | InitializeSSLServerCertVerificationThreads() |
166 | 3 | { |
167 | 3 | // TODO: tuning, make parameters preferences |
168 | 3 | gCertVerificationThreadPool = new nsThreadPool(); |
169 | 3 | NS_ADDREF(gCertVerificationThreadPool); |
170 | 3 | |
171 | 3 | (void) gCertVerificationThreadPool->SetIdleThreadLimit(5); |
172 | 3 | (void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000); |
173 | 3 | (void) gCertVerificationThreadPool->SetThreadLimit(5); |
174 | 3 | (void) gCertVerificationThreadPool->SetName(NS_LITERAL_CSTRING("SSL Cert")); |
175 | 3 | } |
176 | | |
177 | | // Called when the socket transport thread finishes, to destroy the thread |
178 | | // pool. Since the socket transport service has stopped processing events, it |
179 | | // will not attempt any more SSL I/O operations, so it is clearly safe to shut |
180 | | // down the SSL cert verification infrastructure. Also, the STS will not |
181 | | // dispatch many SSL verification result events at this point, so any pending |
182 | | // cert verifications will (correctly) fail at the point they are dispatched. |
183 | | // |
184 | | // The other shutdown race condition that is possible is a race condition with |
185 | | // shutdown of the nsNSSComponent service. We use the |
186 | | // nsNSSShutdownPreventionLock where needed (not here) to prevent that. |
187 | | void StopSSLServerCertVerificationThreads() |
188 | 0 | { |
189 | 0 | if (gCertVerificationThreadPool) { |
190 | 0 | gCertVerificationThreadPool->Shutdown(); |
191 | 0 | NS_RELEASE(gCertVerificationThreadPool); |
192 | 0 | } |
193 | 0 | } |
194 | | |
195 | | namespace { |
196 | | |
197 | | // Dispatched to the STS thread to notify the infoObject of the verification |
198 | | // result. |
199 | | // |
200 | | // This will cause the PR_Poll in the STS thread to return, so things work |
201 | | // correctly even if the STS thread is blocked polling (only) on the file |
202 | | // descriptor that is waiting for this result. |
203 | | class SSLServerCertVerificationResult : public Runnable |
204 | | { |
205 | | public: |
206 | | NS_DECL_NSIRUNNABLE |
207 | | |
208 | | SSLServerCertVerificationResult(nsNSSSocketInfo* infoObject, |
209 | | PRErrorCode errorCode, |
210 | | Telemetry::HistogramID telemetryID = Telemetry::HistogramCount, |
211 | | uint32_t telemetryValue = -1); |
212 | | |
213 | | void Dispatch(); |
214 | | private: |
215 | | const RefPtr<nsNSSSocketInfo> mInfoObject; |
216 | | public: |
217 | | const PRErrorCode mErrorCode; |
218 | | const Telemetry::HistogramID mTelemetryID; |
219 | | const uint32_t mTelemetryValue; |
220 | | }; |
221 | | |
222 | | class CertErrorRunnable : public SyncRunnableBase |
223 | | { |
224 | | public: |
225 | | CertErrorRunnable(const void* fdForLogging, |
226 | | nsIX509Cert* cert, |
227 | | nsNSSSocketInfo* infoObject, |
228 | | PRErrorCode defaultErrorCodeToReport, |
229 | | uint32_t collectedErrors, |
230 | | PRErrorCode errorCodeTrust, |
231 | | PRErrorCode errorCodeMismatch, |
232 | | PRErrorCode errorCodeTime, |
233 | | uint32_t providerFlags) |
234 | | : mFdForLogging(fdForLogging), mCert(cert), mInfoObject(infoObject), |
235 | | mDefaultErrorCodeToReport(defaultErrorCodeToReport), |
236 | | mCollectedErrors(collectedErrors), |
237 | | mErrorCodeTrust(errorCodeTrust), |
238 | | mErrorCodeMismatch(errorCodeMismatch), |
239 | | mErrorCodeTime(errorCodeTime), |
240 | | mProviderFlags(providerFlags) |
241 | 0 | { |
242 | 0 | } |
243 | | |
244 | | virtual void RunOnTargetThread() override; |
245 | | RefPtr<SSLServerCertVerificationResult> mResult; // out |
246 | | private: |
247 | | SSLServerCertVerificationResult* CheckCertOverrides(); |
248 | | nsresult OverrideAllowedForHost(/*out*/ bool& overrideAllowed); |
249 | | |
250 | | const void* const mFdForLogging; // may become an invalid pointer; do not dereference |
251 | | const nsCOMPtr<nsIX509Cert> mCert; |
252 | | const RefPtr<nsNSSSocketInfo> mInfoObject; |
253 | | const PRErrorCode mDefaultErrorCodeToReport; |
254 | | const uint32_t mCollectedErrors; |
255 | | const PRErrorCode mErrorCodeTrust; |
256 | | const PRErrorCode mErrorCodeMismatch; |
257 | | const PRErrorCode mErrorCodeTime; |
258 | | const uint32_t mProviderFlags; |
259 | | }; |
260 | | |
261 | | // A probe value of 1 means "no error". |
262 | | uint32_t |
263 | | MapOverridableErrorToProbeValue(PRErrorCode errorCode) |
264 | 0 | { |
265 | 0 | switch (errorCode) |
266 | 0 | { |
267 | 0 | case SEC_ERROR_UNKNOWN_ISSUER: return 2; |
268 | 0 | case SEC_ERROR_CA_CERT_INVALID: return 3; |
269 | 0 | case SEC_ERROR_UNTRUSTED_ISSUER: return 4; |
270 | 0 | case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: return 5; |
271 | 0 | case SEC_ERROR_UNTRUSTED_CERT: return 6; |
272 | 0 | case SEC_ERROR_INADEQUATE_KEY_USAGE: return 7; |
273 | 0 | case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: return 8; |
274 | 0 | case SSL_ERROR_BAD_CERT_DOMAIN: return 9; |
275 | 0 | case SEC_ERROR_EXPIRED_CERTIFICATE: return 10; |
276 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: return 11; |
277 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA: return 12; |
278 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE: return 13; |
279 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE: return 14; |
280 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE: |
281 | 0 | return 15; |
282 | 0 | case SEC_ERROR_INVALID_TIME: return 16; |
283 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME: return 17; |
284 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED: |
285 | 0 | return 18; |
286 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT: return 19; |
287 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_MITM_DETECTED: return 20; |
288 | 0 | } |
289 | 0 | NS_WARNING("Unknown certificate error code. Does MapOverridableErrorToProbeValue " |
290 | 0 | "handle everything in DetermineCertOverrideErrors?"); |
291 | 0 | return 0; |
292 | 0 | } |
293 | | |
294 | | static uint32_t |
295 | | MapCertErrorToProbeValue(PRErrorCode errorCode) |
296 | 0 | { |
297 | 0 | uint32_t probeValue; |
298 | 0 | switch (errorCode) |
299 | 0 | { |
300 | 0 | // see security/pkix/include/pkix/Result.h |
301 | 0 | #define MOZILLA_PKIX_MAP(name, value, nss_name) case nss_name: probeValue = value; break; |
302 | 0 | MOZILLA_PKIX_MAP_LIST |
303 | 0 | #undef MOZILLA_PKIX_MAP |
304 | 0 | default: return 0; |
305 | 0 | } |
306 | 0 | |
307 | 0 | // Since FATAL_ERROR_FLAG is 0x800, fatal error values are much larger than |
308 | 0 | // non-fatal error values. To conserve space, we remap these so they start at |
309 | 0 | // (decimal) 90 instead of 0x800. Currently there are ~50 non-fatal errors |
310 | 0 | // mozilla::pkix might return, so saving space for 90 should be sufficient |
311 | 0 | // (similarly, there are 4 fatal errors, so saving space for 10 should also |
312 | 0 | // be sufficient). |
313 | 0 | static_assert(FATAL_ERROR_FLAG == 0x800, |
314 | 0 | "mozilla::pkix::FATAL_ERROR_FLAG is not what we were expecting"); |
315 | 0 | if (probeValue & FATAL_ERROR_FLAG) { |
316 | 0 | probeValue ^= FATAL_ERROR_FLAG; |
317 | 0 | probeValue += 90; |
318 | 0 | } |
319 | 0 | return probeValue; |
320 | 0 | } |
321 | | |
322 | | SECStatus |
323 | | DetermineCertOverrideErrors(const UniqueCERTCertificate& cert, |
324 | | const nsACString& hostName, |
325 | | PRTime now, PRErrorCode defaultErrorCodeToReport, |
326 | | /*out*/ uint32_t& collectedErrors, |
327 | | /*out*/ PRErrorCode& errorCodeTrust, |
328 | | /*out*/ PRErrorCode& errorCodeMismatch, |
329 | | /*out*/ PRErrorCode& errorCodeTime) |
330 | 0 | { |
331 | 0 | MOZ_ASSERT(cert); |
332 | 0 | MOZ_ASSERT(collectedErrors == 0); |
333 | 0 | MOZ_ASSERT(errorCodeTrust == 0); |
334 | 0 | MOZ_ASSERT(errorCodeMismatch == 0); |
335 | 0 | MOZ_ASSERT(errorCodeTime == 0); |
336 | 0 |
|
337 | 0 | // Assumes the error prioritization described in mozilla::pkix's |
338 | 0 | // BuildForward function. Also assumes that CheckCertHostname was only |
339 | 0 | // called if CertVerifier::VerifyCert succeeded. |
340 | 0 | switch (defaultErrorCodeToReport) { |
341 | 0 | case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: |
342 | 0 | case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: |
343 | 0 | case SEC_ERROR_UNKNOWN_ISSUER: |
344 | 0 | case SEC_ERROR_CA_CERT_INVALID: |
345 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_ADDITIONAL_POLICY_CONSTRAINT_FAILED: |
346 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY: |
347 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME: |
348 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE: |
349 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_MITM_DETECTED: |
350 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE: |
351 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_SELF_SIGNED_CERT: |
352 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA: |
353 | 0 | { |
354 | 0 | collectedErrors = nsICertOverrideService::ERROR_UNTRUSTED; |
355 | 0 | errorCodeTrust = defaultErrorCodeToReport; |
356 | 0 |
|
357 | 0 | SECCertTimeValidity validity = CERT_CheckCertValidTimes(cert.get(), now, |
358 | 0 | false); |
359 | 0 | if (validity == secCertTimeUndetermined) { |
360 | 0 | // This only happens if cert is null. CERT_CheckCertValidTimes will |
361 | 0 | // have set the error code to SEC_ERROR_INVALID_ARGS. We should really |
362 | 0 | // be using mozilla::pkix here anyway. |
363 | 0 | MOZ_ASSERT(PR_GetError() == SEC_ERROR_INVALID_ARGS); |
364 | 0 | return SECFailure; |
365 | 0 | } |
366 | 0 | if (validity == secCertTimeExpired) { |
367 | 0 | collectedErrors |= nsICertOverrideService::ERROR_TIME; |
368 | 0 | errorCodeTime = SEC_ERROR_EXPIRED_CERTIFICATE; |
369 | 0 | } else if (validity == secCertTimeNotValidYet) { |
370 | 0 | collectedErrors |= nsICertOverrideService::ERROR_TIME; |
371 | 0 | errorCodeTime = |
372 | 0 | mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE; |
373 | 0 | } |
374 | 0 | break; |
375 | 0 | } |
376 | 0 |
|
377 | 0 | case SEC_ERROR_INVALID_TIME: |
378 | 0 | case SEC_ERROR_EXPIRED_CERTIFICATE: |
379 | 0 | case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE: |
380 | 0 | collectedErrors = nsICertOverrideService::ERROR_TIME; |
381 | 0 | errorCodeTime = defaultErrorCodeToReport; |
382 | 0 | break; |
383 | 0 |
|
384 | 0 | case SSL_ERROR_BAD_CERT_DOMAIN: |
385 | 0 | collectedErrors = nsICertOverrideService::ERROR_MISMATCH; |
386 | 0 | errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN; |
387 | 0 | break; |
388 | 0 |
|
389 | 0 | case 0: |
390 | 0 | NS_ERROR("No error code set during certificate validation failure."); |
391 | 0 | PR_SetError(PR_INVALID_STATE_ERROR, 0); |
392 | 0 | return SECFailure; |
393 | 0 |
|
394 | 0 | default: |
395 | 0 | PR_SetError(defaultErrorCodeToReport, 0); |
396 | 0 | return SECFailure; |
397 | 0 | } |
398 | 0 | |
399 | 0 | if (defaultErrorCodeToReport != SSL_ERROR_BAD_CERT_DOMAIN) { |
400 | 0 | Input certInput; |
401 | 0 | if (certInput.Init(cert->derCert.data, cert->derCert.len) != Success) { |
402 | 0 | PR_SetError(SEC_ERROR_BAD_DER, 0); |
403 | 0 | return SECFailure; |
404 | 0 | } |
405 | 0 | Input hostnameInput; |
406 | 0 | Result result = hostnameInput.Init( |
407 | 0 | BitwiseCast<const uint8_t*, const char*>(hostName.BeginReading()), |
408 | 0 | hostName.Length()); |
409 | 0 | if (result != Success) { |
410 | 0 | PR_SetError(SEC_ERROR_INVALID_ARGS, 0); |
411 | 0 | return SECFailure; |
412 | 0 | } |
413 | 0 | // Use a lax policy so as to not generate potentially spurious name |
414 | 0 | // mismatch "hints". |
415 | 0 | BRNameMatchingPolicy nameMatchingPolicy( |
416 | 0 | BRNameMatchingPolicy::Mode::DoNotEnforce); |
417 | 0 | // CheckCertHostname expects that its input represents a certificate that |
418 | 0 | // has already been successfully validated by BuildCertChain. This is |
419 | 0 | // obviously not the case, however, because we're in the error path of |
420 | 0 | // certificate verification. Thus, this is problematic. In the future, it |
421 | 0 | // would be nice to remove this optimistic additional error checking and |
422 | 0 | // simply punt to the front-end, which can more easily (and safely) perform |
423 | 0 | // extra checks to give the user hints as to why verification failed. |
424 | 0 | result = CheckCertHostname(certInput, hostnameInput, nameMatchingPolicy); |
425 | 0 | // Treat malformed name information as a domain mismatch. |
426 | 0 | if (result == Result::ERROR_BAD_DER || |
427 | 0 | result == Result::ERROR_BAD_CERT_DOMAIN) { |
428 | 0 | collectedErrors |= nsICertOverrideService::ERROR_MISMATCH; |
429 | 0 | errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN; |
430 | 0 | } else if (IsFatalError(result)) { |
431 | 0 | // Because its input has not been validated by BuildCertChain, |
432 | 0 | // CheckCertHostname can return an error that is less important than the |
433 | 0 | // original certificate verification error. Only return an error result |
434 | 0 | // from this function if we've encountered a fatal error. |
435 | 0 | PR_SetError(MapResultToPRErrorCode(result), 0); |
436 | 0 | return SECFailure; |
437 | 0 | } |
438 | 0 | } |
439 | 0 | |
440 | 0 | return SECSuccess; |
441 | 0 | } |
442 | | |
443 | | // Helper function to determine if overrides are allowed for this host. |
444 | | // Overrides are not allowed for known HSTS or HPKP hosts. However, an IP |
445 | | // address is never considered an HSTS or HPKP host. |
446 | | nsresult |
447 | | CertErrorRunnable::OverrideAllowedForHost(/*out*/ bool& overrideAllowed) |
448 | 0 | { |
449 | 0 | overrideAllowed = false; |
450 | 0 |
|
451 | 0 | // If this is an IP address, overrides are allowed, because an IP address is |
452 | 0 | // never an HSTS or HPKP host. nsISiteSecurityService takes this into account |
453 | 0 | // already, but the real problem here is that calling NS_NewURI with an IPv6 |
454 | 0 | // address fails. We do this to avoid that. A more comprehensive fix would be |
455 | 0 | // to have Necko provide an nsIURI to PSM and to use that here (and |
456 | 0 | // everywhere). However, that would be a wide-spanning change. |
457 | 0 | const nsACString& hostname = mInfoObject->GetHostName(); |
458 | 0 | if (net_IsValidIPv6Addr(hostname.BeginReading(), hostname.Length())) { |
459 | 0 | overrideAllowed = true; |
460 | 0 | return NS_OK; |
461 | 0 | } |
462 | 0 | |
463 | 0 | // If this is an HTTP Strict Transport Security host or a pinned host and the |
464 | 0 | // certificate is bad, don't allow overrides (RFC 6797 section 12.1, |
465 | 0 | // HPKP draft spec section 2.6). |
466 | 0 | bool strictTransportSecurityEnabled = false; |
467 | 0 | bool hasPinningInformation = false; |
468 | 0 | nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID)); |
469 | 0 | if (!sss) { |
470 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
471 | 0 | ("[%p][%p] couldn't get nsISiteSecurityService to check HSTS/HPKP", |
472 | 0 | mFdForLogging, this)); |
473 | 0 | return NS_ERROR_FAILURE; |
474 | 0 | } |
475 | 0 | nsCOMPtr<nsIURI> uri; |
476 | 0 | nsresult rv = NS_NewURI(getter_AddRefs(uri), |
477 | 0 | NS_LITERAL_CSTRING("https://") + hostname); |
478 | 0 | if (NS_FAILED(rv)) { |
479 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
480 | 0 | ("[%p][%p] Creating new URI failed", mFdForLogging, this)); |
481 | 0 | return rv; |
482 | 0 | } |
483 | 0 | rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HSTS, |
484 | 0 | uri, |
485 | 0 | mProviderFlags, |
486 | 0 | mInfoObject->GetOriginAttributes(), |
487 | 0 | nullptr, |
488 | 0 | nullptr, |
489 | 0 | &strictTransportSecurityEnabled); |
490 | 0 | if (NS_FAILED(rv)) { |
491 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
492 | 0 | ("[%p][%p] checking for HSTS failed", mFdForLogging, this)); |
493 | 0 | return rv; |
494 | 0 | } |
495 | 0 | rv = sss->IsSecureURI(nsISiteSecurityService::HEADER_HPKP, |
496 | 0 | uri, |
497 | 0 | mProviderFlags, |
498 | 0 | mInfoObject->GetOriginAttributes(), |
499 | 0 | nullptr, |
500 | 0 | nullptr, |
501 | 0 | &hasPinningInformation); |
502 | 0 | if (NS_FAILED(rv)) { |
503 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
504 | 0 | ("[%p][%p] checking for HPKP failed", mFdForLogging, this)); |
505 | 0 | return rv; |
506 | 0 | } |
507 | 0 |
|
508 | 0 | overrideAllowed = !strictTransportSecurityEnabled && !hasPinningInformation; |
509 | 0 | return NS_OK; |
510 | 0 | } |
511 | | |
512 | | SSLServerCertVerificationResult* |
513 | | CertErrorRunnable::CheckCertOverrides() |
514 | 0 | { |
515 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p][%p] top of CheckCertOverrides\n", |
516 | 0 | mFdForLogging, this)); |
517 | 0 | // "Use" mFdForLogging in non-PR_LOGGING builds, too, to suppress |
518 | 0 | // clang's -Wunused-private-field build warning for this variable: |
519 | 0 | Unused << mFdForLogging; |
520 | 0 |
|
521 | 0 | if (!NS_IsMainThread()) { |
522 | 0 | NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread"); |
523 | 0 | return new SSLServerCertVerificationResult(mInfoObject, |
524 | 0 | mDefaultErrorCodeToReport); |
525 | 0 | } |
526 | 0 |
|
527 | 0 | int32_t port = mInfoObject->GetPort(); |
528 | 0 |
|
529 | 0 | nsAutoCString hostWithPortString(mInfoObject->GetHostName()); |
530 | 0 | hostWithPortString.Append(':'); |
531 | 0 | hostWithPortString.AppendInt(port); |
532 | 0 |
|
533 | 0 | uint32_t remaining_display_errors = mCollectedErrors; |
534 | 0 |
|
535 | 0 | bool overrideAllowed; |
536 | 0 | if (NS_FAILED(OverrideAllowedForHost(overrideAllowed))) { |
537 | 0 | return new SSLServerCertVerificationResult(mInfoObject, |
538 | 0 | mDefaultErrorCodeToReport); |
539 | 0 | } |
540 | 0 | |
541 | 0 | if (overrideAllowed) { |
542 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
543 | 0 | ("[%p][%p] no HSTS or HPKP - overrides allowed\n", |
544 | 0 | mFdForLogging, this)); |
545 | 0 | nsCOMPtr<nsICertOverrideService> overrideService = |
546 | 0 | do_GetService(NS_CERTOVERRIDE_CONTRACTID); |
547 | 0 | // it is fine to continue without the nsICertOverrideService |
548 | 0 |
|
549 | 0 | uint32_t overrideBits = 0; |
550 | 0 |
|
551 | 0 | if (overrideService) { |
552 | 0 | bool haveOverride; |
553 | 0 | bool isTemporaryOverride; // we don't care |
554 | 0 | const nsACString& hostString(mInfoObject->GetHostName()); |
555 | 0 | nsresult rv = overrideService->HasMatchingOverride(hostString, port, |
556 | 0 | mCert, |
557 | 0 | &overrideBits, |
558 | 0 | &isTemporaryOverride, |
559 | 0 | &haveOverride); |
560 | 0 | if (NS_SUCCEEDED(rv) && haveOverride) { |
561 | 0 | // remove the errors that are already overriden |
562 | 0 | remaining_display_errors &= ~overrideBits; |
563 | 0 | } |
564 | 0 | } |
565 | 0 |
|
566 | 0 | if (!remaining_display_errors) { |
567 | 0 | // This can double- or triple-count one certificate with multiple |
568 | 0 | // different types of errors. Since this is telemetry and we just |
569 | 0 | // want a ballpark answer, we don't care. |
570 | 0 | if (mErrorCodeTrust != 0) { |
571 | 0 | uint32_t probeValue = MapOverridableErrorToProbeValue(mErrorCodeTrust); |
572 | 0 | Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue); |
573 | 0 | } |
574 | 0 | if (mErrorCodeMismatch != 0) { |
575 | 0 | uint32_t probeValue = MapOverridableErrorToProbeValue(mErrorCodeMismatch); |
576 | 0 | Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue); |
577 | 0 | } |
578 | 0 | if (mErrorCodeTime != 0) { |
579 | 0 | uint32_t probeValue = MapOverridableErrorToProbeValue(mErrorCodeTime); |
580 | 0 | Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue); |
581 | 0 | } |
582 | 0 |
|
583 | 0 | // all errors are covered by override rules, so let's accept the cert |
584 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
585 | 0 | ("[%p][%p] All errors covered by override rules\n", |
586 | 0 | mFdForLogging, this)); |
587 | 0 | return new SSLServerCertVerificationResult(mInfoObject, 0); |
588 | 0 | } |
589 | 0 | } else { |
590 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
591 | 0 | ("[%p][%p] HSTS or HPKP - no overrides allowed\n", |
592 | 0 | mFdForLogging, this)); |
593 | 0 | } |
594 | 0 |
|
595 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
596 | 0 | ("[%p][%p] Certificate error was not overridden\n", |
597 | 0 | mFdForLogging, this)); |
598 | 0 |
|
599 | 0 | // Ok, this is a full stop. |
600 | 0 | // First, deliver the technical details of the broken SSL status. |
601 | 0 |
|
602 | 0 | // Try to get a nsIBadCertListener2 implementation from the socket consumer. |
603 | 0 | nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface( |
604 | 0 | NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject)); |
605 | 0 | if (sslSocketControl) { |
606 | 0 | nsCOMPtr<nsIInterfaceRequestor> cb; |
607 | 0 | sslSocketControl->GetNotificationCallbacks(getter_AddRefs(cb)); |
608 | 0 | if (cb) { |
609 | 0 | nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb); |
610 | 0 | if (bcl) { |
611 | 0 | nsIInterfaceRequestor* csi |
612 | 0 | = static_cast<nsIInterfaceRequestor*>(mInfoObject); |
613 | 0 | bool suppressMessage = false; // obsolete, ignored |
614 | 0 | Unused << bcl->NotifyCertProblem(csi, mInfoObject, |
615 | 0 | hostWithPortString, &suppressMessage); |
616 | 0 | } |
617 | 0 | } |
618 | 0 | } |
619 | 0 |
|
620 | 0 | // pick the error code to report by priority |
621 | 0 | PRErrorCode errorCodeToReport = mErrorCodeTrust ? mErrorCodeTrust |
622 | 0 | : mErrorCodeMismatch ? mErrorCodeMismatch |
623 | 0 | : mErrorCodeTime ? mErrorCodeTime |
624 | 0 | : mDefaultErrorCodeToReport; |
625 | 0 |
|
626 | 0 | SSLServerCertVerificationResult* result = |
627 | 0 | new SSLServerCertVerificationResult(mInfoObject, |
628 | 0 | errorCodeToReport, |
629 | 0 | Telemetry::HistogramCount, |
630 | 0 | -1); |
631 | 0 |
|
632 | 0 | return result; |
633 | 0 | } |
634 | | |
635 | | void |
636 | | CertErrorRunnable::RunOnTargetThread() |
637 | 0 | { |
638 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
639 | 0 |
|
640 | 0 | mResult = CheckCertOverrides(); |
641 | 0 |
|
642 | 0 | MOZ_ASSERT(mResult); |
643 | 0 | } |
644 | | |
645 | | // Returns null with the error code (PR_GetError()) set if it does not create |
646 | | // the CertErrorRunnable. |
647 | | CertErrorRunnable* |
648 | | CreateCertErrorRunnable(CertVerifier& certVerifier, |
649 | | PRErrorCode defaultErrorCodeToReport, |
650 | | nsNSSSocketInfo* infoObject, |
651 | | const UniqueCERTCertificate& cert, |
652 | | const void* fdForLogging, |
653 | | uint32_t providerFlags, |
654 | | PRTime now) |
655 | 0 | { |
656 | 0 | MOZ_ASSERT(infoObject); |
657 | 0 | MOZ_ASSERT(cert); |
658 | 0 |
|
659 | 0 | uint32_t probeValue = MapCertErrorToProbeValue(defaultErrorCodeToReport); |
660 | 0 | Telemetry::Accumulate(Telemetry::SSL_CERT_VERIFICATION_ERRORS, probeValue); |
661 | 0 |
|
662 | 0 | uint32_t collected_errors = 0; |
663 | 0 | PRErrorCode errorCodeTrust = 0; |
664 | 0 | PRErrorCode errorCodeMismatch = 0; |
665 | 0 | PRErrorCode errorCodeTime = 0; |
666 | 0 | if (DetermineCertOverrideErrors(cert, infoObject->GetHostName(), now, |
667 | 0 | defaultErrorCodeToReport, collected_errors, |
668 | 0 | errorCodeTrust, errorCodeMismatch, |
669 | 0 | errorCodeTime) != SECSuccess) { |
670 | 0 | // Attempt to enforce that if DetermineCertOverrideErrors failed, |
671 | 0 | // PR_SetError was set with a non-overridable error. This is because if we |
672 | 0 | // return from CreateCertErrorRunnable without calling |
673 | 0 | // infoObject->SetStatusErrorBits, we won't have the required information |
674 | 0 | // to actually add a certificate error override. This results in a broken |
675 | 0 | // UI which is annoying but not a security disaster. |
676 | 0 | MOZ_ASSERT(!ErrorIsOverridable(PR_GetError())); |
677 | 0 | return nullptr; |
678 | 0 | } |
679 | 0 |
|
680 | 0 | RefPtr<nsNSSCertificate> nssCert(nsNSSCertificate::Create(cert.get())); |
681 | 0 | if (!nssCert) { |
682 | 0 | NS_ERROR("nsNSSCertificate::Create failed"); |
683 | 0 | PR_SetError(SEC_ERROR_NO_MEMORY, 0); |
684 | 0 | return nullptr; |
685 | 0 | } |
686 | 0 |
|
687 | 0 | if (!collected_errors) { |
688 | 0 | // This will happen when CERT_*Verify* only returned error(s) that are |
689 | 0 | // not on our whitelist of overridable certificate errors. |
690 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] !collected_errors: %d\n", |
691 | 0 | fdForLogging, static_cast<int>(defaultErrorCodeToReport))); |
692 | 0 | PR_SetError(defaultErrorCodeToReport, 0); |
693 | 0 | return nullptr; |
694 | 0 | } |
695 | 0 |
|
696 | 0 | infoObject->SetStatusErrorBits(nssCert, collected_errors); |
697 | 0 |
|
698 | 0 | return new CertErrorRunnable(fdForLogging, |
699 | 0 | static_cast<nsIX509Cert*>(nssCert.get()), |
700 | 0 | infoObject, defaultErrorCodeToReport, |
701 | 0 | collected_errors, errorCodeTrust, |
702 | 0 | errorCodeMismatch, errorCodeTime, |
703 | 0 | providerFlags); |
704 | 0 | } |
705 | | |
706 | | // When doing async cert processing, we dispatch one of these runnables to the |
707 | | // socket transport service thread, which blocks the socket transport |
708 | | // service thread while it waits for the inner CertErrorRunnable to execute |
709 | | // CheckCertOverrides on the main thread. CheckCertOverrides must block events |
710 | | // on both of these threads because it calls TransportSecurityInfo::GetInterface(), |
711 | | // which may call nsHttpConnection::GetInterface() through |
712 | | // TransportSecurityInfo::mCallbacks. nsHttpConnection::GetInterface must always |
713 | | // execute on the main thread, with the socket transport service thread |
714 | | // blocked. |
715 | | class CertErrorRunnableRunnable : public Runnable |
716 | | { |
717 | | public: |
718 | | explicit CertErrorRunnableRunnable(CertErrorRunnable* certErrorRunnable) |
719 | | : Runnable("psm::CertErrorRunnableRunnable") |
720 | | , mCertErrorRunnable(certErrorRunnable) |
721 | 0 | { |
722 | 0 | } |
723 | | private: |
724 | | NS_IMETHOD Run() override |
725 | 0 | { |
726 | 0 | nsresult rv = mCertErrorRunnable->DispatchToMainThreadAndWait(); |
727 | 0 | // The result must run on the socket transport thread, which we are already |
728 | 0 | // on, so we can just run it directly, instead of dispatching it. |
729 | 0 | if (NS_SUCCEEDED(rv)) { |
730 | 0 | rv = mCertErrorRunnable->mResult ? mCertErrorRunnable->mResult->Run() |
731 | 0 | : NS_ERROR_UNEXPECTED; |
732 | 0 | } |
733 | 0 | return rv; |
734 | 0 | } |
735 | | RefPtr<CertErrorRunnable> mCertErrorRunnable; |
736 | | }; |
737 | | |
738 | | class SSLServerCertVerificationJob : public Runnable |
739 | | { |
740 | | public: |
741 | | // Must be called only on the socket transport thread |
742 | | static SECStatus Dispatch(const RefPtr<SharedCertVerifier>& certVerifier, |
743 | | const void* fdForLogging, |
744 | | nsNSSSocketInfo* infoObject, |
745 | | const UniqueCERTCertificate& serverCert, |
746 | | const UniqueCERTCertList& peerCertChain, |
747 | | const SECItem* stapledOCSPResponse, |
748 | | const SECItem* sctsFromTLSExtension, |
749 | | uint32_t providerFlags, |
750 | | Time time, |
751 | | PRTime prtime); |
752 | | private: |
753 | | NS_DECL_NSIRUNNABLE |
754 | | |
755 | | // Must be called only on the socket transport thread |
756 | | SSLServerCertVerificationJob(const RefPtr<SharedCertVerifier>& certVerifier, |
757 | | const void* fdForLogging, |
758 | | nsNSSSocketInfo* infoObject, |
759 | | const UniqueCERTCertificate& cert, |
760 | | UniqueCERTCertList peerCertChain, |
761 | | const SECItem* stapledOCSPResponse, |
762 | | const SECItem* sctsFromTLSExtension, |
763 | | uint32_t providerFlags, |
764 | | Time time, |
765 | | PRTime prtime); |
766 | | const RefPtr<SharedCertVerifier> mCertVerifier; |
767 | | const void* const mFdForLogging; |
768 | | const RefPtr<nsNSSSocketInfo> mInfoObject; |
769 | | const UniqueCERTCertificate mCert; |
770 | | UniqueCERTCertList mPeerCertChain; |
771 | | const uint32_t mProviderFlags; |
772 | | const Time mTime; |
773 | | const PRTime mPRTime; |
774 | | const TimeStamp mJobStartTime; |
775 | | const UniqueSECItem mStapledOCSPResponse; |
776 | | const UniqueSECItem mSCTsFromTLSExtension; |
777 | | }; |
778 | | |
779 | | SSLServerCertVerificationJob::SSLServerCertVerificationJob( |
780 | | const RefPtr<SharedCertVerifier>& certVerifier, |
781 | | const void* fdForLogging, |
782 | | nsNSSSocketInfo* infoObject, |
783 | | const UniqueCERTCertificate& cert, |
784 | | UniqueCERTCertList peerCertChain, |
785 | | const SECItem* stapledOCSPResponse, |
786 | | const SECItem* sctsFromTLSExtension, |
787 | | uint32_t providerFlags, |
788 | | Time time, |
789 | | PRTime prtime) |
790 | | : Runnable("psm::SSLServerCertVerificationJob") |
791 | | , mCertVerifier(certVerifier) |
792 | | , mFdForLogging(fdForLogging) |
793 | | , mInfoObject(infoObject) |
794 | | , mCert(CERT_DupCertificate(cert.get())) |
795 | | , mPeerCertChain(std::move(peerCertChain)) |
796 | | , mProviderFlags(providerFlags) |
797 | | , mTime(time) |
798 | | , mPRTime(prtime) |
799 | | , mJobStartTime(TimeStamp::Now()) |
800 | | , mStapledOCSPResponse(SECITEM_DupItem(stapledOCSPResponse)) |
801 | | , mSCTsFromTLSExtension(SECITEM_DupItem(sctsFromTLSExtension)) |
802 | 0 | { |
803 | 0 | } |
804 | | |
805 | | // This function assumes that we will only use the SPDY connection coalescing |
806 | | // feature on connections where we have negotiated SPDY using NPN. If we ever |
807 | | // talk SPDY without having negotiated it with SPDY, this code will give wrong |
808 | | // and perhaps unsafe results. |
809 | | // |
810 | | // Returns SECSuccess on the initial handshake of all connections, on |
811 | | // renegotiations for any connections where we did not negotiate SPDY, or on any |
812 | | // SPDY connection where the server's certificate did not change. |
813 | | // |
814 | | // Prohibit changing the server cert only if we negotiated SPDY, |
815 | | // in order to support SPDY's cross-origin connection pooling. |
816 | | static SECStatus |
817 | | BlockServerCertChangeForSpdy(nsNSSSocketInfo* infoObject, |
818 | | const UniqueCERTCertificate& serverCert) |
819 | 0 | { |
820 | 0 | // Get the existing cert. If there isn't one, then there is |
821 | 0 | // no cert change to worry about. |
822 | 0 | nsCOMPtr<nsIX509Cert> cert; |
823 | 0 |
|
824 | 0 | if (!infoObject->IsHandshakeCompleted()) { |
825 | 0 | // first handshake on this connection, not a |
826 | 0 | // renegotiation. |
827 | 0 | return SECSuccess; |
828 | 0 | } |
829 | 0 | |
830 | 0 | infoObject->GetServerCert(getter_AddRefs(cert)); |
831 | 0 | if (!cert) { |
832 | 0 | MOZ_ASSERT_UNREACHABLE( |
833 | 0 | "TransportSecurityInfo must have a cert implementing nsIX509Cert"); |
834 | 0 | PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0); |
835 | 0 | return SECFailure; |
836 | 0 | } |
837 | 0 |
|
838 | 0 | // Filter out sockets that did not neogtiate SPDY via NPN |
839 | 0 | nsAutoCString negotiatedNPN; |
840 | 0 | nsresult rv = infoObject->GetNegotiatedNPN(negotiatedNPN); |
841 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv), "GetNegotiatedNPN() failed during renegotiation"); |
842 | 0 |
|
843 | 0 | if (NS_SUCCEEDED(rv) && !StringBeginsWith(negotiatedNPN, |
844 | 0 | NS_LITERAL_CSTRING("spdy/"))) { |
845 | 0 | return SECSuccess; |
846 | 0 | } |
847 | 0 | // If GetNegotiatedNPN() failed we will assume spdy for safety's safe |
848 | 0 | if (NS_FAILED(rv)) { |
849 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
850 | 0 | ("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call." |
851 | 0 | " Assuming spdy.\n")); |
852 | 0 | } |
853 | 0 |
|
854 | 0 | // Check to see if the cert has actually changed |
855 | 0 | UniqueCERTCertificate c(cert->GetCert()); |
856 | 0 | MOZ_ASSERT(c, "Somehow couldn't get underlying cert from nsIX509Cert"); |
857 | 0 | bool sameCert = CERT_CompareCerts(c.get(), serverCert.get()); |
858 | 0 | if (sameCert) { |
859 | 0 | return SECSuccess; |
860 | 0 | } |
861 | 0 | |
862 | 0 | // Report an error - changed cert is confirmed |
863 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
864 | 0 | ("SPDY Refused to allow new cert during renegotiation\n")); |
865 | 0 | PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0); |
866 | 0 | return SECFailure; |
867 | 0 | } |
868 | | |
869 | | void |
870 | | AccumulateSubjectCommonNameTelemetry(const char* commonName, |
871 | | bool commonNameInSubjectAltNames) |
872 | 0 | { |
873 | 0 | if (!commonName) { |
874 | 0 | // 1 means no common name present |
875 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_2_SUBJECT_COMMON_NAME, 1); |
876 | 0 | } else if (!commonNameInSubjectAltNames) { |
877 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
878 | 0 | ("BR telemetry: common name '%s' not in subject alt. names " |
879 | 0 | "(or the subject alt. names extension is not present)\n", |
880 | 0 | commonName)); |
881 | 0 | // 2 means the common name is not present in subject alt names |
882 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_2_SUBJECT_COMMON_NAME, 2); |
883 | 0 | } else { |
884 | 0 | // 0 means the common name is present in subject alt names |
885 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_2_SUBJECT_COMMON_NAME, 0); |
886 | 0 | } |
887 | 0 | } |
888 | | |
889 | | // Returns true if and only if commonName ends with altName (minus its leading |
890 | | // "*"). altName has already been checked to be of the form "*.<something>". |
891 | | // commonName may be NULL. |
892 | | static bool |
893 | | TryMatchingWildcardSubjectAltName(const char* commonName, |
894 | | const nsACString& altName) |
895 | 0 | { |
896 | 0 | return commonName && |
897 | 0 | StringEndsWith(nsDependentCString(commonName), Substring(altName, 1)); |
898 | 0 | } |
899 | | |
900 | | // Gathers telemetry on Baseline Requirements 9.2.1 (Subject Alternative |
901 | | // Names Extension) and 9.2.2 (Subject Common Name Field). |
902 | | // Specifically: |
903 | | // - whether or not the subject common name field is present |
904 | | // - whether or not the subject alternative names extension is present |
905 | | // - if there is a malformed entry in the subject alt. names extension |
906 | | // - if there is an entry in the subject alt. names extension corresponding |
907 | | // to the subject common name |
908 | | // Telemetry is only gathered for certificates that chain to a trusted root |
909 | | // in Mozilla's Root CA program. |
910 | | // certList consists of a validated certificate chain. The end-entity |
911 | | // certificate is first and the root (trust anchor) is last. |
912 | | void |
913 | | GatherBaselineRequirementsTelemetry(const UniqueCERTCertList& certList) |
914 | 0 | { |
915 | 0 | CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList); |
916 | 0 | CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); |
917 | 0 | MOZ_ASSERT(!(CERT_LIST_END(endEntityNode, certList) || |
918 | 0 | CERT_LIST_END(rootNode, certList))); |
919 | 0 | if (CERT_LIST_END(endEntityNode, certList) || |
920 | 0 | CERT_LIST_END(rootNode, certList)) { |
921 | 0 | return; |
922 | 0 | } |
923 | 0 | CERTCertificate* cert = endEntityNode->cert; |
924 | 0 | MOZ_ASSERT(cert); |
925 | 0 | if (!cert) { |
926 | 0 | return; |
927 | 0 | } |
928 | 0 | UniquePORTString commonName(CERT_GetCommonName(&cert->subject)); |
929 | 0 | // This only applies to certificates issued by authorities in our root |
930 | 0 | // program. |
931 | 0 | CERTCertificate* rootCert = rootNode->cert; |
932 | 0 | MOZ_ASSERT(rootCert); |
933 | 0 | if (!rootCert) { |
934 | 0 | return; |
935 | 0 | } |
936 | 0 | bool isBuiltIn = false; |
937 | 0 | Result result = IsCertBuiltInRoot(rootCert, isBuiltIn); |
938 | 0 | if (result != Success || !isBuiltIn) { |
939 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
940 | 0 | ("BR telemetry: root certificate for '%s' is not a built-in root " |
941 | 0 | "(or IsCertBuiltInRoot failed)\n", commonName.get())); |
942 | 0 | return; |
943 | 0 | } |
944 | 0 | ScopedAutoSECItem altNameExtension; |
945 | 0 | SECStatus rv = CERT_FindCertExtension(cert, SEC_OID_X509_SUBJECT_ALT_NAME, |
946 | 0 | &altNameExtension); |
947 | 0 | if (rv != SECSuccess) { |
948 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
949 | 0 | ("BR telemetry: no subject alt names extension for '%s'\n", |
950 | 0 | commonName.get())); |
951 | 0 | // 1 means there is no subject alt names extension |
952 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_1_SUBJECT_ALT_NAMES, 1); |
953 | 0 | AccumulateSubjectCommonNameTelemetry(commonName.get(), false); |
954 | 0 | return; |
955 | 0 | } |
956 | 0 |
|
957 | 0 | UniquePLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE)); |
958 | 0 | CERTGeneralName* subjectAltNames = |
959 | 0 | CERT_DecodeAltNameExtension(arena.get(), &altNameExtension); |
960 | 0 | if (!subjectAltNames) { |
961 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
962 | 0 | ("BR telemetry: could not decode subject alt names for '%s'\n", |
963 | 0 | commonName.get())); |
964 | 0 | // 2 means the subject alt names extension could not be decoded |
965 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_1_SUBJECT_ALT_NAMES, 2); |
966 | 0 | AccumulateSubjectCommonNameTelemetry(commonName.get(), false); |
967 | 0 | return; |
968 | 0 | } |
969 | 0 |
|
970 | 0 | CERTGeneralName* currentName = subjectAltNames; |
971 | 0 | bool commonNameInSubjectAltNames = false; |
972 | 0 | bool nonDNSNameOrIPAddressPresent = false; |
973 | 0 | bool malformedDNSNameOrIPAddressPresent = false; |
974 | 0 | bool nonFQDNPresent = false; |
975 | 0 | do { |
976 | 0 | nsAutoCString altName; |
977 | 0 | if (currentName->type == certDNSName) { |
978 | 0 | altName.Assign(BitwiseCast<char*, unsigned char*>( |
979 | 0 | currentName->name.other.data), |
980 | 0 | currentName->name.other.len); |
981 | 0 | nsDependentCString altNameWithoutWildcard(altName, 0); |
982 | 0 | if (StringBeginsWith(altNameWithoutWildcard, NS_LITERAL_CSTRING("*."))) { |
983 | 0 | altNameWithoutWildcard.Rebind(altName, 2); |
984 | 0 | commonNameInSubjectAltNames |= |
985 | 0 | TryMatchingWildcardSubjectAltName(commonName.get(), altName); |
986 | 0 | } |
987 | 0 | // net_IsValidHostName appears to return true for valid IP addresses, |
988 | 0 | // which would be invalid for a DNS name. |
989 | 0 | // Note that the net_IsValidHostName check will catch things like |
990 | 0 | // "a.*.example.com". |
991 | 0 | if (!net_IsValidHostName(altNameWithoutWildcard) || |
992 | 0 | net_IsValidIPv4Addr(altName.get(), altName.Length()) || |
993 | 0 | net_IsValidIPv6Addr(altName.get(), altName.Length())) { |
994 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
995 | 0 | ("BR telemetry: DNSName '%s' not valid (for '%s')\n", |
996 | 0 | altName.get(), commonName.get())); |
997 | 0 | malformedDNSNameOrIPAddressPresent = true; |
998 | 0 | } |
999 | 0 | if (!altName.Contains('.')) { |
1000 | 0 | nonFQDNPresent = true; |
1001 | 0 | } |
1002 | 0 | } else if (currentName->type == certIPAddress) { |
1003 | 0 | // According to DNS.h, this includes space for the null-terminator |
1004 | 0 | char buf[net::kNetAddrMaxCStrBufSize] = { 0 }; |
1005 | 0 | PRNetAddr addr; |
1006 | 0 | if (currentName->name.other.len == 4) { |
1007 | 0 | addr.inet.family = PR_AF_INET; |
1008 | 0 | memcpy(&addr.inet.ip, currentName->name.other.data, |
1009 | 0 | currentName->name.other.len); |
1010 | 0 | if (PR_NetAddrToString(&addr, buf, sizeof(buf) - 1) != PR_SUCCESS) { |
1011 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1012 | 0 | ("BR telemetry: IPAddress (v4) not valid (for '%s')\n", |
1013 | 0 | commonName.get())); |
1014 | 0 | malformedDNSNameOrIPAddressPresent = true; |
1015 | 0 | } else { |
1016 | 0 | altName.Assign(buf); |
1017 | 0 | } |
1018 | 0 | } else if (currentName->name.other.len == 16) { |
1019 | 0 | addr.inet.family = PR_AF_INET6; |
1020 | 0 | memcpy(&addr.ipv6.ip, currentName->name.other.data, |
1021 | 0 | currentName->name.other.len); |
1022 | 0 | if (PR_NetAddrToString(&addr, buf, sizeof(buf) - 1) != PR_SUCCESS) { |
1023 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1024 | 0 | ("BR telemetry: IPAddress (v6) not valid (for '%s')\n", |
1025 | 0 | commonName.get())); |
1026 | 0 | malformedDNSNameOrIPAddressPresent = true; |
1027 | 0 | } else { |
1028 | 0 | altName.Assign(buf); |
1029 | 0 | } |
1030 | 0 | } else { |
1031 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1032 | 0 | ("BR telemetry: IPAddress not valid (for '%s')\n", |
1033 | 0 | commonName.get())); |
1034 | 0 | malformedDNSNameOrIPAddressPresent = true; |
1035 | 0 | } |
1036 | 0 | } else { |
1037 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1038 | 0 | ("BR telemetry: non-DNSName, non-IPAddress present for '%s'\n", |
1039 | 0 | commonName.get())); |
1040 | 0 | nonDNSNameOrIPAddressPresent = true; |
1041 | 0 | } |
1042 | 0 | if (commonName && altName.Equals(commonName.get())) { |
1043 | 0 | commonNameInSubjectAltNames = true; |
1044 | 0 | } |
1045 | 0 | currentName = CERT_GetNextGeneralName(currentName); |
1046 | 0 | } while (currentName && currentName != subjectAltNames); |
1047 | 0 |
|
1048 | 0 | if (nonDNSNameOrIPAddressPresent) { |
1049 | 0 | // 3 means there's an entry that isn't an ip address or dns name |
1050 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_1_SUBJECT_ALT_NAMES, 3); |
1051 | 0 | } |
1052 | 0 | if (malformedDNSNameOrIPAddressPresent) { |
1053 | 0 | // 4 means there's a malformed ip address or dns name entry |
1054 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_1_SUBJECT_ALT_NAMES, 4); |
1055 | 0 | } |
1056 | 0 | if (nonFQDNPresent) { |
1057 | 0 | // 5 means there's a DNS name entry with a non-fully-qualified domain name |
1058 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_1_SUBJECT_ALT_NAMES, 5); |
1059 | 0 | } |
1060 | 0 | if (!nonDNSNameOrIPAddressPresent && !malformedDNSNameOrIPAddressPresent && |
1061 | 0 | !nonFQDNPresent) { |
1062 | 0 | // 0 means the extension is acceptable |
1063 | 0 | Telemetry::Accumulate(Telemetry::BR_9_2_1_SUBJECT_ALT_NAMES, 0); |
1064 | 0 | } |
1065 | 0 |
|
1066 | 0 | AccumulateSubjectCommonNameTelemetry(commonName.get(), |
1067 | 0 | commonNameInSubjectAltNames); |
1068 | 0 | } |
1069 | | |
1070 | | // Gather telemetry on whether the end-entity cert for a server has the |
1071 | | // required TLS Server Authentication EKU, or any others |
1072 | | void |
1073 | | GatherEKUTelemetry(const UniqueCERTCertList& certList) |
1074 | 0 | { |
1075 | 0 | CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList); |
1076 | 0 | CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); |
1077 | 0 | MOZ_ASSERT(!(CERT_LIST_END(endEntityNode, certList) || |
1078 | 0 | CERT_LIST_END(rootNode, certList))); |
1079 | 0 | if (CERT_LIST_END(endEntityNode, certList) || |
1080 | 0 | CERT_LIST_END(rootNode, certList)) { |
1081 | 0 | return; |
1082 | 0 | } |
1083 | 0 | CERTCertificate* endEntityCert = endEntityNode->cert; |
1084 | 0 | MOZ_ASSERT(endEntityCert); |
1085 | 0 | if (!endEntityCert) { |
1086 | 0 | return; |
1087 | 0 | } |
1088 | 0 | |
1089 | 0 | // Only log telemetry if the root CA is built-in |
1090 | 0 | CERTCertificate* rootCert = rootNode->cert; |
1091 | 0 | MOZ_ASSERT(rootCert); |
1092 | 0 | if (!rootCert) { |
1093 | 0 | return; |
1094 | 0 | } |
1095 | 0 | bool isBuiltIn = false; |
1096 | 0 | Result rv = IsCertBuiltInRoot(rootCert, isBuiltIn); |
1097 | 0 | if (rv != Success || !isBuiltIn) { |
1098 | 0 | return; |
1099 | 0 | } |
1100 | 0 | |
1101 | 0 | // Find the EKU extension, if present |
1102 | 0 | bool foundEKU = false; |
1103 | 0 | SECOidTag oidTag; |
1104 | 0 | CERTCertExtension* ekuExtension = nullptr; |
1105 | 0 | for (size_t i = 0; endEntityCert->extensions && endEntityCert->extensions[i]; |
1106 | 0 | i++) { |
1107 | 0 | oidTag = SECOID_FindOIDTag(&endEntityCert->extensions[i]->id); |
1108 | 0 | if (oidTag == SEC_OID_X509_EXT_KEY_USAGE) { |
1109 | 0 | foundEKU = true; |
1110 | 0 | ekuExtension = endEntityCert->extensions[i]; |
1111 | 0 | } |
1112 | 0 | } |
1113 | 0 |
|
1114 | 0 | if (!foundEKU) { |
1115 | 0 | Telemetry::Accumulate(Telemetry::SSL_SERVER_AUTH_EKU, 0); |
1116 | 0 | return; |
1117 | 0 | } |
1118 | 0 | |
1119 | 0 | // Parse the EKU extension |
1120 | 0 | UniqueCERTOidSequence ekuSequence( |
1121 | 0 | CERT_DecodeOidSequence(&ekuExtension->value)); |
1122 | 0 | if (!ekuSequence) { |
1123 | 0 | return; |
1124 | 0 | } |
1125 | 0 | |
1126 | 0 | // Search through the available EKUs |
1127 | 0 | bool foundServerAuth = false; |
1128 | 0 | bool foundOther = false; |
1129 | 0 | for (SECItem** oids = ekuSequence->oids; oids && *oids; oids++) { |
1130 | 0 | oidTag = SECOID_FindOIDTag(*oids); |
1131 | 0 | if (oidTag == SEC_OID_EXT_KEY_USAGE_SERVER_AUTH) { |
1132 | 0 | foundServerAuth = true; |
1133 | 0 | } else { |
1134 | 0 | foundOther = true; |
1135 | 0 | } |
1136 | 0 | } |
1137 | 0 |
|
1138 | 0 | // Cases 3 is included only for completeness. It should never |
1139 | 0 | // appear in these statistics, because CheckExtendedKeyUsage() |
1140 | 0 | // should require the EKU extension, if present, to contain the |
1141 | 0 | // value id_kp_serverAuth. |
1142 | 0 | if (foundServerAuth && !foundOther) { |
1143 | 0 | Telemetry::Accumulate(Telemetry::SSL_SERVER_AUTH_EKU, 1); |
1144 | 0 | } else if (foundServerAuth && foundOther) { |
1145 | 0 | Telemetry::Accumulate(Telemetry::SSL_SERVER_AUTH_EKU, 2); |
1146 | 0 | } else if (!foundServerAuth) { |
1147 | 0 | Telemetry::Accumulate(Telemetry::SSL_SERVER_AUTH_EKU, 3); |
1148 | 0 | } |
1149 | 0 | } |
1150 | | |
1151 | | // Gathers telemetry on which CA is the root of a given cert chain. |
1152 | | // If the root is a built-in root, then the telemetry makes a count |
1153 | | // by root. Roots that are not built-in are counted in one bin. |
1154 | | void |
1155 | | GatherRootCATelemetry(const UniqueCERTCertList& certList) |
1156 | 0 | { |
1157 | 0 | CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); |
1158 | 0 | MOZ_ASSERT(rootNode); |
1159 | 0 | if (!rootNode) { |
1160 | 0 | return; |
1161 | 0 | } |
1162 | 0 | MOZ_ASSERT(!CERT_LIST_END(rootNode, certList)); |
1163 | 0 | if (CERT_LIST_END(rootNode, certList)) { |
1164 | 0 | return; |
1165 | 0 | } |
1166 | 0 | CERTCertificate* rootCert = rootNode->cert; |
1167 | 0 | MOZ_ASSERT(rootCert); |
1168 | 0 | if (!rootCert) { |
1169 | 0 | return; |
1170 | 0 | } |
1171 | 0 | AccumulateTelemetryForRootCA(Telemetry::CERT_VALIDATION_SUCCESS_BY_CA, |
1172 | 0 | rootCert); |
1173 | 0 | } |
1174 | | |
1175 | | // These time are appoximate, i.e., doesn't account for leap seconds, etc |
1176 | | const uint64_t ONE_WEEK_IN_SECONDS = (7 * (24 * 60 *60)); |
1177 | | const uint64_t ONE_YEAR_IN_WEEKS = 52; |
1178 | | |
1179 | | // Gathers telemetry on the certificate lifetimes we observe in the wild |
1180 | | void |
1181 | | GatherEndEntityTelemetry(const UniqueCERTCertList& certList) |
1182 | 0 | { |
1183 | 0 | CERTCertListNode* endEntityNode = CERT_LIST_HEAD(certList); |
1184 | 0 | MOZ_ASSERT(endEntityNode && !CERT_LIST_END(endEntityNode, certList)); |
1185 | 0 | if (!endEntityNode || CERT_LIST_END(endEntityNode, certList)) { |
1186 | 0 | return; |
1187 | 0 | } |
1188 | 0 | |
1189 | 0 | CERTCertificate* endEntityCert = endEntityNode->cert; |
1190 | 0 | MOZ_ASSERT(endEntityCert); |
1191 | 0 | if (!endEntityCert) { |
1192 | 0 | return; |
1193 | 0 | } |
1194 | 0 | |
1195 | 0 | PRTime notBefore; |
1196 | 0 | PRTime notAfter; |
1197 | 0 |
|
1198 | 0 | if (CERT_GetCertTimes(endEntityCert, ¬Before, ¬After) != SECSuccess) { |
1199 | 0 | return; |
1200 | 0 | } |
1201 | 0 | |
1202 | 0 | MOZ_ASSERT(notAfter > notBefore); |
1203 | 0 | if (notAfter <= notBefore) { |
1204 | 0 | return; |
1205 | 0 | } |
1206 | 0 | |
1207 | 0 | uint64_t durationInWeeks = (notAfter - notBefore) |
1208 | 0 | / PR_USEC_PER_SEC |
1209 | 0 | / ONE_WEEK_IN_SECONDS; |
1210 | 0 |
|
1211 | 0 | if (durationInWeeks > (2 * ONE_YEAR_IN_WEEKS)) { |
1212 | 0 | durationInWeeks = (2 * ONE_YEAR_IN_WEEKS) + 1; |
1213 | 0 | } |
1214 | 0 |
|
1215 | 0 | Telemetry::Accumulate(Telemetry::SSL_OBSERVED_END_ENTITY_CERTIFICATE_LIFETIME, |
1216 | 0 | durationInWeeks); |
1217 | 0 | } |
1218 | | |
1219 | | // There are various things that we want to measure about certificate |
1220 | | // chains that we accept. This is a single entry point for all of them. |
1221 | | void |
1222 | | GatherSuccessfulValidationTelemetry(const UniqueCERTCertList& certList) |
1223 | 0 | { |
1224 | 0 | GatherBaselineRequirementsTelemetry(certList); |
1225 | 0 | GatherEKUTelemetry(certList); |
1226 | 0 | GatherRootCATelemetry(certList); |
1227 | 0 | GatherEndEntityTelemetry(certList); |
1228 | 0 | } |
1229 | | |
1230 | | void |
1231 | | GatherTelemetryForSingleSCT(const ct::VerifiedSCT& verifiedSct) |
1232 | 0 | { |
1233 | 0 | // See SSL_SCTS_ORIGIN in Histograms.json. |
1234 | 0 | uint32_t origin = 0; |
1235 | 0 | switch (verifiedSct.origin) { |
1236 | 0 | case ct::VerifiedSCT::Origin::Embedded: |
1237 | 0 | origin = 1; |
1238 | 0 | break; |
1239 | 0 | case ct::VerifiedSCT::Origin::TLSExtension: |
1240 | 0 | origin = 2; |
1241 | 0 | break; |
1242 | 0 | case ct::VerifiedSCT::Origin::OCSPResponse: |
1243 | 0 | origin = 3; |
1244 | 0 | break; |
1245 | 0 | default: |
1246 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected VerifiedSCT::Origin type"); |
1247 | 0 | } |
1248 | 0 | Telemetry::Accumulate(Telemetry::SSL_SCTS_ORIGIN, origin); |
1249 | 0 |
|
1250 | 0 | // See SSL_SCTS_VERIFICATION_STATUS in Histograms.json. |
1251 | 0 | uint32_t verificationStatus = 0; |
1252 | 0 | switch (verifiedSct.status) { |
1253 | 0 | case ct::VerifiedSCT::Status::Valid: |
1254 | 0 | verificationStatus = 1; |
1255 | 0 | break; |
1256 | 0 | case ct::VerifiedSCT::Status::UnknownLog: |
1257 | 0 | verificationStatus = 2; |
1258 | 0 | break; |
1259 | 0 | case ct::VerifiedSCT::Status::InvalidSignature: |
1260 | 0 | verificationStatus = 3; |
1261 | 0 | break; |
1262 | 0 | case ct::VerifiedSCT::Status::InvalidTimestamp: |
1263 | 0 | verificationStatus = 4; |
1264 | 0 | break; |
1265 | 0 | case ct::VerifiedSCT::Status::ValidFromDisqualifiedLog: |
1266 | 0 | verificationStatus = 5; |
1267 | 0 | break; |
1268 | 0 | default: |
1269 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected VerifiedSCT::Status type"); |
1270 | 0 | } |
1271 | 0 | Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS, |
1272 | 0 | verificationStatus); |
1273 | 0 | } |
1274 | | |
1275 | | void |
1276 | | GatherCertificateTransparencyTelemetry(const UniqueCERTCertList& certList, |
1277 | | bool isEV, |
1278 | | const CertificateTransparencyInfo& info) |
1279 | 0 | { |
1280 | 0 | if (!info.enabled) { |
1281 | 0 | // No telemetry is gathered when CT is disabled. |
1282 | 0 | return; |
1283 | 0 | } |
1284 | 0 | |
1285 | 0 | for (const ct::VerifiedSCT& sct : info.verifyResult.verifiedScts) { |
1286 | 0 | GatherTelemetryForSingleSCT(sct); |
1287 | 0 | } |
1288 | 0 |
|
1289 | 0 | // Decoding errors are reported to the 0th bucket |
1290 | 0 | // of the SSL_SCTS_VERIFICATION_STATUS enumerated probe. |
1291 | 0 | for (size_t i = 0; i < info.verifyResult.decodingErrors; ++i) { |
1292 | 0 | Telemetry::Accumulate(Telemetry::SSL_SCTS_VERIFICATION_STATUS, 0); |
1293 | 0 | } |
1294 | 0 |
|
1295 | 0 | // Handle the histogram of SCTs counts. |
1296 | 0 | uint32_t sctsCount = |
1297 | 0 | static_cast<uint32_t>(info.verifyResult.verifiedScts.length()); |
1298 | 0 | // Note that sctsCount can also be 0 in case we've received SCT binary data, |
1299 | 0 | // but it failed to parse (e.g. due to unsupported CT protocol version). |
1300 | 0 | Telemetry::Accumulate(Telemetry::SSL_SCTS_PER_CONNECTION, sctsCount); |
1301 | 0 |
|
1302 | 0 | // Report CT Policy compliance of EV certificates. |
1303 | 0 | if (isEV) { |
1304 | 0 | uint32_t evCompliance = 0; |
1305 | 0 | switch (info.policyCompliance) { |
1306 | 0 | case ct::CTPolicyCompliance::Compliant: |
1307 | 0 | evCompliance = 1; |
1308 | 0 | break; |
1309 | 0 | case ct::CTPolicyCompliance::NotEnoughScts: |
1310 | 0 | evCompliance = 2; |
1311 | 0 | break; |
1312 | 0 | case ct::CTPolicyCompliance::NotDiverseScts: |
1313 | 0 | evCompliance = 3; |
1314 | 0 | break; |
1315 | 0 | case ct::CTPolicyCompliance::Unknown: |
1316 | 0 | default: |
1317 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected CTPolicyCompliance type"); |
1318 | 0 | } |
1319 | 0 | Telemetry::Accumulate(Telemetry::SSL_CT_POLICY_COMPLIANCE_OF_EV_CERTS, |
1320 | 0 | evCompliance); |
1321 | 0 | } |
1322 | 0 |
|
1323 | 0 | // Get the root cert. |
1324 | 0 | CERTCertListNode* rootNode = CERT_LIST_TAIL(certList); |
1325 | 0 | MOZ_ASSERT(rootNode); |
1326 | 0 | if (!rootNode) { |
1327 | 0 | return; |
1328 | 0 | } |
1329 | 0 | MOZ_ASSERT(!CERT_LIST_END(rootNode, certList)); |
1330 | 0 | if (CERT_LIST_END(rootNode, certList)) { |
1331 | 0 | return; |
1332 | 0 | } |
1333 | 0 | CERTCertificate* rootCert = rootNode->cert; |
1334 | 0 | MOZ_ASSERT(rootCert); |
1335 | 0 | if (!rootCert) { |
1336 | 0 | return; |
1337 | 0 | } |
1338 | 0 | |
1339 | 0 | // Report CT Policy compliance by CA. |
1340 | 0 | switch (info.policyCompliance) { |
1341 | 0 | case ct::CTPolicyCompliance::Compliant: |
1342 | 0 | AccumulateTelemetryForRootCA( |
1343 | 0 | Telemetry::SSL_CT_POLICY_COMPLIANT_CONNECTIONS_BY_CA, rootCert); |
1344 | 0 | break; |
1345 | 0 | case ct::CTPolicyCompliance::NotEnoughScts: |
1346 | 0 | case ct::CTPolicyCompliance::NotDiverseScts: |
1347 | 0 | AccumulateTelemetryForRootCA( |
1348 | 0 | Telemetry::SSL_CT_POLICY_NON_COMPLIANT_CONNECTIONS_BY_CA, rootCert); |
1349 | 0 | break; |
1350 | 0 | case ct::CTPolicyCompliance::Unknown: |
1351 | 0 | default: |
1352 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected CTPolicyCompliance type"); |
1353 | 0 | } |
1354 | 0 | } |
1355 | | |
1356 | | // Note: Takes ownership of |peerCertChain| if SECSuccess is not returned. |
1357 | | SECStatus |
1358 | | AuthCertificate(CertVerifier& certVerifier, |
1359 | | nsNSSSocketInfo* infoObject, |
1360 | | const UniqueCERTCertificate& cert, |
1361 | | UniqueCERTCertList& peerCertChain, |
1362 | | const SECItem* stapledOCSPResponse, |
1363 | | const SECItem* sctsFromTLSExtension, |
1364 | | uint32_t providerFlags, |
1365 | | Time time) |
1366 | 0 | { |
1367 | 0 | MOZ_ASSERT(infoObject); |
1368 | 0 | MOZ_ASSERT(cert); |
1369 | 0 |
|
1370 | 0 | // We want to avoid storing any intermediate cert information when browsing |
1371 | 0 | // in private, transient contexts. |
1372 | 0 | bool saveIntermediates = |
1373 | 0 | !(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE); |
1374 | 0 |
|
1375 | 0 | SECOidTag evOidPolicy; |
1376 | 0 | UniqueCERTCertList builtCertChain; |
1377 | 0 | CertVerifier::OCSPStaplingStatus ocspStaplingStatus = |
1378 | 0 | CertVerifier::OCSP_STAPLING_NEVER_CHECKED; |
1379 | 0 | KeySizeStatus keySizeStatus = KeySizeStatus::NeverChecked; |
1380 | 0 | SHA1ModeResult sha1ModeResult = SHA1ModeResult::NeverChecked; |
1381 | 0 | PinningTelemetryInfo pinningTelemetryInfo; |
1382 | 0 | CertificateTransparencyInfo certificateTransparencyInfo; |
1383 | 0 |
|
1384 | 0 | int flags = 0; |
1385 | 0 | if (!infoObject->SharedState().IsOCSPStaplingEnabled() || |
1386 | 0 | !infoObject->SharedState().IsOCSPMustStapleEnabled()) { |
1387 | 0 | flags |= CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST; |
1388 | 0 | } |
1389 | 0 |
|
1390 | 0 | Result rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse, |
1391 | 0 | sctsFromTLSExtension, time, |
1392 | 0 | infoObject, |
1393 | 0 | infoObject->GetHostName(), |
1394 | 0 | builtCertChain, |
1395 | 0 | saveIntermediates, flags, |
1396 | 0 | infoObject-> |
1397 | 0 | GetOriginAttributes(), |
1398 | 0 | &evOidPolicy, |
1399 | 0 | &ocspStaplingStatus, |
1400 | 0 | &keySizeStatus, &sha1ModeResult, |
1401 | 0 | &pinningTelemetryInfo, |
1402 | 0 | &certificateTransparencyInfo); |
1403 | 0 |
|
1404 | 0 | uint32_t evStatus = (rv != Success) ? 0 // 0 = Failure |
1405 | 0 | : (evOidPolicy == SEC_OID_UNKNOWN) ? 1 // 1 = DV |
1406 | 0 | : 2; // 2 = EV |
1407 | 0 | Telemetry::Accumulate(Telemetry::CERT_EV_STATUS, evStatus); |
1408 | 0 |
|
1409 | 0 | if (ocspStaplingStatus != CertVerifier::OCSP_STAPLING_NEVER_CHECKED) { |
1410 | 0 | Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, ocspStaplingStatus); |
1411 | 0 | } |
1412 | 0 | if (keySizeStatus != KeySizeStatus::NeverChecked) { |
1413 | 0 | Telemetry::Accumulate(Telemetry::CERT_CHAIN_KEY_SIZE_STATUS, |
1414 | 0 | static_cast<uint32_t>(keySizeStatus)); |
1415 | 0 | } |
1416 | 0 | if (sha1ModeResult != SHA1ModeResult::NeverChecked) { |
1417 | 0 | Telemetry::Accumulate(Telemetry::CERT_CHAIN_SHA1_POLICY_STATUS, |
1418 | 0 | static_cast<uint32_t>(sha1ModeResult)); |
1419 | 0 | } |
1420 | 0 |
|
1421 | 0 | if (pinningTelemetryInfo.accumulateForRoot) { |
1422 | 0 | Telemetry::Accumulate(Telemetry::CERT_PINNING_FAILURES_BY_CA, |
1423 | 0 | pinningTelemetryInfo.rootBucket); |
1424 | 0 | } |
1425 | 0 |
|
1426 | 0 | if (pinningTelemetryInfo.accumulateResult) { |
1427 | 0 | MOZ_ASSERT(pinningTelemetryInfo.certPinningResultHistogram.isSome()); |
1428 | 0 | Telemetry::Accumulate(pinningTelemetryInfo.certPinningResultHistogram.value(), |
1429 | 0 | pinningTelemetryInfo.certPinningResultBucket); |
1430 | 0 | } |
1431 | 0 |
|
1432 | 0 | if (rv == Success) { |
1433 | 0 | // Certificate verification succeeded. Delete any potential record of |
1434 | 0 | // certificate error bits. |
1435 | 0 | RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject, |
1436 | 0 | SECSuccess); |
1437 | 0 | GatherSuccessfulValidationTelemetry(builtCertChain); |
1438 | 0 | GatherCertificateTransparencyTelemetry(builtCertChain, |
1439 | 0 | /*isEV*/ evOidPolicy != SEC_OID_UNKNOWN, |
1440 | 0 | certificateTransparencyInfo); |
1441 | 0 |
|
1442 | 0 | EVStatus evStatus; |
1443 | 0 | if (evOidPolicy == SEC_OID_UNKNOWN) { |
1444 | 0 | evStatus = EVStatus::NotEV; |
1445 | 0 | } else { |
1446 | 0 | evStatus = EVStatus::EV; |
1447 | 0 | } |
1448 | 0 |
|
1449 | 0 | RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(cert.get()); |
1450 | 0 | infoObject->SetServerCert(nsc, evStatus); |
1451 | 0 |
|
1452 | 0 | infoObject->SetSucceededCertChain(std::move(builtCertChain)); |
1453 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1454 | 0 | ("AuthCertificate setting NEW cert %p", nsc.get())); |
1455 | 0 |
|
1456 | 0 | infoObject->SetCertificateTransparencyInfo(certificateTransparencyInfo); |
1457 | 0 | } |
1458 | 0 |
|
1459 | 0 | if (rv != Success) { |
1460 | 0 | // Certificate validation failed; store the peer certificate chain on |
1461 | 0 | // infoObject so it can be used for error reporting. |
1462 | 0 | infoObject->SetFailedCertChain(std::move(peerCertChain)); |
1463 | 0 | PR_SetError(MapResultToPRErrorCode(rv), 0); |
1464 | 0 | } |
1465 | 0 |
|
1466 | 0 | return rv == Success ? SECSuccess : SECFailure; |
1467 | 0 | } |
1468 | | |
1469 | | /*static*/ SECStatus |
1470 | | SSLServerCertVerificationJob::Dispatch( |
1471 | | const RefPtr<SharedCertVerifier>& certVerifier, |
1472 | | const void* fdForLogging, |
1473 | | nsNSSSocketInfo* infoObject, |
1474 | | const UniqueCERTCertificate& serverCert, |
1475 | | const UniqueCERTCertList& peerCertChain, |
1476 | | const SECItem* stapledOCSPResponse, |
1477 | | const SECItem* sctsFromTLSExtension, |
1478 | | uint32_t providerFlags, |
1479 | | Time time, |
1480 | | PRTime prtime) |
1481 | 0 | { |
1482 | 0 | // Runs on the socket transport thread |
1483 | 0 | if (!certVerifier || !infoObject || !serverCert) { |
1484 | 0 | NS_ERROR("Invalid parameters for SSL server cert validation"); |
1485 | 0 | PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); |
1486 | 0 | return SECFailure; |
1487 | 0 | } |
1488 | 0 |
|
1489 | 0 | if (!gCertVerificationThreadPool) { |
1490 | 0 | PR_SetError(PR_INVALID_STATE_ERROR, 0); |
1491 | 0 | return SECFailure; |
1492 | 0 | } |
1493 | 0 | |
1494 | 0 | // Copy the certificate list so the runnable can take ownership of it in the |
1495 | 0 | // constructor. |
1496 | 0 | UniqueCERTCertList peerCertChainCopy = |
1497 | 0 | nsNSSCertList::DupCertList(peerCertChain); |
1498 | 0 | if (!peerCertChainCopy) { |
1499 | 0 | PR_SetError(SEC_ERROR_NO_MEMORY, 0); |
1500 | 0 | return SECFailure; |
1501 | 0 | } |
1502 | 0 | |
1503 | 0 | RefPtr<SSLServerCertVerificationJob> job( |
1504 | 0 | new SSLServerCertVerificationJob(certVerifier, fdForLogging, infoObject, |
1505 | 0 | serverCert, std::move(peerCertChainCopy), |
1506 | 0 | stapledOCSPResponse, sctsFromTLSExtension, |
1507 | 0 | providerFlags, time, prtime)); |
1508 | 0 |
|
1509 | 0 | nsresult nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL); |
1510 | 0 | if (NS_FAILED(nrv)) { |
1511 | 0 | // We can't call SetCertVerificationResult here to change |
1512 | 0 | // mCertVerificationState because SetCertVerificationResult will call |
1513 | 0 | // libssl functions that acquire SSL locks that are already being held at |
1514 | 0 | // this point. However, we can set an error with PR_SetError and return |
1515 | 0 | // SECFailure, and the correct thing will happen (the error will be |
1516 | 0 | // propagated and this connection will be terminated). |
1517 | 0 | PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY |
1518 | 0 | ? PR_OUT_OF_MEMORY_ERROR |
1519 | 0 | : PR_INVALID_STATE_ERROR; |
1520 | 0 | PR_SetError(error, 0); |
1521 | 0 | return SECFailure; |
1522 | 0 | } |
1523 | 0 | |
1524 | 0 | PR_SetError(PR_WOULD_BLOCK_ERROR, 0); |
1525 | 0 | return SECWouldBlock; |
1526 | 0 | } |
1527 | | |
1528 | | NS_IMETHODIMP |
1529 | | SSLServerCertVerificationJob::Run() |
1530 | 0 | { |
1531 | 0 | // Runs on a cert verification thread |
1532 | 0 |
|
1533 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1534 | 0 | ("[%p] SSLServerCertVerificationJob::Run\n", mInfoObject.get())); |
1535 | 0 |
|
1536 | 0 | PRErrorCode error; |
1537 | 0 |
|
1538 | 0 | Telemetry::HistogramID successTelemetry |
1539 | 0 | = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_MOZILLAPKIX; |
1540 | 0 | Telemetry::HistogramID failureTelemetry |
1541 | 0 | = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_MOZILLAPKIX; |
1542 | 0 |
|
1543 | 0 | // Reset the error code here so we can detect if AuthCertificate fails to |
1544 | 0 | // set the error code if/when it fails. |
1545 | 0 | PR_SetError(0, 0); |
1546 | 0 | SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert, |
1547 | 0 | mPeerCertChain, mStapledOCSPResponse.get(), |
1548 | 0 | mSCTsFromTLSExtension.get(), |
1549 | 0 | mProviderFlags, mTime); |
1550 | 0 | MOZ_ASSERT((mPeerCertChain && rv == SECSuccess) || |
1551 | 0 | (!mPeerCertChain && rv != SECSuccess), |
1552 | 0 | "AuthCertificate() should take ownership of chain on failure"); |
1553 | 0 | if (rv == SECSuccess) { |
1554 | 0 | uint32_t interval = (uint32_t) ((TimeStamp::Now() - mJobStartTime).ToMilliseconds()); |
1555 | 0 | RefPtr<SSLServerCertVerificationResult> restart( |
1556 | 0 | new SSLServerCertVerificationResult(mInfoObject, 0, |
1557 | 0 | successTelemetry, interval)); |
1558 | 0 | restart->Dispatch(); |
1559 | 0 | Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1); |
1560 | 0 | return NS_OK; |
1561 | 0 | } |
1562 | 0 | |
1563 | 0 | // Note: the interval is not calculated once as PR_GetError MUST be called |
1564 | 0 | // before any other function call |
1565 | 0 | error = PR_GetError(); |
1566 | 0 |
|
1567 | 0 | TimeStamp now = TimeStamp::Now(); |
1568 | 0 | Telemetry::AccumulateTimeDelta(failureTelemetry, mJobStartTime, now); |
1569 | 0 |
|
1570 | 0 | if (error != 0) { |
1571 | 0 | RefPtr<CertErrorRunnable> runnable( |
1572 | 0 | CreateCertErrorRunnable(*mCertVerifier, error, mInfoObject, mCert, |
1573 | 0 | mFdForLogging, mProviderFlags, mPRTime)); |
1574 | 0 | if (!runnable) { |
1575 | 0 | // CreateCertErrorRunnable set a new error code |
1576 | 0 | error = PR_GetError(); |
1577 | 0 | } else { |
1578 | 0 | // We must block the the socket transport service thread while the |
1579 | 0 | // main thread executes the CertErrorRunnable. The CertErrorRunnable |
1580 | 0 | // will dispatch the result asynchronously, so we don't have to block |
1581 | 0 | // this thread waiting for it. |
1582 | 0 |
|
1583 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1584 | 0 | ("[%p][%p] Before dispatching CertErrorRunnable\n", |
1585 | 0 | mFdForLogging, runnable.get())); |
1586 | 0 |
|
1587 | 0 | nsresult nrv; |
1588 | 0 | nsCOMPtr<nsIEventTarget> stsTarget |
1589 | 0 | = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv); |
1590 | 0 | if (NS_SUCCEEDED(nrv)) { |
1591 | 0 | nrv = stsTarget->Dispatch(new CertErrorRunnableRunnable(runnable), |
1592 | 0 | NS_DISPATCH_NORMAL); |
1593 | 0 | } |
1594 | 0 | if (NS_SUCCEEDED(nrv)) { |
1595 | 0 | return NS_OK; |
1596 | 0 | } |
1597 | 0 | |
1598 | 0 | NS_ERROR("Failed to dispatch CertErrorRunnable"); |
1599 | 0 | error = PR_INVALID_STATE_ERROR; |
1600 | 0 | } |
1601 | 0 | } |
1602 | 0 |
|
1603 | 0 | if (error == 0) { |
1604 | 0 | MOZ_ASSERT_UNREACHABLE("No error set during certificate validation failure"); |
1605 | 0 | error = PR_INVALID_STATE_ERROR; |
1606 | 0 | } |
1607 | 0 |
|
1608 | 0 | RefPtr<SSLServerCertVerificationResult> failure( |
1609 | 0 | new SSLServerCertVerificationResult(mInfoObject, error)); |
1610 | 0 | failure->Dispatch(); |
1611 | 0 | return NS_OK; |
1612 | 0 | } |
1613 | | |
1614 | | } // unnamed namespace |
1615 | | |
1616 | | // Extracts whatever information we need out of fd (using SSL_*) and passes it |
1617 | | // to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should |
1618 | | // never do anything with fd except logging. |
1619 | | SECStatus |
1620 | | AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer) |
1621 | 0 | { |
1622 | 0 | RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier()); |
1623 | 0 | if (!certVerifier) { |
1624 | 0 | PR_SetError(SEC_ERROR_NOT_INITIALIZED, 0); |
1625 | 0 | return SECFailure; |
1626 | 0 | } |
1627 | 0 | |
1628 | 0 | // Runs on the socket transport thread |
1629 | 0 | |
1630 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1631 | 0 | ("[%p] starting AuthCertificateHook\n", fd)); |
1632 | 0 |
|
1633 | 0 | // Modern libssl always passes PR_TRUE for checkSig, and we have no means of |
1634 | 0 | // doing verification without checking signatures. |
1635 | 0 | MOZ_ASSERT(checkSig, "AuthCertificateHook: checkSig unexpectedly false"); |
1636 | 0 |
|
1637 | 0 | // PSM never causes libssl to call this function with PR_TRUE for isServer, |
1638 | 0 | // and many things in PSM assume that we are a client. |
1639 | 0 | MOZ_ASSERT(!isServer, "AuthCertificateHook: isServer unexpectedly true"); |
1640 | 0 |
|
1641 | 0 | nsNSSSocketInfo* socketInfo = static_cast<nsNSSSocketInfo*>(arg); |
1642 | 0 |
|
1643 | 0 | UniqueCERTCertificate serverCert(SSL_PeerCertificate(fd)); |
1644 | 0 |
|
1645 | 0 | if (!checkSig || isServer || !socketInfo || !serverCert) { |
1646 | 0 | PR_SetError(PR_INVALID_STATE_ERROR, 0); |
1647 | 0 | return SECFailure; |
1648 | 0 | } |
1649 | 0 | |
1650 | 0 | // Get the peer certificate chain for error reporting |
1651 | 0 | UniqueCERTCertList peerCertChain(SSL_PeerCertificateChain(fd)); |
1652 | 0 | if (!peerCertChain) { |
1653 | 0 | PR_SetError(PR_INVALID_STATE_ERROR, 0); |
1654 | 0 | return SECFailure; |
1655 | 0 | } |
1656 | 0 | |
1657 | 0 | socketInfo->SetFullHandshake(); |
1658 | 0 |
|
1659 | 0 | Time now(Now()); |
1660 | 0 | PRTime prnow(PR_Now()); |
1661 | 0 |
|
1662 | 0 | if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess) |
1663 | 0 | return SECFailure; |
1664 | 0 | |
1665 | 0 | nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface( |
1666 | 0 | NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, socketInfo)); |
1667 | 0 | if (sslSocketControl && sslSocketControl->GetBypassAuthentication()) { |
1668 | 0 | MOZ_LOG(gPIPNSSLog, LogLevel::Debug, |
1669 | 0 | ("[%p] Bypass Auth in AuthCertificateHook\n", fd)); |
1670 | 0 | return SECSuccess; |
1671 | 0 | } |
1672 | 0 |
|
1673 | 0 | bool onSTSThread; |
1674 | 0 | nsresult nrv; |
1675 | 0 | nsCOMPtr<nsIEventTarget> sts |
1676 | 0 | = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv); |
1677 | 0 | if (NS_SUCCEEDED(nrv)) { |
1678 | 0 | nrv = sts->IsOnCurrentThread(&onSTSThread); |
1679 | 0 | } |
1680 | 0 |
|
1681 | 0 | if (NS_FAILED(nrv)) { |
1682 | 0 | NS_ERROR("Could not get STS service or IsOnCurrentThread failed"); |
1683 | 0 | PR_SetError(PR_UNKNOWN_ERROR, 0); |
1684 | 0 | return SECFailure; |
1685 | 0 | } |
1686 | 0 |
|
1687 | 0 | // SSL_PeerStapledOCSPResponses will never return a non-empty response if |
1688 | 0 | // OCSP stapling wasn't enabled because libssl wouldn't have let the server |
1689 | 0 | // return a stapled OCSP response. |
1690 | 0 | // We don't own these pointers. |
1691 | 0 | const SECItemArray* csa = SSL_PeerStapledOCSPResponses(fd); |
1692 | 0 | SECItem* stapledOCSPResponse = nullptr; |
1693 | 0 | // we currently only support single stapled responses |
1694 | 0 | if (csa && csa->len == 1) { |
1695 | 0 | stapledOCSPResponse = &csa->items[0]; |
1696 | 0 | } |
1697 | 0 |
|
1698 | 0 | const SECItem* sctsFromTLSExtension = SSL_PeerSignedCertTimestamps(fd); |
1699 | 0 | if (sctsFromTLSExtension && sctsFromTLSExtension->len == 0) { |
1700 | 0 | // SSL_PeerSignedCertTimestamps returns null on error and empty item |
1701 | 0 | // when no extension was returned by the server. We always use null when |
1702 | 0 | // no extension was received (for whatever reason), ignoring errors. |
1703 | 0 | sctsFromTLSExtension = nullptr; |
1704 | 0 | } |
1705 | 0 |
|
1706 | 0 | uint32_t providerFlags = 0; |
1707 | 0 | socketInfo->GetProviderFlags(&providerFlags); |
1708 | 0 |
|
1709 | 0 | if (onSTSThread) { |
1710 | 0 |
|
1711 | 0 | // We *must* do certificate verification on a background thread because |
1712 | 0 | // we need the socket transport thread to be free for our OCSP requests, |
1713 | 0 | // and we *want* to do certificate verification on a background thread |
1714 | 0 | // because of the performance benefits of doing so. |
1715 | 0 | socketInfo->SetCertVerificationWaiting(); |
1716 | 0 | SECStatus rv = SSLServerCertVerificationJob::Dispatch( |
1717 | 0 | certVerifier, static_cast<const void*>(fd), socketInfo, |
1718 | 0 | serverCert, peerCertChain, stapledOCSPResponse, |
1719 | 0 | sctsFromTLSExtension, providerFlags, now, prnow); |
1720 | 0 | return rv; |
1721 | 0 | } |
1722 | 0 | |
1723 | 0 | // We can't do certificate verification on a background thread, because the |
1724 | 0 | // thread doing the network I/O may not interrupt its network I/O on receipt |
1725 | 0 | // of our SSLServerCertVerificationResult event, and/or it might not even be |
1726 | 0 | // a non-blocking socket. |
1727 | 0 | |
1728 | 0 | SECStatus rv = AuthCertificate(*certVerifier, socketInfo, serverCert, |
1729 | 0 | peerCertChain, stapledOCSPResponse, |
1730 | 0 | sctsFromTLSExtension, providerFlags, now); |
1731 | 0 | MOZ_ASSERT((peerCertChain && rv == SECSuccess) || |
1732 | 0 | (!peerCertChain && rv != SECSuccess), |
1733 | 0 | "AuthCertificate() should take ownership of chain on failure"); |
1734 | 0 | if (rv == SECSuccess) { |
1735 | 0 | Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1); |
1736 | 0 | return SECSuccess; |
1737 | 0 | } |
1738 | 0 | |
1739 | 0 | PRErrorCode error = PR_GetError(); |
1740 | 0 | if (error != 0) { |
1741 | 0 | RefPtr<CertErrorRunnable> runnable( |
1742 | 0 | CreateCertErrorRunnable(*certVerifier, error, socketInfo, serverCert, |
1743 | 0 | static_cast<const void*>(fd), providerFlags, |
1744 | 0 | prnow)); |
1745 | 0 | if (!runnable) { |
1746 | 0 | // CreateCertErrorRunnable sets a new error code when it fails |
1747 | 0 | error = PR_GetError(); |
1748 | 0 | } else { |
1749 | 0 | // We have to return SECSuccess or SECFailure based on the result of the |
1750 | 0 | // override processing, so we must block this thread waiting for it. The |
1751 | 0 | // CertErrorRunnable will NOT dispatch the result at all, since we passed |
1752 | 0 | // false for CreateCertErrorRunnable's async parameter |
1753 | 0 | nrv = runnable->DispatchToMainThreadAndWait(); |
1754 | 0 | if (NS_FAILED(nrv)) { |
1755 | 0 | NS_ERROR("Failed to dispatch CertErrorRunnable"); |
1756 | 0 | PR_SetError(PR_INVALID_STATE_ERROR, 0); |
1757 | 0 | return SECFailure; |
1758 | 0 | } |
1759 | 0 |
|
1760 | 0 | if (!runnable->mResult) { |
1761 | 0 | NS_ERROR("CertErrorRunnable did not set result"); |
1762 | 0 | PR_SetError(PR_INVALID_STATE_ERROR, 0); |
1763 | 0 | return SECFailure; |
1764 | 0 | } |
1765 | 0 |
|
1766 | 0 | if (runnable->mResult->mErrorCode == 0) { |
1767 | 0 | return SECSuccess; // cert error override occurred. |
1768 | 0 | } |
1769 | 0 | |
1770 | 0 | socketInfo->SetCanceled(runnable->mResult->mErrorCode); |
1771 | 0 | error = runnable->mResult->mErrorCode; |
1772 | 0 | } |
1773 | 0 | } |
1774 | 0 |
|
1775 | 0 | if (error == 0) { |
1776 | 0 | NS_ERROR("error code not set"); |
1777 | 0 | error = PR_UNKNOWN_ERROR; |
1778 | 0 | } |
1779 | 0 |
|
1780 | 0 | PR_SetError(error, 0); |
1781 | 0 | return SECFailure; |
1782 | 0 | } |
1783 | | |
1784 | | SSLServerCertVerificationResult::SSLServerCertVerificationResult( |
1785 | | nsNSSSocketInfo* infoObject, |
1786 | | PRErrorCode errorCode, |
1787 | | Telemetry::HistogramID telemetryID, |
1788 | | uint32_t telemetryValue) |
1789 | | : Runnable("psm::SSLServerCertVerificationResult") |
1790 | | , mInfoObject(infoObject) |
1791 | | , mErrorCode(errorCode) |
1792 | | , mTelemetryID(telemetryID) |
1793 | | , mTelemetryValue(telemetryValue) |
1794 | 0 | { |
1795 | 0 | // We accumulate telemetry for (only) successful validations on the main thread |
1796 | 0 | // to avoid adversely affecting performance by acquiring the mutex that we use |
1797 | 0 | // when accumulating the telemetry for unsuccessful validations. Unsuccessful |
1798 | 0 | // validations times are accumulated elsewhere. |
1799 | 0 | MOZ_ASSERT(telemetryID == Telemetry::HistogramCount || errorCode == 0); |
1800 | 0 | } |
1801 | | |
1802 | | void |
1803 | | SSLServerCertVerificationResult::Dispatch() |
1804 | 0 | { |
1805 | 0 | nsresult rv; |
1806 | 0 | nsCOMPtr<nsIEventTarget> stsTarget |
1807 | 0 | = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv); |
1808 | 0 | MOZ_ASSERT(stsTarget, |
1809 | 0 | "Failed to get socket transport service event target"); |
1810 | 0 | rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL); |
1811 | 0 | MOZ_ASSERT(NS_SUCCEEDED(rv), |
1812 | 0 | "Failed to dispatch SSLServerCertVerificationResult"); |
1813 | 0 | } |
1814 | | |
1815 | | NS_IMETHODIMP |
1816 | | SSLServerCertVerificationResult::Run() |
1817 | 0 | { |
1818 | 0 | // TODO: Assert that we're on the socket transport thread |
1819 | 0 | if (mTelemetryID != Telemetry::HistogramCount) { |
1820 | 0 | Telemetry::Accumulate(mTelemetryID, mTelemetryValue); |
1821 | 0 | } |
1822 | 0 | mInfoObject->SetCertVerificationResult(mErrorCode); |
1823 | 0 | return NS_OK; |
1824 | 0 | } |
1825 | | |
1826 | | } } // namespace mozilla::psm |