Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(26)

Unified Diff: nss/lib/certhigh/ocsp.c

Issue 105893015: Update third_party/nss to NSS 3.15.4. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/deps/third_party/nss/
Patch Set: Remove SVN property on new file nss/lib/freebl/rsapkcs.c Created 6 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « nss/lib/certhigh/ocsp.h ('k') | nss/lib/certhigh/ocspi.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: nss/lib/certhigh/ocsp.c
===================================================================
--- nss/lib/certhigh/ocsp.c (revision 239365)
+++ nss/lib/certhigh/ocsp.c (working copy)
@@ -24,6 +24,7 @@
#include "hasht.h"
#include "sechash.h"
#include "secasn1.h"
+#include "plbase64.h"
#include "keyhi.h"
#include "cryptohi.h"
#include "ocsp.h"
@@ -86,6 +87,7 @@
OCSPCacheData cache;
SEC_OcspFailureMode ocspFailureMode;
CERT_StringFromCertFcn alternateOCSPAIAFcn;
+ PRBool forcePost;
} OCSP_Global = { NULL,
NULL,
DEFAULT_OCSP_CACHE_SIZE,
@@ -94,7 +96,8 @@
DEFAULT_OSCP_TIMEOUT_SECONDS,
{NULL, 0, NULL, NULL},
ocspMode_FailureIsVerificationFailure,
- NULL
+ NULL,
+ PR_FALSE
};
@@ -103,7 +106,9 @@
static SECItem *
ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena,
CERTOCSPRequest *request,
- const char *location, PRTime time,
+ const char *location,
+ const char *method,
+ PRTime time,
PRBool addServiceLocator,
void *pwArg,
CERTOCSPRequest **pRequest);
@@ -117,25 +122,16 @@
SECStatus *rv_ocsp);
static SECStatus
-ocsp_CacheEncodedOCSPResponse(CERTCertDBHandle *handle,
- CERTOCSPCertID *certID,
- CERTCertificate *cert,
- PRTime time,
- void *pwArg,
- const SECItem *encodedResponse,
- PRBool cacheInvalid,
- PRBool *certIDWasConsumed,
- SECStatus *rv_ocsp);
+ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID,
+ CERTCertificate *cert,
+ PRTime time,
+ void *pwArg,
+ const SECItem *encodedResponse,
+ CERTOCSPResponse **pDecodedResponse,
+ CERTOCSPSingleResponse **pSingle);
static SECStatus
-ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle,
- CERTOCSPResponse *response,
- CERTOCSPCertID *certID,
- CERTCertificate *signerCert,
- PRTime time,
- CERTOCSPSingleResponse **pSingleResponse);
-
-static SECStatus
ocsp_CertRevokedAfter(ocspRevokedInfo *revokedInfo, PRTime time);
static CERTOCSPCertID *
@@ -754,14 +750,23 @@
ocsp_IsCacheItemFresh(OCSPCacheItem *cacheItem)
{
PRTime now;
- PRBool retval;
+ PRBool fresh;
- PR_EnterMonitor(OCSP_Global.monitor);
now = PR_Now();
- retval = (cacheItem->nextFetchAttemptTime > now);
- OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", retval));
- PR_ExitMonitor(OCSP_Global.monitor);
- return retval;
+
+ fresh = cacheItem->nextFetchAttemptTime > now;
+
+ /* Work around broken OCSP responders that return unknown responses for
+ * certificates, especially certificates that were just recently issued.
+ */
+ if (fresh && cacheItem->certStatusArena &&
+ cacheItem->certStatus.certStatusType == ocspCertStatus_unknown) {
+ fresh = PR_FALSE;
+ }
+
+ OCSP_TRACE(("OCSP ocsp_IsCacheItemFresh: %d\n", fresh));
+
+ return fresh;
}
/*
@@ -788,6 +793,19 @@
PORT_Assert(OCSP_Global.maxCacheEntries >= 0);
cacheItem = ocsp_FindCacheEntry(cache, certID);
+
+ /* Don't replace an unknown or revoked entry with an error entry, even if
+ * the existing entry is expired. Instead, we'll continue to use the
+ * existing (possibly expired) cache entry until we receive a valid signed
+ * response to replace it.
+ */
+ if (!single && cacheItem && cacheItem->certStatusArena &&
+ (cacheItem->certStatus.certStatusType == ocspCertStatus_revoked ||
+ cacheItem->certStatus.certStatusType == ocspCertStatus_unknown)) {
+ PR_ExitMonitor(OCSP_Global.monitor);
+ return SECSuccess;
+ }
+
if (!cacheItem) {
CERTOCSPCertID *myCertID;
if (certIDWasConsumed) {
@@ -1460,15 +1478,12 @@
CERT_EncodeOCSPRequest(PLArenaPool *arena, CERTOCSPRequest *request,
void *pwArg)
{
- ocspTBSRequest *tbsRequest;
SECStatus rv;
/* XXX All of these should generate errors if they fail. */
PORT_Assert(request);
PORT_Assert(request->tbsRequest);
- tbsRequest = request->tbsRequest;
-
if (request->tbsRequest->extensionHandle != NULL) {
rv = CERT_FinishExtensions(request->tbsRequest->extensionHandle);
request->tbsRequest->extensionHandle = NULL;
@@ -1636,8 +1651,8 @@
* results in a NULL being returned (and an appropriate error set).
*/
SECItem *
-CERT_GetSPKIDigest(PLArenaPool *arena, const CERTCertificate *cert,
- SECOidTag digestAlg, SECItem *fill)
+CERT_GetSubjectPublicKeyDigest(PLArenaPool *arena, const CERTCertificate *cert,
+ SECOidTag digestAlg, SECItem *fill)
{
SECItem spk;
@@ -1655,9 +1670,9 @@
/*
* Digest the cert's subject name using the specified algorithm.
*/
-static SECItem *
-cert_GetSubjectNameDigest(PLArenaPool *arena, const CERTCertificate *cert,
- SECOidTag digestAlg, SECItem *fill)
+SECItem *
+CERT_GetSubjectNameDigest(PLArenaPool *arena, const CERTCertificate *cert,
+ SECOidTag digestAlg, SECItem *fill)
{
SECItem name;
@@ -1706,36 +1721,36 @@
goto loser;
}
- if (cert_GetSubjectNameDigest(arena, issuerCert, SEC_OID_SHA1,
+ if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_SHA1,
&(certID->issuerNameHash)) == NULL) {
goto loser;
}
certID->issuerSHA1NameHash.data = certID->issuerNameHash.data;
certID->issuerSHA1NameHash.len = certID->issuerNameHash.len;
- if (cert_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD5,
+ if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD5,
&(certID->issuerMD5NameHash)) == NULL) {
goto loser;
}
- if (cert_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD2,
+ if (CERT_GetSubjectNameDigest(arena, issuerCert, SEC_OID_MD2,
&(certID->issuerMD2NameHash)) == NULL) {
goto loser;
}
- if (CERT_GetSPKIDigest(arena, issuerCert, SEC_OID_SHA1,
- &(certID->issuerKeyHash)) == NULL) {
+ if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_SHA1,
+ &certID->issuerKeyHash) == NULL) {
goto loser;
}
certID->issuerSHA1KeyHash.data = certID->issuerKeyHash.data;
certID->issuerSHA1KeyHash.len = certID->issuerKeyHash.len;
/* cache the other two hash algorithms as well */
- if (CERT_GetSPKIDigest(arena, issuerCert, SEC_OID_MD5,
- &(certID->issuerMD5KeyHash)) == NULL) {
+ if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD5,
+ &certID->issuerMD5KeyHash) == NULL) {
goto loser;
}
- if (CERT_GetSPKIDigest(arena, issuerCert, SEC_OID_MD2,
- &(certID->issuerMD2KeyHash)) == NULL) {
+ if (CERT_GetSubjectPublicKeyDigest(arena, issuerCert, SEC_OID_MD2,
+ &certID->issuerMD2KeyHash) == NULL) {
goto loser;
}
@@ -2980,6 +2995,12 @@
* SEC_ERROR_CERT_BAD_ACCESS_LOCATION. Other errors are likely problems
* connecting to it, or writing to it, or allocating memory, and the low-level
* errors appropriate to the problem will be set.
+ * if (encodedRequest == NULL)
+ * then location MUST already include the full request,
+ * including base64 and urlencode,
+ * and the request will be sent with GET
+ * if (encodedRequest != NULL)
+ * then the request will be sent with POST
*/
static PRFileDesc *
ocsp_SendEncodedRequest(const char *location, const SECItem *encodedRequest)
@@ -3012,25 +3033,41 @@
PR_snprintf(portstr, sizeof(portstr), ":%d", port);
}
- header = PR_smprintf("POST %s HTTP/1.0\r\n"
- "Host: %s%s\r\n"
- "Content-Type: application/ocsp-request\r\n"
- "Content-Length: %u\r\n\r\n",
- path, hostname, portstr, encodedRequest->len);
- if (header == NULL)
- goto loser;
+ if (!encodedRequest) {
+ header = PR_smprintf("GET %s HTTP/1.0\r\n"
+ "Host: %s%s\r\n\r\n",
+ path, hostname, portstr);
+ if (header == NULL)
+ goto loser;
- /*
- * The NSPR documentation promises that if it can, it will write the full
- * amount; this will not return a partial value expecting us to loop.
- */
- if (PR_Write(sock, header, (PRInt32) PORT_Strlen(header)) < 0)
- goto loser;
+ /*
+ * The NSPR documentation promises that if it can, it will write the full
+ * amount; this will not return a partial value expecting us to loop.
+ */
+ if (PR_Write(sock, header, (PRInt32) PORT_Strlen(header)) < 0)
+ goto loser;
+ }
+ else {
+ header = PR_smprintf("POST %s HTTP/1.0\r\n"
+ "Host: %s%s\r\n"
+ "Content-Type: application/ocsp-request\r\n"
+ "Content-Length: %u\r\n\r\n",
+ path, hostname, portstr, encodedRequest->len);
+ if (header == NULL)
+ goto loser;
- if (PR_Write(sock, encodedRequest->data,
- (PRInt32) encodedRequest->len) < 0)
- goto loser;
+ /*
+ * The NSPR documentation promises that if it can, it will write the full
+ * amount; this will not return a partial value expecting us to loop.
+ */
+ if (PR_Write(sock, header, (PRInt32) PORT_Strlen(header)) < 0)
+ goto loser;
+ if (PR_Write(sock, encodedRequest->data,
+ (PRInt32) encodedRequest->len) < 0)
+ goto loser;
+ }
+
returnSock = sock;
sock = NULL;
@@ -3338,6 +3375,13 @@
*/
#define MAX_WANTED_OCSP_RESPONSE_LEN 64*1024
+/* if (encodedRequest == NULL)
+ * then location MUST already include the full request,
+ * including base64 and urlencode,
+ * and the request will be sent with GET
+ * if (encodedRequest != NULL)
+ * then the request will be sent with POST
+ */
static SECItem *
fetchOcspHttpClientV1(PLArenaPool *arena,
const SEC_HttpClientFcnV1 *hcv1,
@@ -3381,14 +3425,15 @@
pServerSession,
"http",
path,
- "POST",
+ encodedRequest ? "POST" : "GET",
PR_TicksPerSecond() * OCSP_Global.timeoutSeconds,
&pRequestSession) != SECSuccess) {
PORT_SetError(SEC_ERROR_OCSP_SERVER_ERROR);
goto loser;
}
- if ((*hcv1->setPostDataFcn)(
+ if (encodedRequest &&
+ (*hcv1->setPostDataFcn)(
pRequestSession,
(char*)encodedRequest->data,
encodedRequest->len,
@@ -3444,7 +3489,7 @@
}
/*
- * FUNCTION: CERT_GetEncodedOCSPResponse
+ * FUNCTION: CERT_GetEncodedOCSPResponseByMethod
* Creates and sends a request to an OCSP responder, then reads and
* returns the (encoded) response.
* INPUTS:
@@ -3462,6 +3507,11 @@
* sent and whether there are any trusted responders in place.
* const char *location
* The location of the OCSP responder (a URL).
+ * const char *method
+ * The protocol method used when retrieving the OCSP response.
+ * Currently support: "GET" (http GET) and "POST" (http POST).
+ * Additionals methods for http or other protocols might be added
+ * in the future.
* PRTime time
* Indicates the time for which the certificate status is to be
* determined -- this may be used in the search for the cert's issuer
@@ -3490,26 +3540,114 @@
* Other errors are low-level problems (no memory, bad database, etc.).
*/
SECItem *
-CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList,
- const char *location, PRTime time,
- PRBool addServiceLocator,
- CERTCertificate *signerCert, void *pwArg,
- CERTOCSPRequest **pRequest)
+CERT_GetEncodedOCSPResponseByMethod(PLArenaPool *arena, CERTCertList *certList,
+ const char *location, const char *method,
+ PRTime time, PRBool addServiceLocator,
+ CERTCertificate *signerCert, void *pwArg,
+ CERTOCSPRequest **pRequest)
{
CERTOCSPRequest *request;
request = CERT_CreateOCSPRequest(certList, time, addServiceLocator,
signerCert);
if (!request)
return NULL;
- return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
- time, addServiceLocator,
+ return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+ method, time, addServiceLocator,
pwArg, pRequest);
}
+/*
+ * FUNCTION: CERT_GetEncodedOCSPResponse
+ * Creates and sends a request to an OCSP responder, then reads and
+ * returns the (encoded) response.
+ *
+ * This is a legacy API that behaves identically to
+ * CERT_GetEncodedOCSPResponseByMethod using the "POST" method.
+ */
+SECItem *
+CERT_GetEncodedOCSPResponse(PLArenaPool *arena, CERTCertList *certList,
+ const char *location, PRTime time,
+ PRBool addServiceLocator,
+ CERTCertificate *signerCert, void *pwArg,
+ CERTOCSPRequest **pRequest)
+{
+ return CERT_GetEncodedOCSPResponseByMethod(arena, certList, location,
+ "POST", time, addServiceLocator,
+ signerCert, pwArg, pRequest);
+}
+
+/* URL encode a buffer that consists of base64-characters, only,
+ * which means we can use a simple encoding logic.
+ *
+ * No output buffer size checking is performed.
+ * You should call the function twice, to calculate the required buffer size.
+ *
+ * If the outpufBuf parameter is NULL, the function will calculate the
+ * required size, including the trailing zero termination char.
+ *
+ * The function returns the number of bytes calculated or produced.
+ */
+size_t
+ocsp_UrlEncodeBase64Buf(const char *base64Buf, char *outputBuf)
+{
+ const char *walkInput = NULL;
+ char *walkOutput = outputBuf;
+ size_t count = 0;
+
+ for (walkInput=base64Buf; *walkInput; ++walkInput) {
+ char c = *walkInput;
+ if (isspace(c))
+ continue;
+ switch (c) {
+ case '+':
+ if (outputBuf) {
+ strcpy(walkOutput, "%2B");
+ walkOutput += 3;
+ }
+ count += 3;
+ break;
+ case '/':
+ if (outputBuf) {
+ strcpy(walkOutput, "%2F");
+ walkOutput += 3;
+ }
+ count += 3;
+ break;
+ case '=':
+ if (outputBuf) {
+ strcpy(walkOutput, "%3D");
+ walkOutput += 3;
+ }
+ count += 3;
+ break;
+ default:
+ if (outputBuf) {
+ *walkOutput = *walkInput;
+ ++walkOutput;
+ }
+ ++count;
+ break;
+ }
+ }
+ if (outputBuf) {
+ *walkOutput = 0;
+ }
+ ++count;
+ return count;
+}
+
+enum { max_get_request_size = 255 }; /* defined by RFC2560 */
+
static SECItem *
+cert_GetOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest);
+
+static SECItem *
ocsp_GetEncodedOCSPResponseFromRequest(PLArenaPool *arena,
CERTOCSPRequest *request,
- const char *location, PRTime time,
+ const char *location,
+ const char *method,
+ PRTime time,
PRBool addServiceLocator,
void *pwArg,
CERTOCSPRequest **pRequest)
@@ -3518,6 +3656,9 @@
SECItem *encodedResponse = NULL;
SECStatus rv;
+ if (!location || !*location) /* location should be at least one byte */
+ goto loser;
+
rv = CERT_AddOCSPAcceptableResponses(request,
SEC_OID_PKIX_OCSP_BASIC_RESPONSE);
if (rv != SECSuccess)
@@ -3527,7 +3668,15 @@
if (encodedRequest == NULL)
goto loser;
- encodedResponse = CERT_PostOCSPRequest(arena, location, encodedRequest);
+ if (!strcmp(method, "GET")) {
+ encodedResponse = cert_GetOCSPResponse(arena, location, encodedRequest);
+ }
+ else if (!strcmp(method, "POST")) {
+ encodedResponse = CERT_PostOCSPRequest(arena, location, encodedRequest);
+ }
+ else {
+ goto loser;
+ }
if (encodedResponse != NULL && pRequest != NULL) {
*pRequest = request;
@@ -3539,14 +3688,90 @@
CERT_DestroyOCSPRequest(request);
if (encodedRequest != NULL)
SECITEM_FreeItem(encodedRequest, PR_TRUE);
-
return encodedResponse;
}
+static SECItem *
+cert_FetchOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest);
+
+/* using HTTP GET method */
+static SECItem *
+cert_GetOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest)
+{
+ char *walkOutput = NULL;
+ char *fullGetPath = NULL;
+ size_t pathLength;
+ PRInt32 urlEncodedBufLength;
+ size_t base64size;
+ char b64ReqBuf[max_get_request_size+1];
+ size_t slashLengthIfNeeded = 0;
+ size_t getURLLength;
+ SECItem *item;
+
+ if (!location || !*location) {
+ return NULL;
+ }
+
+ pathLength = strlen(location);
+ if (location[pathLength-1] != '/') {
+ slashLengthIfNeeded = 1;
+ }
+
+ /* Calculation as documented by PL_Base64Encode function.
+ * Use integer conversion to avoid having to use function ceil().
+ */
+ base64size = (((encodedRequest->len +2)/3) * 4);
+ if (base64size > max_get_request_size) {
+ return NULL;
+ }
+ memset(b64ReqBuf, 0, sizeof(b64ReqBuf));
+ PL_Base64Encode((const char*)encodedRequest->data, encodedRequest->len,
+ b64ReqBuf);
+
+ urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL);
+ getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded;
+
+ /* urlEncodedBufLength already contains room for the zero terminator.
+ * Add another if we must add the '/' char.
+ */
+ if (arena) {
+ fullGetPath = (char*)PORT_ArenaAlloc(arena, getURLLength);
+ } else {
+ fullGetPath = (char*)PORT_Alloc(getURLLength);
+ }
+ if (!fullGetPath) {
+ return NULL;
+ }
+
+ strcpy(fullGetPath, location);
+ walkOutput = fullGetPath + pathLength;
+
+ if (walkOutput > fullGetPath && slashLengthIfNeeded) {
+ strcpy(walkOutput, "/");
+ ++walkOutput;
+ }
+ ocsp_UrlEncodeBase64Buf(b64ReqBuf, walkOutput);
+
+ item = cert_FetchOCSPResponse(arena, fullGetPath, NULL);
+ if (!arena) {
+ PORT_Free(fullGetPath);
+ }
+ return item;
+}
+
SECItem *
CERT_PostOCSPRequest(PLArenaPool *arena, const char *location,
const SECItem *encodedRequest)
{
+ return cert_FetchOCSPResponse(arena, location, encodedRequest);
+}
+
+SECItem *
+cert_FetchOCSPResponse(PLArenaPool *arena, const char *location,
+ const SECItem *encodedRequest)
+{
const SEC_HttpClientFcn *registeredHttpClient;
SECItem *encodedResponse = NULL;
@@ -3574,7 +3799,9 @@
ocsp_GetEncodedOCSPResponseForSingleCert(PLArenaPool *arena,
CERTOCSPCertID *certID,
CERTCertificate *singleCert,
- const char *location, PRTime time,
+ const char *location,
+ const char *method,
+ PRTime time,
PRBool addServiceLocator,
void *pwArg,
CERTOCSPRequest **pRequest)
@@ -3584,8 +3811,8 @@
addServiceLocator, NULL);
if (!request)
return NULL;
- return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
- time, addServiceLocator,
+ return ocsp_GetEncodedOCSPResponseFromRequest(arena, request, location,
+ method, time, addServiceLocator,
pwArg, pRequest);
}
@@ -3676,19 +3903,22 @@
item.data = buf;
item.len = SHA1_LENGTH;
- if (CERT_GetSPKIDigest(NULL,testCert,SEC_OID_SHA1, &item) == NULL) {
+ if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_SHA1,
+ &item) == NULL) {
return PR_FALSE;
}
if (SECITEM_ItemsAreEqual(certIndex,&item)) {
return PR_TRUE;
}
- if (CERT_GetSPKIDigest(NULL,testCert,SEC_OID_MD5, &item) == NULL) {
+ if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_MD5,
+ &item) == NULL) {
return PR_FALSE;
}
if (SECITEM_ItemsAreEqual(certIndex,&item)) {
return PR_TRUE;
}
- if (CERT_GetSPKIDigest(NULL,testCert,SEC_OID_MD2, &item) == NULL) {
+ if (CERT_GetSubjectPublicKeyDigest(NULL,testCert,SEC_OID_MD2,
+ &item) == NULL) {
return PR_FALSE;
}
if (SECITEM_ItemsAreEqual(certIndex,&item)) {
@@ -3789,6 +4019,9 @@
signerCert = CERT_DupCertificate(certs[i]);
}
}
+ if (signerCert == NULL) {
+ PORT_SetError(SEC_ERROR_UNKNOWN_CERT);
+ }
}
finish:
@@ -4238,7 +4471,7 @@
hashAlg = SECOID_FindOIDTag(&certID->hashAlgorithm.algorithm);
- keyHash = CERT_GetSPKIDigest(NULL, signerCert, hashAlg, NULL);
+ keyHash = CERT_GetSubjectPublicKeyDigest(NULL, signerCert, hashAlg, NULL);
if (keyHash != NULL) {
keyHashEQ =
@@ -4247,7 +4480,7 @@
SECITEM_FreeItem(keyHash, PR_TRUE);
}
if (keyHashEQ &&
- (nameHash = cert_GetSubjectNameDigest(NULL, signerCert,
+ (nameHash = CERT_GetSubjectNameDigest(NULL, signerCert,
hashAlg, NULL))) {
nameHashEQ =
(SECITEM_CompareItem(nameHash,
@@ -4285,8 +4518,8 @@
return PR_FALSE;
}
- keyHash = CERT_GetSPKIDigest(NULL, issuerCert, hashAlg, NULL);
- nameHash = cert_GetSubjectNameDigest(NULL, issuerCert, hashAlg, NULL);
+ keyHash = CERT_GetSubjectPublicKeyDigest(NULL, issuerCert, hashAlg, NULL);
+ nameHash = CERT_GetSubjectNameDigest(NULL, issuerCert, hashAlg, NULL);
CERT_DestroyCertificate(issuerCert);
@@ -4672,7 +4905,7 @@
* See if the cert represented in the single response had a good status
* at the specified time.
*/
-static SECStatus
+SECStatus
ocsp_CertHasGoodStatus(ocspCertStatus *status, PRTime time)
{
SECStatus rv;
@@ -4704,7 +4937,7 @@
return ocsp_CertHasGoodStatus(single->certStatus, time);
}
-/* Return value SECFailure means: not found or not fresh.
+/* SECFailure means the arguments were invalid.
* On SECSuccess, the out parameters contain the OCSP status.
* rvOcsp contains the overall result of the OCSP operation.
* Depending on input parameter ignoreGlobalOcspFailureSetting,
@@ -4712,34 +4945,39 @@
* If the cached attempt to obtain OCSP information had resulted
* in a failure, missingResponseError shows the error code of
* that failure.
+ * cacheFreshness is ocspMissing if no entry was found,
+ * ocspFresh if a fresh entry was found, or
+ * ocspStale if a stale entry was found.
*/
SECStatus
-ocsp_GetCachedOCSPResponseStatusIfFresh(CERTOCSPCertID *certID,
- PRTime time,
- PRBool ignoreGlobalOcspFailureSetting,
- SECStatus *rvOcsp,
- SECErrorCodes *missingResponseError)
+ocsp_GetCachedOCSPResponseStatus(CERTOCSPCertID *certID,
+ PRTime time,
+ PRBool ignoreGlobalOcspFailureSetting,
+ SECStatus *rvOcsp,
+ SECErrorCodes *missingResponseError,
+ OCSPFreshness *cacheFreshness)
{
OCSPCacheItem *cacheItem = NULL;
- SECStatus rv = SECFailure;
- if (!certID || !missingResponseError || !rvOcsp) {
+ if (!certID || !missingResponseError || !rvOcsp || !cacheFreshness) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
*rvOcsp = SECFailure;
*missingResponseError = 0;
+ *cacheFreshness = ocspMissing;
PR_EnterMonitor(OCSP_Global.monitor);
cacheItem = ocsp_FindCacheEntry(&OCSP_Global.cache, certID);
- if (cacheItem && ocsp_IsCacheItemFresh(cacheItem)) {
+ if (cacheItem) {
+ *cacheFreshness = ocsp_IsCacheItemFresh(cacheItem) ? ocspFresh
+ : ocspStale;
/* having an arena means, we have a cached certStatus */
if (cacheItem->certStatusArena) {
*rvOcsp = ocsp_CertHasGoodStatus(&cacheItem->certStatus, time);
if (*rvOcsp != SECSuccess) {
*missingResponseError = PORT_GetError();
}
- rv = SECSuccess;
} else {
/*
* No status cached, the previous attempt failed.
@@ -4747,17 +4985,17 @@
* However, if OCSP is optional, a recent OCSP failure is
* an allowed good state.
*/
- if (!ignoreGlobalOcspFailureSetting &&
+ if (*cacheFreshness == ocspFresh &&
+ !ignoreGlobalOcspFailureSetting &&
OCSP_Global.ocspFailureMode ==
ocspMode_FailureIsNotAVerificationFailure) {
- rv = SECSuccess;
*rvOcsp = SECSuccess;
}
*missingResponseError = cacheItem->missingResponseError;
}
}
PR_ExitMonitor(OCSP_Global.monitor);
- return rv;
+ return SECSuccess;
}
PRBool
@@ -4828,9 +5066,10 @@
{
CERTOCSPCertID *certID;
PRBool certIDWasConsumed = PR_FALSE;
- SECStatus rv = SECFailure;
+ SECStatus rv;
SECStatus rvOcsp;
- SECErrorCodes dummy_error_code; /* we ignore this */
+ SECErrorCodes cachedErrorCode;
+ OCSPFreshness cachedResponseFreshness;
OCSP_TRACE_CERT(cert);
OCSP_TRACE_TIME("## requested validity time:", time);
@@ -4838,21 +5077,41 @@
certID = CERT_CreateOCSPCertID(cert, time);
if (!certID)
return SECFailure;
- rv = ocsp_GetCachedOCSPResponseStatusIfFresh(
+ rv = ocsp_GetCachedOCSPResponseStatus(
certID, time, PR_FALSE, /* ignoreGlobalOcspFailureSetting */
- &rvOcsp, &dummy_error_code);
- if (rv == SECSuccess) {
+ &rvOcsp, &cachedErrorCode, &cachedResponseFreshness);
+ if (rv != SECSuccess) {
CERT_DestroyOCSPCertID(certID);
+ return SECFailure;
+ }
+ if (cachedResponseFreshness == ocspFresh) {
+ CERT_DestroyOCSPCertID(certID);
return rvOcsp;
}
- rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg,
+
+ rv = ocsp_GetOCSPStatusFromNetwork(handle, certID, cert, time, pwArg,
&certIDWasConsumed,
&rvOcsp);
if (rv != SECSuccess) {
- /* we were unable to obtain ocsp status. Check if we should
- * return cert status revoked. */
- rvOcsp = ocsp_FetchingFailureIsVerificationFailure() ?
- SECFailure : SECSuccess;
+ PRErrorCode err = PORT_GetError();
+ if (ocsp_FetchingFailureIsVerificationFailure()) {
+ PORT_SetError(err);
+ rvOcsp = SECFailure;
+ } else if (cachedResponseFreshness == ocspStale &&
+ (cachedErrorCode == SEC_ERROR_OCSP_UNKNOWN_CERT ||
+ cachedErrorCode == SEC_ERROR_REVOKED_CERTIFICATE)) {
+ /* If we couldn't get a response for a certificate that the OCSP
+ * responder previously told us was bad, then assume it is still
+ * bad until we hear otherwise, as it is very unlikely that the
+ * certificate status has changed from "revoked" to "good" and it
+ * is also unlikely that the certificate status has changed from
+ * "unknown" to "good", except for some buggy OCSP responders.
+ */
+ PORT_SetError(cachedErrorCode);
+ rvOcsp = SECFailure;
+ } else {
+ rvOcsp = SECSuccess;
+ }
}
if (!certIDWasConsumed) {
CERT_DestroyOCSPCertID(certID);
@@ -4898,8 +5157,11 @@
CERTOCSPCertID *certID = NULL;
PRBool certIDWasConsumed = PR_FALSE;
SECStatus rv = SECFailure;
- SECStatus rvOcsp;
+ SECStatus rvOcsp = SECFailure;
SECErrorCodes dummy_error_code; /* we ignore this */
+ CERTOCSPResponse *decodedResponse = NULL;
+ CERTOCSPSingleResponse *singleResponse = NULL;
+ OCSPFreshness freshness;
/* The OCSP cache can be in three states regarding this certificate:
* + Good (cached, timely, 'good' response, or revoked in the future)
@@ -4940,17 +5202,21 @@
* side channel.
*/
- if (!cert) {
+ if (!cert || !encodedResponse) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
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) {
+
+ /* We pass PR_TRUE for ignoreGlobalOcspFailureSetting so that a cached
+ * error entry is not interpreted as being a 'Good' entry here.
+ */
+ rv = ocsp_GetCachedOCSPResponseStatus(
+ certID, time, PR_TRUE, /* ignoreGlobalOcspFailureSetting */
+ &rvOcsp, &dummy_error_code, &freshness);
+ if (rv == SECSuccess && rvOcsp == SECSuccess && freshness == ocspFresh) {
/* The cached value is good. We don't want to waste time validating
* this OCSP response. This is the first column in the table above. */
CERT_DestroyOCSPCertID(certID);
@@ -4958,12 +5224,21 @@
}
/* The logic for caching the more recent response is handled in
- * ocsp_CreateOrUpdateCacheEntry, which is called by this function. */
- rv = ocsp_CacheEncodedOCSPResponse(handle, certID, cert, time,
- pwArg, encodedResponse,
- PR_FALSE /* don't cache if invalid */,
- &certIDWasConsumed,
- &rvOcsp);
+ * ocsp_CacheSingleResponse. */
+
+ rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
+ time, pwArg,
+ encodedResponse,
+ &decodedResponse,
+ &singleResponse);
+ if (rv == SECSuccess) {
+ rvOcsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);
+ /* Cache any valid singleResponse, regardless of status. */
+ ocsp_CacheSingleResponse(certID, singleResponse, &certIDWasConsumed);
+ }
+ if (decodedResponse) {
+ CERT_DestroyOCSPResponse(decodedResponse);
+ }
if (!certIDWasConsumed) {
CERT_DestroyOCSPCertID(certID);
}
@@ -4989,6 +5264,11 @@
CERTOCSPRequest *request = NULL;
SECStatus rv = SECFailure;
+ CERTOCSPResponse *decodedResponse = NULL;
+ CERTOCSPSingleResponse *singleResponse = NULL;
+ enum { stageGET, stagePOST } currentStage;
+ PRBool retry = PR_FALSE;
+
if (!certIDWasConsumed || !rv_ocsp) {
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
@@ -4996,6 +5276,18 @@
*certIDWasConsumed = PR_FALSE;
*rv_ocsp = SECFailure;
+ if (!OCSP_Global.monitor) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+ PR_EnterMonitor(OCSP_Global.monitor);
+ if (OCSP_Global.forcePost) {
+ currentStage = stagePOST;
+ } else {
+ currentStage = stageGET;
+ }
+ PR_ExitMonitor(OCSP_Global.monitor);
+
/*
* The first thing we need to do is find the location of the responder.
* This will be the value of the default responder (if enabled), else
@@ -5041,36 +5333,88 @@
* should be passed into this function or retrieved via some operation
* on the handle/context.
*/
- encodedResponse =
- ocsp_GetEncodedOCSPResponseForSingleCert(NULL, certID, cert, location,
- time, locationIsDefault,
- pwArg, &request);
- if (encodedResponse == NULL) {
- goto loser;
- }
- rv = ocsp_CacheEncodedOCSPResponse(handle, certID, cert, time, pwArg,
- encodedResponse,
- PR_TRUE /* cache if invalid */,
- certIDWasConsumed, rv_ocsp);
+ do {
+ const char *method;
+ PRBool validResponseWithAccurateInfo = PR_FALSE;
+ retry = PR_FALSE;
+ *rv_ocsp = SECFailure;
-loser:
- if (request != NULL)
- CERT_DestroyOCSPRequest(request);
- if (encodedResponse != NULL)
- SECITEM_FreeItem(encodedResponse, PR_TRUE);
- if (location != NULL)
- PORT_Free(location);
+ if (currentStage == stageGET) {
+ method = "GET";
+ } else {
+ PORT_Assert(currentStage == stagePOST);
+ method = "POST";
+ }
+ encodedResponse =
+ ocsp_GetEncodedOCSPResponseForSingleCert(NULL, certID, cert,
+ location, method,
+ time, locationIsDefault,
+ pwArg, &request);
+
+ if (encodedResponse) {
+ rv = ocsp_GetDecodedVerifiedSingleResponseForID(handle, certID, cert,
+ time, pwArg,
+ encodedResponse,
+ &decodedResponse,
+ &singleResponse);
+ if (rv == SECSuccess) {
+ switch (singleResponse->certStatus->certStatusType) {
+ case ocspCertStatus_good:
+ case ocspCertStatus_revoked:
+ validResponseWithAccurateInfo = PR_TRUE;
+ break;
+ default:
+ break;
+ }
+ *rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(singleResponse, time);
+ }
+ }
+
+ if (currentStage == stageGET) {
+ /* only accept GET response if good or revoked */
+ if (validResponseWithAccurateInfo) {
+ ocsp_CacheSingleResponse(certID, singleResponse,
+ certIDWasConsumed);
+ } else {
+ retry = PR_TRUE;
+ currentStage = stagePOST;
+ }
+ } else {
+ /* cache the POST respone, regardless of status */
+ if (!singleResponse) {
+ cert_RememberOCSPProcessingFailure(certID, certIDWasConsumed);
+ } else {
+ ocsp_CacheSingleResponse(certID, singleResponse,
+ certIDWasConsumed);
+ }
+ }
+
+ if (encodedResponse) {
+ SECITEM_FreeItem(encodedResponse, PR_TRUE);
+ encodedResponse = NULL;
+ }
+ if (request) {
+ CERT_DestroyOCSPRequest(request);
+ request = NULL;
+ }
+ if (decodedResponse) {
+ CERT_DestroyOCSPResponse(decodedResponse);
+ decodedResponse = NULL;
+ }
+ singleResponse = NULL;
+
+ } while (retry);
+
+ PORT_Free(location);
return rv;
}
/*
- * FUNCTION: ocsp_CacheEncodedOCSPResponse
+ * FUNCTION: ocsp_GetDecodedVerifiedSingleResponseForID
* 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.
+ * concerning the given certificate.
*
* 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.
@@ -5090,42 +5434,38 @@
* the opaque argument to the password prompting function.
* SECItem *encodedResponse
* the DER encoded bytes of the OCSP response
- * PRBool cacheInvalid
- * If true then invalid responses will cause a negative cache entry to be
- * created. (Invalid means bad syntax, bad signature etc)
- * 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).
+ * CERTOCSPResponse **pDecodedResponse
+ * (output) The caller must ALWAYS check for this output parameter,
+ * and if it's non-null, must destroy it using CERT_DestroyOCSPResponse.
+ * CERTOCSPSingleResponse **pSingle
+ * (output) on success, this points to the single response that corresponds
+ * to the certID parameter. Points to the inside of pDecodedResponse.
+ * It isn't a copy, don't free it.
* RETURN:
* SECSuccess iff the response is valid.
*/
static SECStatus
-ocsp_CacheEncodedOCSPResponse(CERTCertDBHandle *handle,
- CERTOCSPCertID *certID,
- CERTCertificate *cert,
- PRTime time,
- void *pwArg,
- const SECItem *encodedResponse,
- PRBool cacheInvalid,
- PRBool *certIDWasConsumed,
- SECStatus *rv_ocsp)
+ocsp_GetDecodedVerifiedSingleResponseForID(CERTCertDBHandle *handle,
+ CERTOCSPCertID *certID,
+ CERTCertificate *cert,
+ PRTime time,
+ void *pwArg,
+ const SECItem *encodedResponse,
+ CERTOCSPResponse **pDecodedResponse,
+ CERTOCSPSingleResponse **pSingle)
{
- 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;
+ if (!pSingle || !pDecodedResponse) {
+ return SECFailure;
}
+ *pSingle = NULL;
+ *pDecodedResponse = CERT_DecodeOCSPResponse(encodedResponse);
+ if (!*pDecodedResponse) {
+ return SECFailure;
+ }
/*
* Okay, we at least have a response that *looks* like a response!
@@ -5136,7 +5476,7 @@
* Otherwise, we continue to find the actual per-cert status
* in the response.
*/
- if (CERT_GetOCSPResponseStatus(response) != SECSuccess) {
+ if (CERT_GetOCSPResponseStatus(*pDecodedResponse) != SECSuccess) {
goto loser;
}
@@ -5145,54 +5485,60 @@
* So, check for that.
*/
issuerCert = CERT_FindCertIssuer(cert, time, certUsageAnyCA);
- rv = CERT_VerifyOCSPResponseSignature(response, handle, pwArg, &signerCert,
- issuerCert);
- if (rv != SECSuccess)
+ rv = CERT_VerifyOCSPResponseSignature(*pDecodedResponse, handle, pwArg,
+ &signerCert, issuerCert);
+ if (rv != SECSuccess) {
goto loser;
+ }
PORT_Assert(signerCert != NULL); /* internal consistency check */
/* XXX probably should set error, return failure if signerCert is null */
-
/*
* Again, we are only doing one request for one cert.
* XXX When we handle cert chains, the following code will obviously
* have to be modified, in coordation with the code above that will
* have to determine how to make multiple requests, etc.
*/
+ rv = ocsp_GetVerifiedSingleResponseForCertID(handle, *pDecodedResponse, certID,
+ signerCert, time, pSingle);
+loser:
+ if (issuerCert != NULL)
+ CERT_DestroyCertificate(issuerCert);
+ if (signerCert != NULL)
+ CERT_DestroyCertificate(signerCert);
+ return rv;
+}
- rv = ocsp_GetVerifiedSingleResponseForCertID(handle, response, certID,
- signerCert, time, &single);
- if (rv != SECSuccess)
- goto loser;
-
- *rv_ocsp = ocsp_SingleResponseCertHasGoodStatus(single, time);
-
-loser:
- /* If single == NULL here then the response was invalid. */
- if (single != NULL || cacheInvalid) {
+/*
+ * FUNCTION: ocsp_CacheSingleResponse
+ * This function requires that the caller has checked that the response
+ * is valid and verified.
+ * The (positive or negative) valid response will be used to update the cache.
+ * INPUTS:
+ * CERTOCSPCertID *certID
+ * the cert ID corresponding to |cert|
+ * PRBool *certIDWasConsumed
+ * (output) on return, this is true iff |certID| was consumed by this
+ * function.
+ */
+void
+ocsp_CacheSingleResponse(CERTOCSPCertID *certID,
+ CERTOCSPSingleResponse *single,
+ PRBool *certIDWasConsumed)
+{
+ if (single != NULL) {
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);
}
-
- /* '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);
- return rv;
}
-static SECStatus
+SECStatus
ocsp_GetVerifiedSingleResponseForCertID(CERTCertDBHandle *handle,
CERTOCSPResponse *response,
CERTOCSPCertID *certID,
@@ -5785,7 +6131,21 @@
return SECSuccess;
}
+SECStatus
+CERT_ForcePostMethodForOCSP(PRBool forcePost)
+{
+ if (!OCSP_Global.monitor) {
+ PORT_SetError(SEC_ERROR_NOT_INITIALIZED);
+ return SECFailure;
+ }
+ PR_EnterMonitor(OCSP_Global.monitor);
+ OCSP_Global.forcePost = forcePost;
+ PR_ExitMonitor(OCSP_Global.monitor);
+
+ return SECSuccess;
+}
+
SECStatus
CERT_GetOCSPResponseStatus(CERTOCSPResponse *response)
{
« no previous file with comments | « nss/lib/certhigh/ocsp.h ('k') | nss/lib/certhigh/ocspi.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698