bug 914034 - cache stapled OCSP responses on the cert verification thread r=briansmit...
[gecko.git] / security / manager / ssl / src / SSLServerCertVerification.cpp
blob8a67160e83abdc4002a3e77e9072c0cc1399ac52
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * For connections that are not processed on the socket transport thread, we do
9 * NOT use the async logic described below. Instead, we authenticate the
10 * certificate on the thread that the connection's I/O happens on,
11 * synchronously. This allows us to do certificate verification for blocking
12 * (not non-blocking) sockets and sockets that have their I/O processed on a
13 * thread other than the socket transport service thread. Also, we DO NOT
14 * support blocking sockets on the socket transport service thread at all.
16 * During certificate authentication, we call CERT_PKIXVerifyCert or
17 * CERT_VerifyCert. These functions may make zero or more HTTP requests
18 * for OCSP responses, CRLs, intermediate certificates, etc. Our fetching logic
19 * for these requests processes them on the socket transport service thread.
21 * If the connection for which we are verifying the certificate is happening
22 * on the socket transport thread (the usually case, at least for HTTP), then
23 * if our cert auth hook were to call the CERT_*Verify* functions directly,
24 * there would be a deadlock: The CERT_*Verify* function would cause an event
25 * to be asynchronously posted to the socket transport thread, and then it
26 * would block the socket transport thread waiting to be notified of the HTTP
27 * response. However, the HTTP request would never actually be processed
28 * because the socket transport thread would be blocked and so it wouldn't be
29 * able process HTTP requests. (i.e. Deadlock.)
31 * Consequently, when we are asked to verify a certificate on the socket
32 * transport service thread, we must always call the CERT_*Verify* cert
33 * functions on another thread. To accomplish this, our auth cert hook
34 * dispatches a SSLServerCertVerificationJob to a pool of background threads,
35 * and then immediatley return SECWouldBlock to libssl. These jobs are where
36 * the CERT_*Verify* functions are actually called.
38 * When our auth cert hook returns SECWouldBlock, libssl will carry on the
39 * handshake while we validate the certificate. This will free up the socket
40 * transport thread so that HTTP requests--in particular, the OCSP/CRL/cert
41 * requests needed for cert verification as mentioned above--can be processed.
43 * Once the CERT_*Verify* function returns, the cert verification job
44 * dispatches a SSLServerCertVerificationResult to the socket transport thread;
45 * the SSLServerCertVerificationResult will notify libssl that the certificate
46 * authentication is complete. Once libssl is notified that the authentication
47 * is complete, it will continue the SSL handshake (if it hasn't already
48 * finished) and it will begin allowing us to send/receive data on the
49 * connection.
51 * Timeline of events (for connections managed by the socket transport service):
53 * * libssl calls SSLServerCertVerificationJob::Dispatch on the socket
54 * transport thread.
55 * * SSLServerCertVerificationJob::Dispatch queues a job
56 * (instance of SSLServerCertVerificationJob) to its background thread
57 * pool and returns.
58 * * One of the background threads calls CERT_*Verify*, which may enqueue
59 * some HTTP request(s) onto the socket transport thread, and then
60 * blocks that background thread waiting for the responses and/or timeouts
61 * or errors for those requests.
62 * * Once those HTTP responses have all come back or failed, the
63 * CERT_*Verify* function returns a result indicating that the validation
64 * succeeded or failed.
65 * * If the validation succeeded, then a SSLServerCertVerificationResult
66 * event is posted to the socket transport thread, and the cert
67 * verification thread becomes free to verify other certificates.
68 * * Otherwise, a CertErrorRunnable is posted to the socket transport thread
69 * and then to the main thread (blocking both, see CertErrorRunnable) to
70 * do cert override processing and bad cert listener notification. Then
71 * the cert verification thread becomes free to verify other certificates.
72 * * After processing cert overrides, the CertErrorRunnable will dispatch a
73 * SSLServerCertVerificationResult event to the socket transport thread to
74 * notify it of the result of the override processing; then it returns,
75 * freeing up the main thread.
76 * * The SSLServerCertVerificationResult event will either wake up the
77 * socket (using SSL_RestartHandshakeAfterServerCert) if validation
78 * succeeded or there was an error override, or it will set an error flag
79 * so that the next I/O operation on the socket will fail, causing the
80 * socket transport thread to close the connection.
82 * Cert override processing must happen on the main thread because it accesses
83 * the nsICertOverrideService, and that service must be accessed on the main
84 * thread because some extensions (Selenium, in particular) replace it with a
85 * Javascript implementation, and chrome JS must always be run on the main
86 * thread.
88 * SSLServerCertVerificationResult must be dispatched to the socket transport
89 * thread because we must only call SSL_* functions on the socket transport
90 * thread since they may do I/O, because many parts of nsNSSSocketInfo (the
91 * subclass of TransportSecurityInfo used when validating certificates during
92 * an SSL handshake) and the PSM NSS I/O layer are not thread-safe, and because
93 * we need the event to interrupt the PR_Poll that may waiting for I/O on the
94 * socket for which we are validating the cert.
97 #include "SSLServerCertVerification.h"
98 #include "CertVerifier.h"
99 #include "nsIBadCertListener2.h"
100 #include "nsICertOverrideService.h"
101 #include "nsIStrictTransportSecurityService.h"
102 #include "nsNSSComponent.h"
103 #include "nsNSSCleaner.h"
104 #include "nsRecentBadCerts.h"
105 #include "nsNSSIOLayer.h"
106 #include "nsNSSShutDown.h"
108 #include "mozilla/Assertions.h"
109 #include "mozilla/Mutex.h"
110 #include "mozilla/Telemetry.h"
111 #include "nsIThreadPool.h"
112 #include "nsNetUtil.h"
113 #include "nsXPCOMCIDInternal.h"
114 #include "nsComponentManagerUtils.h"
115 #include "nsServiceManagerUtils.h"
116 #include "nsIConsoleService.h"
117 #include "PSMRunnable.h"
118 #include "SharedSSLState.h"
120 #include "ssl.h"
121 #include "secerr.h"
122 #include "secport.h"
123 #include "sslerr.h"
124 #include "ocsp.h"
126 #ifdef PR_LOGGING
127 extern PRLogModuleInfo* gPIPNSSLog;
128 #endif
130 namespace mozilla { namespace psm {
132 namespace {
134 NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
136 NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
137 NSSCleanupAutoPtrClass_WithParam(PLArenaPool, PORT_FreeArena, FalseParam, false)
139 // do not use a nsCOMPtr to avoid static initializer/destructor
140 nsIThreadPool * gCertVerificationThreadPool = nullptr;
142 // We avoid using a mutex for the success case to avoid lock-related
143 // performance issues. However, we do use a lock in the error case to simplify
144 // the code, since performance in the error case is not important.
145 Mutex *gSSLVerificationTelemetryMutex = nullptr;
147 } // unnamed namespace
149 // Called when the socket transport thread starts, to initialize the SSL cert
150 // verification thread pool. By tying the thread pool startup/shutdown directly
151 // to the STS thread's lifetime, we ensure that they are *always* available for
152 // SSL connections and that there are no races during startup and especially
153 // shutdown. (Previously, we have had multiple problems with races in PSM
154 // background threads, and the race-prevention/shutdown logic used there is
155 // brittle. Since this service is critical to things like downloading updates,
156 // we take no chances.) Also, by doing things this way, we avoid the need for
157 // locks, since gCertVerificationThreadPool is only ever accessed on the socket
158 // transport thread.
159 void
160 InitializeSSLServerCertVerificationThreads()
162 gSSLVerificationTelemetryMutex = new Mutex("SSLVerificationTelemetryMutex");
163 // TODO: tuning, make parameters preferences
164 // XXX: instantiate nsThreadPool directly, to make this more bulletproof.
165 // Currently, the nsThreadPool.h header isn't exported for us to do so.
166 nsresult rv = CallCreateInstance(NS_THREADPOOL_CONTRACTID,
167 &gCertVerificationThreadPool);
168 if (NS_FAILED(rv)) {
169 NS_WARNING("Failed to create SSL cert verification threads.");
170 return;
173 (void) gCertVerificationThreadPool->SetIdleThreadLimit(5);
174 (void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000);
175 (void) gCertVerificationThreadPool->SetThreadLimit(5);
176 (void) gCertVerificationThreadPool->SetName(NS_LITERAL_CSTRING("SSL Cert"));
179 // Called when the socket transport thread finishes, to destroy the thread
180 // pool. Since the socket transport service has stopped processing events, it
181 // will not attempt any more SSL I/O operations, so it is clearly safe to shut
182 // down the SSL cert verification infrastructure. Also, the STS will not
183 // dispatch many SSL verification result events at this point, so any pending
184 // cert verifications will (correctly) fail at the point they are dispatched.
186 // The other shutdown race condition that is possible is a race condition with
187 // shutdown of the nsNSSComponent service. We use the
188 // nsNSSShutdownPreventionLock where needed (not here) to prevent that.
189 void StopSSLServerCertVerificationThreads()
191 if (gCertVerificationThreadPool) {
192 gCertVerificationThreadPool->Shutdown();
193 NS_RELEASE(gCertVerificationThreadPool);
195 if (gSSLVerificationTelemetryMutex) {
196 delete gSSLVerificationTelemetryMutex;
197 gSSLVerificationTelemetryMutex = nullptr;
201 namespace {
203 void
204 LogInvalidCertError(TransportSecurityInfo *socketInfo,
205 const nsACString &host,
206 const nsACString &hostWithPort,
207 int32_t port,
208 PRErrorCode errorCode,
209 ::mozilla::psm::SSLErrorMessageType errorMessageType,
210 nsIX509Cert* ix509)
212 nsString message;
213 socketInfo->GetErrorLogMessage(errorCode, errorMessageType, message);
215 if (!message.IsEmpty()) {
216 nsCOMPtr<nsIConsoleService> console;
217 console = do_GetService(NS_CONSOLESERVICE_CONTRACTID);
218 if (console) {
219 console->LogStringMessage(message.get());
224 // Dispatched to the STS thread to notify the infoObject of the verification
225 // result.
227 // This will cause the PR_Poll in the STS thread to return, so things work
228 // correctly even if the STS thread is blocked polling (only) on the file
229 // descriptor that is waiting for this result.
230 class SSLServerCertVerificationResult : public nsRunnable
232 public:
233 NS_DECL_NSIRUNNABLE
235 SSLServerCertVerificationResult(TransportSecurityInfo * infoObject,
236 PRErrorCode errorCode,
237 Telemetry::ID telemetryID = Telemetry::HistogramCount,
238 uint32_t telemetryValue = -1,
239 SSLErrorMessageType errorMessageType =
240 PlainErrorMessage);
242 void Dispatch();
243 private:
244 const RefPtr<TransportSecurityInfo> mInfoObject;
245 public:
246 const PRErrorCode mErrorCode;
247 const SSLErrorMessageType mErrorMessageType;
248 const Telemetry::ID mTelemetryID;
249 const uint32_t mTelemetryValue;
252 class CertErrorRunnable : public SyncRunnableBase
254 public:
255 CertErrorRunnable(const void * fdForLogging,
256 nsIX509Cert * cert,
257 TransportSecurityInfo * infoObject,
258 PRErrorCode defaultErrorCodeToReport,
259 uint32_t collectedErrors,
260 PRErrorCode errorCodeTrust,
261 PRErrorCode errorCodeMismatch,
262 PRErrorCode errorCodeExpired,
263 uint32_t providerFlags)
264 : mFdForLogging(fdForLogging), mCert(cert), mInfoObject(infoObject),
265 mDefaultErrorCodeToReport(defaultErrorCodeToReport),
266 mCollectedErrors(collectedErrors),
267 mErrorCodeTrust(errorCodeTrust),
268 mErrorCodeMismatch(errorCodeMismatch),
269 mErrorCodeExpired(errorCodeExpired),
270 mProviderFlags(providerFlags)
274 virtual void RunOnTargetThread();
275 RefPtr<SSLServerCertVerificationResult> mResult; // out
276 private:
277 SSLServerCertVerificationResult *CheckCertOverrides();
279 const void * const mFdForLogging; // may become an invalid pointer; do not dereference
280 const nsCOMPtr<nsIX509Cert> mCert;
281 const RefPtr<TransportSecurityInfo> mInfoObject;
282 const PRErrorCode mDefaultErrorCodeToReport;
283 const uint32_t mCollectedErrors;
284 const PRErrorCode mErrorCodeTrust;
285 const PRErrorCode mErrorCodeMismatch;
286 const PRErrorCode mErrorCodeExpired;
287 const uint32_t mProviderFlags;
290 SSLServerCertVerificationResult *
291 CertErrorRunnable::CheckCertOverrides()
293 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] top of CheckCertOverrides\n",
294 mFdForLogging, this));
296 if (!NS_IsMainThread()) {
297 NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
298 return new SSLServerCertVerificationResult(mInfoObject,
299 mDefaultErrorCodeToReport);
302 int32_t port;
303 mInfoObject->GetPort(&port);
305 nsCString hostWithPortString;
306 hostWithPortString.AppendASCII(mInfoObject->GetHostName());
307 hostWithPortString.AppendLiteral(":");
308 hostWithPortString.AppendInt(port);
310 uint32_t remaining_display_errors = mCollectedErrors;
312 nsresult nsrv;
314 // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
315 // connections must be dropped when there are any certificate errors
316 // (STS Spec section 7.3).
317 bool strictTransportSecurityEnabled = false;
318 nsCOMPtr<nsIStrictTransportSecurityService> stss
319 = do_GetService(NS_STSSERVICE_CONTRACTID, &nsrv);
320 if (NS_SUCCEEDED(nsrv)) {
321 nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
322 NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
323 nsrv = stss->IsStsHost(mInfoObject->GetHostName(),
324 mProviderFlags,
325 &strictTransportSecurityEnabled);
327 if (NS_FAILED(nsrv)) {
328 return new SSLServerCertVerificationResult(mInfoObject,
329 mDefaultErrorCodeToReport);
332 if (!strictTransportSecurityEnabled) {
333 nsCOMPtr<nsICertOverrideService> overrideService =
334 do_GetService(NS_CERTOVERRIDE_CONTRACTID);
335 // it is fine to continue without the nsICertOverrideService
337 uint32_t overrideBits = 0;
339 if (overrideService)
341 bool haveOverride;
342 bool isTemporaryOverride; // we don't care
343 nsCString hostString(mInfoObject->GetHostName());
344 nsrv = overrideService->HasMatchingOverride(hostString, port,
345 mCert,
346 &overrideBits,
347 &isTemporaryOverride,
348 &haveOverride);
349 if (NS_SUCCEEDED(nsrv) && haveOverride)
351 // remove the errors that are already overriden
352 remaining_display_errors &= ~overrideBits;
356 if (!remaining_display_errors) {
357 // all errors are covered by override rules, so let's accept the cert
358 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
359 ("[%p][%p] All errors covered by override rules\n",
360 mFdForLogging, this));
361 return new SSLServerCertVerificationResult(mInfoObject, 0);
363 } else {
364 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
365 ("[%p][%p] Strict-Transport-Security is violated: untrusted "
366 "transport layer\n", mFdForLogging, this));
369 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
370 ("[%p][%p] Certificate error was not overridden\n",
371 mFdForLogging, this));
373 // Ok, this is a full stop.
374 // First, deliver the technical details of the broken SSL status.
376 // Try to get a nsIBadCertListener2 implementation from the socket consumer.
377 nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
378 NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
379 if (sslSocketControl) {
380 nsCOMPtr<nsIInterfaceRequestor> cb;
381 sslSocketControl->GetNotificationCallbacks(getter_AddRefs(cb));
382 if (cb) {
383 nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
384 if (bcl) {
385 nsIInterfaceRequestor *csi
386 = static_cast<nsIInterfaceRequestor*>(mInfoObject);
387 bool suppressMessage = false; // obsolete, ignored
388 nsrv = bcl->NotifyCertProblem(csi, mInfoObject->SSLStatus(),
389 hostWithPortString, &suppressMessage);
394 nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
395 nsCOMPtr<nsIRecentBadCerts> recentBadCertsService;
396 if (certdb) {
397 bool isPrivate = mProviderFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
398 certdb->GetRecentBadCerts(isPrivate, getter_AddRefs(recentBadCertsService));
401 if (recentBadCertsService) {
402 NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString);
403 recentBadCertsService->AddBadCert(hostWithPortStringUTF16,
404 mInfoObject->SSLStatus());
407 // pick the error code to report by priority
408 PRErrorCode errorCodeToReport = mErrorCodeTrust ? mErrorCodeTrust
409 : mErrorCodeMismatch ? mErrorCodeMismatch
410 : mErrorCodeExpired ? mErrorCodeExpired
411 : mDefaultErrorCodeToReport;
413 SSLServerCertVerificationResult *result =
414 new SSLServerCertVerificationResult(mInfoObject,
415 errorCodeToReport,
416 Telemetry::HistogramCount,
418 OverridableCertErrorMessage);
420 LogInvalidCertError(mInfoObject,
421 nsDependentCString(mInfoObject->GetHostName()),
422 hostWithPortString,
423 port,
424 result->mErrorCode,
425 result->mErrorMessageType,
426 mCert);
428 return result;
431 void
432 CertErrorRunnable::RunOnTargetThread()
434 MOZ_ASSERT(NS_IsMainThread());
436 mResult = CheckCertOverrides();
438 MOZ_ASSERT(mResult);
441 // Returns null with the error code (PR_GetError()) set if it does not create
442 // the CertErrorRunnable.
443 CertErrorRunnable *
444 CreateCertErrorRunnable(PRErrorCode defaultErrorCodeToReport,
445 TransportSecurityInfo * infoObject,
446 CERTCertificate * cert,
447 const void * fdForLogging,
448 uint32_t providerFlags,
449 PRTime now)
451 MOZ_ASSERT(infoObject);
452 MOZ_ASSERT(cert);
454 // cert was revoked, don't do anything else
455 if (defaultErrorCodeToReport == SEC_ERROR_REVOKED_CERTIFICATE) {
456 PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
457 return nullptr;
460 if (defaultErrorCodeToReport == 0) {
461 NS_ERROR("No error code set during certificate validation failure.");
462 PR_SetError(PR_INVALID_STATE_ERROR, 0);
463 return nullptr;
466 RefPtr<nsNSSCertificate> nssCert(nsNSSCertificate::Create(cert));
467 if (!nssCert) {
468 NS_ERROR("nsNSSCertificate::Create failed");
469 PR_SetError(SEC_ERROR_NO_MEMORY, 0);
470 return nullptr;
473 SECStatus srv;
475 RefPtr<CertVerifier> certVerifier(GetDefaultCertVerifier());
476 if (!certVerifier) {
477 NS_ERROR("GetDefaultCerVerifier failed");
478 PR_SetError(defaultErrorCodeToReport, 0);
479 return nullptr;
482 PLArenaPool *log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
483 PLArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
484 if (!log_arena) {
485 NS_ERROR("PORT_NewArena failed");
486 return nullptr; // PORT_NewArena set error code
489 CERTVerifyLog * verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
490 if (!verify_log) {
491 NS_ERROR("PORT_ArenaZNew failed");
492 return nullptr; // PORT_ArenaZNew set error code
494 CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
495 verify_log->arena = log_arena;
497 srv = certVerifier->VerifyCert(cert, certificateUsageSSLServer, now,
498 infoObject, 0, nullptr, nullptr, verify_log);
500 // We ignore the result code of the cert verification.
501 // Either it is a failure, which is expected, and we'll process the
502 // verify log below.
503 // Or it is a success, then a domain mismatch is the only
504 // possible failure.
506 PRErrorCode errorCodeMismatch = 0;
507 PRErrorCode errorCodeTrust = 0;
508 PRErrorCode errorCodeExpired = 0;
510 uint32_t collected_errors = 0;
512 if (infoObject->IsCertIssuerBlacklisted()) {
513 collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
514 errorCodeTrust = defaultErrorCodeToReport;
517 // Check the name field against the desired hostname.
518 if (CERT_VerifyCertName(cert, infoObject->GetHostName()) != SECSuccess) {
519 collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
520 errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
523 CERTVerifyLogNode *i_node;
524 for (i_node = verify_log->head; i_node; i_node = i_node->next)
526 switch (i_node->error)
528 case SEC_ERROR_UNKNOWN_ISSUER:
529 case SEC_ERROR_CA_CERT_INVALID:
530 case SEC_ERROR_UNTRUSTED_ISSUER:
531 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
532 case SEC_ERROR_UNTRUSTED_CERT:
533 case SEC_ERROR_INADEQUATE_KEY_USAGE:
534 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
535 // We group all these errors as "cert not trusted"
536 collected_errors |= nsICertOverrideService::ERROR_UNTRUSTED;
537 if (errorCodeTrust == SECSuccess) {
538 errorCodeTrust = i_node->error;
540 break;
541 case SSL_ERROR_BAD_CERT_DOMAIN:
542 collected_errors |= nsICertOverrideService::ERROR_MISMATCH;
543 if (errorCodeMismatch == SECSuccess) {
544 errorCodeMismatch = i_node->error;
546 break;
547 case SEC_ERROR_EXPIRED_CERTIFICATE:
548 collected_errors |= nsICertOverrideService::ERROR_TIME;
549 if (errorCodeExpired == SECSuccess) {
550 errorCodeExpired = i_node->error;
552 break;
553 default:
554 PR_SetError(i_node->error, 0);
555 return nullptr;
559 if (!collected_errors)
561 // This will happen when CERT_*Verify* only returned error(s) that are
562 // not on our whitelist of overridable certificate errors.
563 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] !collected_errors: %d\n",
564 fdForLogging, static_cast<int>(defaultErrorCodeToReport)));
565 PR_SetError(defaultErrorCodeToReport, 0);
566 return nullptr;
569 infoObject->SetStatusErrorBits(*nssCert, collected_errors);
571 return new CertErrorRunnable(fdForLogging,
572 static_cast<nsIX509Cert*>(nssCert.get()),
573 infoObject, defaultErrorCodeToReport,
574 collected_errors, errorCodeTrust,
575 errorCodeMismatch, errorCodeExpired,
576 providerFlags);
579 // When doing async cert processing, we dispatch one of these runnables to the
580 // socket transport service thread, which blocks the socket transport
581 // service thread while it waits for the inner CertErrorRunnable to execute
582 // CheckCertOverrides on the main thread. CheckCertOverrides must block events
583 // on both of these threads because it calls TransportSecurityInfo::GetInterface(),
584 // which may call nsHttpConnection::GetInterface() through
585 // TransportSecurityInfo::mCallbacks. nsHttpConnection::GetInterface must always
586 // execute on the main thread, with the socket transport service thread
587 // blocked.
588 class CertErrorRunnableRunnable : public nsRunnable
590 public:
591 CertErrorRunnableRunnable(CertErrorRunnable * certErrorRunnable)
592 : mCertErrorRunnable(certErrorRunnable)
595 private:
596 NS_IMETHOD Run()
598 nsresult rv = mCertErrorRunnable->DispatchToMainThreadAndWait();
599 // The result must run on the socket transport thread, which we are already
600 // on, so we can just run it directly, instead of dispatching it.
601 if (NS_SUCCEEDED(rv)) {
602 rv = mCertErrorRunnable->mResult ? mCertErrorRunnable->mResult->Run()
603 : NS_ERROR_UNEXPECTED;
605 return rv;
607 RefPtr<CertErrorRunnable> mCertErrorRunnable;
610 class SSLServerCertVerificationJob : public nsRunnable
612 public:
613 // Must be called only on the socket transport thread
614 static SECStatus Dispatch(const void * fdForLogging,
615 TransportSecurityInfo * infoObject,
616 CERTCertificate * serverCert,
617 SECItem * stapledOCSPResponse,
618 uint32_t providerFlags);
619 private:
620 NS_DECL_NSIRUNNABLE
622 // Must be called only on the socket transport thread
623 SSLServerCertVerificationJob(const void * fdForLogging,
624 TransportSecurityInfo * infoObject,
625 CERTCertificate * cert,
626 SECItem * stapledOCSPResponse,
627 uint32_t providerFlags);
628 const void * const mFdForLogging;
629 const RefPtr<TransportSecurityInfo> mInfoObject;
630 const ScopedCERTCertificate mCert;
631 const uint32_t mProviderFlags;
632 const TimeStamp mJobStartTime;
633 const ScopedSECItem mStapledOCSPResponse;
636 SSLServerCertVerificationJob::SSLServerCertVerificationJob(
637 const void * fdForLogging, TransportSecurityInfo * infoObject,
638 CERTCertificate * cert, SECItem * stapledOCSPResponse,
639 uint32_t providerFlags)
640 : mFdForLogging(fdForLogging)
641 , mInfoObject(infoObject)
642 , mCert(CERT_DupCertificate(cert))
643 , mProviderFlags(providerFlags)
644 , mJobStartTime(TimeStamp::Now())
645 , mStapledOCSPResponse(SECITEM_DupItem(stapledOCSPResponse))
649 SECStatus
650 PSM_SSL_PKIX_AuthCertificate(CERTCertificate *peerCert,
651 nsIInterfaceRequestor * pinarg,
652 const char * hostname,
653 CERTCertList **validationChain,
654 SECOidTag *evOidPolicy)
656 RefPtr<CertVerifier> certVerifier(GetDefaultCertVerifier());
657 if (!certVerifier) {
658 PR_SetError(PR_INVALID_STATE_ERROR, 0);
659 return SECFailure;
662 SECStatus rv = certVerifier->VerifyCert(peerCert,
663 certificateUsageSSLServer, PR_Now(),
664 pinarg, 0, validationChain , evOidPolicy);
666 if (rv == SECSuccess) {
667 /* cert is OK. This is the client side of an SSL connection.
668 * Now check the name field in the cert against the desired hostname.
669 * NB: This is our only defense against Man-In-The-Middle (MITM) attacks!
671 if (hostname && hostname[0])
672 rv = CERT_VerifyCertName(peerCert, hostname);
673 else
674 rv = SECFailure;
675 if (rv != SECSuccess)
676 PORT_SetError(SSL_ERROR_BAD_CERT_DOMAIN);
679 return rv;
682 struct nsSerialBinaryBlacklistEntry
684 unsigned int len;
685 const char *binary_serial;
688 // bug 642395
689 static struct nsSerialBinaryBlacklistEntry myUTNBlacklistEntries[] = {
690 { 17, "\x00\x92\x39\xd5\x34\x8f\x40\xd1\x69\x5a\x74\x54\x70\xe1\xf2\x3f\x43" },
691 { 17, "\x00\xd8\xf3\x5f\x4e\xb7\x87\x2b\x2d\xab\x06\x92\xe3\x15\x38\x2f\xb0" },
692 { 16, "\x72\x03\x21\x05\xc5\x0c\x08\x57\x3d\x8e\xa5\x30\x4e\xfe\xe8\xb0" },
693 { 17, "\x00\xb0\xb7\x13\x3e\xd0\x96\xf9\xb5\x6f\xae\x91\xc8\x74\xbd\x3a\xc0" },
694 { 16, "\x39\x2a\x43\x4f\x0e\x07\xdf\x1f\x8a\xa3\x05\xde\x34\xe0\xc2\x29" },
695 { 16, "\x3e\x75\xce\xd4\x6b\x69\x30\x21\x21\x88\x30\xae\x86\xa8\x2a\x71" },
696 { 17, "\x00\xe9\x02\x8b\x95\x78\xe4\x15\xdc\x1a\x71\x0a\x2b\x88\x15\x44\x47" },
697 { 17, "\x00\xd7\x55\x8f\xda\xf5\xf1\x10\x5b\xb2\x13\x28\x2b\x70\x77\x29\xa3" },
698 { 16, "\x04\x7e\xcb\xe9\xfc\xa5\x5f\x7b\xd0\x9e\xae\x36\xe1\x0c\xae\x1e" },
699 { 17, "\x00\xf5\xc8\x6a\xf3\x61\x62\xf1\x3a\x64\xf5\x4f\x6d\xc9\x58\x7c\x06" },
700 { 0, 0 } // end marker
703 // Call this if we have already decided that a cert should be treated as INVALID,
704 // in order to check if we to worsen the error to REVOKED.
705 PRErrorCode
706 PSM_SSL_DigiNotarTreatAsRevoked(CERTCertificate * serverCert,
707 CERTCertList * serverCertChain)
709 // If any involved cert was issued by DigiNotar,
710 // and serverCert was issued after 01-JUL-2011,
711 // then worsen the error to revoked.
713 PRTime cutoff = 0;
714 PRStatus status = PR_ParseTimeString("01-JUL-2011 00:00", true, &cutoff);
715 if (status != PR_SUCCESS) {
716 NS_ASSERTION(status == PR_SUCCESS, "PR_ParseTimeString failed");
717 // be safe, assume it's afterwards, keep going
718 } else {
719 PRTime notBefore = 0, notAfter = 0;
720 if (CERT_GetCertTimes(serverCert, &notBefore, &notAfter) == SECSuccess &&
721 notBefore < cutoff) {
722 // no worsening for certs issued before the cutoff date
723 return 0;
727 for (CERTCertListNode *node = CERT_LIST_HEAD(serverCertChain);
728 !CERT_LIST_END(node, serverCertChain);
729 node = CERT_LIST_NEXT(node)) {
730 if (node->cert->issuerName &&
731 strstr(node->cert->issuerName, "CN=DigiNotar")) {
732 return SEC_ERROR_REVOKED_CERTIFICATE;
736 return 0;
739 // Call this only if a cert has been reported by NSS as VALID
740 PRErrorCode
741 PSM_SSL_BlacklistDigiNotar(CERTCertificate * serverCert,
742 CERTCertList * serverCertChain)
744 bool isDigiNotarIssuedCert = false;
746 for (CERTCertListNode *node = CERT_LIST_HEAD(serverCertChain);
747 !CERT_LIST_END(node, serverCertChain);
748 node = CERT_LIST_NEXT(node)) {
749 if (!node->cert->issuerName)
750 continue;
752 if (strstr(node->cert->issuerName, "CN=DigiNotar")) {
753 isDigiNotarIssuedCert = true;
757 if (isDigiNotarIssuedCert) {
758 // let's see if we want to worsen the error code to revoked.
759 PRErrorCode revoked_code = PSM_SSL_DigiNotarTreatAsRevoked(serverCert, serverCertChain);
760 return (revoked_code != 0) ? revoked_code : SEC_ERROR_UNTRUSTED_ISSUER;
763 return 0;
766 // This function assumes that we will only use the SPDY connection coalescing
767 // feature on connections where we have negotiated SPDY using NPN. If we ever
768 // talk SPDY without having negotiated it with SPDY, this code will give wrong
769 // and perhaps unsafe results.
771 // Returns SECSuccess on the initial handshake of all connections, on
772 // renegotiations for any connections where we did not negotiate SPDY, or on any
773 // SPDY connection where the server's certificate did not change.
775 // Prohibit changing the server cert only if we negotiated SPDY,
776 // in order to support SPDY's cross-origin connection pooling.
778 static SECStatus
779 BlockServerCertChangeForSpdy(nsNSSSocketInfo *infoObject,
780 CERTCertificate *serverCert)
782 // Get the existing cert. If there isn't one, then there is
783 // no cert change to worry about.
784 nsCOMPtr<nsIX509Cert> cert;
785 nsCOMPtr<nsIX509Cert2> cert2;
787 RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
788 if (!status) {
789 // If we didn't have a status, then this is the
790 // first handshake on this connection, not a
791 // renegotiation.
792 return SECSuccess;
795 status->GetServerCert(getter_AddRefs(cert));
796 cert2 = do_QueryInterface(cert);
797 if (!cert2) {
798 NS_NOTREACHED("every nsSSLStatus must have a cert"
799 "that implements nsIX509Cert2");
800 PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
801 return SECFailure;
804 // Filter out sockets that did not neogtiate SPDY via NPN
805 nsAutoCString negotiatedNPN;
806 nsresult rv = infoObject->GetNegotiatedNPN(negotiatedNPN);
807 NS_ASSERTION(NS_SUCCEEDED(rv),
808 "GetNegotiatedNPN() failed during renegotiation");
810 if (NS_SUCCEEDED(rv) && !StringBeginsWith(negotiatedNPN,
811 NS_LITERAL_CSTRING("spdy/")))
812 return SECSuccess;
814 // If GetNegotiatedNPN() failed we will assume spdy for safety's safe
815 if (NS_FAILED(rv)) {
816 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
817 ("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call."
818 " Assuming spdy.\n"));
821 // Check to see if the cert has actually changed
822 ScopedCERTCertificate c(cert2->GetCert());
823 NS_ASSERTION(c, "very bad and hopefully impossible state");
824 bool sameCert = CERT_CompareCerts(c, serverCert);
825 if (sameCert)
826 return SECSuccess;
828 // Report an error - changed cert is confirmed
829 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
830 ("SPDY Refused to allow new cert during renegotiation\n"));
831 PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0);
832 return SECFailure;
835 SECStatus
836 AuthCertificate(TransportSecurityInfo * infoObject, CERTCertificate * cert,
837 SECItem * stapledOCSPResponse, uint32_t providerFlags)
839 if (cert->serialNumber.data &&
840 cert->issuerName &&
841 !strcmp(cert->issuerName,
842 "CN=UTN-USERFirst-Hardware,OU=http://www.usertrust.com,O=The USERTRUST Network,L=Salt Lake City,ST=UT,C=US")) {
844 unsigned char *server_cert_comparison_start = cert->serialNumber.data;
845 unsigned int server_cert_comparison_len = cert->serialNumber.len;
847 while (server_cert_comparison_len) {
848 if (*server_cert_comparison_start != 0)
849 break;
851 ++server_cert_comparison_start;
852 --server_cert_comparison_len;
855 nsSerialBinaryBlacklistEntry *walk = myUTNBlacklistEntries;
856 for ( ; walk && walk->len; ++walk) {
858 unsigned char *locked_cert_comparison_start = (unsigned char*)walk->binary_serial;
859 unsigned int locked_cert_comparison_len = walk->len;
861 while (locked_cert_comparison_len) {
862 if (*locked_cert_comparison_start != 0)
863 break;
865 ++locked_cert_comparison_start;
866 --locked_cert_comparison_len;
869 if (server_cert_comparison_len == locked_cert_comparison_len &&
870 !memcmp(server_cert_comparison_start, locked_cert_comparison_start, locked_cert_comparison_len)) {
871 PR_SetError(SEC_ERROR_REVOKED_CERTIFICATE, 0);
872 return SECFailure;
877 SECStatus rv;
878 if (stapledOCSPResponse) {
879 CERTCertDBHandle *handle = CERT_GetDefaultCertDB();
880 rv = CERT_CacheOCSPResponseFromSideChannel(handle, cert, PR_Now(),
881 stapledOCSPResponse,
882 infoObject);
883 if (rv != SECSuccess) {
884 return rv;
888 CERTCertList *verifyCertChain = nullptr;
889 SECOidTag evOidPolicy;
890 rv = PSM_SSL_PKIX_AuthCertificate(cert, infoObject, infoObject->GetHostName(),
891 &verifyCertChain, &evOidPolicy);
893 // We want to remember the CA certs in the temp db, so that the application can find the
894 // complete chain at any time it might need it.
895 // But we keep only those CA certs in the temp db, that we didn't already know.
897 RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
898 RefPtr<nsNSSCertificate> nsc;
900 if (!status || !status->mServerCert) {
901 if( rv == SECSuccess ){
902 nsc = nsNSSCertificate::Create(cert, &evOidPolicy);
904 else {
905 nsc = nsNSSCertificate::Create(cert);
909 ScopedCERTCertList certList(verifyCertChain);
911 if (!certList) {
912 rv = SECFailure;
913 } else {
914 PRErrorCode blacklistErrorCode;
915 if (rv == SECSuccess) { // PSM_SSL_PKIX_AuthCertificate said "valid cert"
916 blacklistErrorCode = PSM_SSL_BlacklistDigiNotar(cert, certList);
917 } else { // PSM_SSL_PKIX_AuthCertificate said "invalid cert"
918 PRErrorCode savedErrorCode = PORT_GetError();
919 // Check if we want to worsen the error code to "revoked".
920 blacklistErrorCode = PSM_SSL_DigiNotarTreatAsRevoked(cert, certList);
921 if (blacklistErrorCode == 0) {
922 // we don't worsen the code, let's keep the original error code from NSS
923 PORT_SetError(savedErrorCode);
927 if (blacklistErrorCode != 0) {
928 infoObject->SetCertIssuerBlacklisted();
929 PORT_SetError(blacklistErrorCode);
930 rv = SECFailure;
934 if (rv == SECSuccess) {
935 // We want to avoid storing any intermediate cert information when browsing
936 // in private, transient contexts.
937 if (!(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE)) {
938 for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
939 !CERT_LIST_END(node, certList);
940 node = CERT_LIST_NEXT(node)) {
942 if (node->cert->slot) {
943 // This cert was found on a token, no need to remember it in the temp db.
944 continue;
947 if (node->cert->isperm) {
948 // We don't need to remember certs already stored in perm db.
949 continue;
952 if (node->cert == cert) {
953 // We don't want to remember the server cert,
954 // the code that cares for displaying page info does this already.
955 continue;
958 // We have found a signer cert that we want to remember.
959 char* nickname = nsNSSCertificate::defaultServerNickname(node->cert);
960 if (nickname && *nickname) {
961 ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
962 if (slot) {
963 PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE,
964 nickname, false);
967 PR_FREEIF(nickname);
971 // The connection may get terminated, for example, if the server requires
972 // a client cert. Let's provide a minimal SSLStatus
973 // to the caller that contains at least the cert and its status.
974 if (!status) {
975 status = new nsSSLStatus();
976 infoObject->SetSSLStatus(status);
979 if (rv == SECSuccess) {
980 // Certificate verification succeeded delete any potential record
981 // of certificate error bits.
982 RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject,
983 nullptr, rv);
985 else {
986 // Certificate verification failed, update the status' bits.
987 RememberCertErrorsTable::GetInstance().LookupCertErrorBits(
988 infoObject, status);
991 if (status && !status->mServerCert) {
992 status->mServerCert = nsc;
993 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
994 ("AuthCertificate setting NEW cert %p\n", status->mServerCert.get()));
998 return rv;
1001 /*static*/ SECStatus
1002 SSLServerCertVerificationJob::Dispatch(const void * fdForLogging,
1003 TransportSecurityInfo * infoObject,
1004 CERTCertificate * serverCert,
1005 SECItem * stapledOCSPResponse,
1006 uint32_t providerFlags)
1008 // Runs on the socket transport thread
1009 if (!infoObject || !serverCert) {
1010 NS_ERROR("Invalid parameters for SSL server cert validation");
1011 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
1012 return SECFailure;
1015 RefPtr<SSLServerCertVerificationJob> job(
1016 new SSLServerCertVerificationJob(fdForLogging, infoObject, serverCert,
1017 stapledOCSPResponse, providerFlags));
1019 nsresult nrv;
1020 if (!gCertVerificationThreadPool) {
1021 nrv = NS_ERROR_NOT_INITIALIZED;
1022 } else {
1023 nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
1025 if (NS_FAILED(nrv)) {
1026 // We can't call SetCertVerificationResult here to change
1027 // mCertVerificationState because SetCertVerificationResult will call
1028 // libssl functions that acquire SSL locks that are already being held at
1029 // this point. infoObject->mCertVerificationState will be stuck at
1030 // waiting_for_cert_verification here, but that is OK because we already
1031 // have to be able to handle cases where we encounter non-cert errors while
1032 // in that state.
1033 PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY
1034 ? SEC_ERROR_NO_MEMORY
1035 : PR_INVALID_STATE_ERROR;
1036 PORT_SetError(error);
1037 return SECFailure;
1040 PORT_SetError(PR_WOULD_BLOCK_ERROR);
1041 return SECWouldBlock;
1044 NS_IMETHODIMP
1045 SSLServerCertVerificationJob::Run()
1047 // Runs on a cert verification thread
1049 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
1050 ("[%p] SSLServerCertVerificationJob::Run\n", mInfoObject.get()));
1052 PRErrorCode error;
1054 nsNSSShutDownPreventionLock nssShutdownPrevention;
1055 if (mInfoObject->isAlreadyShutDown()) {
1056 error = SEC_ERROR_USER_CANCELLED;
1057 } else {
1058 // Reset the error code here so we can detect if AuthCertificate fails to
1059 // set the error code if/when it fails.
1060 PR_SetError(0, 0);
1061 SECStatus rv = AuthCertificate(mInfoObject, mCert, mStapledOCSPResponse,
1062 mProviderFlags);
1063 if (rv == SECSuccess) {
1064 uint32_t interval = (uint32_t) ((TimeStamp::Now() - mJobStartTime).ToMilliseconds());
1065 Telemetry::ID telemetryID;
1066 #ifndef NSS_NO_LIBPKIX
1067 if(nsNSSComponent::globalConstFlagUsePKIXVerification){
1068 telemetryID = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_LIBPKIX;
1070 else{
1071 #endif
1072 telemetryID = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_CLASSIC;
1073 #ifndef NSS_NO_LIBPKIX
1075 #endif
1076 RefPtr<SSLServerCertVerificationResult> restart(
1077 new SSLServerCertVerificationResult(mInfoObject, 0,
1078 telemetryID, interval));
1079 restart->Dispatch();
1080 return NS_OK;
1083 // Note: the interval is not calculated once as PR_GetError MUST be called
1084 // before any other function call
1085 error = PR_GetError();
1087 TimeStamp now = TimeStamp::Now();
1088 Telemetry::ID telemetryID;
1089 #ifndef NSS_NO_LIBPKIX
1090 if(nsNSSComponent::globalConstFlagUsePKIXVerification){
1091 telemetryID = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_LIBPKIX;
1093 else{
1094 #endif
1095 telemetryID = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_CLASSIC;
1096 #ifndef NSS_NO_LIBPKIX
1098 #endif
1099 MutexAutoLock telemetryMutex(*gSSLVerificationTelemetryMutex);
1100 Telemetry::AccumulateTimeDelta(telemetryID,
1101 mJobStartTime,
1102 now);
1104 if (error != 0) {
1105 RefPtr<CertErrorRunnable> runnable(CreateCertErrorRunnable(
1106 error, mInfoObject, mCert, mFdForLogging, mProviderFlags, PR_Now()));
1107 if (!runnable) {
1108 // CreateCertErrorRunnable set a new error code
1109 error = PR_GetError();
1110 } else {
1111 // We must block the the socket transport service thread while the
1112 // main thread executes the CertErrorRunnable. The CertErrorRunnable
1113 // will dispatch the result asynchronously, so we don't have to block
1114 // this thread waiting for it.
1116 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
1117 ("[%p][%p] Before dispatching CertErrorRunnable\n",
1118 mFdForLogging, runnable.get()));
1120 nsresult nrv;
1121 nsCOMPtr<nsIEventTarget> stsTarget
1122 = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
1123 if (NS_SUCCEEDED(nrv)) {
1124 nrv = stsTarget->Dispatch(new CertErrorRunnableRunnable(runnable),
1125 NS_DISPATCH_NORMAL);
1127 if (NS_SUCCEEDED(nrv)) {
1128 return NS_OK;
1131 NS_ERROR("Failed to dispatch CertErrorRunnable");
1132 error = PR_INVALID_STATE_ERROR;
1137 if (error == 0) {
1138 NS_NOTREACHED("no error set during certificate validation failure");
1139 error = PR_INVALID_STATE_ERROR;
1142 RefPtr<SSLServerCertVerificationResult> failure(
1143 new SSLServerCertVerificationResult(mInfoObject, error));
1144 failure->Dispatch();
1145 return NS_OK;
1148 } // unnamed namespace
1150 // Extracts whatever information we need out of fd (using SSL_*) and passes it
1151 // to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should
1152 // never do anything with fd except logging.
1153 SECStatus
1154 AuthCertificateHook(void *arg, PRFileDesc *fd, PRBool checkSig, PRBool isServer)
1156 // Runs on the socket transport thread
1158 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
1159 ("[%p] starting AuthCertificateHook\n", fd));
1161 // Modern libssl always passes PR_TRUE for checkSig, and we have no means of
1162 // doing verification without checking signatures.
1163 NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
1165 // PSM never causes libssl to call this function with PR_TRUE for isServer,
1166 // and many things in PSM assume that we are a client.
1167 NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
1169 nsNSSSocketInfo *socketInfo = static_cast<nsNSSSocketInfo*>(arg);
1171 if (socketInfo) {
1172 // This is the first callback during full handshakes.
1173 socketInfo->SetFirstServerHelloReceived();
1176 ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd));
1178 if (!checkSig || isServer || !socketInfo || !serverCert) {
1179 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1180 return SECFailure;
1183 if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess)
1184 return SECFailure;
1186 bool onSTSThread;
1187 nsresult nrv;
1188 nsCOMPtr<nsIEventTarget> sts
1189 = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
1190 if (NS_SUCCEEDED(nrv)) {
1191 nrv = sts->IsOnCurrentThread(&onSTSThread);
1194 if (NS_FAILED(nrv)) {
1195 NS_ERROR("Could not get STS service or IsOnCurrentThread failed");
1196 PR_SetError(PR_UNKNOWN_ERROR, 0);
1197 return SECFailure;
1200 // SSL_PeerStapledOCSPResponses will never return a non-empty response if
1201 // OCSP stapling wasn't enabled because libssl wouldn't have let the server
1202 // return a stapled OCSP response.
1203 // We don't own these pointers.
1204 const SECItemArray *csa = SSL_PeerStapledOCSPResponses(fd);
1205 SECItem *stapledOCSPResponse = nullptr;
1206 // we currently only support single stapled responses
1207 if (csa && csa->len == 1) {
1208 stapledOCSPResponse = &csa->items[0];
1211 uint32_t providerFlags = 0;
1212 socketInfo->GetProviderFlags(&providerFlags);
1214 if (onSTSThread) {
1216 // We *must* do certificate verification on a background thread because
1217 // we need the socket transport thread to be free for our OCSP requests,
1218 // and we *want* to do certificate verification on a background thread
1219 // because of the performance benefits of doing so.
1220 socketInfo->SetCertVerificationWaiting();
1221 SECStatus rv = SSLServerCertVerificationJob::Dispatch(
1222 static_cast<const void *>(fd), socketInfo, serverCert,
1223 stapledOCSPResponse, providerFlags);
1224 return rv;
1227 // We can't do certificate verification on a background thread, because the
1228 // thread doing the network I/O may not interrupt its network I/O on receipt
1229 // of our SSLServerCertVerificationResult event, and/or it might not even be
1230 // a non-blocking socket.
1232 SECStatus rv = AuthCertificate(socketInfo, serverCert, stapledOCSPResponse,
1233 providerFlags);
1234 if (rv == SECSuccess) {
1235 return SECSuccess;
1238 PRErrorCode error = PR_GetError();
1239 if (error != 0) {
1240 RefPtr<CertErrorRunnable> runnable(CreateCertErrorRunnable(
1241 error, socketInfo, serverCert,
1242 static_cast<const void *>(fd), providerFlags, PR_Now()));
1243 if (!runnable) {
1244 // CreateCertErrorRunnable sets a new error code when it fails
1245 error = PR_GetError();
1246 } else {
1247 // We have to return SECSuccess or SECFailure based on the result of the
1248 // override processing, so we must block this thread waiting for it. The
1249 // CertErrorRunnable will NOT dispatch the result at all, since we passed
1250 // false for CreateCertErrorRunnable's async parameter
1251 nrv = runnable->DispatchToMainThreadAndWait();
1252 if (NS_FAILED(nrv)) {
1253 NS_ERROR("Failed to dispatch CertErrorRunnable");
1254 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1255 return SECFailure;
1258 if (!runnable->mResult) {
1259 NS_ERROR("CertErrorRunnable did not set result");
1260 PR_SetError(PR_INVALID_STATE_ERROR, 0);
1261 return SECFailure;
1264 if (runnable->mResult->mErrorCode == 0) {
1265 return SECSuccess; // cert error override occurred.
1268 // We must call SetCanceled here to set the error message type
1269 // in case it isn't PlainErrorMessage, which is what we would
1270 // default to if we just called
1271 // PR_SetError(runnable->mResult->mErrorCode, 0) and returned
1272 // SECFailure without doing this.
1273 socketInfo->SetCanceled(runnable->mResult->mErrorCode,
1274 runnable->mResult->mErrorMessageType);
1275 error = runnable->mResult->mErrorCode;
1279 if (error == 0) {
1280 NS_ERROR("error code not set");
1281 error = PR_UNKNOWN_ERROR;
1284 PR_SetError(error, 0);
1285 return SECFailure;
1288 #ifndef NSS_NO_LIBPKIX
1289 class InitializeIdentityInfo : public nsRunnable
1290 , public nsNSSShutDownObject
1292 private:
1293 NS_IMETHOD Run()
1295 nsNSSShutDownPreventionLock nssShutdownPrevention;
1296 if (isAlreadyShutDown())
1297 return NS_OK;
1299 nsresult rv;
1300 nsCOMPtr<nsINSSComponent> inss = do_GetService(PSM_COMPONENT_CONTRACTID, &rv);
1301 if (NS_SUCCEEDED(rv))
1302 inss->EnsureIdentityInfoLoaded();
1303 return NS_OK;
1306 virtual void virtualDestroyNSSReference()
1310 ~InitializeIdentityInfo()
1312 nsNSSShutDownPreventionLock nssShutdownPrevention;
1313 if (!isAlreadyShutDown())
1314 shutdown(calledFromObject);
1317 #endif
1319 void EnsureServerVerificationInitialized()
1321 #ifndef NSS_NO_LIBPKIX
1322 // Should only be called from socket transport thread due to the static
1323 // variable and the reference to gCertVerificationThreadPool
1325 static bool triggeredCertVerifierInit = false;
1326 if (triggeredCertVerifierInit)
1327 return;
1328 triggeredCertVerifierInit = true;
1330 RefPtr<InitializeIdentityInfo> initJob = new InitializeIdentityInfo();
1331 if (gCertVerificationThreadPool)
1332 gCertVerificationThreadPool->Dispatch(initJob, NS_DISPATCH_NORMAL);
1333 #endif
1336 SSLServerCertVerificationResult::SSLServerCertVerificationResult(
1337 TransportSecurityInfo * infoObject, PRErrorCode errorCode,
1338 Telemetry::ID telemetryID, uint32_t telemetryValue,
1339 SSLErrorMessageType errorMessageType)
1340 : mInfoObject(infoObject)
1341 , mErrorCode(errorCode)
1342 , mErrorMessageType(errorMessageType)
1343 , mTelemetryID(telemetryID)
1344 , mTelemetryValue(telemetryValue)
1346 // We accumulate telemetry for (only) successful validations on the main thread
1347 // to avoid adversely affecting performance by acquiring the mutex that we use
1348 // when accumulating the telemetry for unsuccessful validations. Unsuccessful
1349 // validations times are accumulated elsewhere.
1350 MOZ_ASSERT(telemetryID == Telemetry::HistogramCount || errorCode == 0);
1353 void
1354 SSLServerCertVerificationResult::Dispatch()
1356 nsresult rv;
1357 nsCOMPtr<nsIEventTarget> stsTarget
1358 = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
1359 NS_ASSERTION(stsTarget,
1360 "Failed to get socket transport service event target");
1361 rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
1362 NS_ASSERTION(NS_SUCCEEDED(rv),
1363 "Failed to dispatch SSLServerCertVerificationResult");
1366 NS_IMETHODIMP
1367 SSLServerCertVerificationResult::Run()
1369 // TODO: Assert that we're on the socket transport thread
1370 if (mTelemetryID != Telemetry::HistogramCount) {
1371 Telemetry::Accumulate(mTelemetryID, mTelemetryValue);
1373 // XXX: This cast will be removed by the next patch
1374 ((nsNSSSocketInfo *) mInfoObject.get())
1375 ->SetCertVerificationResult(mErrorCode, mErrorMessageType);
1376 return NS_OK;
1379 } } // namespace mozilla::psm