| /* 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); |
| } |