| Index: nss/mozilla/security/nss/lib/certhigh/ocsp.c
|
| ===================================================================
|
| --- nss/mozilla/security/nss/lib/certhigh/ocsp.c (revision 38521)
|
| +++ nss/mozilla/security/nss/lib/certhigh/ocsp.c (working copy)
|
| @@ -39,7 +39,7 @@
|
| * Implementation of OCSP services, for both client and server.
|
| * (XXX, really, mostly just for client right now, but intended to do both.)
|
| *
|
| - * $Id: ocsp.c,v 1.61 2010/01/08 01:06:47 wtc%google.com Exp $
|
| + * $Id: ocsp.c,v 1.64 2010/02/01 20:09:31 wtc%google.com Exp $
|
| */
|
|
|
| #include "prerror.h"
|
| @@ -150,7 +150,19 @@
|
| void *pwArg,
|
| PRBool *certIDWasConsumed,
|
| SECStatus *rv_ocsp);
|
| +
|
| static SECStatus
|
| +ocsp_CacheEncodedOCSPResponse(CERTCertDBHandle *handle,
|
| + CERTOCSPCertID *certID,
|
| + CERTCertificate *cert,
|
| + int64 time,
|
| + void *pwArg,
|
| + SECItem *encodedResponse,
|
| + PRBool *certIDWasConsumed,
|
| + PRBool cacheNegative,
|
| + SECStatus *rv_ocsp);
|
| +
|
| +static SECStatus
|
| ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle,
|
| CERTOCSPResponse *response,
|
| CERTOCSPCertID *certID,
|
| @@ -4797,6 +4809,77 @@
|
| }
|
|
|
| /*
|
| + * FUNCTION: CERT_CacheOCSPResponseFromSideChannel
|
| + * First, this function checks the OCSP cache to see if a good response
|
| + * for the given certificate already exists. If it does, then the function
|
| + * returns successfully.
|
| + *
|
| + * If not, then it validates that the given OCSP response is a valid,
|
| + * good response for the given certificate and inserts it into the
|
| + * cache.
|
| + *
|
| + * This function is intended for use when OCSP responses are provided via a
|
| + * side-channel, i.e. TLS OCSP stapling (a.k.a. the status_request extension).
|
| + *
|
| + * INPUTS:
|
| + * CERTCertDBHandle *handle
|
| + * certificate DB of the cert that is being checked
|
| + * CERTCertificate *cert
|
| + * the certificate being checked
|
| + * int64 time
|
| + * time for which status is to be determined
|
| + * SECItem *encodedResponse
|
| + * the DER encoded bytes of the OCSP response
|
| + * void *pwArg
|
| + * argument for password prompting, if needed
|
| + * RETURN:
|
| + * SECSuccess if the cert was found in the cache, or if the OCSP response was
|
| + * found to be valid and inserted into the cache. SECFailure otherwise.
|
| + */
|
| +SECStatus
|
| +CERT_CacheOCSPResponseFromSideChannel(CERTCertDBHandle *handle,
|
| + CERTCertificate *cert,
|
| + int64 time,
|
| + SECItem *encodedResponse,
|
| + void *pwArg)
|
| +{
|
| + CERTOCSPCertID *certID;
|
| + PRBool certIDWasConsumed = PR_FALSE;
|
| + SECStatus rv = SECFailure;
|
| + SECStatus rvOcsp;
|
| + SECErrorCodes dummy_error_code; /* we ignore this */
|
| +
|
| + certID = CERT_CreateOCSPCertID(cert, time);
|
| + if (!certID)
|
| + return SECFailure;
|
| + rv = ocsp_GetCachedOCSPResponseStatusIfFresh(
|
| + certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */
|
| + &rvOcsp, &dummy_error_code);
|
| + if (rv == SECSuccess && rvOcsp == SECSuccess) {
|
| + /* The cached value is good. We don't want to waste time validating
|
| + * this OCSP response. */
|
| + CERT_DestroyOCSPCertID(certID);
|
| + return rv;
|
| + }
|
| +
|
| + /* Since the OCSP response came from a side channel it is attacker
|
| + * controlled. The attacker can have chosen any valid OCSP response,
|
| + * including responses from the past. In this case,
|
| + * ocsp_GetVerifiedSingleResponseForCertID will fail. If we recorded a
|
| + * negative cache entry in this case, then the attacker would have
|
| + * 'poisoned' our cache (denial of service), so we don't record negative
|
| + * results. */
|
| + rv = ocsp_CacheEncodedOCSPResponse(handle, certID, cert, time, pwArg,
|
| + encodedResponse, &certIDWasConsumed,
|
| + PR_FALSE /* don't cache failures */,
|
| + &rvOcsp);
|
| + if (!certIDWasConsumed) {
|
| + CERT_DestroyOCSPCertID(certID);
|
| + }
|
| + return rv == SECSuccess ? rvOcsp : rv;
|
| +}
|
| +
|
| +/*
|
| * Status in *certIDWasConsumed will always be correct, regardless of
|
| * return value.
|
| */
|
| @@ -4813,11 +4896,7 @@
|
| PRBool locationIsDefault;
|
| SECItem *encodedResponse = NULL;
|
| CERTOCSPRequest *request = NULL;
|
| - CERTOCSPResponse *response = NULL;
|
| - CERTCertificate *signerCert = NULL;
|
| - CERTCertificate *issuerCert = NULL;
|
| SECStatus rv = SECFailure;
|
| - CERTOCSPSingleResponse *single = NULL;
|
|
|
| if (!certIDWasConsumed || !rv_ocsp) {
|
| PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
| @@ -4879,6 +4958,75 @@
|
| goto loser;
|
| }
|
|
|
| + rv = ocsp_CacheEncodedOCSPResponse(handle, certID, cert, time, pwArg,
|
| + encodedResponse, certIDWasConsumed,
|
| + PR_TRUE /* cache failures */, rv_ocsp);
|
| +
|
| +loser:
|
| + if (request != NULL)
|
| + CERT_DestroyOCSPRequest(request);
|
| + if (encodedResponse != NULL)
|
| + SECITEM_FreeItem(encodedResponse, PR_TRUE);
|
| + if (location != NULL)
|
| + PORT_Free(location);
|
| +
|
| + return rv;
|
| +}
|
| +
|
| +/*
|
| + * FUNCTION: ocsp_CacheEncodedOCSPResponse
|
| + * This function decodes an OCSP response and checks for a valid response
|
| + * concerning the given certificate. If such a response is not found
|
| + * then nothing is cached. Otherwise, if it is a good response, or if
|
| + * cacheNegative is true, the results are stored in the OCSP cache.
|
| + *
|
| + * Note: a 'valid' response is one that parses successfully, is not an OCSP
|
| + * exception (see RFC 2560 Section 2.3), is correctly signed and is current.
|
| + * A 'good' response is a valid response that attests that the certificate
|
| + * is not currently revoked (see RFC 2560 Section 2.2).
|
| + *
|
| + * INPUTS:
|
| + * CERTCertDBHandle *handle
|
| + * certificate DB of the cert that is being checked
|
| + * CERTOCSPCertID *certID
|
| + * the cert ID corresponding to |cert|
|
| + * CERTCertificate *cert
|
| + * the certificate being checked
|
| + * int64 time
|
| + * time for which status is to be determined
|
| + * void *pwArg
|
| + * the opaque argument to the password prompting function.
|
| + * SECItem *encodedResponse
|
| + * the DER encoded bytes of the OCSP response
|
| + * PRBool *certIDWasConsumed
|
| + * (output) on return, this is true iff |certID| was consumed by this
|
| + * function.
|
| + * SECStatus *rv_ocsp
|
| + * (output) on return, this is SECSuccess iff the response is good (see
|
| + * definition of 'good' above).
|
| + * RETURN:
|
| + * SECSuccess iff the response is valid.
|
| + */
|
| +static SECStatus
|
| +ocsp_CacheEncodedOCSPResponse(CERTCertDBHandle *handle,
|
| + CERTOCSPCertID *certID,
|
| + CERTCertificate *cert,
|
| + int64 time,
|
| + void *pwArg,
|
| + SECItem *encodedResponse,
|
| + PRBool *certIDWasConsumed,
|
| + PRBool cacheNegative,
|
| + SECStatus *rv_ocsp)
|
| +{
|
| + CERTOCSPResponse *response = NULL;
|
| + CERTCertificate *signerCert = NULL;
|
| + CERTCertificate *issuerCert = NULL;
|
| + CERTOCSPSingleResponse *single = NULL;
|
| + SECStatus rv = SECFailure;
|
| +
|
| + *certIDWasConsumed = PR_FALSE;
|
| + *rv_ocsp = SECFailure;
|
| +
|
| response = CERT_DecodeOCSPResponse(encodedResponse);
|
| if (response == NULL) {
|
| goto loser;
|
| @@ -4926,27 +5074,25 @@
|
| *rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(single, time);
|
|
|
| loser:
|
| - PR_EnterMonitor(OCSP_Global.monitor);
|
| - if (OCSP_Global.maxCacheEntries >= 0) {
|
| - /* single == NULL means: remember response failure */
|
| - ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, single,
|
| - certIDWasConsumed);
|
| - /* ignore cache update failures */
|
| + if (cacheNegative || *rv_ocsp == SECSuccess) {
|
| + PR_EnterMonitor(OCSP_Global.monitor);
|
| + if (OCSP_Global.maxCacheEntries >= 0) {
|
| + /* single == NULL means: remember response failure */
|
| + ocsp_CreateOrUpdateCacheEntry(&OCSP_Global.cache, certID, single,
|
| + certIDWasConsumed);
|
| + /* ignore cache update failures */
|
| + }
|
| + PR_ExitMonitor(OCSP_Global.monitor);
|
| }
|
| - PR_ExitMonitor(OCSP_Global.monitor);
|
|
|
| + /* 'single' points within the response so there's no need to free it. */
|
| +
|
| if (issuerCert != NULL)
|
| CERT_DestroyCertificate(issuerCert);
|
| if (signerCert != NULL)
|
| CERT_DestroyCertificate(signerCert);
|
| if (response != NULL)
|
| CERT_DestroyOCSPResponse(response);
|
| - if (request != NULL)
|
| - CERT_DestroyOCSPRequest(request);
|
| - if (encodedResponse != NULL)
|
| - SECITEM_FreeItem(encodedResponse, PR_TRUE);
|
| - if (location != NULL)
|
| - PORT_Free(location);
|
| return rv;
|
| }
|
|
|
|
|