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; |
} |