security/manager/ssl/src/SSLServerCertVerification.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
michael@0 2 *
michael@0 3 * This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 // For connections that are not processed on the socket transport thread, we do
michael@0 8 // NOT use the async logic described below. Instead, we authenticate the
michael@0 9 // certificate on the thread that the connection's I/O happens on,
michael@0 10 // synchronously. This allows us to do certificate verification for blocking
michael@0 11 // (not non-blocking) sockets and sockets that have their I/O processed on a
michael@0 12 // thread other than the socket transport service thread. Also, we DO NOT
michael@0 13 // support blocking sockets on the socket transport service thread at all.
michael@0 14 //
michael@0 15 // During certificate authentication, we call CERT_PKIXVerifyCert or
michael@0 16 // CERT_VerifyCert. These functions may make zero or more HTTP requests
michael@0 17 // for OCSP responses, CRLs, intermediate certificates, etc. Our fetching logic
michael@0 18 // for these requests processes them on the socket transport service thread.
michael@0 19 //
michael@0 20 // If the connection for which we are verifying the certificate is happening
michael@0 21 // on the socket transport thread (the usually case, at least for HTTP), then
michael@0 22 // if our cert auth hook were to call the CERT_*Verify* functions directly,
michael@0 23 // there would be a deadlock: The CERT_*Verify* function would cause an event
michael@0 24 // to be asynchronously posted to the socket transport thread, and then it
michael@0 25 // would block the socket transport thread waiting to be notified of the HTTP
michael@0 26 // response. However, the HTTP request would never actually be processed
michael@0 27 // because the socket transport thread would be blocked and so it wouldn't be
michael@0 28 // able process HTTP requests. (i.e. Deadlock.)
michael@0 29 //
michael@0 30 // Consequently, when we are asked to verify a certificate on the socket
michael@0 31 // transport service thread, we must always call the CERT_*Verify* cert
michael@0 32 // functions on another thread. To accomplish this, our auth cert hook
michael@0 33 // dispatches a SSLServerCertVerificationJob to a pool of background threads,
michael@0 34 // and then immediatley return SECWouldBlock to libssl. These jobs are where
michael@0 35 // the CERT_*Verify* functions are actually called.
michael@0 36 //
michael@0 37 // When our auth cert hook returns SECWouldBlock, libssl will carry on the
michael@0 38 // handshake while we validate the certificate. This will free up the socket
michael@0 39 // transport thread so that HTTP requests--in particular, the OCSP/CRL/cert
michael@0 40 // requests needed for cert verification as mentioned above--can be processed.
michael@0 41 //
michael@0 42 // Once the CERT_*Verify* function returns, the cert verification job
michael@0 43 // dispatches a SSLServerCertVerificationResult to the socket transport thread;
michael@0 44 // the SSLServerCertVerificationResult will notify libssl that the certificate
michael@0 45 // authentication is complete. Once libssl is notified that the authentication
michael@0 46 // is complete, it will continue the SSL handshake (if it hasn't already
michael@0 47 // finished) and it will begin allowing us to send/receive data on the
michael@0 48 // connection.
michael@0 49 //
michael@0 50 // Timeline of events (for connections managed by the socket transport service):
michael@0 51 //
michael@0 52 // * libssl calls SSLServerCertVerificationJob::Dispatch on the socket
michael@0 53 // transport thread.
michael@0 54 // * SSLServerCertVerificationJob::Dispatch queues a job
michael@0 55 // (instance of SSLServerCertVerificationJob) to its background thread
michael@0 56 // pool and returns.
michael@0 57 // * One of the background threads calls CERT_*Verify*, which may enqueue
michael@0 58 // some HTTP request(s) onto the socket transport thread, and then
michael@0 59 // blocks that background thread waiting for the responses and/or timeouts
michael@0 60 // or errors for those requests.
michael@0 61 // * Once those HTTP responses have all come back or failed, the
michael@0 62 // CERT_*Verify* function returns a result indicating that the validation
michael@0 63 // succeeded or failed.
michael@0 64 // * If the validation succeeded, then a SSLServerCertVerificationResult
michael@0 65 // event is posted to the socket transport thread, and the cert
michael@0 66 // verification thread becomes free to verify other certificates.
michael@0 67 // * Otherwise, a CertErrorRunnable is posted to the socket transport thread
michael@0 68 // and then to the main thread (blocking both, see CertErrorRunnable) to
michael@0 69 // do cert override processing and bad cert listener notification. Then
michael@0 70 // the cert verification thread becomes free to verify other certificates.
michael@0 71 // * After processing cert overrides, the CertErrorRunnable will dispatch a
michael@0 72 // SSLServerCertVerificationResult event to the socket transport thread to
michael@0 73 // notify it of the result of the override processing; then it returns,
michael@0 74 // freeing up the main thread.
michael@0 75 // * The SSLServerCertVerificationResult event will either wake up the
michael@0 76 // socket (using SSL_RestartHandshakeAfterServerCert) if validation
michael@0 77 // succeeded or there was an error override, or it will set an error flag
michael@0 78 // so that the next I/O operation on the socket will fail, causing the
michael@0 79 // socket transport thread to close the connection.
michael@0 80 //
michael@0 81 // Cert override processing must happen on the main thread because it accesses
michael@0 82 // the nsICertOverrideService, and that service must be accessed on the main
michael@0 83 // thread because some extensions (Selenium, in particular) replace it with a
michael@0 84 // Javascript implementation, and chrome JS must always be run on the main
michael@0 85 // thread.
michael@0 86 //
michael@0 87 // SSLServerCertVerificationResult must be dispatched to the socket transport
michael@0 88 // thread because we must only call SSL_* functions on the socket transport
michael@0 89 // thread since they may do I/O, because many parts of nsNSSSocketInfo (the
michael@0 90 // subclass of TransportSecurityInfo used when validating certificates during
michael@0 91 // an SSL handshake) and the PSM NSS I/O layer are not thread-safe, and because
michael@0 92 // we need the event to interrupt the PR_Poll that may waiting for I/O on the
michael@0 93 // socket for which we are validating the cert.
michael@0 94
michael@0 95 #include "SSLServerCertVerification.h"
michael@0 96
michael@0 97 #include <cstring>
michael@0 98
michael@0 99 #include "pkix/pkixtypes.h"
michael@0 100 #include "CertVerifier.h"
michael@0 101 #include "CryptoTask.h"
michael@0 102 #include "ExtendedValidation.h"
michael@0 103 #include "NSSCertDBTrustDomain.h"
michael@0 104 #include "nsIBadCertListener2.h"
michael@0 105 #include "nsICertOverrideService.h"
michael@0 106 #include "nsISiteSecurityService.h"
michael@0 107 #include "nsNSSComponent.h"
michael@0 108 #include "nsNSSCleaner.h"
michael@0 109 #include "nsRecentBadCerts.h"
michael@0 110 #include "nsNSSIOLayer.h"
michael@0 111 #include "nsNSSShutDown.h"
michael@0 112
michael@0 113 #include "mozilla/Assertions.h"
michael@0 114 #include "mozilla/Mutex.h"
michael@0 115 #include "mozilla/Telemetry.h"
michael@0 116 #include "mozilla/unused.h"
michael@0 117 #include "nsIThreadPool.h"
michael@0 118 #include "nsNetUtil.h"
michael@0 119 #include "nsXPCOMCIDInternal.h"
michael@0 120 #include "nsComponentManagerUtils.h"
michael@0 121 #include "nsServiceManagerUtils.h"
michael@0 122 #include "PSMRunnable.h"
michael@0 123 #include "SharedSSLState.h"
michael@0 124 #include "nsContentUtils.h"
michael@0 125
michael@0 126 #include "ssl.h"
michael@0 127 #include "secerr.h"
michael@0 128 #include "secport.h"
michael@0 129 #include "sslerr.h"
michael@0 130 #include "ocsp.h"
michael@0 131
michael@0 132 #ifdef PR_LOGGING
michael@0 133 extern PRLogModuleInfo* gPIPNSSLog;
michael@0 134 #endif
michael@0 135
michael@0 136 namespace mozilla { namespace psm {
michael@0 137
michael@0 138 namespace {
michael@0 139
michael@0 140 NS_DEFINE_CID(kNSSComponentCID, NS_NSSCOMPONENT_CID);
michael@0 141
michael@0 142 NSSCleanupAutoPtrClass(CERTCertificate, CERT_DestroyCertificate)
michael@0 143 NSSCleanupAutoPtrClass_WithParam(PLArenaPool, PORT_FreeArena, FalseParam, false)
michael@0 144
michael@0 145 // do not use a nsCOMPtr to avoid static initializer/destructor
michael@0 146 nsIThreadPool* gCertVerificationThreadPool = nullptr;
michael@0 147
michael@0 148 // We avoid using a mutex for the success case to avoid lock-related
michael@0 149 // performance issues. However, we do use a lock in the error case to simplify
michael@0 150 // the code, since performance in the error case is not important.
michael@0 151 Mutex* gSSLVerificationTelemetryMutex = nullptr;
michael@0 152
michael@0 153 // We add a mutex to serialize PKCS11 database operations
michael@0 154 Mutex* gSSLVerificationPK11Mutex = nullptr;
michael@0 155
michael@0 156 } // unnamed namespace
michael@0 157
michael@0 158 // Called when the socket transport thread starts, to initialize the SSL cert
michael@0 159 // verification thread pool. By tying the thread pool startup/shutdown directly
michael@0 160 // to the STS thread's lifetime, we ensure that they are *always* available for
michael@0 161 // SSL connections and that there are no races during startup and especially
michael@0 162 // shutdown. (Previously, we have had multiple problems with races in PSM
michael@0 163 // background threads, and the race-prevention/shutdown logic used there is
michael@0 164 // brittle. Since this service is critical to things like downloading updates,
michael@0 165 // we take no chances.) Also, by doing things this way, we avoid the need for
michael@0 166 // locks, since gCertVerificationThreadPool is only ever accessed on the socket
michael@0 167 // transport thread.
michael@0 168 void
michael@0 169 InitializeSSLServerCertVerificationThreads()
michael@0 170 {
michael@0 171 gSSLVerificationTelemetryMutex = new Mutex("SSLVerificationTelemetryMutex");
michael@0 172 gSSLVerificationPK11Mutex = new Mutex("SSLVerificationPK11Mutex");
michael@0 173 // TODO: tuning, make parameters preferences
michael@0 174 // XXX: instantiate nsThreadPool directly, to make this more bulletproof.
michael@0 175 // Currently, the nsThreadPool.h header isn't exported for us to do so.
michael@0 176 nsresult rv = CallCreateInstance(NS_THREADPOOL_CONTRACTID,
michael@0 177 &gCertVerificationThreadPool);
michael@0 178 if (NS_FAILED(rv)) {
michael@0 179 NS_WARNING("Failed to create SSL cert verification threads.");
michael@0 180 return;
michael@0 181 }
michael@0 182
michael@0 183 (void) gCertVerificationThreadPool->SetIdleThreadLimit(5);
michael@0 184 (void) gCertVerificationThreadPool->SetIdleThreadTimeout(30 * 1000);
michael@0 185 (void) gCertVerificationThreadPool->SetThreadLimit(5);
michael@0 186 (void) gCertVerificationThreadPool->SetName(NS_LITERAL_CSTRING("SSL Cert"));
michael@0 187 }
michael@0 188
michael@0 189 // Called when the socket transport thread finishes, to destroy the thread
michael@0 190 // pool. Since the socket transport service has stopped processing events, it
michael@0 191 // will not attempt any more SSL I/O operations, so it is clearly safe to shut
michael@0 192 // down the SSL cert verification infrastructure. Also, the STS will not
michael@0 193 // dispatch many SSL verification result events at this point, so any pending
michael@0 194 // cert verifications will (correctly) fail at the point they are dispatched.
michael@0 195 //
michael@0 196 // The other shutdown race condition that is possible is a race condition with
michael@0 197 // shutdown of the nsNSSComponent service. We use the
michael@0 198 // nsNSSShutdownPreventionLock where needed (not here) to prevent that.
michael@0 199 void StopSSLServerCertVerificationThreads()
michael@0 200 {
michael@0 201 if (gCertVerificationThreadPool) {
michael@0 202 gCertVerificationThreadPool->Shutdown();
michael@0 203 NS_RELEASE(gCertVerificationThreadPool);
michael@0 204 }
michael@0 205 if (gSSLVerificationTelemetryMutex) {
michael@0 206 delete gSSLVerificationTelemetryMutex;
michael@0 207 gSSLVerificationTelemetryMutex = nullptr;
michael@0 208 }
michael@0 209 if (gSSLVerificationPK11Mutex) {
michael@0 210 delete gSSLVerificationPK11Mutex;
michael@0 211 gSSLVerificationPK11Mutex = nullptr;
michael@0 212 }
michael@0 213 }
michael@0 214
michael@0 215 namespace {
michael@0 216
michael@0 217 void
michael@0 218 LogInvalidCertError(TransportSecurityInfo* socketInfo,
michael@0 219 PRErrorCode errorCode,
michael@0 220 ::mozilla::psm::SSLErrorMessageType errorMessageType)
michael@0 221 {
michael@0 222 nsString message;
michael@0 223 socketInfo->GetErrorLogMessage(errorCode, errorMessageType, message);
michael@0 224 if (!message.IsEmpty()) {
michael@0 225 nsContentUtils::LogSimpleConsoleError(message, "SSL");
michael@0 226 }
michael@0 227 }
michael@0 228
michael@0 229 // Dispatched to the STS thread to notify the infoObject of the verification
michael@0 230 // result.
michael@0 231 //
michael@0 232 // This will cause the PR_Poll in the STS thread to return, so things work
michael@0 233 // correctly even if the STS thread is blocked polling (only) on the file
michael@0 234 // descriptor that is waiting for this result.
michael@0 235 class SSLServerCertVerificationResult : public nsRunnable
michael@0 236 {
michael@0 237 public:
michael@0 238 NS_DECL_NSIRUNNABLE
michael@0 239
michael@0 240 SSLServerCertVerificationResult(TransportSecurityInfo* infoObject,
michael@0 241 PRErrorCode errorCode,
michael@0 242 Telemetry::ID telemetryID = Telemetry::HistogramCount,
michael@0 243 uint32_t telemetryValue = -1,
michael@0 244 SSLErrorMessageType errorMessageType =
michael@0 245 PlainErrorMessage);
michael@0 246
michael@0 247 void Dispatch();
michael@0 248 private:
michael@0 249 const RefPtr<TransportSecurityInfo> mInfoObject;
michael@0 250 public:
michael@0 251 const PRErrorCode mErrorCode;
michael@0 252 const SSLErrorMessageType mErrorMessageType;
michael@0 253 const Telemetry::ID mTelemetryID;
michael@0 254 const uint32_t mTelemetryValue;
michael@0 255 };
michael@0 256
michael@0 257 class CertErrorRunnable : public SyncRunnableBase
michael@0 258 {
michael@0 259 public:
michael@0 260 CertErrorRunnable(const void* fdForLogging,
michael@0 261 nsIX509Cert* cert,
michael@0 262 TransportSecurityInfo* infoObject,
michael@0 263 PRErrorCode defaultErrorCodeToReport,
michael@0 264 uint32_t collectedErrors,
michael@0 265 PRErrorCode errorCodeTrust,
michael@0 266 PRErrorCode errorCodeMismatch,
michael@0 267 PRErrorCode errorCodeExpired,
michael@0 268 uint32_t providerFlags)
michael@0 269 : mFdForLogging(fdForLogging), mCert(cert), mInfoObject(infoObject),
michael@0 270 mDefaultErrorCodeToReport(defaultErrorCodeToReport),
michael@0 271 mCollectedErrors(collectedErrors),
michael@0 272 mErrorCodeTrust(errorCodeTrust),
michael@0 273 mErrorCodeMismatch(errorCodeMismatch),
michael@0 274 mErrorCodeExpired(errorCodeExpired),
michael@0 275 mProviderFlags(providerFlags)
michael@0 276 {
michael@0 277 }
michael@0 278
michael@0 279 virtual void RunOnTargetThread();
michael@0 280 RefPtr<SSLServerCertVerificationResult> mResult; // out
michael@0 281 private:
michael@0 282 SSLServerCertVerificationResult* CheckCertOverrides();
michael@0 283
michael@0 284 const void* const mFdForLogging; // may become an invalid pointer; do not dereference
michael@0 285 const nsCOMPtr<nsIX509Cert> mCert;
michael@0 286 const RefPtr<TransportSecurityInfo> mInfoObject;
michael@0 287 const PRErrorCode mDefaultErrorCodeToReport;
michael@0 288 const uint32_t mCollectedErrors;
michael@0 289 const PRErrorCode mErrorCodeTrust;
michael@0 290 const PRErrorCode mErrorCodeMismatch;
michael@0 291 const PRErrorCode mErrorCodeExpired;
michael@0 292 const uint32_t mProviderFlags;
michael@0 293 };
michael@0 294
michael@0 295 // A probe value of 1 means "no error".
michael@0 296 uint32_t
michael@0 297 MapCertErrorToProbeValue(PRErrorCode errorCode)
michael@0 298 {
michael@0 299 switch (errorCode)
michael@0 300 {
michael@0 301 case SEC_ERROR_UNKNOWN_ISSUER: return 2;
michael@0 302 case SEC_ERROR_CA_CERT_INVALID: return 3;
michael@0 303 case SEC_ERROR_UNTRUSTED_ISSUER: return 4;
michael@0 304 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE: return 5;
michael@0 305 case SEC_ERROR_UNTRUSTED_CERT: return 6;
michael@0 306 case SEC_ERROR_INADEQUATE_KEY_USAGE: return 7;
michael@0 307 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED: return 8;
michael@0 308 case SSL_ERROR_BAD_CERT_DOMAIN: return 9;
michael@0 309 case SEC_ERROR_EXPIRED_CERTIFICATE: return 10;
michael@0 310 }
michael@0 311 NS_WARNING("Unknown certificate error code. Does MapCertErrorToProbeValue "
michael@0 312 "handle everything in PRErrorCodeToOverrideType?");
michael@0 313 return 0;
michael@0 314 }
michael@0 315
michael@0 316 SECStatus
michael@0 317 MozillaPKIXDetermineCertOverrideErrors(CERTCertificate* cert,
michael@0 318 const char* hostName, PRTime now,
michael@0 319 PRErrorCode defaultErrorCodeToReport,
michael@0 320 /*out*/ uint32_t& collectedErrors,
michael@0 321 /*out*/ PRErrorCode& errorCodeTrust,
michael@0 322 /*out*/ PRErrorCode& errorCodeMismatch,
michael@0 323 /*out*/ PRErrorCode& errorCodeExpired)
michael@0 324 {
michael@0 325 MOZ_ASSERT(cert);
michael@0 326 MOZ_ASSERT(hostName);
michael@0 327 MOZ_ASSERT(collectedErrors == 0);
michael@0 328 MOZ_ASSERT(errorCodeTrust == 0);
michael@0 329 MOZ_ASSERT(errorCodeMismatch == 0);
michael@0 330 MOZ_ASSERT(errorCodeExpired == 0);
michael@0 331
michael@0 332 // Assumes the error prioritization described in mozilla::pkix's
michael@0 333 // BuildForward function. Also assumes that CERT_VerifyCertName was only
michael@0 334 // called if CertVerifier::VerifyCert succeeded.
michael@0 335 switch (defaultErrorCodeToReport) {
michael@0 336 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
michael@0 337 case SEC_ERROR_CA_CERT_INVALID:
michael@0 338 case SEC_ERROR_UNKNOWN_ISSUER:
michael@0 339 {
michael@0 340 collectedErrors = nsICertOverrideService::ERROR_UNTRUSTED;
michael@0 341 errorCodeTrust = defaultErrorCodeToReport;
michael@0 342
michael@0 343 SECCertTimeValidity validity = CERT_CheckCertValidTimes(cert, now, false);
michael@0 344 if (validity == secCertTimeUndetermined) {
michael@0 345 PR_SetError(defaultErrorCodeToReport, 0);
michael@0 346 return SECFailure;
michael@0 347 }
michael@0 348 if (validity != secCertTimeValid) {
michael@0 349 collectedErrors |= nsICertOverrideService::ERROR_TIME;
michael@0 350 errorCodeExpired = SEC_ERROR_EXPIRED_CERTIFICATE;
michael@0 351 }
michael@0 352 break;
michael@0 353 }
michael@0 354
michael@0 355 case SEC_ERROR_EXPIRED_CERTIFICATE:
michael@0 356 collectedErrors = nsICertOverrideService::ERROR_TIME;
michael@0 357 errorCodeExpired = SEC_ERROR_EXPIRED_CERTIFICATE;
michael@0 358 break;
michael@0 359
michael@0 360 case SSL_ERROR_BAD_CERT_DOMAIN:
michael@0 361 collectedErrors = nsICertOverrideService::ERROR_MISMATCH;
michael@0 362 errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
michael@0 363 break;
michael@0 364
michael@0 365 case 0:
michael@0 366 NS_ERROR("No error code set during certificate validation failure.");
michael@0 367 PR_SetError(PR_INVALID_STATE_ERROR, 0);
michael@0 368 return SECFailure;
michael@0 369
michael@0 370 default:
michael@0 371 PR_SetError(defaultErrorCodeToReport, 0);
michael@0 372 return SECFailure;
michael@0 373 }
michael@0 374
michael@0 375 if (defaultErrorCodeToReport != SSL_ERROR_BAD_CERT_DOMAIN) {
michael@0 376 if (CERT_VerifyCertName(cert, hostName) != SECSuccess) {
michael@0 377 if (PR_GetError() != SSL_ERROR_BAD_CERT_DOMAIN) {
michael@0 378 PR_SetError(defaultErrorCodeToReport, 0);
michael@0 379 return SECFailure;
michael@0 380 }
michael@0 381
michael@0 382 collectedErrors |= nsICertOverrideService::ERROR_MISMATCH;
michael@0 383 errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
michael@0 384 }
michael@0 385 }
michael@0 386
michael@0 387 return SECSuccess;
michael@0 388 }
michael@0 389
michael@0 390 SSLServerCertVerificationResult*
michael@0 391 CertErrorRunnable::CheckCertOverrides()
michael@0 392 {
michael@0 393 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p][%p] top of CheckCertOverrides\n",
michael@0 394 mFdForLogging, this));
michael@0 395 // "Use" mFdForLogging in non-PR_LOGGING builds, too, to suppress
michael@0 396 // clang's -Wunused-private-field build warning for this variable:
michael@0 397 unused << mFdForLogging;
michael@0 398
michael@0 399 if (!NS_IsMainThread()) {
michael@0 400 NS_ERROR("CertErrorRunnable::CheckCertOverrides called off main thread");
michael@0 401 return new SSLServerCertVerificationResult(mInfoObject,
michael@0 402 mDefaultErrorCodeToReport);
michael@0 403 }
michael@0 404
michael@0 405 int32_t port;
michael@0 406 mInfoObject->GetPort(&port);
michael@0 407
michael@0 408 nsCString hostWithPortString;
michael@0 409 hostWithPortString.AppendASCII(mInfoObject->GetHostNameRaw());
michael@0 410 hostWithPortString.AppendLiteral(":");
michael@0 411 hostWithPortString.AppendInt(port);
michael@0 412
michael@0 413 uint32_t remaining_display_errors = mCollectedErrors;
michael@0 414
michael@0 415 nsresult nsrv;
michael@0 416
michael@0 417 // Enforce Strict-Transport-Security for hosts that are "STS" hosts:
michael@0 418 // connections must be dropped when there are any certificate errors
michael@0 419 // (STS Spec section 7.3).
michael@0 420 bool strictTransportSecurityEnabled = false;
michael@0 421 nsCOMPtr<nsISiteSecurityService> sss
michael@0 422 = do_GetService(NS_SSSERVICE_CONTRACTID, &nsrv);
michael@0 423 if (NS_SUCCEEDED(nsrv)) {
michael@0 424 nsrv = sss->IsSecureHost(nsISiteSecurityService::HEADER_HSTS,
michael@0 425 mInfoObject->GetHostNameRaw(),
michael@0 426 mProviderFlags,
michael@0 427 &strictTransportSecurityEnabled);
michael@0 428 }
michael@0 429 if (NS_FAILED(nsrv)) {
michael@0 430 return new SSLServerCertVerificationResult(mInfoObject,
michael@0 431 mDefaultErrorCodeToReport);
michael@0 432 }
michael@0 433
michael@0 434 if (!strictTransportSecurityEnabled) {
michael@0 435 nsCOMPtr<nsICertOverrideService> overrideService =
michael@0 436 do_GetService(NS_CERTOVERRIDE_CONTRACTID);
michael@0 437 // it is fine to continue without the nsICertOverrideService
michael@0 438
michael@0 439 uint32_t overrideBits = 0;
michael@0 440
michael@0 441 if (overrideService)
michael@0 442 {
michael@0 443 bool haveOverride;
michael@0 444 bool isTemporaryOverride; // we don't care
michael@0 445 nsCString hostString(mInfoObject->GetHostName());
michael@0 446 nsrv = overrideService->HasMatchingOverride(hostString, port,
michael@0 447 mCert,
michael@0 448 &overrideBits,
michael@0 449 &isTemporaryOverride,
michael@0 450 &haveOverride);
michael@0 451 if (NS_SUCCEEDED(nsrv) && haveOverride)
michael@0 452 {
michael@0 453 // remove the errors that are already overriden
michael@0 454 remaining_display_errors &= ~overrideBits;
michael@0 455 }
michael@0 456 }
michael@0 457
michael@0 458 if (!remaining_display_errors) {
michael@0 459 // This can double- or triple-count one certificate with multiple
michael@0 460 // different types of errors. Since this is telemetry and we just
michael@0 461 // want a ballpark answer, we don't care.
michael@0 462 if (mErrorCodeTrust != 0) {
michael@0 463 uint32_t probeValue = MapCertErrorToProbeValue(mErrorCodeTrust);
michael@0 464 Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue);
michael@0 465 }
michael@0 466 if (mErrorCodeMismatch != 0) {
michael@0 467 uint32_t probeValue = MapCertErrorToProbeValue(mErrorCodeMismatch);
michael@0 468 Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue);
michael@0 469 }
michael@0 470 if (mErrorCodeExpired != 0) {
michael@0 471 uint32_t probeValue = MapCertErrorToProbeValue(mErrorCodeExpired);
michael@0 472 Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, probeValue);
michael@0 473 }
michael@0 474
michael@0 475 // all errors are covered by override rules, so let's accept the cert
michael@0 476 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 477 ("[%p][%p] All errors covered by override rules\n",
michael@0 478 mFdForLogging, this));
michael@0 479 return new SSLServerCertVerificationResult(mInfoObject, 0);
michael@0 480 }
michael@0 481 } else {
michael@0 482 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 483 ("[%p][%p] Strict-Transport-Security is violated: untrusted "
michael@0 484 "transport layer\n", mFdForLogging, this));
michael@0 485 }
michael@0 486
michael@0 487 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 488 ("[%p][%p] Certificate error was not overridden\n",
michael@0 489 mFdForLogging, this));
michael@0 490
michael@0 491 // Ok, this is a full stop.
michael@0 492 // First, deliver the technical details of the broken SSL status.
michael@0 493
michael@0 494 // Try to get a nsIBadCertListener2 implementation from the socket consumer.
michael@0 495 nsCOMPtr<nsISSLSocketControl> sslSocketControl = do_QueryInterface(
michael@0 496 NS_ISUPPORTS_CAST(nsITransportSecurityInfo*, mInfoObject));
michael@0 497 if (sslSocketControl) {
michael@0 498 nsCOMPtr<nsIInterfaceRequestor> cb;
michael@0 499 sslSocketControl->GetNotificationCallbacks(getter_AddRefs(cb));
michael@0 500 if (cb) {
michael@0 501 nsCOMPtr<nsIBadCertListener2> bcl = do_GetInterface(cb);
michael@0 502 if (bcl) {
michael@0 503 nsIInterfaceRequestor* csi
michael@0 504 = static_cast<nsIInterfaceRequestor*>(mInfoObject);
michael@0 505 bool suppressMessage = false; // obsolete, ignored
michael@0 506 nsrv = bcl->NotifyCertProblem(csi, mInfoObject->SSLStatus(),
michael@0 507 hostWithPortString, &suppressMessage);
michael@0 508 }
michael@0 509 }
michael@0 510 }
michael@0 511
michael@0 512 nsCOMPtr<nsIX509CertDB> certdb = do_GetService(NS_X509CERTDB_CONTRACTID);
michael@0 513 nsCOMPtr<nsIRecentBadCerts> recentBadCertsService;
michael@0 514 if (certdb) {
michael@0 515 bool isPrivate = mProviderFlags & nsISocketProvider::NO_PERMANENT_STORAGE;
michael@0 516 certdb->GetRecentBadCerts(isPrivate, getter_AddRefs(recentBadCertsService));
michael@0 517 }
michael@0 518
michael@0 519 if (recentBadCertsService) {
michael@0 520 NS_ConvertUTF8toUTF16 hostWithPortStringUTF16(hostWithPortString);
michael@0 521 recentBadCertsService->AddBadCert(hostWithPortStringUTF16,
michael@0 522 mInfoObject->SSLStatus());
michael@0 523 }
michael@0 524
michael@0 525 // pick the error code to report by priority
michael@0 526 PRErrorCode errorCodeToReport = mErrorCodeTrust ? mErrorCodeTrust
michael@0 527 : mErrorCodeMismatch ? mErrorCodeMismatch
michael@0 528 : mErrorCodeExpired ? mErrorCodeExpired
michael@0 529 : mDefaultErrorCodeToReport;
michael@0 530
michael@0 531 SSLServerCertVerificationResult* result =
michael@0 532 new SSLServerCertVerificationResult(mInfoObject,
michael@0 533 errorCodeToReport,
michael@0 534 Telemetry::HistogramCount,
michael@0 535 -1,
michael@0 536 OverridableCertErrorMessage);
michael@0 537
michael@0 538 LogInvalidCertError(mInfoObject,
michael@0 539 result->mErrorCode,
michael@0 540 result->mErrorMessageType);
michael@0 541
michael@0 542 return result;
michael@0 543 }
michael@0 544
michael@0 545 void
michael@0 546 CertErrorRunnable::RunOnTargetThread()
michael@0 547 {
michael@0 548 MOZ_ASSERT(NS_IsMainThread());
michael@0 549
michael@0 550 mResult = CheckCertOverrides();
michael@0 551
michael@0 552 MOZ_ASSERT(mResult);
michael@0 553 }
michael@0 554
michael@0 555 // Converts a PRErrorCode into one of
michael@0 556 // nsICertOverrideService::ERROR_UNTRUSTED,
michael@0 557 // nsICertOverrideService::ERROR_MISMATCH,
michael@0 558 // nsICertOverrideService::ERROR_TIME
michael@0 559 // if the given error code is an overridable error.
michael@0 560 // If it is not, then 0 is returned.
michael@0 561 uint32_t
michael@0 562 PRErrorCodeToOverrideType(PRErrorCode errorCode)
michael@0 563 {
michael@0 564 switch (errorCode)
michael@0 565 {
michael@0 566 case SEC_ERROR_UNKNOWN_ISSUER:
michael@0 567 case SEC_ERROR_UNTRUSTED_ISSUER:
michael@0 568 case SEC_ERROR_EXPIRED_ISSUER_CERTIFICATE:
michael@0 569 case SEC_ERROR_UNTRUSTED_CERT:
michael@0 570 case SEC_ERROR_INADEQUATE_KEY_USAGE:
michael@0 571 case SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED:
michael@0 572 case SEC_ERROR_CA_CERT_INVALID:
michael@0 573 // We group all these errors as "cert not trusted"
michael@0 574 return nsICertOverrideService::ERROR_UNTRUSTED;
michael@0 575 case SSL_ERROR_BAD_CERT_DOMAIN:
michael@0 576 return nsICertOverrideService::ERROR_MISMATCH;
michael@0 577 case SEC_ERROR_EXPIRED_CERTIFICATE:
michael@0 578 return nsICertOverrideService::ERROR_TIME;
michael@0 579 default:
michael@0 580 return 0;
michael@0 581 }
michael@0 582 }
michael@0 583
michael@0 584 SECStatus
michael@0 585 NSSDetermineCertOverrideErrors(CertVerifier& certVerifier,
michael@0 586 CERTCertificate* cert,
michael@0 587 const SECItem* stapledOCSPResponse,
michael@0 588 TransportSecurityInfo* infoObject,
michael@0 589 PRTime now,
michael@0 590 PRErrorCode defaultErrorCodeToReport,
michael@0 591 /*out*/ uint32_t& collectedErrors,
michael@0 592 /*out*/ PRErrorCode& errorCodeTrust,
michael@0 593 /*out*/ PRErrorCode& errorCodeMismatch,
michael@0 594 /*out*/ PRErrorCode& errorCodeExpired)
michael@0 595 {
michael@0 596 MOZ_ASSERT(cert);
michael@0 597 MOZ_ASSERT(infoObject);
michael@0 598 MOZ_ASSERT(defaultErrorCodeToReport != 0);
michael@0 599 MOZ_ASSERT(collectedErrors == 0);
michael@0 600 MOZ_ASSERT(errorCodeTrust == 0);
michael@0 601 MOZ_ASSERT(errorCodeMismatch == 0);
michael@0 602 MOZ_ASSERT(errorCodeExpired == 0);
michael@0 603
michael@0 604 if (defaultErrorCodeToReport == 0) {
michael@0 605 NS_ERROR("No error code set during certificate validation failure.");
michael@0 606 PR_SetError(PR_INVALID_STATE_ERROR, 0);
michael@0 607 return SECFailure;
michael@0 608 }
michael@0 609
michael@0 610 // We only allow overrides for certain errors. Return early if the error
michael@0 611 // is not one of them. This is to avoid doing revocation fetching in the
michael@0 612 // case of OCSP stapling and probably for other reasons.
michael@0 613 if (PRErrorCodeToOverrideType(defaultErrorCodeToReport) == 0) {
michael@0 614 PR_SetError(defaultErrorCodeToReport, 0);
michael@0 615 return SECFailure;
michael@0 616 }
michael@0 617
michael@0 618 PLArenaPool* log_arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 619 PLArenaPoolCleanerFalseParam log_arena_cleaner(log_arena);
michael@0 620 if (!log_arena) {
michael@0 621 NS_ERROR("PORT_NewArena failed");
michael@0 622 return SECFailure; // PORT_NewArena set error code
michael@0 623 }
michael@0 624
michael@0 625 CERTVerifyLog* verify_log = PORT_ArenaZNew(log_arena, CERTVerifyLog);
michael@0 626 if (!verify_log) {
michael@0 627 NS_ERROR("PORT_ArenaZNew failed");
michael@0 628 return SECFailure; // PORT_ArenaZNew set error code
michael@0 629 }
michael@0 630 CERTVerifyLogContentsCleaner verify_log_cleaner(verify_log);
michael@0 631 verify_log->arena = log_arena;
michael@0 632
michael@0 633 // We ignore the result code of the cert verification (i.e. VerifyCert's rv)
michael@0 634 // Either it is a failure, which is expected, and we'll process the
michael@0 635 // verify log below.
michael@0 636 // Or it is a success, then a domain mismatch is the only
michael@0 637 // possible failure.
michael@0 638 // XXX TODO: convert to VerifySSLServerCert
michael@0 639 // XXX TODO: get rid of error log
michael@0 640 certVerifier.VerifyCert(cert, certificateUsageSSLServer,
michael@0 641 now, infoObject, infoObject->GetHostNameRaw(),
michael@0 642 0, stapledOCSPResponse, nullptr, nullptr, verify_log);
michael@0 643
michael@0 644 // Check the name field against the desired hostname.
michael@0 645 if (CERT_VerifyCertName(cert, infoObject->GetHostNameRaw()) != SECSuccess) {
michael@0 646 collectedErrors |= nsICertOverrideService::ERROR_MISMATCH;
michael@0 647 errorCodeMismatch = SSL_ERROR_BAD_CERT_DOMAIN;
michael@0 648 }
michael@0 649
michael@0 650 CERTVerifyLogNode* i_node;
michael@0 651 for (i_node = verify_log->head; i_node; i_node = i_node->next) {
michael@0 652 uint32_t overrideType = PRErrorCodeToOverrideType(i_node->error);
michael@0 653 // If this isn't an overridable error, set the error and return.
michael@0 654 if (overrideType == 0) {
michael@0 655 PR_SetError(i_node->error, 0);
michael@0 656 return SECFailure;
michael@0 657 }
michael@0 658 collectedErrors |= overrideType;
michael@0 659 if (overrideType == nsICertOverrideService::ERROR_UNTRUSTED) {
michael@0 660 if (errorCodeTrust == 0) {
michael@0 661 errorCodeTrust = i_node->error;
michael@0 662 }
michael@0 663 } else if (overrideType == nsICertOverrideService::ERROR_MISMATCH) {
michael@0 664 if (errorCodeMismatch == 0) {
michael@0 665 errorCodeMismatch = i_node->error;
michael@0 666 }
michael@0 667 } else if (overrideType == nsICertOverrideService::ERROR_TIME) {
michael@0 668 if (errorCodeExpired == 0) {
michael@0 669 errorCodeExpired = i_node->error;
michael@0 670 }
michael@0 671 } else {
michael@0 672 MOZ_CRASH("unexpected return value from PRErrorCodeToOverrideType");
michael@0 673 }
michael@0 674 }
michael@0 675
michael@0 676 return SECSuccess;
michael@0 677 }
michael@0 678
michael@0 679 // Returns null with the error code (PR_GetError()) set if it does not create
michael@0 680 // the CertErrorRunnable.
michael@0 681 CertErrorRunnable*
michael@0 682 CreateCertErrorRunnable(CertVerifier& certVerifier,
michael@0 683 PRErrorCode defaultErrorCodeToReport,
michael@0 684 TransportSecurityInfo* infoObject,
michael@0 685 CERTCertificate* cert,
michael@0 686 const SECItem* stapledOCSPResponse,
michael@0 687 const void* fdForLogging,
michael@0 688 uint32_t providerFlags,
michael@0 689 PRTime now)
michael@0 690 {
michael@0 691 MOZ_ASSERT(infoObject);
michael@0 692 MOZ_ASSERT(cert);
michael@0 693
michael@0 694 uint32_t collected_errors = 0;
michael@0 695 PRErrorCode errorCodeTrust = 0;
michael@0 696 PRErrorCode errorCodeMismatch = 0;
michael@0 697 PRErrorCode errorCodeExpired = 0;
michael@0 698
michael@0 699 SECStatus rv;
michael@0 700 switch (certVerifier.mImplementation) {
michael@0 701 case CertVerifier::classic:
michael@0 702 #ifndef NSS_NO_LIBPKIX
michael@0 703 case CertVerifier::libpkix:
michael@0 704 #endif
michael@0 705 rv = NSSDetermineCertOverrideErrors(certVerifier, cert, stapledOCSPResponse,
michael@0 706 infoObject, now,
michael@0 707 defaultErrorCodeToReport,
michael@0 708 collected_errors, errorCodeTrust,
michael@0 709 errorCodeMismatch, errorCodeExpired);
michael@0 710 break;
michael@0 711
michael@0 712 case CertVerifier::mozillapkix:
michael@0 713 rv = MozillaPKIXDetermineCertOverrideErrors(cert,
michael@0 714 infoObject->GetHostNameRaw(),
michael@0 715 now, defaultErrorCodeToReport,
michael@0 716 collected_errors,
michael@0 717 errorCodeTrust,
michael@0 718 errorCodeMismatch,
michael@0 719 errorCodeExpired);
michael@0 720 break;
michael@0 721
michael@0 722 default:
michael@0 723 MOZ_CRASH("unexpected CertVerifier implementation");
michael@0 724 PR_SetError(defaultErrorCodeToReport, 0);
michael@0 725 return nullptr;
michael@0 726
michael@0 727 }
michael@0 728 if (rv != SECSuccess) {
michael@0 729 return nullptr;
michael@0 730 }
michael@0 731
michael@0 732 RefPtr<nsNSSCertificate> nssCert(nsNSSCertificate::Create(cert));
michael@0 733 if (!nssCert) {
michael@0 734 NS_ERROR("nsNSSCertificate::Create failed");
michael@0 735 PR_SetError(SEC_ERROR_NO_MEMORY, 0);
michael@0 736 return nullptr;
michael@0 737 }
michael@0 738
michael@0 739 if (!collected_errors) {
michael@0 740 // This will happen when CERT_*Verify* only returned error(s) that are
michael@0 741 // not on our whitelist of overridable certificate errors.
michael@0 742 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG, ("[%p] !collected_errors: %d\n",
michael@0 743 fdForLogging, static_cast<int>(defaultErrorCodeToReport)));
michael@0 744 PR_SetError(defaultErrorCodeToReport, 0);
michael@0 745 return nullptr;
michael@0 746 }
michael@0 747
michael@0 748 infoObject->SetStatusErrorBits(*nssCert, collected_errors);
michael@0 749
michael@0 750 return new CertErrorRunnable(fdForLogging,
michael@0 751 static_cast<nsIX509Cert*>(nssCert.get()),
michael@0 752 infoObject, defaultErrorCodeToReport,
michael@0 753 collected_errors, errorCodeTrust,
michael@0 754 errorCodeMismatch, errorCodeExpired,
michael@0 755 providerFlags);
michael@0 756 }
michael@0 757
michael@0 758 // When doing async cert processing, we dispatch one of these runnables to the
michael@0 759 // socket transport service thread, which blocks the socket transport
michael@0 760 // service thread while it waits for the inner CertErrorRunnable to execute
michael@0 761 // CheckCertOverrides on the main thread. CheckCertOverrides must block events
michael@0 762 // on both of these threads because it calls TransportSecurityInfo::GetInterface(),
michael@0 763 // which may call nsHttpConnection::GetInterface() through
michael@0 764 // TransportSecurityInfo::mCallbacks. nsHttpConnection::GetInterface must always
michael@0 765 // execute on the main thread, with the socket transport service thread
michael@0 766 // blocked.
michael@0 767 class CertErrorRunnableRunnable : public nsRunnable
michael@0 768 {
michael@0 769 public:
michael@0 770 CertErrorRunnableRunnable(CertErrorRunnable* certErrorRunnable)
michael@0 771 : mCertErrorRunnable(certErrorRunnable)
michael@0 772 {
michael@0 773 }
michael@0 774 private:
michael@0 775 NS_IMETHOD Run()
michael@0 776 {
michael@0 777 nsresult rv = mCertErrorRunnable->DispatchToMainThreadAndWait();
michael@0 778 // The result must run on the socket transport thread, which we are already
michael@0 779 // on, so we can just run it directly, instead of dispatching it.
michael@0 780 if (NS_SUCCEEDED(rv)) {
michael@0 781 rv = mCertErrorRunnable->mResult ? mCertErrorRunnable->mResult->Run()
michael@0 782 : NS_ERROR_UNEXPECTED;
michael@0 783 }
michael@0 784 return rv;
michael@0 785 }
michael@0 786 RefPtr<CertErrorRunnable> mCertErrorRunnable;
michael@0 787 };
michael@0 788
michael@0 789 class SSLServerCertVerificationJob : public nsRunnable
michael@0 790 {
michael@0 791 public:
michael@0 792 // Must be called only on the socket transport thread
michael@0 793 static SECStatus Dispatch(const RefPtr<SharedCertVerifier>& certVerifier,
michael@0 794 const void* fdForLogging,
michael@0 795 TransportSecurityInfo* infoObject,
michael@0 796 CERTCertificate* serverCert,
michael@0 797 SECItem* stapledOCSPResponse,
michael@0 798 uint32_t providerFlags,
michael@0 799 PRTime time);
michael@0 800 private:
michael@0 801 NS_DECL_NSIRUNNABLE
michael@0 802
michael@0 803 // Must be called only on the socket transport thread
michael@0 804 SSLServerCertVerificationJob(const RefPtr<SharedCertVerifier>& certVerifier,
michael@0 805 const void* fdForLogging,
michael@0 806 TransportSecurityInfo* infoObject,
michael@0 807 CERTCertificate* cert,
michael@0 808 SECItem* stapledOCSPResponse,
michael@0 809 uint32_t providerFlags,
michael@0 810 PRTime time);
michael@0 811 const RefPtr<SharedCertVerifier> mCertVerifier;
michael@0 812 const void* const mFdForLogging;
michael@0 813 const RefPtr<TransportSecurityInfo> mInfoObject;
michael@0 814 const mozilla::pkix::ScopedCERTCertificate mCert;
michael@0 815 const uint32_t mProviderFlags;
michael@0 816 const PRTime mTime;
michael@0 817 const TimeStamp mJobStartTime;
michael@0 818 const ScopedSECItem mStapledOCSPResponse;
michael@0 819 };
michael@0 820
michael@0 821 SSLServerCertVerificationJob::SSLServerCertVerificationJob(
michael@0 822 const RefPtr<SharedCertVerifier>& certVerifier, const void* fdForLogging,
michael@0 823 TransportSecurityInfo* infoObject, CERTCertificate* cert,
michael@0 824 SECItem* stapledOCSPResponse, uint32_t providerFlags, PRTime time)
michael@0 825 : mCertVerifier(certVerifier)
michael@0 826 , mFdForLogging(fdForLogging)
michael@0 827 , mInfoObject(infoObject)
michael@0 828 , mCert(CERT_DupCertificate(cert))
michael@0 829 , mProviderFlags(providerFlags)
michael@0 830 , mTime(time)
michael@0 831 , mJobStartTime(TimeStamp::Now())
michael@0 832 , mStapledOCSPResponse(SECITEM_DupItem(stapledOCSPResponse))
michael@0 833 {
michael@0 834 }
michael@0 835
michael@0 836 // This function assumes that we will only use the SPDY connection coalescing
michael@0 837 // feature on connections where we have negotiated SPDY using NPN. If we ever
michael@0 838 // talk SPDY without having negotiated it with SPDY, this code will give wrong
michael@0 839 // and perhaps unsafe results.
michael@0 840 //
michael@0 841 // Returns SECSuccess on the initial handshake of all connections, on
michael@0 842 // renegotiations for any connections where we did not negotiate SPDY, or on any
michael@0 843 // SPDY connection where the server's certificate did not change.
michael@0 844 //
michael@0 845 // Prohibit changing the server cert only if we negotiated SPDY,
michael@0 846 // in order to support SPDY's cross-origin connection pooling.
michael@0 847 static SECStatus
michael@0 848 BlockServerCertChangeForSpdy(nsNSSSocketInfo* infoObject,
michael@0 849 CERTCertificate* serverCert)
michael@0 850 {
michael@0 851 // Get the existing cert. If there isn't one, then there is
michael@0 852 // no cert change to worry about.
michael@0 853 nsCOMPtr<nsIX509Cert> cert;
michael@0 854 nsCOMPtr<nsIX509Cert2> cert2;
michael@0 855
michael@0 856 RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
michael@0 857 if (!status) {
michael@0 858 // If we didn't have a status, then this is the
michael@0 859 // first handshake on this connection, not a
michael@0 860 // renegotiation.
michael@0 861 return SECSuccess;
michael@0 862 }
michael@0 863
michael@0 864 status->GetServerCert(getter_AddRefs(cert));
michael@0 865 cert2 = do_QueryInterface(cert);
michael@0 866 if (!cert2) {
michael@0 867 NS_NOTREACHED("every nsSSLStatus must have a cert"
michael@0 868 "that implements nsIX509Cert2");
michael@0 869 PR_SetError(SEC_ERROR_LIBRARY_FAILURE, 0);
michael@0 870 return SECFailure;
michael@0 871 }
michael@0 872
michael@0 873 // Filter out sockets that did not neogtiate SPDY via NPN
michael@0 874 nsAutoCString negotiatedNPN;
michael@0 875 nsresult rv = infoObject->GetNegotiatedNPN(negotiatedNPN);
michael@0 876 NS_ASSERTION(NS_SUCCEEDED(rv),
michael@0 877 "GetNegotiatedNPN() failed during renegotiation");
michael@0 878
michael@0 879 if (NS_SUCCEEDED(rv) && !StringBeginsWith(negotiatedNPN,
michael@0 880 NS_LITERAL_CSTRING("spdy/")))
michael@0 881 return SECSuccess;
michael@0 882
michael@0 883 // If GetNegotiatedNPN() failed we will assume spdy for safety's safe
michael@0 884 if (NS_FAILED(rv)) {
michael@0 885 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 886 ("BlockServerCertChangeForSpdy failed GetNegotiatedNPN() call."
michael@0 887 " Assuming spdy.\n"));
michael@0 888 }
michael@0 889
michael@0 890 // Check to see if the cert has actually changed
michael@0 891 ScopedCERTCertificate c(cert2->GetCert());
michael@0 892 NS_ASSERTION(c, "very bad and hopefully impossible state");
michael@0 893 bool sameCert = CERT_CompareCerts(c, serverCert);
michael@0 894 if (sameCert)
michael@0 895 return SECSuccess;
michael@0 896
michael@0 897 // Report an error - changed cert is confirmed
michael@0 898 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 899 ("SPDY Refused to allow new cert during renegotiation\n"));
michael@0 900 PR_SetError(SSL_ERROR_RENEGOTIATION_NOT_ALLOWED, 0);
michael@0 901 return SECFailure;
michael@0 902 }
michael@0 903
michael@0 904 SECStatus
michael@0 905 AuthCertificate(CertVerifier& certVerifier, TransportSecurityInfo* infoObject,
michael@0 906 CERTCertificate* cert, SECItem* stapledOCSPResponse,
michael@0 907 uint32_t providerFlags, PRTime time)
michael@0 908 {
michael@0 909 MOZ_ASSERT(infoObject);
michael@0 910 MOZ_ASSERT(cert);
michael@0 911
michael@0 912 SECStatus rv;
michael@0 913
michael@0 914 // TODO: Remove this after we switch to mozilla::pkix as the
michael@0 915 // only option
michael@0 916 if (certVerifier.mImplementation == CertVerifier::classic) {
michael@0 917 if (stapledOCSPResponse) {
michael@0 918 CERTCertDBHandle* handle = CERT_GetDefaultCertDB();
michael@0 919 rv = CERT_CacheOCSPResponseFromSideChannel(handle, cert, PR_Now(),
michael@0 920 stapledOCSPResponse,
michael@0 921 infoObject);
michael@0 922 if (rv != SECSuccess) {
michael@0 923 // Due to buggy servers that will staple expired OCSP responses
michael@0 924 // (see for example http://trac.nginx.org/nginx/ticket/425),
michael@0 925 // don't terminate the connection if the stapled response is expired.
michael@0 926 // We will fall back to fetching revocation information.
michael@0 927 PRErrorCode ocspErrorCode = PR_GetError();
michael@0 928 if (ocspErrorCode != SEC_ERROR_OCSP_OLD_RESPONSE) {
michael@0 929 // stapled OCSP response present but invalid for some reason
michael@0 930 Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 4);
michael@0 931 return rv;
michael@0 932 } else {
michael@0 933 // stapled OCSP response present but expired
michael@0 934 Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 3);
michael@0 935 }
michael@0 936 } else {
michael@0 937 // stapled OCSP response present and good
michael@0 938 Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 1);
michael@0 939 }
michael@0 940 } else {
michael@0 941 // no stapled OCSP response
michael@0 942 Telemetry::Accumulate(Telemetry::SSL_OCSP_STAPLING, 2);
michael@0 943
michael@0 944 uint32_t reasonsForNotFetching = 0;
michael@0 945
michael@0 946 char* ocspURI = CERT_GetOCSPAuthorityInfoAccessLocation(cert);
michael@0 947 if (!ocspURI) {
michael@0 948 reasonsForNotFetching |= 1; // invalid/missing OCSP URI
michael@0 949 } else {
michael@0 950 if (std::strncmp(ocspURI, "http://", 7)) { // approximation
michael@0 951 reasonsForNotFetching |= 1; // invalid/missing OCSP URI
michael@0 952 }
michael@0 953 PORT_Free(ocspURI);
michael@0 954 }
michael@0 955
michael@0 956 if (!certVerifier.mOCSPDownloadEnabled) {
michael@0 957 reasonsForNotFetching |= 2;
michael@0 958 }
michael@0 959
michael@0 960 Telemetry::Accumulate(Telemetry::SSL_OCSP_MAY_FETCH,
michael@0 961 reasonsForNotFetching);
michael@0 962 }
michael@0 963 }
michael@0 964
michael@0 965 // We want to avoid storing any intermediate cert information when browsing
michael@0 966 // in private, transient contexts.
michael@0 967 bool saveIntermediates =
michael@0 968 !(providerFlags & nsISocketProvider::NO_PERMANENT_STORAGE);
michael@0 969
michael@0 970 mozilla::pkix::ScopedCERTCertList certList;
michael@0 971 SECOidTag evOidPolicy;
michael@0 972 rv = certVerifier.VerifySSLServerCert(cert, stapledOCSPResponse,
michael@0 973 time, infoObject,
michael@0 974 infoObject->GetHostNameRaw(),
michael@0 975 saveIntermediates, nullptr,
michael@0 976 &evOidPolicy);
michael@0 977
michael@0 978 // We want to remember the CA certs in the temp db, so that the application can find the
michael@0 979 // complete chain at any time it might need it.
michael@0 980 // But we keep only those CA certs in the temp db, that we didn't already know.
michael@0 981
michael@0 982 RefPtr<nsSSLStatus> status(infoObject->SSLStatus());
michael@0 983 RefPtr<nsNSSCertificate> nsc;
michael@0 984
michael@0 985 if (!status || !status->mServerCert) {
michael@0 986 if( rv == SECSuccess ){
michael@0 987 nsc = nsNSSCertificate::Create(cert, &evOidPolicy);
michael@0 988 }
michael@0 989 else {
michael@0 990 nsc = nsNSSCertificate::Create(cert);
michael@0 991 }
michael@0 992 }
michael@0 993
michael@0 994 if (rv == SECSuccess) {
michael@0 995 // The connection may get terminated, for example, if the server requires
michael@0 996 // a client cert. Let's provide a minimal SSLStatus
michael@0 997 // to the caller that contains at least the cert and its status.
michael@0 998 if (!status) {
michael@0 999 status = new nsSSLStatus();
michael@0 1000 infoObject->SetSSLStatus(status);
michael@0 1001 }
michael@0 1002
michael@0 1003 if (rv == SECSuccess) {
michael@0 1004 // Certificate verification succeeded delete any potential record
michael@0 1005 // of certificate error bits.
michael@0 1006 RememberCertErrorsTable::GetInstance().RememberCertHasError(infoObject,
michael@0 1007 nullptr, rv);
michael@0 1008 }
michael@0 1009 else {
michael@0 1010 // Certificate verification failed, update the status' bits.
michael@0 1011 RememberCertErrorsTable::GetInstance().LookupCertErrorBits(
michael@0 1012 infoObject, status);
michael@0 1013 }
michael@0 1014
michael@0 1015 if (status && !status->mServerCert) {
michael@0 1016 status->mServerCert = nsc;
michael@0 1017 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1018 ("AuthCertificate setting NEW cert %p\n", status->mServerCert.get()));
michael@0 1019 }
michael@0 1020 }
michael@0 1021
michael@0 1022 return rv;
michael@0 1023 }
michael@0 1024
michael@0 1025 /*static*/ SECStatus
michael@0 1026 SSLServerCertVerificationJob::Dispatch(
michael@0 1027 const RefPtr<SharedCertVerifier>& certVerifier,
michael@0 1028 const void* fdForLogging,
michael@0 1029 TransportSecurityInfo* infoObject,
michael@0 1030 CERTCertificate* serverCert,
michael@0 1031 SECItem* stapledOCSPResponse,
michael@0 1032 uint32_t providerFlags,
michael@0 1033 PRTime time)
michael@0 1034 {
michael@0 1035 // Runs on the socket transport thread
michael@0 1036 if (!certVerifier || !infoObject || !serverCert) {
michael@0 1037 NS_ERROR("Invalid parameters for SSL server cert validation");
michael@0 1038 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
michael@0 1039 return SECFailure;
michael@0 1040 }
michael@0 1041
michael@0 1042 RefPtr<SSLServerCertVerificationJob> job(
michael@0 1043 new SSLServerCertVerificationJob(certVerifier, fdForLogging, infoObject,
michael@0 1044 serverCert, stapledOCSPResponse,
michael@0 1045 providerFlags, time));
michael@0 1046
michael@0 1047 nsresult nrv;
michael@0 1048 if (!gCertVerificationThreadPool) {
michael@0 1049 nrv = NS_ERROR_NOT_INITIALIZED;
michael@0 1050 } else {
michael@0 1051 nrv = gCertVerificationThreadPool->Dispatch(job, NS_DISPATCH_NORMAL);
michael@0 1052 }
michael@0 1053 if (NS_FAILED(nrv)) {
michael@0 1054 // We can't call SetCertVerificationResult here to change
michael@0 1055 // mCertVerificationState because SetCertVerificationResult will call
michael@0 1056 // libssl functions that acquire SSL locks that are already being held at
michael@0 1057 // this point. infoObject->mCertVerificationState will be stuck at
michael@0 1058 // waiting_for_cert_verification here, but that is OK because we already
michael@0 1059 // have to be able to handle cases where we encounter non-cert errors while
michael@0 1060 // in that state.
michael@0 1061 PRErrorCode error = nrv == NS_ERROR_OUT_OF_MEMORY
michael@0 1062 ? SEC_ERROR_NO_MEMORY
michael@0 1063 : PR_INVALID_STATE_ERROR;
michael@0 1064 PORT_SetError(error);
michael@0 1065 return SECFailure;
michael@0 1066 }
michael@0 1067
michael@0 1068 PORT_SetError(PR_WOULD_BLOCK_ERROR);
michael@0 1069 return SECWouldBlock;
michael@0 1070 }
michael@0 1071
michael@0 1072 NS_IMETHODIMP
michael@0 1073 SSLServerCertVerificationJob::Run()
michael@0 1074 {
michael@0 1075 // Runs on a cert verification thread
michael@0 1076
michael@0 1077 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1078 ("[%p] SSLServerCertVerificationJob::Run\n", mInfoObject.get()));
michael@0 1079
michael@0 1080 PRErrorCode error;
michael@0 1081
michael@0 1082 nsNSSShutDownPreventionLock nssShutdownPrevention;
michael@0 1083 if (mInfoObject->isAlreadyShutDown()) {
michael@0 1084 error = SEC_ERROR_USER_CANCELLED;
michael@0 1085 } else {
michael@0 1086 Telemetry::ID successTelemetry;
michael@0 1087 Telemetry::ID failureTelemetry;
michael@0 1088 switch (mCertVerifier->mImplementation) {
michael@0 1089 case CertVerifier::classic:
michael@0 1090 successTelemetry
michael@0 1091 = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_CLASSIC;
michael@0 1092 failureTelemetry
michael@0 1093 = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_CLASSIC;
michael@0 1094 break;
michael@0 1095 case CertVerifier::mozillapkix:
michael@0 1096 successTelemetry
michael@0 1097 = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_MOZILLAPKIX;
michael@0 1098 failureTelemetry
michael@0 1099 = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_MOZILLAPKIX;
michael@0 1100 break;
michael@0 1101 #ifndef NSS_NO_LIBPKIX
michael@0 1102 case CertVerifier::libpkix:
michael@0 1103 successTelemetry
michael@0 1104 = Telemetry::SSL_SUCCESFUL_CERT_VALIDATION_TIME_LIBPKIX;
michael@0 1105 failureTelemetry
michael@0 1106 = Telemetry::SSL_INITIAL_FAILED_CERT_VALIDATION_TIME_LIBPKIX;
michael@0 1107 break;
michael@0 1108 #endif
michael@0 1109 default:
michael@0 1110 MOZ_CRASH("Unknown CertVerifier mode");
michael@0 1111 }
michael@0 1112
michael@0 1113 // XXX
michael@0 1114 // Reset the error code here so we can detect if AuthCertificate fails to
michael@0 1115 // set the error code if/when it fails.
michael@0 1116 PR_SetError(0, 0);
michael@0 1117 SECStatus rv = AuthCertificate(*mCertVerifier, mInfoObject, mCert.get(),
michael@0 1118 mStapledOCSPResponse, mProviderFlags,
michael@0 1119 mTime);
michael@0 1120 if (rv == SECSuccess) {
michael@0 1121 uint32_t interval = (uint32_t) ((TimeStamp::Now() - mJobStartTime).ToMilliseconds());
michael@0 1122 RefPtr<SSLServerCertVerificationResult> restart(
michael@0 1123 new SSLServerCertVerificationResult(mInfoObject, 0,
michael@0 1124 successTelemetry, interval));
michael@0 1125 restart->Dispatch();
michael@0 1126 Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
michael@0 1127 return NS_OK;
michael@0 1128 }
michael@0 1129
michael@0 1130 // Note: the interval is not calculated once as PR_GetError MUST be called
michael@0 1131 // before any other function call
michael@0 1132 error = PR_GetError();
michael@0 1133 {
michael@0 1134 TimeStamp now = TimeStamp::Now();
michael@0 1135 MutexAutoLock telemetryMutex(*gSSLVerificationTelemetryMutex);
michael@0 1136 Telemetry::AccumulateTimeDelta(failureTelemetry, mJobStartTime, now);
michael@0 1137 }
michael@0 1138 if (error != 0) {
michael@0 1139 RefPtr<CertErrorRunnable> runnable(
michael@0 1140 CreateCertErrorRunnable(*mCertVerifier, error, mInfoObject,
michael@0 1141 mCert.get(), mStapledOCSPResponse,
michael@0 1142 mFdForLogging, mProviderFlags, mTime));
michael@0 1143 if (!runnable) {
michael@0 1144 // CreateCertErrorRunnable set a new error code
michael@0 1145 error = PR_GetError();
michael@0 1146 } else {
michael@0 1147 // We must block the the socket transport service thread while the
michael@0 1148 // main thread executes the CertErrorRunnable. The CertErrorRunnable
michael@0 1149 // will dispatch the result asynchronously, so we don't have to block
michael@0 1150 // this thread waiting for it.
michael@0 1151
michael@0 1152 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1153 ("[%p][%p] Before dispatching CertErrorRunnable\n",
michael@0 1154 mFdForLogging, runnable.get()));
michael@0 1155
michael@0 1156 nsresult nrv;
michael@0 1157 nsCOMPtr<nsIEventTarget> stsTarget
michael@0 1158 = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
michael@0 1159 if (NS_SUCCEEDED(nrv)) {
michael@0 1160 nrv = stsTarget->Dispatch(new CertErrorRunnableRunnable(runnable),
michael@0 1161 NS_DISPATCH_NORMAL);
michael@0 1162 }
michael@0 1163 if (NS_SUCCEEDED(nrv)) {
michael@0 1164 return NS_OK;
michael@0 1165 }
michael@0 1166
michael@0 1167 NS_ERROR("Failed to dispatch CertErrorRunnable");
michael@0 1168 error = PR_INVALID_STATE_ERROR;
michael@0 1169 }
michael@0 1170 }
michael@0 1171 }
michael@0 1172
michael@0 1173 if (error == 0) {
michael@0 1174 NS_NOTREACHED("no error set during certificate validation failure");
michael@0 1175 error = PR_INVALID_STATE_ERROR;
michael@0 1176 }
michael@0 1177
michael@0 1178 RefPtr<SSLServerCertVerificationResult> failure(
michael@0 1179 new SSLServerCertVerificationResult(mInfoObject, error));
michael@0 1180 failure->Dispatch();
michael@0 1181 return NS_OK;
michael@0 1182 }
michael@0 1183
michael@0 1184 } // unnamed namespace
michael@0 1185
michael@0 1186 // Extracts whatever information we need out of fd (using SSL_*) and passes it
michael@0 1187 // to SSLServerCertVerificationJob::Dispatch. SSLServerCertVerificationJob should
michael@0 1188 // never do anything with fd except logging.
michael@0 1189 SECStatus
michael@0 1190 AuthCertificateHook(void* arg, PRFileDesc* fd, PRBool checkSig, PRBool isServer)
michael@0 1191 {
michael@0 1192 RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
michael@0 1193 if (!certVerifier) {
michael@0 1194 PR_SetError(SEC_ERROR_NOT_INITIALIZED, 0);
michael@0 1195 return SECFailure;
michael@0 1196 }
michael@0 1197
michael@0 1198 // Runs on the socket transport thread
michael@0 1199
michael@0 1200 PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
michael@0 1201 ("[%p] starting AuthCertificateHook\n", fd));
michael@0 1202
michael@0 1203 // Modern libssl always passes PR_TRUE for checkSig, and we have no means of
michael@0 1204 // doing verification without checking signatures.
michael@0 1205 NS_ASSERTION(checkSig, "AuthCertificateHook: checkSig unexpectedly false");
michael@0 1206
michael@0 1207 // PSM never causes libssl to call this function with PR_TRUE for isServer,
michael@0 1208 // and many things in PSM assume that we are a client.
michael@0 1209 NS_ASSERTION(!isServer, "AuthCertificateHook: isServer unexpectedly true");
michael@0 1210
michael@0 1211 nsNSSSocketInfo* socketInfo = static_cast<nsNSSSocketInfo*>(arg);
michael@0 1212
michael@0 1213 ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd));
michael@0 1214
michael@0 1215 if (!checkSig || isServer || !socketInfo || !serverCert) {
michael@0 1216 PR_SetError(PR_INVALID_STATE_ERROR, 0);
michael@0 1217 return SECFailure;
michael@0 1218 }
michael@0 1219
michael@0 1220 socketInfo->SetFullHandshake();
michael@0 1221
michael@0 1222 // This value of "now" is used both here for OCSP stapling and later
michael@0 1223 // when calling CreateCertErrorRunnable.
michael@0 1224 PRTime now = PR_Now();
michael@0 1225
michael@0 1226 if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess)
michael@0 1227 return SECFailure;
michael@0 1228
michael@0 1229 bool onSTSThread;
michael@0 1230 nsresult nrv;
michael@0 1231 nsCOMPtr<nsIEventTarget> sts
michael@0 1232 = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &nrv);
michael@0 1233 if (NS_SUCCEEDED(nrv)) {
michael@0 1234 nrv = sts->IsOnCurrentThread(&onSTSThread);
michael@0 1235 }
michael@0 1236
michael@0 1237 if (NS_FAILED(nrv)) {
michael@0 1238 NS_ERROR("Could not get STS service or IsOnCurrentThread failed");
michael@0 1239 PR_SetError(PR_UNKNOWN_ERROR, 0);
michael@0 1240 return SECFailure;
michael@0 1241 }
michael@0 1242
michael@0 1243 // SSL_PeerStapledOCSPResponses will never return a non-empty response if
michael@0 1244 // OCSP stapling wasn't enabled because libssl wouldn't have let the server
michael@0 1245 // return a stapled OCSP response.
michael@0 1246 // We don't own these pointers.
michael@0 1247 const SECItemArray* csa = SSL_PeerStapledOCSPResponses(fd);
michael@0 1248 SECItem* stapledOCSPResponse = nullptr;
michael@0 1249 // we currently only support single stapled responses
michael@0 1250 if (csa && csa->len == 1) {
michael@0 1251 stapledOCSPResponse = &csa->items[0];
michael@0 1252 }
michael@0 1253
michael@0 1254 uint32_t providerFlags = 0;
michael@0 1255 socketInfo->GetProviderFlags(&providerFlags);
michael@0 1256
michael@0 1257 if (onSTSThread) {
michael@0 1258
michael@0 1259 // We *must* do certificate verification on a background thread because
michael@0 1260 // we need the socket transport thread to be free for our OCSP requests,
michael@0 1261 // and we *want* to do certificate verification on a background thread
michael@0 1262 // because of the performance benefits of doing so.
michael@0 1263 socketInfo->SetCertVerificationWaiting();
michael@0 1264 SECStatus rv = SSLServerCertVerificationJob::Dispatch(
michael@0 1265 certVerifier, static_cast<const void*>(fd), socketInfo,
michael@0 1266 serverCert, stapledOCSPResponse, providerFlags, now);
michael@0 1267 return rv;
michael@0 1268 }
michael@0 1269
michael@0 1270 // We can't do certificate verification on a background thread, because the
michael@0 1271 // thread doing the network I/O may not interrupt its network I/O on receipt
michael@0 1272 // of our SSLServerCertVerificationResult event, and/or it might not even be
michael@0 1273 // a non-blocking socket.
michael@0 1274
michael@0 1275 SECStatus rv = AuthCertificate(*certVerifier, socketInfo, serverCert,
michael@0 1276 stapledOCSPResponse, providerFlags, now);
michael@0 1277 if (rv == SECSuccess) {
michael@0 1278 Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1);
michael@0 1279 return SECSuccess;
michael@0 1280 }
michael@0 1281
michael@0 1282 PRErrorCode error = PR_GetError();
michael@0 1283 if (error != 0) {
michael@0 1284 RefPtr<CertErrorRunnable> runnable(
michael@0 1285 CreateCertErrorRunnable(*certVerifier, error, socketInfo, serverCert,
michael@0 1286 stapledOCSPResponse,
michael@0 1287 static_cast<const void*>(fd), providerFlags,
michael@0 1288 now));
michael@0 1289 if (!runnable) {
michael@0 1290 // CreateCertErrorRunnable sets a new error code when it fails
michael@0 1291 error = PR_GetError();
michael@0 1292 } else {
michael@0 1293 // We have to return SECSuccess or SECFailure based on the result of the
michael@0 1294 // override processing, so we must block this thread waiting for it. The
michael@0 1295 // CertErrorRunnable will NOT dispatch the result at all, since we passed
michael@0 1296 // false for CreateCertErrorRunnable's async parameter
michael@0 1297 nrv = runnable->DispatchToMainThreadAndWait();
michael@0 1298 if (NS_FAILED(nrv)) {
michael@0 1299 NS_ERROR("Failed to dispatch CertErrorRunnable");
michael@0 1300 PR_SetError(PR_INVALID_STATE_ERROR, 0);
michael@0 1301 return SECFailure;
michael@0 1302 }
michael@0 1303
michael@0 1304 if (!runnable->mResult) {
michael@0 1305 NS_ERROR("CertErrorRunnable did not set result");
michael@0 1306 PR_SetError(PR_INVALID_STATE_ERROR, 0);
michael@0 1307 return SECFailure;
michael@0 1308 }
michael@0 1309
michael@0 1310 if (runnable->mResult->mErrorCode == 0) {
michael@0 1311 return SECSuccess; // cert error override occurred.
michael@0 1312 }
michael@0 1313
michael@0 1314 // We must call SetCanceled here to set the error message type
michael@0 1315 // in case it isn't PlainErrorMessage, which is what we would
michael@0 1316 // default to if we just called
michael@0 1317 // PR_SetError(runnable->mResult->mErrorCode, 0) and returned
michael@0 1318 // SECFailure without doing this.
michael@0 1319 socketInfo->SetCanceled(runnable->mResult->mErrorCode,
michael@0 1320 runnable->mResult->mErrorMessageType);
michael@0 1321 error = runnable->mResult->mErrorCode;
michael@0 1322 }
michael@0 1323 }
michael@0 1324
michael@0 1325 if (error == 0) {
michael@0 1326 NS_ERROR("error code not set");
michael@0 1327 error = PR_UNKNOWN_ERROR;
michael@0 1328 }
michael@0 1329
michael@0 1330 PR_SetError(error, 0);
michael@0 1331 return SECFailure;
michael@0 1332 }
michael@0 1333
michael@0 1334 #ifndef MOZ_NO_EV_CERTS
michael@0 1335 class InitializeIdentityInfo : public CryptoTask
michael@0 1336 {
michael@0 1337 virtual nsresult CalculateResult() MOZ_OVERRIDE
michael@0 1338 {
michael@0 1339 EnsureIdentityInfoLoaded();
michael@0 1340 return NS_OK;
michael@0 1341 }
michael@0 1342
michael@0 1343 virtual void ReleaseNSSResources() MOZ_OVERRIDE { } // no-op
michael@0 1344 virtual void CallCallback(nsresult rv) MOZ_OVERRIDE { } // no-op
michael@0 1345 };
michael@0 1346 #endif
michael@0 1347
michael@0 1348 void EnsureServerVerificationInitialized()
michael@0 1349 {
michael@0 1350 #ifndef MOZ_NO_EV_CERTS
michael@0 1351 // Should only be called from socket transport thread due to the static
michael@0 1352 // variable and the reference to gCertVerificationThreadPool
michael@0 1353
michael@0 1354 static bool triggeredCertVerifierInit = false;
michael@0 1355 if (triggeredCertVerifierInit)
michael@0 1356 return;
michael@0 1357 triggeredCertVerifierInit = true;
michael@0 1358
michael@0 1359 RefPtr<InitializeIdentityInfo> initJob = new InitializeIdentityInfo();
michael@0 1360 if (gCertVerificationThreadPool)
michael@0 1361 gCertVerificationThreadPool->Dispatch(initJob, NS_DISPATCH_NORMAL);
michael@0 1362 #endif
michael@0 1363 }
michael@0 1364
michael@0 1365 SSLServerCertVerificationResult::SSLServerCertVerificationResult(
michael@0 1366 TransportSecurityInfo* infoObject, PRErrorCode errorCode,
michael@0 1367 Telemetry::ID telemetryID, uint32_t telemetryValue,
michael@0 1368 SSLErrorMessageType errorMessageType)
michael@0 1369 : mInfoObject(infoObject)
michael@0 1370 , mErrorCode(errorCode)
michael@0 1371 , mErrorMessageType(errorMessageType)
michael@0 1372 , mTelemetryID(telemetryID)
michael@0 1373 , mTelemetryValue(telemetryValue)
michael@0 1374 {
michael@0 1375 // We accumulate telemetry for (only) successful validations on the main thread
michael@0 1376 // to avoid adversely affecting performance by acquiring the mutex that we use
michael@0 1377 // when accumulating the telemetry for unsuccessful validations. Unsuccessful
michael@0 1378 // validations times are accumulated elsewhere.
michael@0 1379 MOZ_ASSERT(telemetryID == Telemetry::HistogramCount || errorCode == 0);
michael@0 1380 }
michael@0 1381
michael@0 1382 void
michael@0 1383 SSLServerCertVerificationResult::Dispatch()
michael@0 1384 {
michael@0 1385 nsresult rv;
michael@0 1386 nsCOMPtr<nsIEventTarget> stsTarget
michael@0 1387 = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
michael@0 1388 NS_ASSERTION(stsTarget,
michael@0 1389 "Failed to get socket transport service event target");
michael@0 1390 rv = stsTarget->Dispatch(this, NS_DISPATCH_NORMAL);
michael@0 1391 NS_ASSERTION(NS_SUCCEEDED(rv),
michael@0 1392 "Failed to dispatch SSLServerCertVerificationResult");
michael@0 1393 }
michael@0 1394
michael@0 1395 NS_IMETHODIMP
michael@0 1396 SSLServerCertVerificationResult::Run()
michael@0 1397 {
michael@0 1398 // TODO: Assert that we're on the socket transport thread
michael@0 1399 if (mTelemetryID != Telemetry::HistogramCount) {
michael@0 1400 Telemetry::Accumulate(mTelemetryID, mTelemetryValue);
michael@0 1401 }
michael@0 1402 // XXX: This cast will be removed by the next patch
michael@0 1403 ((nsNSSSocketInfo*) mInfoObject.get())
michael@0 1404 ->SetCertVerificationResult(mErrorCode, mErrorMessageType);
michael@0 1405 return NS_OK;
michael@0 1406 }
michael@0 1407
michael@0 1408 } } // namespace mozilla::psm

mercurial