12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196 |
- /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- // For connections that are not processed on the socket transport thread, we do
- // NOT use the async logic described below. Instead, we authenticate the
- // certificate on the thread that the connection's I/O happens on,
- // synchronously. This allows us to do certificate verification for blocking
- // (not non-blocking) sockets and sockets that have their I/O processed on a
- // thread other than the socket transport service thread. Also, we DO NOT
- // support blocking sockets on the socket transport service thread at all.
- //
- // During certificate authentication, we call CERT_PKIXVerifyCert or
- // CERT_VerifyCert. These functions may make zero or more HTTP requests
- // for OCSP responses, CRLs, intermediate certificates, etc. Our fetching logic
- // for these requests processes them on the socket transport service thread.
- //
- // If the connection for which we are verifying the certificate is happening
- // on the socket transport thread (the usually case, at least for HTTP), then
- // if our cert auth hook were to call the CERT_*Verify* functions directly,
- // there would be a deadlock: The CERT_*Verify* function would cause an event
- // to be asynchronously posted to the socket transport thread, and then it
- // would block the socket transport thread waiting to be notified of the HTTP
- // response. However, the HTTP request would never actually be processed
- // because the socket transport thread would be blocked and so it wouldn't be
- // able process HTTP requests. (i.e. Deadlock.)
- //
- // Consequently, when we are asked to verify a certificate on the socket
- // transport service thread, we must always call the CERT_*Verify* cert
- // functions on another thread. To accomplish this, our auth cert hook
- // dispatches a SSLServerCertVerificationJob to a pool of background threads,
- // and then immediately returns SECWouldBlock to libssl. These jobs are where
- // the CERT_*Verify* functions are actually called.
- //
- // When our auth cert hook returns SECWouldBlock, libssl will carry on the
- // handshake while we validate the certificate. This will free up the socket
- // transport thread so that HTTP requests--in particular, the OCSP/CRL/cert
- // requests needed for cert verification as mentioned above--can be processed.
- //
- // Once the CERT_*Verify* function returns, the cert verification job
- // dispatches a SSLServerCertVerificationResult to the socket transport thread;
- // the SSLServerCertVerificationResult will notify libssl that the certificate
- // authentication is complete. Once libssl is notified that the authentication
- // is complete, it will continue the SSL handshake (if it hasn't already
- // finished) and it will begin allowing us to send/receive data on the
- // connection.
- //
- // Timeline of events (for connections managed by the socket transport service):
- //
- // * libssl calls SSLServerCertVerificationJob::Dispatch on the socket
- // transport thread.
- // * SSLServerCertVerificationJob::Dispatch queues a job
- // (instance of SSLServerCertVerificationJob) to its background thread
- // pool and returns.
- // * One of the background threads calls CERT_*Verify*, which may enqueue
- // some HTTP request(s) onto the socket transport thread, and then
- // blocks that background thread waiting for the responses and/or timeouts
- // or errors for those requests.
- // * Once those HTTP responses have all come back or failed, the
- // CERT_*Verify* function returns a result indicating that the validation
- // succeeded or failed.
- // * If the validation succeeded, then a SSLServerCertVerificationResult
- // event is posted to the socket transport thread, and the cert
- // verification thread becomes free to verify other certificates.
- // * Otherwise, a CertErrorRunnable is posted to the socket transport thread
- // and then to the main thread (blocking both, see CertErrorRunnable) to
- // do cert override processing and bad cert listener notification. Then
- // the cert verification thread becomes free to verify other certificates.
- // * After processing cert overrides, the CertErrorRunnable will dispatch a
- // SSLServerCertVerificationResult event to the socket transport thread to
- // notify it of the result of the override processing; then it returns,
- // freeing up the main thread.
- // * The SSLServerCertVerificationResult event will either wake up the
- // socket (using SSL_RestartHandshakeAfterServerCert) if validation
- // succeeded or there was an error override, or it will set an error flag
- // so that the next I/O operation on the socket will fail, causing the
- // socket transport thread to close the connection.
- //
- // Cert override processing must happen on the main thread because it accesses
- // the nsICertOverrideService, and that service must be accessed on the main
- // thread because some extensions (Selenium, in particular) replace it with a
- // Javascript implementation, and chrome JS must always be run on the main
- // thread.
- //
- // SSLServerCertVerificationResult must be dispatched to the socket transport
- // thread because we must only call SSL_* functions on the socket transport
- // thread since they may do I/O, because many parts of nsNSSSocketInfo (the
- // subclass of TransportSecurityInfo used when validating certificates during
- // an SSL handshake) and the PSM NSS I/O layer are not thread-safe, and because
- // we need the event to interrupt the PR_Poll that may waiting for I/O on the
- // socket for which we are validating the cert.
- #include "SSLServerCertVerification.h"
- #include <cstring>
- #include "BRNameMatchingPolicy.h"
- #include "CertVerifier.h"
- #include "CryptoTask.h"
- #include "ExtendedValidation.h"
- #include "NSSCertDBTrustDomain.h"
- #include "PSMRunnable.h"
- #include "ScopedNSSTypes.h"
- #include "SharedCertVerifier.h"
- #include "SharedSSLState.h"
- #include "TransportSecurityInfo.h" // For RememberCertErrorsTable
- #include "cert.h"
- #include "mozilla/Assertions.h"
- #include "mozilla/Casting.h"
- #include "mozilla/Mutex.h"
- #include "mozilla/RefPtr.h"
- #include "mozilla/UniquePtr.h"
- #include "mozilla/Unused.h"
- #include "mozilla/net/DNS.h"
- #include "nsComponentManagerUtils.h"
- #include "nsContentUtils.h"
- #include "nsIBadCertListener2.h"
- #include "nsICertOverrideService.h"
- #include "nsISiteSecurityService.h"
- #include "nsISocketProvider.h"
- #include "nsIThreadPool.h"
- #include "nsNSSCertificate.h"
- #include "nsNSSComponent.h"
- #include "nsNSSIOLayer.h"
- #include "nsNSSShutDown.h"
- #include "nsSSLStatus.h"
- #include "nsServiceManagerUtils.h"
- #include "nsURLHelper.h"
- #include "nsXPCOMCIDInternal.h"
- #include "pkix/pkix.h"
- #include "pkix/pkixnss.h"
- #include "secerr.h"
- #include "secoidt.h"
- #include "secport.h"
- #include "ssl.h"
- #include "sslerr.h"
- extern mozilla::LazyLogModule gPIPNSSLog;
- using namespace mozilla::pkix;
- namespace mozilla { namespace psm {
- namespace {
- // do not use a nsCOMPtr to avoid static initializer/destructor
- nsIThreadPool* gCertVerificationThreadPool = nullptr;
- // We add a mutex to serialize PKCS11 database operations
- Mutex* gSSLVerificationPK11Mutex = nullptr;
- } // unnamed namespace
- // Called when the socket transport thread starts, to initialize the SSL cert
- // verification thread pool. By tying the thread pool startup/shutdown directly
- // to the STS thread's lifetime, we ensure that they are *always* available for
- // SSL connections and that there are no races during startup and especially
- // shutdown. (Previously, we have had multiple problems with races in PSM
- // background threads, and the race-prevention/shutdown logic used there is
- // brittle. Since this service is critical to things like downloading updates,
- // we take no chances.) Also, by doing things this way, we avoid the need for
- // locks, since gCertVerificationThreadPool is only ever accessed on the socket
- // transport thread.
- void
- InitializeSSLServerCertVerificationThreads()
- {
- gSSLVerificationPK11Mutex = new Mutex("SSLVerificationPK11Mutex");
- // TODO: tuning, make parameters preferences
- // XXX: instantiate nsThreadPool directly, to make this more bulletproof.
- // Currently, the nsThreadPool.h header isn't exported for us to do so.
- nsresult rv = CallCreateInstance(NS_THREADPOOL_CONTRACTID,
- &gCertVerificationThreadPool);
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to create SSL cert verification threads.");
- return;
- }
- (void) gCertVerificationThreadPool->SetIdleThreadLimit(5);
- (void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000);
- (void) gCertVerificationThreadPool->SetThreadLimit(5);
- (void) gCertVerificationThreadPool->SetName(NS_LITERAL_CSTRING("SSL Cert"));
- }
- // Called when the socket transport thread finishes, to destroy the thread
- // pool. Since the socket transport service has stopped processing events, it
- // will not attempt any more SSL I/O operations, so it is clearly safe to shut
- // down the SSL cert verification infrastructure. Also, the STS will not
- // dispatch many SSL verification result events at this point, so any pending
- // cert verifications will (correctly) fail at the point they are dispatched.
- //
- // The other shutdown race condition that is possible is a race condition with
- // shutdown of the nsNSSComponent service. We use the
- // nsNSSShutdownPreventionLock where needed (not here) to prevent that.
- void StopSSLServerCertVerificationThreads()
- {
- if (gCertVerificationThreadPool) {
- gCertVerificationThreadPool->Shutdown();
- NS_RELEASE(gCertVerificationThreadPool);
- }
- if (gSSLVerificationPK11Mutex) {
- delete gSSLVerificationPK11Mutex;
- gSSLVerificationPK11Mutex = nullptr;
- }
- }
- namespace {
- void
- LogInvalidCertError(nsNSSSocketInfo* socketInfo,
- PRErrorCode errorCode,
- ::mozilla::psm::SSLErrorMessageType errorMessageType)
- {
- nsString message;
- socketInfo->GetErrorLogMessage(errorCode, errorMessageType, message);
- if (!message.IsEmpty()) {
- nsContentUtils::LogSimpleConsoleError(message, "SSL");
- }
- }
- // Dispatched to the STS thread to notify the infoObject of the verification
- // result.
- //
- // This will cause the PR_Poll in the STS thread to return, so things work
- // correctly even if the STS thread is blocked polling (only) on the file
- // descriptor that is waiting for this result.
- class SSLServerCertVerificationResult : public Runnable
- {
- public:
- NS_DECL_NSIRUNNABLE
- SSLServerCertVerificationResult(nsNSSSocketInfo* infoObject,
- PRErrorCode errorCode,
- SSLErrorMessageType errorMessageType =
- PlainErrorMessage);
- void Dispatch();
- private:
- const RefPtr<nsNSSSocketInfo> mInfoObject;
- public:
- const PRErrorCode mErrorCode;
- const SSLErrorMessageType mErrorMessageType;
- };
- class CertErrorRunnable : public SyncRunnableBase
- {
- public:
- CertErrorRunnable(const void* fdForLogging,
- nsIX509Cert* cert,
- nsNSSSocketInfo* infoObject,
- PRErrorCode defaultErrorCodeToReport,
- uint32_t collectedErrors,
- PRErrorCode errorCodeTrust,
- PRErrorCode errorCodeMismatch,
- PRErrorCode errorCodeTime,
- uint32_t providerFlags)
- : mFdForLogging(fdForLogging), mCert(cert), mInfoObject(infoObject),
- mDefaultErrorCodeToReport(defaultErrorCodeToReport),
- mCollectedErrors(collectedErrors),
- mErrorCodeTrust(errorCodeTrust),
- mErrorCodeMismatch(errorCodeMismatch),
- mErrorCodeTime(errorCodeTime),
- mProviderFlags(providerFlags)
- {
- }
- virtual void RunOnTargetThread();
- RefPtr<SSLServerCertVerificationResult> mResult; // out
- private:
- SSLServerCertVerificationResult* CheckCertOverrides();
- const void* const mFdForLogging; // may become an invalid pointer; do not dereference
- const nsCOMPtr<nsIX509Cert> mCert;
- const RefPtr<nsNSSSocketInfo> mInfoObject;
- const PRErrorCode mDefaultErrorCodeToReport;
- const uint32_t mCollectedErrors;
- const PRErrorCode mErrorCodeTrust;
- const PRErrorCode mErrorCodeMismatch;
- const PRErrorCode mErrorCodeTime;
- const uint32_t mProviderFlags;
- };
- SECStatus
- DetermineCertOverrideErrors(const UniqueCERTCertificate& cert,
- const char* hostName,
- PRTime now, PRErrorCode defaultErrorCodeToReport,
- /*out*/ uint32_t& collectedErrors,
- /*out*/ PRErrorCode& errorCodeTrust,
- /*out*/ PRErrorCode& errorCodeMismatch,
- /*out*/ PRErrorCode& errorCodeTime)
- {
- MOZ_ASSERT(cert);
- MOZ_ASSERT(hostName);
- MOZ_ASSERT(collectedErrors == 0);
- MOZ_ASSERT(errorCodeTrust == 0);
- MOZ_ASSERT(errorCodeMismatch == 0);
- MOZ_ASSERT(errorCodeTime == 0);
- // Assumes the error prioritization described in mozilla::pkix's
- // BuildForward function. Also assumes that CheckCertHostname was only
- // called if CertVerifier::VerifyCert succeeded.
- switch (defaultErrorCodeToReport) {
- case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
- case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
- case SEC_ERROR_UNKNOWN_ISSUER:
- case SEC_ERROR_CA_CERT_INVALID:
- case mozilla::pkix::MOZILLA_PKIX_ERROR_CA_CERT_USED_AS_END_ENTITY:
- case mozilla::pkix::MOZILLA_PKIX_ERROR_INADEQUATE_KEY_SIZE:
- case mozilla::pkix::MOZILLA_PKIX_ERROR_V1_CERT_USED_AS_CA:
- case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_ISSUER_CERTIFICATE:
- case mozilla::pkix::MOZILLA_PKIX_ERROR_EMPTY_ISSUER_NAME:
- {
- collectedErrors = nsICertOverrideService::ERROR_UNTRUSTED;
- errorCodeTrust = defaultErrorCodeToReport;
- SECCertTimeValidity validity = CERT_CheckCertValidTimes(cert.get(), now,
- false);
- if (validity == secCertTimeUndetermined) {
- // This only happens if cert is null. CERT_CheckCertValidTimes will
- // have set the error code to SEC_ERROR_INVALID_ARGS. We should really
- // be using mozilla::pkix here anyway.
- MOZ_ASSERT(PR_GetError() == SEC_ERROR_INVALID_ARGS);
- return SECFailure;
- }
- if (validity == secCertTimeExpired) {
- collectedErrors |= nsICertOverrideService::ERROR_TIME;
- errorCodeTime = SEC_ERROR_EXPIRED_CERTIFICATE;
- } else if (validity == secCertTimeNotValidYet) {
- collectedErrors |= nsICertOverrideService::ERROR_TIME;
- errorCodeTime =
- mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE;
- }
- break;
- }
- case SEC_ERROR_INVALID_TIME:
- case SEC_ERROR_EXPIRED_CERTIFICATE:
- case mozilla::pkix::MOZILLA_PKIX_ERROR_NOT_YET_VALID_CERTIFICATE:
- collectedErrors = nsICertOverrideService::ERROR_TIME;
- errorCodeTime = defaultErrorCodeToReport;
- break;
- case SSL_ERROR_BAD_CERT_DOMAIN:
- collectedErrors = nsICertOverrideService::ERROR_MISMATCH;
- errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
- break;
- case 0:
- NS_ERROR("No error code set during certificate validation failure.");
- PR_SetError(PR_INVALID_STATE_ERROR, 0);
- return SECFailure;
- default:
- PR_SetError(defaultErrorCodeToReport, 0);
- return SECFailure;
- }
- if (defaultErrorCodeToReport != SSL_ERROR_BAD_CERT_DOMAIN) {
- Input certInput;
- if (certInput.Init(cert->derCert.data, cert->derCert.len) != Success) {
- PR_SetError(SEC_ERROR_BAD_DER, 0);
- return SECFailure;
- }
- Input hostnameInput;
- Result result = hostnameInput.Init(
- BitwiseCast<const uint8_t*, const char*>(hostName),
- strlen(hostName));
- if (result != Success) {
- PR_SetError(SEC_ERROR_INVALID_ARGS, 0);
- return SECFailure;
- }
- // Use a lax policy so as to not generate potentially spurious name
- // mismatch "hints".
- BRNameMatchingPolicy nameMatchingPolicy(
- BRNameMatchingPolicy::Mode::DoNotEnforce);
- // CheckCertHostname expects that its input represents a certificate that
- // has already been successfully validated by BuildCertChain. This is
- // obviously not the case, however, because we're in the error path of
- // certificate verification. Thus, this is problematic. In the future, it
- // would be nice to remove this optimistic additional error checking and
- // simply punt to the front-end, which can more easily (and safely) perform
- // extra checks to give the user hints as to why verification failed.
- result = CheckCertHostname(certInput, hostnameInput, nameMatchingPolicy);
- // Treat malformed name information as a domain mismatch.
- if (result == Result::ERROR_BAD_DER ||
- result == Result::ERROR_BAD_CERT_DOMAIN) {
- collectedErrors |= nsICertOverrideService::ERROR_MISMATCH;
- errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
- } else if (IsFatalError(result)) {
- // Because its input has not been validated by BuildCertChain,
- // CheckCertHostname can return an error that is less important than the
- // original certificate verification error. Only return an error result
- // from this function if we've encountered a fatal error.
- PR_SetError(MapResultToPRErrorCode(result), 0);
- return SECFailure;
- }
- }
- return SECSuccess;
- }
- SSLServerCertVerificationResult*
- CertErrorRunnable::CheckCertOverrides()
- {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p][%p] top of CheckCertOverrides\n",
- mFdForLogging, this));
- // "Use" mFdForLogging in non-PR_LOGGING builds, too, to suppress
- // clang's -Wunused-private-field build warning for this variable:
- Unused << mFdForLogging;
- if (!NS_IsMainThread()) {
- NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
- return new SSLServerCertVerificationResult(mInfoObject,
- mDefaultErrorCodeToReport);
- }
- int32_t port;
- mInfoObject->GetPort(&port);
- nsAutoCString hostWithPortString(mInfoObject->GetHostName());
- hostWithPortString.Append(':');
- hostWithPortString.AppendInt(port);
- uint32_t remaining_display_errors = mCollectedErrors;
- // If this is an HTTP Strict Transport Security host, don't allow overrides
- // RFC 6797 section 12.1.
- bool strictTransportSecurityEnabled = false;
- nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
- if (!sss) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p][%p] couldn't get nsISiteSecurityService to check for HSTS/HPKP\n",
- mFdForLogging, this));
- return new SSLServerCertVerificationResult(mInfoObject,
- mDefaultErrorCodeToReport);
- }
- nsresult nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HSTS,
- mInfoObject->GetHostNameRaw(),
- mProviderFlags,
- nullptr,
- &strictTransportSecurityEnabled);
- if (NS_FAILED(nsrv)) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p][%p] checking for HSTS failed\n", mFdForLogging, this));
- return new SSLServerCertVerificationResult(mInfoObject,
- mDefaultErrorCodeToReport);
- }
- if (!strictTransportSecurityEnabled) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p][%p] no HSTS - overrides allowed\n",
- mFdForLogging, this));
- nsCOMPtr<nsICertOverrideService> overrideService =
- do_GetService(NS_CERTOVERRIDE_CONTRACTID);
- // it is fine to continue without the nsICertOverrideService
- uint32_t overrideBits = 0;
- if (overrideService)
- {
- bool haveOverride;
- bool isTemporaryOverride; // we don't care
- const nsACString& hostString(mInfoObject->GetHostName());
- nsrv = overrideService->HasMatchingOverride(hostString, port,
- mCert,
- &overrideBits,
- &isTemporaryOverride,
- &haveOverride);
- if (NS_SUCCEEDED(nsrv) && haveOverride)
- {
- // remove the errors that are already overriden
- remaining_display_errors &= ~overrideBits;
- }
- }
- if (!remaining_display_errors) {
- // all errors are covered by override rules, so let's accept the cert
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p][%p] All errors covered by override rules\n",
- mFdForLogging, this));
- return new SSLServerCertVerificationResult(mInfoObject, 0);
- }
- } else {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p][%p] HSTS - no overrides allowed\n",
- mFdForLogging, this));
- }
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p][%p] Certificate error was not overridden\n",
- mFdForLogging, this));
- // Ok, this is a full stop.
- // First, deliver the technical details of the broken SSL status.
- // Try to get a nsIBadCertListener2 implementation from the socket consumer.
- nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
- NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
- if (sslSocketControl) {
- nsCOMPtr<nsIInterfaceRequestor> cb;
- sslSocketControl->GetNotificationCallbacks(getter_AddRefs(cb));
- if (cb) {
- nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
- if (bcl) {
- nsIInterfaceRequestor* csi
- = static_cast<nsIInterfaceRequestor*>(mInfoObject);
- bool suppressMessage = false; // obsolete, ignored
- nsrv = bcl->NotifyCertProblem(csi, mInfoObject->SSLStatus(),
- hostWithPortString, &suppressMessage);
- }
- }
- }
- // pick the error code to report by priority
- PRErrorCode errorCodeToReport = mErrorCodeTrust ? mErrorCodeTrust
- : mErrorCodeMismatch ? mErrorCodeMismatch
- : mErrorCodeTime ? mErrorCodeTime
- : mDefaultErrorCodeToReport;
- SSLServerCertVerificationResult* result =
- new SSLServerCertVerificationResult(mInfoObject,
- errorCodeToReport,
- OverridableCertErrorMessage);
- LogInvalidCertError(mInfoObject,
- result->mErrorCode,
- result->mErrorMessageType);
- return result;
- }
- void
- CertErrorRunnable::RunOnTargetThread()
- {
- MOZ_ASSERT(NS_IsMainThread());
- mResult = CheckCertOverrides();
- MOZ_ASSERT(mResult);
- }
- // Returns null with the error code (PR_GetError()) set if it does not create
- // the CertErrorRunnable.
- CertErrorRunnable*
- CreateCertErrorRunnable(CertVerifier& certVerifier,
- PRErrorCode defaultErrorCodeToReport,
- nsNSSSocketInfo* infoObject,
- const UniqueCERTCertificate& cert,
- const void* fdForLogging,
- uint32_t providerFlags,
- PRTime now)
- {
- MOZ_ASSERT(infoObject);
- MOZ_ASSERT(cert);
- uint32_t collected_errors = 0;
- PRErrorCode errorCodeTrust = 0;
- PRErrorCode errorCodeMismatch = 0;
- PRErrorCode errorCodeTime = 0;
- if (DetermineCertOverrideErrors(cert, infoObject->GetHostNameRaw(), now,
- defaultErrorCodeToReport, collected_errors,
- errorCodeTrust, errorCodeMismatch,
- errorCodeTime) != SECSuccess) {
- // Attempt to enforce that if DetermineCertOverrideErrors failed,
- // PR_SetError was set with a non-overridable error. This is because if we
- // return from CreateCertErrorRunnable without calling
- // infoObject->SetStatusErrorBits, we won't have the required information
- // to actually add a certificate error override. This results in a broken
- // UI which is annoying but not a security disaster.
- MOZ_ASSERT(!ErrorIsOverridable(PR_GetError()));
- return nullptr;
- }
- RefPtr<nsNSSCertificate> nssCert(nsNSSCertificate::Create(cert.get()));
- if (!nssCert) {
- NS_ERROR("nsNSSCertificate::Create failed");
- PR_SetError(SEC_ERROR_NO_MEMORY, 0);
- return nullptr;
- }
- if (!collected_errors) {
- // This will happen when CERT_*Verify* only returned error(s) that are
- // not on our whitelist of overridable certificate errors.
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug, ("[%p] !collected_errors: %d\n",
- fdForLogging, static_cast<int>(defaultErrorCodeToReport)));
- PR_SetError(defaultErrorCodeToReport, 0);
- return nullptr;
- }
- infoObject->SetStatusErrorBits(nssCert, collected_errors);
- return new CertErrorRunnable(fdForLogging,
- static_cast<nsIX509Cert*>(nssCert.get()),
- infoObject, defaultErrorCodeToReport,
- collected_errors, errorCodeTrust,
- errorCodeMismatch, errorCodeTime,
- providerFlags);
- }
- // When doing async cert processing, we dispatch one of these runnables to the
- // socket transport service thread, which blocks the socket transport
- // service thread while it waits for the inner CertErrorRunnable to execute
- // CheckCertOverrides on the main thread. CheckCertOverrides must block events
- // on both of these threads because it calls TransportSecurityInfo::GetInterface(),
- // which may call nsHttpConnection::GetInterface() through
- // TransportSecurityInfo::mCallbacks. nsHttpConnection::GetInterface must always
- // execute on the main thread, with the socket transport service thread
- // blocked.
- class CertErrorRunnableRunnable : public Runnable
- {
- public:
- explicit CertErrorRunnableRunnable(CertErrorRunnable* certErrorRunnable)
- : mCertErrorRunnable(certErrorRunnable)
- {
- }
- private:
- NS_IMETHOD Run() override
- {
- nsresult rv = mCertErrorRunnable->DispatchToMainThreadAndWait();
- // The result must run on the socket transport thread, which we are already
- // on, so we can just run it directly, instead of dispatching it.
- if (NS_SUCCEEDED(rv)) {
- rv = mCertErrorRunnable->mResult ? mCertErrorRunnable->mResult->Run()
- : NS_ERROR_UNEXPECTED;
- }
- return rv;
- }
- RefPtr<CertErrorRunnable> mCertErrorRunnable;
- };
- class SSLServerCertVerificationJob : public Runnable
- {
- public:
- // Must be called only on the socket transport thread
- static SECStatus Dispatch(const RefPtr<SharedCertVerifier>& certVerifier,
- const void* fdForLogging,
- nsNSSSocketInfo* infoObject,
- const UniqueCERTCertificate& serverCert,
- const UniqueCERTCertList& peerCertChain,
- const SECItem* stapledOCSPResponse,
- const SECItem* sctsFromTLSExtension,
- uint32_t providerFlags,
- Time time,
- PRTime prtime);
- private:
- NS_DECL_NSIRUNNABLE
- // Must be called only on the socket transport thread
- SSLServerCertVerificationJob(const RefPtr<SharedCertVerifier>& certVerifier,
- const void* fdForLogging,
- nsNSSSocketInfo* infoObject,
- const UniqueCERTCertificate& cert,
- UniqueCERTCertList peerCertChain,
- const SECItem* stapledOCSPResponse,
- const SECItem* sctsFromTLSExtension,
- uint32_t providerFlags,
- Time time,
- PRTime prtime);
- const RefPtr<SharedCertVerifier> mCertVerifier;
- const void* const mFdForLogging;
- const RefPtr<nsNSSSocketInfo> mInfoObject;
- const UniqueCERTCertificate mCert;
- UniqueCERTCertList mPeerCertChain;
- const uint32_t mProviderFlags;
- const Time mTime;
- const PRTime mPRTime;
- const TimeStamp mJobStartTime;
- const UniqueSECItem mStapledOCSPResponse;
- const UniqueSECItem mSCTsFromTLSExtension;
- };
- SSLServerCertVerificationJob::SSLServerCertVerificationJob(
- const RefPtr<SharedCertVerifier>& certVerifier, const void* fdForLogging,
- nsNSSSocketInfo* infoObject, const UniqueCERTCertificate& cert,
- UniqueCERTCertList peerCertChain, const SECItem* stapledOCSPResponse,
- const SECItem* sctsFromTLSExtension,
- uint32_t providerFlags, Time time, PRTime prtime)
- : mCertVerifier(certVerifier)
- , mFdForLogging(fdForLogging)
- , mInfoObject(infoObject)
- , mCert(CERT_DupCertificate(cert.get()))
- , mPeerCertChain(Move(peerCertChain))
- , mProviderFlags(providerFlags)
- , mTime(time)
- , mPRTime(prtime)
- , mJobStartTime(TimeStamp::Now())
- , mStapledOCSPResponse(SECITEM_DupItem(stapledOCSPResponse))
- , mSCTsFromTLSExtension(SECITEM_DupItem(sctsFromTLSExtension))
- {
- }
- // This function assumes that we will only use the SPDY connection coalescing
- // feature on connections where we have negotiated SPDY using NPN. If we ever
- // talk SPDY without having negotiated it with SPDY, this code will give wrong
- // and perhaps unsafe results.
- //
- // Returns SECSuccess on the initial handshake of all connections, on
- // renegotiations for any connections where we did not negotiate SPDY, or on any
- // SPDY connection where the server's certificate did not change.
- //
- // Prohibit changing the server cert only if we negotiated SPDY,
- // in order to support SPDY's cross-origin connection pooling.
- static SECStatus
- BlockServerCertChangeForSpdy(nsNSSSocketInfo* infoObject,
- const UniqueCERTCertificate& serverCert)
- {
- // Get the existing cert. If there isn't one, then there is
- // no cert change to worry about.
- nsCOMPtr<nsIX509Cert> cert;
- RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
- if (!status) {
- // If we didn't have a status, then this is the
- // first handshake on this connection, not a
- // renegotiation.
- return SECSuccess;
- }
- status->GetServerCert(getter_AddRefs(cert));
- if (!cert) {
- NS_NOTREACHED("every nsSSLStatus must have a cert"
- "that implements nsIX509Cert");
- PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
- return SECFailure;
- }
- // Filter out sockets that did not neogtiate SPDY via NPN
- nsAutoCString negotiatedNPN;
- nsresult rv = infoObject->GetNegotiatedNPN(negotiatedNPN);
- NS_ASSERTION(NS_SUCCEEDED(rv),
- "GetNegotiatedNPN() failed during renegotiation");
- if (NS_SUCCEEDED(rv) && !StringBeginsWith(negotiatedNPN,
- NS_LITERAL_CSTRING("spdy/"))) {
- return SECSuccess;
- }
- // If GetNegotiatedNPN() failed we will assume spdy for safety's safe
- if (NS_FAILED(rv)) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call."
- " Assuming spdy.\n"));
- }
- // Check to see if the cert has actually changed
- UniqueCERTCertificate c(cert->GetCert());
- NS_ASSERTION(c, "very bad and hopefully impossible state");
- bool sameCert = CERT_CompareCerts(c.get(), serverCert.get());
- if (sameCert) {
- return SECSuccess;
- }
- // Report an error - changed cert is confirmed
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("SPDY Refused to allow new cert during renegotiation\n"));
- PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0);
- return SECFailure;
- }
- // Note: Takes ownership of |peerCertChain| if SECSuccess is not returned.
- SECStatus
- AuthCertificate(CertVerifier& certVerifier,
- nsNSSSocketInfo* infoObject,
- const UniqueCERTCertificate& cert,
- UniqueCERTCertList& peerCertChain,
- const SECItem* stapledOCSPResponse,
- const SECItem* sctsFromTLSExtension,
- uint32_t providerFlags,
- Time time)
- {
- MOZ_ASSERT(infoObject);
- MOZ_ASSERT(cert);
- // We want to avoid storing any intermediate cert information when browsing
- // in private, transient contexts.
- bool saveIntermediates =
- !(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE);
- SECOidTag evOidPolicy;
- UniqueCERTCertList certList;
- CertVerifier::OCSPStaplingStatus ocspStaplingStatus =
- CertVerifier::OCSP_STAPLING_NEVER_CHECKED;
- KeySizeStatus keySizeStatus = KeySizeStatus::NeverChecked;
- SHA1ModeResult sha1ModeResult = SHA1ModeResult::NeverChecked;
- CertificateTransparencyInfo certificateTransparencyInfo;
- int flags = 0;
- if (!infoObject->SharedState().IsOCSPStaplingEnabled() ||
- !infoObject->SharedState().IsOCSPMustStapleEnabled()) {
- flags |= CertVerifier::FLAG_TLS_IGNORE_STATUS_REQUEST;
- }
- Result rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
- sctsFromTLSExtension, time,
- infoObject,
- infoObject->GetHostNameRaw(),
- certList, saveIntermediates,
- flags, infoObject->
- GetOriginAttributes(),
- &evOidPolicy,
- &ocspStaplingStatus,
- &keySizeStatus, &sha1ModeResult,
- &certificateTransparencyInfo);
- if (rv == Success) {
- // Certificate verification succeeded. Delete any potential record of
- // certificate error bits.
- RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject,
- nullptr,
- SECSuccess);
- // The connection may get terminated, for example, if the server requires
- // a client cert. Let's provide a minimal SSLStatus
- // to the caller that contains at least the cert and its status.
- RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
- if (!status) {
- status = new nsSSLStatus();
- infoObject->SetSSLStatus(status);
- }
- if (!status->HasServerCert()) {
- EVStatus evStatus;
- if (evOidPolicy == SEC_OID_UNKNOWN) {
- evStatus = EVStatus::NotEV;
- } else {
- evStatus = EVStatus::EV;
- }
- RefPtr<nsNSSCertificate> nsc = nsNSSCertificate::Create(cert.get());
- status->SetServerCert(nsc, evStatus);
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("AuthCertificate setting NEW cert %p", nsc.get()));
- }
- status->SetCertificateTransparencyInfo(certificateTransparencyInfo);
- }
- if (rv != Success) {
- // Certificate validation failed; store the peer certificate chain on
- // infoObject so it can be used for error reporting.
- infoObject->SetFailedCertChain(Move(peerCertChain));
- PR_SetError(MapResultToPRErrorCode(rv), 0);
- }
- return rv == Success ? SECSuccess : SECFailure;
- }
- /*static*/ SECStatus
- SSLServerCertVerificationJob::Dispatch(
- const RefPtr<SharedCertVerifier>& certVerifier,
- const void* fdForLogging,
- nsNSSSocketInfo* infoObject,
- const UniqueCERTCertificate& serverCert,
- const UniqueCERTCertList& peerCertChain,
- const SECItem* stapledOCSPResponse,
- const SECItem* sctsFromTLSExtension,
- uint32_t providerFlags,
- Time time,
- PRTime prtime)
- {
- // Runs on the socket transport thread
- if (!certVerifier || !infoObject || !serverCert) {
- NS_ERROR("Invalid parameters for SSL server cert validation");
- PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
- return SECFailure;
- }
- // Copy the certificate list so the runnable can take ownership of it in the
- // constructor.
- // We can safely skip checking if NSS has already shut down here since we're
- // in the middle of verifying a certificate.
- nsNSSShutDownPreventionLock lock;
- UniqueCERTCertList peerCertChainCopy =
- nsNSSCertList::DupCertList(peerCertChain, lock);
- if (!peerCertChainCopy) {
- PR_SetError(SEC_ERROR_NO_MEMORY, 0);
- return SECFailure;
- }
- RefPtr<SSLServerCertVerificationJob> job(
- new SSLServerCertVerificationJob(certVerifier, fdForLogging, infoObject,
- serverCert, Move(peerCertChainCopy),
- stapledOCSPResponse, sctsFromTLSExtension,
- providerFlags, time, prtime));
- nsresult nrv;
- if (!gCertVerificationThreadPool) {
- nrv = NS_ERROR_NOT_INITIALIZED;
- } else {
- nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
- }
- if (NS_FAILED(nrv)) {
- // We can't call SetCertVerificationResult here to change
- // mCertVerificationState because SetCertVerificationResult will call
- // libssl functions that acquire SSL locks that are already being held at
- // this point. infoObject->mCertVerificationState will be stuck at
- // waiting_for_cert_verification here, but that is OK because we already
- // have to be able to handle cases where we encounter non-cert errors while
- // in that state.
- PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY
- ? SEC_ERROR_NO_MEMORY
- : PR_INVALID_STATE_ERROR;
- PORT_SetError(error);
- return SECFailure;
- }
- PORT_SetError(PR_WOULD_BLOCK_ERROR);
- return SECWouldBlock;
- }
- NS_IMETHODIMP
- SSLServerCertVerificationJob::Run()
- {
- // Runs on a cert verification thread
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] SSLServerCertVerificationJob::Run\n", mInfoObject.get()));
- PRErrorCode error;
- nsNSSShutDownPreventionLock nssShutdownPrevention;
- if (mInfoObject->isAlreadyShutDown()) {
- error = SEC_ERROR_USER_CANCELLED;
- } else {
- // Reset the error code here so we can detect if AuthCertificate fails to
- // set the error code if/when it fails.
- PR_SetError(0, 0);
- SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert,
- mPeerCertChain, mStapledOCSPResponse.get(),
- mSCTsFromTLSExtension.get(),
- mProviderFlags, mTime);
- MOZ_ASSERT(mPeerCertChain || rv != SECSuccess,
- "AuthCertificate() should take ownership of chain on failure");
- if (rv == SECSuccess) {
- RefPtr<SSLServerCertVerificationResult> restart(
- new SSLServerCertVerificationResult(mInfoObject, 0));
- restart->Dispatch();
- return NS_OK;
- }
- // Note: the interval is not calculated once as PR_GetError MUST be called
- // before any other function call
- error = PR_GetError();
- if (error != 0) {
- RefPtr<CertErrorRunnable> runnable(
- CreateCertErrorRunnable(*mCertVerifier, error, mInfoObject, mCert,
- mFdForLogging, mProviderFlags, mPRTime));
- if (!runnable) {
- // CreateCertErrorRunnable set a new error code
- error = PR_GetError();
- } else {
- // We must block the the socket transport service thread while the
- // main thread executes the CertErrorRunnable. The CertErrorRunnable
- // will dispatch the result asynchronously, so we don't have to block
- // this thread waiting for it.
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p][%p] Before dispatching CertErrorRunnable\n",
- mFdForLogging, runnable.get()));
- nsresult nrv;
- nsCOMPtr<nsIEventTarget> stsTarget
- = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
- if (NS_SUCCEEDED(nrv)) {
- nrv = stsTarget->Dispatch(new CertErrorRunnableRunnable(runnable),
- NS_DISPATCH_NORMAL);
- }
- if (NS_SUCCEEDED(nrv)) {
- return NS_OK;
- }
- NS_ERROR("Failed to dispatch CertErrorRunnable");
- error = PR_INVALID_STATE_ERROR;
- }
- }
- }
- if (error == 0) {
- NS_NOTREACHED("no error set during certificate validation failure");
- error = PR_INVALID_STATE_ERROR;
- }
- RefPtr<SSLServerCertVerificationResult> failure(
- new SSLServerCertVerificationResult(mInfoObject, error));
- failure->Dispatch();
- return NS_OK;
- }
- } // unnamed namespace
- // Extracts whatever information we need out of fd (using SSL_*) and passes it
- // to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should
- // never do anything with fd except logging.
- SECStatus
- AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
- {
- RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
- if (!certVerifier) {
- PR_SetError(SEC_ERROR_NOT_INITIALIZED, 0);
- return SECFailure;
- }
- // Runs on the socket transport thread
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] starting AuthCertificateHook\n", fd));
- // Modern libssl always passes PR_TRUE for checkSig, and we have no means of
- // doing verification without checking signatures.
- NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
- // PSM never causes libssl to call this function with PR_TRUE for isServer,
- // and many things in PSM assume that we are a client.
- NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
- nsNSSSocketInfo* socketInfo = static_cast<nsNSSSocketInfo*>(arg);
- UniqueCERTCertificate serverCert(SSL_PeerCertificate(fd));
- if (!checkSig || isServer || !socketInfo || !serverCert) {
- PR_SetError(PR_INVALID_STATE_ERROR, 0);
- return SECFailure;
- }
- // Get the peer certificate chain for error reporting
- UniqueCERTCertList peerCertChain(SSL_PeerCertificateChain(fd));
- if (!peerCertChain) {
- PR_SetError(PR_INVALID_STATE_ERROR, 0);
- return SECFailure;
- }
- socketInfo->SetFullHandshake();
- Time now(Now());
- PRTime prnow(PR_Now());
- if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess)
- return SECFailure;
- nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
- NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, socketInfo));
- if (sslSocketControl && sslSocketControl->GetBypassAuthentication()) {
- MOZ_LOG(gPIPNSSLog, LogLevel::Debug,
- ("[%p] Bypass Auth in AuthCertificateHook\n", fd));
- return SECSuccess;
- }
- bool onSTSThread;
- nsresult nrv;
- nsCOMPtr<nsIEventTarget> sts
- = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
- if (NS_SUCCEEDED(nrv)) {
- nrv = sts->IsOnCurrentThread(&onSTSThread);
- }
- if (NS_FAILED(nrv)) {
- NS_ERROR("Could not get STS service or IsOnCurrentThread failed");
- PR_SetError(PR_UNKNOWN_ERROR, 0);
- return SECFailure;
- }
- // SSL_PeerStapledOCSPResponses will never return a non-empty response if
- // OCSP stapling wasn't enabled because libssl wouldn't have let the server
- // return a stapled OCSP response.
- // We don't own these pointers.
- const SECItemArray* csa = SSL_PeerStapledOCSPResponses(fd);
- SECItem* stapledOCSPResponse = nullptr;
- // we currently only support single stapled responses
- if (csa && csa->len == 1) {
- stapledOCSPResponse = &csa->items[0];
- }
- const SECItem* sctsFromTLSExtension = SSL_PeerSignedCertTimestamps(fd);
- if (sctsFromTLSExtension && sctsFromTLSExtension->len == 0) {
- // SSL_PeerSignedCertTimestamps returns null on error and empty item
- // when no extension was returned by the server. We always use null when
- // no extension was received (for whatever reason), ignoring errors.
- sctsFromTLSExtension = nullptr;
- }
- uint32_t providerFlags = 0;
- socketInfo->GetProviderFlags(&providerFlags);
- if (onSTSThread) {
- // We *must* do certificate verification on a background thread because
- // we need the socket transport thread to be free for our OCSP requests,
- // and we *want* to do certificate verification on a background thread
- // because of the performance benefits of doing so.
- socketInfo->SetCertVerificationWaiting();
- SECStatus rv = SSLServerCertVerificationJob::Dispatch(
- certVerifier, static_cast<const void*>(fd), socketInfo,
- serverCert, peerCertChain, stapledOCSPResponse,
- sctsFromTLSExtension, providerFlags, now, prnow);
- return rv;
- }
- // We can't do certificate verification on a background thread, because the
- // thread doing the network I/O may not interrupt its network I/O on receipt
- // of our SSLServerCertVerificationResult event, and/or it might not even be
- // a non-blocking socket.
- SECStatus rv = AuthCertificate(*certVerifier, socketInfo, serverCert,
- peerCertChain, stapledOCSPResponse,
- sctsFromTLSExtension, providerFlags, now);
- MOZ_ASSERT(peerCertChain || rv != SECSuccess,
- "AuthCertificate() should take ownership of chain on failure");
- if (rv == SECSuccess) {
- return SECSuccess;
- }
- PRErrorCode error = PR_GetError();
- if (error != 0) {
- RefPtr<CertErrorRunnable> runnable(
- CreateCertErrorRunnable(*certVerifier, error, socketInfo, serverCert,
- static_cast<const void*>(fd), providerFlags,
- prnow));
- if (!runnable) {
- // CreateCertErrorRunnable sets a new error code when it fails
- error = PR_GetError();
- } else {
- // We have to return SECSuccess or SECFailure based on the result of the
- // override processing, so we must block this thread waiting for it. The
- // CertErrorRunnable will NOT dispatch the result at all, since we passed
- // false for CreateCertErrorRunnable's async parameter
- nrv = runnable->DispatchToMainThreadAndWait();
- if (NS_FAILED(nrv)) {
- NS_ERROR("Failed to dispatch CertErrorRunnable");
- PR_SetError(PR_INVALID_STATE_ERROR, 0);
- return SECFailure;
- }
- if (!runnable->mResult) {
- NS_ERROR("CertErrorRunnable did not set result");
- PR_SetError(PR_INVALID_STATE_ERROR, 0);
- return SECFailure;
- }
- if (runnable->mResult->mErrorCode == 0) {
- return SECSuccess; // cert error override occurred.
- }
- // We must call SetCanceled here to set the error message type
- // in case it isn't PlainErrorMessage, which is what we would
- // default to if we just called
- // PR_SetError(runnable->mResult->mErrorCode, 0) and returned
- // SECFailure without doing this.
- socketInfo->SetCanceled(runnable->mResult->mErrorCode,
- runnable->mResult->mErrorMessageType);
- error = runnable->mResult->mErrorCode;
- }
- }
- if (error == 0) {
- NS_ERROR("error code not set");
- error = PR_UNKNOWN_ERROR;
- }
- PR_SetError(error, 0);
- return SECFailure;
- }
- SSLServerCertVerificationResult::SSLServerCertVerificationResult(
- nsNSSSocketInfo* infoObject, PRErrorCode errorCode,
- SSLErrorMessageType errorMessageType)
- : mInfoObject(infoObject)
- , mErrorCode(errorCode)
- , mErrorMessageType(errorMessageType)
- {
- }
- void
- SSLServerCertVerificationResult::Dispatch()
- {
- nsresult rv;
- nsCOMPtr<nsIEventTarget> stsTarget
- = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
- NS_ASSERTION(stsTarget,
- "Failed to get socket transport service event target");
- rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
- NS_ASSERTION(NS_SUCCEEDED(rv),
- "Failed to dispatch SSLServerCertVerificationResult");
- }
- NS_IMETHODIMP
- SSLServerCertVerificationResult::Run()
- {
- // XXX: This cast will be removed by the next patch
- ((nsNSSSocketInfo*) mInfoObject.get())
- ->SetCertVerificationResult(mErrorCode, mErrorMessageType);
- return NS_OK;
- }
- } } // namespace mozilla::psm
|