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