Source code

Revision control

Copy as Markdown

Other Tools

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/*
* pkix_pl_ocspresponse.c
*
*/
#include "pkix_pl_ocspresponse.h"
/* ----Public functions------------------------------------- */
/*
* This is the libpkix replacement for CERT_VerifyOCSPResponseSignature.
* It is used if it has been set as the verifyFcn member of ocspChecker.
*/
PKIX_Error *
PKIX_PL_OcspResponse_UseBuildChain(
PKIX_PL_Cert *signerCert,
PKIX_PL_Date *producedAt,
PKIX_ProcessingParams *procParams,
void **pNBIOContext,
void **pState,
PKIX_BuildResult **pBuildResult,
PKIX_VerifyNode **pVerifyTree,
void *plContext)
{
PKIX_ProcessingParams *caProcParams = NULL;
PKIX_PL_Date *date = NULL;
PKIX_ComCertSelParams *certSelParams = NULL;
PKIX_CertSelector *certSelector = NULL;
void *nbioContext = NULL;
PKIX_Error *buildError = NULL;
PKIX_ENTER(OCSPRESPONSE, "pkix_OcspResponse_UseBuildChain");
PKIX_NULLCHECK_THREE(signerCert, producedAt, procParams);
PKIX_NULLCHECK_THREE(pNBIOContext, pState, pBuildResult);
nbioContext = *pNBIOContext;
*pNBIOContext = NULL;
/* Are we resuming after a WOULDBLOCK return, or starting anew ? */
if (nbioContext == NULL) {
/* Starting anew */
PKIX_CHECK(PKIX_PL_Object_Duplicate
((PKIX_PL_Object *)procParams,
(PKIX_PL_Object **)&caProcParams,
plContext),
PKIX_OBJECTDUPLICATEFAILED);
PKIX_CHECK(PKIX_ProcessingParams_SetDate(procParams, date, plContext),
PKIX_PROCESSINGPARAMSSETDATEFAILED);
/* create CertSelector with target certificate in params */
PKIX_CHECK(PKIX_CertSelector_Create
(NULL, NULL, &certSelector, plContext),
PKIX_CERTSELECTORCREATEFAILED);
PKIX_CHECK(PKIX_ComCertSelParams_Create
(&certSelParams, plContext),
PKIX_COMCERTSELPARAMSCREATEFAILED);
PKIX_CHECK(PKIX_ComCertSelParams_SetCertificate
(certSelParams, signerCert, plContext),
PKIX_COMCERTSELPARAMSSETCERTIFICATEFAILED);
PKIX_CHECK(PKIX_CertSelector_SetCommonCertSelectorParams
(certSelector, certSelParams, plContext),
PKIX_CERTSELECTORSETCOMMONCERTSELECTORPARAMSFAILED);
PKIX_CHECK(PKIX_ProcessingParams_SetTargetCertConstraints
(caProcParams, certSelector, plContext),
PKIX_PROCESSINGPARAMSSETTARGETCERTCONSTRAINTSFAILED);
}
buildError = PKIX_BuildChain
(caProcParams,
&nbioContext,
pState,
pBuildResult,
pVerifyTree,
plContext);
/* non-null nbioContext means the build would block */
if (nbioContext != NULL) {
*pNBIOContext = nbioContext;
/* no buildResult means the build has failed */
} else if (buildError) {
pkixErrorResult = buildError;
buildError = NULL;
} else {
PKIX_DECREF(*pState);
}
cleanup:
PKIX_DECREF(caProcParams);
PKIX_DECREF(date);
PKIX_DECREF(certSelParams);
PKIX_DECREF(certSelector);
PKIX_RETURN(OCSPRESPONSE);
}
/* --Private-OcspResponse-Functions------------------------------------- */
/*
* FUNCTION: pkix_pl_OcspResponse_Destroy
* (see comments for PKIX_PL_DestructorCallback in pkix_pl_system.h)
*/
static PKIX_Error *
pkix_pl_OcspResponse_Destroy(
PKIX_PL_Object *object,
void *plContext)
{
PKIX_PL_OcspResponse *ocspRsp = NULL;
const SEC_HttpClientFcn *httpClient = NULL;
const SEC_HttpClientFcnV1 *hcv1 = NULL;
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_Destroy");
PKIX_NULLCHECK_ONE(object);
PKIX_CHECK(pkix_CheckType(object, PKIX_OCSPRESPONSE_TYPE, plContext),
PKIX_OBJECTNOTANOCSPRESPONSE);
ocspRsp = (PKIX_PL_OcspResponse *)object;
if (ocspRsp->nssOCSPResponse != NULL) {
CERT_DestroyOCSPResponse(ocspRsp->nssOCSPResponse);
ocspRsp->nssOCSPResponse = NULL;
}
if (ocspRsp->signerCert != NULL) {
CERT_DestroyCertificate(ocspRsp->signerCert);
ocspRsp->signerCert = NULL;
}
httpClient = (const SEC_HttpClientFcn *)(ocspRsp->httpClient);
if (httpClient && (httpClient->version == 1)) {
hcv1 = &(httpClient->fcnTable.ftable1);
if (ocspRsp->sessionRequest != NULL) {
(*hcv1->freeFcn)(ocspRsp->sessionRequest);
ocspRsp->sessionRequest = NULL;
}
if (ocspRsp->serverSession != NULL) {
(*hcv1->freeSessionFcn)(ocspRsp->serverSession);
ocspRsp->serverSession = NULL;
}
}
if (ocspRsp->arena != NULL) {
PORT_FreeArena(ocspRsp->arena, PR_FALSE);
ocspRsp->arena = NULL;
}
PKIX_DECREF(ocspRsp->producedAtDate);
PKIX_DECREF(ocspRsp->pkixSignerCert);
PKIX_DECREF(ocspRsp->request);
cleanup:
PKIX_RETURN(OCSPRESPONSE);
}
/*
* FUNCTION: pkix_pl_OcspResponse_Hashcode
* (see comments for PKIX_PL_HashcodeCallback in pkix_pl_system.h)
*/
static PKIX_Error *
pkix_pl_OcspResponse_Hashcode(
PKIX_PL_Object *object,
PKIX_UInt32 *pHashcode,
void *plContext)
{
PKIX_PL_OcspResponse *ocspRsp = NULL;
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_Hashcode");
PKIX_NULLCHECK_TWO(object, pHashcode);
PKIX_CHECK(pkix_CheckType(object, PKIX_OCSPRESPONSE_TYPE, plContext),
PKIX_OBJECTNOTANOCSPRESPONSE);
ocspRsp = (PKIX_PL_OcspResponse *)object;
if (ocspRsp->encodedResponse->data == NULL) {
*pHashcode = 0;
} else {
PKIX_CHECK(pkix_hash
(ocspRsp->encodedResponse->data,
ocspRsp->encodedResponse->len,
pHashcode,
plContext),
PKIX_HASHFAILED);
}
cleanup:
PKIX_RETURN(OCSPRESPONSE);
}
/*
* FUNCTION: pkix_pl_OcspResponse_Equals
* (see comments for PKIX_PL_Equals_Callback in pkix_pl_system.h)
*/
static PKIX_Error *
pkix_pl_OcspResponse_Equals(
PKIX_PL_Object *firstObj,
PKIX_PL_Object *secondObj,
PKIX_Boolean *pResult,
void *plContext)
{
PKIX_UInt32 secondType = 0;
PKIX_UInt32 firstLen = 0;
PKIX_UInt32 i = 0;
PKIX_PL_OcspResponse *rsp1 = NULL;
PKIX_PL_OcspResponse *rsp2 = NULL;
const unsigned char *firstData = NULL;
const unsigned char *secondData = NULL;
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_Equals");
PKIX_NULLCHECK_THREE(firstObj, secondObj, pResult);
/* test that firstObj is a OcspResponse */
PKIX_CHECK(pkix_CheckType(firstObj, PKIX_OCSPRESPONSE_TYPE, plContext),
PKIX_FIRSTOBJARGUMENTNOTANOCSPRESPONSE);
/*
* Since we know firstObj is a OcspResponse, if both references are
* identical, they must be equal
*/
if (firstObj == secondObj){
*pResult = PKIX_TRUE;
goto cleanup;
}
/*
* If secondObj isn't a OcspResponse, we don't throw an error.
* We simply return a Boolean result of FALSE
*/
*pResult = PKIX_FALSE;
PKIX_CHECK(PKIX_PL_Object_GetType(secondObj, &secondType, plContext),
PKIX_COULDNOTGETTYPEOFSECONDARGUMENT);
if (secondType != PKIX_OCSPRESPONSE_TYPE) {
goto cleanup;
}
rsp1 = (PKIX_PL_OcspResponse *)firstObj;
rsp2 = (PKIX_PL_OcspResponse *)secondObj;
/* If either lacks an encoded string, they cannot be compared */
firstData = (const unsigned char *)rsp1->encodedResponse->data;
secondData = (const unsigned char *)rsp2->encodedResponse->data;
if ((firstData == NULL) || (secondData == NULL)) {
goto cleanup;
}
firstLen = rsp1->encodedResponse->len;
if (firstLen != rsp2->encodedResponse->len) {
goto cleanup;
}
for (i = 0; i < firstLen; i++) {
if (*firstData++ != *secondData++) {
goto cleanup;
}
}
*pResult = PKIX_TRUE;
cleanup:
PKIX_RETURN(OCSPRESPONSE);
}
/*
* FUNCTION: pkix_pl_OcspResponse_RegisterSelf
* DESCRIPTION:
* Registers PKIX_OCSPRESPONSE_TYPE and its related functions with
* systemClasses[]
* PARAMETERS:
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Not Thread Safe - for performance and complexity reasons
*
* Since this function is only called by PKIX_PL_Initialize, which should
* only be called once, it is acceptable that this function is not
* thread-safe.
*/
PKIX_Error *
pkix_pl_OcspResponse_RegisterSelf(void *plContext)
{
extern pkix_ClassTable_Entry systemClasses[PKIX_NUMTYPES];
pkix_ClassTable_Entry *entry = &systemClasses[PKIX_OCSPRESPONSE_TYPE];
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_RegisterSelf");
entry->description = "OcspResponse";
entry->typeObjectSize = sizeof(PKIX_PL_OcspResponse);
entry->destructor = pkix_pl_OcspResponse_Destroy;
entry->equalsFunction = pkix_pl_OcspResponse_Equals;
entry->hashcodeFunction = pkix_pl_OcspResponse_Hashcode;
entry->duplicateFunction = pkix_duplicateImmutable;
PKIX_RETURN(OCSPRESPONSE);
}
/* --Public-Functions------------------------------------------------------- */
/*
* FUNCTION: pkix_pl_OcspResponse_Create
* DESCRIPTION:
*
* This function transmits the OcspRequest pointed to by "request" and obtains
* an OcspResponse, which it stores at "pOcspResponse". If the HTTPClient
* supports non-blocking I/O this function may store a non-NULL value at
* "pNBIOContext" (the WOULDBLOCK condition). In that case the caller should
* make a subsequent call with the same value in "pNBIOContext" and
* "pOcspResponse" to resume the operation. Additional WOULDBLOCK returns may
* occur; the caller should persist until a return occurs with NULL stored at
* "pNBIOContext".
*
* If a SEC_HttpClientFcn "responder" is supplied, it is used as the client
* to which the OCSP query is sent. If none is supplied, the default responder
* is used.
*
* If an OcspResponse_VerifyCallback "verifyFcn" is supplied, it is used to
* verify the Cert received from the responder as the signer. If none is
* supplied, the default verification function is used.
*
* The contents of "request" are ignored on calls subsequent to a WOULDBLOCK
* return, and the caller is permitted to supply NULL.
*
* PARAMETERS
* "request"
* Address of the OcspRequest for which a response is desired.
* "httpMethod"
* GET or POST
* "responder"
* Address, if non-NULL, of the SEC_HttpClientFcn to be sent the OCSP
* query.
* "verifyFcn"
* Address, if non-NULL, of the OcspResponse_VerifyCallback function to be
* used to verify the Cert of the OCSP responder.
* "pNBIOContext"
* Address at which platform-dependent information is stored for handling
* of non-blocking I/O. Must be non-NULL.
* "pOcspResponse"
* The address where the created OcspResponse is stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns an OcspResponse Error if the function fails in a non-fatal way.
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
pkix_pl_OcspResponse_Create(
PKIX_PL_OcspRequest *request,
const char *httpMethod,
void *responder,
PKIX_PL_VerifyCallback verifyFcn,
void **pNBIOContext,
PKIX_PL_OcspResponse **pResponse,
void *plContext)
{
void *nbioContext = NULL;
PKIX_PL_OcspResponse *ocspResponse = NULL;
const SEC_HttpClientFcn *httpClient = NULL;
const SEC_HttpClientFcnV1 *hcv1 = NULL;
SECStatus rv = SECFailure;
char *location = NULL;
char *hostname = NULL;
char *path = NULL;
char *responseContentType = NULL;
PRUint16 port = 0;
SEC_HTTP_SERVER_SESSION serverSession = NULL;
SEC_HTTP_REQUEST_SESSION sessionRequest = NULL;
SECItem *encodedRequest = NULL;
PRUint16 responseCode = 0;
char *responseData = NULL;
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_Create");
PKIX_NULLCHECK_TWO(pNBIOContext, pResponse);
if (!strcmp(httpMethod, "GET") && !strcmp(httpMethod, "POST")) {
PKIX_ERROR(PKIX_INVALIDOCSPHTTPMETHOD);
}
nbioContext = *pNBIOContext;
*pNBIOContext = NULL;
if (nbioContext != NULL) {
ocspResponse = *pResponse;
PKIX_NULLCHECK_ONE(ocspResponse);
httpClient = ocspResponse->httpClient;
serverSession = ocspResponse->serverSession;
sessionRequest = ocspResponse->sessionRequest;
PKIX_NULLCHECK_THREE(httpClient, serverSession, sessionRequest);
} else {
PKIX_UInt32 timeout =
((PKIX_PL_NssContext*)plContext)->timeoutSeconds;
PKIX_NULLCHECK_ONE(request);
PKIX_CHECK(pkix_pl_OcspRequest_GetEncoded
(request, &encodedRequest, plContext),
PKIX_OCSPREQUESTGETENCODEDFAILED);
/* prepare initial message to HTTPClient */
/* Is there a default responder and is it enabled? */
if (responder) {
httpClient = (const SEC_HttpClientFcn *)responder;
} else {
httpClient = SEC_GetRegisteredHttpClient();
}
if (httpClient && (httpClient->version == 1)) {
char *fullGetPath = NULL;
const char *sessionPath = NULL;
PRBool usePOST = !strcmp(httpMethod, "POST");
hcv1 = &(httpClient->fcnTable.ftable1);
PKIX_CHECK(pkix_pl_OcspRequest_GetLocation
(request, &location, plContext),
PKIX_OCSPREQUESTGETLOCATIONFAILED);
/* parse location -> hostname, port, path */
rv = CERT_ParseURL(location, &hostname, &port, &path);
if (rv == SECFailure || hostname == NULL || path == NULL) {
PKIX_ERROR(PKIX_URLPARSINGFAILED);
}
rv = (*hcv1->createSessionFcn)(hostname, port,
&serverSession);
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_OCSPSERVERERROR);
}
if (usePOST) {
sessionPath = path;
} else {
/* calculate, are we allowed to use GET? */
enum { max_get_request_size = 255 }; /* defined by RFC2560 */
char b64ReqBuf[max_get_request_size+1];
size_t base64size;
size_t slashLengthIfNeeded = 0;
size_t pathLength;
PRInt32 urlEncodedBufLength;
size_t getURLLength;
char *walkOutput = NULL;
pathLength = strlen(path);
if (path[pathLength-1] != '/') {
slashLengthIfNeeded = 1;
}
base64size = (((encodedRequest->len +2)/3) * 4);
if (base64size > max_get_request_size) {
PKIX_ERROR(PKIX_OCSPGETREQUESTTOOBIG);
}
memset(b64ReqBuf, 0, sizeof(b64ReqBuf));
PL_Base64Encode((const char *)encodedRequest->data, encodedRequest->len, b64ReqBuf);
urlEncodedBufLength = ocsp_UrlEncodeBase64Buf(b64ReqBuf, NULL);
getURLLength = pathLength + urlEncodedBufLength + slashLengthIfNeeded;
fullGetPath = (char*)PORT_Alloc(getURLLength);
if (!fullGetPath) {
PKIX_ERROR(PKIX_OUTOFMEMORY);
}
strcpy(fullGetPath, path);
walkOutput = fullGetPath + pathLength;
if (walkOutput > fullGetPath && slashLengthIfNeeded) {
strcpy(walkOutput, "/");
++walkOutput;
}
ocsp_UrlEncodeBase64Buf(b64ReqBuf, walkOutput);
sessionPath = fullGetPath;
}
rv = (*hcv1->createFcn)(serverSession, "http",
sessionPath, httpMethod,
PR_SecondsToInterval(timeout),
&sessionRequest);
sessionPath = NULL;
if (fullGetPath) {
PORT_Free(fullGetPath);
fullGetPath = NULL;
}
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_OCSPSERVERERROR);
}
if (usePOST) {
rv = (*hcv1->setPostDataFcn)(sessionRequest,
(char *)encodedRequest->data,
encodedRequest->len,
"application/ocsp-request");
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_OCSPSERVERERROR);
}
}
/* create a PKIX_PL_OcspResponse object */
PKIX_CHECK(PKIX_PL_Object_Alloc
(PKIX_OCSPRESPONSE_TYPE,
sizeof (PKIX_PL_OcspResponse),
(PKIX_PL_Object **)&ocspResponse,
plContext),
PKIX_COULDNOTCREATEOBJECT);
PKIX_INCREF(request);
ocspResponse->request = request;
ocspResponse->httpClient = httpClient;
ocspResponse->serverSession = serverSession;
serverSession = NULL;
ocspResponse->sessionRequest = sessionRequest;
sessionRequest = NULL;
ocspResponse->verifyFcn = verifyFcn;
ocspResponse->handle = CERT_GetDefaultCertDB();
ocspResponse->encodedResponse = NULL;
ocspResponse->arena = NULL;
ocspResponse->producedAt = 0;
ocspResponse->producedAtDate = NULL;
ocspResponse->pkixSignerCert = NULL;
ocspResponse->nssOCSPResponse = NULL;
ocspResponse->signerCert = NULL;
}
}
/* begin or resume IO to HTTPClient */
if (httpClient && (httpClient->version == 1)) {
PRUint32 responseDataLen =
((PKIX_PL_NssContext*)plContext)->maxResponseLength;
hcv1 = &(httpClient->fcnTable.ftable1);
rv = (*hcv1->trySendAndReceiveFcn)(ocspResponse->sessionRequest,
(PRPollDesc **)&nbioContext,
&responseCode,
(const char **)&responseContentType,
NULL, /* responseHeaders */
(const char **)&responseData,
&responseDataLen);
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_OCSPSERVERERROR);
}
/* responseContentType is a pointer to the null-terminated
* string returned by httpclient. Memory allocated for context
* type will be freed with freeing of the HttpClient struct. */
if (PORT_Strcasecmp(responseContentType,
"application/ocsp-response")) {
PKIX_ERROR(PKIX_OCSPSERVERERROR);
}
if (nbioContext != NULL) {
*pNBIOContext = nbioContext;
goto cleanup;
}
if (responseCode != 200) {
PKIX_ERROR(PKIX_OCSPBADHTTPRESPONSE);
}
ocspResponse->arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
if (ocspResponse->arena == NULL) {
PKIX_ERROR(PKIX_OUTOFMEMORY);
}
ocspResponse->encodedResponse = SECITEM_AllocItem
(ocspResponse->arena, NULL, responseDataLen);
if (ocspResponse->encodedResponse == NULL) {
PKIX_ERROR(PKIX_OUTOFMEMORY);
}
PORT_Memcpy(ocspResponse->encodedResponse->data,
responseData, responseDataLen);
}
*pResponse = ocspResponse;
ocspResponse = NULL;
cleanup:
if (path != NULL) {
PORT_Free(path);
}
if (hostname != NULL) {
PORT_Free(hostname);
}
if (ocspResponse) {
PKIX_DECREF(ocspResponse);
}
if (serverSession) {
hcv1->freeSessionFcn(serverSession);
}
if (sessionRequest) {
hcv1->freeFcn(sessionRequest);
}
PKIX_RETURN(OCSPRESPONSE);
}
/*
* FUNCTION: pkix_pl_OcspResponse_Decode
* DESCRIPTION:
*
* This function decodes the DER data contained in the OcspResponse pointed to
* by "response", storing PKIX_TRUE at "pPassed" if the decoding was
* successful, and PKIX_FALSE otherwise.
*
* PARAMETERS
* "response"
* The address of the OcspResponse whose DER data is to be decoded. Must
* be non-NULL.
* "pPassed"
* Address at which the Boolean result is stored. Must be non-NULL.
* "pReturnCode"
* Address at which the SECErrorCodes result is stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns an OcspResponse Error if the function fails in a non-fatal way.
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
pkix_pl_OcspResponse_Decode(
PKIX_PL_OcspResponse *response,
PKIX_Boolean *pPassed,
SECErrorCodes *pReturnCode,
void *plContext)
{
PKIX_ENTER(OCSPRESPONSE, "PKIX_PL_OcspResponse_Decode");
PKIX_NULLCHECK_TWO(response, response->encodedResponse);
response->nssOCSPResponse =
CERT_DecodeOCSPResponse(response->encodedResponse);
if (response->nssOCSPResponse != NULL) {
*pPassed = PKIX_TRUE;
*pReturnCode = 0;
} else {
*pPassed = PKIX_FALSE;
*pReturnCode = PORT_GetError();
}
PKIX_RETURN(OCSPRESPONSE);
}
/*
* FUNCTION: pkix_pl_OcspResponse_GetStatus
* DESCRIPTION:
*
* This function checks the response status of the OcspResponse pointed to
* by "response", storing PKIX_TRUE at "pPassed" if the responder understood
* the request and considered it valid, and PKIX_FALSE otherwise.
*
* PARAMETERS
* "response"
* The address of the OcspResponse whose status is to be retrieved. Must
* be non-NULL.
* "pPassed"
* Address at which the Boolean result is stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns an OcspResponse Error if the function fails in a non-fatal way.
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
pkix_pl_OcspResponse_GetStatus(
PKIX_PL_OcspResponse *response,
PKIX_Boolean *pPassed,
SECErrorCodes *pReturnCode,
void *plContext)
{
SECStatus rv = SECFailure;
PKIX_ENTER(OCSPRESPONSE, "PKIX_PL_OcspResponse_GetStatus");
PKIX_NULLCHECK_FOUR(response, response->nssOCSPResponse, pPassed, pReturnCode);
rv = CERT_GetOCSPResponseStatus(response->nssOCSPResponse);
if (rv == SECSuccess) {
*pPassed = PKIX_TRUE;
*pReturnCode = 0;
} else {
*pPassed = PKIX_FALSE;
*pReturnCode = PORT_GetError();
}
PKIX_RETURN(OCSPRESPONSE);
}
static PKIX_Error*
pkix_pl_OcspResponse_VerifyResponse(
PKIX_PL_OcspResponse *response,
PKIX_ProcessingParams *procParams,
SECCertUsage certUsage,
void **state,
PKIX_BuildResult **buildResult,
void **pNBIOContext,
void *plContext)
{
SECStatus rv = SECFailure;
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_VerifyResponse");
if (response->verifyFcn != NULL) {
void *lplContext = NULL;
PKIX_CHECK(
PKIX_PL_NssContext_Create(((SECCertificateUsage)1) << certUsage,
PKIX_FALSE, NULL, &lplContext),
PKIX_NSSCONTEXTCREATEFAILED);
PKIX_CHECK(
(response->verifyFcn)((PKIX_PL_Object*)response->pkixSignerCert,
NULL, response->producedAtDate,
procParams, pNBIOContext,
state, buildResult,
NULL, lplContext),
PKIX_CERTVERIFYKEYUSAGEFAILED);
rv = SECSuccess;
} else {
/* checkSig is !isRoot */
PRBool checkSig = response->signerCert->isRoot ? PR_FALSE : PR_TRUE;
rv = CERT_VerifyCert(response->handle, response->signerCert, checkSig,
certUsage, response->producedAt, NULL, NULL);
if (rv != SECSuccess) {
PKIX_ERROR(PKIX_CERTVERIFYKEYUSAGEFAILED);
}
}
cleanup:
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
}
PKIX_RETURN(OCSPRESPONSE);
}
/*
* FUNCTION: pkix_pl_OcspResponse_VerifySignature
* DESCRIPTION:
*
* This function verifies the ocspResponse signature field in the OcspResponse
* pointed to by "response", storing PKIX_TRUE at "pPassed" if verification
* is successful and PKIX_FALSE otherwise. If verification is unsuccessful an
* error code (an enumeration of type SECErrorCodes) is stored at *pReturnCode.
*
* PARAMETERS
* "response"
* The address of the OcspResponse whose signature field is to be
* retrieved. Must be non-NULL.
* "cert"
* The address of the Cert for which the OCSP query was made. Must be
* non-NULL.
* "procParams"
* Address of ProcessingParams used to initialize the ExpirationChecker
* and TargetCertChecker. Must be non-NULL.
* "pPassed"
* Address at which the Boolean result is stored. Must be non-NULL.
* "pNBIOContext"
* Address at which the NBIOContext is stored indicating whether the
* checking is complete. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns an OcspResponse Error if the function fails in a non-fatal way.
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
pkix_pl_OcspResponse_VerifySignature(
PKIX_PL_OcspResponse *response,
PKIX_PL_Cert *cert,
PKIX_ProcessingParams *procParams,
PKIX_Boolean *pPassed,
void **pNBIOContext,
void *plContext)
{
SECStatus rv = SECFailure;
CERTOCSPResponse *nssOCSPResponse = NULL;
CERTCertificate *issuerCert = NULL;
PKIX_BuildResult *buildResult = NULL;
void *nbio = NULL;
void *state = NULL;
ocspSignature *signature = NULL;
ocspResponseData *tbsData = NULL;
SECItem *tbsResponseDataDER = NULL;
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_VerifySignature");
PKIX_NULLCHECK_FOUR(response, cert, pPassed, pNBIOContext);
nbio = *pNBIOContext;
*pNBIOContext = NULL;
nssOCSPResponse = response->nssOCSPResponse;
if (nssOCSPResponse == NULL) {
PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
goto cleanup;
}
tbsData =
ocsp_GetResponseData(nssOCSPResponse, &tbsResponseDataDER);
signature = ocsp_GetResponseSignature(nssOCSPResponse);
/* Are we resuming after a WOULDBLOCK response? */
if (nbio == NULL) {
/* No, this is a new query */
issuerCert = CERT_FindCertIssuer(cert->nssCert, PR_Now(),
certUsageAnyCA);
/*
* If this signature has already gone through verification,
* just return the cached result.
*/
if (signature->wasChecked) {
if (signature->status == SECSuccess) {
response->signerCert =
CERT_DupCertificate(signature->cert);
} else {
PORT_SetError(signature->failureReason);
goto cleanup;
}
}
response->signerCert =
ocsp_GetSignerCertificate(response->handle, tbsData,
signature, issuerCert);
if (response->signerCert == NULL) {
if (PORT_GetError() == SEC_ERROR_UNKNOWN_CERT) {
/* Make the error a little more specific. */
PORT_SetError(SEC_ERROR_OCSP_INVALID_SIGNING_CERT);
}
goto cleanup;
}
PKIX_CHECK(
PKIX_PL_Cert_CreateFromCERTCertificate(response->signerCert,
&(response->pkixSignerCert),
plContext),
PKIX_CERTCREATEWITHNSSCERTFAILED);
/*
* We could mark this true at the top of this function, or
* always below at "finish", but if the problem was just that
* we could not find the signer's cert, leave that as if the
* signature hasn't been checked. Maybe a subsequent call will
* have better luck.
*/
signature->wasChecked = PR_TRUE;
/*
* We are about to verify the signer certificate; we need to
* specify *when* that certificate must be valid -- for our
* purposes we expect it to be valid when the response was
* signed. The value of "producedAt" is the signing time.
*/
rv = DER_GeneralizedTimeToTime(&response->producedAt,
&tbsData->producedAt);
if (rv != SECSuccess) {
PORT_SetError(SEC_ERROR_OCSP_MALFORMED_RESPONSE);
goto cleanup;
}
/*
* We need producedAtDate and pkixSignerCert if we are calling a
* user-supplied verification function. Let's put their
* creation before the code that gets repeated when
* non-blocking I/O is used.
*/
PKIX_CHECK(
pkix_pl_Date_CreateFromPRTime((PRTime)response->producedAt,
&(response->producedAtDate),
plContext),
PKIX_DATECREATEFROMPRTIMEFAILED);
}
/*
* Just because we have a cert does not mean it is any good; check
* it for validity, trust and usage. Use the caller-supplied
* verification function, if one was supplied.
*/
if (ocsp_CertIsOCSPDefaultResponder(response->handle,
response->signerCert)) {
rv = SECSuccess;
} else {
SECCertUsage certUsage;
if (CERT_IsCACert(response->signerCert, NULL)) {
certUsage = certUsageAnyCA;
} else {
certUsage = certUsageStatusResponder;
}
PKIX_CHECK_ONLY_FATAL(
pkix_pl_OcspResponse_VerifyResponse(response, procParams,
certUsage, &state,
&buildResult, &nbio,
plContext),
PKIX_CERTVERIFYKEYUSAGEFAILED);
if (pkixTempErrorReceived) {
rv = SECFailure;
goto cleanup;
}
if (nbio != NULL) {
*pNBIOContext = nbio;
goto cleanup;
}
}
rv = ocsp_VerifyResponseSignature(response->signerCert, signature,
tbsResponseDataDER, NULL);
cleanup:
if (rv == SECSuccess) {
*pPassed = PKIX_TRUE;
} else {
*pPassed = PKIX_FALSE;
}
if (signature) {
if (signature->wasChecked) {
signature->status = rv;
}
if (rv != SECSuccess) {
signature->failureReason = PORT_GetError();
if (response->signerCert != NULL) {
CERT_DestroyCertificate(response->signerCert);
response->signerCert = NULL;
}
} else {
/* Save signer's certificate in signature. */
signature->cert = CERT_DupCertificate(response->signerCert);
}
}
if (issuerCert)
CERT_DestroyCertificate(issuerCert);
PKIX_RETURN(OCSPRESPONSE);
}
/*
* FUNCTION: pkix_pl_OcspResponse_GetStatusForCert
* DESCRIPTION:
*
* This function checks the revocation status of the Cert for which the
* OcspResponse was obtained, storing PKIX_TRUE at "pPassed" if the Cert has
* not been revoked and PKIX_FALSE otherwise.
*
* PARAMETERS
* "response"
* The address of the OcspResponse whose certificate status is to be
* retrieved. Must be non-NULL.
* "pPassed"
* Address at which the Boolean result is stored. Must be non-NULL.
* "pReturnCode"
* Address at which the SECErrorCodes result is stored. Must be non-NULL.
* "plContext"
* Platform-specific context pointer.
* THREAD SAFETY:
* Thread Safe (see Thread Safety Definitions in Programmer's Guide)
* RETURNS:
* Returns NULL if the function succeeds.
* Returns an OcspResponse Error if the function fails in a non-fatal way.
* Returns a Fatal Error if the function fails in an unrecoverable way.
*/
PKIX_Error *
pkix_pl_OcspResponse_GetStatusForCert(
PKIX_PL_OcspCertID *cid,
PKIX_PL_OcspResponse *response,
PKIX_Boolean allowCachingOfFailures,
PKIX_PL_Date *validity,
PKIX_Boolean *pPassed,
SECErrorCodes *pReturnCode,
void *plContext)
{
PRTime time = 0;
SECStatus rv = SECFailure;
CERTOCSPSingleResponse *single = NULL;
PKIX_ENTER(OCSPRESPONSE, "pkix_pl_OcspResponse_GetStatusForCert");
PKIX_NULLCHECK_THREE(response, pPassed, pReturnCode);
/*
* It is an error to call this function except following a successful
* return from pkix_pl_OcspResponse_VerifySignature, which would have
* set response->signerCert.
*/
PKIX_NULLCHECK_TWO(response->signerCert, response->request);
PKIX_NULLCHECK_TWO(cid, cid->certID);
if (validity != NULL) {
PKIX_Error *er = pkix_pl_Date_GetPRTime(validity, &time, plContext);
PKIX_DECREF(er);
}
if (!time) {
time = PR_Now();
}
rv = ocsp_GetVerifiedSingleResponseForCertID(response->handle,
response->nssOCSPResponse,
cid->certID,
response->signerCert,
time, &single);
if (rv == SECSuccess) {
/*
* Check whether the status says revoked, and if so
* how that compares to the time value passed into this routine.
*/
rv = ocsp_CertHasGoodStatus(single->certStatus, time);
}
if (rv == SECSuccess || allowCachingOfFailures) {
/* allowed to update the cache */
PRBool certIDWasConsumed = PR_FALSE;
if (single) {
ocsp_CacheSingleResponse(cid->certID,single,
&certIDWasConsumed);
} else {
cert_RememberOCSPProcessingFailure(cid->certID,
&certIDWasConsumed);
}
if (certIDWasConsumed) {
cid->certID = NULL;
}
}
if (rv == SECSuccess) {
*pPassed = PKIX_TRUE;
*pReturnCode = 0;
} else {
*pPassed = PKIX_FALSE;
*pReturnCode = PORT_GetError();
}
PKIX_RETURN(OCSPRESPONSE);
}