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