// 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();
    }

    CERTCertificate *serverCert = SSL_PeerCertificate(fd);
    CERTCertificateCleaner serverCertCleaner(serverCert);

    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) {
        nsRefPtr<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;
}
Пример #2
0
SECStatus PR_CALLBACK AuthCertificateCallback(void* client_data, PRFileDesc* fd,
                                              PRBool checksig, PRBool isServer) {
  nsNSSShutDownPreventionLock locker;

  // first the default action
  SECStatus rv = SSL_AuthCertificate(CERT_GetDefaultCertDB(), fd, checksig, isServer);

  // We want to remember the CA certs in the temp db, so that the application can find the
  // complete chain at any time it might need it.
  // But we keep only those CA certs in the temp db, that we didn't already know.
  
  CERTCertificate *serverCert = SSL_PeerCertificate(fd);
  CERTCertificateCleaner serverCertCleaner(serverCert);

  if (serverCert) {
    nsNSSSocketInfo* infoObject = (nsNSSSocketInfo*) fd->higher->secret;
    nsRefPtr<nsSSLStatus> status = infoObject->SSLStatus();
    nsRefPtr<nsNSSCertificate> nsc;

    if (!status || !status->mServerCert) {
      nsc = new nsNSSCertificate(serverCert);
    }

    if (SECSuccess == rv) {
      if (nsc) {
        PRBool dummyIsEV;
        nsc->GetIsExtendedValidation(&dummyIsEV); // the nsc object will cache the status
      }
    
      CERTCertList *certList = CERT_GetCertChainFromCert(serverCert, PR_Now(), certUsageSSLCA);

      nsCOMPtr<nsINSSComponent> nssComponent;
      
      for (CERTCertListNode *node = CERT_LIST_HEAD(certList);
           !CERT_LIST_END(node, certList);
           node = CERT_LIST_NEXT(node)) {

        if (node->cert->slot) {
          // This cert was found on a token, no need to remember it in the temp db.
          continue;
        }

        if (node->cert->isperm) {
          // We don't need to remember certs already stored in perm db.
          continue;
        }
        
        if (node->cert == serverCert) {
          // We don't want to remember the server cert, 
          // the code that cares for displaying page info does this already.
          continue;
        }

        // We have found a signer cert that we want to remember.
        nsCAutoString nickname;
        nickname = nsNSSCertificate::defaultServerNickname(node->cert);
        if (!nickname.IsEmpty()) {
          PK11SlotInfo *slot = PK11_GetInternalKeySlot();
          if (slot) {
            PK11_ImportCert(slot, node->cert, CK_INVALID_HANDLE, 
                            const_cast<char*>(nickname.get()), PR_FALSE);
            PK11_FreeSlot(slot);
          }
        }
      }

      CERT_DestroyCertList(certList);
    }

    // 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.
    if (!status) {
      status = new nsSSLStatus();
      infoObject->SetSSLStatus(status);
    }
    if (status && !status->mServerCert) {
      status->mServerCert = nsc;
      PR_LOG(gPIPNSSLog, PR_LOG_DEBUG,
             ("AuthCertificateCallback setting NEW cert %p\n", status->mServerCert.get()));
    }
  }

  return rv;
}