mirror of
https://github.com/classilla/tenfourfox.git
synced 2024-08-11 05:29:00 +00:00
1068 lines
37 KiB
C
1068 lines
37 KiB
C
/* 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 {
|
|
rv = CERT_VerifyCert(response->handle, response->signerCert, PKIX_TRUE,
|
|
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);
|
|
}
|