// 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 PR_LOG(gPIPNSSLog, PR_LOG_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); ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd)); if (!checkSig || isServer || !socketInfo || !serverCert) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } socketInfo->SetFullHandshake(); // This value of "now" is used both here for OCSP stapling and later // when calling CreateCertErrorRunnable. PRTime now = PR_Now(); if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess) return SECFailure; 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]; } 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, stapledOCSPResponse, providerFlags, now); 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, stapledOCSPResponse, providerFlags, now); if (rv == SECSuccess) { Telemetry::Accumulate(Telemetry::SSL_CERT_ERROR_OVERRIDES, 1); return SECSuccess; } PRErrorCode error = PR_GetError(); if (error != 0) { RefPtr<CertErrorRunnable> runnable( CreateCertErrorRunnable(*certVerifier, error, socketInfo, serverCert, static_cast<const void*>(fd), providerFlags, now)); 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; }
// 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) { // Runs on the socket transport thread PR_LOG(gPIPNSSLog, PR_LOG_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); if (socketInfo) { // This is the first callback during full handshakes. socketInfo->SetFirstServerHelloReceived(); } ScopedCERTCertificate serverCert(SSL_PeerCertificate(fd)); if (!checkSig || isServer || !socketInfo || !serverCert) { PR_SetError(PR_INVALID_STATE_ERROR, 0); return SECFailure; } if (BlockServerCertChangeForSpdy(socketInfo, serverCert) != SECSuccess) return SECFailure; 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; } 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( static_cast<const void *>(fd), socketInfo, serverCert); 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(socketInfo, serverCert); if (rv == SECSuccess) { return SECSuccess; } PRErrorCode error = PR_GetError(); if (error != 0) { RefPtr<CertErrorRunnable> runnable(CreateCertErrorRunnable( error, socketInfo, serverCert, static_cast<const void *>(fd))); 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; }